eth 0.4.17 → 0.4.18
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 +4 -4
- data/.github/workflows/build.yml +13 -3
- data/.github/workflows/codeql.yml +9 -3
- data/.github/workflows/docs.yml +26 -0
- data/Gemfile +4 -1
- data/eth.gemspec +29 -28
- data/lib/eth/address.rb +0 -3
- data/lib/eth/gas.rb +0 -2
- data/lib/eth/key/decrypter.rb +17 -20
- data/lib/eth/key/encrypter.rb +12 -14
- data/lib/eth/key.rb +6 -7
- data/lib/eth/open_ssl.rb +305 -174
- data/lib/eth/secp256k1.rb +0 -2
- data/lib/eth/sedes.rb +0 -1
- data/lib/eth/tx.rb +4 -5
- data/lib/eth/utils.rb +8 -11
- data/lib/eth/version.rb +1 -1
- data/lib/eth.rb +13 -13
- metadata +21 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cfb1dbda97ae8033690aeb0ffe7410e2db112dbb3d2c78f34bad15db7dcbf45
|
4
|
+
data.tar.gz: '039a0fc3b15cd132f1409fc8636f32d501f237e338628c63672f21fed81047af'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e69bf16bccb10f673673c8f45167817930549813a3e9fa33b64e868a618f6bbd810c23a8512c1a0f535a18d38238b71ab18afdfd438e6ebd8cffb2deb5256a79
|
7
|
+
data.tar.gz: 67c6de579f25a7b9cdecd8fee97f5df927d8f0145c03b9604c324e86925ba905f6e98b42e49b1487178842ea25efe3a8f0ea35162ee96666db85345bf0281bd4
|
data/.github/workflows/build.yml
CHANGED
@@ -11,16 +11,26 @@ on:
|
|
11
11
|
|
12
12
|
jobs:
|
13
13
|
build:
|
14
|
-
runs-on:
|
14
|
+
runs-on: ${{ matrix.os }}
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
os: [ubuntu-latest, macos-latest]
|
19
|
+
ruby: ['2.7', '3.0']
|
15
20
|
steps:
|
16
21
|
- uses: actions/checkout@v2
|
17
22
|
- uses: ruby/setup-ruby@v1
|
18
23
|
with:
|
19
|
-
ruby-version:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true
|
26
|
+
- name: Brew Automake
|
27
|
+
run: |
|
28
|
+
brew install automake
|
29
|
+
if: startsWith(matrix.os, 'macOS')
|
20
30
|
- name: Install Dependencies
|
21
31
|
run: |
|
22
32
|
git submodule update --init
|
23
33
|
bundle install
|
24
34
|
- name: Run Tests
|
25
35
|
run: |
|
26
|
-
rspec
|
36
|
+
bundle exec rspec
|
@@ -8,9 +8,6 @@ on:
|
|
8
8
|
push:
|
9
9
|
branches:
|
10
10
|
- develop
|
11
|
-
schedule:
|
12
|
-
-
|
13
|
-
cron: "45 1 * * 3"
|
14
11
|
|
15
12
|
jobs:
|
16
13
|
analyze:
|
@@ -36,3 +33,12 @@ jobs:
|
|
36
33
|
uses: github/codeql-action/autobuild@v1
|
37
34
|
- name: "Perform CodeQL Analysis"
|
38
35
|
uses: github/codeql-action/analyze@v1
|
36
|
+
- uses: ruby/setup-ruby@v1
|
37
|
+
with:
|
38
|
+
ruby-version: '2.7'
|
39
|
+
bundler-cache: true
|
40
|
+
- name: "Run rufo code formatting checks"
|
41
|
+
run: |
|
42
|
+
gem install rufo
|
43
|
+
rufo --check ./lib
|
44
|
+
rufo --check ./spec
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
name: Docs
|
3
|
+
|
4
|
+
on:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- develop
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
docs:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: '2.7'
|
17
|
+
bundler-cache: true
|
18
|
+
- name: Run Yard Doc
|
19
|
+
run: |
|
20
|
+
gem install yard
|
21
|
+
yard doc
|
22
|
+
- name: Deploy GH Pages
|
23
|
+
uses: JamesIves/github-pages-deploy-action@4.1.7
|
24
|
+
with:
|
25
|
+
branch: gh-pages
|
26
|
+
folder: doc/
|
data/Gemfile
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# ref: https://github.com/GemHQ/money-tree/issues/50
|
4
|
+
gem "money-tree", git: "https://github.com/GemHQ/money-tree.git"
|
5
|
+
|
6
|
+
source "https://rubygems.org"
|
4
7
|
|
5
8
|
# Specify your gem's dependencies in eth.gemspec
|
6
9
|
gemspec
|
data/eth.gemspec
CHANGED
@@ -1,46 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
lib = File.expand_path(
|
4
|
+
lib = File.expand_path("lib", __dir__).freeze
|
5
5
|
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
|
6
6
|
|
7
|
-
require
|
7
|
+
require "eth/version"
|
8
8
|
|
9
9
|
Gem::Specification.new do |spec|
|
10
|
-
spec.name
|
11
|
-
spec.version
|
12
|
-
spec.authors
|
13
|
-
spec.email
|
10
|
+
spec.name = "eth"
|
11
|
+
spec.version = Eth::VERSION
|
12
|
+
spec.authors = ["Steve Ellis", "Afri Schoedon"]
|
13
|
+
spec.email = ["email@steveell.is", "ruby@q9f.cc"]
|
14
14
|
|
15
|
-
spec.summary
|
16
|
-
spec.description
|
17
|
-
spec.homepage
|
18
|
-
spec.license
|
15
|
+
spec.summary = %q{Simple API to sign Ethereum transactions.}
|
16
|
+
spec.description = %q{Library to build, parse, and sign Ethereum transactions.}
|
17
|
+
spec.homepage = "https://github.com/se3000/ruby-eth"
|
18
|
+
spec.license = "MIT"
|
19
19
|
|
20
20
|
spec.metadata = {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
"homepage_uri" => "https://github.com/se3000/ruby-eth",
|
22
|
+
"source_code_uri" => "https://github.com/se3000/ruby-eth",
|
23
|
+
"github_repo" => "https://github.com/se3000/ruby-eth",
|
24
|
+
"bug_tracker_uri" => "https://github.com/se3000/ruby-eth/issues",
|
25
25
|
}.freeze
|
26
26
|
|
27
|
-
spec.files
|
28
|
-
spec.bindir
|
29
|
-
spec.executables
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ["lib"]
|
31
|
-
spec.test_files
|
31
|
+
spec.test_files = spec.files.grep %r{^(test|spec|features)/}
|
32
32
|
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
33
|
+
spec.add_dependency "keccak", "~> 1.3"
|
34
|
+
spec.add_dependency "ffi", "~> 1.15"
|
35
|
+
spec.add_dependency "money-tree", "~> 0.11"
|
36
|
+
spec.add_dependency "openssl", "~> 3.0"
|
37
|
+
spec.add_dependency "rlp", "~> 0.7"
|
38
|
+
spec.add_dependency "scrypt", "~> 3.0"
|
38
39
|
|
39
40
|
spec.platform = Gem::Platform::RUBY
|
40
|
-
spec.required_ruby_version = ">= 2.
|
41
|
+
spec.required_ruby_version = ">= 2.6", "< 4.0"
|
41
42
|
|
42
|
-
spec.add_development_dependency
|
43
|
-
spec.add_development_dependency
|
44
|
-
spec.add_development_dependency
|
45
|
-
spec.add_development_dependency
|
43
|
+
spec.add_development_dependency "bundler", "~> 2.2"
|
44
|
+
spec.add_development_dependency "pry", "~> 0.14"
|
45
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
46
|
+
spec.add_development_dependency "rspec", "~> 3.10"
|
46
47
|
end
|
data/lib/eth/address.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Eth
|
2
2
|
class Address
|
3
|
-
|
4
3
|
def initialize(address)
|
5
4
|
@address = Utils.prefix_hex(address)
|
6
5
|
end
|
@@ -25,7 +24,6 @@ module Eth
|
|
25
24
|
Utils.prefix_hex(cased.join)
|
26
25
|
end
|
27
26
|
|
28
|
-
|
29
27
|
private
|
30
28
|
|
31
29
|
attr_reader :address
|
@@ -57,6 +55,5 @@ module Eth
|
|
57
55
|
def unprefixed
|
58
56
|
Utils.remove_hex_prefix address
|
59
57
|
end
|
60
|
-
|
61
58
|
end
|
62
59
|
end
|
data/lib/eth/gas.rb
CHANGED
data/lib/eth/key/decrypter.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "json"
|
2
|
+
require "scrypt"
|
3
3
|
|
4
4
|
class Eth::Key::Decrypter
|
5
5
|
include Eth::Utils
|
@@ -19,16 +19,15 @@ class Eth::Key::Decrypter
|
|
19
19
|
bin_to_hex decrypted_data
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
22
|
private
|
24
23
|
|
25
24
|
attr_reader :data, :key, :password
|
26
25
|
|
27
26
|
def derive_key(password)
|
28
27
|
case kdf
|
29
|
-
when
|
28
|
+
when "pbkdf2"
|
30
29
|
@key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
|
31
|
-
when
|
30
|
+
when "scrypt"
|
32
31
|
# OpenSSL 1.1 inclues OpenSSL::KDF.scrypt, but it is not available usually, otherwise we could do: OpenSSL::KDF.scrypt(password, salt: salt, N: n, r: r, p: p, length: key_length)
|
33
32
|
@key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
|
34
33
|
else
|
@@ -37,8 +36,8 @@ class Eth::Key::Decrypter
|
|
37
36
|
end
|
38
37
|
|
39
38
|
def check_macs
|
40
|
-
mac1 = keccak256(key[(key_length/2), key_length] + ciphertext)
|
41
|
-
mac2 = hex_to_bin crypto_data[
|
39
|
+
mac1 = keccak256(key[(key_length / 2), key_length] + ciphertext)
|
40
|
+
mac2 = hex_to_bin crypto_data["mac"]
|
42
41
|
|
43
42
|
if mac1 != mac2
|
44
43
|
raise "Message Authentications Codes do not match!"
|
@@ -50,11 +49,11 @@ class Eth::Key::Decrypter
|
|
50
49
|
end
|
51
50
|
|
52
51
|
def crypto_data
|
53
|
-
@crypto_data ||= data[
|
52
|
+
@crypto_data ||= data["crypto"] || data["Crypto"]
|
54
53
|
end
|
55
54
|
|
56
55
|
def ciphertext
|
57
|
-
hex_to_bin crypto_data[
|
56
|
+
hex_to_bin crypto_data["ciphertext"]
|
58
57
|
end
|
59
58
|
|
60
59
|
def cipher_name
|
@@ -64,41 +63,41 @@ class Eth::Key::Decrypter
|
|
64
63
|
def cipher
|
65
64
|
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
|
66
65
|
cipher.decrypt
|
67
|
-
cipher.key = key[0, (key_length/2)]
|
66
|
+
cipher.key = key[0, (key_length / 2)]
|
68
67
|
cipher.iv = iv
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
72
71
|
def iv
|
73
|
-
hex_to_bin crypto_data[
|
72
|
+
hex_to_bin crypto_data["cipherparams"]["iv"]
|
74
73
|
end
|
75
74
|
|
76
75
|
def salt
|
77
|
-
hex_to_bin crypto_data[
|
76
|
+
hex_to_bin crypto_data["kdfparams"]["salt"]
|
78
77
|
end
|
79
78
|
|
80
79
|
def iterations
|
81
|
-
crypto_data[
|
80
|
+
crypto_data["kdfparams"]["c"].to_i
|
82
81
|
end
|
83
82
|
|
84
83
|
def kdf
|
85
|
-
crypto_data[
|
84
|
+
crypto_data["kdf"]
|
86
85
|
end
|
87
86
|
|
88
87
|
def key_length
|
89
|
-
crypto_data[
|
88
|
+
crypto_data["kdfparams"]["dklen"].to_i
|
90
89
|
end
|
91
90
|
|
92
91
|
def n
|
93
|
-
crypto_data[
|
92
|
+
crypto_data["kdfparams"]["n"].to_i
|
94
93
|
end
|
95
94
|
|
96
95
|
def r
|
97
|
-
crypto_data[
|
96
|
+
crypto_data["kdfparams"]["r"].to_i
|
98
97
|
end
|
99
98
|
|
100
99
|
def p
|
101
|
-
crypto_data[
|
100
|
+
crypto_data["kdfparams"]["p"].to_i
|
102
101
|
end
|
103
102
|
|
104
103
|
def digest
|
@@ -108,6 +107,4 @@ class Eth::Key::Decrypter
|
|
108
107
|
def digest_name
|
109
108
|
"sha256"
|
110
109
|
end
|
111
|
-
|
112
|
-
|
113
110
|
end
|
data/lib/eth/key/encrypter.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "json"
|
2
|
+
require "securerandom"
|
3
3
|
|
4
4
|
class Eth::Key::Encrypter
|
5
5
|
include Eth::Utils
|
@@ -48,7 +48,6 @@ class Eth::Key::Encrypter
|
|
48
48
|
@id ||= options[:id] || SecureRandom.uuid
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
51
|
private
|
53
52
|
|
54
53
|
attr_reader :derived_key, :encrypted_key, :key, :options
|
@@ -57,7 +56,7 @@ class Eth::Key::Encrypter
|
|
57
56
|
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
|
58
57
|
cipher.encrypt
|
59
58
|
cipher.iv = iv
|
60
|
-
cipher.key = derived_key[0, (key_length/2)]
|
59
|
+
cipher.key = derived_key[0, (key_length / 2)]
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
@@ -74,7 +73,7 @@ class Eth::Key::Encrypter
|
|
74
73
|
end
|
75
74
|
|
76
75
|
def mac
|
77
|
-
keccak256(derived_key[(key_length/2), key_length] + encrypted_key)
|
76
|
+
keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
|
78
77
|
end
|
79
78
|
|
80
79
|
def cipher_name
|
@@ -107,22 +106,21 @@ class Eth::Key::Encrypter
|
|
107
106
|
|
108
107
|
def salt
|
109
108
|
@salt ||= if options[:salt]
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
109
|
+
hex_to_bin options[:salt]
|
110
|
+
else
|
111
|
+
SecureRandom.random_bytes(salt_length)
|
112
|
+
end
|
114
113
|
end
|
115
114
|
|
116
115
|
def iv
|
117
116
|
@iv ||= if options[:iv]
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
117
|
+
hex_to_bin options[:iv]
|
118
|
+
else
|
119
|
+
SecureRandom.random_bytes(iv_length)
|
120
|
+
end
|
122
121
|
end
|
123
122
|
|
124
123
|
def address
|
125
124
|
Eth::Key.new(priv: key).address
|
126
125
|
end
|
127
|
-
|
128
126
|
end
|
data/lib/eth/key.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Eth
|
2
2
|
class Key
|
3
|
-
autoload :Decrypter,
|
4
|
-
autoload :Encrypter,
|
3
|
+
autoload :Decrypter, "eth/key/decrypter"
|
4
|
+
autoload :Encrypter, "eth/key/encrypter"
|
5
5
|
|
6
6
|
attr_reader :private_key, :public_key
|
7
7
|
|
@@ -17,7 +17,7 @@ module Eth
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.personal_recover(message, signature)
|
20
|
-
bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack(
|
20
|
+
bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack("c*")
|
21
21
|
OpenSsl.recover_compact(Utils.keccak256(Utils.prefix_message(message)), bin_signature)
|
22
22
|
end
|
23
23
|
|
@@ -41,6 +41,7 @@ module Eth
|
|
41
41
|
def address
|
42
42
|
Utils.public_key_to_address public_hex
|
43
43
|
end
|
44
|
+
|
44
45
|
alias_method :to_address, :address
|
45
46
|
|
46
47
|
def sign(message)
|
@@ -60,10 +61,9 @@ module Eth
|
|
60
61
|
end
|
61
62
|
|
62
63
|
def personal_sign(message)
|
63
|
-
Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack(
|
64
|
+
Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack("c*"))
|
64
65
|
end
|
65
66
|
|
66
|
-
|
67
67
|
private
|
68
68
|
|
69
69
|
def message_hash(message)
|
@@ -72,8 +72,7 @@ module Eth
|
|
72
72
|
|
73
73
|
def valid_s?(signature)
|
74
74
|
s_value = Utils.v_r_s_for(signature).last
|
75
|
-
s_value <= Secp256k1::N/2 && s_value != 0
|
75
|
+
s_value <= Secp256k1::N / 2 && s_value != 0
|
76
76
|
end
|
77
|
-
|
78
77
|
end
|
79
78
|
end
|
data/lib/eth/open_ssl.rb
CHANGED
@@ -1,28 +1,22 @@
|
|
1
1
|
# originally lifted from https://github.com/lian/bitcoin-ruby
|
2
2
|
# thanks to everyone there for figuring this out
|
3
3
|
|
4
|
+
# encoding: ascii-8bit
|
5
|
+
|
6
|
+
require "openssl"
|
7
|
+
require "ffi"
|
8
|
+
|
4
9
|
module Eth
|
5
10
|
class OpenSsl
|
6
11
|
extend FFI::Library
|
7
12
|
|
8
|
-
|
9
|
-
|
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
|
13
|
+
# Use the library loaded by the extension require above.
|
14
|
+
ffi_lib FFI::CURRENT_PROCESS
|
17
15
|
|
18
16
|
NID_secp256k1 = 714
|
19
17
|
POINT_CONVERSION_COMPRESSED = 2
|
20
18
|
POINT_CONVERSION_UNCOMPRESSED = 4
|
21
19
|
|
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
20
|
# OpenSSL 1.1.0 engine constants, taken from:
|
27
21
|
# https://github.com/openssl/openssl/blob/2be8c56a39b0ec2ec5af6ceaf729df154d784a43/include/openssl/crypto.h
|
28
22
|
OPENSSL_INIT_ENGINE_RDRAND = 0x00000200
|
@@ -30,13 +24,11 @@ module Eth
|
|
30
24
|
OPENSSL_INIT_ENGINE_CRYPTODEV = 0x00001000
|
31
25
|
OPENSSL_INIT_ENGINE_CAPI = 0x00002000
|
32
26
|
OPENSSL_INIT_ENGINE_PADLOCK = 0x00004000
|
33
|
-
OPENSSL_INIT_ENGINE_ALL_BUILTIN = (
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
OPENSSL_INIT_ENGINE_PADLOCK
|
39
|
-
)
|
27
|
+
OPENSSL_INIT_ENGINE_ALL_BUILTIN = (OPENSSL_INIT_ENGINE_RDRAND |
|
28
|
+
OPENSSL_INIT_ENGINE_DYNAMIC |
|
29
|
+
OPENSSL_INIT_ENGINE_CRYPTODEV |
|
30
|
+
OPENSSL_INIT_ENGINE_CAPI |
|
31
|
+
OPENSSL_INIT_ENGINE_PADLOCK)
|
40
32
|
|
41
33
|
# OpenSSL 1.1.0 load strings constant, taken from:
|
42
34
|
# https://github.com/openssl/openssl/blob/c162c126be342b8cd97996346598ecf7db56130f/include/openssl/ssl.h
|
@@ -50,215 +42,354 @@ module Eth
|
|
50
42
|
attach_function :SSLeay, [], :long
|
51
43
|
end
|
52
44
|
|
53
|
-
|
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
|
45
|
+
begin
|
65
46
|
# Initialization procedure for the library was changed in OpenSSL 1.1.0
|
66
47
|
attach_function :OPENSSL_init_ssl, [:uint64, :pointer], :int
|
67
|
-
|
48
|
+
rescue FFI::NotFoundError
|
68
49
|
attach_function :SSL_library_init, [], :int
|
69
50
|
attach_function :ERR_load_crypto_strings, [], :void
|
70
51
|
attach_function :SSL_load_error_strings, [], :void
|
71
52
|
end
|
72
53
|
|
73
54
|
attach_function :RAND_poll, [], :int
|
55
|
+
|
74
56
|
attach_function :BN_CTX_free, [:pointer], :int
|
75
57
|
attach_function :BN_CTX_new, [], :pointer
|
76
|
-
attach_function :BN_add, [
|
77
|
-
attach_function :BN_bin2bn, [
|
78
|
-
attach_function :BN_bn2bin, [
|
79
|
-
attach_function :BN_cmp, [
|
58
|
+
attach_function :BN_add, %i[pointer pointer pointer], :int
|
59
|
+
attach_function :BN_bin2bn, %i[pointer int pointer], :pointer
|
60
|
+
attach_function :BN_bn2bin, %i[pointer pointer], :int
|
61
|
+
attach_function :BN_cmp, %i[pointer pointer], :int
|
80
62
|
attach_function :BN_dup, [:pointer], :pointer
|
81
63
|
attach_function :BN_free, [:pointer], :int
|
82
|
-
attach_function :BN_mod_inverse, [
|
83
|
-
attach_function :BN_mod_mul, [
|
84
|
-
attach_function :BN_mod_sub, [
|
85
|
-
attach_function :BN_mul_word, [
|
64
|
+
attach_function :BN_mod_inverse, %i[pointer pointer pointer pointer], :pointer
|
65
|
+
attach_function :BN_mod_mul, %i[pointer pointer pointer pointer pointer], :int
|
66
|
+
attach_function :BN_mod_sub, %i[pointer pointer pointer pointer pointer], :int
|
67
|
+
attach_function :BN_mul_word, %i[pointer int], :int
|
86
68
|
attach_function :BN_new, [], :pointer
|
87
|
-
attach_function :
|
88
|
-
attach_function :
|
89
|
-
attach_function :BN_set_word, [
|
90
|
-
attach_function :
|
91
|
-
attach_function :
|
92
|
-
attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
69
|
+
attach_function :BN_rshift, %i[pointer pointer int], :int
|
70
|
+
attach_function :BN_rshift1, %i[pointer pointer], :int
|
71
|
+
attach_function :BN_set_word, %i[pointer int], :int
|
72
|
+
attach_function :BN_sub, %i[pointer pointer pointer], :int
|
73
|
+
attach_function :EC_GROUP_get_curve_GFp, %i[pointer pointer pointer pointer pointer], :int
|
93
74
|
attach_function :EC_GROUP_get_degree, [:pointer], :int
|
94
|
-
attach_function :EC_GROUP_get_order, [
|
75
|
+
attach_function :EC_GROUP_get_order, %i[pointer pointer pointer], :int
|
95
76
|
attach_function :EC_KEY_free, [:pointer], :int
|
96
77
|
attach_function :EC_KEY_get0_group, [:pointer], :pointer
|
78
|
+
attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
|
97
79
|
attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
|
98
|
-
attach_function :EC_KEY_set_conv_form, [
|
99
|
-
attach_function :EC_KEY_set_private_key, [
|
100
|
-
attach_function :EC_KEY_set_public_key,
|
80
|
+
attach_function :EC_KEY_set_conv_form, %i[pointer int], :void
|
81
|
+
attach_function :EC_KEY_set_private_key, %i[pointer pointer], :int
|
82
|
+
attach_function :EC_KEY_set_public_key, %i[pointer pointer], :int
|
101
83
|
attach_function :EC_POINT_free, [:pointer], :int
|
102
|
-
attach_function :EC_POINT_mul, [
|
84
|
+
attach_function :EC_POINT_mul, %i[pointer pointer pointer pointer pointer pointer], :int
|
103
85
|
attach_function :EC_POINT_new, [:pointer], :pointer
|
104
|
-
attach_function :EC_POINT_set_compressed_coordinates_GFp,
|
105
|
-
|
86
|
+
attach_function :EC_POINT_set_compressed_coordinates_GFp,
|
87
|
+
%i[pointer pointer pointer int pointer], :int
|
88
|
+
attach_function :i2o_ECPublicKey, %i[pointer pointer], :uint
|
89
|
+
attach_function :ECDSA_do_sign, %i[pointer uint pointer], :pointer
|
90
|
+
attach_function :BN_num_bits, [:pointer], :int
|
91
|
+
attach_function :ECDSA_SIG_free, [:pointer], :void
|
92
|
+
attach_function :EC_POINT_add, %i[pointer pointer pointer pointer pointer], :int
|
93
|
+
attach_function :EC_POINT_point2hex, %i[pointer pointer int pointer], :string
|
94
|
+
attach_function :EC_POINT_hex2point, %i[pointer string pointer pointer], :pointer
|
95
|
+
attach_function :d2i_ECDSA_SIG, %i[pointer pointer long], :pointer
|
96
|
+
attach_function :i2d_ECDSA_SIG, %i[pointer pointer], :int
|
97
|
+
attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
|
98
|
+
|
99
|
+
def self.BN_num_bytes(ptr) # rubocop:disable Naming/MethodName
|
100
|
+
(BN_num_bits(ptr) + 7) / 8
|
101
|
+
end
|
106
102
|
|
107
|
-
|
108
|
-
|
109
|
-
|
103
|
+
# resolve public from private key, using ffi and libssl.so
|
104
|
+
# example:
|
105
|
+
# keypair = Bitcoin.generate_key; Bitcoin::OpenSSL_EC.regenerate_key(keypair.first) == keypair
|
106
|
+
def self.regenerate_key(private_key)
|
107
|
+
private_key = [private_key].pack("H*") if private_key.bytesize >= (32 * 2)
|
108
|
+
private_key_hex = private_key.unpack("H*")[0]
|
109
|
+
|
110
|
+
group = OpenSSL::PKey::EC::Group.new("secp256k1")
|
111
|
+
key = OpenSSL::PKey::EC.new(group)
|
112
|
+
key.private_key = OpenSSL::BN.new(private_key_hex, 16)
|
113
|
+
key.public_key = group.generator.mul(key.private_key)
|
114
|
+
|
115
|
+
priv_hex = key.private_key.to_bn.to_s(16).downcase.rjust(64, "0")
|
116
|
+
if priv_hex != private_key_hex
|
117
|
+
raise "regenerated wrong private_key, raise here before generating a faulty public_key too!"
|
110
118
|
end
|
111
119
|
|
112
|
-
|
113
|
-
|
114
|
-
pubkey_compressed = false
|
120
|
+
[priv_hex, key.public_key.to_bn.to_s(16).downcase]
|
121
|
+
end
|
115
122
|
|
116
|
-
|
117
|
-
|
118
|
-
|
123
|
+
# Given the components of a signature and a selector value, recover and
|
124
|
+
# return the public key that generated the signature according to the
|
125
|
+
# algorithm in SEC1v2 section 4.1.6.
|
126
|
+
#
|
127
|
+
# rec_id is an index from 0 to 3 that indicates which of the 4 possible
|
128
|
+
# keys is the correct one. Because the key recovery operation yields
|
129
|
+
# multiple potential keys, the correct key must either be stored alongside
|
130
|
+
# the signature, or you must be willing to try each rec_id in turn until
|
131
|
+
# you find one that outputs the key you are expecting.
|
132
|
+
#
|
133
|
+
# If this method returns nil, it means recovery was not possible and rec_id
|
134
|
+
# should be iterated.
|
135
|
+
#
|
136
|
+
# Given the above two points, a correct usage of this method is inside a
|
137
|
+
# for loop from 0 to 3, and if the output is nil OR a key that is not the
|
138
|
+
# one you expect, you try again with the next rec_id.
|
139
|
+
#
|
140
|
+
# message_hash = hash of the signed message.
|
141
|
+
# signature = the R and S components of the signature, wrapped.
|
142
|
+
# rec_id = which possible key to recover.
|
143
|
+
# is_compressed = whether or not the original pubkey was compressed.
|
144
|
+
def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
|
145
|
+
return nil if rec_id < 0 || signature.bytesize != 65
|
146
|
+
init_ffi_ssl
|
147
|
+
|
148
|
+
signature = FFI::MemoryPointer.from_string(signature)
|
149
|
+
# signature_bn = BN_bin2bn(signature, 65, BN_new())
|
150
|
+
r = BN_bin2bn(signature[1], 32, BN_new())
|
151
|
+
s = BN_bin2bn(signature[33], 32, BN_new())
|
152
|
+
|
153
|
+
i = rec_id / 2
|
154
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
155
|
+
|
156
|
+
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
|
157
|
+
|
158
|
+
group = EC_KEY_get0_group(eckey)
|
159
|
+
order = BN_new()
|
160
|
+
EC_GROUP_get_order(group, order, nil)
|
161
|
+
x = BN_dup(order)
|
162
|
+
BN_mul_word(x, i)
|
163
|
+
BN_add(x, x, r)
|
164
|
+
|
165
|
+
field = BN_new()
|
166
|
+
EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
|
167
|
+
|
168
|
+
if BN_cmp(x, field) >= 0
|
169
|
+
[r, s, order, x, field].each { |item| BN_free(item) }
|
170
|
+
EC_KEY_free(eckey)
|
171
|
+
return nil
|
172
|
+
end
|
119
173
|
|
120
|
-
|
121
|
-
|
174
|
+
big_r = EC_POINT_new(group)
|
175
|
+
EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
|
176
|
+
|
177
|
+
big_q = EC_POINT_new(group)
|
178
|
+
n = EC_GROUP_get_degree(group)
|
179
|
+
e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
|
180
|
+
BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
|
181
|
+
|
182
|
+
ctx = BN_CTX_new()
|
183
|
+
zero = BN_new()
|
184
|
+
rr = BN_new()
|
185
|
+
sor = BN_new()
|
186
|
+
eor = BN_new()
|
187
|
+
BN_set_word(zero, 0)
|
188
|
+
BN_mod_sub(e, zero, e, order, ctx)
|
189
|
+
BN_mod_inverse(rr, r, order, ctx)
|
190
|
+
BN_mod_mul(sor, s, rr, order, ctx)
|
191
|
+
BN_mod_mul(eor, e, rr, order, ctx)
|
192
|
+
EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
|
193
|
+
EC_KEY_set_public_key(eckey, big_q)
|
194
|
+
BN_CTX_free(ctx)
|
195
|
+
|
196
|
+
[r, s, order, x, field, e, zero, rr, sor, eor].each { |item| BN_free(item) }
|
197
|
+
[big_r, big_q].each { |item| EC_POINT_free(item) }
|
198
|
+
|
199
|
+
length = i2o_ECPublicKey(eckey, nil)
|
200
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
201
|
+
ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
|
202
|
+
pub_hex = buf.read_string(length).unpack("H*")[0] if i2o_ECPublicKey(eckey, ptr) == length
|
203
|
+
|
204
|
+
EC_KEY_free(eckey)
|
205
|
+
|
206
|
+
pub_hex
|
207
|
+
end
|
122
208
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
209
|
+
# Regenerate a DER-encoded signature such that the S-value complies with the BIP62
|
210
|
+
# specification.
|
211
|
+
#
|
212
|
+
def self.signature_to_low_s(signature)
|
213
|
+
init_ffi_ssl
|
214
|
+
|
215
|
+
buf = FFI::MemoryPointer.new(:uint8, 34)
|
216
|
+
temp = signature.unpack("C*")
|
217
|
+
length_r = temp[3]
|
218
|
+
length_s = temp[5 + length_r]
|
219
|
+
sig = FFI::MemoryPointer.from_string(signature)
|
220
|
+
|
221
|
+
# Calculate the lower s value
|
222
|
+
s = BN_bin2bn(sig[6 + length_r], length_s, BN_new())
|
223
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
224
|
+
group = EC_KEY_get0_group(eckey)
|
225
|
+
order = BN_new()
|
226
|
+
halforder = BN_new()
|
227
|
+
ctx = BN_CTX_new()
|
228
|
+
|
229
|
+
EC_GROUP_get_order(group, order, ctx)
|
230
|
+
BN_rshift1(halforder, order)
|
231
|
+
BN_sub(s, order, s) if BN_cmp(s, halforder) > 0
|
232
|
+
|
233
|
+
BN_free(halforder)
|
234
|
+
BN_free(order)
|
235
|
+
BN_CTX_free(ctx)
|
236
|
+
|
237
|
+
length_s = BN_bn2bin(s, buf)
|
238
|
+
# p buf.read_string(length_s).unpack("H*")
|
239
|
+
|
240
|
+
# Re-encode the signature in DER format
|
241
|
+
sig = [0x30, 0, 0x02, length_r]
|
242
|
+
sig.concat(temp.slice(4, length_r))
|
243
|
+
sig << 0x02
|
244
|
+
sig << length_s
|
245
|
+
sig.concat(buf.read_string(length_s).unpack("C*"))
|
246
|
+
sig[1] = sig.size - 2
|
247
|
+
|
248
|
+
BN_free(s)
|
249
|
+
EC_KEY_free(eckey)
|
250
|
+
|
251
|
+
sig.pack("C*")
|
252
|
+
end
|
127
253
|
|
128
|
-
|
254
|
+
def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
|
255
|
+
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
|
129
256
|
|
130
|
-
|
131
|
-
|
132
|
-
EC_POINT_free(pub_key)
|
133
|
-
BN_free(priv_key)
|
134
|
-
EC_KEY_free(eckey)
|
257
|
+
private_key = [private_key].pack("H*") if private_key.bytesize >= 64
|
258
|
+
private_key_hex = private_key.unpack("H*")[0]
|
135
259
|
|
136
|
-
|
137
|
-
|
260
|
+
public_key_hex ||= regenerate_key(private_key_hex).last
|
261
|
+
pubkey_compressed ||= public_key_hex[0..1] != "04"
|
138
262
|
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
263
|
+
init_ffi_ssl
|
264
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
265
|
+
priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
|
147
266
|
|
148
|
-
|
267
|
+
group = EC_KEY_get0_group(eckey)
|
268
|
+
order = BN_new()
|
269
|
+
ctx = BN_CTX_new()
|
270
|
+
EC_GROUP_get_order(group, order, ctx)
|
149
271
|
|
150
|
-
|
151
|
-
|
272
|
+
pub_key = EC_POINT_new(group)
|
273
|
+
EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
|
274
|
+
EC_KEY_set_private_key(eckey, priv_key)
|
275
|
+
EC_KEY_set_public_key(eckey, pub_key)
|
152
276
|
|
153
|
-
|
154
|
-
return nil if rec_id < 0 or signature.bytesize != 65
|
155
|
-
init_ffi_ssl
|
277
|
+
signature = ECDSA_do_sign(msg32, msg32.size, eckey)
|
156
278
|
|
157
|
-
|
158
|
-
|
159
|
-
|
279
|
+
BN_free(order)
|
280
|
+
BN_CTX_free(ctx)
|
281
|
+
EC_POINT_free(pub_key)
|
282
|
+
BN_free(priv_key)
|
283
|
+
EC_KEY_free(eckey)
|
160
284
|
|
161
|
-
|
162
|
-
|
285
|
+
buf = FFI::MemoryPointer.new(:uint8, 32)
|
286
|
+
head = nil
|
287
|
+
r, s = signature.get_array_of_pointer(0, 2).map do |i|
|
288
|
+
BN_bn2bin(i, buf)
|
289
|
+
buf.read_string(BN_num_bytes(i)).rjust(32, "\x00")
|
290
|
+
end
|
163
291
|
|
164
|
-
|
292
|
+
rec_id = nil
|
293
|
+
if signature.get_array_of_pointer(0, 2).all? { |i| BN_num_bits(i) <= 256 }
|
294
|
+
4.times do |i|
|
295
|
+
head = [27 + i + (pubkey_compressed ? 4 : 0)].pack("C")
|
296
|
+
recovered_key = recover_public_key_from_signature(
|
297
|
+
msg32.read_string(32), [head, r, s].join, i, pubkey_compressed
|
298
|
+
)
|
299
|
+
if public_key_hex == recovered_key
|
300
|
+
rec_id = i
|
301
|
+
break
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
165
305
|
|
166
|
-
|
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)
|
306
|
+
ECDSA_SIG_free(signature)
|
172
307
|
|
173
|
-
|
174
|
-
|
308
|
+
[head, [r, s]].join if rec_id
|
309
|
+
end
|
175
310
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
311
|
+
def self.recover_compact(hash, signature)
|
312
|
+
return false if signature.bytesize != 65
|
313
|
+
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
|
314
|
+
|
315
|
+
version = signature.unpack("C")[0]
|
181
316
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
317
|
+
# Version of signature should be 27 or 28, but 0 and 1 are also possible versions
|
318
|
+
# which can show up in Ledger hardwallet signings
|
319
|
+
if version < 27
|
320
|
+
version += 27
|
205
321
|
end
|
206
322
|
|
207
|
-
|
208
|
-
return false if signature.bytesize != 65
|
323
|
+
return false if version < 27 || version > 34
|
209
324
|
|
210
|
-
|
325
|
+
compressed = version >= 31
|
326
|
+
version -= 4 if compressed
|
211
327
|
|
212
|
-
|
213
|
-
|
214
|
-
if version < 27
|
215
|
-
version += 27
|
216
|
-
end
|
328
|
+
recover_public_key_from_signature(msg32.read_string(32), signature, version - 27, compressed)
|
329
|
+
end
|
217
330
|
|
218
|
-
|
331
|
+
# lifted from https://github.com/GemHQ/money-tree
|
332
|
+
def self.ec_add(point0, point1)
|
333
|
+
init_ffi_ssl
|
219
334
|
|
220
|
-
|
335
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
336
|
+
group = EC_KEY_get0_group(eckey)
|
221
337
|
|
222
|
-
|
223
|
-
|
338
|
+
point_0_hex = point0.to_bn.to_s(16)
|
339
|
+
point_0_pt = EC_POINT_hex2point(group, point_0_hex, nil, nil)
|
340
|
+
point_1_hex = point1.to_bn.to_s(16)
|
341
|
+
point_1_pt = EC_POINT_hex2point(group, point_1_hex, nil, nil)
|
224
342
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
else
|
233
|
-
SSL_library_init()
|
234
|
-
ERR_load_crypto_strings()
|
235
|
-
SSL_load_error_strings()
|
236
|
-
end
|
343
|
+
sum_point = EC_POINT_new(group)
|
344
|
+
EC_POINT_add(group, sum_point, point_0_pt, point_1_pt, nil)
|
345
|
+
hex = EC_POINT_point2hex(group, sum_point, POINT_CONVERSION_UNCOMPRESSED, nil)
|
346
|
+
EC_KEY_free(eckey)
|
347
|
+
EC_POINT_free(sum_point)
|
348
|
+
hex
|
349
|
+
end
|
237
350
|
|
238
|
-
|
239
|
-
|
240
|
-
|
351
|
+
# repack signature for OpenSSL 1.0.1k handling of DER signatures
|
352
|
+
# https://github.com/bitcoin/bitcoin/pull/5634/files
|
353
|
+
def self.repack_der_signature(signature)
|
354
|
+
init_ffi_ssl
|
241
355
|
|
356
|
+
return false if signature.empty?
|
242
357
|
|
243
|
-
|
358
|
+
# New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
|
359
|
+
norm_der = FFI::MemoryPointer.new(:pointer)
|
360
|
+
sig_ptr = FFI::MemoryPointer.new(:pointer).put_pointer(
|
361
|
+
0, FFI::MemoryPointer.from_string(signature)
|
362
|
+
)
|
244
363
|
|
245
|
-
|
246
|
-
list.each{|j| BN_free(j) }
|
247
|
-
end
|
364
|
+
norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
|
248
365
|
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
366
|
+
derlen = i2d_ECDSA_SIG(norm_sig, norm_der)
|
367
|
+
ECDSA_SIG_free(norm_sig)
|
368
|
+
return false if derlen <= 0
|
256
369
|
|
257
|
-
|
370
|
+
ret = norm_der.read_pointer.read_string(derlen)
|
371
|
+
OPENSSL_free(norm_der.read_pointer)
|
258
372
|
|
259
|
-
|
260
|
-
end
|
373
|
+
ret
|
261
374
|
end
|
262
375
|
|
376
|
+
def self.init_ffi_ssl
|
377
|
+
@ssl_loaded ||= false
|
378
|
+
return if @ssl_loaded
|
379
|
+
|
380
|
+
if self.method_defined?(:OPENSSL_init_ssl)
|
381
|
+
OPENSSL_init_ssl(
|
382
|
+
OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_ENGINE_ALL_BUILTIN,
|
383
|
+
nil
|
384
|
+
)
|
385
|
+
else
|
386
|
+
SSL_library_init()
|
387
|
+
ERR_load_crypto_strings()
|
388
|
+
SSL_load_error_strings()
|
389
|
+
end
|
390
|
+
|
391
|
+
RAND_poll()
|
392
|
+
@ssl_loaded = true
|
393
|
+
end
|
263
394
|
end
|
264
395
|
end
|
data/lib/eth/secp256k1.rb
CHANGED
data/lib/eth/sedes.rb
CHANGED
data/lib/eth/tx.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Eth
|
2
2
|
class Tx
|
3
|
-
|
4
3
|
include RLP::Sedes::Serializable
|
5
4
|
extend Sedes
|
6
5
|
|
@@ -13,7 +12,7 @@ module Eth
|
|
13
12
|
data_bin: binary,
|
14
13
|
v: big_endian_int,
|
15
14
|
r: big_endian_int,
|
16
|
-
s: big_endian_int
|
15
|
+
s: big_endian_int,
|
17
16
|
})
|
18
17
|
|
19
18
|
attr_writer :signature
|
@@ -28,7 +27,7 @@ module Eth
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def initialize(params)
|
31
|
-
fields = {v: 0, r: 0, s: 0}.merge params
|
30
|
+
fields = { v: 0, r: 0, s: 0 }.merge params
|
32
31
|
fields[:to] = Utils.normalize_address(fields[:to])
|
33
32
|
|
34
33
|
self.chain_id = (params[:chain_id]) ? params.delete(:chain_id) : Eth.chain_id
|
@@ -105,6 +104,7 @@ module Eth
|
|
105
104
|
def hash
|
106
105
|
"0x#{Utils.bin_to_hex Utils.keccak256_rlp(self)}"
|
107
106
|
end
|
107
|
+
|
108
108
|
alias_method :id, :hash
|
109
109
|
|
110
110
|
def data_hex
|
@@ -120,7 +120,7 @@ module Eth
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def data=(string)
|
123
|
-
Eth.tx_data_hex? ? self.data_hex=(string) : self.data_bin=(string)
|
123
|
+
Eth.tx_data_hex? ? self.data_hex = (string) : self.data_bin = (string)
|
124
124
|
end
|
125
125
|
|
126
126
|
def chain_id
|
@@ -190,7 +190,6 @@ module Eth
|
|
190
190
|
UnsignedTx
|
191
191
|
end
|
192
192
|
end
|
193
|
-
|
194
193
|
end
|
195
194
|
|
196
195
|
UnsignedTx = Tx.exclude([:v, :r, :s])
|
data/lib/eth/utils.rb
CHANGED
@@ -3,11 +3,11 @@ module Eth
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def normalize_address(address)
|
6
|
-
if address.nil? || address ==
|
7
|
-
|
6
|
+
if address.nil? || address == ""
|
7
|
+
""
|
8
8
|
elsif address.size == 40
|
9
9
|
hex_to_bin address
|
10
|
-
elsif address.size == 42 && address[0..1] ==
|
10
|
+
elsif address.size == 42 && address[0..1] == "0x"
|
11
11
|
hex_to_bin address[2..-1]
|
12
12
|
else
|
13
13
|
address
|
@@ -23,7 +23,7 @@ module Eth
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def base256_to_int(str)
|
26
|
-
RLP::Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/,
|
26
|
+
RLP::Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, "")
|
27
27
|
end
|
28
28
|
|
29
29
|
def int_to_base256(int)
|
@@ -43,7 +43,7 @@ module Eth
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def remove_hex_prefix(s)
|
46
|
-
s[0,2] ==
|
46
|
+
s[0, 2] == "0x" ? s[2..-1] : s
|
47
47
|
end
|
48
48
|
|
49
49
|
def bin_to_prefixed_hex(binary)
|
@@ -89,14 +89,14 @@ module Eth
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def zunpad(x)
|
92
|
-
x.sub(/\A\x00+/,
|
92
|
+
x.sub(/\A\x00+/, "")
|
93
93
|
end
|
94
94
|
|
95
|
-
def zpad_int(n, l=32)
|
95
|
+
def zpad_int(n, l = 32)
|
96
96
|
zpad encode_int(n), l
|
97
97
|
end
|
98
98
|
|
99
|
-
def zpad_hex(s, l=32)
|
99
|
+
def zpad_hex(s, l = 32)
|
100
100
|
zpad decode_hex(s), l
|
101
101
|
end
|
102
102
|
|
@@ -108,8 +108,6 @@ module Eth
|
|
108
108
|
Address.new(address).checksummed
|
109
109
|
end
|
110
110
|
|
111
|
-
|
112
|
-
|
113
111
|
private
|
114
112
|
|
115
113
|
def lpad(x, symbol, l)
|
@@ -124,6 +122,5 @@ module Eth
|
|
124
122
|
|
125
123
|
int_to_base256 n
|
126
124
|
end
|
127
|
-
|
128
125
|
end
|
129
126
|
end
|
data/lib/eth/version.rb
CHANGED
data/lib/eth.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "digest/keccak"
|
2
|
+
require "ffi"
|
3
|
+
require "money-tree"
|
4
|
+
require "rlp"
|
5
5
|
|
6
6
|
module Eth
|
7
7
|
BYTE_ZERO = "\x00".freeze
|
8
|
-
UINT_MAX = 2**256 - 1
|
8
|
+
UINT_MAX = 2 ** 256 - 1
|
9
9
|
|
10
|
-
autoload :Address,
|
11
|
-
autoload :Gas,
|
12
|
-
autoload :Key,
|
13
|
-
autoload :OpenSsl,
|
14
|
-
autoload :Secp256k1,
|
15
|
-
autoload :Sedes,
|
16
|
-
autoload :Tx,
|
17
|
-
autoload :Utils,
|
10
|
+
autoload :Address, "eth/address"
|
11
|
+
autoload :Gas, "eth/gas"
|
12
|
+
autoload :Key, "eth/key"
|
13
|
+
autoload :OpenSsl, "eth/open_ssl"
|
14
|
+
autoload :Secp256k1, "eth/secp256k1"
|
15
|
+
autoload :Sedes, "eth/sedes"
|
16
|
+
autoload :Tx, "eth/tx"
|
17
|
+
autoload :Utils, "eth/utils"
|
18
18
|
|
19
19
|
class << self
|
20
20
|
def configure
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Ellis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: keccak
|
@@ -45,14 +45,28 @@ dependencies:
|
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0.
|
48
|
+
version: '0.11'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0.
|
55
|
+
version: '0.11'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: openssl
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: rlp
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -147,6 +161,7 @@ extra_rdoc_files: []
|
|
147
161
|
files:
|
148
162
|
- ".github/workflows/build.yml"
|
149
163
|
- ".github/workflows/codeql.yml"
|
164
|
+
- ".github/workflows/docs.yml"
|
150
165
|
- ".gitignore"
|
151
166
|
- ".gitmodules"
|
152
167
|
- ".rspec"
|
@@ -186,7 +201,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
201
|
requirements:
|
187
202
|
- - ">="
|
188
203
|
- !ruby/object:Gem::Version
|
189
|
-
version: '2.
|
204
|
+
version: '2.6'
|
190
205
|
- - "<"
|
191
206
|
- !ruby/object:Gem::Version
|
192
207
|
version: '4.0'
|
@@ -196,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
211
|
- !ruby/object:Gem::Version
|
197
212
|
version: '0'
|
198
213
|
requirements: []
|
199
|
-
rubygems_version: 3.2.
|
214
|
+
rubygems_version: 3.2.32
|
200
215
|
signing_key:
|
201
216
|
specification_version: 4
|
202
217
|
summary: Simple API to sign Ethereum transactions.
|