eth 0.3.4 → 0.4.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 +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
|