Module: Reth::JSONRPC::Handler

Includes:
Helper
Included in:
App
Defined in:
lib/reth/jsonrpc/handler.rb

Instance Method Summary collapse

Methods included from Helper

#bytes_to_hex, #chain, #decode_block_tag, #discovery, #encode_block, #encode_loglist, #encode_tx, #get_block, #get_compilers, #get_ivar_value, #hex_to_bytes, #hex_to_int, #int_to_hex, #peermanager

Instance Method Details

#handle_db_getHex(db_name, k) ⇒ Object

Raises:

  • (NotImplementedError)


360
361
362
# File 'lib/reth/jsonrpc/handler.rb', line 360

def handle_db_getHex(db_name, k)
  raise NotImplementedError
end

#handle_db_getString(db_name, k) ⇒ Object

Raises:

  • (NotImplementedError)


352
353
354
# File 'lib/reth/jsonrpc/handler.rb', line 352

def handle_db_getString(db_name, k)
  raise NotImplementedError
end

#handle_db_putHex(db_name, k, v) ⇒ Object

Raises:

  • (NotImplementedError)


356
357
358
# File 'lib/reth/jsonrpc/handler.rb', line 356

def handle_db_putHex(db_name, k, v)
  raise NotImplementedError
end

#handle_db_putString(db_name, k, v) ⇒ Object

Raises:

  • (NotImplementedError)


348
349
350
# File 'lib/reth/jsonrpc/handler.rb', line 348

def handle_db_putString(db_name, k, v)
  raise NotImplementedError
end

#handle_eth_accountsObject



60
61
62
# File 'lib/reth/jsonrpc/handler.rb', line 60

def handle_eth_accounts
  Account.test_accounts.keys
end

#handle_eth_blockNumberObject



64
65
66
# File 'lib/reth/jsonrpc/handler.rb', line 64

def handle_eth_blockNumber
  int_to_hex chain.chain.head.number
end

#handle_eth_call(obj, block_tag = 'pending') ⇒ Object



153
154
155
156
157
158
159
160
# File 'lib/reth/jsonrpc/handler.rb', line 153

def handle_eth_call(obj, block_tag='pending')
  success, output, _, _ = tentatively_execute obj, block_tag
  if success == 1
    bytes_to_hex output
  else
    false
  end
end

#handle_eth_coinbaseObject

TODO: real accounts



44
45
46
# File 'lib/reth/jsonrpc/handler.rb', line 44

def handle_eth_coinbase
  bytes_to_hex Ethereum::PrivateKey.new(Account.test_accounts.values.first).to_address
end

#handle_eth_compileLLL(code) ⇒ Object



276
277
278
279
# File 'lib/reth/jsonrpc/handler.rb', line 276

def handle_eth_compileLLL(code)
  compiler, method = get_compilers[:lll]
  bytes_to_hex compiler.send(method, code)
end

#handle_eth_compileSerpent(code) ⇒ Object



281
282
283
284
# File 'lib/reth/jsonrpc/handler.rb', line 281

def handle_eth_compileSerpent(code)
  compiler, method = get_compilers[:serpent]
  bytes_to_hex compiler.send(method, code)
end

#handle_eth_compileSolidity(code) ⇒ Object



271
272
273
274
# File 'lib/reth/jsonrpc/handler.rb', line 271

def handle_eth_compileSolidity(code)
  compiler, method = get_compilers[:solidity]
  compiler.send method, code
end

#handle_eth_estimateGas(obj, block_tag = 'pending') ⇒ Object



162
163
164
165
# File 'lib/reth/jsonrpc/handler.rb', line 162

def handle_eth_estimateGas(obj, block_tag='pending')
  _, _, block, test_block = tentatively_execute obj, block_tag
  test_block.gas_used - block.gas_used
end

#handle_eth_gasPriceObject



56
57
58
# File 'lib/reth/jsonrpc/handler.rb', line 56

def handle_eth_gasPrice
  int_to_hex 1
end

#handle_eth_getBalance(address, block_tag = @default_block) ⇒ Object



68
69
70
71
# File 'lib/reth/jsonrpc/handler.rb', line 68

def handle_eth_getBalance(address, block_tag=@default_block)
  block = get_block decode_block_tag(block_tag)
  int_to_hex block.get_balance(hex_to_bytes(address))
end

#handle_eth_getBlockByHash(blockhash, include_transactions) ⇒ Object



167
168
169
170
# File 'lib/reth/jsonrpc/handler.rb', line 167

def handle_eth_getBlockByHash(blockhash, include_transactions)
  block = get_block hex_to_bytes(blockhash)
  encode_block block, include_transactions
end

#handle_eth_getBlockByNumber(block_tag, include_transactions) ⇒ Object



172
173
174
175
176
# File 'lib/reth/jsonrpc/handler.rb', line 172

def handle_eth_getBlockByNumber(block_tag, include_transactions)
  block = get_block decode_block_tag(block_tag)
  pending = block_tag == 'pending'
  encode_block block, include_transactions, pending
end

#handle_eth_getBlockTransactionCountByHash(blockhash) ⇒ Object



83
84
85
86
# File 'lib/reth/jsonrpc/handler.rb', line 83

def handle_eth_getBlockTransactionCountByHash(blockhash)
  block = get_block hex_to_bytes(blockhash)
  int_to_hex block.transaction_count
end

#handle_eth_getBlockTransactionCountByNumber(number) ⇒ Object



88
89
90
91
# File 'lib/reth/jsonrpc/handler.rb', line 88

def handle_eth_getBlockTransactionCountByNumber(number)
  block = get_block hex_to_int(number)
  int_to_hex block.transaction_count
end

#handle_eth_getCode(address, block_tag = @default_block) ⇒ Object



103
104
105
106
# File 'lib/reth/jsonrpc/handler.rb', line 103

def handle_eth_getCode(address, block_tag=@default_block)
  block = get_block decode_block_tag(block_tag)
  bytes_to_hex block.get_code(hex_to_bytes(address))
end

#handle_eth_getCompilersObject



267
268
269
# File 'lib/reth/jsonrpc/handler.rb', line 267

def handle_eth_getCompilers
  get_compilers.keys
end

#handle_eth_getFilterChanges(id) ⇒ Object

Raises:

  • (ArgumentError)


309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/reth/jsonrpc/handler.rb', line 309

def handle_eth_getFilterChanges(id)
  id = hex_to_int id
  raise ArgumentError, "unknown filter id" unless Filter.include?(id)

  filter = Filter.find id
  if [BlockFilter,PendingTransactionFilter].include?(filter.class)
    filter.check.map {|block_or_tx| bytes_to_hex block_or_tx.full_hash }
  elsif filter.instance_of?(LogFilter)
    encode_loglist filter.new_logs
  else
    raise "invalid filter"
  end
end

#handle_eth_getFilterLogs(id) ⇒ Object

Raises:

  • (ArgumentError)


323
324
325
326
327
328
329
# File 'lib/reth/jsonrpc/handler.rb', line 323

def handle_eth_getFilterLogs(id)
  id = hex_to_int id
  raise ArgumentError, "unknown filter id" unless Filter.include?(id)

  filter = Filter.find id
  encode_loglist filter.logs
end

#handle_eth_getLogs(obj) ⇒ Object



331
332
333
334
# File 'lib/reth/jsonrpc/handler.rb', line 331

def handle_eth_getLogs(obj)
  filter = LogFilter.new(obj, @node.state.chain)
  encode_loglist filter.logs
end

#handle_eth_getStorageAt(address, key, block_tag = @default_block) ⇒ Object



73
74
75
76
# File 'lib/reth/jsonrpc/handler.rb', line 73

def handle_eth_getStorageAt(address, key, block_tag=@default_block)
  block = get_block decode_block_tag(block_tag)
  bytes_to_hex Utils.zpad_int(block.get_storage_data(hex_to_bytes(address), hex_to_int(key)))
end

#handle_eth_getTransactionByBlockHashAndIndex(blockhash, index) ⇒ Object



192
193
194
195
196
197
198
199
200
201
# File 'lib/reth/jsonrpc/handler.rb', line 192

def handle_eth_getTransactionByBlockHashAndIndex(blockhash, index)
  block = get_block blockhash
  i = hex_to_int index

  tx = block.get_transaction i
  pending = blockhash == 'pending'
  encode_tx tx, block, i, pending
rescue IndexError
  nil
end

#handle_eth_getTransactionByHash(txhash) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/reth/jsonrpc/handler.rb', line 178

def handle_eth_getTransactionByHash(txhash)
  tx, block, index = @node.state.chain.index.get_transaction hex_to_bytes(txhash)

  if @node.state.chain.in_main_branch?(block)
    encode_tx tx, block, index, false
  else
    nil
  end
rescue IndexError
  puts $!
  puts $!.backtrace.join("\n")
  nil
end

#handle_eth_getTransactionCount(address, block_tag = @default_block) ⇒ Object



78
79
80
81
# File 'lib/reth/jsonrpc/handler.rb', line 78

def handle_eth_getTransactionCount(address, block_tag=@default_block)
  block = get_block decode_block_tag(block_tag)
  int_to_hex block.get_nonce(hex_to_bytes(address))
end

#handle_eth_getTransactionReceipt(txhash) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/reth/jsonrpc/handler.rb', line 203

def handle_eth_getTransactionReceipt(txhash)
  tx, block, index = @node.state.chain.index.get_transaction hex_to_bytes(txhash)

  return nil unless @node.state.chain.in_main_branch?(block)

  receipt = block.get_receipt index
  h = {
    transactionHash: bytes_to_hex(tx.full_hash),
    transactionIndex: int_to_hex(index),
    blockHash: bytes_to_hex(block.full_hash),
    blockNumber: int_to_hex(block.number),
    cumulativeGasUsed: int_to_hex(receipt.gas_used),
    contractAddress: tx.creates ? bytes_to_hex(tx.creates) : nil
  }

  if index == 0
    h[:gasUsed] = int_to_hex(receipt.gas_used)
  else
    prev_receipt = block.get_receipt(index - 1)
    raise "invalid previous receipt" unless prev_receipt.gas_used < receipt.gas_used
    h[:gasUsed] = int_to_hex(receipt.gas_used - prev_receipt.gas_used)
  end

  logs = receipt.logs.each_with_index.map do |log, i|
    {
      log: log,
      log_idx: i,
      block: block,
      txhash: tx.full_hash,
      tx_idx: index,
      pending: false
    }
  end
  h[:logs] = encode_loglist logs

  h
rescue IndexError
  nil
end

#handle_eth_getUncleByBlockHashAndIndex(blockhash, index) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
# File 'lib/reth/jsonrpc/handler.rb', line 243

def handle_eth_getUncleByBlockHashAndIndex(blockhash, index)
  return nil if blockhash == 'pending'

  block = get_block blockhash
  i = hex_to_int index

  uncle = block.uncles[i]
  return nil unless uncle

  encode_block uncle, false, false, true
end

#handle_eth_getUncleByBlockNumberAndIndex(block_tag, index) ⇒ Object



255
256
257
258
259
260
261
262
263
264
265
# File 'lib/reth/jsonrpc/handler.rb', line 255

def handle_eth_getUncleByBlockNumberAndIndex(block_tag, index)
  return nil if block_tag == 'pending'

  block = get_block decode_block_tag(block_tag)
  i = hex_to_int index

  uncle = block.uncles[i]
  return nil unless uncle

  encode_block uncle, false, false, true
end

#handle_eth_getUncleCountByBlockHash(blockhash) ⇒ Object



93
94
95
96
# File 'lib/reth/jsonrpc/handler.rb', line 93

def handle_eth_getUncleCountByBlockHash(blockhash)
  block = get_block hex_to_bytes(blockhash)
  int_to_hex block.uncles.size
end

#handle_eth_getUncleCountByBlockNumber(number) ⇒ Object



98
99
100
101
# File 'lib/reth/jsonrpc/handler.rb', line 98

def handle_eth_getUncleCountByBlockNumber(number)
  block = get_block hex_to_int(number)
  int_to_hex block.uncles.size
end

#handle_eth_getWorkObject

Raises:

  • (NotImplementedError)


336
337
338
# File 'lib/reth/jsonrpc/handler.rb', line 336

def handle_eth_getWork
  raise NotImplementedError
end

#handle_eth_hashrateObject



52
53
54
# File 'lib/reth/jsonrpc/handler.rb', line 52

def handle_eth_hashrate
  int_to_hex 0
end

#handle_eth_miningObject



48
49
50
# File 'lib/reth/jsonrpc/handler.rb', line 48

def handle_eth_mining
  false
end

#handle_eth_newBlockFilterObject



290
291
292
# File 'lib/reth/jsonrpc/handler.rb', line 290

def handle_eth_newBlockFilter
  int_to_hex BlockFilter.create(@node.state.chain)
end

#handle_eth_newFilter(obj) ⇒ Object



286
287
288
# File 'lib/reth/jsonrpc/handler.rb', line 286

def handle_eth_newFilter(obj)
  int_to_hex LogFilter.create(obj, @node.state.chain)
end

#handle_eth_newPendingTransactionFilterObject



294
295
296
# File 'lib/reth/jsonrpc/handler.rb', line 294

def handle_eth_newPendingTransactionFilter
  int_to_hex PendingTransactionFilter.create(@node.state.chain)
end

#handle_eth_protocolVersionObject



28
29
30
# File 'lib/reth/jsonrpc/handler.rb', line 28

def handle_eth_protocolVersion
  ETHProtocol.version
end

#handle_eth_sendRawTransaction(data) ⇒ Object

Raises:

  • (NotImplementedError)


149
150
151
# File 'lib/reth/jsonrpc/handler.rb', line 149

def handle_eth_sendRawTransaction(data)
  raise NotImplementedError
end

#handle_eth_sendTransaction(obj) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/reth/jsonrpc/handler.rb', line 112

def handle_eth_sendTransaction(obj)
  to = hex_to_bytes(obj['to'] || '')

  startgas_hex = obj['gas'] || obj['startgas']
  startgas = startgas_hex ? hex_to_int(startgas_hex) : @default_startgas

  gas_price_hex = obj['gasPrice'] || obj['gasprice']
  gas_price = gas_price_hex ? hex_to_int(gas_price_hex) : @default_gas_price

  value = hex_to_int(obj['value'] || '')
  data = hex_to_bytes(obj['data'] || '')

  v = hex_to_int(obj['v'] || '')
  r = hex_to_int(obj['r'] || '')
  s = hex_to_int(obj['s'] || '')

  nonce = obj['nonce'] ? hex_to_int(obj['nonce']) : nil
  sender = hex_to_bytes(obj['from'] || @default_address)

  if v > 0
    raise "signed but no nonce provided" if nonce.nil?
    raise "invalid signature" unless r > 0 && s > 0
  else
    nonce ||= chain.chain.head_candidate.get_nonce(sender)

    addr = bytes_to_hex(sender)
    privkey = Account.test_accounts[addr]
    raise "no privkey found for address #{addr}" unless privkey
  end

  tx = Transaction.new nonce, gas_price, startgas, to, value, data, v, r, s
  # TODO

  puts "tx added: #{tx.log_dict}"
  bytes_to_hex tx.full_hash
end

#handle_eth_sign(address, data) ⇒ Object

Raises:

  • (NotImplementedError)


108
109
110
# File 'lib/reth/jsonrpc/handler.rb', line 108

def handle_eth_sign(address, data)
  raise NotImplementedError
end

#handle_eth_submitHashrateObject

Raises:

  • (NotImplementedError)


344
345
346
# File 'lib/reth/jsonrpc/handler.rb', line 344

def handle_eth_submitHashrate
  raise NotImplementedError
end

#handle_eth_submitWorkObject

Raises:

  • (NotImplementedError)


340
341
342
# File 'lib/reth/jsonrpc/handler.rb', line 340

def handle_eth_submitWork
  raise NotImplementedError
end

#handle_eth_syncingObject



32
33
34
35
36
37
38
39
40
41
# File 'lib/reth/jsonrpc/handler.rb', line 32

def handle_eth_syncing
  return false unless chain.syncing?

  synctask = chain.synchronizer.synctask
  {
    startingBlock: int_to_hex(synctask.start_block_number),
    currentBlock: int_to_hex(chain.chain.head.number),
    highestBlock: int_to_hex(synctask.end_block_number)
  }
end

#handle_eth_uninstallFilter(id) ⇒ Object



298
299
300
301
302
303
304
305
306
307
# File 'lib/reth/jsonrpc/handler.rb', line 298

def handle_eth_uninstallFilter(id)
  id = hex_to_int id

  if Filter.include?(id)
    Filter.delete id
    true
  else
    false
  end
end

#handle_net_listeningObject



20
21
22
# File 'lib/reth/jsonrpc/handler.rb', line 20

def handle_net_listening
  peermanager.num_peers < peermanager.config.p2p.min_peers
end

#handle_net_peerCountObject



24
25
26
# File 'lib/reth/jsonrpc/handler.rb', line 24

def handle_net_peerCount
  int_to_hex peermanager.num_peers
end

#handle_net_versionObject



16
17
18
# File 'lib/reth/jsonrpc/handler.rb', line 16

def handle_net_version
  discovery.protocol.class::VERSION
end

#handle_web3_clientVersionObject



7
8
9
# File 'lib/reth/jsonrpc/handler.rb', line 7

def handle_web3_clientVersion
  CLIENT_VERSION_STRING
end

#handle_web3_sha3(hex) ⇒ Object



11
12
13
14
# File 'lib/reth/jsonrpc/handler.rb', line 11

def handle_web3_sha3(hex)
  bytes = hex_to_bytes hex
  bytes_to_hex Utils.keccak256(bytes)
end

#tentatively_execute(obj, block_tag) ⇒ Object

Raises:

  • (ArgumentError)


364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/reth/jsonrpc/handler.rb', line 364

def tentatively_execute(obj, block_tag)
  raise ArgumentError, 'first parameter must be an object' unless obj.instance_of?(Hash)

  raise ArgumentError, 'missing message receiver (to)' unless obj['to']
  to = hex_to_bytes obj['to']

  block = get_block decode_block_tag(block_tag)
  snapshot_before = block.snapshot
  tx_root_before = snapshot_before[:txs].root_hash

  if block.has_parent?
    parent = block.get_parent
    test_block = Block.build_from_parent parent, block.coinbase, timestamp: block.timestamp

    block.get_transactions.each do |tx|
      success, output = test_block.apply_transaction tx
      raise "failed to prepare test block" if success == 0
    end
  else
    env = Env.new block.db
    test_block = Block.genesis env

    original = snapshot_before.dup
    original.delete :txs
    original = Marshal.load Marshal.dump(original) # deepcopy
    original[:txs] = Ethereum::Trie.new snapshot_before[:txs].db, snapshot_before[:txs].root_hash

    test_block = Block.genesis env
    test_block.revert original
  end

  startgas = obj['gas'] ? hex_to_int(obj['gas']) : (test_block.gas_limit - test_block.gas_used)
  gas_price = obj['gasPrice'] ? hex_to_int(obj['gasPrice']) : 0
  value = obj['value'] ? hex_to_int(obj['value']) : 0
  data = obj['data'] ? hex_to_bytes(obj['data']) : ''
  sender = obj['from'] ? hex_to_bytes(obj['from']) : Ethereum::Address::ZERO

  nonce = test_block.get_nonce sender
  tx = Transaction.new nonce, gas_price, startgas, to, value, data
  tx.sender = sender

  begin
    # FIXME: tx.check_low_s will raise exception if block is after homestead fork
    success, output = test_block.apply_transaction tx
  rescue Ethereum::InvalidTransaction
    puts $!
    puts $!.backtrace[0,10].join("\n")
    success = 0
  end

  snapshot_after = block.snapshot
  raise "real data should not be changed" unless snapshot_after == snapshot_before
  raise "real data should not be changed" unless snapshot_after[:txs].root_hash == tx_root_before

  return success, output, block, test_block
end