ciri 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +14 -0
  3. data/.rspec +2 -1
  4. data/.travis.yml +11 -4
  5. data/Gemfile.lock +3 -0
  6. data/README.md +44 -34
  7. data/Rakefile +47 -4
  8. data/ciri.gemspec +13 -12
  9. data/docker/Base +34 -0
  10. data/lib/ciri/actor.rb +223 -0
  11. data/lib/ciri/chain.rb +293 -0
  12. data/lib/ciri/chain/block.rb +47 -0
  13. data/lib/ciri/chain/header.rb +62 -0
  14. data/lib/ciri/chain/transaction.rb +145 -0
  15. data/lib/ciri/crypto.rb +58 -5
  16. data/lib/ciri/db/backend/memory.rb +68 -0
  17. data/lib/ciri/db/backend/rocks.rb +104 -0
  18. data/lib/ciri/db/backend/rocks_db.rb +278 -0
  19. data/lib/ciri/devp2p/peer.rb +10 -2
  20. data/lib/ciri/devp2p/protocol.rb +11 -3
  21. data/lib/ciri/devp2p/protocol_io.rb +6 -3
  22. data/lib/ciri/devp2p/rlpx.rb +1 -0
  23. data/lib/ciri/devp2p/rlpx/encryption_handshake.rb +1 -1
  24. data/lib/ciri/devp2p/rlpx/frame_io.rb +1 -1
  25. data/lib/ciri/devp2p/rlpx/message.rb +4 -4
  26. data/lib/ciri/devp2p/server.rb +14 -13
  27. data/lib/ciri/eth.rb +33 -0
  28. data/lib/ciri/eth/peer.rb +64 -0
  29. data/lib/ciri/eth/protocol_manage.rb +122 -0
  30. data/lib/ciri/eth/protocol_messages.rb +158 -0
  31. data/lib/ciri/eth/synchronizer.rb +188 -0
  32. data/lib/ciri/ethash.rb +123 -0
  33. data/lib/ciri/evm.rb +140 -0
  34. data/lib/ciri/evm/account.rb +50 -0
  35. data/lib/ciri/evm/block_info.rb +31 -0
  36. data/lib/ciri/evm/forks/frontier.rb +183 -0
  37. data/lib/ciri/evm/instruction.rb +92 -0
  38. data/lib/ciri/evm/machine_state.rb +81 -0
  39. data/lib/ciri/evm/op.rb +536 -0
  40. data/lib/ciri/evm/serialize.rb +60 -0
  41. data/lib/ciri/evm/sub_state.rb +64 -0
  42. data/lib/ciri/evm/vm.rb +379 -0
  43. data/lib/ciri/forks.rb +38 -0
  44. data/lib/ciri/forks/frontier.rb +43 -0
  45. data/lib/ciri/key.rb +7 -1
  46. data/lib/ciri/pow.rb +95 -0
  47. data/lib/ciri/rlp.rb +3 -53
  48. data/lib/ciri/rlp/decode.rb +100 -40
  49. data/lib/ciri/rlp/encode.rb +95 -34
  50. data/lib/ciri/rlp/serializable.rb +61 -91
  51. data/lib/ciri/types/address.rb +70 -0
  52. data/lib/ciri/types/errors.rb +36 -0
  53. data/lib/ciri/utils.rb +45 -13
  54. data/lib/ciri/utils/lib_c.rb +46 -0
  55. data/lib/ciri/utils/logger.rb +99 -0
  56. data/lib/ciri/utils/number.rb +67 -0
  57. data/lib/ciri/version.rb +1 -1
  58. metadata +67 -7
  59. data/lib/ciri/devp2p/actor.rb +0 -224
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'forwardable'
25
+ require_relative 'chain/block'
26
+ require_relative 'chain/header'
27
+ require_relative 'chain/transaction'
28
+ require_relative 'pow'
29
+
30
+ module Ciri
31
+
32
+ # Chain manipulate logic
33
+ # store via rocksdb
34
+ class Chain
35
+
36
+ class Error < StandardError
37
+ end
38
+
39
+ class InvalidHeaderError < Error
40
+ end
41
+
42
+ class InvalidBlockError < Error
43
+ end
44
+
45
+ # HeaderChain
46
+ # store headers
47
+ class HeaderChain
48
+ HEAD = 'head'
49
+ GENESIS = 'genesis'
50
+ HEADER_PREFIX = 'h'
51
+ TD_SUFFIX = 't'
52
+ NUM_SUFFIX = 'n'
53
+
54
+ attr_reader :store, :byzantium_block, :homestead_block
55
+
56
+ def initialize(store, byzantium_block: nil, homestead_block: nil)
57
+ @store = store
58
+ @byzantium_block = byzantium_block
59
+ @homestead_block = homestead_block
60
+ end
61
+
62
+ def head
63
+ encoded = store[HEAD]
64
+ encoded && Header.rlp_decode!(encoded)
65
+ end
66
+
67
+ def head=(header)
68
+ store[HEAD] = header.rlp_encode!
69
+ end
70
+
71
+ def get_header(hash)
72
+ encoded = store[HEADER_PREFIX + hash]
73
+ encoded && Header.rlp_decode!(encoded)
74
+ end
75
+
76
+ def get_header_by_number(number)
77
+ hash = get_header_hash_by_number(number)
78
+ hash && get_header(hash)
79
+ end
80
+
81
+ def valid?(header)
82
+ # ignore genesis header if there not exist one
83
+ return true if header.number == 0 && get_header_by_number(0).nil?
84
+
85
+ parent_header = get_header(header.parent_hash)
86
+ return false unless parent_header
87
+ # check height
88
+ return false unless parent_header.number + 1 == header.number
89
+ # check timestamp
90
+ return false unless parent_header.timestamp < header.timestamp
91
+
92
+ # check gas limit range
93
+ parent_gas_limit = parent_header.gas_limit
94
+ gas_limit_max = parent_gas_limit + parent_gas_limit / 1024
95
+ gas_limit_min = parent_gas_limit - parent_gas_limit / 1024
96
+ gas_limit = header.gas_limit
97
+ return false unless gas_limit >= 5000 && gas_limit > gas_limit_min && gas_limit < gas_limit_max
98
+ return false unless calculate_difficulty(header, parent_header) == header.difficulty
99
+
100
+ # check pow
101
+ begin
102
+ POW.check_pow(header.number, header.mining_hash, header.mix_hash, header.nonce, header.difficulty)
103
+ rescue POW::InvalidError
104
+ return false
105
+ end
106
+
107
+ true
108
+ end
109
+
110
+ # calculate header difficulty
111
+ # you can find explain in Ethereum yellow paper: Block Header Validity section.
112
+ def calculate_difficulty(header, parent_header)
113
+ return header.difficulty if header.number == 0
114
+
115
+ x = parent_header.difficulty / 2048
116
+ y = header.ommers_hash == Utils::BLANK_SHA3 ? 1 : 2
117
+
118
+ # handle byzantium fork
119
+ # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-609.md
120
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-100.md
121
+ byzantium_fork = byzantium_block && header.number > byzantium_block
122
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-2.md
123
+ homestead_fork = homestead_block && header.number > homestead_block
124
+
125
+ time_factor = if byzantium_fork
126
+ [y - (header.timestamp - parent_header.timestamp) / 9, -99].max
127
+ elsif homestead_fork
128
+ [1 - (header.timestamp - parent_header.timestamp) / 10, -99].max
129
+ else
130
+ (header.timestamp - parent_header.timestamp) < 13 ? 1 : -1
131
+ end
132
+
133
+ # difficulty bomb
134
+ height = byzantium_fork ? [(header.number - 3000000), 0].max : header.number
135
+ height_factor = 2 ** (height / 100000 - 2)
136
+
137
+ difficulty = (parent_header.difficulty + x * time_factor + height_factor).to_i
138
+ [header.difficulty, difficulty].max
139
+ end
140
+
141
+ # write header
142
+ def write(header)
143
+ hash = header.get_hash
144
+ # get total difficulty
145
+ td = if header.number == 0
146
+ header.difficulty
147
+ else
148
+ parent_header = get_header(header.parent_hash)
149
+ raise "can't find parent from db" unless parent_header
150
+ parent_td = total_difficulty(parent_header.get_hash)
151
+ parent_td + header.difficulty
152
+ end
153
+ # write header and td
154
+ store.batch do |b|
155
+ b.put(HEADER_PREFIX + hash, header.rlp_encode!)
156
+ b.put(HEADER_PREFIX + hash + TD_SUFFIX, RLP.encode(td, Integer))
157
+ end
158
+ end
159
+
160
+ def write_header_hash_number(header_hash, number)
161
+ enc_number = Utils.big_endian_encode number
162
+ store[HEADER_PREFIX + enc_number + NUM_SUFFIX] = header_hash
163
+ end
164
+
165
+ def get_header_hash_by_number(number)
166
+ enc_number = Utils.big_endian_encode number
167
+ store[HEADER_PREFIX + enc_number + NUM_SUFFIX]
168
+ end
169
+
170
+ def total_difficulty(header_hash = head.nil? ? nil : head.get_hash)
171
+ return 0 if header_hash.nil?
172
+ RLP.decode(store[HEADER_PREFIX + header_hash + TD_SUFFIX], Integer)
173
+ end
174
+ end
175
+
176
+ extend Forwardable
177
+
178
+ BODY_PREFIX = 'b'
179
+
180
+ def_delegators :@header_chain, :head, :total_difficulty, :get_header_by_number, :get_header
181
+
182
+ attr_reader :genesis, :network_id, :store, :header_chain
183
+
184
+ def initialize(store, genesis:, network_id:, byzantium_block: nil, homestead_block: nil)
185
+ @store = store
186
+ @header_chain = HeaderChain.new(store, byzantium_block: byzantium_block, homestead_block: homestead_block)
187
+ @genesis = genesis
188
+ @network_id = network_id
189
+ load_or_init_store
190
+ end
191
+
192
+ def genesis_hash
193
+ genesis.header.get_hash
194
+ end
195
+
196
+ def current_block
197
+ get_block(head.get_hash)
198
+ end
199
+
200
+ def current_height
201
+ head.number
202
+ end
203
+
204
+ # insert blocks in order
205
+ # blocks must be ordered from lower height to higher height
206
+ def insert_blocks(blocks)
207
+ prev_block = blocks[0]
208
+ blocks[1..-1].each do |block|
209
+ unless block.number == prev_block.number + 1 && block.parent_hash == prev_block.get_hash
210
+ raise InvalidBlockError.new("blocks insert orders not correct")
211
+ end
212
+ end
213
+
214
+ blocks.each do |block|
215
+ write_block block
216
+ end
217
+ end
218
+
219
+ def get_block_by_number(number)
220
+ hash = @header_chain.get_header_hash_by_number(number)
221
+ hash && get_block(hash)
222
+ end
223
+
224
+ def get_block(hash)
225
+ encoded = store[BODY_PREFIX + hash]
226
+ encoded && Block.rlp_decode!(encoded)
227
+ end
228
+
229
+ def write_block(block)
230
+ # write header
231
+ header = block.header
232
+ raise InvalidHeaderError.new("invalid header: #{header.number}") unless @header_chain.valid?(header)
233
+ @header_chain.write(header)
234
+
235
+ # write body
236
+ store[BODY_PREFIX + header.get_hash] = block.rlp_encode!
237
+
238
+ td = total_difficulty(header.get_hash)
239
+
240
+ if td > total_difficulty
241
+ # new block not follow current head, need reorg chain
242
+ if head && ((header.number <= head.number) || (header.number == head.number + 1 && header.parent_hash != head.get_hash))
243
+ reorg_chain(block, current_block)
244
+ else
245
+ # otherwise, new block extend current chain, just update chain head
246
+ @header_chain.head = header
247
+ @header_chain.write_header_hash_number(header.get_hash, header.number)
248
+ end
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ # reorg chain
255
+ def reorg_chain(new_block, old_block)
256
+ new_chain = []
257
+ # find common ancestor block
258
+ # move new_block and old_block to same height
259
+ while new_block.number > old_block.number
260
+ new_chain << new_block
261
+ new_block = get_block(new_block.parent_hash)
262
+ end
263
+
264
+ while old_block.number > new_block.number
265
+ old_block = get_block(old_block.parent_hash)
266
+ end
267
+
268
+ while old_block.get_hash != new_block.get_hash
269
+ new_chain << new_block
270
+ old_block = get_block(old_block.parent_hash)
271
+ new_block = get_block(new_block.parent_hash)
272
+ end
273
+
274
+ # rewrite chain
275
+ new_chain.reverse_each {|block| rewrite_block(block)}
276
+ end
277
+
278
+ # rewrite block
279
+ # this method will treat block as canonical chain block
280
+ def rewrite_block(block)
281
+ @header_chain.head = block.header
282
+ @header_chain.write_header_hash_number(block.get_hash, block.number)
283
+ end
284
+
285
+ def load_or_init_store
286
+ # write genesis block, is chain head not exists
287
+ if head.nil?
288
+ write_block(genesis)
289
+ end
290
+ end
291
+ end
292
+
293
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'ciri/rlp'
25
+ require_relative 'header'
26
+ require_relative 'transaction'
27
+ require 'forwardable'
28
+
29
+ module Ciri
30
+ class Chain
31
+
32
+ # structure for ethereum block
33
+ class Block
34
+ include RLP::Serializable
35
+ schema [
36
+ {header: Header},
37
+ {transactions: [Transaction]},
38
+ {ommers: [Header]}, # or uncles
39
+ ]
40
+
41
+ extend Forwardable
42
+
43
+ def_delegators :header, :number, :get_hash, :mining_hash, :parent_hash
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ module Ciri
25
+ class Chain
26
+
27
+ # block header
28
+ class Header
29
+ include Ciri::RLP::Serializable
30
+
31
+ schema [
32
+ :parent_hash,
33
+ :ommers_hash,
34
+ :beneficiary,
35
+ :state_root,
36
+ :transactions_root,
37
+ :receipts_root,
38
+ :logs_bloom,
39
+ {difficulty: Integer},
40
+ {number: Integer},
41
+ {gas_limit: Integer},
42
+ {gas_used: Integer},
43
+ {timestamp: Integer},
44
+ :extra_data,
45
+ :mix_hash,
46
+ :nonce,
47
+ ]
48
+
49
+ # header hash
50
+ def get_hash
51
+ Utils.sha3(rlp_encode!)
52
+ end
53
+
54
+ # mining_hash, used for mining
55
+ def mining_hash
56
+ Utils.sha3(rlp_encode! skip_keys: [:mix_hash, :nonce])
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'ciri/rlp'
25
+ require 'ciri/crypto'
26
+ require 'ciri/types/address'
27
+
28
+ module Ciri
29
+ class Chain
30
+
31
+ class Transaction
32
+
33
+ class InvalidError < StandardError
34
+ end
35
+
36
+ EIP155_CHAIN_ID_OFFSET = 35
37
+ V_OFFSET = 27
38
+
39
+ include RLP::Serializable
40
+
41
+ schema [
42
+ {nonce: Integer},
43
+ {gas_price: Integer},
44
+ {gas_limit: Integer},
45
+ {to: Types::Address},
46
+ {value: Integer},
47
+ :data,
48
+ {v: Integer},
49
+ {r: Integer},
50
+ {s: Integer}
51
+ ]
52
+
53
+ default_data v: 0, r: 0, s: 0, data: "\x00".b
54
+
55
+ # sender address
56
+ # @return address String
57
+ def sender
58
+ @sender ||= begin
59
+ address = Types::Address.new(Utils.sha3(Crypto.ecdsa_recover(sign_hash(chain_id), signature)[1..-1])[-20..-1])
60
+ address.validate
61
+ address
62
+ end
63
+ end
64
+
65
+ def signature
66
+ v = if eip_155_signed_transaction?
67
+ # retrieve actually v from transaction.v, see EIP-155(prevent replay attack)
68
+ (self.v - 1) % 2
69
+ elsif [27, 28].include?(self.v)
70
+ self.v - 27
71
+ else
72
+ self.v
73
+ end
74
+ Crypto::Signature.new(vrs: [v, r, s])
75
+ end
76
+
77
+ # @param key Key
78
+ def sign_with_key!(key)
79
+ signature = key.ecdsa_signature(sign_hash)
80
+ self.v = signature.v
81
+ self.r = signature.r
82
+ self.s = signature.s
83
+ nil
84
+ end
85
+
86
+ def contract_creation?
87
+ to.empty?
88
+ end
89
+
90
+ def sign_hash(chain_id = nil)
91
+ param = data || ''.b
92
+ list = [nonce, gas_price, gas_limit, to, value, param]
93
+ if chain_id
94
+ list += [chain_id, ''.b, ''.b]
95
+ end
96
+ Utils.sha3(RLP.encode_simple list)
97
+ end
98
+
99
+ def get_hash
100
+ Utils.sha3 rlp_encode!
101
+ end
102
+
103
+ # validate transaction
104
+ # @param intrinsic_gas_of_transaction Proc
105
+ def validate!(intrinsic_gas_of_transaction: nil)
106
+ begin
107
+ sender
108
+ rescue Ciri::Crypto::ECDSASignatureError => e
109
+ raise InvalidError.new("recover signature error, error: #{e}")
110
+ rescue Ciri::Types::Errors::InvalidError => e
111
+ raise InvalidError.new(e.to_s)
112
+ end
113
+
114
+ raise InvalidError.new('signature rvs error') unless signature.valid?
115
+ raise InvalidError.new('signature s is low') unless signature.low_s?
116
+
117
+ if intrinsic_gas_of_transaction
118
+ begin
119
+ intrinsic_gas = intrinsic_gas_of_transaction[self]
120
+ rescue StandardError
121
+ raise InvalidError.new 'intrinsic gas calculation error'
122
+ end
123
+ raise InvalidError.new 'intrinsic gas not enough' unless intrinsic_gas <= gas_limit
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ # return chain_id by v
130
+ def chain_id
131
+ if eip_155_signed_transaction?
132
+ # retrieve chain_id from v, see EIP-155
133
+ (v - 35) / 2
134
+ end
135
+ end
136
+
137
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-155.md
138
+ def eip_155_signed_transaction?
139
+ v >= EIP155_CHAIN_ID_OFFSET
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+ end