reth 0.1.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.
@@ -0,0 +1,424 @@
1
+ module Reth
2
+ module JSONRPC
3
+
4
+ module Handler
5
+ include Helper
6
+
7
+ def handle_web3_clientVersion
8
+ CLIENT_VERSION_STRING
9
+ end
10
+
11
+ def handle_web3_sha3(hex)
12
+ bytes = hex_to_bytes hex
13
+ bytes_to_hex Utils.keccak256(bytes)
14
+ end
15
+
16
+ def handle_net_version
17
+ discovery.protocol.class::VERSION
18
+ end
19
+
20
+ def handle_net_listening
21
+ peermanager.num_peers < peermanager.config.p2p.min_peers
22
+ end
23
+
24
+ def handle_net_peerCount
25
+ int_to_hex peermanager.num_peers
26
+ end
27
+
28
+ def handle_eth_protocolVersion
29
+ ETHProtocol.version
30
+ end
31
+
32
+ def handle_eth_syncing
33
+ return false unless chain.syncing?
34
+
35
+ synctask = chain.synchronizer.synctask
36
+ {
37
+ startingBlock: int_to_hex(synctask.start_block_number),
38
+ currentBlock: int_to_hex(chain.chain.head.number),
39
+ highestBlock: int_to_hex(synctask.end_block_number)
40
+ }
41
+ end
42
+
43
+ # TODO: real accounts
44
+ def handle_eth_coinbase
45
+ bytes_to_hex Ethereum::PrivateKey.new(Account.test_accounts.values.first).to_address
46
+ end
47
+
48
+ def handle_eth_mining
49
+ false
50
+ end
51
+
52
+ def handle_eth_hashrate
53
+ int_to_hex 0
54
+ end
55
+
56
+ def handle_eth_gasPrice
57
+ int_to_hex 1
58
+ end
59
+
60
+ def handle_eth_accounts
61
+ Account.test_accounts.keys
62
+ end
63
+
64
+ def handle_eth_blockNumber
65
+ int_to_hex chain.chain.head.number
66
+ end
67
+
68
+ def handle_eth_getBalance(address, block_tag=@default_block)
69
+ block = get_block decode_block_tag(block_tag)
70
+ int_to_hex block.get_balance(hex_to_bytes(address))
71
+ end
72
+
73
+ def handle_eth_getStorageAt(address, key, block_tag=@default_block)
74
+ block = get_block decode_block_tag(block_tag)
75
+ bytes_to_hex Utils.zpad_int(block.get_storage_data(hex_to_bytes(address), hex_to_int(key)))
76
+ end
77
+
78
+ def handle_eth_getTransactionCount(address, block_tag=@default_block)
79
+ block = get_block decode_block_tag(block_tag)
80
+ int_to_hex block.get_nonce(hex_to_bytes(address))
81
+ end
82
+
83
+ def handle_eth_getBlockTransactionCountByHash(blockhash)
84
+ block = get_block hex_to_bytes(blockhash)
85
+ int_to_hex block.transaction_count
86
+ end
87
+
88
+ def handle_eth_getBlockTransactionCountByNumber(number)
89
+ block = get_block hex_to_int(number)
90
+ int_to_hex block.transaction_count
91
+ end
92
+
93
+ def handle_eth_getUncleCountByBlockHash(blockhash)
94
+ block = get_block hex_to_bytes(blockhash)
95
+ int_to_hex block.uncles.size
96
+ end
97
+
98
+ def handle_eth_getUncleCountByBlockNumber(number)
99
+ block = get_block hex_to_int(number)
100
+ int_to_hex block.uncles.size
101
+ end
102
+
103
+ def handle_eth_getCode(address, block_tag=@default_block)
104
+ block = get_block decode_block_tag(block_tag)
105
+ bytes_to_hex block.get_code(hex_to_bytes(address))
106
+ end
107
+
108
+ def handle_eth_sign(address, data)
109
+ raise NotImplementedError
110
+ end
111
+
112
+ def handle_eth_sendTransaction(obj)
113
+ to = hex_to_bytes(obj['to'] || '')
114
+
115
+ startgas_hex = obj['gas'] || obj['startgas']
116
+ startgas = startgas_hex ? hex_to_int(startgas_hex) : @default_startgas
117
+
118
+ gas_price_hex = obj['gasPrice'] || obj['gasprice']
119
+ gas_price = gas_price_hex ? hex_to_int(gas_price_hex) : @default_gas_price
120
+
121
+ value = hex_to_int(obj['value'] || '')
122
+ data = hex_to_bytes(obj['data'] || '')
123
+
124
+ v = hex_to_int(obj['v'] || '')
125
+ r = hex_to_int(obj['r'] || '')
126
+ s = hex_to_int(obj['s'] || '')
127
+
128
+ nonce = obj['nonce'] ? hex_to_int(obj['nonce']) : nil
129
+ sender = hex_to_bytes(obj['from'] || @default_address)
130
+
131
+ if v > 0
132
+ raise "signed but no nonce provided" if nonce.nil?
133
+ raise "invalid signature" unless r > 0 && s > 0
134
+ else
135
+ nonce ||= chain.chain.head_candidate.get_nonce(sender)
136
+
137
+ addr = bytes_to_hex(sender)
138
+ privkey = Account.test_accounts[addr]
139
+ raise "no privkey found for address #{addr}" unless privkey
140
+ end
141
+
142
+ tx = Transaction.new nonce, gas_price, startgas, to, value, data, v, r, s
143
+ # TODO
144
+
145
+ puts "tx added: #{tx.log_dict}"
146
+ bytes_to_hex tx.full_hash
147
+ end
148
+
149
+ def handle_eth_sendRawTransaction(data)
150
+ raise NotImplementedError
151
+ end
152
+
153
+ def handle_eth_call(obj, block_tag='pending')
154
+ success, output, _, _ = tentatively_execute obj, block_tag
155
+ if success == 1
156
+ bytes_to_hex output
157
+ else
158
+ false
159
+ end
160
+ end
161
+
162
+ def handle_eth_estimateGas(obj, block_tag='pending')
163
+ _, _, block, test_block = tentatively_execute obj, block_tag
164
+ test_block.gas_used - block.gas_used
165
+ end
166
+
167
+ def handle_eth_getBlockByHash(blockhash, include_transactions)
168
+ block = get_block hex_to_bytes(blockhash)
169
+ encode_block block, include_transactions
170
+ end
171
+
172
+ def handle_eth_getBlockByNumber(block_tag, include_transactions)
173
+ block = get_block decode_block_tag(block_tag)
174
+ pending = block_tag == 'pending'
175
+ encode_block block, include_transactions, pending
176
+ end
177
+
178
+ def handle_eth_getTransactionByHash(txhash)
179
+ tx, block, index = @node.state.chain.index.get_transaction hex_to_bytes(txhash)
180
+
181
+ if @node.state.chain.in_main_branch?(block)
182
+ encode_tx tx, block, index, false
183
+ else
184
+ nil
185
+ end
186
+ rescue IndexError
187
+ puts $!
188
+ puts $!.backtrace.join("\n")
189
+ nil
190
+ end
191
+
192
+ def handle_eth_getTransactionByBlockHashAndIndex(blockhash, index)
193
+ block = get_block blockhash
194
+ i = hex_to_int index
195
+
196
+ tx = block.get_transaction i
197
+ pending = blockhash == 'pending'
198
+ encode_tx tx, block, i, pending
199
+ rescue IndexError
200
+ nil
201
+ end
202
+
203
+ def handle_eth_getTransactionReceipt(txhash)
204
+ tx, block, index = @node.state.chain.index.get_transaction hex_to_bytes(txhash)
205
+
206
+ return nil unless @node.state.chain.in_main_branch?(block)
207
+
208
+ receipt = block.get_receipt index
209
+ h = {
210
+ transactionHash: bytes_to_hex(tx.full_hash),
211
+ transactionIndex: int_to_hex(index),
212
+ blockHash: bytes_to_hex(block.full_hash),
213
+ blockNumber: int_to_hex(block.number),
214
+ cumulativeGasUsed: int_to_hex(receipt.gas_used),
215
+ contractAddress: tx.creates ? bytes_to_hex(tx.creates) : nil
216
+ }
217
+
218
+ if index == 0
219
+ h[:gasUsed] = int_to_hex(receipt.gas_used)
220
+ else
221
+ prev_receipt = block.get_receipt(index - 1)
222
+ raise "invalid previous receipt" unless prev_receipt.gas_used < receipt.gas_used
223
+ h[:gasUsed] = int_to_hex(receipt.gas_used - prev_receipt.gas_used)
224
+ end
225
+
226
+ logs = receipt.logs.each_with_index.map do |log, i|
227
+ {
228
+ log: log,
229
+ log_idx: i,
230
+ block: block,
231
+ txhash: tx.full_hash,
232
+ tx_idx: index,
233
+ pending: false
234
+ }
235
+ end
236
+ h[:logs] = encode_loglist logs
237
+
238
+ h
239
+ rescue IndexError
240
+ nil
241
+ end
242
+
243
+ def handle_eth_getUncleByBlockHashAndIndex(blockhash, index)
244
+ return nil if blockhash == 'pending'
245
+
246
+ block = get_block blockhash
247
+ i = hex_to_int index
248
+
249
+ uncle = block.uncles[i]
250
+ return nil unless uncle
251
+
252
+ encode_block uncle, false, false, true
253
+ end
254
+
255
+ def handle_eth_getUncleByBlockNumberAndIndex(block_tag, index)
256
+ return nil if block_tag == 'pending'
257
+
258
+ block = get_block decode_block_tag(block_tag)
259
+ i = hex_to_int index
260
+
261
+ uncle = block.uncles[i]
262
+ return nil unless uncle
263
+
264
+ encode_block uncle, false, false, true
265
+ end
266
+
267
+ def handle_eth_getCompilers
268
+ get_compilers.keys
269
+ end
270
+
271
+ def handle_eth_compileSolidity(code)
272
+ compiler, method = get_compilers[:solidity]
273
+ compiler.send method, code
274
+ end
275
+
276
+ def handle_eth_compileLLL(code)
277
+ compiler, method = get_compilers[:lll]
278
+ bytes_to_hex compiler.send(method, code)
279
+ end
280
+
281
+ def handle_eth_compileSerpent(code)
282
+ compiler, method = get_compilers[:serpent]
283
+ bytes_to_hex compiler.send(method, code)
284
+ end
285
+
286
+ def handle_eth_newFilter(obj)
287
+ int_to_hex LogFilter.create(obj, @node.state.chain)
288
+ end
289
+
290
+ def handle_eth_newBlockFilter
291
+ int_to_hex BlockFilter.create(@node.state.chain)
292
+ end
293
+
294
+ def handle_eth_newPendingTransactionFilter
295
+ int_to_hex PendingTransactionFilter.create(@node.state.chain)
296
+ end
297
+
298
+ def handle_eth_uninstallFilter(id)
299
+ id = hex_to_int id
300
+
301
+ if Filter.include?(id)
302
+ Filter.delete id
303
+ true
304
+ else
305
+ false
306
+ end
307
+ end
308
+
309
+ def handle_eth_getFilterChanges(id)
310
+ id = hex_to_int id
311
+ raise ArgumentError, "unknown filter id" unless Filter.include?(id)
312
+
313
+ filter = Filter.find id
314
+ if [BlockFilter,PendingTransactionFilter].include?(filter.class)
315
+ filter.check.map {|block_or_tx| bytes_to_hex block_or_tx.full_hash }
316
+ elsif filter.instance_of?(LogFilter)
317
+ encode_loglist filter.new_logs
318
+ else
319
+ raise "invalid filter"
320
+ end
321
+ end
322
+
323
+ def handle_eth_getFilterLogs(id)
324
+ id = hex_to_int id
325
+ raise ArgumentError, "unknown filter id" unless Filter.include?(id)
326
+
327
+ filter = Filter.find id
328
+ encode_loglist filter.logs
329
+ end
330
+
331
+ def handle_eth_getLogs(obj)
332
+ filter = LogFilter.new(obj, @node.state.chain)
333
+ encode_loglist filter.logs
334
+ end
335
+
336
+ def handle_eth_getWork
337
+ raise NotImplementedError
338
+ end
339
+
340
+ def handle_eth_submitWork
341
+ raise NotImplementedError
342
+ end
343
+
344
+ def handle_eth_submitHashrate
345
+ raise NotImplementedError
346
+ end
347
+
348
+ def handle_db_putString(db_name, k, v)
349
+ raise NotImplementedError
350
+ end
351
+
352
+ def handle_db_getString(db_name, k)
353
+ raise NotImplementedError
354
+ end
355
+
356
+ def handle_db_putHex(db_name, k, v)
357
+ raise NotImplementedError
358
+ end
359
+
360
+ def handle_db_getHex(db_name, k)
361
+ raise NotImplementedError
362
+ end
363
+
364
+ def tentatively_execute(obj, block_tag)
365
+ raise ArgumentError, 'first parameter must be an object' unless obj.instance_of?(Hash)
366
+
367
+ raise ArgumentError, 'missing message receiver (to)' unless obj['to']
368
+ to = hex_to_bytes obj['to']
369
+
370
+ block = get_block decode_block_tag(block_tag)
371
+ snapshot_before = block.snapshot
372
+ tx_root_before = snapshot_before[:txs].root_hash
373
+
374
+ if block.has_parent?
375
+ parent = block.get_parent
376
+ test_block = Block.build_from_parent parent, block.coinbase, timestamp: block.timestamp
377
+
378
+ block.get_transactions.each do |tx|
379
+ success, output = test_block.apply_transaction tx
380
+ raise "failed to prepare test block" if success == 0
381
+ end
382
+ else
383
+ env = Env.new block.db
384
+ test_block = Block.genesis env
385
+
386
+ original = snapshot_before.dup
387
+ original.delete :txs
388
+ original = Marshal.load Marshal.dump(original) # deepcopy
389
+ original[:txs] = Ethereum::Trie.new snapshot_before[:txs].db, snapshot_before[:txs].root_hash
390
+
391
+ test_block = Block.genesis env
392
+ test_block.revert original
393
+ end
394
+
395
+ startgas = obj['gas'] ? hex_to_int(obj['gas']) : (test_block.gas_limit - test_block.gas_used)
396
+ gas_price = obj['gasPrice'] ? hex_to_int(obj['gasPrice']) : 0
397
+ value = obj['value'] ? hex_to_int(obj['value']) : 0
398
+ data = obj['data'] ? hex_to_bytes(obj['data']) : ''
399
+ sender = obj['from'] ? hex_to_bytes(obj['from']) : Ethereum::Address::ZERO
400
+
401
+ nonce = test_block.get_nonce sender
402
+ tx = Transaction.new nonce, gas_price, startgas, to, value, data
403
+ tx.sender = sender
404
+
405
+ begin
406
+ # FIXME: tx.check_low_s will raise exception if block is after homestead fork
407
+ success, output = test_block.apply_transaction tx
408
+ rescue Ethereum::InvalidTransaction
409
+ puts $!
410
+ puts $!.backtrace[0,10].join("\n")
411
+ success = 0
412
+ end
413
+
414
+ snapshot_after = block.snapshot
415
+ raise "real data should not be changed" unless snapshot_after == snapshot_before
416
+ raise "real data should not be changed" unless snapshot_after[:txs].root_hash == tx_root_before
417
+
418
+ return success, output, block, test_block
419
+ end
420
+
421
+ end
422
+
423
+ end
424
+ end
@@ -0,0 +1,156 @@
1
+ module Reth
2
+ module JSONRPC
3
+
4
+ module Helper
5
+
6
+ def chain
7
+ @node.services.chain
8
+ end
9
+
10
+ def peermanager
11
+ @node.services.peermanager
12
+ end
13
+
14
+ def discovery
15
+ @node.services.discovery
16
+ end
17
+
18
+ def bytes_to_hex(bytes)
19
+ "0x#{Utils.encode_hex(bytes)}"
20
+ end
21
+
22
+ def hex_to_bytes(hex)
23
+ hex = hex[2..-1] if hex[0,2] == '0x'
24
+ Utils.decode_hex hex
25
+ end
26
+
27
+ def int_to_hex(n)
28
+ hex = Utils.encode_hex Utils.int_to_big_endian(n)
29
+ hex = hex.gsub(/\A0+/, '')
30
+ "0x#{hex.empty? ? '0' : hex}"
31
+ end
32
+
33
+ def hex_to_int(hex)
34
+ hex = hex[2..-1] if hex[0,2] == '0x'
35
+ hex = '0' + hex if hex.size % 2 == 1 # padding left to make size even
36
+ Utils.big_endian_to_int hex_to_bytes(hex)
37
+ end
38
+
39
+ def get_ivar_value(ivar)
40
+ raise "operation failed: #{ivar.reason}" if ivar.rejected?
41
+ ivar.value
42
+ end
43
+
44
+ def get_block(id)
45
+ case id
46
+ when 'latest'
47
+ chain.chain.head
48
+ when 'earliest'
49
+ chain.chain.genesis
50
+ when 'pending'
51
+ chain.chain.head_candidate
52
+ when Integer
53
+ hash = chain.chain.index.get_block_by_number id
54
+ chain.chain.get hash
55
+ when String
56
+ id = hex_to_bytes(id) if id[0,2] == '0x'
57
+ chain.chain.get id
58
+ else
59
+ raise "unknown block id: #{id}"
60
+ end
61
+ end
62
+
63
+ def decode_block_tag(tag)
64
+ return tag if tag.nil?
65
+ return tag if %w(latest earliest pending).include?(tag)
66
+ return hex_to_int(tag)
67
+ end
68
+
69
+ def encode_block(block, include_transactions=false, pending=false, is_header=false)
70
+ raise ArgumentError, "cannot include transactions for header" if include_transactions && is_header
71
+
72
+ h = {
73
+ number: pending ? nil : int_to_hex(block.number),
74
+ hash: pending ? nil : bytes_to_hex(block.full_hash),
75
+ parentHash: bytes_to_hex(block.prevhash),
76
+ nonce: pending ? nil : bytes_to_hex(block.nonce),
77
+ sha3Uncles: bytes_to_hex(block.uncles_hash),
78
+ logsBloom: pending ? nil : bytes_to_hex(Utils.int_to_big_endian(block.bloom)),
79
+ transactionsRoot: bytes_to_hex(block.tx_list_root),
80
+ stateRoot: bytes_to_hex(block.state_root),
81
+ miner: pending ? nil : bytes_to_hex(block.coinbase),
82
+ difficulty: int_to_hex(block.difficulty),
83
+ extraData: bytes_to_hex(block.extra_data),
84
+ gasLimit: int_to_hex(block.gas_limit),
85
+ gasUsed: int_to_hex(block.gas_used),
86
+ timestamp: int_to_hex(block.timestamp)
87
+ }
88
+
89
+ unless is_header
90
+ h[:totalDifficulty] = int_to_hex(block.chain_difficulty)
91
+ h[:size] = int_to_hex(RLP.encode(block).size)
92
+ h[:uncles] = block.uncles.map {|u| bytes_to_hex(u.full_hash) }
93
+
94
+ if include_transactions
95
+ h[:transactions] = block.get_transactions.each_with_index.map {|tx, i| encode_tx(tx, block, i, pending) }
96
+ else
97
+ h[:transactions] = block.get_transactions.map {|tx| bytes_to_hex(tx.full_hash) }
98
+ end
99
+ end
100
+
101
+ h
102
+ end
103
+
104
+ def encode_tx(transaction, block, i, pending)
105
+ {
106
+ hash: bytes_to_hex(transaction.full_hash),
107
+ nonce: int_to_hex(transaction.nonce),
108
+ blockHash: bytes_to_hex(block.full_hash),
109
+ blockNumber: pending ? nil : int_to_hex(block.number),
110
+ transactionIndex: int_to_hex(i),
111
+ from: bytes_to_hex(transaction.sender),
112
+ to: bytes_to_hex(transaction.to),
113
+ value: int_to_hex(transaction.value),
114
+ gasPrice: int_to_hex(transaction.gasprice),
115
+ gas: int_to_hex(transaction.startgas),
116
+ input: bytes_to_hex(transaction.data)
117
+ }
118
+ end
119
+
120
+ def encode_loglist(logs)
121
+ logs.map do |l|
122
+ {
123
+ logIndex: l[:pending] ? nil : int_to_hex(l[:log_idx]),
124
+ transactionIndex: l[:pending] ? nil : int_to_hex(l[:tx_idx]),
125
+ transactionHash: l[:pending] ? nil : bytes_to_hex(l[:txhash]),
126
+ blockHash: l[:pending] ? nil : bytes_to_hex(l[:block].full_hash),
127
+ blockNumber: l[:pending] ? nil : int_to_hex(l[:block].number),
128
+ address: bytes_to_hex(l[:log].address),
129
+ data: bytes_to_hex(l[:log].data),
130
+ topics: l[:log].topics.map {|t| bytes_to_hex Utils.zpad_int(t) },
131
+ type: l[:pending] ? 'pending' : 'mined'
132
+ }
133
+ end
134
+ end
135
+
136
+ def get_compilers
137
+ return @compilers if @compilers
138
+
139
+ @compilers = {}
140
+
141
+ if serpent = Ethereum::Tester::Language.all[:serpent]
142
+ @compilers[:serpent] = [serpent, :compile]
143
+ @compilers[:lll] = [serpent, :compile_lll]
144
+ end
145
+
146
+ if solidity = Ethereum::Tester::Language.all[:solidity]
147
+ @compilers[:solidity] = [solidity, :compile_rich]
148
+ end
149
+
150
+ @compilers
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+ end