ciri 0.0.3 → 0.0.4

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