ciri 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -3
  3. data/README.md +5 -0
  4. data/Rakefile +24 -2
  5. data/ciri-rlp/.gitignore +11 -0
  6. data/ciri-rlp/.rspec +3 -0
  7. data/ciri-rlp/.travis.yml +5 -0
  8. data/ciri-rlp/CODE_OF_CONDUCT.md +74 -0
  9. data/ciri-rlp/Gemfile +6 -0
  10. data/ciri-rlp/Gemfile.lock +39 -0
  11. data/ciri-rlp/LICENSE.txt +21 -0
  12. data/ciri-rlp/README.md +98 -0
  13. data/ciri-rlp/Rakefile +6 -0
  14. data/ciri-rlp/bin/console +14 -0
  15. data/ciri-rlp/bin/setup +8 -0
  16. data/ciri-rlp/ciri-rlp.gemspec +28 -0
  17. data/{lib → ciri-rlp/lib}/ciri/rlp.rb +2 -2
  18. data/ciri-rlp/lib/ciri/rlp/decode.rb +144 -0
  19. data/ciri-rlp/lib/ciri/rlp/encode.rb +140 -0
  20. data/ciri-rlp/lib/ciri/rlp/serializable.rb +241 -0
  21. data/ciri-rlp/lib/ciri/rlp/version.rb +5 -0
  22. data/ciri-rlp/spec/ciri/fixture_spec.rb +95 -0
  23. data/ciri-rlp/spec/ciri/rlp/decode_spec.rb +68 -0
  24. data/ciri-rlp/spec/ciri/rlp/encode_spec.rb +72 -0
  25. data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +147 -0
  26. data/ciri-rlp/spec/ciri/rlp_spec.rb +5 -0
  27. data/ciri-rlp/spec/spec_helper.rb +14 -0
  28. data/ciri-utils/.gitignore +11 -0
  29. data/ciri-utils/.rspec +3 -0
  30. data/ciri-utils/.travis.yml +5 -0
  31. data/ciri-utils/CODE_OF_CONDUCT.md +74 -0
  32. data/ciri-utils/Gemfile +6 -0
  33. data/ciri-utils/Gemfile.lock +37 -0
  34. data/ciri-utils/LICENSE.txt +21 -0
  35. data/ciri-utils/README.md +61 -0
  36. data/ciri-utils/Rakefile +6 -0
  37. data/ciri-utils/bin/console +14 -0
  38. data/ciri-utils/bin/setup +8 -0
  39. data/ciri-utils/ciri-utils.gemspec +28 -0
  40. data/{lib → ciri-utils/lib}/ciri/utils.rb +9 -33
  41. data/{lib → ciri-utils/lib}/ciri/utils/logger.rb +0 -0
  42. data/{lib → ciri-utils/lib}/ciri/utils/number.rb +15 -12
  43. data/ciri-utils/lib/ciri/utils/version.rb +5 -0
  44. data/ciri-utils/spec/ciri/utils_spec.rb +5 -0
  45. data/ciri-utils/spec/spec_helper.rb +14 -0
  46. data/ciri.gemspec +6 -3
  47. data/lib/ciri/bloom_filter.rb +83 -0
  48. data/lib/ciri/chain.rb +67 -130
  49. data/lib/ciri/chain/header.rb +2 -2
  50. data/lib/ciri/chain/header_chain.rb +136 -0
  51. data/lib/ciri/chain/transaction.rb +1 -1
  52. data/lib/ciri/crypto.rb +2 -2
  53. data/lib/ciri/db/account_db.rb +145 -0
  54. data/lib/ciri/db/backend/memory.rb +24 -1
  55. data/lib/ciri/devp2p/peer.rb +1 -1
  56. data/lib/ciri/devp2p/rlpx/connection.rb +5 -5
  57. data/lib/ciri/eth/peer.rb +3 -3
  58. data/lib/ciri/eth/protocol_manage.rb +3 -3
  59. data/lib/ciri/eth/protocol_messages.rb +27 -26
  60. data/lib/ciri/ethash.rb +18 -3
  61. data/lib/ciri/evm.rb +101 -56
  62. data/lib/ciri/{utils/lib_c.rb → evm/errors.rb} +28 -18
  63. data/lib/ciri/evm/instruction.rb +3 -1
  64. data/lib/ciri/evm/{serialize.rb → log_entry.rb} +9 -27
  65. data/lib/ciri/evm/machine_state.rb +21 -2
  66. data/lib/ciri/evm/op.rb +19 -16
  67. data/lib/ciri/evm/state.rb +61 -0
  68. data/lib/ciri/evm/vm.rb +78 -102
  69. data/lib/ciri/forks.rb +1 -4
  70. data/lib/ciri/forks/base.rb +55 -0
  71. data/lib/ciri/forks/frontier.rb +37 -10
  72. data/lib/ciri/forks/frontier/cost.rb +186 -0
  73. data/lib/ciri/key.rb +1 -1
  74. data/lib/ciri/pow.rb +1 -1
  75. data/lib/ciri/rlp/decode.rb +6 -3
  76. data/lib/ciri/rlp/encode.rb +10 -10
  77. data/lib/ciri/rlp/serializable.rb +12 -9
  78. data/lib/ciri/serialize.rb +58 -0
  79. data/lib/ciri/trie.rb +362 -0
  80. data/lib/ciri/trie/nibbles.rb +97 -0
  81. data/lib/ciri/trie/nodes.rb +187 -0
  82. data/lib/ciri/{evm → types}/account.rb +20 -13
  83. data/lib/ciri/types/address.rb +16 -11
  84. data/lib/ciri/types/number.rb +73 -0
  85. data/lib/ciri/types/receipt.rb +57 -0
  86. data/lib/ciri/version.rb +1 -1
  87. metadata +82 -19
  88. data/lib/ciri/evm/forks/frontier.rb +0 -183
data/lib/ciri/chain.rb CHANGED
@@ -22,9 +22,13 @@
22
22
 
23
23
 
24
24
  require 'forwardable'
25
+ require 'ciri/evm'
26
+ require 'ciri/utils/logger'
27
+ require_relative 'chain/header_chain'
25
28
  require_relative 'chain/block'
26
29
  require_relative 'chain/header'
27
30
  require_relative 'chain/transaction'
31
+ require_relative 'types/receipt'
28
32
  require_relative 'pow'
29
33
 
30
34
  module Ciri
@@ -32,6 +36,7 @@ module Ciri
32
36
  # Chain manipulate logic
33
37
  # store via rocksdb
34
38
  class Chain
39
+ include Utils::Logger
35
40
 
36
41
  class Error < StandardError
37
42
  end
@@ -42,151 +47,75 @@ module Ciri
42
47
  class InvalidBlockError < Error
43
48
  end
44
49
 
45
- # HeaderChain
46
- # store headers
47
- class HeaderChain
48
- HEAD = 'head'
49
- GENESIS = 'genesis'
50
- HEADER_PREFIX = 'h'
51
- TD_SUFFIX = 't'
52
- NUM_SUFFIX = 'n'
53
-
54
- attr_reader :store, :byzantium_block, :homestead_block
55
-
56
- def initialize(store, byzantium_block: nil, homestead_block: nil)
57
- @store = store
58
- @byzantium_block = byzantium_block
59
- @homestead_block = homestead_block
60
- end
61
-
62
- def head
63
- encoded = store[HEAD]
64
- encoded && Header.rlp_decode!(encoded)
65
- end
50
+ extend Forwardable
66
51
 
67
- def head=(header)
68
- store[HEAD] = header.rlp_encode!
69
- end
52
+ BODY_PREFIX = 'b'
70
53
 
71
- def get_header(hash)
72
- encoded = store[HEADER_PREFIX + hash]
73
- encoded && Header.rlp_decode!(encoded)
74
- end
54
+ def_delegators :@header_chain, :head, :total_difficulty, :get_header_by_number, :get_header
75
55
 
76
- def get_header_by_number(number)
77
- hash = get_header_hash_by_number(number)
78
- hash && get_header(hash)
79
- end
56
+ attr_reader :genesis, :network_id, :store, :header_chain
80
57
 
81
- def valid?(header)
82
- # ignore genesis header if there not exist one
83
- return true if header.number == 0 && get_header_by_number(0).nil?
84
-
85
- parent_header = get_header(header.parent_hash)
86
- return false unless parent_header
87
- # check height
88
- return false unless parent_header.number + 1 == header.number
89
- # check timestamp
90
- return false unless parent_header.timestamp < header.timestamp
91
-
92
- # check gas limit range
93
- parent_gas_limit = parent_header.gas_limit
94
- gas_limit_max = parent_gas_limit + parent_gas_limit / 1024
95
- gas_limit_min = parent_gas_limit - parent_gas_limit / 1024
96
- gas_limit = header.gas_limit
97
- return false unless gas_limit >= 5000 && gas_limit > gas_limit_min && gas_limit < gas_limit_max
98
- return false unless calculate_difficulty(header, parent_header) == header.difficulty
99
-
100
- # check pow
101
- begin
102
- POW.check_pow(header.number, header.mining_hash, header.mix_hash, header.nonce, header.difficulty)
103
- rescue POW::InvalidError
104
- return false
105
- end
58
+ def initialize(store, genesis:, network_id:, evm: nil, byzantium_block: nil, homestead_block: nil)
59
+ @store = store
60
+ @header_chain = HeaderChain.new(store, byzantium_block: byzantium_block, homestead_block: homestead_block)
61
+ @genesis = genesis
62
+ @network_id = network_id
63
+ @evm = evm
64
+ load_or_init_store
65
+ end
106
66
 
107
- true
108
- end
67
+ # run block
68
+ def import_block(block)
69
+ validate_block(block)
70
+ write_block(block)
109
71
 
110
- # calculate header difficulty
111
- # you can find explain in Ethereum yellow paper: Block Header Validity section.
112
- def calculate_difficulty(header, parent_header)
113
- return header.difficulty if header.number == 0
114
-
115
- x = parent_header.difficulty / 2048
116
- y = header.ommers_hash == Utils::BLANK_SHA3 ? 1 : 2
117
-
118
- # handle byzantium fork
119
- # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-609.md
120
- # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-100.md
121
- byzantium_fork = byzantium_block && header.number > byzantium_block
122
- # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-2.md
123
- homestead_fork = homestead_block && header.number > homestead_block
124
-
125
- time_factor = if byzantium_fork
126
- [y - (header.timestamp - parent_header.timestamp) / 9, -99].max
127
- elsif homestead_fork
128
- [1 - (header.timestamp - parent_header.timestamp) / 10, -99].max
129
- else
130
- (header.timestamp - parent_header.timestamp) < 13 ? 1 : -1
131
- end
132
-
133
- # difficulty bomb
134
- height = byzantium_fork ? [(header.number - 3000000), 0].max : header.number
135
- height_factor = 2 ** (height / 100000 - 2)
136
-
137
- difficulty = (parent_header.difficulty + x * time_factor + height_factor).to_i
138
- [header.difficulty, difficulty].max
139
- end
72
+ # update state
73
+ # apply_changes
74
+ end
140
75
 
141
- # write header
142
- def write(header)
143
- hash = header.get_hash
144
- # get total difficulty
145
- td = if header.number == 0
146
- header.difficulty
147
- else
148
- parent_header = get_header(header.parent_hash)
149
- raise "can't find parent from db" unless parent_header
150
- parent_td = total_difficulty(parent_header.get_hash)
151
- parent_td + header.difficulty
152
- end
153
- # write header and td
154
- store.batch do |b|
155
- b.put(HEADER_PREFIX + hash, header.rlp_encode!)
156
- b.put(HEADER_PREFIX + hash + TD_SUFFIX, RLP.encode(td, Integer))
76
+ # validate block, effect current state
77
+ def validate_block(block)
78
+ raise InvalidBlockError.new('invalid header') unless @header_chain.valid?(block.header)
79
+ # valid ommers
80
+ raise InvalidBlockError.new('ommers blocks can not more than 2') if block.ommers.size > 2
81
+ block.ommers.each do |ommer|
82
+ unless is_kin?(ommer, get_block(block.header.parent_hash), 6)
83
+ raise InvalidBlockError.new("invalid ommer relation")
157
84
  end
158
85
  end
159
86
 
160
- def write_header_hash_number(header_hash, number)
161
- enc_number = Utils.big_endian_encode number
162
- store[HEADER_PREFIX + enc_number + NUM_SUFFIX] = header_hash
87
+ # valid transactions and gas
88
+ begin
89
+ receipts = @evm.transition(block)
90
+ rescue EVM::InvalidTransition => e
91
+ raise InvalidBlockError.new(e.message)
163
92
  end
164
93
 
165
- def get_header_hash_by_number(number)
166
- enc_number = Utils.big_endian_encode number
167
- store[HEADER_PREFIX + enc_number + NUM_SUFFIX]
94
+ # verify state root
95
+ if @evm.state_root != block.header.state_root
96
+ error("incorrect state_root, evm: #{Utils.to_hex @evm.state_root}, header: #{Utils.to_hex block.header.state_root} height: #{block.header.number}")
97
+ raise InvalidBlockError.new("incorrect state_root")
168
98
  end
169
99
 
170
- def total_difficulty(header_hash = head.nil? ? nil : head.get_hash)
171
- return 0 if header_hash.nil?
172
- RLP.decode(store[HEADER_PREFIX + header_hash + TD_SUFFIX], Integer)
100
+ # verify receipts root
101
+ trie = Trie.new
102
+ receipts.each_with_index do |r, i|
103
+ trie[RLP.encode(i)] = RLP.encode(r)
173
104
  end
174
- end
175
-
176
- extend Forwardable
177
105
 
178
- BODY_PREFIX = 'b'
179
-
180
- def_delegators :@header_chain, :head, :total_difficulty, :get_header_by_number, :get_header
106
+ if trie.root_hash != block.header.receipts_root
107
+ raise InvalidBlockError.new("incorrect receipts_root")
108
+ end
181
109
 
182
- attr_reader :genesis, :network_id, :store, :header_chain
110
+ # verify state root
111
+ trie = Trie.new
112
+ block.transactions.each_with_index do |t, i|
113
+ trie[RLP.encode(i)] = RLP.encode(t)
114
+ end
183
115
 
184
- def initialize(store, genesis:, network_id:, byzantium_block: nil, homestead_block: nil)
185
- @store = store
186
- @header_chain = HeaderChain.new(store, byzantium_block: byzantium_block, homestead_block: homestead_block)
187
- @genesis = genesis
188
- @network_id = network_id
189
- load_or_init_store
116
+ if trie.root_hash != block.header.transactions_root
117
+ raise InvalidBlockError.new("incorrect transactions_root")
118
+ end
190
119
  end
191
120
 
192
121
  def genesis_hash
@@ -223,17 +152,17 @@ module Ciri
223
152
 
224
153
  def get_block(hash)
225
154
  encoded = store[BODY_PREFIX + hash]
226
- encoded && Block.rlp_decode!(encoded)
155
+ encoded && Block.rlp_decode(encoded)
227
156
  end
228
157
 
229
158
  def write_block(block)
230
159
  # write header
231
160
  header = block.header
232
- raise InvalidHeaderError.new("invalid header: #{header.number}") unless @header_chain.valid?(header)
161
+ # raise InvalidHeaderError.new("invalid header: #{header.number}") unless @header_chain.valid?(header)
233
162
  @header_chain.write(header)
234
163
 
235
164
  # write body
236
- store[BODY_PREFIX + header.get_hash] = block.rlp_encode!
165
+ store[BODY_PREFIX + header.get_hash] = block.rlp_encode
237
166
 
238
167
  td = total_difficulty(header.get_hash)
239
168
 
@@ -251,6 +180,14 @@ module Ciri
251
180
 
252
181
  private
253
182
 
183
+ def is_kin?(ommer, parent, n)
184
+ return false if n == 0
185
+ return true if get_header(ommer.parent_hash) == get_header(parent.header.parent_hash) &&
186
+ ommer != parent.header &&
187
+ !parent.ommers.include?(ommer)
188
+ is_kin?(ommer, get_block(parent.header.parent_hash), n - 1)
189
+ end
190
+
254
191
  # reorg chain
255
192
  def reorg_chain(new_block, old_block)
256
193
  new_chain = []
@@ -48,12 +48,12 @@ module Ciri
48
48
 
49
49
  # header hash
50
50
  def get_hash
51
- Utils.sha3(rlp_encode!)
51
+ Utils.sha3(rlp_encode)
52
52
  end
53
53
 
54
54
  # mining_hash, used for mining
55
55
  def mining_hash
56
- Utils.sha3(rlp_encode! skip_keys: [:mix_hash, :nonce])
56
+ Utils.sha3(rlp_encode skip_keys: [:mix_hash, :nonce])
57
57
  end
58
58
 
59
59
  end
@@ -0,0 +1,136 @@
1
+ # HeaderChain
2
+ # store headers
3
+
4
+ module Ciri
5
+ class Chain
6
+ class HeaderChain
7
+ HEAD = 'head'
8
+ GENESIS = 'genesis'
9
+ HEADER_PREFIX = 'h'
10
+ TD_SUFFIX = 't'
11
+ NUM_SUFFIX = 'n'
12
+
13
+ attr_reader :store, :byzantium_block, :homestead_block
14
+
15
+ def initialize(store, byzantium_block: nil, homestead_block: nil)
16
+ @store = store
17
+ @byzantium_block = byzantium_block
18
+ @homestead_block = homestead_block
19
+ end
20
+
21
+ def head
22
+ encoded = store[HEAD]
23
+ encoded && Header.rlp_decode(encoded)
24
+ end
25
+
26
+ def head=(header)
27
+ store[HEAD] = header.rlp_encode
28
+ end
29
+
30
+ def get_header(hash)
31
+ encoded = store[HEADER_PREFIX + hash]
32
+ encoded && Header.rlp_decode(encoded)
33
+ end
34
+
35
+ def get_header_by_number(number)
36
+ hash = get_header_hash_by_number(number)
37
+ hash && get_header(hash)
38
+ end
39
+
40
+ def valid?(header)
41
+ # ignore genesis header if there not exist one
42
+ return true if header.number == 0 && get_header_by_number(0).nil?
43
+
44
+ parent_header = get_header(header.parent_hash)
45
+ return false unless parent_header
46
+ # check height
47
+ return false unless parent_header.number + 1 == header.number
48
+ # check timestamp
49
+ return false unless parent_header.timestamp < header.timestamp
50
+
51
+ # check gas limit range
52
+ parent_gas_limit = parent_header.gas_limit
53
+ gas_limit_max = parent_gas_limit + parent_gas_limit / 1024
54
+ gas_limit_min = parent_gas_limit - parent_gas_limit / 1024
55
+ gas_limit = header.gas_limit
56
+ return false unless gas_limit >= 5000 && gas_limit > gas_limit_min && gas_limit < gas_limit_max
57
+ return false unless calculate_difficulty(header, parent_header) == header.difficulty
58
+
59
+ # check pow
60
+ begin
61
+ POW.check_pow(header.number, header.mining_hash, header.mix_hash, header.nonce, header.difficulty)
62
+ rescue POW::InvalidError
63
+ return false
64
+ end
65
+
66
+ true
67
+ end
68
+
69
+ # calculate header difficulty
70
+ # you can find explain in Ethereum yellow paper: Block Header Validity section.
71
+ def calculate_difficulty(header, parent_header)
72
+ return header.difficulty if header.number == 0
73
+
74
+ x = parent_header.difficulty / 2048
75
+ y = header.ommers_hash == Utils::BLANK_SHA3 ? 1 : 2
76
+
77
+ # handle byzantium fork
78
+ # https://github.com/ethereum/EIPs/blob/181867ae830df5419eb9982d2a24797b2dcad28f/EIPS/eip-609.md
79
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-100.md
80
+ byzantium_fork = byzantium_block && header.number > byzantium_block
81
+ # https://github.com/ethereum/EIPs/blob/984cf5de90bbf5fbe7e49be227b0c2f9567e661e/EIPS/eip-2.md
82
+ homestead_fork = homestead_block && header.number > homestead_block
83
+
84
+ time_factor = if byzantium_fork
85
+ [y - (header.timestamp - parent_header.timestamp) / 9, -99].max
86
+ elsif homestead_fork
87
+ [1 - (header.timestamp - parent_header.timestamp) / 10, -99].max
88
+ else
89
+ (header.timestamp - parent_header.timestamp) < 13 ? 1 : -1
90
+ end
91
+
92
+ # difficulty bomb
93
+ height = byzantium_fork ? [(header.number - 3000000), 0].max : header.number
94
+ height_factor = 2 ** (height / 100000 - 2)
95
+
96
+ difficulty = (parent_header.difficulty + x * time_factor + height_factor).to_i
97
+ [header.difficulty, difficulty].max
98
+ end
99
+
100
+ # write header
101
+ def write(header)
102
+ hash = header.get_hash
103
+ # get total difficulty
104
+ td = if header.number == 0
105
+ header.difficulty
106
+ else
107
+ parent_header = get_header(header.parent_hash)
108
+ raise "can't find parent from db" unless parent_header
109
+ parent_td = total_difficulty(parent_header.get_hash)
110
+ parent_td + header.difficulty
111
+ end
112
+ # write header and td
113
+ store.batch do |b|
114
+ b.put(HEADER_PREFIX + hash, header.rlp_encode)
115
+ b.put(HEADER_PREFIX + hash + TD_SUFFIX, RLP.encode(td, Integer))
116
+ end
117
+ end
118
+
119
+ def write_header_hash_number(header_hash, number)
120
+ enc_number = Utils.big_endian_encode number
121
+ store[HEADER_PREFIX + enc_number + NUM_SUFFIX] = header_hash
122
+ end
123
+
124
+ def get_header_hash_by_number(number)
125
+ enc_number = Utils.big_endian_encode number
126
+ store[HEADER_PREFIX + enc_number + NUM_SUFFIX]
127
+ end
128
+
129
+ def total_difficulty(header_hash = head.nil? ? nil : head.get_hash)
130
+ return 0 if header_hash.nil?
131
+ RLP.decode(store[HEADER_PREFIX + header_hash + TD_SUFFIX], Integer)
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -97,7 +97,7 @@ module Ciri
97
97
  end
98
98
 
99
99
  def get_hash
100
- Utils.sha3 rlp_encode!
100
+ Utils.sha3 rlp_encode
101
101
  end
102
102
 
103
103
  # validate transaction
data/lib/ciri/crypto.rb CHANGED
@@ -67,8 +67,8 @@ module Ciri
67
67
  end
68
68
 
69
69
  def signature
70
- @signature ||= Utils.big_endian_encode_to_size(@r, "\x00".b, size: 32) +
71
- Utils.big_endian_encode_to_size(@s, "\x00".b, size: 32) +
70
+ @signature ||= Utils.big_endian_encode(@r, "\x00".b, size: 32) +
71
+ Utils.big_endian_encode(@s, "\x00".b, size: 32) +
72
72
  Utils.big_endian_encode(@v, "\x00".b)
73
73
  end
74
74
 
@@ -0,0 +1,145 @@
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/trie'
25
+ require 'ciri/types/account'
26
+ require 'ciri/serialize'
27
+ require 'ciri/utils/logger'
28
+
29
+ module Ciri
30
+ module DB
31
+ class AccountDB
32
+
33
+ include Serialize
34
+ include Utils::Logger
35
+
36
+ attr_reader :db
37
+
38
+ def initialize(db, root_hash: nil)
39
+ @db = db
40
+ root_hash ||= Trie::BLANK_NODE_HASH
41
+ @trie = Trie.new(db: @db, root_hash: root_hash, prune: true)
42
+ end
43
+
44
+ def root_hash
45
+ @trie.root_hash
46
+ end
47
+
48
+ def store(address, key, value)
49
+ account = find_account address
50
+ trie = Trie.new(db: @db, root_hash: account.storage_root)
51
+
52
+ converted_key = convert_key Utils.big_endian_encode(key, size: 32)
53
+
54
+ if value && value != 0
55
+ trie[converted_key] = RLP.encode(value)
56
+ else
57
+ trie.delete(converted_key)
58
+ end
59
+ account.storage_root = trie.root_hash
60
+ update_account(address, account)
61
+ end
62
+
63
+ def fetch(address, key)
64
+ # remove unnecessary null byte from key
65
+ converted_key = convert_key Utils.big_endian_encode(key, size: 32)
66
+ account = find_account address
67
+ trie = Trie.new(db: @db, root_hash: account.storage_root)
68
+ value = trie[converted_key]
69
+ value.empty? ? 0 : RLP.decode(value, Integer)
70
+ end
71
+
72
+ def set_nonce(address, nonce)
73
+ account = find_account(address)
74
+ account.nonce = nonce
75
+ update_account(address, account)
76
+ end
77
+
78
+ def increment_nonce(address, value = 1)
79
+ account = find_account(address)
80
+ account.nonce += value
81
+ update_account(address, account)
82
+ end
83
+
84
+ def set_balance(address, balance)
85
+ account = find_account(address)
86
+ account.balance = balance
87
+ update_account(address, account)
88
+ end
89
+
90
+ def add_balance(address, value)
91
+ account = find_account(address)
92
+ account.balance += value
93
+ raise "value can't be negative" if account.balance < 0
94
+ update_account(address, account)
95
+ end
96
+
97
+ def set_account_code(address, code)
98
+ code ||= ''.b
99
+ account = find_account(address)
100
+ account.code_hash = Utils.sha3(code)
101
+ update_account(address, account)
102
+ db[account.code_hash] = code
103
+ end
104
+
105
+ def get_account_code(address)
106
+ db[find_account(address).code_hash] || ''.b
107
+ end
108
+
109
+ def touch_account(address)
110
+ update_account(address, find_account(address))
111
+ end
112
+
113
+ def find_account(address)
114
+ rlp_encoded_account = @trie[convert_key address]
115
+ if rlp_encoded_account.nil? || rlp_encoded_account.size == 0
116
+ Types::Account.new_empty
117
+ else
118
+ Types::Account.rlp_decode(rlp_encoded_account)
119
+ end
120
+ end
121
+
122
+ def delete_account(address)
123
+ @trie.delete(convert_key address)
124
+ end
125
+
126
+ def account_dead?(address)
127
+ find_account(address).empty?
128
+ end
129
+
130
+ private
131
+
132
+ def update_account(address, account)
133
+ debug 'update account'
134
+ debug Utils.to_hex(address)
135
+ debug account.serializable_attributes
136
+ @trie[convert_key address] = Types::Account.rlp_encode account
137
+ end
138
+
139
+ def convert_key(key)
140
+ Utils.sha3 key.to_s
141
+ end
142
+
143
+ end
144
+ end
145
+ end