ruby-ethereum 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,211 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ##
6
+ # A block header.
7
+ #
8
+ # If the block with this header exists as an instance of {Block}, the
9
+ # connection can be made explicit by setting `BlockHeader.block`. Then,
10
+ # `BlockHeader.state_root`, `BlockHeader.tx_list_root` and
11
+ # `BlockHeader.receipts_root` always refer to the up-to-date value in the
12
+ # block instance.
13
+ #
14
+ # * `@block` - an instance of {Block} or `nil`
15
+ # * `@prevhash` - the 32 byte hash of the previous block
16
+ # * `@uncles_hash` - the 32 byte hash of the RLP encoded list of uncle headers
17
+ # * `@coinbase` - the 20 byte coinbase address
18
+ # * `@state_root` - the root of the block's state trie
19
+ # * `@tx_list_root` - the root of the block's transaction trie
20
+ # * `@receipts_root` - the root of the block's receipts trie
21
+ # * `@bloom` - bloom filter
22
+ # * `@difficulty` - the block's difficulty
23
+ # * `@number` - the number of ancestors of this block (0 for the genesis block)
24
+ # * `@gas_limit` - the block's gas limit
25
+ # * `@gas_used` - the total amount of gas used by all transactions in this block
26
+ # * `@timestamp` - a UNIX timestamp
27
+ # * `@extra_data` - up to 1024 bytes of additional data
28
+ # * `@nonce` - a 8 byte nonce constituting a proof-of-work, or the empty
29
+ # string as a placeholder
30
+ #
31
+ class BlockHeader
32
+ include RLP::Sedes::Serializable
33
+
34
+ extend Sedes
35
+
36
+ set_serializable_fields(
37
+ prevhash: hash32,
38
+ uncles_hash: hash32,
39
+ coinbase: address,
40
+ state_root: trie_root,
41
+ tx_list_root: trie_root,
42
+ receipts_root: trie_root,
43
+ bloom: int256,
44
+ difficulty: big_endian_int,
45
+ number: big_endian_int,
46
+ gas_limit: big_endian_int,
47
+ gas_used: big_endian_int,
48
+ timestamp: big_endian_int,
49
+ extra_data: binary,
50
+ mixhash: binary,
51
+ nonce: RLP::Sedes::Binary.new(min_length: 8, allow_empty: true) # FIXME: should be fixed length 8?
52
+ )
53
+
54
+ class <<self
55
+ def from_block_rlp(rlp_data)
56
+ block_data = RLP.decode_lazy rlp_data
57
+ deserialize block_data[0]
58
+ end
59
+
60
+ def find(db, hash)
61
+ bh = from_block_rlp db.get(hash)
62
+ raise ValidationError, "BlockHeader.hash is broken" if bh.full_hash != hash
63
+ bh
64
+ end
65
+ end
66
+
67
+ attr_accessor :block
68
+
69
+ def initialize(options={})
70
+ fields = {
71
+ prevhash: Env::DEFAULT_CONFIG[:genesis_prevhash],
72
+ uncles_hash: Utils.keccak256_rlp([]),
73
+ coinbase: Env::DEFAULT_CONFIG[:genesis_coinbase],
74
+ state_root: PruningTrie::BLANK_ROOT,
75
+ tx_list_root: PruningTrie::BLANK_ROOT,
76
+ receipts_root: PruningTrie::BLANK_ROOT,
77
+ bloom: 0,
78
+ difficulty: Env::DEFAULT_CONFIG[:genesis_difficulty],
79
+ number: 0,
80
+ gas_limit: Env::DEFAULT_CONFIG[:genesis_gas_limit],
81
+ gas_used: 0,
82
+ timestamp: 0,
83
+ extra_data: '',
84
+ mixhash: Env::DEFAULT_CONFIG[:genesis_mixhash],
85
+ nonce: ''
86
+ }.merge(options)
87
+
88
+ fields[:coinbase] = Utils.decode_hex(fields[:coinbase]) if fields[:coinbase].size == 40
89
+ raise ArgumentError, "invalid coinbase #{fields[:coinbase]}" unless fields[:coinbase].size == 20
90
+ raise ArgumentError, "invalid difficulty" unless fields[:difficulty] > 0
91
+
92
+ self.block = nil
93
+ @fimxe_hash = nil
94
+
95
+ super(**fields)
96
+ end
97
+
98
+ def _state_root
99
+ @state_root
100
+ end
101
+
102
+ def state_root
103
+ get_with_block :state_root
104
+ end
105
+
106
+ def state_root=(v)
107
+ set_with_block :state_root, v
108
+ end
109
+
110
+ def tx_list_root
111
+ get_with_block :tx_list_root
112
+ end
113
+
114
+ def tx_list_root=(v)
115
+ set_with_block :tx_list_root, v
116
+ end
117
+
118
+ def receipts_root
119
+ get_with_block :receipts_root
120
+ end
121
+
122
+ def receipts_root=(v)
123
+ set_with_block :receipts_root, v
124
+ end
125
+
126
+ def full_hash
127
+ Utils.keccak256_rlp self
128
+ end
129
+
130
+ def full_hash_hex
131
+ Utils.encode_hex full_hash
132
+ end
133
+
134
+ def mining_hash
135
+ Utils.keccak256 RLP.encode(self, sedes: self.class.exclude(%i(mixhash nonce)))
136
+ end
137
+
138
+ ##
139
+ # Check if the proof-of-work of the block is valid.
140
+ #
141
+ # @param nonce [String] if given the proof of work function will be
142
+ # evaluated with this nonce instead of the one already present in the
143
+ # header
144
+ #
145
+ # @return [Bool]
146
+ #
147
+ def check_pow(nonce=nil)
148
+ logger.debug "checking pow", block: full_hash_hex[0,8]
149
+ Miner.check_pow(number, mining_hash, mixhash, nonce || self.nonce, difficulty)
150
+ end
151
+
152
+ ##
153
+ # Serialize the header to a readable hash.
154
+ #
155
+ def to_h
156
+ h = {}
157
+
158
+ %i(prevhash uncles_hash extra_data nonce mixhash).each do |field|
159
+ h[field] = "0x#{Utils.encode_hex(send field)}"
160
+ end
161
+
162
+ %i(state_root tx_list_root receipts_root coinbase).each do |field|
163
+ h[field] = Utils.encode_hex send(field)
164
+ end
165
+
166
+ %i(number difficulty gas_limit gas_used timestamp).each do |field|
167
+ h[field] = send(field).to_s
168
+ end
169
+
170
+ h[:bloom] = Utils.encode_hex Sedes.int256.serialize(bloom)
171
+
172
+ h
173
+ end
174
+
175
+ def to_s
176
+ "#<#{self.class.name}:#{object_id} ##{number} #{full_hash_hex[0,8]}>"
177
+ end
178
+ alias :inspect :to_s
179
+
180
+ ##
181
+ # Two blockheader are equal iff they have the same hash.
182
+ #
183
+ def ==(other)
184
+ other.instance_of?(BlockHeader) && full_hash == other.full_hash
185
+ end
186
+ alias :eql? :==
187
+
188
+ def hash
189
+ Utils.big_endian_to_int full_hash
190
+ end
191
+
192
+ private
193
+
194
+ def logger
195
+ @logger ||= Logger.new 'eth.block.header'
196
+ end
197
+
198
+ def get_with_block(attr)
199
+ block ? block.send(attr) : instance_variable_get(:"@#{attr}")
200
+ end
201
+
202
+ def set_with_block(attr, v)
203
+ if block
204
+ block.send :"#{attr}=", v
205
+ else
206
+ _set_field attr, v
207
+ end
208
+ end
209
+
210
+ end
211
+ end
@@ -0,0 +1,83 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ###
6
+ # Blooms are the 3-point, 2048-bit (11-bits/point) Bloom filter of each
7
+ # component (except data) of each log entry of each transaction.
8
+ #
9
+ # We set the bits of a 2048-bit value whose indices are given by the low
10
+ # order 9-bits of the first three double-bytes of the SHA3 of each value.
11
+ #
12
+ # @example
13
+ # bloom(0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6)
14
+ # sha3: bd2b01afcd27800b54d2179edc49e2bffde5078bb6d0b204694169b1643fb108
15
+ # first 3 double-bytes: bd2b, 01af, cd27
16
+ # bits in bloom: 1323, 431, 1319
17
+ #
18
+ # Blooms are type of `Integer`.
19
+ #
20
+ class Bloom
21
+
22
+ BITS = 2048
23
+ MASK = 2047
24
+ BUCKETS = 3
25
+
26
+ class <<self
27
+ def from(v)
28
+ insert(0, v)
29
+ end
30
+
31
+ def from_array(args)
32
+ blooms = args.map {|arg| from(arg) }
33
+ combine *blooms
34
+ end
35
+
36
+ def insert(bloom, v)
37
+ h = Utils.keccak256 v
38
+ BUCKETS.times {|i| bloom |= get_index(h, i) }
39
+ bloom
40
+ end
41
+
42
+ def query(bloom, v)
43
+ b = from v
44
+ (bloom & b) == b
45
+ end
46
+
47
+ def combine(*args)
48
+ args.reduce(0, &:|)
49
+ end
50
+
51
+ def bits(v)
52
+ h = Utils.keccak256 v
53
+ BUCKETS.times.map {|i| bits_in_number get_index(h, i) }
54
+ end
55
+
56
+ def bits_in_number(v)
57
+ BITS.times.select {|i| (1<<i) & v > 0 }
58
+ end
59
+
60
+ def b256(int_bloom)
61
+ Utils.zpad Utils.int_to_big_endian(int_bloom), 256
62
+ end
63
+
64
+ ##
65
+ # Get index for hash double-byte in bloom.
66
+ #
67
+ # @param hash [String] value hash
68
+ # @param pos [Integer] double-byte position in hash, can only be 0, 1, 2
69
+ #
70
+ # @return [Integer] bloom index
71
+ #
72
+ def get_index(hash, pos)
73
+ raise ArgumentError, "invalid double-byte position" unless [0,1,2].include?(pos)
74
+
75
+ i = pos*2
76
+ hi = hash[i].ord << 8
77
+ lo = hash[i+1].ord
78
+ 1 << ((hi+lo) & MASK)
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ module Ethereum
2
+ module CachedBlock
3
+
4
+ def self.create_cached(blk)
5
+ blk.singleton_class.send :include, self
6
+ blk
7
+ end
8
+
9
+ def state_root=(*args)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def revert(*args)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def commit_state
18
+ # do nothing
19
+ end
20
+
21
+ def hash
22
+ Utils.big_endian_to_int full_hash
23
+ end
24
+
25
+ def full_hash
26
+ @full_hash ||= super
27
+ end
28
+
29
+ private
30
+
31
+ def set_account_item(*args)
32
+ raise NotImplementedError
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,400 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ##
6
+ # Manages the chain and requests to it.
7
+ #
8
+ class Chain
9
+
10
+ HEAD_KEY = 'HEAD'.freeze
11
+
12
+ attr :env, :index, :head_candidate
13
+
14
+ ##
15
+ # @param env [Ethereum::Env] configuration of the chain
16
+ #
17
+ def initialize(env, genesis: nil, new_head_cb: nil, coinbase: Address::ZERO)
18
+ raise ArgumentError, "env must be instance of Env" unless env.instance_of?(Env)
19
+
20
+ @env = env
21
+ @db = env.db
22
+
23
+ @new_head_cb = new_head_cb
24
+ @index = Index.new env
25
+ @coinbase = coinbase
26
+
27
+ initialize_blockchain(genesis) unless @db.has_key?(HEAD_KEY)
28
+ logger.debug "chain @ head_hash=#{head}"
29
+
30
+ @genesis = get @index.get_block_by_number(0)
31
+ logger.debug "got genesis", nonce: Utils.encode_hex(@genesis.nonce), difficulty: @genesis.difficulty
32
+
33
+ @head_candidate = nil
34
+ update_head_candidate
35
+ end
36
+
37
+ def head
38
+ initialize_blockchain unless @db && @db.has_key?(HEAD_KEY)
39
+ ptr = @db.get HEAD_KEY
40
+ Block.find @env, ptr
41
+ end
42
+
43
+ def coinbase
44
+ raise AssertError, "coinbase changed!" unless @head_candidate.coinbase == @coinbase
45
+ @coinbase
46
+ end
47
+
48
+ def coinbase=(v)
49
+ @coinbase = v
50
+ # block reward goes to different address => redo finalization of head candidate
51
+ update_head head
52
+ end
53
+
54
+ ##
55
+ # Return the uncles of `block`.
56
+ #
57
+ def get_uncles(block)
58
+ if block.has_parent?
59
+ get_brothers(block.get_parent)
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ ##
66
+ # Return the uncles of the hypothetical child of `block`.
67
+ #
68
+ def get_brothers(block)
69
+ o = []
70
+ i = 0
71
+
72
+ while block.has_parent? && i < @env.config[:max_uncle_depth]
73
+ parent = block.get_parent
74
+ children = get_children(parent).select {|c| c != block }
75
+ o.concat children
76
+ block = parent
77
+ i += 1
78
+ end
79
+
80
+ o
81
+ end
82
+
83
+ def get(blockhash)
84
+ raise ArgumentError, "blockhash must be a String" unless blockhash.instance_of?(String)
85
+ raise ArgumentError, "blockhash size must be 32" unless blockhash.size == 32
86
+ Block.find(@env, blockhash)
87
+ end
88
+
89
+ def get_bloom(blockhash)
90
+ b = RLP.decode RLP.descend(@db.get(blockhash), 0, 6)
91
+ Utils.big_endian_to_int b
92
+ end
93
+
94
+ def has_block(blockhash)
95
+ raise ArgumentError, "blockhash must be a String" unless blockhash.instance_of?(String)
96
+ raise ArgumentError, "blockhash size must be 32" unless blockhash.size == 32
97
+ @db.include?(blockhash)
98
+ end
99
+ alias :include? :has_block
100
+ alias :has_key? :has_block
101
+
102
+ def commit
103
+ @db.commit
104
+ end
105
+
106
+ ##
107
+ # Returns `true` if block was added successfully.
108
+ #
109
+ def add_block(block, forward_pending_transaction=true)
110
+ unless block.has_parent? || block.genesis?
111
+ logger.debug "missing parent", block_hash: block
112
+ return false
113
+ end
114
+
115
+ unless block.validate_uncles
116
+ logger.debug "invalid uncles", block_hash: block
117
+ return false
118
+ end
119
+
120
+ unless block.header.check_pow || block.genesis?
121
+ logger.debug "invalid nonce", block_hash: block
122
+ return false
123
+ end
124
+
125
+ if block.has_parent?
126
+ begin
127
+ Block.verify(block, block.get_parent)
128
+ rescue InvalidBlock => e
129
+ log.fatal "VERIFICATION FAILED", block_hash: block, error: e
130
+
131
+ f = File.join Utils.data_dir, 'badblock.log'
132
+ File.write(f, Utils.encode_hex(RLP.encode(block)))
133
+ return false
134
+ end
135
+ end
136
+
137
+ if block.number < head.number
138
+ logger.debug "older than head", block_hash: block, head_hash: head
139
+ end
140
+
141
+ @index.add_block block
142
+ store_block block
143
+
144
+ # set to head if this makes the longest chain w/ most work for that number
145
+ if block.chain_difficulty > head.chain_difficulty
146
+ logger.debug "new head", block_hash: block, num_tx: block.transaction_count
147
+ update_head block, forward_pending_transaction
148
+ elsif block.number > head.number
149
+ logger.warn "has higher blk number than head but lower chain_difficulty", block_has: block, head_hash: head, block_difficulty: block.chain_difficulty, head_difficulty: head.chain_difficulty
150
+ end
151
+
152
+ # Refactor the long calling chain
153
+ block.transactions.clear_all
154
+ block.receipts.clear_all
155
+ block.state.db.commit_refcount_changes block.number
156
+ block.state.db.cleanup block.number
157
+
158
+ commit # batch commits all changes that came with the new block
159
+ true
160
+ end
161
+
162
+ def get_children(block)
163
+ @index.get_children(block.full_hash).map {|c| get(c) }
164
+ end
165
+
166
+ ##
167
+ # Add a transaction to the `head_candidate` block.
168
+ #
169
+ # If the transaction is invalid, the block will not be changed.
170
+ #
171
+ # @return [Bool,NilClass] `true` is the transaction was successfully added or
172
+ # `false` if the transaction was invalid, `nil` if it's already included
173
+ #
174
+ def add_transaction(transaction)
175
+ raise AssertError, "head candiate cannot be nil" unless @head_candidate
176
+
177
+ hc = @head_candidate
178
+ logger.debug "add tx", num_txs: transaction_count, tx: transaction, on: hc
179
+
180
+ if @head_candidate.include_transaction?(transaction.full_hash)
181
+ logger.debug "known tx"
182
+ return
183
+ end
184
+
185
+ old_state_root = hc.state_root
186
+ # revert finalization
187
+ hc.state_root = @pre_finalize_state_root
188
+ begin
189
+ success, output = hc.apply_transaction(transaction)
190
+ rescue InvalidTransaction => e
191
+ # if unsuccessful the prerequisites were not fullfilled and the tx is
192
+ # invalid, state must not have changed
193
+ logger.debug "invalid tx", error: e
194
+ hc.state_root = old_state_root
195
+ return false
196
+ end
197
+ logger.debug "valid tx"
198
+
199
+ # we might have a new head_candidate (due to ctx switches in up layer)
200
+ if @head_candidate != hc
201
+ logger.debug "head_candidate changed during validation, trying again"
202
+ return add_transaction(transaction)
203
+ end
204
+
205
+ @pre_finalize_state_root = hc.state_root
206
+ hc.finalize
207
+ logger.debug "tx applied", result: output
208
+
209
+ raise AssertError, "state root unchanged!" unless old_state_root != hc.state_root
210
+ true
211
+ end
212
+
213
+ ##
214
+ # Get a list of new transactions not yet included in a mined block but
215
+ # known to the chain.
216
+ #
217
+ def get_transactions
218
+ if @head_candidate
219
+ logger.debug "get_transactions called", on: @head_candidate
220
+ @head_candidate.get_transactions
221
+ else
222
+ []
223
+ end
224
+ end
225
+
226
+ def transaction_count
227
+ @head_candidate ? @head_candidate.transaction_count : 0
228
+ end
229
+
230
+ ##
231
+ # Return `count` of blocks starting from head or `start`.
232
+ #
233
+ def get_chain(start: '', count: 10)
234
+ logger.debug "get_chain", start: Utils.encode_hex(start), count: count
235
+
236
+ if start.true?
237
+ return [] unless @index.db.include?(start)
238
+
239
+ block = get start
240
+ return [] unless in_main_branch?(block)
241
+ else
242
+ block = head
243
+ end
244
+
245
+ blocks = []
246
+ count.times do |i|
247
+ blocks.push block
248
+ break if block.genesis?
249
+ block = block.get_parent
250
+ end
251
+
252
+ blocks
253
+ end
254
+
255
+ def in_main_branch?(block)
256
+ block.full_hash == @index.get_block_by_number(block.number)
257
+ rescue KeyError
258
+ false
259
+ end
260
+
261
+ def get_descendants(block, count: 1)
262
+ logger.debug "get_descendants", block_hash: block
263
+ raise AssertError, "cannot find block hash in current chain" unless include?(block.full_hash)
264
+
265
+ block_numbers = (block.number+1)...([head.number+1, block.number+count+1].min)
266
+ block_numbers.map {|n| get @index.get_block_by_number(n) }
267
+ end
268
+
269
+ def update_head(block, forward_pending_transaction=true)
270
+ logger.debug "updating head"
271
+ logger.debug "New Head is on a different branch", head_hash: block, old_head_hash: head if !block.genesis? && block.get_parent != head
272
+
273
+ # Some temporary auditing to make sure pruning is working well
274
+ if block.number > 0 && block.number % 500 == 0 && @db.instance_of?(DB::RefcountDB)
275
+ # TODO
276
+ end
277
+
278
+ # Fork detected, revert death row and change logs
279
+ if block.number > 0
280
+ b = block.get_parent
281
+ h = head
282
+ b_children = []
283
+
284
+ if b.full_hash != h.full_hash
285
+ logger.warn "reverting"
286
+
287
+ while h.number > b.number
288
+ h.state.db.revert_refcount_changes h.number
289
+ h = h.get_parent
290
+ end
291
+ while b.number > h.number
292
+ b_children.push b
293
+ b = b.get_parent
294
+ end
295
+
296
+ while b.full_hash != h.full_hash
297
+ h.state.db.revert_refcount_changes h.number
298
+ h = h.get_parent
299
+
300
+ b_children.push b
301
+ b = b.get_parent
302
+ end
303
+
304
+ b_children.each do |bc|
305
+ Block.verify(bc, bc.get_parent)
306
+ end
307
+ end
308
+ end
309
+
310
+ @db.put HEAD_KEY, block.full_hash
311
+ raise "Chain write error!" unless @db.get(HEAD_KEY) == block.full_hash
312
+
313
+ @index.update_blocknumbers(head)
314
+ raise "Fail to update head!" unless head == block
315
+
316
+ logger.debug "set new head", head: head
317
+ update_head_candidate forward_pending_transaction
318
+
319
+ @new_head_cb.call(block) if @new_head_cb && !block.genesis?
320
+ end
321
+
322
+ private
323
+
324
+ def logger
325
+ @logger ||= Logger.new 'eth.chain'
326
+ end
327
+
328
+ def initialize_blockchain(genesis=nil)
329
+ logger.info "Initializing new chain"
330
+
331
+ unless genesis
332
+ genesis = Block.genesis(@env)
333
+ logger.info "new genesis", genesis_hash: genesis, difficulty: genesis.difficulty
334
+ @index.add_block genesis
335
+ end
336
+
337
+ store_block genesis
338
+ raise "failed to store block" unless genesis == Block.find(@env, genesis.full_hash)
339
+
340
+ update_head genesis
341
+ raise "falied to update head" unless include?(genesis.full_hash)
342
+
343
+ commit
344
+ end
345
+
346
+ def store_block(block)
347
+ if block.number > 0
348
+ @db.put_temporarily block.full_hash, RLP.encode(block)
349
+ else
350
+ @db.put block.full_hash, RLP.encode(block)
351
+ end
352
+ end
353
+
354
+ # after new head is set
355
+ def update_head_candidate(forward_pending_transaction=true)
356
+ logger.debug "updating head candidate", head: head
357
+
358
+ # collect uncles
359
+ blk = head # parent of the block we are collecting uncles for
360
+ uncles = get_brothers(blk).map(&:header).uniq
361
+
362
+ (@env.config[:max_uncle_depth]+2).times do |i|
363
+ blk.uncles.each {|u| uncles.delete u }
364
+ blk = blk.get_parent if blk.has_parent?
365
+ end
366
+
367
+ raise "strange uncle found!" unless uncles.empty? || uncles.map(&:number).max <= head.number
368
+
369
+ uncles = uncles[0, @env.config[:max_uncles]]
370
+
371
+ # create block
372
+ ts = [Time.now.to_i, head.timestamp+1].max
373
+ _env = Env.new DB::OverlayDB.new(head.db), config: @env.config, global_config: @env.global_config
374
+ hc = Block.build_from_parent head, @coinbase, timestamp: ts, uncles: uncles, env: _env
375
+ raise ValidationError, "invalid uncles" unless hc.validate_uncles
376
+
377
+ @pre_finalize_state_root = hc.state_root
378
+ hc.finalize
379
+
380
+ # add transactions from previous head candidate
381
+ old_hc = @head_candidate
382
+ @head_candidate = hc
383
+
384
+ if old_hc
385
+ tx_hashes = head.get_transaction_hashes
386
+ pending = old_hc.get_transactions.select {|tx| !tx_hashes.include?(tx.full_hash) }
387
+
388
+ if pending.true?
389
+ if forward_pending_transaction
390
+ logger.debug "forwarding pending transaction", num: pending.size
391
+ pending.each {|tx| add_transaction tx }
392
+ else
393
+ logger.debug "discarding pending transaction", num: pending.size
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ end
400
+ end