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,192 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Reth
4
+
5
+ ##
6
+ # Handles the synchronization of blocks.
7
+ #
8
+ # There's only one sync task active at a time. In order to deal with the
9
+ # worst case of initially syncing the wrong chain, a checkpoint blockhash
10
+ # can be specified and synced via force_sync.
11
+ #
12
+ # Received blocks are given to chainservice.add_block, which has a fixed
13
+ # size queue, the synchronization blocks if the queue is full.
14
+ #
15
+ # on_status:
16
+ # if peer.head.chain_difficulty > chain.head.chain_difficulty
17
+ # fetch peer.head and handle as newblock
18
+ #
19
+ # on_newblock:
20
+ # if block.parent
21
+ # add
22
+ # else
23
+ # sync
24
+ #
25
+ # on_blocks/on_blockhashes:
26
+ # if synctask
27
+ # handle to requester
28
+ # elsif unkonwn and has parent
29
+ # add to chain
30
+ # else
31
+ # drop
32
+ #
33
+ class Synchronizer
34
+
35
+ MAX_NEWBLOCK_AGE = 5 # maximum age (in blocks) of blocks received as newblock
36
+
37
+ attr :chain, :chainservice, :synctask
38
+
39
+ ##
40
+ # @param force_sync [Array, NilClass] If passed in array, it must be in
41
+ # the form of tuple: (blockhash, chain_difficulty). Helper for long
42
+ # initial syncs to get on the right chain used with first
43
+ # status_received.
44
+ #
45
+ def initialize(chainservice, force_sync=nil)
46
+ @chainservice = chainservice
47
+ @force_sync = force_sync
48
+ @chain = chainservice.chain
49
+ @protocols = {} # proto => chain_difficulty
50
+ @synctask = nil
51
+ end
52
+
53
+ def syncing?
54
+ !!@synctask
55
+ end
56
+
57
+ def synctask_exited(success=false)
58
+ @force_sync = nil if success
59
+ @synctask = nil
60
+ end
61
+
62
+ def protocols
63
+ @protocols = @protocols
64
+ .map {|proto, diff| [proto, diff] }
65
+ .select {|tuple| !tuple[0].stopped? }
66
+ .to_h
67
+
68
+ @protocols.keys.sort_by {|proto| -@protocols[proto] }
69
+ end
70
+
71
+ ##
72
+ # Called if there's a newblock announced on the network.
73
+ #
74
+ def receive_newblock(proto, t_block, chain_difficulty)
75
+ logger.debug 'newblock', proto: proto, block: t_block, chain_difficulty: chain_difficulty, client: proto.peer.remote_client_version
76
+
77
+ if @chain.include?(t_block.header.full_hash)
78
+ raise AssertError, 'chain difficulty mismatch' unless chain_difficulty == @chain.get(t_block.header.full_hash).chain_difficulty
79
+ end
80
+
81
+ @protocols[proto] = chain_difficulty
82
+
83
+ if @chainservice.knows_block(t_block.header.full_hash)
84
+ logger.debug 'known block'
85
+ return
86
+ end
87
+
88
+ expected_difficulty = @chain.head.chain_difficulty + t_block.header.difficulty
89
+ if chain_difficulty >= @chain.head.chain_difficulty
90
+ # broadcast duplicates filtering is done in chainservice
91
+ logger.debug 'sufficient difficulty, broadcasting', client: proto.peer.remote_client_version
92
+ @chainservice.broadcast_newblock t_block, chain_difficulty, proto
93
+ else
94
+ age = @chain.head.number - t_block.header.number
95
+ logger.debug "low difficulty", client: proto.peer.remote_client_version, chain_difficulty: chain_difficulty, expected_difficulty: expected_difficulty, block_age: age
96
+
97
+ if age > MAX_NEWBLOCK_AGE
98
+ logger.debug 'newblock is too old, not adding', block_age: age, max_age: MAX_NEWBLOCK_AGE
99
+ return
100
+ end
101
+ end
102
+
103
+ if @chainservice.knows_block(t_block.header.prevhash)
104
+ logger.debug 'adding block'
105
+ @chainservice.add_block t_block, proto
106
+ else
107
+ logger.debug 'missing parent'
108
+ if @synctask
109
+ logger.debug 'existing task, discarding'
110
+ else
111
+ @synctask = SyncTask.new self, proto, t_block.header.full_hash, chain_difficulty
112
+ end
113
+ end
114
+ end
115
+
116
+ ##
117
+ # Called if a new peer is connected.
118
+ #
119
+ def receive_status(proto, blockhash, chain_difficulty)
120
+ logger.debug 'status received', proto: proto, chain_difficulty: chain_difficulty
121
+
122
+ @protocols[proto] = chain_difficulty
123
+
124
+ if @chainservice.knows_block(blockhash) || @synctask
125
+ logger.debug 'existing task or known hash, discarding'
126
+ return
127
+ end
128
+
129
+ if @force_sync
130
+ blockhash, difficulty = force_sync
131
+ logger.debug 'starting forced synctask', blockhash: Utils.encode_hex(blockhash)
132
+ @synctask = SyncTask.new self, proto, blockhash, difficulty
133
+ elsif chain_difficulty > @chain.head.chain_difficulty
134
+ logger.debug 'sufficient difficulty'
135
+ @synctask = SyncTask.new self, proto, blockhash, chain_difficulty
136
+ end
137
+ rescue
138
+ logger.debug $!
139
+ logger.debug $!.backtrace[0,10].join("\n")
140
+ end
141
+
142
+ ##
143
+ # No way to check if this really an interesting block at this point.
144
+ # Might lead to an amplification attack, need to track this proto and
145
+ # judge usefulness.
146
+ #
147
+ def receive_newblockhashes(proto, newblockhashes)
148
+ logger.debug 'received newblockhashes', num: newblockhashes.size, proto: proto
149
+
150
+ newblockhashes = newblockhashes.select {|h| !@chainservice.knows_block(h) }
151
+
152
+ known = @protocols.include?(proto)
153
+ if !known || newblockhashes.empty? || @synctask
154
+ logger.debug 'discarding', known: known, synctask: syncing?, num: newblockhashes.size
155
+ return
156
+ end
157
+
158
+ if newblockhashes.size != 1
159
+ logger.warn 'supporting only one newblockhash', num: newblockhashes.size
160
+ end
161
+ blockhash = newblockhashes[0]
162
+
163
+ logger.debug 'starting synctask for newblockhashes', blockhash: Utils.encode_hex(blockhash)
164
+ @synctask = SyncTask.new self, proto, blockhash, 0, true
165
+ end
166
+
167
+ def receive_blocks(proto, t_blocks)
168
+ logger.debug 'blocks received', proto: proto, num: t_blocks.size
169
+ if @synctask
170
+ @synctask.receive_blocks proto, t_blocks
171
+ else
172
+ logger.warn 'no synctask, not expecting blocks'
173
+ end
174
+ end
175
+
176
+ def receive_blockhashes(proto, blockhashes)
177
+ logger.debug 'blockhashes received', proto: proto, num: blockhashes.size
178
+ if @synctask
179
+ @synctask.receive_blockhashes proto, blockhashes
180
+ else
181
+ logger.warn 'no synctask, not expecting blockhashes'
182
+ end
183
+ end
184
+
185
+ private
186
+
187
+ def logger
188
+ @logger ||= Logger.new('eth.sync')
189
+ end
190
+ end
191
+
192
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Reth
4
+
5
+ class TransientBlock
6
+ include RLP::Sedes::Serializable
7
+
8
+ set_serializable_fields(
9
+ header: BlockHeader,
10
+ transaction_list: RLP::Sedes::CountableList.new(Transaction),
11
+ uncles: RLP::Sedes::CountableList.new(BlockHeader)
12
+ )
13
+
14
+ attr :newblock_timestamp
15
+
16
+ def initialize(block_data, newblock_timestamp=0)
17
+ @newblock_timestamp = newblock_timestamp
18
+
19
+ header = BlockHeader.deserialize block_data[0]
20
+ transaction_list = RLP::Sedes::CountableList.new(Transaction).deserialize block_data[1]
21
+ uncles = RLP::Sedes::CountableList.new(BlockHeader).deserialize block_data[2]
22
+ super(header, transaction_list, uncles)
23
+ end
24
+
25
+ def to_block(env, parent=nil)
26
+ Block.new header: header, transaction_list: transaction_list, uncles: uncles, env: env, parent: parent
27
+ end
28
+
29
+ def full_hash_hex
30
+ header.full_hash_hex
31
+ end
32
+
33
+ def to_s
34
+ "<TransientBlock(##{header.number} #{header.full_hash_hex[0,8]})"
35
+ end
36
+ alias inspect to_s
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'securerandom'
4
+
5
+ module Reth
6
+
7
+ module Utils
8
+
9
+ include ::Ethereum::Utils
10
+ extend self
11
+
12
+ def mk_random_privkey
13
+ SecureRandom.random_bytes(32)
14
+ end
15
+
16
+ def mk_privkey(seed)
17
+ keccak256 seed
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,3 @@
1
+ module Reth
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Xie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-ethereum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: devp2p
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: slop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra-contrib
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 5.8.3
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 5.8.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.8.7.6
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 0.8.7.6
125
+ - !ruby/object:Gem::Dependency
126
+ name: serpent
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.3'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.3'
139
+ description: A ethereum full node written in ruby.
140
+ email:
141
+ - jan.h.xie@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - LICENSE
147
+ - README.md
148
+ - bin/reth
149
+ - lib/reth.rb
150
+ - lib/reth/account.rb
151
+ - lib/reth/account_service.rb
152
+ - lib/reth/app.rb
153
+ - lib/reth/chain_service.rb
154
+ - lib/reth/config.rb
155
+ - lib/reth/db_service.rb
156
+ - lib/reth/duplicates_filter.rb
157
+ - lib/reth/eth_protocol.rb
158
+ - lib/reth/genesisdata/genesis_frontier.json
159
+ - lib/reth/genesisdata/genesis_morden.json
160
+ - lib/reth/genesisdata/genesis_olympic.json
161
+ - lib/reth/jsonrpc.rb
162
+ - lib/reth/jsonrpc/app.rb
163
+ - lib/reth/jsonrpc/filter.rb
164
+ - lib/reth/jsonrpc/handler.rb
165
+ - lib/reth/jsonrpc/helper.rb
166
+ - lib/reth/jsonrpc/server.rb
167
+ - lib/reth/jsonrpc/service.rb
168
+ - lib/reth/keystore.rb
169
+ - lib/reth/leveldb_service.rb
170
+ - lib/reth/profile.rb
171
+ - lib/reth/sync_task.rb
172
+ - lib/reth/synchronizer.rb
173
+ - lib/reth/transient_block.rb
174
+ - lib/reth/utils.rb
175
+ - lib/reth/version.rb
176
+ homepage: https://github.com/janx/reth
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.4.5.1
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Ethereum full node.
200
+ test_files: []
201
+ has_rdoc: