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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +6 -0
- data/bin/reth +208 -0
- data/lib/reth.rb +44 -0
- data/lib/reth/account.rb +195 -0
- data/lib/reth/account_service.rb +361 -0
- data/lib/reth/app.rb +15 -0
- data/lib/reth/chain_service.rb +500 -0
- data/lib/reth/config.rb +88 -0
- data/lib/reth/db_service.rb +90 -0
- data/lib/reth/duplicates_filter.rb +29 -0
- data/lib/reth/eth_protocol.rb +209 -0
- data/lib/reth/genesisdata/genesis_frontier.json +26691 -0
- data/lib/reth/genesisdata/genesis_morden.json +27 -0
- data/lib/reth/genesisdata/genesis_olympic.json +48 -0
- data/lib/reth/jsonrpc.rb +13 -0
- data/lib/reth/jsonrpc/app.rb +79 -0
- data/lib/reth/jsonrpc/filter.rb +288 -0
- data/lib/reth/jsonrpc/handler.rb +424 -0
- data/lib/reth/jsonrpc/helper.rb +156 -0
- data/lib/reth/jsonrpc/server.rb +24 -0
- data/lib/reth/jsonrpc/service.rb +37 -0
- data/lib/reth/keystore.rb +150 -0
- data/lib/reth/leveldb_service.rb +79 -0
- data/lib/reth/profile.rb +66 -0
- data/lib/reth/sync_task.rb +273 -0
- data/lib/reth/synchronizer.rb +192 -0
- data/lib/reth/transient_block.rb +40 -0
- data/lib/reth/utils.rb +22 -0
- data/lib/reth/version.rb +3 -0
- metadata +201 -0
@@ -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
|
data/lib/reth/utils.rb
ADDED
@@ -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
|
data/lib/reth/version.rb
ADDED
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:
|