digix-eth 0.5.2

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.
@@ -0,0 +1,26 @@
1
+ module Eth
2
+ class RpcSigner
3
+
4
+ attr_accessor :key
5
+
6
+ def initialize(key)
7
+ @key = key
8
+ end
9
+
10
+ def sign_message(message)
11
+ payload = Eth::Signature.new(message)
12
+ payload.prefixed_message = Eth::Utils.prefix_message(message)
13
+ payload.hash = Eth::Utils.keccak256(payload.prefixed_message)
14
+ payload.hash_hex = Eth::Utils.bin_to_prefixed_hex(payload.hash)
15
+ payload.signature = @key.sign(payload.prefixed_message)
16
+ payload.signature_hex = Eth::Utils.bin_to_prefixed_hex(payload.signature)
17
+ payload.v, payload.r, payload.s = Eth::Utils.v_r_s_for(payload.signature)
18
+ payload.rpc = Eth::Utils.zpad_int(payload.r, 32) + Eth::Utils.zpad_int(payload.s, 32) + [(payload.v - 27)].pack('C')
19
+ payload.rpc_hex = Eth::Utils.bin_to_prefixed_hex(payload.rpc)
20
+ return payload
21
+ end
22
+
23
+ end
24
+
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ module Eth
2
+ class Secp256k1
3
+
4
+ N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
5
+
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ module Eth
2
+ class Signature
3
+
4
+ attr_accessor :message, :signer, :prefixed_message, :hash, :hash_hex, :signature, :signature_hex, :rpc, :rpc_hex, :v, :r, :s
5
+
6
+ def initialize(message)
7
+ @message = message
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,154 @@
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
+ deserialize(RLP.decode data)
24
+ end
25
+
26
+ def initialize(params)
27
+ fields = {v: 0, r: 0, s: 0}.merge params
28
+ fields[:to] = Utils.normalize_address(fields[:to])
29
+
30
+ if params[:data]
31
+ self.data = params.delete(:data)
32
+ fields[:data_bin] = data_bin
33
+ end
34
+ serializable_initialize fields
35
+
36
+ check_transaction_validity
37
+ end
38
+
39
+ def unsigned_encoded
40
+ RLP.encode(unsigned, sedes: sedes)
41
+ end
42
+
43
+ def signing_data
44
+ Utils.bin_to_prefixed_hex unsigned_encoded
45
+ end
46
+
47
+ def encoded
48
+ RLP.encode self
49
+ end
50
+
51
+ def hex
52
+ Utils.bin_to_prefixed_hex encoded
53
+ end
54
+
55
+ def sign(key)
56
+ self.signature = key.sign(unsigned_encoded)
57
+ vrs = Utils.v_r_s_for signature
58
+ self.v = vrs[0]
59
+ self.r = vrs[1]
60
+ self.s = vrs[2]
61
+
62
+ self
63
+ end
64
+
65
+ def to_h
66
+ hash_keys.inject({}) do |hash, field|
67
+ hash[field] = send field
68
+ hash
69
+ end
70
+ end
71
+
72
+ def from
73
+ if signature
74
+ public_key = OpenSsl.recover_compact(signature_hash, signature)
75
+ Utils.public_key_to_address(public_key) if public_key
76
+ end
77
+ end
78
+
79
+ def signature
80
+ return @signature if @signature
81
+ self.signature = [
82
+ Utils.int_to_base256(v),
83
+ Utils.zpad_int(r),
84
+ Utils.zpad_int(s),
85
+ ].join if [v, r, s].all?
86
+ end
87
+
88
+ def hash
89
+ "0x#{Utils.bin_to_hex Utils.keccak256_rlp(self)}"
90
+ end
91
+ alias_method :id, :hash
92
+
93
+ def data_hex
94
+ Utils.bin_to_prefixed_hex data_bin
95
+ end
96
+
97
+ def data_hex=(hex)
98
+ self.data_bin = Utils.hex_to_bin(hex)
99
+ end
100
+
101
+ def data
102
+ Eth.tx_data_hex? ? data_hex : data_bin
103
+ end
104
+
105
+ def data=(string)
106
+ Eth.tx_data_hex? ? self.data_hex=(string) : self.data_bin=(string)
107
+ end
108
+
109
+
110
+ private
111
+
112
+ def hash_keys
113
+ keys = self.class.serializable_fields.keys
114
+ keys.delete(:data_bin)
115
+ keys + [:data]
116
+ end
117
+
118
+ def check_transaction_validity
119
+ if [gas_price, gas_limit, value, nonce].max > UINT_MAX
120
+ raise InvalidTransaction, "Values way too high!"
121
+ elsif gas_limit < intrinsic_gas_used
122
+ raise InvalidTransaction, "Gas limit too low"
123
+ end
124
+ end
125
+
126
+ def intrinsic_gas_used
127
+ num_zero_bytes = data_bin.count(BYTE_ZERO)
128
+ num_non_zero_bytes = data_bin.size - num_zero_bytes
129
+
130
+ Gas::GTXCOST +
131
+ Gas::GTXDATAZERO * num_zero_bytes +
132
+ Gas::GTXDATANONZERO * num_non_zero_bytes
133
+ end
134
+
135
+ def signature_hash
136
+ Utils.keccak256 unsigned_encoded
137
+ end
138
+
139
+ def unsigned
140
+ Tx.new to_h.merge(v: Eth.chain_id, r: 0, s: 0)
141
+ end
142
+
143
+ def sedes
144
+ if Eth.prevent_replays? && !(Eth.replayable_v? v)
145
+ self.class
146
+ else
147
+ UnsignedTx
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ UnsignedTx = Tx.exclude([:v, :r, :s])
154
+ end
@@ -0,0 +1,147 @@
1
+ module Eth
2
+ module Utils
3
+
4
+ extend self
5
+
6
+ def prefix_message(message)
7
+ prefix = "\u0019Ethereum Signed Message:\n#{message.length}"
8
+ return "#{prefix}#{message}"
9
+ end
10
+
11
+
12
+ def normalize_address(address)
13
+ if address.nil? || address == ''
14
+ ''
15
+ elsif address.size == 40
16
+ hex_to_bin address
17
+ elsif address.size == 42 && address[0..1] == '0x'
18
+ hex_to_bin address[2..-1]
19
+ else
20
+ address
21
+ end
22
+ end
23
+
24
+ def bin_to_hex(string)
25
+ RLP::Utils.encode_hex string
26
+ end
27
+
28
+ def hex_to_bin(string)
29
+ RLP::Utils.decode_hex remove_hex_prefix(string)
30
+ end
31
+
32
+ def base256_to_int(str)
33
+ RLP::Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, '')
34
+ end
35
+
36
+ def int_to_base256(int)
37
+ RLP::Sedes.big_endian_int.serialize int
38
+ end
39
+
40
+ def from_rpc_signature_hex(signature)
41
+ buffer = Eth::Utils.hex_to_bin(signature).bytes
42
+ if buffer.length != 65
43
+ raise ArgumentError.new('Invalid signature length')
44
+ end
45
+ v = buffer[64]
46
+ v += 27 if v < 27
47
+ r = buffer[0..32]
48
+ s = buffer[33..64]
49
+ signature = [v, r, s].flatten.pack('C*')
50
+ signature_hex = Eth::Util.bin_to_prefixed_hex(signature)
51
+ payload = {v: v, r: r, s: s, signature: signature, signature_hex: signature_hex}
52
+ return payload
53
+ end
54
+
55
+ def v_r_s_for(signature)
56
+ [
57
+ signature[0].bytes[0],
58
+ Utils.base256_to_int(signature[1..32]),
59
+ Utils.base256_to_int(signature[33..65]),
60
+ ]
61
+ end
62
+
63
+ def prefix_hex(hex)
64
+ hex.match(/\A0x/) ? hex : "0x#{hex}"
65
+ end
66
+
67
+ def remove_hex_prefix(s)
68
+ s[0,2] == '0x' ? s[2..-1] : s
69
+ end
70
+
71
+ def bin_to_prefixed_hex(binary)
72
+ prefix_hex bin_to_hex(binary)
73
+ end
74
+
75
+ def public_key_to_address(hex)
76
+ bytes = hex_to_bin(hex)
77
+ address_bytes = Utils.keccak256(bytes[1..-1])[-20..-1]
78
+ format_address bin_to_prefixed_hex(address_bytes)
79
+ end
80
+
81
+ def sha256(x)
82
+ Digest::SHA256.digest x
83
+ end
84
+
85
+ def keccak256(x)
86
+ Digest::SHA3.new(256).digest(x)
87
+ end
88
+
89
+ def keccak512(x)
90
+ Digest::SHA3.new(512).digest(x)
91
+ end
92
+
93
+ def keccak256_rlp(x)
94
+ keccak256 RLP.encode(x)
95
+ end
96
+
97
+ def ripemd160(x)
98
+ Digest::RMD160.digest x
99
+ end
100
+
101
+ def hash160(x)
102
+ ripemd160 sha256(x)
103
+ end
104
+
105
+ def zpad(x, l)
106
+ lpad x, BYTE_ZERO, l
107
+ end
108
+
109
+ def zunpad(x)
110
+ x.sub(/\A\x00+/, '')
111
+ end
112
+
113
+ def zpad_int(n, l=32)
114
+ zpad encode_int(n), l
115
+ end
116
+
117
+ def zpad_hex(s, l=32)
118
+ zpad decode_hex(s), l
119
+ end
120
+
121
+ def valid_address?(address)
122
+ Address.new(address).valid?
123
+ end
124
+
125
+ def format_address(address)
126
+ Address.new(address).checksummed
127
+ end
128
+
129
+
130
+
131
+ private
132
+
133
+ def lpad(x, symbol, l)
134
+ return x if x.size >= l
135
+ symbol * (l - x.size) + x
136
+ end
137
+
138
+ def encode_int(n)
139
+ unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
140
+ raise ArgumentError, "Integer invalid or out of range: #{n}"
141
+ end
142
+
143
+ int_to_base256 n
144
+ end
145
+
146
+ end
147
+ end
@@ -0,0 +1,45 @@
1
+ module Eth
2
+ class Vault
3
+
4
+ attr_accessor :hd_path_string, :master, :mnemonic
5
+
6
+ class << self
7
+
8
+ def pad_mnemonic(mnemonic)
9
+ mnemonic.rjust(180, ' ')
10
+ end
11
+
12
+ def unpad_mnemonic(mnemonic)
13
+ mnemonic.strip!
14
+ end
15
+ end
16
+
17
+ def initialize(opts = {secret_seed_phrase: nil}, hd_path_string = "m/0'/0'/0'")
18
+ if opts[:secret_seed_phrase]
19
+ secret_seed_phrase = self.class.pad_mnemonic(opts[:secret_seed_phrase])
20
+ seed_hex = Bitcoin::Trezor::Mnemonic.to_seed(secret_seed_phrase)
21
+ else
22
+ secret_seed_phrase = self.class.pad_mnemonic(Bitcoin::Trezor::Mnemonic.to_mnemonic(RbNaCl::Random.random_bytes(32)))
23
+ seed_hex = Bitcoin::Trezor::Mnemonic.to_seed(secret_seed_phrase)
24
+ end
25
+ @mnemonic = self.class.unpad_mnemonic(secret_seed_phrase)
26
+ @hd_path_string = hd_path_string
27
+ @master = MoneyTree::Master.new(seed_hex: seed_hex)
28
+ end
29
+
30
+ def get_node(index = 0)
31
+ if index == 0
32
+ hd_path = @hd_path_string
33
+ else
34
+ hd_path = "#{@hd_path_string}/#{index}'"
35
+ end
36
+ return @master.node_for_path(hd_path)
37
+ end
38
+
39
+ def get_key(index = 0)
40
+ node = get_node(index)
41
+ return Eth::Key.from_node(node)
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module Eth
2
+ VERSION = "0.5.2"
3
+ end
metadata ADDED
@@ -0,0 +1,229 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: digix-eth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.2
5
+ platform: ruby
6
+ authors:
7
+ - Steve Ellis
8
+ - DigixGlobal
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-07-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: digest-sha3
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: ffi
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: money-tree
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.9'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.9'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rlp
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.7.3
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.7.3
70
+ - !ruby/object:Gem::Dependency
71
+ name: activesupport
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.1'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.1'
84
+ - !ruby/object:Gem::Dependency
85
+ name: bitcoin-ruby
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '='
89
+ - !ruby/object:Gem::Version
90
+ version: 0.0.11
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '='
96
+ - !ruby/object:Gem::Version
97
+ version: 0.0.11
98
+ - !ruby/object:Gem::Dependency
99
+ name: rbnacl
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '5.0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '5.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: bundler
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1.12'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '1.12'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '0.1'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.1'
140
+ - !ruby/object:Gem::Dependency
141
+ name: rake
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '10.0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '10.0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: rspec
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '3.0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '3.0'
168
+ description: 'Forked from: github.com/se3000/ruby-eth. Library to build, parse, and
169
+ sign Ethereum transactions. This forked gem adds additional support for Trezor style
170
+ mnemonic and RPC signing'
171
+ email:
172
+ - email@steveell.is
173
+ - support@digix.io
174
+ executables: []
175
+ extensions: []
176
+ extra_rdoc_files: []
177
+ files:
178
+ - ".gitignore"
179
+ - ".gitmodules"
180
+ - ".rspec"
181
+ - ".travis.yml"
182
+ - CHANGELOG.md
183
+ - Gemfile
184
+ - LICENSE.txt
185
+ - README.md
186
+ - Rakefile
187
+ - bin/console
188
+ - bin/setup
189
+ - eth.gemspec
190
+ - lib/eth.rb
191
+ - lib/eth/address.rb
192
+ - lib/eth/gas.rb
193
+ - lib/eth/key.rb
194
+ - lib/eth/key/decrypter.rb
195
+ - lib/eth/key/encrypter.rb
196
+ - lib/eth/open_ssl.rb
197
+ - lib/eth/rpc_signer.rb
198
+ - lib/eth/secp256k1.rb
199
+ - lib/eth/sedes.rb
200
+ - lib/eth/signature.rb
201
+ - lib/eth/tx.rb
202
+ - lib/eth/utils.rb
203
+ - lib/eth/vault.rb
204
+ - lib/eth/version.rb
205
+ homepage: https://github.com/digixglobal/ruby-eth
206
+ licenses:
207
+ - MIT
208
+ metadata: {}
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ requirements: []
224
+ rubyforge_project:
225
+ rubygems_version: 2.6.12
226
+ signing_key:
227
+ specification_version: 4
228
+ summary: Simple API to sign Ethereum transactions.
229
+ test_files: []