ciri 0.0.1 → 0.0.2
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/Gemfile.lock +8 -3
- data/README.md +5 -0
- data/Rakefile +24 -2
- data/ciri-rlp/.gitignore +11 -0
- data/ciri-rlp/.rspec +3 -0
- data/ciri-rlp/.travis.yml +5 -0
- data/ciri-rlp/CODE_OF_CONDUCT.md +74 -0
- data/ciri-rlp/Gemfile +6 -0
- data/ciri-rlp/Gemfile.lock +39 -0
- data/ciri-rlp/LICENSE.txt +21 -0
- data/ciri-rlp/README.md +98 -0
- data/ciri-rlp/Rakefile +6 -0
- data/ciri-rlp/bin/console +14 -0
- data/ciri-rlp/bin/setup +8 -0
- data/ciri-rlp/ciri-rlp.gemspec +28 -0
- data/{lib → ciri-rlp/lib}/ciri/rlp.rb +2 -2
- data/ciri-rlp/lib/ciri/rlp/decode.rb +144 -0
- data/ciri-rlp/lib/ciri/rlp/encode.rb +140 -0
- data/ciri-rlp/lib/ciri/rlp/serializable.rb +241 -0
- data/ciri-rlp/lib/ciri/rlp/version.rb +5 -0
- data/ciri-rlp/spec/ciri/fixture_spec.rb +95 -0
- data/ciri-rlp/spec/ciri/rlp/decode_spec.rb +68 -0
- data/ciri-rlp/spec/ciri/rlp/encode_spec.rb +72 -0
- data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +147 -0
- data/ciri-rlp/spec/ciri/rlp_spec.rb +5 -0
- data/ciri-rlp/spec/spec_helper.rb +14 -0
- data/ciri-utils/.gitignore +11 -0
- data/ciri-utils/.rspec +3 -0
- data/ciri-utils/.travis.yml +5 -0
- data/ciri-utils/CODE_OF_CONDUCT.md +74 -0
- data/ciri-utils/Gemfile +6 -0
- data/ciri-utils/Gemfile.lock +37 -0
- data/ciri-utils/LICENSE.txt +21 -0
- data/ciri-utils/README.md +61 -0
- data/ciri-utils/Rakefile +6 -0
- data/ciri-utils/bin/console +14 -0
- data/ciri-utils/bin/setup +8 -0
- data/ciri-utils/ciri-utils.gemspec +28 -0
- data/{lib → ciri-utils/lib}/ciri/utils.rb +9 -33
- data/{lib → ciri-utils/lib}/ciri/utils/logger.rb +0 -0
- data/{lib → ciri-utils/lib}/ciri/utils/number.rb +15 -12
- data/ciri-utils/lib/ciri/utils/version.rb +5 -0
- data/ciri-utils/spec/ciri/utils_spec.rb +5 -0
- data/ciri-utils/spec/spec_helper.rb +14 -0
- data/ciri.gemspec +6 -3
- data/lib/ciri/bloom_filter.rb +83 -0
- data/lib/ciri/chain.rb +67 -130
- data/lib/ciri/chain/header.rb +2 -2
- data/lib/ciri/chain/header_chain.rb +136 -0
- data/lib/ciri/chain/transaction.rb +1 -1
- data/lib/ciri/crypto.rb +2 -2
- data/lib/ciri/db/account_db.rb +145 -0
- data/lib/ciri/db/backend/memory.rb +24 -1
- data/lib/ciri/devp2p/peer.rb +1 -1
- data/lib/ciri/devp2p/rlpx/connection.rb +5 -5
- data/lib/ciri/eth/peer.rb +3 -3
- data/lib/ciri/eth/protocol_manage.rb +3 -3
- data/lib/ciri/eth/protocol_messages.rb +27 -26
- data/lib/ciri/ethash.rb +18 -3
- data/lib/ciri/evm.rb +101 -56
- data/lib/ciri/{utils/lib_c.rb → evm/errors.rb} +28 -18
- data/lib/ciri/evm/instruction.rb +3 -1
- data/lib/ciri/evm/{serialize.rb → log_entry.rb} +9 -27
- data/lib/ciri/evm/machine_state.rb +21 -2
- data/lib/ciri/evm/op.rb +19 -16
- data/lib/ciri/evm/state.rb +61 -0
- data/lib/ciri/evm/vm.rb +78 -102
- data/lib/ciri/forks.rb +1 -4
- data/lib/ciri/forks/base.rb +55 -0
- data/lib/ciri/forks/frontier.rb +37 -10
- data/lib/ciri/forks/frontier/cost.rb +186 -0
- data/lib/ciri/key.rb +1 -1
- data/lib/ciri/pow.rb +1 -1
- data/lib/ciri/rlp/decode.rb +6 -3
- data/lib/ciri/rlp/encode.rb +10 -10
- data/lib/ciri/rlp/serializable.rb +12 -9
- data/lib/ciri/serialize.rb +58 -0
- data/lib/ciri/trie.rb +362 -0
- data/lib/ciri/trie/nibbles.rb +97 -0
- data/lib/ciri/trie/nodes.rb +187 -0
- data/lib/ciri/{evm → types}/account.rb +20 -13
- data/lib/ciri/types/address.rb +16 -11
- data/lib/ciri/types/number.rb +73 -0
- data/lib/ciri/types/receipt.rb +57 -0
- data/lib/ciri/version.rb +1 -1
- metadata +82 -19
- data/lib/ciri/evm/forks/frontier.rb +0 -183
@@ -21,25 +21,35 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
|
24
|
-
require 'ffi'
|
25
|
-
|
26
24
|
module Ciri
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
25
|
+
class EVM
|
26
|
+
|
27
|
+
class Error < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
class InvalidTransition < Error
|
31
|
+
end
|
32
|
+
|
33
|
+
class InvalidTransaction < Error
|
34
|
+
end
|
35
|
+
|
36
|
+
# VM errors
|
37
|
+
class VMError < Error
|
38
|
+
end
|
39
|
+
|
40
|
+
class InvalidOpCodeError < VMError
|
41
|
+
end
|
42
|
+
|
43
|
+
class GasNotEnoughError < VMError
|
44
|
+
end
|
45
|
+
|
46
|
+
class StackError < VMError
|
47
|
+
end
|
48
|
+
|
49
|
+
class InvalidJumpError < VMError
|
50
|
+
end
|
51
|
+
|
52
|
+
class ReturnError < VMError
|
43
53
|
end
|
44
54
|
|
45
55
|
end
|
data/lib/ciri/evm/instruction.rb
CHANGED
@@ -21,40 +21,22 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
|
24
|
+
require 'ciri/rlp'
|
25
|
+
require 'ciri/utils'
|
24
26
|
require 'ciri/types/address'
|
27
|
+
require 'ciri/types/number'
|
25
28
|
|
26
29
|
module Ciri
|
27
30
|
class EVM
|
28
|
-
module Serialize
|
29
31
|
|
30
|
-
|
32
|
+
class LogEntry
|
33
|
+
include RLP::Serializable
|
34
|
+
schema [{address: Types::Address}, {topics: [Types::Int32]}, :data]
|
31
35
|
|
32
|
-
def
|
33
|
-
|
34
|
-
when Integer
|
35
|
-
Utils.big_endian_encode(item)
|
36
|
-
when Types::Address
|
37
|
-
item.to_s
|
38
|
-
else
|
39
|
-
item
|
40
|
-
end
|
36
|
+
def to_blooms
|
37
|
+
[address.to_s, *topics.map {|t| Utils.big_endian_encode(t, size: 32)}]
|
41
38
|
end
|
42
|
-
|
43
|
-
def deserialize(type, item)
|
44
|
-
if type == Integer && !item.is_a?(Integer)
|
45
|
-
Utils.big_endian_decode(item.to_s)
|
46
|
-
elsif type == Types::Address && !item.is_a?(Types::Address)
|
47
|
-
# check if address represent in Integer
|
48
|
-
item = Utils.big_endian_encode(item) if item.is_a?(Integer)
|
49
|
-
Types::Address.new(item.size >= 20 ? item[-20..-1] : ''.b)
|
50
|
-
elsif type.nil?
|
51
|
-
# get serialized word
|
52
|
-
serialize(item).rjust(32, "\x00".b)
|
53
|
-
else
|
54
|
-
item
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
39
|
end
|
40
|
+
|
59
41
|
end
|
60
42
|
end
|
@@ -21,13 +21,32 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
|
24
|
-
|
24
|
+
require 'ciri/serialize'
|
25
|
+
require 'ciri/evm/errors'
|
25
26
|
|
26
27
|
module Ciri
|
27
28
|
class EVM
|
28
29
|
|
29
30
|
# represent current vm status, include stack, memory..
|
30
|
-
MachineState
|
31
|
+
class MachineState
|
32
|
+
|
33
|
+
attr_reader :remain_gas, :memory, :stack
|
34
|
+
attr_accessor :pc, :output, :memory_item
|
35
|
+
|
36
|
+
def initialize(remain_gas:, pc:, memory:, memory_item:, stack:, output: ''.b)
|
37
|
+
raise ArgumentError.new("remain_gas must more than 0") if remain_gas < 0
|
38
|
+
@remain_gas = remain_gas
|
39
|
+
@pc = pc
|
40
|
+
@memory = memory
|
41
|
+
@memory_item = memory_item
|
42
|
+
@stack = stack
|
43
|
+
@output = output
|
44
|
+
end
|
45
|
+
|
46
|
+
def consume_gas(gas)
|
47
|
+
raise GasNotEnoughError.new("can't consume gas to negative, remain_gas: #{remain_gas}, consumed: #{gas}") if gas > remain_gas
|
48
|
+
@remain_gas -= gas
|
49
|
+
end
|
31
50
|
|
32
51
|
# fetch a list of items from stack
|
33
52
|
def pop_list(count, type = nil)
|
data/lib/ciri/evm/op.rb
CHANGED
@@ -24,7 +24,6 @@
|
|
24
24
|
require 'ciri/utils'
|
25
25
|
require 'ciri/utils/number'
|
26
26
|
require 'ciri/types/address'
|
27
|
-
require_relative 'serialize'
|
28
27
|
|
29
28
|
module Ciri
|
30
29
|
class EVM
|
@@ -267,7 +266,7 @@ module Ciri
|
|
267
266
|
|
268
267
|
def_op :EXTCODESIZE, 0x3b, 0, 1 do |vm|
|
269
268
|
address = vm.pop(Address)
|
270
|
-
code_size = vm.
|
269
|
+
code_size = vm.get_account_code(address).size
|
271
270
|
vm.push code_size
|
272
271
|
end
|
273
272
|
|
@@ -275,8 +274,7 @@ module Ciri
|
|
275
274
|
address = vm.pop(Address)
|
276
275
|
mem_pos, data_pos, size = vm.pop_list(3, Integer)
|
277
276
|
|
278
|
-
|
279
|
-
code = account.code || ''.b
|
277
|
+
code = vm.get_account_code(address)
|
280
278
|
data_end_pos = data_pos + size - 1
|
281
279
|
data = if data_pos >= code.size
|
282
280
|
''.b
|
@@ -340,13 +338,13 @@ module Ciri
|
|
340
338
|
end
|
341
339
|
|
342
340
|
def_op :SLOAD, 0x54, 1, 1 do |vm|
|
343
|
-
key = vm.pop
|
341
|
+
key = vm.pop(Integer)
|
344
342
|
vm.push vm.fetch(vm.instruction.address, key)
|
345
343
|
end
|
346
344
|
|
347
345
|
def_op :SSTORE, 0x55, 2, 0 do |vm|
|
348
|
-
key = vm.pop
|
349
|
-
value = vm.pop
|
346
|
+
key = vm.pop(Integer)
|
347
|
+
value = vm.pop(Integer)
|
350
348
|
|
351
349
|
vm.store(vm.instruction.address, key, value)
|
352
350
|
end
|
@@ -374,7 +372,7 @@ module Ciri
|
|
374
372
|
end
|
375
373
|
|
376
374
|
def_op :GAS, 0x5a, 0, 1 do |vm|
|
377
|
-
vm.push vm.machine_state.
|
375
|
+
vm.push vm.machine_state.remain_gas
|
378
376
|
end
|
379
377
|
|
380
378
|
def_op :JUMPDEST, 0x5b, 0, 0
|
@@ -421,7 +419,7 @@ module Ciri
|
|
421
419
|
pos, size = vm.pop_list(2, Integer)
|
422
420
|
log_data = vm.memory_fetch(pos, size)
|
423
421
|
vm.extend_memory(pos, size)
|
424
|
-
topics = vm.pop_list(i)
|
422
|
+
topics = vm.pop_list(i, Integer)
|
425
423
|
vm.add_log_entry(topics, log_data)
|
426
424
|
end
|
427
425
|
end.call(i))
|
@@ -435,7 +433,7 @@ module Ciri
|
|
435
433
|
init = vm.memory_fetch(mem_pos, size)
|
436
434
|
vm.extend_memory(mem_pos, size)
|
437
435
|
|
438
|
-
contract_address = vm.create_contract(value: value, init: init)
|
436
|
+
contract_address, _ = vm.create_contract(value: value, init: init)
|
439
437
|
vm.push contract_address
|
440
438
|
end
|
441
439
|
|
@@ -504,7 +502,13 @@ module Ciri
|
|
504
502
|
end
|
505
503
|
|
506
504
|
STATICCALL = 0xfa
|
507
|
-
|
505
|
+
|
506
|
+
# TODO REVERT need to be implement in forks
|
507
|
+
def_op :REVERT, -0xfd, 2, 0 do |vm|
|
508
|
+
index, size = vm.pop_list(2, Integer)
|
509
|
+
vm.output = vm.memory_fetch(index, size)
|
510
|
+
vm.extend_memory(index, size)
|
511
|
+
end
|
508
512
|
|
509
513
|
def_op :INVALID, 0xfe, 0, 0 do |vm|
|
510
514
|
raise 'should not invoke INVALID'
|
@@ -514,7 +518,6 @@ module Ciri
|
|
514
518
|
refund_address = vm.pop(Address)
|
515
519
|
refund_account = vm.find_account(refund_address)
|
516
520
|
|
517
|
-
vm.sub_state.suicide_accounts << vm.instruction.address
|
518
521
|
contract_account = vm.find_account vm.instruction.address
|
519
522
|
|
520
523
|
if refund_address != vm.instruction.address
|
@@ -523,12 +526,12 @@ module Ciri
|
|
523
526
|
|
524
527
|
contract_account.balance = 0
|
525
528
|
|
526
|
-
vm.
|
527
|
-
vm.
|
529
|
+
vm.state.set_balance(refund_address, refund_account.balance)
|
530
|
+
vm.state.set_balance(vm.instruction.address, contract_account.balance)
|
528
531
|
|
529
532
|
# register changed accounts
|
530
|
-
vm.add_refund_account(
|
531
|
-
vm.add_suicide_account(
|
533
|
+
vm.add_refund_account(refund_address)
|
534
|
+
vm.add_suicide_account(vm.instruction.address)
|
532
535
|
end
|
533
536
|
|
534
537
|
end
|
@@ -0,0 +1,61 @@
|
|
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 'ciri/db/account_db'
|
26
|
+
|
27
|
+
module Ciri
|
28
|
+
class EVM
|
29
|
+
class State
|
30
|
+
|
31
|
+
extend Forwardable
|
32
|
+
|
33
|
+
def_delegators :@account_db, :set_nonce, :increment_nonce, :set_balance, :add_balance, :touch_account,
|
34
|
+
:find_account, :delete_account, :account_dead?, :store, :fetch, :set_account_code, :get_account_code
|
35
|
+
|
36
|
+
def initialize(db, state_root: nil)
|
37
|
+
@db = db
|
38
|
+
@account_db = DB::AccountDB.new(db, root_hash: state_root)
|
39
|
+
end
|
40
|
+
|
41
|
+
def snapshot
|
42
|
+
[state_root, @db.dup]
|
43
|
+
end
|
44
|
+
|
45
|
+
def revert(snapshot)
|
46
|
+
state_root, db = snapshot
|
47
|
+
@db = db
|
48
|
+
@account_db = DB::AccountDB.new(db, root_hash: state_root)
|
49
|
+
end
|
50
|
+
|
51
|
+
def commit(snapshot)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def state_root
|
56
|
+
@account_db.root_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/ciri/evm/vm.rb
CHANGED
@@ -21,11 +21,13 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
|
24
|
+
require 'ciri/utils/logger'
|
25
|
+
require_relative 'errors'
|
24
26
|
require_relative 'machine_state'
|
25
27
|
require_relative 'instruction'
|
26
28
|
require_relative 'sub_state'
|
27
29
|
require_relative 'block_info'
|
28
|
-
require_relative '
|
30
|
+
require_relative 'log_entry'
|
29
31
|
|
30
32
|
module Ciri
|
31
33
|
class EVM
|
@@ -40,25 +42,12 @@ module Ciri
|
|
40
42
|
# other logic of EVM (include transaction logic) in EVM module.
|
41
43
|
class VM
|
42
44
|
|
43
|
-
class VMError < StandardError
|
44
|
-
end
|
45
|
-
class InvalidOpCodeError < VMError
|
46
|
-
end
|
47
|
-
class GasNotEnoughError < VMError
|
48
|
-
end
|
49
|
-
class StackError < VMError
|
50
|
-
end
|
51
|
-
class InvalidJumpError < VMError
|
52
|
-
end
|
53
|
-
class ReturnError < VMError
|
54
|
-
end
|
55
|
-
|
56
45
|
class << self
|
57
46
|
# this method provide a simpler interface to create VM and execute code
|
58
47
|
# VM.spawn(...) == VM.new(...)
|
59
48
|
# @return VM
|
60
|
-
def spawn(state:, gas_limit:, header: nil, block_info: nil, instruction
|
61
|
-
ms = MachineState.new(
|
49
|
+
def spawn(state:, gas_limit:, header: nil, block_info: nil, instruction: EVM::Instruction.new, fork_config:)
|
50
|
+
ms = MachineState.new(remain_gas: gas_limit, pc: 0, stack: [], memory: "\x00".b * 256, memory_item: 0)
|
62
51
|
|
63
52
|
block_info = block_info || header && BlockInfo.new(
|
64
53
|
coinbase: header.beneficiary,
|
@@ -84,17 +73,18 @@ module Ciri
|
|
84
73
|
|
85
74
|
# helper methods
|
86
75
|
include Utils::Logger
|
87
|
-
include Serialize
|
88
76
|
|
89
|
-
def_delegators :@machine_state, :stack, :pc, :pop, :push, :pop_list, :get_stack,
|
90
|
-
:
|
77
|
+
def_delegators :@machine_state, :stack, :pc, :pop, :push, :pop_list, :get_stack, :memory_item, :memory_item=,
|
78
|
+
:memory_store, :memory_fetch, :extend_memory, :remain_gas, :consume_gas
|
91
79
|
def_delegators :@instruction, :get_op, :get_code, :next_valid_instruction_pos, :get_data, :data, :sender
|
92
80
|
def_delegators :@sub_state, :add_refund_account, :add_touched_account, :add_suicide_account
|
81
|
+
def_delegators :@state, :find_account, :account_dead?, :store, :fetch, :set_account_code, :get_account_code
|
93
82
|
|
94
|
-
attr_reader :machine_state, :instruction, :sub_state, :block_info, :fork_config
|
83
|
+
attr_reader :state, :machine_state, :instruction, :sub_state, :block_info, :fork_config
|
95
84
|
attr_accessor :output, :exception
|
96
85
|
|
97
|
-
def initialize(state:, machine_state:, sub_state: nil, instruction:, block_info:,
|
86
|
+
def initialize(state:, machine_state:, sub_state: nil, instruction:, block_info:,
|
87
|
+
fork_config:, burn_gas_on_exception: true)
|
98
88
|
@state = state
|
99
89
|
@machine_state = machine_state
|
100
90
|
@instruction = instruction
|
@@ -102,31 +92,7 @@ module Ciri
|
|
102
92
|
@output = nil
|
103
93
|
@block_info = block_info
|
104
94
|
@fork_config = fork_config
|
105
|
-
|
106
|
-
|
107
|
-
# store data to address
|
108
|
-
def store(address, key, data)
|
109
|
-
data_is_blank = Ciri::Utils.blank_binary?(data)
|
110
|
-
# key_is_blank = Ciri::Utils.blank_binary?(key)
|
111
|
-
|
112
|
-
return unless data && !data_is_blank
|
113
|
-
|
114
|
-
# remove unnecessary null byte from key
|
115
|
-
key = serialize(key).gsub(/\A\0+/, ''.b)
|
116
|
-
key = "\x00".b if key.empty?
|
117
|
-
|
118
|
-
account = find_account address
|
119
|
-
account.storage[key] = serialize(data).rjust(32, "\x00".b)
|
120
|
-
update_account(account)
|
121
|
-
end
|
122
|
-
|
123
|
-
# fetch data from address
|
124
|
-
def fetch(address, key)
|
125
|
-
# remove unnecessary null byte from key
|
126
|
-
key = serialize(key).gsub(/\A\0+/, ''.b)
|
127
|
-
key = "\x00".b if key.empty?
|
128
|
-
|
129
|
-
find_account(address).storage[key] || ''.b
|
95
|
+
@burn_gas_on_exception = burn_gas_on_exception
|
130
96
|
end
|
131
97
|
|
132
98
|
# run vm
|
@@ -138,21 +104,22 @@ module Ciri
|
|
138
104
|
# low_level create_contract interface
|
139
105
|
# CREATE_CONTRACT op is based on this method
|
140
106
|
def create_contract(value:, init:)
|
141
|
-
|
107
|
+
caller_address = instruction.address
|
108
|
+
account = find_account(caller_address)
|
142
109
|
|
143
110
|
# return contract address 0 represent execution failed
|
144
111
|
return 0 unless account.balance >= value || instruction.execute_depth > 1024
|
145
112
|
|
146
|
-
|
113
|
+
state.increment_nonce(caller_address)
|
114
|
+
snapshot = state.snapshot
|
147
115
|
|
148
116
|
# generate contract_address
|
149
|
-
material = RLP.encode_simple([
|
117
|
+
material = RLP.encode_simple([caller_address.to_s, account.nonce])
|
150
118
|
contract_address = Utils.sha3(material)[-20..-1]
|
151
119
|
|
152
120
|
# initialize contract account
|
153
121
|
contract_account = find_account(contract_address)
|
154
|
-
contract_account.nonce = 1
|
155
|
-
contract_account.code = Utils::BLANK_SHA3
|
122
|
+
# contract_account.nonce = 1
|
156
123
|
|
157
124
|
# execute initialize code
|
158
125
|
create_contract_instruction = instruction.dup
|
@@ -160,34 +127,48 @@ module Ciri
|
|
160
127
|
create_contract_instruction.execute_depth += 1
|
161
128
|
create_contract_instruction.address = contract_address
|
162
129
|
|
130
|
+
# TODO refactoring: Maybe should use call_message to execute data
|
163
131
|
call_instruction(create_contract_instruction) do
|
164
132
|
execute
|
165
133
|
|
166
|
-
|
167
|
-
|
134
|
+
deposit_code_gas = fork_config.calculate_deposit_code_gas(output)
|
135
|
+
|
136
|
+
if deposit_code_gas > remain_gas
|
137
|
+
# deposit_code_gas not enough
|
138
|
+
contract_address = 0
|
139
|
+
elsif exception
|
140
|
+
state.touch_account(contract_address)
|
168
141
|
contract_address = 0
|
142
|
+
state.revert(snapshot)
|
169
143
|
else
|
170
144
|
# set contract code
|
171
|
-
|
145
|
+
set_account_code(contract_address, output)
|
146
|
+
# minus deposit_code_fee
|
147
|
+
machine_state.consume_gas deposit_code_gas
|
172
148
|
# transact value
|
173
149
|
account.balance -= value
|
174
150
|
contract_account.balance += value
|
151
|
+
|
152
|
+
state.set_balance(contract_address, contract_account.balance)
|
153
|
+
state.set_balance(caller_address, account.balance)
|
154
|
+
state.commit(snapshot)
|
175
155
|
end
|
156
|
+
[contract_address, exception]
|
176
157
|
end
|
177
|
-
|
178
|
-
# update account
|
179
|
-
update_account(contract_account)
|
180
|
-
update_account(account)
|
181
|
-
|
182
|
-
contract_address
|
183
158
|
end
|
184
159
|
|
185
160
|
# low level call message interface
|
186
161
|
# CALL, CALLCODE, DELEGATECALL ops is base on this method
|
187
|
-
def call_message(sender:, value:, receipt:, data:, code_address:)
|
162
|
+
def call_message(sender:, value:, receipt:, data:, code_address: receipt)
|
188
163
|
# return status code 0 represent execution failed
|
189
164
|
return [0, ''.b] unless value <= find_account(sender).balance && instruction.execute_depth <= 1024
|
190
165
|
|
166
|
+
state.increment_nonce(sender)
|
167
|
+
|
168
|
+
snapshot = state.snapshot
|
169
|
+
|
170
|
+
transact(sender: sender, value: value, to: receipt)
|
171
|
+
|
191
172
|
message_call_instruction = instruction.dup
|
192
173
|
message_call_instruction.address = receipt
|
193
174
|
message_call_instruction.sender = sender
|
@@ -196,54 +177,48 @@ module Ciri
|
|
196
177
|
message_call_instruction.execute_depth += 1
|
197
178
|
|
198
179
|
message_call_instruction.data = data
|
199
|
-
message_call_instruction.bytes_code =
|
180
|
+
message_call_instruction.bytes_code = get_account_code(code_address)
|
200
181
|
|
201
|
-
transact(sender: sender, value: value, to: receipt)
|
202
182
|
call_instruction(message_call_instruction) do
|
203
183
|
execute
|
204
|
-
|
205
|
-
|
184
|
+
|
185
|
+
if exception
|
186
|
+
state.revert(snapshot)
|
187
|
+
else
|
188
|
+
state.commit(snapshot)
|
189
|
+
end
|
190
|
+
|
191
|
+
[status, output || ''.b, exception]
|
206
192
|
end
|
207
193
|
end
|
208
194
|
|
195
|
+
def status
|
196
|
+
exception.nil? ? 0 : 1
|
197
|
+
end
|
198
|
+
|
209
199
|
# jump to pc
|
210
200
|
# only valid if current op code is allowed to modify pc
|
211
201
|
def jump_to(pc)
|
212
202
|
@jump_to = pc
|
213
203
|
end
|
214
204
|
|
215
|
-
def account_dead?(address)
|
216
|
-
Account.account_dead?(@state, address)
|
217
|
-
end
|
218
|
-
|
219
|
-
def find_account(address)
|
220
|
-
Account.find_account(@state, address)
|
221
|
-
end
|
222
|
-
|
223
|
-
# the only method which touch state
|
224
|
-
# VM do not consider state revert/commit, we let it to state implementation
|
225
|
-
def update_account(account)
|
226
|
-
address = account.address.to_s
|
227
|
-
@state[address] = account
|
228
|
-
add_touched_account(account)
|
229
|
-
end
|
230
|
-
|
231
205
|
def add_log_entry(topics, log_data)
|
232
|
-
sub_state.log_series <<
|
206
|
+
sub_state.log_series << LogEntry.new(address: instruction.address, topics: topics, data: log_data)
|
233
207
|
end
|
234
208
|
|
235
209
|
# transact value from sender to target address
|
236
210
|
def transact(sender:, value:, to:)
|
237
|
-
|
238
|
-
|
211
|
+
sender_account = find_account(sender)
|
212
|
+
to_account = find_account(to)
|
239
213
|
|
240
|
-
raise VMError.new("balance not enough") if
|
214
|
+
raise VMError.new("balance not enough") if sender_account.balance < value
|
241
215
|
|
242
|
-
|
243
|
-
|
216
|
+
sender_account.balance -= value
|
217
|
+
to_account.balance += value
|
244
218
|
|
245
|
-
|
246
|
-
|
219
|
+
state.set_nonce(sender, sender_account.nonce)
|
220
|
+
state.set_balance(sender, sender_account.balance)
|
221
|
+
state.set_balance(to, to_account.balance)
|
247
222
|
end
|
248
223
|
|
249
224
|
# call instruction
|
@@ -268,13 +243,14 @@ module Ciri
|
|
268
243
|
def execute
|
269
244
|
loop do
|
270
245
|
if (@exception ||= check_exception(@state, machine_state, instruction))
|
271
|
-
|
246
|
+
if @burn_gas_on_exception
|
247
|
+
debug("exception: #{@exception}, burn gas #{machine_state.remain_gas} to zero")
|
248
|
+
machine_state.consume_gas machine_state.remain_gas
|
249
|
+
end
|
272
250
|
return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, EMPTY_SET]
|
273
251
|
elsif get_op(machine_state.pc) == OP::REVERT
|
274
252
|
o = halt
|
275
|
-
|
276
|
-
machine_state.gas_remain -= gas_cost
|
277
|
-
return [EMPTY_SET, machine_state, sub_state, instruction, o]
|
253
|
+
return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, o]
|
278
254
|
elsif (o = halt) != EMPTY_SET
|
279
255
|
return [@state, machine_state, sub_state, instruction, o]
|
280
256
|
else
|
@@ -294,23 +270,23 @@ module Ciri
|
|
294
270
|
|
295
271
|
raise "can't find operation #{w}, pc #{ms.pc}" unless operation
|
296
272
|
|
297
|
-
op_cost = fork_config.
|
298
|
-
old_memory_cost = fork_config.
|
299
|
-
ms.
|
273
|
+
op_cost = fork_config.gas_of_operation(self)
|
274
|
+
old_memory_cost = fork_config.gas_of_memory(ms.memory_item)
|
275
|
+
ms.consume_gas op_cost
|
300
276
|
|
301
277
|
prev_sub_state = sub_state.dup
|
302
278
|
|
303
279
|
# call operation
|
304
280
|
operation.call(self)
|
305
281
|
# calculate gas_cost
|
306
|
-
new_memory_cost = fork_config.
|
282
|
+
new_memory_cost = fork_config.gas_of_memory(ms.memory_item)
|
307
283
|
memory_gas_cost = new_memory_cost - old_memory_cost
|
308
284
|
|
309
|
-
if ms.
|
310
|
-
ms.
|
285
|
+
if ms.remain_gas >= memory_gas_cost
|
286
|
+
ms.consume_gas memory_gas_cost
|
311
287
|
else
|
312
288
|
# memory gas_not_enough
|
313
|
-
@exception = GasNotEnoughError.new "gas not enough: gas remain:#{ms.
|
289
|
+
@exception = GasNotEnoughError.new "gas not enough: gas remain:#{ms.remain_gas} gas cost: #{memory_gas_cost}"
|
314
290
|
end
|
315
291
|
|
316
292
|
# revert sub_state and return if exception occur
|
@@ -356,8 +332,8 @@ module Ciri
|
|
356
332
|
InvalidOpCodeError.new "can't find op code #{w}"
|
357
333
|
when ms.stack.size < (consume = OP.input_count(w))
|
358
334
|
StackError.new "stack not enough: stack:#{ms.stack.size} next consume: #{consume}"
|
359
|
-
when ms.
|
360
|
-
GasNotEnoughError.new "gas not enough: gas remain:#{ms.
|
335
|
+
when ms.remain_gas < (gas_cost = fork_config.gas_of_operation(self))
|
336
|
+
GasNotEnoughError.new "gas not enough: gas remain:#{ms.remain_gas} gas cost: #{gas_cost}"
|
361
337
|
when w == OP::JUMP && instruction.destinations.include?(ms.get_stack(0, Integer))
|
362
338
|
InvalidJumpError.new "invalid jump dest #{ms.get_stack(0, Integer)}"
|
363
339
|
when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 && instruction.destinations.include?(ms.get_stack(0, Integer))
|