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
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
|
-
|
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
|
-
|
68
|
-
store[HEAD] = header.rlp_encode!
|
69
|
-
end
|
52
|
+
BODY_PREFIX = 'b'
|
70
53
|
|
71
|
-
|
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
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
108
|
-
|
67
|
+
# run block
|
68
|
+
def import_block(block)
|
69
|
+
validate_block(block)
|
70
|
+
write_block(block)
|
109
71
|
|
110
|
-
#
|
111
|
-
#
|
112
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
106
|
+
if trie.root_hash != block.header.receipts_root
|
107
|
+
raise InvalidBlockError.new("incorrect receipts_root")
|
108
|
+
end
|
181
109
|
|
182
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
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
|
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 = []
|
data/lib/ciri/chain/header.rb
CHANGED
@@ -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
|
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
|
data/lib/ciri/crypto.rb
CHANGED
@@ -67,8 +67,8 @@ module Ciri
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def signature
|
70
|
-
@signature ||= Utils.
|
71
|
-
Utils.
|
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
|