eth-patched 0.4.13

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.
data/lib/eth/key.rb ADDED
@@ -0,0 +1,79 @@
1
+ module Eth
2
+ class Key
3
+ autoload :Decrypter, 'eth/key/decrypter'
4
+ autoload :Encrypter, 'eth/key/encrypter'
5
+
6
+ attr_reader :private_key, :public_key
7
+
8
+ def self.encrypt(key, password)
9
+ key = new(priv: key) unless key.is_a?(Key)
10
+
11
+ Encrypter.perform key.private_hex, password
12
+ end
13
+
14
+ def self.decrypt(data, password)
15
+ priv = Decrypter.perform data, password
16
+ new priv: priv
17
+ end
18
+
19
+ def self.personal_recover(message, signature)
20
+ bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack('c*')
21
+ OpenSsl.recover_compact(Utils.keccak256(Utils.prefix_message(message)), bin_signature)
22
+ end
23
+
24
+ def initialize(priv: nil)
25
+ @private_key = MoneyTree::PrivateKey.new key: priv
26
+ @public_key = MoneyTree::PublicKey.new private_key, compressed: false
27
+ end
28
+
29
+ def private_hex
30
+ private_key.to_hex
31
+ end
32
+
33
+ def public_bytes
34
+ public_key.to_bytes
35
+ end
36
+
37
+ def public_hex
38
+ public_key.to_hex
39
+ end
40
+
41
+ def address
42
+ Utils.public_key_to_address public_hex
43
+ end
44
+ alias_method :to_address, :address
45
+
46
+ def sign(message)
47
+ sign_hash message_hash(message)
48
+ end
49
+
50
+ def sign_hash(hash)
51
+ loop do
52
+ signature = OpenSsl.sign_compact hash, private_hex, public_hex
53
+ return signature if valid_s? signature
54
+ end
55
+ end
56
+
57
+ def verify_signature(message, signature)
58
+ hash = message_hash(message)
59
+ public_hex == OpenSsl.recover_compact(hash, signature)
60
+ end
61
+
62
+ def personal_sign(message)
63
+ Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack('c*'))
64
+ end
65
+
66
+
67
+ private
68
+
69
+ def message_hash(message)
70
+ Utils.keccak256 message
71
+ end
72
+
73
+ def valid_s?(signature)
74
+ s_value = Utils.v_r_s_for(signature).last
75
+ s_value <= Secp256k1::N/2 && s_value != 0
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,264 @@
1
+ # originally lifted from https://github.com/lian/bitcoin-ruby
2
+ # thanks to everyone there for figuring this out
3
+
4
+ module Eth
5
+ class OpenSsl
6
+ extend FFI::Library
7
+
8
+ if FFI::Platform.windows?
9
+ ffi_lib 'libeay32', 'ssleay32'
10
+ else
11
+ ffi_lib [
12
+ 'libssl.so.1.1.0', 'libssl.so.1.1',
13
+ 'libssl.so.1.0.0', 'libssl.so.10',
14
+ 'ssl'
15
+ ]
16
+ end
17
+
18
+ NID_secp256k1 = 714
19
+ POINT_CONVERSION_COMPRESSED = 2
20
+ POINT_CONVERSION_UNCOMPRESSED = 4
21
+
22
+ # OpenSSL 1.1.0 version as a numerical version value as defined in:
23
+ # https://www.openssl.org/docs/man1.1.0/man3/OpenSSL_version.html
24
+ VERSION_1_1_0_NUM = 0x10100000
25
+
26
+ # OpenSSL 1.1.0 engine constants, taken from:
27
+ # https://github.com/openssl/openssl/blob/2be8c56a39b0ec2ec5af6ceaf729df154d784a43/include/openssl/crypto.h
28
+ OPENSSL_INIT_ENGINE_RDRAND = 0x00000200
29
+ OPENSSL_INIT_ENGINE_DYNAMIC = 0x00000400
30
+ OPENSSL_INIT_ENGINE_CRYPTODEV = 0x00001000
31
+ OPENSSL_INIT_ENGINE_CAPI = 0x00002000
32
+ OPENSSL_INIT_ENGINE_PADLOCK = 0x00004000
33
+ OPENSSL_INIT_ENGINE_ALL_BUILTIN = (
34
+ OPENSSL_INIT_ENGINE_RDRAND |
35
+ OPENSSL_INIT_ENGINE_DYNAMIC |
36
+ OPENSSL_INIT_ENGINE_CRYPTODEV |
37
+ OPENSSL_INIT_ENGINE_CAPI |
38
+ OPENSSL_INIT_ENGINE_PADLOCK
39
+ )
40
+
41
+ # OpenSSL 1.1.0 load strings constant, taken from:
42
+ # https://github.com/openssl/openssl/blob/c162c126be342b8cd97996346598ecf7db56130f/include/openssl/ssl.h
43
+ OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000
44
+
45
+ # This is the very first function we need to use to determine what version
46
+ # of OpenSSL we are interacting with.
47
+ begin
48
+ attach_function :OpenSSL_version_num, [], :ulong
49
+ rescue FFI::NotFoundError
50
+ attach_function :SSLeay, [], :long
51
+ end
52
+
53
+ # Returns the version of SSL present.
54
+ #
55
+ # @return [Integer] version number as an integer.
56
+ def self.version
57
+ if self.respond_to?(:OpenSSL_version_num)
58
+ OpenSSL_version_num()
59
+ else
60
+ SSLeay()
61
+ end
62
+ end
63
+
64
+ if version >= VERSION_1_1_0_NUM
65
+ # Initialization procedure for the library was changed in OpenSSL 1.1.0
66
+ attach_function :OPENSSL_init_ssl, [:uint64, :pointer], :int
67
+ else
68
+ attach_function :SSL_library_init, [], :int
69
+ attach_function :ERR_load_crypto_strings, [], :void
70
+ attach_function :SSL_load_error_strings, [], :void
71
+ end
72
+
73
+ attach_function :RAND_poll, [], :int
74
+ attach_function :BN_CTX_free, [:pointer], :int
75
+ attach_function :BN_CTX_new, [], :pointer
76
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
77
+ attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
78
+ attach_function :BN_bn2bin, [:pointer, :pointer], :int
79
+ attach_function :BN_cmp, [:pointer, :pointer], :int
80
+ attach_function :BN_dup, [:pointer], :pointer
81
+ attach_function :BN_free, [:pointer], :int
82
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
83
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
84
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
85
+ attach_function :BN_mul_word, [:pointer, :int], :int
86
+ attach_function :BN_new, [], :pointer
87
+ attach_function :BN_num_bits, [:pointer], :int
88
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
89
+ attach_function :BN_set_word, [:pointer, :int], :int
90
+ attach_function :ECDSA_SIG_free, [:pointer], :void
91
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
92
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
93
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
94
+ attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
95
+ attach_function :EC_KEY_free, [:pointer], :int
96
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
97
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
98
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
99
+ attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
100
+ attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
101
+ attach_function :EC_POINT_free, [:pointer], :int
102
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
103
+ attach_function :EC_POINT_new, [:pointer], :pointer
104
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
105
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
106
+
107
+ class << self
108
+ def BN_num_bytes(ptr)
109
+ (BN_num_bits(ptr) + 7) / 8
110
+ end
111
+
112
+ def sign_compact(hash, private_key, public_key_hex)
113
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
114
+ pubkey_compressed = false
115
+
116
+ init_ffi_ssl
117
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
118
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
119
+
120
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
121
+ EC_GROUP_get_order(group, order, ctx)
122
+
123
+ pub_key = EC_POINT_new(group)
124
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
125
+ EC_KEY_set_private_key(eckey, priv_key)
126
+ EC_KEY_set_public_key(eckey, pub_key)
127
+
128
+ signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
129
+
130
+ BN_free(order)
131
+ BN_CTX_free(ctx)
132
+ EC_POINT_free(pub_key)
133
+ BN_free(priv_key)
134
+ EC_KEY_free(eckey)
135
+
136
+ buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
137
+ 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") }
138
+
139
+ if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
140
+ 4.times{|i|
141
+ head = [ Eth.v_base + i ].pack("C")
142
+ if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
143
+ rec_id = i; break
144
+ end
145
+ }
146
+ end
147
+
148
+ ECDSA_SIG_free(signature)
149
+
150
+ [ head, [r,s] ].join if rec_id
151
+ end
152
+
153
+ def recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
154
+ return nil if rec_id < 0 or signature.bytesize != 65
155
+ init_ffi_ssl
156
+
157
+ signature = FFI::MemoryPointer.from_string(signature)
158
+ r = BN_bin2bn(signature[1], 32, BN_new())
159
+ s = BN_bin2bn(signature[33], 32, BN_new())
160
+
161
+ _n, i = 0, rec_id / 2
162
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
163
+
164
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
165
+
166
+ group = EC_KEY_get0_group(eckey)
167
+ order = BN_new()
168
+ EC_GROUP_get_order(group, order, nil)
169
+ x = BN_dup(order)
170
+ BN_mul_word(x, i)
171
+ BN_add(x, x, r)
172
+
173
+ field = BN_new()
174
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
175
+
176
+ if BN_cmp(x, field) >= 0
177
+ bn_free_each r, s, order, x, field
178
+ EC_KEY_free(eckey)
179
+ return nil
180
+ end
181
+
182
+ big_r = EC_POINT_new(group)
183
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
184
+
185
+ big_q = EC_POINT_new(group)
186
+ n = EC_GROUP_get_degree(group)
187
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
188
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
189
+
190
+ ctx = BN_CTX_new()
191
+ zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
192
+ BN_set_word(zero, 0)
193
+ BN_mod_sub(e, zero, e, order, ctx)
194
+ BN_mod_inverse(rr, r, order, ctx)
195
+ BN_mod_mul(sor, s, rr, order, ctx)
196
+ BN_mod_mul(eor, e, rr, order, ctx)
197
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
198
+ EC_KEY_set_public_key(eckey, big_q)
199
+ BN_CTX_free(ctx)
200
+
201
+ bn_free_each r, s, order, x, field, e, zero, rr, sor, eor
202
+ [big_r, big_q].each{|j| EC_POINT_free(j) }
203
+
204
+ recover_public_hex eckey
205
+ end
206
+
207
+ def recover_compact(hash, signature)
208
+ return false if signature.bytesize != 65
209
+
210
+ version = signature.unpack('C')[0]
211
+
212
+ # Version of signature should be 27 or 28, but 0 and 1 are also possible versions
213
+ # which can show up in Ledger hardwallet signings
214
+ if version < 27
215
+ version += 27
216
+ end
217
+
218
+ v_base = Eth.replayable_v?(version) ? Eth.replayable_chain_id : Eth.v_base
219
+
220
+ return false if version < v_base
221
+
222
+ recover_public_key_from_signature(hash, signature, (version - v_base), false)
223
+ end
224
+
225
+ def init_ffi_ssl
226
+ return if @ssl_loaded
227
+ if version >= VERSION_1_1_0_NUM
228
+ OPENSSL_init_ssl(
229
+ OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_ENGINE_ALL_BUILTIN,
230
+ nil
231
+ )
232
+ else
233
+ SSL_library_init()
234
+ ERR_load_crypto_strings()
235
+ SSL_load_error_strings()
236
+ end
237
+
238
+ RAND_poll()
239
+ @ssl_loaded = true
240
+ end
241
+
242
+
243
+ private
244
+
245
+ def bn_free_each(*list)
246
+ list.each{|j| BN_free(j) }
247
+ end
248
+
249
+ def recover_public_hex(eckey)
250
+ length = i2o_ECPublicKey(eckey, nil)
251
+ buf = FFI::MemoryPointer.new(:uint8, length)
252
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
253
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
254
+ buf.read_string(length).unpack("H*")[0]
255
+ end
256
+
257
+ EC_KEY_free(eckey)
258
+
259
+ pub_hex
260
+ end
261
+ end
262
+
263
+ end
264
+ end
@@ -0,0 +1,7 @@
1
+ module Eth
2
+ class Secp256k1
3
+
4
+ N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
5
+
6
+ end
7
+ end
data/lib/eth/sedes.rb ADDED
@@ -0,0 +1,40 @@
1
+ module Eth
2
+ module Sedes
3
+ include RLP::Sedes
4
+
5
+ extend self
6
+
7
+ def address
8
+ Binary.fixed_length(20, allow_empty: true)
9
+ end
10
+
11
+ def int20
12
+ BigEndianInt.new(20)
13
+ end
14
+
15
+ def int32
16
+ BigEndianInt.new(32)
17
+ end
18
+
19
+ def int256
20
+ BigEndianInt.new(256)
21
+ end
22
+
23
+ def hash32
24
+ Binary.fixed_length(32)
25
+ end
26
+
27
+ def trie_root
28
+ Binary.fixed_length(32, allow_empty: true)
29
+ end
30
+
31
+ def big_endian_int
32
+ RLP::Sedes.big_endian_int
33
+ end
34
+
35
+ def binary
36
+ RLP::Sedes.binary
37
+ end
38
+
39
+ end
40
+ end
data/lib/eth/tx.rb ADDED
@@ -0,0 +1,197 @@
1
+ module Eth
2
+ class Tx
3
+
4
+ include RLP::Sedes::Serializable
5
+ extend Sedes
6
+
7
+ set_serializable_fields({
8
+ nonce: big_endian_int,
9
+ gas_price: big_endian_int,
10
+ gas_limit: big_endian_int,
11
+ to: address,
12
+ value: big_endian_int,
13
+ data_bin: binary,
14
+ v: big_endian_int,
15
+ r: big_endian_int,
16
+ s: big_endian_int
17
+ })
18
+
19
+ attr_writer :signature
20
+
21
+ def self.decode(data)
22
+ data = Utils.hex_to_bin(data) if data.match(/\A(?:0x)?\h+\Z/)
23
+ txh = deserialize(RLP.decode data).to_h
24
+
25
+ txh[:chain_id] = Eth.chain_id_from_signature(txh)
26
+
27
+ self.new txh
28
+ end
29
+
30
+ def initialize(params)
31
+ fields = {v: 0, r: 0, s: 0}.merge params
32
+ fields[:to] = Utils.normalize_address(fields[:to])
33
+
34
+ self.chain_id = (params[:chain_id]) ? params.delete(:chain_id) : Eth.chain_id
35
+
36
+ if params[:data]
37
+ self.data = params.delete(:data)
38
+ fields[:data_bin] = data_bin
39
+ end
40
+ serializable_initialize fields
41
+
42
+ check_transaction_validity
43
+ end
44
+
45
+ def unsigned_encoded
46
+ us = unsigned
47
+ RLP.encode(us, sedes: us.sedes)
48
+ end
49
+
50
+ def signing_data
51
+ Utils.bin_to_prefixed_hex unsigned_encoded
52
+ end
53
+
54
+ def encoded
55
+ RLP.encode self
56
+ end
57
+
58
+ def hex
59
+ Utils.bin_to_prefixed_hex encoded
60
+ end
61
+
62
+ def sign(key)
63
+ sig = key.sign(unsigned_encoded)
64
+ vrs = Utils.v_r_s_for sig
65
+ self.v = (self.chain_id) ? ((self.chain_id * 2) + vrs[0] + 8) : vrs[0]
66
+ self.r = vrs[1]
67
+ self.s = vrs[2]
68
+
69
+ clear_signature
70
+ self
71
+ end
72
+
73
+ def to_h
74
+ hash_keys.inject({}) do |hash, field|
75
+ hash[field] = send field
76
+ hash
77
+ end
78
+ end
79
+
80
+ def from
81
+ if ecdsa_signature
82
+ public_key = OpenSsl.recover_compact(signature_hash, ecdsa_signature)
83
+ Utils.public_key_to_address(public_key) if public_key
84
+ end
85
+ end
86
+
87
+ def signature
88
+ return @signature if @signature
89
+ @signature = { v: v, r: r, s: s } if [v, r, s].all? && (v > 0)
90
+ end
91
+
92
+ def ecdsa_signature
93
+ return @ecdsa_signature if @ecdsa_signature
94
+
95
+ if [v, r, s].all? && (v > 0)
96
+ s_v = (self.chain_id) ? (v - (self.chain_id * 2) - 8) : v
97
+ @ecdsa_signature = [
98
+ Utils.int_to_base256(s_v),
99
+ Utils.zpad_int(r),
100
+ Utils.zpad_int(s),
101
+ ].join
102
+ end
103
+ end
104
+
105
+ def hash
106
+ "0x#{Utils.bin_to_hex Utils.keccak256_rlp(self)}"
107
+ end
108
+ alias_method :id, :hash
109
+
110
+ def data_hex
111
+ Utils.bin_to_prefixed_hex data_bin
112
+ end
113
+
114
+ def data_hex=(hex)
115
+ self.data_bin = Utils.hex_to_bin(hex)
116
+ end
117
+
118
+ def data
119
+ Eth.tx_data_hex? ? data_hex : data_bin
120
+ end
121
+
122
+ def data=(string)
123
+ Eth.tx_data_hex? ? self.data_hex=(string) : self.data_bin=(string)
124
+ end
125
+
126
+ def chain_id
127
+ @chain_id
128
+ end
129
+
130
+ def chain_id=(cid)
131
+ if cid != @chain_id
132
+ self.v = 0
133
+ self.r = 0
134
+ self.s = 0
135
+
136
+ clear_signature
137
+ end
138
+
139
+ @chain_id = (cid == 0) ? nil : cid
140
+ end
141
+
142
+ def prevent_replays?
143
+ !self.chain_id.nil?
144
+ end
145
+
146
+ private
147
+
148
+ def clear_signature
149
+ @signature = nil
150
+ @ecdsa_signature = nil
151
+ end
152
+
153
+ def hash_keys
154
+ keys = self.class.serializable_fields.keys
155
+ keys.delete(:data_bin)
156
+ keys + [:data, :chain_id]
157
+ end
158
+
159
+ def check_transaction_validity
160
+ if [gas_price, gas_limit, value, nonce].max > UINT_MAX
161
+ raise InvalidTransaction, "Values way too high!"
162
+ elsif gas_limit < intrinsic_gas_used
163
+ raise InvalidTransaction, "Gas limit too low"
164
+ end
165
+ end
166
+
167
+ def intrinsic_gas_used
168
+ num_zero_bytes = data_bin.count(BYTE_ZERO)
169
+ num_non_zero_bytes = data_bin.size - num_zero_bytes
170
+
171
+ Gas::GTXCOST +
172
+ Gas::GTXDATAZERO * num_zero_bytes +
173
+ Gas::GTXDATANONZERO * num_non_zero_bytes
174
+ end
175
+
176
+ def signature_hash
177
+ Utils.keccak256 unsigned_encoded
178
+ end
179
+
180
+ def unsigned
181
+ Tx.new to_h.merge(v: (self.chain_id) ? self.chain_id : 0, r: 0, s: 0)
182
+ end
183
+
184
+ protected
185
+
186
+ def sedes
187
+ if self.prevent_replays? && !(Eth.replayable_v? v)
188
+ self.class
189
+ else
190
+ UnsignedTx
191
+ end
192
+ end
193
+
194
+ end
195
+
196
+ UnsignedTx = Tx.exclude([:v, :r, :s])
197
+ end
data/lib/eth/utils.rb ADDED
@@ -0,0 +1,130 @@
1
+ module Eth
2
+ module Utils
3
+
4
+ extend self
5
+
6
+ def normalize_address(address)
7
+ if address.nil? || address == ''
8
+ ''
9
+ elsif address.size == 40
10
+ hex_to_bin address
11
+ elsif address.size == 42 && address[0..1] == '0x'
12
+ hex_to_bin address[2..-1]
13
+ else
14
+ address
15
+ end
16
+ end
17
+
18
+ def bin_to_hex(string)
19
+ RLP::Utils.encode_hex string
20
+ end
21
+
22
+ def hex_to_bin(string)
23
+ RLP::Utils.decode_hex remove_hex_prefix(string)
24
+ end
25
+
26
+ def base256_to_int(str)
27
+ RLP::Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, '')
28
+ end
29
+
30
+ def int_to_base256(int)
31
+ RLP::Sedes.big_endian_int.serialize int
32
+ end
33
+
34
+ def v_r_s_for(signature)
35
+ [
36
+ signature[0].bytes[0],
37
+ Utils.base256_to_int(signature[1..32]),
38
+ Utils.base256_to_int(signature[33..65]),
39
+ ]
40
+ end
41
+
42
+ def prefix_hex(hex)
43
+ hex.match(/\A0x/) ? hex : "0x#{hex}"
44
+ end
45
+
46
+ def remove_hex_prefix(s)
47
+ s[0,2] == '0x' ? s[2..-1] : s
48
+ end
49
+
50
+ def bin_to_prefixed_hex(binary)
51
+ prefix_hex bin_to_hex(binary)
52
+ end
53
+
54
+ def prefix_message(message)
55
+ "\x19Ethereum Signed Message:\n#{message.length}#{message}"
56
+ end
57
+
58
+ def public_key_to_address(hex)
59
+ bytes = hex_to_bin(hex)
60
+ address_bytes = Utils.keccak256(bytes[1..-1])[-20..-1]
61
+ format_address bin_to_prefixed_hex(address_bytes)
62
+ end
63
+
64
+ def sha256(x)
65
+ Digest::SHA256.digest x
66
+ end
67
+
68
+ def keccak256(x)
69
+ Digest::SHA3.new(256).digest(x)
70
+ end
71
+
72
+ def keccak512(x)
73
+ Digest::SHA3.new(512).digest(x)
74
+ end
75
+
76
+ def keccak256_rlp(x)
77
+ keccak256 RLP.encode(x)
78
+ end
79
+
80
+ def ripemd160(x)
81
+ Digest::RMD160.digest x
82
+ end
83
+
84
+ def hash160(x)
85
+ ripemd160 sha256(x)
86
+ end
87
+
88
+ def zpad(x, l)
89
+ lpad x, BYTE_ZERO, l
90
+ end
91
+
92
+ def zunpad(x)
93
+ x.sub(/\A\x00+/, '')
94
+ end
95
+
96
+ def zpad_int(n, l=32)
97
+ zpad encode_int(n), l
98
+ end
99
+
100
+ def zpad_hex(s, l=32)
101
+ zpad decode_hex(s), l
102
+ end
103
+
104
+ def valid_address?(address)
105
+ Address.new(address).valid?
106
+ end
107
+
108
+ def format_address(address)
109
+ Address.new(address).checksummed
110
+ end
111
+
112
+
113
+
114
+ private
115
+
116
+ def lpad(x, symbol, l)
117
+ return x if x.size >= l
118
+ symbol * (l - x.size) + x
119
+ end
120
+
121
+ def encode_int(n)
122
+ unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
123
+ raise ArgumentError, "Integer invalid or out of range: #{n}"
124
+ end
125
+
126
+ int_to_base256 n
127
+ end
128
+
129
+ end
130
+ end