ciri 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/Gemfile.lock +6 -6
  4. data/README.md +68 -48
  5. data/Rakefile +16 -11
  6. data/ciri-crypto/lib/ciri/crypto/signature.rb +7 -5
  7. data/ciri-rlp/ciri-rlp.gemspec +1 -1
  8. data/ciri-rlp/lib/ciri/rlp.rb +1 -1
  9. data/ciri-rlp/lib/ciri/rlp/decode.rb +23 -7
  10. data/ciri-rlp/lib/ciri/rlp/encode.rb +8 -2
  11. data/ciri-rlp/lib/ciri/rlp/serializable.rb +14 -4
  12. data/ciri-rlp/lib/ciri/rlp/version.rb +1 -1
  13. data/ciri-rlp/spec/ciri/fixture_spec.rb +2 -2
  14. data/ciri-utils/Gemfile.lock +1 -1
  15. data/ciri-utils/lib/ciri/utils/version.rb +1 -1
  16. data/ciri.gemspec +4 -4
  17. data/lib/ciri/chain/header.rb +13 -11
  18. data/lib/ciri/chain/header_chain.rb +1 -10
  19. data/lib/ciri/chain/transaction.rb +5 -23
  20. data/lib/ciri/db/account_db.rb +0 -4
  21. data/lib/ciri/db/backend/errors.rb +27 -0
  22. data/lib/ciri/db/backend/memory.rb +10 -12
  23. data/lib/ciri/db/backend/rocks.rb +13 -6
  24. data/lib/ciri/db/backend/rocks_db.rb +4 -0
  25. data/lib/ciri/evm.rb +6 -1
  26. data/lib/ciri/evm/execution_context.rb +6 -2
  27. data/lib/ciri/evm/instruction.rb +0 -1
  28. data/lib/ciri/evm/op.rb +11 -5
  29. data/lib/ciri/evm/op/errors.rb +37 -0
  30. data/lib/ciri/evm/state.rb +1 -1
  31. data/lib/ciri/evm/sub_state.rb +6 -6
  32. data/lib/ciri/evm/vm.rb +53 -33
  33. data/lib/ciri/forks.rb +4 -0
  34. data/lib/ciri/forks/base.rb +28 -0
  35. data/lib/ciri/forks/byzantium.rb +45 -11
  36. data/lib/ciri/forks/byzantium/opcodes.rb +37 -0
  37. data/lib/ciri/forks/constantinople.rb +29 -0
  38. data/lib/ciri/forks/frontier.rb +49 -29
  39. data/lib/ciri/forks/frontier/cost.rb +91 -95
  40. data/lib/ciri/forks/frontier/opcodes.rb +99 -0
  41. data/lib/ciri/forks/frontier/transaction.rb +62 -0
  42. data/lib/ciri/forks/homestead.rb +31 -7
  43. data/lib/ciri/forks/homestead/opcodes.rb +37 -0
  44. data/lib/ciri/forks/homestead/transaction.rb +43 -0
  45. data/lib/ciri/forks/spurious_dragon.rb +55 -0
  46. data/lib/ciri/forks/spurious_dragon/cost.rb +91 -0
  47. data/lib/ciri/forks/spurious_dragon/transaction.rb +44 -0
  48. data/lib/ciri/forks/tangerine_whistle.rb +37 -0
  49. data/lib/ciri/forks/tangerine_whistle/cost.rb +98 -0
  50. data/lib/ciri/rlp/decode.rb +4 -4
  51. data/lib/ciri/rlp/encode.rb +2 -2
  52. data/lib/ciri/version.rb +1 -1
  53. metadata +22 -10
  54. data/lib/ciri/forks/homestead/cost.rb +0 -195
@@ -24,7 +24,7 @@ module Ciri
24
24
 
25
25
  extend Forwardable
26
26
 
27
- def_delegators :@account_db, :set_nonce, :increment_nonce, :set_balance, :add_balance, :touch_account,
27
+ def_delegators :@account_db, :set_nonce, :increment_nonce, :set_balance, :add_balance,
28
28
  :find_account, :delete_account, :account_dead?, :store, :fetch,
29
29
  :set_account_code, :get_account_code, :account_exist?
30
30
 
@@ -41,16 +41,16 @@ module Ciri
41
41
  @refunds = orig.refunds.dup
42
42
  end
43
43
 
44
- def add_refund_account(account)
45
- @refunds << account
44
+ def add_refund_account(address)
45
+ @refunds << address
46
46
  end
47
47
 
48
- def add_touched_account(account)
49
- @touched_accounts << account
48
+ def add_touched_account(address)
49
+ @touched_accounts << address
50
50
  end
51
51
 
52
- def add_suicide_account(account)
53
- @suicide_accounts << account
52
+ def add_suicide_account(address)
53
+ @suicide_accounts << address
54
54
  end
55
55
  end
56
56
 
@@ -24,6 +24,7 @@ require_relative 'instruction'
24
24
  require_relative 'sub_state'
25
25
  require_relative 'block_info'
26
26
  require_relative 'log_entry'
27
+ require_relative 'op/errors'
27
28
 
28
29
  using Ciri::CoreExt
29
30
  module Ciri
@@ -101,26 +102,34 @@ module Ciri
101
102
  end
102
103
 
103
104
  deposit_code_gas = fork_schema.calculate_deposit_code_gas(output)
105
+ gas_is_not_enough = deposit_code_gas > remain_gas
106
+ deposit_code_reach_limit = output.size > fork_schema.contract_code_size_limit
104
107
 
105
- if deposit_code_gas > remain_gas
106
- # deposit_code_gas not enough
108
+ # check deposit_code_gas
109
+ if gas_is_not_enough || deposit_code_reach_limit
107
110
  contract_address = 0
111
+ if fork_schema.exception_on_deposit_code_gas_not_enough
112
+ if deposit_code_reach_limit
113
+ set_exception GasNotEnoughError.new("deposit_code size reach limit, code size: #{output.size}, limit size: #{fork_schema.contract_code_size_limit}")
114
+ else
115
+ set_exception GasNotEnoughError.new("deposit_code_gas not enough, deposit_code_gas: #{deposit_code_gas}, remain_gas: #{remain_gas}")
116
+ end
117
+ else
118
+ set_output ''.b
119
+ end
108
120
  elsif exception
109
- # state.touch_account(contract_address)
110
121
  contract_address = 0
111
- if burn_gas_on_exception
112
- debug("exception: #{exception}, burn gas #{remain_gas} to zero... op code: 0x#{get_op(pc).to_s(16)}")
113
- consume_gas remain_gas
114
- end
115
- execution_context.revert
116
- state.revert(snapshot)
117
122
  else
118
123
  # set contract code
119
124
  set_account_code(contract_address, output)
125
+ if fork_schema.contract_init_nonce != 0
126
+ state.set_nonce(contract_address, fork_schema.contract_init_nonce)
127
+ end
120
128
  # minus deposit_code_fee
121
129
  consume_gas deposit_code_gas
122
- state.commit(snapshot)
123
130
  end
131
+
132
+ finalize_message(snapshot)
124
133
  [contract_address, exception]
125
134
  end
126
135
  end
@@ -148,18 +157,7 @@ module Ciri
148
157
  set_exception(e)
149
158
  end
150
159
 
151
- if exception
152
- if burn_gas_on_exception
153
- debug("exception: #{exception}, burn gas #{remain_gas} to zero... op code: 0x#{get_op(pc).to_s(16)}")
154
- consume_gas remain_gas
155
- end
156
- execution_context.revert
157
-
158
- state.revert(snapshot)
159
- else
160
- state.commit(snapshot)
161
- end
162
-
160
+ finalize_message(snapshot)
163
161
  [status, output || ''.b, exception]
164
162
  end
165
163
  end
@@ -172,6 +170,8 @@ module Ciri
172
170
  def transact(sender:, value:, to:)
173
171
  sender_account = find_account(sender)
174
172
  raise VMError.new("balance not enough") if sender_account.balance < value
173
+ add_touched_account(sender)
174
+ add_touched_account(to)
175
175
  state.add_balance(sender, -value)
176
176
  state.add_balance(to, value)
177
177
  end
@@ -207,12 +207,21 @@ module Ciri
207
207
  machine_state.extend_memory(execution_context, pos, size)
208
208
  end
209
209
 
210
+ def delete_empty_accounts
211
+ return unless fork_schema.clean_empty_accounts?
212
+ sub_state.touched_accounts.to_set.select do |address|
213
+ account_dead?(address)
214
+ end.each do |address|
215
+ state.delete_account(address)
216
+ end
217
+ end
218
+
210
219
  private
211
220
 
212
221
  # O(σ, μ, A, I) ≡ (σ′, μ′, A′, I)
213
222
  def operate
214
223
  w = get_op(pc)
215
- operation = OP.get(w)
224
+ operation = fork_schema.get_operation(w)
216
225
 
217
226
  raise "can't find operation #{w}, pc #{pc}" unless operation
218
227
 
@@ -230,12 +239,6 @@ module Ciri
230
239
  set_exception(e)
231
240
  end
232
241
 
233
- # revert sub_state and return if exception occur
234
- if exception
235
- execution_context.revert
236
- return
237
- end
238
-
239
242
  set_pc case
240
243
  when w == OP::JUMP
241
244
  jump_pc
@@ -254,8 +257,6 @@ module Ciri
254
257
  output
255
258
  elsif w == OP::STOP || w == OP::SELFDESTRUCT
256
259
  operate
257
- # return empty sequence: nil
258
- # debug("#{pc} #{OP.get(w).name} gas: 0 stack: #{stack.size}")
259
260
  nil
260
261
  else
261
262
  EMPTY_SET
@@ -266,7 +267,7 @@ module Ciri
266
267
  def check_exception(state, ms, instruction)
267
268
  w = instruction.get_op(pc)
268
269
  case
269
- when w == OP::INVALID || OP.input_count(w).nil?
270
+ when w == OP::INVALID || fork_schema.get_operation(w).nil?
270
271
  InvalidOpCodeError.new "can't find op code 0x#{w.to_s(16)} pc: #{pc}"
271
272
  when ms.stack.size < (consume = OP.input_count(w))
272
273
  StackError.new "stack not enough: stack:#{ms.stack.size} next consume: #{consume}"
@@ -280,7 +281,6 @@ module Ciri
280
281
  ReturnError.new "return data copy error"
281
282
  when stack.size - OP.input_count(w) + OP.output_count(w) > stack_size
282
283
  StackError.new "stack size reach #{stack_size} limit"
283
- # A condition in yellow paper but I can't understand..: (¬Iw ∧W(w,μ))
284
284
  when depth > max_depth
285
285
  StackError.new "call depth reach #{max_depth} limit"
286
286
  else
@@ -288,6 +288,26 @@ module Ciri
288
288
  end
289
289
  end
290
290
 
291
+ def finalize_message(snapshot)
292
+ # check exception and commit/revert state
293
+ if exception.is_a?(OP::RevertError)
294
+ execution_context.revert_sub_state
295
+ state.revert(snapshot)
296
+ # cleanup exception
297
+ set_exception(nil)
298
+ elsif exception
299
+ if burn_gas_on_exception
300
+ debug("exception: #{exception}, burn gas #{remain_gas} to zero... op code: 0x#{get_op(pc).to_s(16)}")
301
+ consume_gas remain_gas
302
+ end
303
+ execution_context.revert_sub_state
304
+ state.revert(snapshot)
305
+ else
306
+ delete_empty_accounts
307
+ state.commit(snapshot)
308
+ end
309
+ end
310
+
291
311
  end
292
312
  end
293
313
  end
@@ -18,6 +18,10 @@
18
18
  require_relative 'forks/config'
19
19
  require_relative 'forks/frontier'
20
20
  require_relative 'forks/homestead'
21
+ require_relative 'forks/tangerine_whistle'
22
+ require_relative 'forks/spurious_dragon'
23
+ require_relative 'forks/byzantium'
24
+ require_relative 'forks/constantinople'
21
25
 
22
26
  module Ciri
23
27
  module Forks
@@ -61,6 +61,34 @@ module Ciri
61
61
  def find_precompile_contract(address)
62
62
  raise NotImplementedError
63
63
  end
64
+
65
+ def transaction_class
66
+ raise NotImplementedError
67
+ end
68
+
69
+ def get_operation(op_code)
70
+ raise NotImplementedError
71
+ end
72
+
73
+ def exception_on_deposit_code_gas_not_enough
74
+ raise NotImplementedError
75
+ end
76
+
77
+ def contract_code_size_limit
78
+ raise NotImplementedError
79
+ end
80
+
81
+ def contract_init_nonce
82
+ raise NotImplementedError
83
+ end
84
+
85
+ def clean_empty_accounts?
86
+ raise NotImplementedError
87
+ end
88
+
89
+ def make_receipt(execution_result:, gas_used:)
90
+ raise NotImplementedError
91
+ end
64
92
  end
65
93
 
66
94
  end
@@ -16,25 +16,59 @@
16
16
 
17
17
 
18
18
  require_relative 'base'
19
- require_relative 'homestead'
19
+ require_relative 'spurious_dragon'
20
+ require_relative 'byzantium/opcodes'
21
+ require 'ciri/types/receipt'
22
+ require 'ciri/utils'
23
+ require 'ciri/rlp'
20
24
 
21
25
  module Ciri
22
26
  module Forks
27
+ # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-609.md
23
28
  module Byzantium
24
- class Schema < Forks::Frontier::Schema
29
+ class Schema < Forks::SpuriousDragon::Schema
25
30
 
26
- include Forks::Frontier::Cost
31
+ BLOCK_REWARD = 3 * 10.pow(18) # 3 ether
27
32
 
28
- # chain difficulty method
29
- # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-609.md
30
- # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-100.md
31
- def difficulty_time_factor(header, parent_header)
32
- y = header.ommers_hash == Utils::BLANK_SHA3 ? 1 : 2
33
- [y - (header.timestamp - parent_header.timestamp) / 9, -99].max
33
+ TRANSACTION_STATUS_FAILURE = ''.b
34
+ TRANSACTION_STATUS_SUCCESS = "\x01".b
35
+
36
+ BLANK_OMMERS_HASH = Utils.keccak(RLP.encode([])).freeze
37
+
38
+ def calculate_difficulty(header, parent_header)
39
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-100.md
40
+ y = parent_header.ommers_hash == BLANK_OMMERS_HASH ? 1 : 2
41
+ difficulty_time_factor = [y - (header.timestamp - parent_header.timestamp) / 9, -99].max
42
+ x = parent_header.difficulty / 2048
43
+
44
+ # difficulty bomb
45
+ height = [(header.number - 3000000), 0].max
46
+ height_factor = 2 ** (height / 100000 - 2)
47
+
48
+ difficulty = (parent_header.difficulty + x * difficulty_time_factor + height_factor).to_i
49
+ [header.difficulty, difficulty].max
50
+ end
51
+
52
+ def get_operation(op)
53
+ OPCODES[op]
54
+ end
55
+
56
+ def mining_rewards_of_block(block)
57
+ rewards = Hash.new(0)
58
+ # reward miner
59
+ rewards[block.header.beneficiary] += ((1 + block.ommers.count.to_f / 32) * BLOCK_REWARD).to_i
60
+
61
+ # reward ommer(uncle) block miners
62
+ block.ommers.each do |ommer|
63
+ rewards[ommer.beneficiary] += ((1 + (ommer.number - block.header.number).to_f / 8) * BLOCK_REWARD).to_i
64
+ end
65
+ rewards
34
66
  end
35
67
 
36
- def difficulty_virtual_height(height)
37
- [(height - 3000000), 0].max
68
+ # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-658.md
69
+ def make_receipt(execution_result:, gas_used:)
70
+ status = execution_result.status == 1 ? TRANSACTION_STATUS_SUCCESS : TRANSACTION_STATUS_FAILURE
71
+ Types::Receipt.new(state_root: status, gas_used: gas_used, logs: execution_result.logs)
38
72
  end
39
73
 
40
74
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Jiang Jinyang <https://justjjy.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ require 'ciri/evm/op'
19
+ require 'ciri/forks/homestead/opcodes'
20
+
21
+ module Ciri
22
+ module Forks
23
+ module Byzantium
24
+
25
+ include Ciri::EVM::OP
26
+
27
+ UPDATE_OPCODES = [
28
+ REVERT,
29
+ ].map do |op|
30
+ [op, Ciri::EVM::OP.get(op)]
31
+ end.to_h.freeze
32
+
33
+ OPCODES = Homestead::OPCODES.merge(UPDATE_OPCODES).freeze
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Jiang Jinyang <https://justjjy.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ require_relative 'base'
19
+ require_relative 'byzantium'
20
+
21
+ module Ciri
22
+ module Forks
23
+ module Constantinople
24
+ class Schema < Forks::Byzantium::Schema
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -17,8 +17,12 @@
17
17
 
18
18
  require_relative 'base'
19
19
  require_relative 'frontier/cost'
20
+ require_relative 'frontier/transaction'
21
+ require_relative 'frontier/opcodes'
22
+ require 'ciri/types/receipt'
20
23
  require 'ciri/core_ext'
21
24
  require 'ciri/evm/precompile_contract'
25
+ require 'forwardable'
22
26
 
23
27
  using Ciri::CoreExt
24
28
 
@@ -27,24 +31,15 @@ module Ciri
27
31
  module Frontier
28
32
  class Schema < Base
29
33
 
30
- BLOCK_REWARD = 5 * 10.pow(18) # 5 ether
31
-
32
- # gas methods
33
- def gas_of_operation(vm)
34
- Cost.cost_of_operation vm
35
- end
34
+ extend Forwardable
36
35
 
37
- def gas_of_memory(word_count)
38
- Cost.cost_of_memory word_count
39
- end
36
+ BLOCK_REWARD = 5 * 10.pow(18) # 5 ether
40
37
 
41
- def gas_of_call(vm:, gas:, to:, value:)
42
- Cost.gas_of_call(vm: vm, gas: gas, to: to, value: value)
38
+ def initialize
39
+ @cost = Cost.new
43
40
  end
44
41
 
45
- def intrinsic_gas_of_transaction(transaction)
46
- Cost.intrinsic_gas_of_transaction transaction
47
- end
42
+ def_delegators :@cost, :gas_of_operation, :gas_of_memory, :gas_of_call, :intrinsic_gas_of_transaction
48
43
 
49
44
  def calculate_deposit_code_gas(code_bytes)
50
45
  Cost::G_CODEDEPOSIT * (code_bytes || ''.b).size
@@ -66,26 +61,23 @@ module Ciri
66
61
  rewards
67
62
  end
68
63
 
69
- def validate_transaction(transaction)
70
- unless transaction.v >= 27 && transaction.v <= 28
71
- "v can be only 27 or 28 in frontier schema, found: #{transaction.v}"
72
- end
73
- end
64
+ def calculate_difficulty(header, parent_header)
65
+ difficulty_time_factor = (header.timestamp - parent_header.timestamp) < 13 ? 1 : -1
66
+ x = parent_header.difficulty / 2048
74
67
 
75
- # chain difficulty method
76
- def difficulty_time_factor(header, parent_header)
77
- (header.timestamp - parent_header.timestamp) < 13 ? 1 : -1
78
- end
68
+ # difficulty bomb
69
+ height = header.number
70
+ height_factor = 2 ** (height / 100000 - 2)
79
71
 
80
- def difficulty_virtual_height(height)
81
- height
72
+ difficulty = (parent_header.difficulty + x * difficulty_time_factor + height_factor).to_i
73
+ [header.difficulty, difficulty].max
82
74
  end
83
75
 
84
76
  PRECOMPILE_CONTRACTS = {
85
- "\x01".pad_zero(20).b => EVM::PrecompileContract::ECRecover.new,
86
- "\x02".pad_zero(20).b => EVM::PrecompileContract::SHA256.new,
87
- "\x03".pad_zero(20).b => EVM::PrecompileContract::RIPEMD160.new,
88
- "\x04".pad_zero(20).b => EVM::PrecompileContract::Identity.new,
77
+ "\x01".pad_zero(20).b => EVM::PrecompileContract::ECRecover.new,
78
+ "\x02".pad_zero(20).b => EVM::PrecompileContract::SHA256.new,
79
+ "\x03".pad_zero(20).b => EVM::PrecompileContract::RIPEMD160.new,
80
+ "\x04".pad_zero(20).b => EVM::PrecompileContract::Identity.new,
89
81
  }.freeze
90
82
 
91
83
  # EVM op code and contract
@@ -93,6 +85,34 @@ module Ciri
93
85
  PRECOMPILE_CONTRACTS[address.to_s]
94
86
  end
95
87
 
88
+ def transaction_class
89
+ Transaction
90
+ end
91
+
92
+ def get_operation(op)
93
+ OPCODES[op]
94
+ end
95
+
96
+ def exception_on_deposit_code_gas_not_enough
97
+ false
98
+ end
99
+
100
+ def contract_code_size_limit
101
+ Float::INFINITY
102
+ end
103
+
104
+ def contract_init_nonce
105
+ 0
106
+ end
107
+
108
+ def clean_empty_accounts?
109
+ false
110
+ end
111
+
112
+ def make_receipt(execution_result:, gas_used:)
113
+ Types::Receipt.new(state_root: execution_result.state_root, gas_used: gas_used, logs: execution_result.logs)
114
+ end
115
+
96
116
  end
97
117
  end
98
118
  end