ciri 0.0.0 → 0.0.1

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.
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