ciri 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,140 @@
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_relative 'evm/op'
25
+ require_relative 'evm/vm'
26
+ require_relative 'evm/account'
27
+ require 'ciri/forks'
28
+
29
+ module Ciri
30
+ class EVM
31
+
32
+ BLOCK_REWARD = 3 * 10.pow(18) # 3 ether
33
+
34
+ attr_reader :state
35
+
36
+ def initialize(state:)
37
+ @state = state
38
+ end
39
+
40
+ # run block
41
+ def finalize_block(block)
42
+ # validate block
43
+ # transition
44
+ # apply_changes
45
+ end
46
+
47
+ def validate_block(block)
48
+ # valid ommers
49
+ # valid transactions(block.gas_used == transactions...gas)
50
+ # Reward miner, ommers(reward == block reward + ommers reward)
51
+ # apply changes
52
+ # verify state and block nonce
53
+ # 1. parent header root == trie(state[i]) 当前状态的 root 相等, 返回 state[i] otherwise state[0]
54
+ end
55
+
56
+ # transition block
57
+ # block -> new block(mining)
58
+ # return new_block and status change
59
+ def transition(block)
60
+ # execute transactions, we don't need to valid transactions, it should be done before evm(in Chain module).
61
+ block.transactions.each do |transaction|
62
+ execute_transaction(transaction, header: block.header)
63
+ end
64
+ # status transfer
65
+ # state[c].balance += mining reward
66
+ # ommers: state[u.c].balance += uncle reward
67
+ #
68
+ # block.nonce
69
+ # block.mix
70
+ # R[i].gas_used = gas_used(state[i - 1], block.transactions[i]) + R[i - 1].gas_used
71
+ # R[i].logs = logs(state[i - 1], block.transactions[i])
72
+ # R[i].z = z(state[i - 1], block.transactions[i])
73
+ end
74
+
75
+ # execute transaction
76
+ # @param t Transaction
77
+ # @param header Chain::Header
78
+ def execute_transaction(t, header: nil, block_info: nil, ignore_exception: false)
79
+ instruction = Instruction.new(
80
+ origin: t.sender,
81
+ price: t.gas_price,
82
+ sender: t.sender,
83
+ value: t.value,
84
+ header: header,
85
+ execute_depth: 0,
86
+ )
87
+
88
+ if t.contract_creation?
89
+ instruction.bytes_code = t.data
90
+ instruction.address = t.sender
91
+ else
92
+ if (account = find_account t.to)
93
+ instruction.bytes_code = account.code
94
+ instruction.address = account.address
95
+ end
96
+ instruction.data = t.data
97
+ end
98
+
99
+ @vm = VM.spawn(
100
+ state: state,
101
+ gas_limit: t.gas_limit,
102
+ instruction: instruction,
103
+ header: header,
104
+ block_info: block_info,
105
+ fork_config: Ciri::Forks.detect_fork(header: header, number: block_info&.number)
106
+ )
107
+
108
+ if t.contract_creation?
109
+ # contract creation
110
+ @vm.create_contract(value: instruction.value, init: instruction.bytes_code)
111
+ else
112
+ # transact ether
113
+ begin
114
+ @vm.transact(sender: t.sender, value: t.value, to: t.to)
115
+ rescue VM::VMError
116
+ raise unless ignore_exception
117
+ return nil
118
+ end
119
+ @vm.run(ignore_exception: ignore_exception)
120
+ end
121
+ nil
122
+ end
123
+
124
+ def logs_hash
125
+ return nil unless @vm
126
+ Utils.sha3(RLP.encode_simple(@vm.sub_state.log_series))
127
+ end
128
+
129
+ private
130
+
131
+ def account_dead?(address)
132
+ Account.account_dead?(state, address)
133
+ end
134
+
135
+ def find_account(address)
136
+ Account.find_account(state, address)
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,50 @@
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/utils'
25
+
26
+ module Ciri
27
+ class EVM
28
+
29
+ Account = Struct.new(:address, :balance, :code, :nonce, :storage, keyword_init: true) do
30
+ # EMPTY(σ,a) ≡ σ[a]c =KEC􏰁()􏰂∧σ[a]n =0∧σ[a]b =0
31
+ def empty?
32
+ code == Utils::BLANK_SHA3 && nonce == 0 && balance == 0
33
+ end
34
+
35
+ def self.new_empty(address)
36
+ Account.new(address: address, balance: 0, nonce: 0, storage: {})
37
+ end
38
+
39
+ def self.find_account(state, address)
40
+ state[address.to_s] || new_empty(address.to_s)
41
+ end
42
+
43
+ def self.account_dead?(state, address)
44
+ account = state[address.to_s]
45
+ account.nil? || account.empty?
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,31 @@
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 EVM
26
+
27
+ # Block Info
28
+ BlockInfo = Struct.new(:coinbase, :difficulty, :gas_limit, :number, :timestamp, keyword_init: true)
29
+
30
+ end
31
+ end
@@ -0,0 +1,183 @@
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/evm/op'
25
+
26
+ module Ciri
27
+ class EVM
28
+ module Forks
29
+ module Frontier
30
+
31
+ module Cost
32
+ # fee schedule, start with G
33
+ G_ZERO = 0
34
+ G_BASE = 2
35
+ G_VERYLOW = 3
36
+ G_LOW = 5
37
+ G_MID = 8
38
+ G_HIGH = 10
39
+ G_EXTCODE = 700
40
+ G_BALANCE = 400
41
+ G_SLOAD = 50
42
+ G_JUMPDEST = 1
43
+ G_SSET = 20000
44
+ G_RESET = 5000
45
+ R_SCLEAR = 15000
46
+ R_SELFDESTRUCT = 24000
47
+ G_SELFDESTRUCT = 0
48
+ G_CREATE = 32000
49
+ G_CODEDEPOSIT = 200
50
+ G_CALL = 700
51
+ G_CALLVALUE = 9000
52
+ G_CALLSTIPEND = 2300
53
+ G_NEWACCOUNT = 25000
54
+ G_EXP = 10
55
+ G_EXPBYTE = 10
56
+ G_MEMORY = 3
57
+ G_TXCREATE = 32000
58
+ G_TXDATAZERO = 4
59
+ G_TXDATANONZERO = 68
60
+ G_TRANSACTION = 21000
61
+ G_LOG = 375
62
+ G_LOGDATA = 8
63
+ G_TOPIC = 375
64
+ G_SHA3 = 30
65
+ G_SHA3WORD = 6
66
+ G_COPY = 3
67
+ G_BLOCKHASH = 20
68
+ G_QUADDIVISOR = 100
69
+
70
+ # operation code by group, for later calculation
71
+ W_ZERO = [OP::STOP, OP::RETURN, OP::REVERT]
72
+ W_BASE = [OP::ADDRESS, OP::ORIGIN, OP::CALLER, OP::CALLVALUE, OP::CALLDATASIZE, OP::CODESIZE, OP::GASPRICE,
73
+ OP::COINBASE, OP::TIMESTAMP, OP::NUMBER, OP::DIFFICULTY, OP::GASLIMIT, OP::RETURNDATASIZE,
74
+ OP::POP, OP::PC, OP::MSIZE, OP::GAS]
75
+ W_VERYLOW = [OP::ADD, OP::SUB, OP::NOT, OP::LT, OP::GT, OP::SLT, OP::SGT, OP::EQ, OP::ISZERO, OP::AND, OP::OR,
76
+ OP::XOR, OP::BYTE, OP::CALLDATALOAD, OP::MLOAD, OP::MSTORE, OP::MSTORE8,
77
+ *(1..32).map {|i| OP.get(OP::PUSH1 + i - 1).code}, # push1 - push32
78
+ *(1..16).map {|i| OP.get(OP::DUP1 + i - 1).code}, # dup1 - dup16
79
+ *(1..16).map {|i| OP.get(OP::SWAP1 + i - 1).code}] # swap1 - swap16
80
+ W_LOW = [OP::MUL, OP::DIV, OP::SDIV, OP::MOD, OP::SMOD, OP::SIGNEXTEND]
81
+ W_MID = [OP::ADDMOD, OP::MULMOD, OP::JUMP]
82
+ W_HIGH = [OP::JUMPI]
83
+ W_EXTCODE = [OP::EXTCODESIZE]
84
+
85
+
86
+ class << self
87
+ # C(σ,μ,I)
88
+ # calculate cost of current operation
89
+ def cost_of_operation(vm)
90
+ ms = vm.machine_state
91
+ instruction = vm.instruction
92
+ w = instruction.get_op(ms.pc)
93
+ if w == OP::SSTORE
94
+ cost_of_sstore(vm)
95
+ elsif w == OP::EXP && ms.get_stack(1, Integer) == 0
96
+ G_EXP
97
+ elsif w == OP::EXP && (x = ms.get_stack(1, Integer)) > 0
98
+ G_EXP + G_EXPBYTE * Utils.ceil_div(x.bit_length, 8)
99
+ elsif w == OP::CALLDATACOPY || w == OP::CODECOPY || w == OP::RETURNDATACOPY
100
+ G_VERYLOW + G_COPY * Utils.ceil_div(ms.get_stack(2, Integer), 32)
101
+ elsif w == OP::EXTCODECOPY
102
+ G_EXTCODE + G_COPY * Utils.ceil_div(ms.get_stack(3, Integer), 32)
103
+ elsif (OP::LOG0..OP::LOG4).include? w
104
+ G_LOG + G_LOGDATA * ms.get_stack(1, Integer) + (w - OP::LOG0) * G_TOPIC
105
+ elsif w == OP::CALL || w == OP::CALLCODE || w == OP::DELEGATECALL
106
+ 1# cost_of_call(state, ms)
107
+ elsif w == OP::SELFDESTRUCT
108
+ cost_of_self_destruct(vm)
109
+ elsif w == OP::CREATE
110
+ G_CREATE
111
+ elsif w == OP::SHA3
112
+ G_SHA3 + G_SHA3WORD * Utils.ceil_div(ms.get_stack(1, Integer), 32)
113
+ elsif w == OP::JUMPDEST
114
+ G_JUMPDEST
115
+ elsif w == OP::SLOAD
116
+ G_SLOAD
117
+ elsif W_ZERO.include? w
118
+ G_ZERO
119
+ elsif W_BASE.include? w
120
+ G_BASE
121
+ elsif W_VERYLOW.include? w
122
+ G_VERYLOW
123
+ elsif W_LOW.include? w
124
+ G_LOW
125
+ elsif W_MID.include? w
126
+ G_MID
127
+ elsif W_HIGH.include? w
128
+ G_HIGH
129
+ elsif W_EXTCODE.include? w
130
+ G_EXTCODE
131
+ elsif w == OP::BALANCE
132
+ G_BALANCE
133
+ elsif w == OP::BLOCKHASH
134
+ G_BLOCKHASH
135
+ else
136
+ raise "can't compute cost for unknown op #{w}"
137
+ end
138
+ end
139
+
140
+ def cost_of_memory(i)
141
+ G_MEMORY * i + (i ** 2) / 512
142
+ end
143
+
144
+ def intrinsic_gas_of_transaction(t)
145
+ gas = (t.data.each_byte || '').reduce(0) {|sum, i| sum + i.zero? ? G_TXDATAZERO : G_TXDATANONZERO}
146
+ gas + (t.to.empty? ? G_TXCREATE : 0) + G_TRANSACTION
147
+ end
148
+
149
+ private
150
+
151
+ def cost_of_self_destruct(vm)
152
+ G_SELFDESTRUCT
153
+ end
154
+
155
+ def cost_of_call
156
+
157
+ end
158
+
159
+ def cost_of_sstore(vm)
160
+ ms = vm.machine_state
161
+ instruction = vm.instruction
162
+
163
+ key = ms.stack[0]
164
+ value = ms.stack[1]
165
+ account = vm.find_account instruction.address
166
+
167
+ value_exists = !Ciri::Utils.blank_binary?(value)
168
+ has_key = account && account.storage[key]
169
+
170
+ if value_exists && !has_key
171
+ G_SSET
172
+ else
173
+ G_RESET
174
+ end
175
+ end
176
+
177
+ end
178
+ end
179
+
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,92 @@
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 EVM
26
+
27
+ # represent instruction
28
+ Instruction = Struct.new(:address, :origin, :price, :data, :sender, :value, :bytes_code, :header, :execute_depth,
29
+ keyword_init: true) do
30
+
31
+ def initialize(*args)
32
+ super
33
+ self.data ||= ''.b
34
+ self.value ||= 0
35
+ self.bytes_code ||= ''.b
36
+ self.execute_depth ||= 0
37
+ end
38
+
39
+ def get_op(pos)
40
+ return OP::INVALID if pos >= (bytes_code || ''.b).size
41
+ bytes_code[pos].ord
42
+ end
43
+
44
+ # get data from instruction
45
+ def get_code(pos, size = 1)
46
+ if size > 0 && pos < bytes_code.size && pos + size - 1 < bytes_code.size
47
+ bytes_code[pos..(pos + size - 1)]
48
+ else
49
+ "\x00".b * size
50
+ end
51
+ end
52
+
53
+ def get_data(pos, size)
54
+ if pos < data.size && size > 0
55
+ data[pos..(pos + size - 1)].ljust(size, "\x00".b)
56
+ else
57
+ "\x00".b * size
58
+ end
59
+ end
60
+
61
+ # valid destinations of bytes_code
62
+ def destinations
63
+ @destinations ||= destinations_by_index(bytes_code, 0)
64
+ end
65
+
66
+ def next_valid_instruction_pos(pos, op_code)
67
+ if (OP::PUSH1..OP::PUSH32).include?(op_code)
68
+ pos + op_code - OP::PUSH1 + 2
69
+ else
70
+ pos + 1
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def destinations_by_index(bytes_code, i)
77
+ destinations = []
78
+ loop do
79
+ if i > bytes_code.size
80
+ break
81
+ elsif bytes_code[i] == OP::JUMPDEST
82
+ destinations << i
83
+ end
84
+ i = next_valid_instruction_pos(i, bytes_code[i])
85
+ end
86
+ destinations
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end