ciri 0.0.2 → 0.0.3
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/.gitignore +2 -0
- data/.rspec +0 -1
- data/Gemfile.lock +11 -7
- data/LICENSE.txt +201 -21
- data/README.md +78 -25
- data/Rakefile +45 -17
- data/bin/bundle +105 -0
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/ciri-crypto/.gitignore +11 -0
- data/ciri-crypto/.rspec +3 -0
- data/ciri-crypto/.travis.yml +5 -0
- data/ciri-crypto/CODE_OF_CONDUCT.md +74 -0
- data/ciri-crypto/Gemfile +6 -0
- data/ciri-crypto/Gemfile.lock +43 -0
- data/ciri-crypto/LICENSE.txt +201 -0
- data/ciri-crypto/README.md +58 -0
- data/ciri-crypto/Rakefile +6 -0
- data/ciri-crypto/bin/console +14 -0
- data/ciri-crypto/bin/setup +8 -0
- data/ciri-crypto/ciri-crypto.gemspec +40 -0
- data/ciri-crypto/lib/ciri/crypto/errors.rb +29 -0
- data/ciri-crypto/lib/ciri/crypto/signature.rb +71 -0
- data/ciri-crypto/lib/ciri/crypto/version.rb +5 -0
- data/{lib → ciri-crypto/lib}/ciri/crypto.rb +16 -69
- data/ciri-crypto/spec/ciri/crypto_spec.rb +56 -0
- data/ciri-crypto/spec/spec_helper.rb +14 -0
- data/ciri-rlp/Gemfile.lock +5 -5
- data/ciri-rlp/LICENSE.txt +201 -21
- data/ciri-rlp/README.md +1 -1
- data/ciri-rlp/ciri-rlp.gemspec +3 -3
- data/ciri-rlp/lib/ciri/rlp/decode.rb +10 -16
- data/ciri-rlp/lib/ciri/rlp/encode.rb +10 -16
- data/ciri-rlp/lib/ciri/rlp/serializable.rb +10 -16
- data/ciri-rlp/lib/ciri/rlp.rb +10 -16
- data/ciri-rlp/spec/ciri/fixture_spec.rb +10 -16
- data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +10 -16
- data/ciri-utils/Gemfile.lock +4 -4
- data/ciri-utils/LICENSE.txt +201 -21
- data/ciri-utils/README.md +1 -1
- data/ciri-utils/ciri-utils.gemspec +2 -2
- data/ciri-utils/lib/ciri/utils/logger.rb +10 -16
- data/ciri-utils/lib/ciri/utils/number.rb +10 -16
- data/ciri-utils/lib/ciri/utils/version.rb +1 -1
- data/ciri-utils/lib/ciri/utils.rb +3 -3
- data/ciri.gemspec +4 -3
- data/docker/{Base → Dockerfile} +9 -3
- data/lib/ciri/actor.rb +10 -16
- data/lib/ciri/bloom_filter.rb +11 -17
- data/lib/ciri/chain/block.rb +10 -16
- data/lib/ciri/chain/header.rb +12 -18
- data/lib/ciri/chain/header_chain.rb +8 -22
- data/lib/ciri/chain/transaction.rb +38 -33
- data/lib/ciri/chain.rb +27 -26
- data/lib/ciri/core_ext.rb +61 -0
- data/lib/ciri/db/account_db.rb +25 -21
- data/lib/ciri/db/backend/memory.rb +10 -16
- data/lib/ciri/db/backend/rocks.rb +10 -16
- data/lib/ciri/db/backend/rocks_db.rb +10 -16
- data/lib/ciri/devp2p/peer.rb +10 -16
- data/lib/ciri/devp2p/protocol.rb +10 -16
- data/lib/ciri/devp2p/protocol_io.rb +10 -16
- data/lib/ciri/devp2p/rlpx/connection.rb +10 -16
- data/lib/ciri/devp2p/rlpx/encryption_handshake.rb +13 -19
- data/lib/ciri/devp2p/rlpx/error.rb +10 -16
- data/lib/ciri/devp2p/rlpx/frame_io.rb +10 -16
- data/lib/ciri/devp2p/rlpx/message.rb +10 -16
- data/lib/ciri/devp2p/rlpx/node.rb +10 -16
- data/lib/ciri/devp2p/rlpx/protocol_handshake.rb +10 -16
- data/lib/ciri/devp2p/rlpx/protocol_messages.rb +10 -16
- data/lib/ciri/devp2p/rlpx/secrets.rb +10 -16
- data/lib/ciri/devp2p/rlpx.rb +10 -16
- data/lib/ciri/devp2p/server.rb +10 -16
- data/lib/ciri/eth/peer.rb +10 -16
- data/lib/ciri/eth/protocol_manage.rb +10 -16
- data/lib/ciri/eth/protocol_messages.rb +10 -16
- data/lib/ciri/eth/synchronizer.rb +10 -16
- data/lib/ciri/eth.rb +10 -16
- data/lib/ciri/ethash.rb +10 -16
- data/lib/ciri/evm/block_info.rb +25 -17
- data/lib/ciri/evm/errors.rb +13 -16
- data/lib/ciri/evm/execution_context.rb +136 -0
- data/lib/ciri/evm/instruction.rb +17 -24
- data/lib/ciri/evm/log_entry.rb +12 -18
- data/lib/ciri/evm/machine_state.rb +28 -33
- data/lib/ciri/evm/op.rb +52 -89
- data/lib/ciri/evm/op_call.rb +114 -0
- data/lib/ciri/evm/precompile_contract.rb +102 -0
- data/lib/ciri/evm/state.rb +28 -21
- data/lib/ciri/evm/sub_state.rb +18 -24
- data/lib/ciri/evm/vm.rb +128 -190
- data/lib/ciri/evm.rb +77 -85
- data/lib/ciri/forks/base.rb +28 -16
- data/lib/ciri/forks/byzantium.rb +43 -0
- data/lib/ciri/forks/config.rb +36 -0
- data/lib/ciri/forks/frontier/cost.rb +42 -33
- data/lib/ciri/forks/frontier.rb +47 -18
- data/lib/ciri/forks/homestead/cost.rb +195 -0
- data/lib/ciri/forks/homestead.rb +46 -0
- data/lib/ciri/forks.rb +12 -22
- data/lib/ciri/key.rb +14 -3
- data/lib/ciri/pow.rb +11 -17
- data/lib/ciri/rlp/decode.rb +10 -16
- data/lib/ciri/rlp/encode.rb +10 -16
- data/lib/ciri/rlp/serializable.rb +10 -16
- data/lib/ciri/serialize.rb +14 -17
- data/lib/ciri/trie/nibbles.rb +10 -16
- data/lib/ciri/trie/nodes.rb +12 -17
- data/lib/ciri/trie.rb +12 -18
- data/lib/ciri/types/account.rb +15 -17
- data/lib/ciri/types/address.rb +11 -17
- data/lib/ciri/types/errors.rb +10 -16
- data/lib/ciri/types/receipt.rb +12 -18
- data/lib/ciri/types/uint.rb +79 -0
- data/lib/ciri/version.rb +1 -1
- data/lib/ciri.rb +10 -16
- metadata +54 -10
- data/lib/ciri/types/number.rb +0 -73
data/lib/ciri/evm/sub_state.rb
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Copyright
|
|
3
|
+
# Copyright 2018 Jiang Jinyang <https://justjjy.com>
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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:
|
|
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
|
|
11
8
|
#
|
|
12
|
-
#
|
|
13
|
-
# all copies or substantial portions of the Software.
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
10
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
# THE SOFTWARE.
|
|
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.
|
|
22
16
|
|
|
23
17
|
|
|
24
18
|
module Ciri
|
|
@@ -27,17 +21,17 @@ module Ciri
|
|
|
27
21
|
# sub state contained changed accounts and log_series
|
|
28
22
|
class SubState
|
|
29
23
|
|
|
30
|
-
EMPTY = SubState.new.freeze
|
|
31
|
-
|
|
32
24
|
attr_reader :suicide_accounts, :log_series, :touched_accounts, :refunds
|
|
33
25
|
|
|
34
26
|
def initialize(suicide_accounts: [], log_series: [], touched_accounts: [], refunds: [])
|
|
35
|
-
@suicide_accounts =
|
|
27
|
+
@suicide_accounts = suicide_accounts
|
|
36
28
|
@log_series = log_series
|
|
37
|
-
@touched_accounts =
|
|
38
|
-
@refunds =
|
|
29
|
+
@touched_accounts = touched_accounts
|
|
30
|
+
@refunds = refunds
|
|
39
31
|
end
|
|
40
32
|
|
|
33
|
+
EMPTY = SubState.new.freeze
|
|
34
|
+
|
|
41
35
|
# support safety copy
|
|
42
36
|
def initialize_copy(orig)
|
|
43
37
|
super
|
|
@@ -48,15 +42,15 @@ module Ciri
|
|
|
48
42
|
end
|
|
49
43
|
|
|
50
44
|
def add_refund_account(account)
|
|
51
|
-
@refunds
|
|
45
|
+
@refunds << account
|
|
52
46
|
end
|
|
53
47
|
|
|
54
48
|
def add_touched_account(account)
|
|
55
|
-
@touched_accounts
|
|
49
|
+
@touched_accounts << account
|
|
56
50
|
end
|
|
57
51
|
|
|
58
52
|
def add_suicide_account(account)
|
|
59
|
-
@suicide_accounts
|
|
53
|
+
@suicide_accounts << account
|
|
60
54
|
end
|
|
61
55
|
end
|
|
62
56
|
|
data/lib/ciri/evm/vm.rb
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Copyright
|
|
3
|
+
# Copyright 2018 Jiang Jinyang <https://justjjy.com>
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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:
|
|
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
|
|
11
8
|
#
|
|
12
|
-
#
|
|
13
|
-
# all copies or substantial portions of the Software.
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
10
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
# THE SOFTWARE.
|
|
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.
|
|
22
16
|
|
|
23
17
|
|
|
24
18
|
require 'ciri/utils/logger'
|
|
19
|
+
require 'ciri/core_ext'
|
|
25
20
|
require_relative 'errors'
|
|
21
|
+
require_relative 'execution_context'
|
|
26
22
|
require_relative 'machine_state'
|
|
27
23
|
require_relative 'instruction'
|
|
28
24
|
require_relative 'sub_state'
|
|
29
25
|
require_relative 'block_info'
|
|
30
26
|
require_relative 'log_entry'
|
|
31
27
|
|
|
28
|
+
using Ciri::CoreExt
|
|
32
29
|
module Ciri
|
|
33
30
|
class EVM
|
|
34
31
|
|
|
@@ -42,57 +39,30 @@ module Ciri
|
|
|
42
39
|
# other logic of EVM (include transaction logic) in EVM module.
|
|
43
40
|
class VM
|
|
44
41
|
|
|
45
|
-
class << self
|
|
46
|
-
# this method provide a simpler interface to create VM and execute code
|
|
47
|
-
# VM.spawn(...) == VM.new(...)
|
|
48
|
-
# @return VM
|
|
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)
|
|
51
|
-
|
|
52
|
-
block_info = block_info || header && BlockInfo.new(
|
|
53
|
-
coinbase: header.beneficiary,
|
|
54
|
-
difficulty: header.difficulty,
|
|
55
|
-
gas_limit: header.gas_limit,
|
|
56
|
-
number: header.number,
|
|
57
|
-
timestamp: header.timestamp
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
vm = VM.new(
|
|
61
|
-
state: state,
|
|
62
|
-
machine_state: ms,
|
|
63
|
-
block_info: block_info,
|
|
64
|
-
instruction: instruction,
|
|
65
|
-
fork_config: fork_config
|
|
66
|
-
)
|
|
67
|
-
yield vm if block_given?
|
|
68
|
-
vm
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
42
|
extend Forwardable
|
|
73
43
|
|
|
74
44
|
# helper methods
|
|
75
45
|
include Utils::Logger
|
|
76
46
|
|
|
77
|
-
def_delegators
|
|
78
|
-
:memory_store, :memory_fetch, :extend_memory
|
|
79
|
-
def_delegators
|
|
80
|
-
|
|
81
|
-
def_delegators :@state, :find_account, :account_dead?, :store, :fetch, :set_account_code, :get_account_code
|
|
47
|
+
def_delegators :machine_state, :stack, :pop, :push, :pop_list, :get_stack, :memory_item, :memory_item=,
|
|
48
|
+
:memory_store, :memory_fetch, :extend_memory
|
|
49
|
+
def_delegators :state, :find_account, :account_dead?, :store, :fetch,
|
|
50
|
+
:set_account_code, :get_account_code, :account_exist?
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
# delegate methods to current execution_context
|
|
53
|
+
def_delegators :execution_context, :instruction, :sub_state, :machine_state, :block_info, :fork_schema,
|
|
54
|
+
:pc, :output, :exception, :set_output, :set_exception, :set_pc, :status, :depth,
|
|
55
|
+
:gas_limit, :refund_gas, :reset_refund_gas, :consume_gas, :remain_gas, :jump_to, :jump_pc
|
|
56
|
+
def_delegators :instruction, :get_op, :get_code, :next_valid_instruction_pos, :get_data, :data, :sender, :destinations
|
|
57
|
+
def_delegators :sub_state, :add_refund_account, :add_touched_account, :add_suicide_account
|
|
85
58
|
|
|
86
|
-
|
|
87
|
-
|
|
59
|
+
attr_reader :state, :execution_context, :burn_gas_on_exception, :max_depth, :stack_size
|
|
60
|
+
|
|
61
|
+
def initialize(state:, burn_gas_on_exception: true, max_depth: 1024, stack_size: 1024)
|
|
88
62
|
@state = state
|
|
89
|
-
@machine_state = machine_state
|
|
90
|
-
@instruction = instruction
|
|
91
|
-
@sub_state = sub_state || SubState.new
|
|
92
|
-
@output = nil
|
|
93
|
-
@block_info = block_info
|
|
94
|
-
@fork_config = fork_config
|
|
95
63
|
@burn_gas_on_exception = burn_gas_on_exception
|
|
64
|
+
@max_depth = max_depth
|
|
65
|
+
@stack_size = stack_size
|
|
96
66
|
end
|
|
97
67
|
|
|
98
68
|
# run vm
|
|
@@ -103,54 +73,52 @@ module Ciri
|
|
|
103
73
|
|
|
104
74
|
# low_level create_contract interface
|
|
105
75
|
# CREATE_CONTRACT op is based on this method
|
|
106
|
-
def create_contract(
|
|
107
|
-
caller_address = instruction.
|
|
76
|
+
def create_contract(context: self.execution_context)
|
|
77
|
+
caller_address = context.instruction.sender
|
|
78
|
+
value = context.instruction.value
|
|
108
79
|
account = find_account(caller_address)
|
|
109
80
|
|
|
110
81
|
# return contract address 0 represent execution failed
|
|
111
|
-
return 0 unless account.balance >= value ||
|
|
112
|
-
|
|
113
|
-
state.increment_nonce(caller_address)
|
|
82
|
+
return 0 unless account.balance >= value || depth > max_depth
|
|
114
83
|
snapshot = state.snapshot
|
|
115
84
|
|
|
116
85
|
# generate contract_address
|
|
117
|
-
material = RLP.encode_simple([caller_address.to_s, account.nonce])
|
|
118
|
-
contract_address = Utils.
|
|
86
|
+
material = RLP.encode_simple([caller_address.to_s, account.nonce - 1])
|
|
87
|
+
contract_address = Utils.keccak(material)[-20..-1]
|
|
88
|
+
|
|
89
|
+
transact(sender: caller_address, value: value, to: contract_address)
|
|
119
90
|
|
|
120
91
|
# initialize contract account
|
|
121
92
|
contract_account = find_account(contract_address)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
call_instruction(create_contract_instruction) do
|
|
132
|
-
execute
|
|
93
|
+
context.instruction.address = contract_address
|
|
94
|
+
with_context(context) do
|
|
95
|
+
if contract_account.has_code? || contract_account.nonce > 0
|
|
96
|
+
err = ContractCollisionError.new("address #{contract_address.to_hex} collision")
|
|
97
|
+
debug(err.message)
|
|
98
|
+
set_exception(err)
|
|
99
|
+
else
|
|
100
|
+
execute
|
|
101
|
+
end
|
|
133
102
|
|
|
134
|
-
deposit_code_gas =
|
|
103
|
+
deposit_code_gas = fork_schema.calculate_deposit_code_gas(output)
|
|
135
104
|
|
|
136
105
|
if deposit_code_gas > remain_gas
|
|
137
106
|
# deposit_code_gas not enough
|
|
138
107
|
contract_address = 0
|
|
139
108
|
elsif exception
|
|
140
|
-
state.touch_account(contract_address)
|
|
109
|
+
# state.touch_account(contract_address)
|
|
141
110
|
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
|
|
142
116
|
state.revert(snapshot)
|
|
143
117
|
else
|
|
144
118
|
# set contract code
|
|
145
119
|
set_account_code(contract_address, output)
|
|
146
120
|
# minus deposit_code_fee
|
|
147
|
-
|
|
148
|
-
# transact value
|
|
149
|
-
account.balance -= value
|
|
150
|
-
contract_account.balance += value
|
|
151
|
-
|
|
152
|
-
state.set_balance(contract_address, contract_account.balance)
|
|
153
|
-
state.set_balance(caller_address, account.balance)
|
|
121
|
+
consume_gas deposit_code_gas
|
|
154
122
|
state.commit(snapshot)
|
|
155
123
|
end
|
|
156
124
|
[contract_address, exception]
|
|
@@ -159,30 +127,34 @@ module Ciri
|
|
|
159
127
|
|
|
160
128
|
# low level call message interface
|
|
161
129
|
# CALL, CALLCODE, DELEGATECALL ops is base on this method
|
|
162
|
-
def call_message(
|
|
130
|
+
def call_message(context: self.execution_context, code_address: context.instruction.address)
|
|
131
|
+
address = context.instruction.address
|
|
132
|
+
value = context.instruction.value
|
|
133
|
+
sender = context.instruction.sender
|
|
163
134
|
# return status code 0 represent execution failed
|
|
164
|
-
return [0, ''.b] unless value <= find_account(sender).balance &&
|
|
165
|
-
|
|
166
|
-
state.increment_nonce(sender)
|
|
135
|
+
return [0, ''.b] unless value <= find_account(sender).balance && depth <= max_depth
|
|
167
136
|
|
|
168
137
|
snapshot = state.snapshot
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
call_instruction(message_call_instruction) do
|
|
183
|
-
execute
|
|
138
|
+
transact(sender: sender, value: value, to: address)
|
|
139
|
+
# enter new execution context
|
|
140
|
+
with_context(context) do
|
|
141
|
+
begin
|
|
142
|
+
if (precompile_contract = fork_schema.find_precompile_contract(code_address))
|
|
143
|
+
precompile_contract.call(self)
|
|
144
|
+
else
|
|
145
|
+
execute
|
|
146
|
+
end
|
|
147
|
+
rescue GasNotEnoughError => e
|
|
148
|
+
set_exception(e)
|
|
149
|
+
end
|
|
184
150
|
|
|
185
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
|
+
|
|
186
158
|
state.revert(snapshot)
|
|
187
159
|
else
|
|
188
160
|
state.commit(snapshot)
|
|
@@ -192,16 +164,6 @@ module Ciri
|
|
|
192
164
|
end
|
|
193
165
|
end
|
|
194
166
|
|
|
195
|
-
def status
|
|
196
|
-
exception.nil? ? 0 : 1
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# jump to pc
|
|
200
|
-
# only valid if current op code is allowed to modify pc
|
|
201
|
-
def jump_to(pc)
|
|
202
|
-
@jump_to = pc
|
|
203
|
-
end
|
|
204
|
-
|
|
205
167
|
def add_log_entry(topics, log_data)
|
|
206
168
|
sub_state.log_series << LogEntry.new(address: instruction.address, topics: topics, data: log_data)
|
|
207
169
|
end
|
|
@@ -209,50 +171,23 @@ module Ciri
|
|
|
209
171
|
# transact value from sender to target address
|
|
210
172
|
def transact(sender:, value:, to:)
|
|
211
173
|
sender_account = find_account(sender)
|
|
212
|
-
to_account = find_account(to)
|
|
213
|
-
|
|
214
174
|
raise VMError.new("balance not enough") if sender_account.balance < value
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
to_account.balance += value
|
|
218
|
-
|
|
219
|
-
state.set_nonce(sender, sender_account.nonce)
|
|
220
|
-
state.set_balance(sender, sender_account.balance)
|
|
221
|
-
state.set_balance(to, to_account.balance)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# call instruction
|
|
225
|
-
def call_instruction(new_instruction)
|
|
226
|
-
origin_instruction = instruction
|
|
227
|
-
origin_pc = pc
|
|
228
|
-
@instruction = new_instruction
|
|
229
|
-
@machine_state.pc = 0
|
|
230
|
-
|
|
231
|
-
return_value = yield
|
|
232
|
-
|
|
233
|
-
@instruction = origin_instruction
|
|
234
|
-
@machine_state.pc = origin_pc
|
|
235
|
-
# clear up state
|
|
236
|
-
@exception = nil
|
|
237
|
-
@output = ''.b
|
|
238
|
-
return_value
|
|
175
|
+
state.add_balance(sender, -value)
|
|
176
|
+
state.add_balance(to, value)
|
|
239
177
|
end
|
|
240
178
|
|
|
241
179
|
# Execute instruction with states
|
|
242
180
|
# Ξ(σ,g,I,T) ≡ (σ′,μ′ ,A,o)
|
|
243
181
|
def execute
|
|
244
182
|
loop do
|
|
245
|
-
if
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
end
|
|
250
|
-
return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, EMPTY_SET]
|
|
251
|
-
elsif get_op(machine_state.pc) == OP::REVERT
|
|
183
|
+
if exception || set_exception(check_exception(@state, machine_state, instruction))
|
|
184
|
+
debug("check exception: #{exception}")
|
|
185
|
+
return
|
|
186
|
+
elsif get_op(pc) == OP::REVERT
|
|
252
187
|
o = halt
|
|
253
|
-
return
|
|
188
|
+
return
|
|
254
189
|
elsif (o = halt) != EMPTY_SET
|
|
255
|
-
return
|
|
190
|
+
return
|
|
256
191
|
else
|
|
257
192
|
operate
|
|
258
193
|
next
|
|
@@ -260,55 +195,60 @@ module Ciri
|
|
|
260
195
|
end
|
|
261
196
|
end
|
|
262
197
|
|
|
198
|
+
def with_context(new_context)
|
|
199
|
+
origin_context = execution_context
|
|
200
|
+
@execution_context = new_context
|
|
201
|
+
return_value = yield
|
|
202
|
+
@execution_context = origin_context
|
|
203
|
+
return_value
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def extend_memory(pos, size)
|
|
207
|
+
machine_state.extend_memory(execution_context, pos, size)
|
|
208
|
+
end
|
|
209
|
+
|
|
263
210
|
private
|
|
264
211
|
|
|
265
212
|
# O(σ, μ, A, I) ≡ (σ′, μ′, A′, I)
|
|
266
213
|
def operate
|
|
267
|
-
|
|
268
|
-
w = get_op(ms.pc)
|
|
214
|
+
w = get_op(pc)
|
|
269
215
|
operation = OP.get(w)
|
|
270
216
|
|
|
271
|
-
raise "can't find operation #{w}, pc #{
|
|
217
|
+
raise "can't find operation #{w}, pc #{pc}" unless operation
|
|
272
218
|
|
|
273
|
-
op_cost =
|
|
274
|
-
old_memory_cost = fork_config.gas_of_memory(ms.memory_item)
|
|
275
|
-
ms.consume_gas op_cost
|
|
219
|
+
op_cost, op_refund = fork_schema.gas_of_operation(self)
|
|
276
220
|
|
|
277
|
-
|
|
221
|
+
debug("depth: #{depth} pc: #{pc} #{operation.name} gas: #{op_cost} stack: #{stack.size} logs: #{sub_state.log_series.size}")
|
|
278
222
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
# calculate gas_cost
|
|
282
|
-
new_memory_cost = fork_config.gas_of_memory(ms.memory_item)
|
|
283
|
-
memory_gas_cost = new_memory_cost - old_memory_cost
|
|
223
|
+
consume_gas op_cost
|
|
224
|
+
refund_gas op_refund if op_refund && op_refund > 0
|
|
284
225
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
226
|
+
# call operation
|
|
227
|
+
begin
|
|
228
|
+
operation.call(self)
|
|
229
|
+
rescue VMError => e
|
|
230
|
+
set_exception(e)
|
|
290
231
|
end
|
|
291
232
|
|
|
292
233
|
# revert sub_state and return if exception occur
|
|
293
234
|
if exception
|
|
294
|
-
|
|
235
|
+
execution_context.revert
|
|
295
236
|
return
|
|
296
237
|
end
|
|
297
238
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
end
|
|
239
|
+
set_pc case
|
|
240
|
+
when w == OP::JUMP
|
|
241
|
+
jump_pc
|
|
242
|
+
when w == OP::JUMPI && jump_pc
|
|
243
|
+
jump_pc
|
|
244
|
+
else
|
|
245
|
+
next_valid_instruction_pos(pc, w)
|
|
246
|
+
end
|
|
307
247
|
end
|
|
308
248
|
|
|
309
249
|
# determinate halt or not halt
|
|
310
250
|
def halt
|
|
311
|
-
w = get_op(
|
|
251
|
+
w = get_op(pc)
|
|
312
252
|
if w == OP::RETURN || w == OP::REVERT
|
|
313
253
|
operate
|
|
314
254
|
output
|
|
@@ -324,27 +264,25 @@ module Ciri
|
|
|
324
264
|
|
|
325
265
|
# check status
|
|
326
266
|
def check_exception(state, ms, instruction)
|
|
327
|
-
w = instruction.get_op(
|
|
267
|
+
w = instruction.get_op(pc)
|
|
328
268
|
case
|
|
329
|
-
when w == OP::INVALID
|
|
330
|
-
InvalidOpCodeError.new "can't find op code #{w}"
|
|
331
|
-
when OP.input_count(w).nil?
|
|
332
|
-
InvalidOpCodeError.new "can't find op code #{w}"
|
|
269
|
+
when w == OP::INVALID || OP.input_count(w).nil?
|
|
270
|
+
InvalidOpCodeError.new "can't find op code 0x#{w.to_s(16)} pc: #{pc}"
|
|
333
271
|
when ms.stack.size < (consume = OP.input_count(w))
|
|
334
272
|
StackError.new "stack not enough: stack:#{ms.stack.size} next consume: #{consume}"
|
|
335
|
-
when
|
|
336
|
-
GasNotEnoughError.new "gas not enough: gas remain:#{
|
|
337
|
-
when w == OP::JUMP &&
|
|
273
|
+
when remain_gas < (gas_cost = fork_schema.gas_of_operation(self).yield_self {|gas_cost, _| gas_cost})
|
|
274
|
+
GasNotEnoughError.new "gas not enough: gas remain:#{remain_gas} gas cost: #{gas_cost}"
|
|
275
|
+
when w == OP::JUMP && !destinations.include?(ms.get_stack(0, Integer))
|
|
338
276
|
InvalidJumpError.new "invalid jump dest #{ms.get_stack(0, Integer)}"
|
|
339
|
-
when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 &&
|
|
277
|
+
when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 && !destinations.include?(ms.get_stack(0, Integer))
|
|
340
278
|
InvalidJumpError.new "invalid condition jump dest #{ms.get_stack(0, Integer)}"
|
|
341
279
|
when w == OP::RETURNDATACOPY && ms.get_stack(1, Integer) + ms.get_stack(2, Integer) > ms.output.size
|
|
342
280
|
ReturnError.new "return data copy error"
|
|
343
|
-
when stack.size - OP.input_count(w) + OP.output_count(w) >
|
|
344
|
-
StackError.new "stack size reach
|
|
281
|
+
when stack.size - OP.input_count(w) + OP.output_count(w) > stack_size
|
|
282
|
+
StackError.new "stack size reach #{stack_size} limit"
|
|
345
283
|
# A condition in yellow paper but I can't understand..: (¬Iw ∧W(w,μ))
|
|
346
|
-
when
|
|
347
|
-
StackError.new "call depth reach
|
|
284
|
+
when depth > max_depth
|
|
285
|
+
StackError.new "call depth reach #{max_depth} limit"
|
|
348
286
|
else
|
|
349
287
|
nil
|
|
350
288
|
end
|