reth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ {
2
+ "nonce": "0x00006d6f7264656e",
3
+ "difficulty": "0x20000",
4
+ "mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
5
+ "coinbase": "0x0000000000000000000000000000000000000000",
6
+ "timestamp": "0x00",
7
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
8
+ "extraData": "0x",
9
+ "gasLimit": "0x2FEFD8",
10
+ "alloc": {
11
+ "0000000000000000000000000000000000000001": {
12
+ "balance": "1"
13
+ },
14
+ "0000000000000000000000000000000000000002": {
15
+ "balance": "1"
16
+ },
17
+ "0000000000000000000000000000000000000003": {
18
+ "balance": "1"
19
+ },
20
+ "0000000000000000000000000000000000000004": {
21
+ "balance": "1"
22
+ },
23
+ "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": {
24
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "nonce": "0x0000000000000042",
3
+ "difficulty": "0x20000",
4
+ "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
5
+ "coinbase": "0x0000000000000000000000000000000000000000",
6
+ "timestamp": "0x00",
7
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
8
+ "extraData": "",
9
+ "gasLimit": "0x2fefd8",
10
+ "alloc": {
11
+ "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": {
12
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
13
+ },
14
+ "e6716f9544a56c530d868e4bfbacb172315bdead": {
15
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
16
+ },
17
+ "b9c015918bdaba24b4ff057a92a3873d6eb201be": {
18
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
19
+ },
20
+ "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {
21
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
22
+ },
23
+ "2ef47100e0787b915105fd5e3f4ff6752079d5cb": {
24
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
25
+ },
26
+ "cd2a3d9f938e13cd947ec05abc7fe734df8dd826": {
27
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
28
+ },
29
+ "6c386a4b26f73c802f34673f7248bb118f97424a": {
30
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
31
+ },
32
+ "e4157b34ea9615cfbde6b4fda419828124b70c78": {
33
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
34
+ },
35
+ "0000000000000000000000000000000000000001": {
36
+ "balance": "1"
37
+ },
38
+ "0000000000000000000000000000000000000002": {
39
+ "balance": "1"
40
+ },
41
+ "0000000000000000000000000000000000000003": {
42
+ "balance": "1"
43
+ },
44
+ "0000000000000000000000000000000000000004": {
45
+ "balance": "1"
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+ require 'rack'
3
+ require 'sinatra/base'
4
+ require 'sinatra/json'
5
+
6
+ require 'ethereum'
7
+
8
+ require 'reth/jsonrpc/helper'
9
+ require 'reth/jsonrpc/filter'
10
+ require 'reth/jsonrpc/handler'
11
+ require 'reth/jsonrpc/server'
12
+ require 'reth/jsonrpc/service'
13
+ require 'reth/jsonrpc/app'
@@ -0,0 +1,79 @@
1
+ module Reth
2
+ module JSONRPC
3
+
4
+ class App < Sinatra::Base
5
+
6
+ configure do
7
+ enable :logging
8
+ end
9
+
10
+ CORS_ORIGIN = "http://localhost:8080"
11
+
12
+ options "*" do
13
+ response.headers["Allow"] = "POST,OPTIONS"
14
+ response.headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept"
15
+ response.headers["Access-Control-Allow-Origin"] = "http://localhost:8080"
16
+ response.headers["Access-Control-Allow-Methods"] = "POST"
17
+
18
+ 200
19
+ end
20
+
21
+ post '/' do
22
+ response.headers["Access-Control-Allow-Origin"] = CORS_ORIGIN
23
+
24
+ begin
25
+ data = JSON.parse request.body.read
26
+ puts "Params: #{data.inspect}"
27
+
28
+ result = if data.instance_of?(Array)
29
+ data.map {|d| dispatch d }
30
+ else
31
+ dispatch data
32
+ end
33
+
34
+ json result
35
+ rescue JSON::ParserError
36
+ puts $!
37
+ puts $!.backtrace[0,10].join("\n")
38
+ json jsonrpc: '2.0', error: $!
39
+ end
40
+ end
41
+
42
+ def initialize(node)
43
+ @node = node
44
+
45
+ @default_block = 'latest'
46
+ @default_address = Account.test_accounts.keys.first
47
+ @default_startgas = 500 * 1000
48
+ @default_gas_price = 60.shannon
49
+
50
+ super()
51
+ end
52
+
53
+ private
54
+
55
+ include Handler
56
+
57
+ def dispatch(data)
58
+ # TODO: filter injection, validate method names
59
+ result = send :"handle_#{data['method']}", *data['params']
60
+
61
+ {jsonrpc: data['jsonrpc'], id: data['id'], result: result}
62
+ rescue NoMethodError
63
+ if $!.message =~ /handle_([a-zA-Z_]+)/
64
+ {jsonrpc: '2.0', id: data['id'], error: "jsonrpc method not defined: #{$1}"}
65
+ else
66
+ puts $!
67
+ puts $!.backtrace[0,10].join("\n")
68
+ {jsonrpc: data['jsonrpc'], id: data['id'], error: $!}
69
+ end
70
+ rescue
71
+ puts $!
72
+ puts $!.backtrace[0,10].join("\n")
73
+ {jsonrpc: data['jsonrpc'], id: data['id'], error: $!}
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,288 @@
1
+ module Reth
2
+ module JSONRPC
3
+
4
+ class Filter
5
+ class <<self
6
+ def next_id
7
+ @next_id ||= 0
8
+ @next_id += 1
9
+ end
10
+
11
+ def map
12
+ @map ||= {}
13
+ end
14
+
15
+ def find(id)
16
+ map[id]
17
+ end
18
+
19
+ def delete(id)
20
+ map.delete id
21
+ end
22
+
23
+ def include?(id)
24
+ map.include?(id)
25
+ end
26
+ end
27
+ end
28
+
29
+ class LogFilter
30
+
31
+ class <<self
32
+ def create(obj, chain)
33
+ f = new obj, chain
34
+ id = Filter.next_id
35
+ Filter.map[id] = f
36
+ id
37
+ end
38
+ end
39
+
40
+ include Helper
41
+
42
+ def initialize(obj, chain)
43
+ @chain = chain
44
+ @first_block, @last_block, @addresses, @topics = parse_obj obj
45
+
46
+ @last_head = @chain.head
47
+ @last_block_checked = nil
48
+
49
+ @log_dict = {}
50
+ end
51
+
52
+ def check
53
+ first, last = get_from_to @first_block, @last_block
54
+ first = [@last_head.number + 1, first].min if @first_block.is_a?(String)
55
+ raise "first must not be greater than last" unless first <= last
56
+
57
+ if @last_block_checked
58
+ first = [@last_block_checked.number + 1, first].max
59
+ return {} if first > last
60
+ end
61
+
62
+ blocks_to_check = []
63
+ (first...last).each do |n|
64
+ blocks_to_check.push @chain.index.get_block_by_number(n)
65
+ end
66
+
67
+ head = @chain.head
68
+ head_candidate = @chain.head_candidate
69
+
70
+ if last == head_candidate.number
71
+ blocks_to_check.push head_candidate
72
+ else
73
+ blocks_to_check.push @chain.get(@chain.index.get_block_by_number(last))
74
+ end
75
+
76
+ int32 = RLP::Sedes::BigEndianInt.new 32
77
+
78
+ new_logs = {}
79
+ blocks_to_check.each_with_index do |block, i|
80
+ unless [Ethereum::Block, Ethereum::CachedBlock].include?(block.class)
81
+ # must be blockhash
82
+ bloom = @chain.get_bloom block
83
+
84
+ if @addresses
85
+ pass_address_check = @addresses.any? {|addr| Ethereum::Bloom.query(bloom, addr) }
86
+ next unless pass_address_check
87
+ end
88
+
89
+ topics = (@topics || []).map {|t| int32.serialize(t) }
90
+ topic_bloom = Ethereum::Bloom.from_array topics
91
+ next if Ethereum::Bloom.combine(bloom, topic_bloom) != bloom
92
+
93
+ block = @chain.get block
94
+ end
95
+
96
+ r_idx = nil
97
+ l_idx = nil
98
+ log = nil
99
+ block.get_receipts.each_with_index do |receipt, ri|
100
+ r_idx = ri
101
+
102
+ receipt.logs.each_with_index do |_log, li|
103
+ log = _log
104
+ l_idx = li
105
+
106
+ next if @addresses && !@addresses.include?(log.address)
107
+
108
+ if @topics
109
+ topic_match = log.topics.size >= @topics.size
110
+ next unless topic_match
111
+
112
+ @topics.zip(log.topics).each do |filter_topic, log_topic|
113
+ if filter_topic && filter_topic != log_topic
114
+ topic_match = false
115
+ break
116
+ end
117
+ end
118
+ next unless topic_match
119
+ end
120
+
121
+ tx = block.get_transaction r_idx
122
+ id = Ethereum::Utils.keccak256 "#{tx.full_hash}#{l_idx}"
123
+ pending = block == head_candidate
124
+ new_logs[id] = {
125
+ log: log,
126
+ log_idx: l_idx,
127
+ block: block,
128
+ txhash: tx.full_hash,
129
+ tx_idx: r_idx
130
+ }
131
+ end
132
+ end
133
+ end
134
+
135
+ @last_block_checked = if blocks_to_check.last != head_candidate
136
+ blocks_to_check.last
137
+ else
138
+ blocks_to_check.size >= 2 ? blocks_to_check[-2] : nil
139
+ end
140
+
141
+ if @last_block_checked && ![Ethereum::Block, Ethereum::CachedBlock].include?(@last_block_checked.class)
142
+ @last_block_checked = @chain.get @last_block_checked
143
+ end
144
+
145
+ actually_new_ids = new_logs.keys - @log_dict.keys
146
+ @log_dict.merge! new_logs
147
+
148
+ actually_new_ids.map {|id| [id, new_logs[id]] }.to_h
149
+ end
150
+
151
+ def logs
152
+ check
153
+ @log_dict.values
154
+ end
155
+
156
+ def new_logs
157
+ check.values
158
+ end
159
+
160
+ def to_s
161
+ "<Filter(addressed=#{@addresses}, topics=#{@topics}, first=#{@first_block}, last=#{@last_block})>"
162
+ end
163
+
164
+ private
165
+
166
+ def parse_obj(obj)
167
+ raise ArgumentError, 'obj must be a Hash' unless obj.instance_of?(Hash)
168
+
169
+ addresses = case obj['address']
170
+ when String
171
+ [hex_to_bytes(obj['address'])]
172
+ when Array
173
+ obj['address'].map {|addr| hex_to_bytes(addr) }
174
+ when NilClass
175
+ nil
176
+ else
177
+ raise ArgumentError, "address must be String or Array of Strings"
178
+ end
179
+
180
+ topics = nil
181
+ if obj.has_key?('topics')
182
+ topics = []
183
+ obj['topics'].each do |t|
184
+ if t
185
+ topics.push(Ethereum::Utils.big_endian_to_int hex_to_bytes(t))
186
+ else
187
+ topics.push(nil)
188
+ end
189
+ end
190
+ end
191
+
192
+ from_block = decode_block_tag(obj['fromBlock'] || 'latest')
193
+ to_block = decode_block_tag(obj['toBlock'] || 'latest')
194
+
195
+ from, to = get_from_to from_block, to_block
196
+ raise ArgumentError, 'fromBlock must not be newer than toBlock' if from > to
197
+
198
+ return from_block, to_block, addresses, topics
199
+ end
200
+
201
+ def get_from_to(from_block, to_block)
202
+ block_tags = {
203
+ 'earliest' => 0,
204
+ 'latest' => @chain.head.number,
205
+ 'pending' => @chain.head_candidate.number
206
+ }
207
+ from = from_block.is_a?(Integer) ? from_block : block_tags[from_block]
208
+ to = to_block.is_a?(Integer) ? to_block : block_tags[to_block]
209
+
210
+ return from, to
211
+ end
212
+
213
+ end
214
+
215
+ class BlockFilter
216
+
217
+ class <<self
218
+ def create(chain)
219
+ f = new chain
220
+ id = Filter.next_id
221
+ Filter.map[id] = f
222
+ id
223
+ end
224
+ end
225
+
226
+ def initialize(chain)
227
+ @chain = chain
228
+ @latest_block = chain.head
229
+ end
230
+
231
+ def check
232
+ new_blocks = []
233
+ block = @chain.head
234
+
235
+ while block.number > @latest_block.number
236
+ new_blocks.push block
237
+ block = block.get_parent
238
+ end
239
+
240
+ puts "previous latest block not in current chain!" if block != @latest_block
241
+ @latest_block = new_blocks.first if new_blocks.size > 0
242
+
243
+ new_blocks.reverse
244
+ end
245
+
246
+ end
247
+
248
+ class PendingTransactionFilter
249
+
250
+ class <<self
251
+ def create(chain)
252
+ f = new chain
253
+ id = Filter.next_id
254
+ Filter.map[id] = f
255
+ id
256
+ end
257
+ end
258
+
259
+ def initialize(chain)
260
+ @chain = chain
261
+ @latest_block = @chain.head_candidate
262
+ @reported_txs = []
263
+ end
264
+
265
+ def check
266
+ head_candidate = @chain.head_candidate
267
+ pending_txs = head_candidate.get_transactions
268
+ new_txs = pending_txs.select {|tx| !@reported_txs.include?(tx.full_hash) }
269
+
270
+ block = head_candidate.get_parent
271
+ while block.number >= @latest_block.number
272
+ block.get_transactions.reverse.each do |tx|
273
+ new_txs.push(tx) unless @reported_txs.include?(tx.full_hash)
274
+ end
275
+
276
+ block = block.get_parent
277
+ end
278
+
279
+ @latest_block = head_candidate
280
+ @reported_txs = pending_txs.map(&:full_hash)
281
+
282
+ new_txs.reverse
283
+ end
284
+
285
+ end
286
+
287
+ end
288
+ end