reth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: