eth 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/CHANGELOG.md +21 -0
- data/README.md +20 -8
- data/lib/eth.rb +21 -11
- data/lib/eth/key.rb +2 -5
- data/lib/eth/open_ssl.rb +1 -1
- data/lib/eth/tx.rb +49 -21
- data/lib/eth/utils.rb +23 -2
- data/lib/eth/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3634eb14f8b7215034c3d182aaa6f1617062e723
|
4
|
+
data.tar.gz: abf2f132aa8eb36db20498fa762543ec5d243d81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfc6c93a18eb92381052080ecdfffeaf017cb119954642dba89d8dcf3fe8ee0f505f652c979c4a9b2067375acb4625a29b130b517f5dfaff0e76a8ea459dc4ab
|
7
|
+
data.tar.gz: 36b54f235dc2256dbd705c3cd7ea235095f9323f09b32f36903712363d687b21de4d553a74b4b92071898c1daf81a1459cd60d0c2675e403dbe03b034e381237
|
data/.gitmodules
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
|
+
|
7
|
+
## [0.4.0]
|
8
|
+
|
9
|
+
### Added
|
10
|
+
- Tx#data_bin returns the data field of a transaction in binary.
|
11
|
+
- Tx#data_hex returns the data field of a transaction as a hexadecimal string.
|
12
|
+
- Tx#id is an alias of Tx#hash
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- Tx#data is configurable to return either hex or binary: `config.tx_data_hex = true`.
|
16
|
+
- Tx#hash includes the '0x' hex prefix.
|
17
|
+
- Tx#hex includes the '0x' hex prefix.
|
18
|
+
- Key#address getter is prepended by '0x'.
|
19
|
+
- Extract public key to address method into Utils.public_key_to_address.
|
20
|
+
- Tx#from returns an address instead of a public key.
|
21
|
+
- Chain ID is updated to the later version of the spec.
|
data/README.md
CHANGED
@@ -35,11 +35,11 @@ old_key = Eth::Key.new priv: private_key
|
|
35
35
|
Build a transaction from scratch:
|
36
36
|
```ruby
|
37
37
|
tx = Eth::Tx.new({
|
38
|
-
data:
|
39
|
-
gas_limit:
|
40
|
-
gas_price:
|
41
|
-
nonce:
|
42
|
-
to:
|
38
|
+
data: hex_data,
|
39
|
+
gas_limit: 21_000,
|
40
|
+
gas_price: 3_141_592,
|
41
|
+
nonce: 1,
|
42
|
+
to: key2.address,
|
43
43
|
value: 1_000_000_000_000,
|
44
44
|
})
|
45
45
|
```
|
@@ -50,9 +50,9 @@ tx = Eth::Tx.decode hex
|
|
50
50
|
|
51
51
|
### Configure
|
52
52
|
In order to prevent replay attacks, you must specify which Ethereum chain your transactions are created for. See [EIP 155](https://github.com/ethereum/EIPs/issues/155) for more detail.
|
53
|
-
```
|
53
|
+
```ruby
|
54
54
|
Eth.configure do |config|
|
55
|
-
config.chain_id =
|
55
|
+
config.chain_id = 1 # nil by default, meaning valid on any chain
|
56
56
|
end
|
57
57
|
```
|
58
58
|
|
@@ -65,7 +65,19 @@ Get the raw transaction with `tx.hex`, and broadcast it through any Ethereum nod
|
|
65
65
|
|
66
66
|
## Contributing
|
67
67
|
|
68
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/se3000/ethereum-tx.
|
68
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/se3000/ethereum-tx. Tests are encouraged.
|
69
|
+
|
70
|
+
### Tests
|
71
|
+
|
72
|
+
First install the [Ethereum common tests](https://github.com/ethereum/tests):
|
73
|
+
```shell
|
74
|
+
git submodule update --init
|
75
|
+
```
|
76
|
+
|
77
|
+
Then run the associated tests:
|
78
|
+
```shell
|
79
|
+
rspec
|
80
|
+
```
|
69
81
|
|
70
82
|
|
71
83
|
## License
|
data/lib/eth.rb
CHANGED
@@ -5,8 +5,6 @@ require 'money-tree'
|
|
5
5
|
require 'rlp'
|
6
6
|
|
7
7
|
module Eth
|
8
|
-
REPLAYABLE_CHAIN_ID = 13
|
9
|
-
|
10
8
|
autoload :Key, 'eth/key'
|
11
9
|
autoload :OpenSsl, 'eth/open_ssl'
|
12
10
|
autoload :Sedes, 'eth/sedes'
|
@@ -18,24 +16,32 @@ module Eth
|
|
18
16
|
yield(configuration)
|
19
17
|
end
|
20
18
|
|
21
|
-
def
|
22
|
-
|
19
|
+
def replayable_chain_id
|
20
|
+
27
|
23
21
|
end
|
24
22
|
|
25
|
-
def
|
26
|
-
|
23
|
+
def chain_id
|
24
|
+
configuration.chain_id
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def v_base
|
28
|
+
if chain_id
|
29
|
+
(chain_id * 2) + 35
|
30
|
+
else
|
31
|
+
replayable_chain_id
|
32
|
+
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def prevent_replays?
|
34
|
-
chain_id
|
36
|
+
!chain_id.nil?
|
35
37
|
end
|
36
38
|
|
37
39
|
def replayable_v?(v)
|
38
|
-
[
|
40
|
+
[replayable_chain_id, replayable_chain_id + 1].include? v
|
41
|
+
end
|
42
|
+
|
43
|
+
def tx_data_hex?
|
44
|
+
!!configuration.tx_data_hex
|
39
45
|
end
|
40
46
|
|
41
47
|
|
@@ -47,6 +53,10 @@ module Eth
|
|
47
53
|
end
|
48
54
|
|
49
55
|
class Configuration
|
50
|
-
attr_accessor :chain_id
|
56
|
+
attr_accessor :chain_id, :tx_data_hex
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
self.tx_data_hex = true
|
60
|
+
end
|
51
61
|
end
|
52
62
|
end
|
data/lib/eth/key.rb
CHANGED
@@ -21,12 +21,9 @@ module Eth
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def address
|
24
|
-
Utils.
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_address
|
28
|
-
address
|
24
|
+
Utils.public_key_to_address public_hex
|
29
25
|
end
|
26
|
+
alias_method :to_address, :address
|
30
27
|
|
31
28
|
def sign(message)
|
32
29
|
sign_hash message_hash(message)
|
data/lib/eth/open_ssl.rb
CHANGED
@@ -163,7 +163,7 @@ module Eth
|
|
163
163
|
return false if signature.bytesize != 65
|
164
164
|
|
165
165
|
version = signature.unpack('C')[0]
|
166
|
-
v_base = Eth.replayable_v?(version) ? Eth.
|
166
|
+
v_base = Eth.replayable_v?(version) ? Eth.replayable_chain_id : Eth.v_base
|
167
167
|
return false if version < v_base
|
168
168
|
|
169
169
|
compressed = false
|
data/lib/eth/tx.rb
CHANGED
@@ -10,7 +10,7 @@ module Eth
|
|
10
10
|
gas_limit: big_endian_int,
|
11
11
|
to: address,
|
12
12
|
value: big_endian_int,
|
13
|
-
|
13
|
+
data_bin: binary,
|
14
14
|
v: big_endian_int,
|
15
15
|
r: big_endian_int,
|
16
16
|
s: big_endian_int
|
@@ -19,14 +19,18 @@ module Eth
|
|
19
19
|
attr_writer :signature
|
20
20
|
|
21
21
|
def self.decode(data)
|
22
|
-
data = Utils.hex_to_bin(data) if data.match(/\A
|
22
|
+
data = Utils.hex_to_bin(data) if data.match(/\A(?:0x)?\h+\Z/)
|
23
23
|
deserialize(RLP.decode data)
|
24
24
|
end
|
25
25
|
|
26
|
-
def initialize(
|
27
|
-
fields = {v: 0, r: 0, s: 0}.merge
|
26
|
+
def initialize(params)
|
27
|
+
fields = {v: 0, r: 0, s: 0}.merge params
|
28
28
|
fields[:to] = Utils.normalize_address(fields[:to])
|
29
29
|
|
30
|
+
if params[:data]
|
31
|
+
self.data = params.delete(:data)
|
32
|
+
fields[:data_bin] = data_bin
|
33
|
+
end
|
30
34
|
serializable_initialize fields
|
31
35
|
|
32
36
|
check_transaction_validity
|
@@ -37,7 +41,7 @@ module Eth
|
|
37
41
|
end
|
38
42
|
|
39
43
|
def signing_data
|
40
|
-
Utils.
|
44
|
+
Utils.bin_to_prefixed_hex unsigned_encoded
|
41
45
|
end
|
42
46
|
|
43
47
|
def encoded
|
@@ -45,42 +49,72 @@ module Eth
|
|
45
49
|
end
|
46
50
|
|
47
51
|
def hex
|
48
|
-
Utils.
|
52
|
+
Utils.bin_to_prefixed_hex encoded
|
49
53
|
end
|
50
54
|
|
51
55
|
def sign(key)
|
52
56
|
self.signature = key.sign(unsigned_encoded)
|
53
|
-
|
57
|
+
vrs = Utils.v_r_s_for signature
|
58
|
+
self.v = vrs[0]
|
59
|
+
self.r = vrs[1]
|
60
|
+
self.s = vrs[2]
|
54
61
|
|
55
62
|
self
|
56
63
|
end
|
57
64
|
|
58
65
|
def to_h
|
59
|
-
|
66
|
+
hash_keys.inject({}) do |hash, field|
|
60
67
|
hash[field] = send field
|
61
68
|
hash
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
65
72
|
def from
|
66
|
-
|
67
|
-
|
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
|
68
77
|
end
|
69
78
|
|
70
79
|
def signature
|
71
80
|
return @signature if @signature
|
72
|
-
self.signature = [
|
73
|
-
Utils.int_to_base256
|
74
|
-
|
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?
|
75
86
|
end
|
76
87
|
|
77
88
|
def hash
|
78
89
|
Utils.bin_to_hex Utils.keccak256_rlp(self)
|
79
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
|
80
108
|
|
81
109
|
|
82
110
|
private
|
83
111
|
|
112
|
+
def hash_keys
|
113
|
+
keys = self.class.serializable_fields.keys
|
114
|
+
keys.delete(:data_bin)
|
115
|
+
keys + [:data]
|
116
|
+
end
|
117
|
+
|
84
118
|
def check_transaction_validity
|
85
119
|
if [gas_price, gas_limit, value, nonce].max > Ethereum::Base::UINT_MAX
|
86
120
|
raise Ethereum::Base::InvalidTransaction, "Values way too high!"
|
@@ -89,15 +123,9 @@ module Eth
|
|
89
123
|
end
|
90
124
|
end
|
91
125
|
|
92
|
-
def vrs=(vrs)
|
93
|
-
self.v = vrs[0]
|
94
|
-
self.r = vrs[1]
|
95
|
-
self.s = vrs[2]
|
96
|
-
end
|
97
|
-
|
98
126
|
def intrinsic_gas_used
|
99
|
-
num_zero_bytes =
|
100
|
-
num_non_zero_bytes =
|
127
|
+
num_zero_bytes = data_bin.count(Ethereum::Base::BYTE_ZERO)
|
128
|
+
num_non_zero_bytes = data_bin.size - num_zero_bytes
|
101
129
|
|
102
130
|
Ethereum::Base::GTXCOST +
|
103
131
|
Ethereum::Base::GTXDATAZERO * num_zero_bytes +
|
data/lib/eth/utils.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Eth
|
2
|
-
|
3
2
|
module Utils
|
4
3
|
|
5
4
|
extend Ethereum::Base::Utils
|
@@ -22,7 +21,7 @@ module Eth
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def hex_to_bin(string)
|
25
|
-
[string].pack("H*")
|
24
|
+
[string.sub(/\A0x/, '')].pack("H*")
|
26
25
|
end
|
27
26
|
|
28
27
|
def base256_to_int(string)
|
@@ -49,5 +48,27 @@ module Eth
|
|
49
48
|
]
|
50
49
|
end
|
51
50
|
|
51
|
+
def prefix_hex(hex)
|
52
|
+
hex.match(/\A0x/) ? hex : "0x#{hex}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def bin_to_prefixed_hex(binary)
|
56
|
+
prefix_hex bin_to_hex(binary)
|
57
|
+
end
|
58
|
+
|
59
|
+
def public_key_to_address(hex)
|
60
|
+
bytes = hex_to_bin(hex)
|
61
|
+
address_bytes = Utils.keccak256(bytes[1..-1])[-20..-1]
|
62
|
+
bin_to_prefixed_hex address_bytes
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def lpad(x, symbol, l)
|
69
|
+
return x if x.size >= l
|
70
|
+
symbol * (l - x.size) + x
|
71
|
+
end
|
72
|
+
|
52
73
|
end
|
53
74
|
end
|
data/lib/eth/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Ellis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ethereum-base
|
@@ -130,8 +130,10 @@ extensions: []
|
|
130
130
|
extra_rdoc_files: []
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
|
+
- ".gitmodules"
|
133
134
|
- ".rspec"
|
134
135
|
- ".travis.yml"
|
136
|
+
- CHANGELOG.md
|
135
137
|
- Gemfile
|
136
138
|
- LICENSE.txt
|
137
139
|
- README.md
|