ruby-ethereum 0.9.0

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.
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