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,1311 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'forwardable'
4
+
5
+ module Ethereum
6
+
7
+ ##
8
+ # A block.
9
+ #
10
+ # All attributes from the block header are accessible via properties (i.e.
11
+ # `block.prevhash` is equivalent to `block.header.prevhash`). It is ensured
12
+ # that no discrepancies between header and the block occur.
13
+ #
14
+ class Block
15
+ include RLP::Sedes::Serializable
16
+
17
+ HeaderGetters = (BlockHeader.serializable_fields.keys - %i(state_root receipts_root tx_list_root)).freeze
18
+ HeaderSetters = HeaderGetters.map {|field| :"#{field}=" }.freeze
19
+
20
+ set_serializable_fields(
21
+ header: BlockHeader,
22
+ transaction_list: RLP::Sedes::CountableList.new(Transaction),
23
+ uncles: RLP::Sedes::CountableList.new(BlockHeader)
24
+ )
25
+
26
+ extend Forwardable
27
+ def_delegators :header, *HeaderGetters, *HeaderSetters
28
+
29
+ attr :env, :db, :config
30
+ attr_accessor :state, :transactions, :receipts, :refunds, :suicides, :ether_delta, :ancestor_hashes, :logs, :log_listeners
31
+
32
+ class <<self
33
+ ##
34
+ # Assumption: blocks loaded from the db are not manipulated -> can be
35
+ # cached including hash.
36
+ def find(env, hash)
37
+ raise ArgumentError, "env must be instance of Env" unless env.instance_of?(Env)
38
+ blk = RLP.decode env.db.get(hash), sedes: Block, env: env
39
+ CachedBlock.create_cached blk
40
+ end
41
+
42
+ def verify(block, parent)
43
+ block2 = RLP.decode RLP.encode(block), sedes: Block, env: parent.env, parent: parent
44
+ raise "block not match" unless block == block2
45
+ true
46
+ rescue InvalidBlock
47
+ false
48
+ end
49
+
50
+ ##
51
+ # Create a block without specifying transactions or uncles.
52
+ #
53
+ # @param header_rlp [String] the RLP encoded block header
54
+ # @param env [Env] provide database for the block
55
+ #
56
+ # @return [Block]
57
+ #
58
+ def build_from_header(header_rlp, env)
59
+ header = RLP.decode header_rlp, sedes: BlockHeader
60
+ new header, transaction_list: nil, uncles: [], env: env
61
+ end
62
+
63
+ ##
64
+ # Create a new block based on a parent block.
65
+ #
66
+ # The block will not include any transactions and will not be finalized.
67
+ #
68
+ def build_from_parent(parent, coinbase, nonce: Constant::BYTE_EMPTY, extra_data: Constant::BYTE_EMPTY, timestamp: Time.now.to_i, uncles: [], env: nil)
69
+ env ||= parent.env
70
+
71
+ header = BlockHeader.new(
72
+ prevhash: parent.full_hash,
73
+ uncles_hash: Utils.keccak256_rlp(uncles),
74
+ coinbase: coinbase,
75
+ state_root: parent.state_root,
76
+ tx_list_root: Trie::BLANK_ROOT,
77
+ receipts_root: Trie::BLANK_ROOT,
78
+ bloom: 0,
79
+ difficulty: calc_difficulty(parent, timestamp),
80
+ mixhash: Constant::BYTE_EMPTY,
81
+ number: parent.number+1,
82
+ gas_limit: calc_gaslimit(parent),
83
+ gas_used: 0,
84
+ timestamp: timestamp,
85
+ extra_data: extra_data,
86
+ nonce: nonce
87
+ )
88
+
89
+ Block.new(
90
+ header,
91
+ transaction_list: [],
92
+ uncles: uncles,
93
+ env: env,
94
+ parent: parent,
95
+ making: true
96
+ ).tap do |blk|
97
+ blk.ancestor_hashes = [parent.full_hash] + parent.ancestor_hashes
98
+ blk.log_listeners = parent.log_listeners
99
+ end
100
+ end
101
+
102
+ def calc_difficulty(parent, ts)
103
+ config = parent.config
104
+ offset = parent.difficulty / config[:block_diff_factor]
105
+
106
+ if parent.number >= (config[:homestead_fork_blknum] - 1)
107
+ sign = [1 - ((ts - parent.timestamp) / config[:homestead_diff_adjustment_cutoff]), -99].max
108
+ else
109
+ sign = (ts - parent.timestamp) < config[:diff_adjustment_cutoff] ? 1 : -1
110
+ end
111
+
112
+ # If we enter a special mode where the genesis difficulty starts off
113
+ # below the minimal difficulty, we allow low-difficulty blocks (this will
114
+ # never happen in the official protocol)
115
+ o = [parent.difficulty + offset*sign, [parent.difficulty, config[:min_diff]].min].max
116
+ period_count = (parent.number + 1) / config[:expdiff_period]
117
+ if period_count >= config[:expdiff_free_periods]
118
+ o = [o + 2**(period_count - config[:expdiff_free_periods]), config[:min_diff]].max
119
+ end
120
+
121
+ o
122
+ end
123
+
124
+ def calc_gaslimit(parent)
125
+ config = parent.config
126
+ decay = parent.gas_limit / config[:gaslimit_ema_factor]
127
+ new_contribution = ((parent.gas_used * config[:blklim_factor_nom]) / config[:blklim_factor_den] / config[:gaslimit_ema_factor])
128
+
129
+ gl = [parent.gas_limit - decay + new_contribution, config[:min_gas_limit]].max
130
+ if gl < config[:genesis_gas_limit]
131
+ gl2 = parent.gas_limit + decay
132
+ gl = [config[:genesis_gas_limit], gl2].min
133
+ end
134
+ raise ValueError, "invalid gas limit" unless check_gaslimit(parent, gl)
135
+
136
+ gl
137
+ end
138
+
139
+ def check_gaslimit(parent, gas_limit)
140
+ config = parent.config
141
+ adjmax = parent.gas_limit / config[:gaslimit_adjmax_factor]
142
+ (gas_limit - parent.gas_limit).abs <= adjmax && gas_limit >= parent.config[:min_gas_limit]
143
+ end
144
+
145
+ ##
146
+ # Build the genesis block.
147
+ #
148
+ def genesis(env, options={})
149
+ allowed_args = %i(start_alloc bloom prevhash coinbase difficulty gas_limit gas_used timestamp extra_data mixhash nonce)
150
+ invalid_options = options.keys - allowed_args
151
+ raise ArgumentError, "invalid options: #{invalid_options}" unless invalid_options.empty?
152
+
153
+ start_alloc = options[:start_alloc] || env.config[:genesis_initial_alloc]
154
+
155
+ header = BlockHeader.new(
156
+ prevhash: options[:prevhash] || env.config[:genesis_prevhash],
157
+ uncles_hash: Utils.keccak256_rlp([]),
158
+ coinbase: options[:coinbase] || env.config[:genesis_coinbase],
159
+ state_root: Trie::BLANK_ROOT,
160
+ tx_list_root: Trie::BLANK_ROOT,
161
+ receipts_root: Trie::BLANK_ROOT,
162
+ bloom: options[:bloom] || 0,
163
+ difficulty: options[:difficulty] || env.config[:genesis_difficulty],
164
+ number: 0,
165
+ gas_limit: options[:gas_limit] || env.config[:genesis_gas_limit],
166
+ gas_used: options[:gas_used] || 0,
167
+ timestamp: options[:timestamp] || 0,
168
+ extra_data: options[:extra_data] || env.config[:genesis_extra_data],
169
+ mixhash: options[:mixhash] || env.config[:genesis_mixhash],
170
+ nonce: options[:nonce] || env.config[:genesis_nonce]
171
+ )
172
+
173
+ block = Block.new header, transaction_list: [], uncles: [], env: env
174
+
175
+ start_alloc.each do |addr, data|
176
+ addr = Utils.normalize_address addr
177
+
178
+ block.set_balance addr, data[:wei] if data[:wei]
179
+ block.set_balance addr, data[:balance] if data[:balance]
180
+ block.set_code addr, data[:code] if data[:code]
181
+ block.set_nonce addr, data[:nonce] if data[:nonce]
182
+
183
+ if data[:storage]
184
+ data[:storage].each {|k, v| block.set_storage_data addr, k, v }
185
+ end
186
+
187
+ end
188
+
189
+ block.commit_state
190
+ block.commit_state_db
191
+
192
+ # genesis block has predefined state root (so no additional
193
+ # finalization necessary)
194
+ block
195
+ end
196
+ end
197
+
198
+ ##
199
+ # Arguments in format of:
200
+ # `header, transaction_list=[], uncles=[], env=nil, parent=nil,
201
+ # making=false`
202
+ #
203
+ # @param args [Array] mix of arguments:
204
+ #
205
+ # * header {BlockHeader} optional. if given, will be used as block
206
+ # header. if not given, you must specify header by `options[:header]`
207
+ # * options (Hash) optional.
208
+ # - transaction_list {Array[Transaction]} a list of transactions
209
+ # which are replayed if the state given by the header is not known.
210
+ # If the state is known, `nil` can be used instead of the empty
211
+ # list.
212
+ # - uncles {Array[BlockHeader]} a list of the headers of the uncles
213
+ # of this block
214
+ # - env {Env} env including db in which the block's state,
215
+ # transactions and receipts are stored (required)
216
+ # - parent {Block} optional parent which if not given may have to be
217
+ # loaded from the database for replay
218
+ #
219
+ def initialize(*args)
220
+ header = args.first.instance_of?(BlockHeader) ? args.first : nil
221
+ options = args.last.instance_of?(Hash) ? args.last : {}
222
+
223
+ header = options.delete(:header) if options.has_key?(:header)
224
+ transaction_list = options.has_key?(:transaction_list) ? options[:transaction_list] : []
225
+ uncles = options.has_key?(:uncles) ? options[:uncles] : []
226
+ env = options.delete(:env)
227
+ parent = options.delete(:parent)
228
+ making = options.has_key?(:making) ? options.delete(:making) : false
229
+
230
+ raise ArgumentError, "No Env object given" unless env.instance_of?(Env)
231
+ raise ArgumentError, "No database object given" unless env.db.is_a?(DB::BaseDB)
232
+
233
+ @env = env
234
+ @db = env.db
235
+ @config = env.config
236
+
237
+ _set_field :header, header
238
+ _set_field :uncles, uncles
239
+
240
+ reset_cache
241
+ @get_transactions_cache = []
242
+
243
+ self.suicides = []
244
+ self.logs = []
245
+ self.log_listeners = []
246
+
247
+ self.refunds = 0
248
+ self.ether_delta = 0
249
+
250
+ self.ancestor_hashes = number > 0 ? [prevhash] : [nil]*256
251
+
252
+ validate_parent!(parent) if parent
253
+
254
+ original_values = {
255
+ bloom: bloom,
256
+ gas_used: gas_used,
257
+ timestamp: timestamp,
258
+ difficulty: difficulty,
259
+ uncles_hash: uncles_hash,
260
+ header_mutable: header.mutable?
261
+ }
262
+
263
+ make_mutable!
264
+ header.make_mutable!
265
+
266
+ @transactions = PruningTrie.new db
267
+ @receipts = PruningTrie.new db
268
+
269
+ initialize_state(transaction_list, parent, making)
270
+
271
+ validate_block!(original_values)
272
+ unless db.has_key?("validated:#{full_hash}")
273
+ if number == 0
274
+ db.put "validated:#{full_hash}", '1'
275
+ else
276
+ db.put_temporarily "validated:#{full_hash}", '1'
277
+ end
278
+ end
279
+
280
+ header.block = self
281
+ header.instance_variable_set :@_mutable, original_values[:header_mutable]
282
+ end
283
+
284
+ def add_listener(l)
285
+ log_listeners.push l
286
+ end
287
+
288
+ ##
289
+ # The binary block hash. This is equivalent to `header.full_hash`.
290
+ #
291
+ def full_hash
292
+ Utils.keccak256_rlp header
293
+ end
294
+
295
+ ##
296
+ # The hex encoded block hash. This is equivalent to `header.full_hash_hex`.
297
+ #
298
+ def full_hash_hex
299
+ Utils.encode_hex full_hash
300
+ end
301
+
302
+ def tx_list_root
303
+ @transactions.root_hash
304
+ end
305
+
306
+ def tx_list_root=(v)
307
+ @transactions = PruningTrie.new db, v
308
+ end
309
+
310
+ def receipts_root
311
+ @receipts.root_hash
312
+ end
313
+
314
+ def receipts_root=(v)
315
+ @receipts = PruningTrie.new db, v
316
+ end
317
+
318
+ def state_root
319
+ commit_state
320
+ @state.root_hash
321
+ end
322
+
323
+ def state_root=(v)
324
+ @state = SecureTrie.new PruningTrie.new(db, v)
325
+ reset_cache
326
+ end
327
+
328
+ def transaction_list
329
+ @transaction_count.times.map {|i| get_transaction(i) }
330
+ end
331
+
332
+ ##
333
+ # Validate the uncles of this block.
334
+ #
335
+ def validate_uncles
336
+ return false if Utils.keccak256_rlp(uncles) != uncles_hash
337
+ return false if uncles.size > config[:max_uncles]
338
+
339
+ uncles.each do |uncle|
340
+ raise InvalidUncles, "Cannot find uncle prevhash in db" unless db.include?(uncle.prevhash)
341
+ if uncle.number == number
342
+ logger.error "uncle at same block height", block: self
343
+ return false
344
+ end
345
+ end
346
+
347
+ max_uncle_depth = config[:max_uncle_depth]
348
+ ancestor_chain = [self] + get_ancestor_list(max_uncle_depth+1)
349
+ raise ValueError, "invalid ancestor chain" unless ancestor_chain.size == [number+1, max_uncle_depth+2].min
350
+
351
+ # Uncles of this block cannot be direct ancestors and cannot also be
352
+ # uncles included 1-6 blocks ago.
353
+ ineligible = []
354
+ ancestor_chain.safe_slice(1..-1).each {|a| ineligible.concat a.uncles }
355
+ ineligible.concat(ancestor_chain.map {|a| a.header })
356
+
357
+ eligible_ancestor_hashes = ancestor_chain.safe_slice(2..-1).map(&:full_hash)
358
+
359
+ uncles.each do |uncle|
360
+ parent = Block.find env, uncle.prevhash
361
+ return false if uncle.difficulty != Block.calc_difficulty(parent, uncle.timestamp)
362
+ return false unless uncle.check_pow
363
+
364
+ unless eligible_ancestor_hashes.include?(uncle.prevhash)
365
+ eligible = eligible_ancestor_hashes.map {|h| Utils.encode_hex(h) }
366
+ logger.error "Uncle does not have a valid ancestor", block: self, eligible: eligible, uncle_prevhash: Utils.encode_hex(uncle.prevhash)
367
+ return false
368
+ end
369
+
370
+ if ineligible.include?(uncle)
371
+ logger.error "Duplicate uncle", block: self, uncle: Utils.encode_hex(Utils.keccak256_rlp(uncle))
372
+ return false
373
+ end
374
+
375
+ # FIXME: what if uncles include previously rewarded uncle?
376
+ ineligible.push uncle
377
+ end
378
+
379
+ true
380
+ end
381
+
382
+ def add_refund(x)
383
+ self.refunds += x
384
+ end
385
+
386
+ ##
387
+ # Add a transaction to the transaction trie.
388
+ #
389
+ # Note that this does not execute anything, i.e. the state is not updated.
390
+ #
391
+ def add_transaction_to_list(tx)
392
+ k = RLP.encode @transaction_count
393
+ @transactions[k] = RLP.encode(tx)
394
+
395
+ r = mk_transaction_receipt tx
396
+ @receipts[k] = RLP.encode(r)
397
+
398
+ self.bloom |= r.bloom
399
+ @transaction_count += 1
400
+ end
401
+
402
+ def build_external_call(tx)
403
+ ExternalCall.new self, tx
404
+ end
405
+
406
+ def apply_transaction(tx)
407
+ validate_transaction tx
408
+
409
+ intrinsic_gas = get_intrinsic_gas tx
410
+
411
+ logger.debug "apply transaction", tx: tx.log_dict
412
+ increment_nonce tx.sender
413
+
414
+ # buy startgas
415
+ delta_balance tx.sender, -tx.startgas*tx.gasprice
416
+
417
+ message_gas = tx.startgas - intrinsic_gas
418
+ message_data = VM::CallData.new tx.data.bytes, 0, tx.data.size
419
+ message = VM::Message.new tx.sender, tx.to, tx.value, message_gas, message_data, code_address: tx.to
420
+
421
+ ec = build_external_call tx
422
+
423
+ if tx.to.true? && tx.to != Address::CREATE_CONTRACT
424
+ result, gas_remained, data = ec.apply_msg message
425
+ logger.debug "_res_", result: result, gas_remained: gas_remained, data: data
426
+ else # CREATE
427
+ result, gas_remained, data = ec.create message
428
+ raise ValueError, "gas remained is not numeric" unless gas_remained.is_a?(Numeric)
429
+ logger.debug "_create_", result: result, gas_remained: gas_remained, data: data
430
+ end
431
+ raise ValueError, "gas remained cannot be negative" unless gas_remained >= 0
432
+ logger.debug "TX APPLIED", result: result, gas_remained: gas_remained, data: data
433
+
434
+ if result.true?
435
+ logger.debug "TX SUCCESS", data: data
436
+
437
+ gas_used = tx.startgas - gas_remained
438
+
439
+ self.refunds += self.suicides.uniq.size * Opcodes::GSUICIDEREFUND
440
+ if refunds > 0
441
+ gas_refund = [refunds, gas_used/2].min
442
+
443
+ logger.debug "Refunding", gas_refunded: gas_refund
444
+ gas_remained += gas_refund
445
+ gas_used -= gas_refund
446
+ self.refunds = 0
447
+ end
448
+
449
+ delta_balance tx.sender, tx.gasprice * gas_remained
450
+ delta_balance coinbase, tx.gasprice * gas_used
451
+ self.gas_used += gas_used
452
+
453
+ output = tx.to.true? ? Utils.int_array_to_bytes(data) : data
454
+ success = 1
455
+ else # 0 = OOG failure in both cases
456
+ logger.debug "TX FAILED", reason: 'out of gas', startgas: tx.startgas, gas_remained: gas_remained
457
+
458
+ self.gas_used += tx.startgas
459
+ delta_balance coinbase, tx.gasprice*tx.startgas
460
+
461
+ output = Constant::BYTE_EMPTY
462
+ success = 0
463
+ end
464
+
465
+ commit_state
466
+
467
+ suicides.each do |s|
468
+ self.ether_delta -= get_balance(s)
469
+ set_balance s, 0 # TODO: redundant with code in SUICIDE op?
470
+ del_account s
471
+ end
472
+ self.suicides = []
473
+
474
+ add_transaction_to_list tx
475
+ self.logs = []
476
+
477
+ # TODO: change success to Bool type
478
+ return success, output
479
+ end
480
+
481
+ def get_intrinsic_gas(tx)
482
+ intrinsic_gas = tx.intrinsic_gas_used
483
+
484
+ if number >= config[:homestead_fork_blknum]
485
+ intrinsic_gas += Opcodes::CREATE[3] if tx.to.false? || tx.to == Address::CREATE_CONTRACT
486
+ end
487
+
488
+ intrinsic_gas
489
+ end
490
+
491
+ ##
492
+ # Get the `num`th transaction in this block.
493
+ #
494
+ # @raise [IndexError] if the transaction does not exist
495
+ #
496
+ def get_transaction(num)
497
+ index = RLP.encode num
498
+ tx = @transactions.get index
499
+
500
+ raise IndexError, "Transaction does not exist" if tx == Trie::BLANK_NODE
501
+ RLP.decode tx, sedes: Transaction
502
+ end
503
+
504
+ ##
505
+ # Build a list of all transactions in this block.
506
+ #
507
+ def get_transactions
508
+ # FIXME: such memoization is potentially buggy - what if pop b from and
509
+ # push a to the cache? size will not change while content changed.
510
+ if @get_transactions_cache.size != @transaction_count
511
+ @get_transactions_cache = transaction_list
512
+ end
513
+
514
+ @get_transactions_cache
515
+ end
516
+
517
+ ##
518
+ # helper to check if block contains a tx.
519
+ #
520
+ def get_transaction_hashes
521
+ @transaction_count.times.map do |i|
522
+ Utils.keccak256 @transactions[RLP.encode(i)]
523
+ end
524
+ end
525
+
526
+ def include_transaction?(tx_hash)
527
+ raise ArgumentError, "argument must be transaction hash in bytes" unless tx_hash.size == 32
528
+ get_transaction_hashes.include?(tx_hash)
529
+ end
530
+
531
+ def transaction_count
532
+ @transaction_count
533
+ end
534
+
535
+ ##
536
+ # Apply rewards and commit.
537
+ #
538
+ def finalize
539
+ delta = @config[:block_reward] + @config[:nephew_reward] * uncles.size
540
+
541
+ delta_balance coinbase, delta
542
+ self.ether_delta += delta
543
+
544
+ uncles.each do |uncle|
545
+ r = @config[:block_reward] * (@config[:uncle_depth_penalty_factor] + uncle.number - number) / @config[:uncle_depth_penalty_factor]
546
+
547
+ delta_balance uncle.coinbase, r
548
+ self.ether_delta += r
549
+ end
550
+
551
+ commit_state
552
+ end
553
+
554
+ ##
555
+ # Serialize the block to a readable hash.
556
+ #
557
+ # @param with_state [Bool] include state for all accounts
558
+ # @param full_transactions [Bool] include serialized transactions (hashes
559
+ # otherwise)
560
+ # @param with_storage_roots [Bool] if account states are included also
561
+ # include their storage roots
562
+ # @param with_uncles [Bool] include uncle hashes
563
+ #
564
+ # @return [Hash] a hash represents the block
565
+ #
566
+ def to_h(with_state: false, full_transactions: false, with_storage_roots: false, with_uncles: false)
567
+ b = { header: header.to_h }
568
+
569
+ txlist = []
570
+ get_transactions.each_with_index do |tx, i|
571
+ receipt_rlp = @receipts[RLP.encode(i)]
572
+ receipt = RLP.decode receipt_rlp, sedes: Receipt
573
+ txjson = full_transactions ? tx.to_h : tx.full_hash
574
+
575
+ logs = receipt.logs.map {|l| Log.serialize(l) }
576
+
577
+ txlist.push(
578
+ tx: txjson,
579
+ medstate: Utils.encode_hex(receipt.state_root),
580
+ gas: receipt.gas_used.to_s,
581
+ logs: logs,
582
+ bloom: Sedes.int256.serialize(receipt.bloom)
583
+ )
584
+ end
585
+ b[:transactions] = txlist
586
+
587
+ if with_state
588
+ state_dump = {}
589
+ @state.each do |address, v|
590
+ state_dump[Utils.encode_hex(address)] = account_to_dict(address, with_storage_root: with_storage_roots)
591
+ end
592
+ b[:state] = state_dump
593
+ end
594
+
595
+ if with_uncles
596
+ b[:uncles] = uncles.map {|u| RLP.decode(u, sedes: BlockHeader) }
597
+ end
598
+
599
+ b
600
+ end
601
+
602
+ def mining_hash
603
+ header.mining_hash
604
+ end
605
+
606
+ ##
607
+ # `true` if this block has a known parent, otherwise `false`.
608
+ #
609
+ def has_parent?
610
+ get_parent
611
+ true
612
+ rescue UnknownParentError
613
+ false
614
+ end
615
+
616
+ def get_parent_header
617
+ raise UnknownParentError, "Genesis block has no parent" if number == 0
618
+ BlockHeader.find db, prevhash
619
+ rescue KeyError
620
+ raise UnknownParentError, Utils.encode_hex(prevhash)
621
+ end
622
+
623
+ ##
624
+ # Get the parent of this block.
625
+ #
626
+ def get_parent
627
+ raise UnknownParentError, "Genesis block has no parent" if number == 0
628
+ Block.find env, prevhash
629
+ rescue KeyError
630
+ raise UnknownParentError, Utils.encode_hex(prevhash)
631
+ end
632
+
633
+ ##
634
+ # Get the summarized difficulty.
635
+ #
636
+ # If the summarized difficulty is not stored in the database, it will be
637
+ # calculated recursively and put int the database.
638
+ #
639
+ def chain_difficulty
640
+ return difficulty if genesis?
641
+
642
+ k = "difficulty:#{Utils.encode_hex(full_hash)}"
643
+ return Utils.decode_int(db.get(k)) if db.has_key?(k)
644
+
645
+ o = difficulty + get_parent.chain_difficulty
646
+ @state.db.put_temporarily k, Utils.encode_int(o)
647
+ o
648
+ end
649
+
650
+ ##
651
+ # Commit account caches. Write the account caches on the corresponding
652
+ # tries.
653
+ #
654
+ def commit_state
655
+ return if @journal.empty?
656
+
657
+ changes = []
658
+ addresses = @caches[:all].keys.sort
659
+
660
+ addresses.each do |addr|
661
+ acct = get_account addr
662
+
663
+ %i(balance nonce code storage).each do |field|
664
+ if v = @caches[field][addr]
665
+ changes.push [field, addr, v]
666
+ acct.send :"#{field}=", v
667
+ end
668
+ end
669
+
670
+ t = SecureTrie.new PruningTrie.new(db, acct.storage)
671
+ @caches.fetch("storage:#{addr}", {}).each do |k, v|
672
+ enckey = Utils.zpad Utils.coerce_to_bytes(k), 32
673
+ val = RLP.encode v
674
+ changes.push ['storage', addr, k, v]
675
+
676
+ v.true? ? t.set(enckey, val) : t.delete(enckey)
677
+ end
678
+
679
+ acct.storage = t.root_hash
680
+ @state[addr] = RLP.encode(acct)
681
+ end
682
+ logger.debug "delta changes=#{changes}"
683
+
684
+ reset_cache
685
+ db.put_temporarily "validated:#{full_hash}", '1'
686
+ end
687
+
688
+ def commit_state_db
689
+ @state.db.commit
690
+ end
691
+
692
+ def account_exists(address)
693
+ address = Utils.normalize_address address
694
+ @state[address].size > 0 || @caches[:all].has_key?(address)
695
+ end
696
+
697
+ def add_log(log)
698
+ logs.push log
699
+ log_listeners.each {|l| l.call log }
700
+ end
701
+
702
+ ##
703
+ # Increase the balance of an account.
704
+ #
705
+ # @param address [String] the address of the account (binary or hex string)
706
+ # @param value [Integer] can be positive or negative
707
+ #
708
+ # @return [Bool] return `true` if successful, otherwise `false`
709
+ #
710
+ def delta_balance(address, value)
711
+ delta_account_item(address, :balance, value)
712
+ end
713
+
714
+ ##
715
+ # Reset cache and journal without commiting any changes.
716
+ #
717
+ def reset_cache
718
+ @caches = {
719
+ all: {},
720
+ balance: {},
721
+ nonce: {},
722
+ code: {},
723
+ storage: {}
724
+ }
725
+ @journal = []
726
+ end
727
+
728
+ ##
729
+ # Make a snapshot of the current state to enable later reverting.
730
+ #
731
+ def snapshot
732
+ { state: @state.root_hash,
733
+ gas: gas_used,
734
+ txs: @transactions,
735
+ txcount: @transaction_count,
736
+ refunds: refunds,
737
+ suicides: suicides,
738
+ suicides_size: suicides.size,
739
+ logs: logs,
740
+ logs_size: logs.size,
741
+ journal: @journal, # pointer to reference, so is not static
742
+ journal_size: @journal.size,
743
+ ether_delta: ether_delta
744
+ }
745
+ end
746
+
747
+ ##
748
+ # Revert to a previously made snapshot.
749
+ #
750
+ # Reverting is for example neccessary when a contract runs out of gas
751
+ # during execution.
752
+ #
753
+ def revert(mysnapshot)
754
+ logger.debug "REVERTING"
755
+
756
+ @journal = mysnapshot[:journal]
757
+ # if @journal changed after snapshot
758
+ while @journal.size > mysnapshot[:journal_size]
759
+ cache, index, prev, post = @journal.pop
760
+ logger.debug "revert journal", cache: cache, index: index, prev: prev, post: post
761
+ if prev
762
+ @caches[cache][index] = prev
763
+ else
764
+ @caches[cache].delete index
765
+ end
766
+ end
767
+
768
+ self.suicides = mysnapshot[:suicides]
769
+ suicides.pop while suicides.size > mysnapshot[:suicides_size]
770
+
771
+ self.logs = mysnapshot[:logs]
772
+ logs.pop while logs.size > mysnapshot[:logs_size]
773
+
774
+ self.refunds = mysnapshot[:refunds]
775
+ self.gas_used = mysnapshot[:gas]
776
+ self.ether_delta = mysnapshot[:ether_delta]
777
+
778
+ @transactions = mysnapshot[:txs]
779
+ @transaction_count = mysnapshot[:txcount]
780
+
781
+ @state.set_root_hash mysnapshot[:state]
782
+
783
+ @get_transactions_cache = []
784
+ end
785
+
786
+ ##
787
+ # Get the receipt of the `num`th transaction.
788
+ #
789
+ # @raise [IndexError] if receipt at index is not found
790
+ #
791
+ # @return [Receipt]
792
+ #
793
+ def get_receipt(num)
794
+ index = RLP.encode num
795
+ receipt = @receipts[index]
796
+
797
+ if receipt == Trie::BLANK_NODE
798
+ raise IndexError, "Receipt does not exist"
799
+ else
800
+ RLP.decode receipt, sedes: Receipt
801
+ end
802
+ end
803
+
804
+ ##
805
+ # Build a list of all receipts in this block.
806
+ #
807
+ def get_receipts
808
+ receipts = []
809
+ i = 0
810
+ loop do
811
+ begin
812
+ receipts.push get_receipt(i)
813
+ rescue IndexError
814
+ return receipts
815
+ end
816
+ end
817
+ end
818
+
819
+ ##
820
+ # Get the nonce of an account.
821
+ #
822
+ # @param address [String] the address of the account (binary or hex string)
823
+ #
824
+ # @return [Integer] the nonce value
825
+ #
826
+ def get_nonce(address)
827
+ get_account_item address, :nonce
828
+ end
829
+
830
+ ##
831
+ # Set the nonce of an account.
832
+ #
833
+ # @param address [String] the address of the account (binary or hex string)
834
+ # @param value [Integer] the new nonce
835
+ #
836
+ # @return [Bool] `true` if successful, otherwise `false`
837
+ #
838
+ def set_nonce(address, value)
839
+ set_account_item address, :nonce, value
840
+ end
841
+
842
+ ##
843
+ # Increment the nonce of an account.
844
+ #
845
+ # @param address [String] the address of the account (binary or hex string)
846
+ #
847
+ # @return [Bool] `true` if successful, otherwise `false`
848
+ #
849
+ def increment_nonce(address)
850
+ if get_nonce(address) == 0
851
+ delta_account_item address, :nonce, config[:account_initial_nonce]+1
852
+ else
853
+ delta_account_item address, :nonce, 1
854
+ end
855
+ end
856
+
857
+ ##
858
+ # Get the balance of an account.
859
+ #
860
+ # @param address [String] the address of the account (binary or hex string)
861
+ #
862
+ # @return [Integer] balance value
863
+ #
864
+ def get_balance(address)
865
+ get_account_item address, :balance
866
+ end
867
+
868
+ ##
869
+ # Set the balance of an account.
870
+ #
871
+ # @param address [String] the address of the account (binary or hex string)
872
+ # @param value [Integer] the new balance value
873
+ #
874
+ # @return [Bool] `true` if successful, otherwise `false`
875
+ #
876
+ def set_balance(address, value)
877
+ set_account_item address, :balance, value
878
+ end
879
+
880
+ ##
881
+ # Increase the balance of an account.
882
+ #
883
+ # @param address [String] the address of the account (binary or hex string)
884
+ # @param value [Integer] can be positive or negative
885
+ #
886
+ # @return [Bool] `true` if successful, otherwise `false`
887
+ #
888
+ def delta_balance(address, value)
889
+ delta_account_item address, :balance, value
890
+ end
891
+
892
+ ##
893
+ # Transfer a value between two account balance.
894
+ #
895
+ # @param from [String] the address of the sending account (binary or hex
896
+ # string)
897
+ # @param to [String] the address of the receiving account (binary or hex
898
+ # string)
899
+ # @param value [Integer] the (positive) value to send
900
+ #
901
+ # @return [Bool] `true` if successful, otherwise `false`
902
+ #
903
+ def transfer_value(from, to, value)
904
+ raise ArgumentError, "value must be greater or equal than zero" unless value >= 0
905
+ delta_balance(from, -value) && delta_balance(to, value)
906
+ end
907
+
908
+ ##
909
+ # Get the code of an account.
910
+ #
911
+ # @param address [String] the address of the account (binary or hex string)
912
+ #
913
+ # @return [String] account code
914
+ #
915
+ def get_code(address)
916
+ get_account_item address, :code
917
+ end
918
+
919
+ ##
920
+ # Set the code of an account.
921
+ #
922
+ # @param address [String] the address of the account (binary or hex string)
923
+ # @param value [String] the new code bytes
924
+ #
925
+ # @return [Bool] `true` if successful, otherwise `false`
926
+ #
927
+ def set_code(address, value)
928
+ set_account_item address, :code, value
929
+ end
930
+
931
+ ##
932
+ # Get the trie holding an account's storage.
933
+ #
934
+ # @param address [String] the address of the account (binary or hex string)
935
+ #
936
+ # @return [Trie] the storage trie of account
937
+ #
938
+ def get_storage(address)
939
+ storage_root = get_account_item address, :storage
940
+ SecureTrie.new PruningTrie.new(db, storage_root)
941
+ end
942
+
943
+ def reset_storage(address)
944
+ set_account_item address, :storage, Constant::BYTE_EMPTY
945
+
946
+ cache_key = "storage:#{address}"
947
+ if @caches.has_key?(cache_key)
948
+ @caches[cache_key].each {|k, v| set_and_journal cache_key, k, 0 }
949
+ end
950
+ end
951
+
952
+ ##
953
+ # Get a specific item in the storage of an account.
954
+ #
955
+ # @param address [String] the address of the account (binary or hex string)
956
+ # @param index [Integer] the index of the requested item in the storage
957
+ #
958
+ # @return [Integer] the value at storage index
959
+ #
960
+ def get_storage_data(address, index)
961
+ address = Utils.normalize_address address
962
+
963
+ cache = @caches["storage:#{address}"]
964
+ return cache[index] if cache && cache.has_key?(index)
965
+
966
+ key = Utils.zpad Utils.coerce_to_bytes(index), 32
967
+ value = get_storage(address)[key]
968
+
969
+ value.true? ? RLP.decode(value, sedes: Sedes.big_endian_int) : 0
970
+ end
971
+
972
+ ##
973
+ # Set a specific item in the storage of an account.
974
+ #
975
+ # @param address [String] the address of the account (binary or hex string)
976
+ # @param index [Integer] the index of the requested item in the storage
977
+ # @param value [Integer] the new value of the item
978
+ #
979
+ def set_storage_data(address, index, value)
980
+ address = Utils.normalize_address address
981
+
982
+ cache_key = "storage:#{address}"
983
+ unless @caches.has_key?(cache_key)
984
+ @caches[cache_key] = {}
985
+ set_and_journal :all, address, true
986
+ end
987
+
988
+ set_and_journal cache_key, index, value
989
+ end
990
+
991
+ ##
992
+ # Delete an account.
993
+ #
994
+ # @param address [String] the address of the account (binary or hex string)
995
+ #
996
+ def del_account(address)
997
+ address = Utils.normalize_address address
998
+ commit_state
999
+ @state.delete address
1000
+ end
1001
+
1002
+ ##
1003
+ # Serialize an account to a hash with human readable entries.
1004
+ #
1005
+ # @param address [String] the account address
1006
+ # @param with_storage_root [Bool] include the account's storage root
1007
+ # @param with_storage [Bool] include the whole account's storage
1008
+ #
1009
+ # @return [Hash] hash represent the account
1010
+ #
1011
+ def account_to_dict(address, with_storage_root: false, with_storage: true)
1012
+ address = Utils.normalize_address address
1013
+
1014
+ # if there are uncommited account changes the current storage root is
1015
+ # meaningless
1016
+ raise ArgumentError, "cannot include storage root with uncommited account changes" if with_storage_root && !@journal.empty?
1017
+
1018
+ h = {}
1019
+ account = get_account address
1020
+
1021
+ h[:nonce] = (@caches[:nonce][address] || account.nonce).to_s
1022
+ h[:balance] = (@caches[:balance][address] || account.balance).to_s
1023
+
1024
+ code = @caches[:code][address] || account.code
1025
+ h[:code] = "0x#{Utils.encode_hex code}"
1026
+
1027
+ storage_trie = SecureTrie.new PruningTrie.new(db, account.storage)
1028
+ h[:storage_root] = Utils.encode_hex storage_trie.root_hash if with_storage_root
1029
+ if with_storage
1030
+ h[:storage] = {}
1031
+ sh = storage_trie.to_h
1032
+
1033
+ cache = @caches["storage:#{address}"] || {}
1034
+ keys = cache.keys.map {|k| Utils.zpad Utils.coerce_to_bytes(k), 32 }
1035
+
1036
+ (sh.keys + keys).each do |k|
1037
+ hexkey = "0x#{Utils.encode_hex Utils.zunpad(k)}"
1038
+
1039
+ v = cache[Utils.big_endian_to_int(k)]
1040
+ if v.true?
1041
+ h[:storage][hexkey] = "0x#{Utils.encode_hex Utils.int_to_big_endian(v)}"
1042
+ else
1043
+ v = sh[k]
1044
+ h[:storage][hexkey] = "0x#{Utils.encode_hex RLP.decode(v)}" if v
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ h
1050
+ end
1051
+
1052
+ ##
1053
+ # Return `n` ancestors of this block.
1054
+ #
1055
+ # @return [Array] array of ancestors in format of `[parent, parent.parent, ...]
1056
+ #
1057
+ def get_ancestor_list(n)
1058
+ raise ArgumentError, "n must be greater or equal than zero" unless n >= 0
1059
+
1060
+ return [] if n == 0 || number == 0
1061
+ parent = get_parent
1062
+ [parent] + parent.get_ancestor_list(n-1)
1063
+ end
1064
+
1065
+ def get_ancestor_hash(n)
1066
+ raise ArgumentError, "n must be greater than 0 and less or equal than 256" unless n > 0 && n <= 256
1067
+
1068
+ while ancestor_hashes.size < n
1069
+ if number == ancestor_hashes.size - 1
1070
+ ancestor_hashes.push nil
1071
+ else
1072
+ ancestor_hashes.push self.class.find(env, ancestor_hashes[-1]).get_parent().full_hash
1073
+ end
1074
+ end
1075
+
1076
+ ancestor_hashes[n-1]
1077
+ end
1078
+
1079
+ def genesis?
1080
+ number == 0
1081
+ end
1082
+
1083
+ ##
1084
+ # Two blocks are equal iff they have the same hash.
1085
+ #
1086
+ def ==(other)
1087
+ (other.instance_of?(Block) || other.instance_of?(CachedBlock)) &&
1088
+ full_hash == other.full_hash
1089
+ end
1090
+
1091
+ def hash
1092
+ Utils.big_endian_to_int full_hash
1093
+ end
1094
+
1095
+ def >(other)
1096
+ number > other.number
1097
+ end
1098
+
1099
+ def <(other)
1100
+ number < other.number
1101
+ end
1102
+
1103
+ def to_s
1104
+ "#<#{self.class.name}:#{object_id} ##{number} #{Utils.encode_hex full_hash[0,8]}>"
1105
+ end
1106
+ alias :inspect :to_s
1107
+
1108
+ private
1109
+
1110
+ def logger
1111
+ @logger ||= Logger.new 'eth.block'
1112
+ end
1113
+
1114
+ def initialize_state(transaction_list, parent, making)
1115
+ state_unknown =
1116
+ prevhash != @config[:genesis_prevhash] &&
1117
+ number != 0 &&
1118
+ header.state_root != PruningTrie::BLANK_ROOT &&
1119
+ (header.state_root.size != 32 || !db.has_key?("validated:#{full_hash}")) &&
1120
+ !making
1121
+
1122
+ if state_unknown
1123
+ raise ArgumentError, "transaction list cannot be nil" unless transaction_list
1124
+
1125
+ parent ||= get_parent_header
1126
+ @state = SecureTrie.new PruningTrie.new(db, parent.state_root)
1127
+ @transaction_count = 0 # TODO - should always equal @transactions.size
1128
+ self.gas_used = 0
1129
+
1130
+ transaction_list.each {|tx| apply_transaction tx }
1131
+
1132
+ finalize
1133
+ else # trust the state root in the header
1134
+ @state = SecureTrie.new PruningTrie.new(db, header._state_root)
1135
+ @transaction_count = 0
1136
+
1137
+ transaction_list.each {|tx| add_transaction_to_list(tx) } if transaction_list
1138
+ raise ValidationError, "Transaction list root hash does not match" if @transactions.root_hash != header.tx_list_root
1139
+
1140
+ # receipts trie populated by add_transaction_to_list is incorrect (it
1141
+ # doesn't know intermediate states), so reset it
1142
+ @receipts = PruningTrie.new db, header.receipts_root
1143
+ end
1144
+ end
1145
+
1146
+ ##
1147
+ # Validate block (header) against previous block.
1148
+ #
1149
+ def validate_parent!(parent)
1150
+ raise ValidationError, "Parent lives in different database" if parent && db != parent.db && db.db != parent.db # TODO: refactor the db.db mess
1151
+ raise ValidationError, "Block's prevhash and parent's hash do not match" if prevhash != parent.full_hash
1152
+ raise ValidationError, "Block's number is not the successor of its parent number" if number != parent.number+1
1153
+ raise ValidationError, "Block's gaslimit is inconsistent with its parent's gaslimit" unless Block.check_gaslimit(parent, gas_limit)
1154
+ raise ValidationError, "Block's difficulty is inconsistent with its parent's difficulty" if difficulty != Block.calc_difficulty(parent, timestamp)
1155
+ raise ValidationError, "Gas used exceeds gas limit" if gas_used > gas_limit
1156
+ raise ValidationError, "Timestamp equal to or before parent" if timestamp <= parent.timestamp
1157
+ raise ValidationError, "Timestamp way too large" if timestamp > Constant::UINT_MAX
1158
+ end
1159
+
1160
+ ##
1161
+ # Validate (transaction applied) block against its header, plus fields and
1162
+ # value check.
1163
+ #
1164
+ def validate_block!(original_values)
1165
+ raise InvalidBlock, "gas_used mistmatch actual: #{gas_used} target: #{original_values[:gas_used]}" if gas_used != original_values[:gas_used]
1166
+ raise InvalidBlock, "timestamp mistmatch actual: #{timestamp} target: #{original_values[:timestamp]}" if timestamp != original_values[:timestamp]
1167
+ raise InvalidBlock, "difficulty mistmatch actual: #{difficulty} target: #{original_values[:difficulty]}" if difficulty != original_values[:difficulty]
1168
+ raise InvalidBlock, "bloom mistmatch actual: #{bloom} target: #{original_values[:bloom]}" if bloom != original_values[:bloom]
1169
+
1170
+ uh = Utils.keccak256_rlp uncles
1171
+ raise InvalidBlock, "uncles_hash mistmatch actual: #{uh} target: #{original_values[:uncles_hash]}" if uh != original_values[:uncles_hash]
1172
+
1173
+ raise InvalidBlock, "header must reference no block" unless header.block.nil?
1174
+
1175
+ raise InvalidBlock, "state_root mistmatch actual: #{Utils.encode_hex @state.root_hash} target: #{Utils.encode_hex header.state_root}" if @state.root_hash != header.state_root
1176
+ raise InvalidBlock, "tx_list_root mistmatch actual: #{@transactions.root_hash} target: #{header.tx_list_root}" if @transactions.root_hash != header.tx_list_root
1177
+ raise InvalidBlock, "receipts_root mistmatch actual: #{@receipts.root_hash} target: #{header.receipts_root}" if @receipts.root_hash != header.receipts_root
1178
+
1179
+ raise ValueError, "Block is invalid" unless validate_fields
1180
+
1181
+ raise ValueError, "Extra data cannot exceed #{config[:max_extradata_length]} bytes" if header.extra_data.size > config[:max_extradata_length]
1182
+ raise ValueError, "Coinbase cannot be empty address" if header.coinbase.false?
1183
+ raise ValueError, "State merkle root of block #{self} not found in database" unless @state.root_hash_valid?
1184
+ raise ValueError, "PoW check failed" if !genesis? && nonce.true? && !header.check_pow
1185
+ end
1186
+
1187
+ def validate_transaction(tx)
1188
+ raise UnsignedTransactionError.new(tx) unless tx.sender
1189
+
1190
+ acct_nonce = get_nonce tx.sender
1191
+ raise InvalidNonce, "#{tx}: nonce actual: #{tx.nonce} target: #{acct_nonce}" if acct_nonce != tx.nonce
1192
+
1193
+ min_gas = get_intrinsic_gas tx
1194
+ raise InsufficientStartGas, "#{tx}: startgas actual: #{tx.startgas} target: #{min_gas}" if tx.startgas < min_gas
1195
+
1196
+ total_cost = tx.value + tx.gasprice * tx.startgas
1197
+ balance = get_balance tx.sender
1198
+ raise InsufficientBalance, "#{tx}: balance actual: #{balance} target: #{total_cost}" if balance < total_cost
1199
+
1200
+ accum_gas = gas_used + tx.startgas
1201
+ raise BlockGasLimitReached, "#{tx}: gaslimit actual: #{accum_gas} target: #{gas_limit}" if accum_gas > gas_limit
1202
+
1203
+ tx.check_low_s if number >= config[:homestead_fork_blknum]
1204
+
1205
+ true
1206
+ end
1207
+
1208
+ ##
1209
+ # Check that the values of all fields are well formed.
1210
+ #
1211
+ # Serialize and deserialize and check that the values didn't change.
1212
+ #
1213
+ def validate_fields
1214
+ l = Block.serialize self
1215
+ RLP.decode(RLP.encode(l)) == l
1216
+ end
1217
+
1218
+ ##
1219
+ # Add a value to an account item.
1220
+ #
1221
+ # If the resulting value would be negative, it is left unchanged and
1222
+ # `false` is returned.
1223
+ #
1224
+ # @param address [String] the address of the account (binary or hex string)
1225
+ # @param param [Symbol] the parameter to increase or decrease (`:nonce`,
1226
+ # `:balance`, `:storage`, or `:code`)
1227
+ # @param value [Integer] can be positive or negative
1228
+ #
1229
+ # @return [Bool] `true` if the operation was successful, `false` if not
1230
+ #
1231
+ def delta_account_item(address, param, value)
1232
+ new_value = get_account_item(address, param) + value
1233
+ return false if new_value < 0
1234
+
1235
+ set_account_item(address, param, new_value % 2**256)
1236
+ true
1237
+ end
1238
+
1239
+ ##
1240
+ # Get a specific parameter of a specific account.
1241
+ #
1242
+ # @param address [String] the address of the account (binary or hex string)
1243
+ # @param param [Symbol] the requested parameter (`:nonce`, `:balance`,
1244
+ # `:storage` or `:code`)
1245
+ #
1246
+ # @return [Object] the value
1247
+ #
1248
+ def get_account_item(address, param)
1249
+ address = Utils.normalize_address address, allow_blank: true
1250
+ return @caches[param][address] if @caches[param].has_key?(address)
1251
+
1252
+ account = get_account address
1253
+ v = account.send param
1254
+ @caches[param][address] = v
1255
+ v
1256
+ end
1257
+
1258
+ ##
1259
+ # Set a specific parameter of a specific account.
1260
+ #
1261
+ # @param address [String] the address of the account (binary or hex string)
1262
+ # @param param [Symbol] the requested parameter (`:nonce`, `:balance`,
1263
+ # `:storage` or `:code`)
1264
+ # @param value [Object] the new value
1265
+ #
1266
+ def set_account_item(address, param, value)
1267
+ raise ArgumentError, "invalid address: #{address}" unless address.size == 20 || address.size == 40
1268
+ address = Utils.decode_hex(address) if address.size == 40
1269
+
1270
+ set_and_journal(param, address, value)
1271
+ set_and_journal(:all, address, true)
1272
+ end
1273
+
1274
+ ##
1275
+ # Get the account with the given address.
1276
+ #
1277
+ # Note that this method ignores cached account items.
1278
+ #
1279
+ def get_account(address)
1280
+ address = Utils.normalize_address address, allow_blank: true
1281
+ rlpdata = @state[address]
1282
+
1283
+ if rlpdata == Trie::BLANK_NODE
1284
+ Account.build_blank db, config[:account_initial_nonce]
1285
+ else
1286
+ RLP.decode(rlpdata, sedes: Account, db: db).tap do |acct|
1287
+ acct.make_mutable!
1288
+ acct._cached_rlp = nil
1289
+ end
1290
+ end
1291
+ end
1292
+
1293
+ ##
1294
+ # @param ns [Symbol] cache namespace
1295
+ # @param k [String] cache key
1296
+ # @param v [Object] cache value
1297
+ #
1298
+ def set_and_journal(ns, k, v)
1299
+ prev = @caches[ns][k]
1300
+ if prev != v
1301
+ @journal.push [ns, k, prev, v]
1302
+ @caches[ns][k] = v
1303
+ end
1304
+ end
1305
+
1306
+ def mk_transaction_receipt(tx)
1307
+ Receipt.new state_root, gas_used, logs
1308
+ end
1309
+
1310
+ end
1311
+ end