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