reth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 81f3646dda45ea703ebdd86931afa3ca03560838
4
+ data.tar.gz: f5d710770445a550aca6fade8a7f268845a085fd
5
+ SHA512:
6
+ metadata.gz: 5d8366c448058a052bccc36b0226361e5d9bb56c23797df2217a4926e06ca44e6bc5f2b856e45c5345f4751b1aff76c0ca607906c551ceabbb7847db9bdb9f3b
7
+ data.tar.gz: d2ce932b075f7895a6db74628050cc10aace81c689a4edbc21b3f861af5917eda2ca882d1617a28ee69102f52f6d5b07aeb82a99ad8d7e3ba60ae03c7de71c8d
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jan Xie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # reth
2
+
3
+
4
+ ## License
5
+
6
+ [MIT License](LICENSE)
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'slop'
6
+ require 'reth'
7
+
8
+ class RethControl
9
+
10
+ Services = [
11
+ Reth::DBService,
12
+ Reth::AccountService,
13
+ DEVp2p::Discovery::Service,
14
+ DEVp2p::PeerManager,
15
+ Reth::ChainService,
16
+ Reth::JSONRPC::Service
17
+ ].freeze
18
+
19
+ attr :app, :config
20
+
21
+ def initialize(options={})
22
+ load_config(options)
23
+ @app = Reth::App.new(@config)
24
+ end
25
+
26
+ def load_config(options)
27
+ datadir = options[:data_dir] or raise ArgumentError.new('missing datadir')
28
+ profile = if options[:network_id]
29
+ Reth::Profile.private(options[:network_id])
30
+ else
31
+ Reth::Profile.public(options[:profile].to_sym)
32
+ end
33
+
34
+ Reth::Config.setup(datadir)
35
+ @config = profile.deep_merge Reth::Config.load(datadir)
36
+
37
+ # TODO: allow bootstrap_node to be Array, merge with defaults
38
+ if options[:bootstrap_node]
39
+ @config.discovery.bootstrap_nodes = [ options[:bootstrap_node] ]
40
+ end
41
+
42
+ update_config_with_defaults @config, Reth::Config.get_default_config([Reth::App] + Services)
43
+ update_config_with_defaults @config, {eth: {block: Reth::Env::DEFAULT_CONFIG}}
44
+
45
+ genesis_from_config_file = @config.fetch(:eth, {})[:genesis]
46
+ if genesis_from_config_file
47
+ # Fixed genesis_hash take from profile must be deleted as custom genesis loaded
48
+ @config[:eth].delete :genesis_hash
49
+ @config[:eth][:genesis] = genesis_from_config_file
50
+ end
51
+
52
+ update_config_from_genesis_json @config[:eth][:genesis]
53
+
54
+ # TODO: bootstrap_nodes_from_config_file
55
+
56
+ @config = @config.deep_merge options
57
+
58
+ dump_config
59
+ end
60
+
61
+ def dump_config
62
+ puts_header 'CONFIGURATION'
63
+
64
+ cfg = @config.to_hash
65
+ alloc = cfg.fetch('eth', {}).fetch('block', {}).fetch('genesis_initial_alloc', {})
66
+ if alloc.size > 100
67
+ puts "omitting reporting of #{alloc.size} accounts in genesis"
68
+ cfg['eth']['block'].delete('genesis_initial_alloc')
69
+ end
70
+
71
+ puts cfg
72
+ end
73
+
74
+ def register_services
75
+ exclude_services = @app.config[:deactivated_services]
76
+ Services.each do |service|
77
+ raise ArgumentError, 'service must be DEVp2p::Service' unless service.instance_of?(Class) && service < DEVp2p::Service
78
+
79
+ next if exclude_services.include?(service.name)
80
+ service.register_with_app @app
81
+ end
82
+ end
83
+
84
+ def start
85
+ register_services
86
+
87
+ puts_header "starting"
88
+ @app.start
89
+
90
+ #trap("INT") { @app.stop }
91
+ #trap("TERM") { @app.stop }
92
+ #trap("QUIT") { @app.stop }
93
+
94
+ #10000.times do |i|
95
+ # sleep 2
96
+ # @app.services.db.put i.to_s, Time.now.to_s
97
+ # @app.services.db.commit
98
+ #end
99
+
100
+ evt_exit = Concurrent::Event.new
101
+ do_exit = proc do
102
+ @app.stop
103
+ evt_exit.set
104
+ puts "\nexit."
105
+ end
106
+
107
+ Signal.trap("INT", &do_exit)
108
+ Signal.trap("TERM", &do_exit)
109
+ Signal.trap("QUIT", &do_exit)
110
+
111
+ Thread.new { evt_exit.wait }.join
112
+ end
113
+
114
+ private
115
+
116
+ def puts_header(text)
117
+ puts "\n>>>>> #{text}"
118
+ end
119
+
120
+ def update_config_with_defaults(config, default_config)
121
+ DEVp2p::Utils.update_config_with_defaults config, default_config
122
+ end
123
+
124
+ def update_config_from_genesis_json(genesis_json_filename_or_hash)
125
+ genesis = genesis_json_filename_or_hash.instance_of?(String) ?
126
+ JSON.parse(File.read(genesis_json_filename_or_hash)) :
127
+ genesis_json_filename_or_hash
128
+
129
+ @config[:eth] ||= {}
130
+ @config[:eth][:block] ||= {}
131
+
132
+ id = ->(x) { x }
133
+ parse_int_or_hex = ->(x) { Reth::Utils.parse_int_or_hex(x) }
134
+ dec = ->(x) { Reth::Utils.decode_hex Reth::Utils.remove_0x_head(x) }
135
+
136
+ m = {
137
+ 'alloc' => [:genesis_initial_alloc, id],
138
+ 'difficulty' => [:genesis_difficulty, parse_int_or_hex],
139
+ 'timestamp' => [:genesis_timestamp, parse_int_or_hex],
140
+ 'extraData' => [:genesis_extra_data, dec],
141
+ 'gasLimit' => [:genesis_gas_limit, parse_int_or_hex],
142
+ 'mixhash' => [:genesis_mixhash, dec],
143
+ 'parentHash' => [:genesis_prevhash, dec],
144
+ 'coinbase' => [:genesis_coinbase, dec],
145
+ 'nonce' => [:genesis_nonce, dec]
146
+ }
147
+
148
+ genesis.each do |k, v|
149
+ target_key, trans = m[k]
150
+ @config[:eth][:block][target_key] = trans.call v
151
+ end
152
+ end
153
+ end
154
+
155
+ format = " %s %s"
156
+ result = Slop.parse do |o|
157
+ o.separator ''
158
+ o.separator 'Commands:'
159
+ o.separator(format % ['run', 'Start the client (--dev to stop on error).'])
160
+
161
+ o.separator ''
162
+ o.separator 'Options:'
163
+
164
+ o.string '--profile', 'Configuration profile, livenet or testnet. [default: livenet]', default: 'livenet'
165
+ o.string '-c', '--config', 'Alternative configuration file.'
166
+ o.string '-C', 'Single configuration parameters (<param>=<value>).'
167
+ o.string '-d', '--data-dir', "Data directory. [default: #{Reth::Config::DEFAULT_DATA_DIR}]", default: Reth::Config::DEFAULT_DATA_DIR
168
+
169
+ o.string '-l', '--log-config', 'Logger configuration. [default: info]', default: 'info'
170
+ o.string '--log-file', 'Log to file instead of standard outputs.'
171
+
172
+ o.int '-n', '--network-id', 'Network id. Any number greater than 2 will create a private network. If specified predefined profile (by --profile) will not be used.'
173
+ o.string '-b', '--bootstrap-node', 'Single bootstrap node as enode://pubkey@host:port.'
174
+
175
+ o.bool '-m', '--mine', 'Enable miner. [default: disabled]', defalut: false
176
+ o.int '--mining-pct', 'CPU percentage used for mining.'
177
+
178
+ o.string '--unlock', 'Unlock an account (prompts for password).'
179
+ o.string '--password', 'Path to a password file.'
180
+
181
+ o.separator ''
182
+ o.separator 'Misc:'
183
+
184
+ o.on '-v', '--version' do
185
+ puts "version: #{Reth::CLIENT_VERSION_STRING}"
186
+ exit
187
+ end
188
+
189
+ o.on '-h', '--help' do
190
+ puts o
191
+ exit
192
+ end
193
+ end
194
+ opts = result.to_hash
195
+ command = ARGV[0]
196
+
197
+ Reth::Logger.level = opts[:log_config]
198
+
199
+ # frontier genesis:
200
+ # d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3
201
+
202
+ case command
203
+ when 'run'
204
+ RethControl.new(opts).start
205
+ else
206
+ puts result
207
+ end
208
+
@@ -0,0 +1,44 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'devp2p'
4
+ require 'ethereum'
5
+
6
+ module Reth
7
+
8
+ CLIENT_NAME = 'reth'
9
+ CLIENT_VERSION = "#{VERSION}/#{RUBY_PLATFORM}/#{RUBY_ENGINE}-#{RUBY_VERSION}"
10
+ CLIENT_VERSION_STRING = "#{CLIENT_NAME}-v#{CLIENT_VERSION}"
11
+
12
+ Env = Ethereum::Env
13
+ DB = Ethereum::DB
14
+ BlockHeader = Ethereum::BlockHeader
15
+ Block = Ethereum::Block
16
+ Transaction = Ethereum::Transaction
17
+ Chain = Ethereum::Chain
18
+
19
+ Logger = BlockLogger
20
+
21
+ end
22
+
23
+ require 'reth/utils'
24
+ require 'reth/config'
25
+ require 'reth/profile'
26
+
27
+ require 'reth/keystore'
28
+ require 'reth/account'
29
+
30
+ require 'reth/duplicates_filter'
31
+ require 'reth/sync_task'
32
+ require 'reth/synchronizer'
33
+
34
+ require 'reth/transient_block'
35
+ require 'reth/eth_protocol'
36
+
37
+ require 'reth/account_service'
38
+ require 'reth/db_service'
39
+ require 'reth/leveldb_service'
40
+ require 'reth/chain_service'
41
+
42
+ require 'reth/jsonrpc'
43
+ require 'reth/app'
44
+
@@ -0,0 +1,195 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Reth
4
+
5
+ class Account
6
+
7
+ class <<self
8
+ def test_accounts
9
+ return @test_accounts if @test_accounts
10
+
11
+ @test_accounts = 9.times.map do |i|
12
+ privkey = (i+1).chr * 32
13
+ address = Ethereum::PrivateKey.new(privkey).to_address
14
+ ["0x#{Ethereum::Utils.encode_hex(address)}", privkey]
15
+ end.to_h
16
+
17
+ @test_accounts
18
+ end
19
+
20
+ def test_addresses
21
+ @test_addresses ||= test_accounts.keys.map {|k| Ethereum::Utils.normalize_address(k) }
22
+ end
23
+
24
+ ##
25
+ # Create a new account.
26
+ #
27
+ # Note that this creates the account in memory and does not store it on
28
+ # disk.
29
+ #
30
+ # @param password [String] used to encrypt the private key
31
+ # @param key [String] the private key, or `nil` to generate a random one
32
+ # @param uuid [String] an optional id
33
+ #
34
+ def create(password, key=nil, uuid=nil, path=nil)
35
+ key ||= Utils.mk_random_privkey
36
+
37
+ json = Keystore.make_json key, password
38
+ json[:id] = uuid
39
+
40
+ new json, password, path
41
+ end
42
+
43
+ ##
44
+ # Load an account from a keystore file.
45
+ #
46
+ # @param path [String] full path to the keyfile
47
+ # @param password [String] the password to decrypt the key file or
48
+ # `nil` to leave it encrypted
49
+ #
50
+ def load(path, password=nil)
51
+ json = JSON.load File.read(path)
52
+ raise ValidationError, 'Invalid keystore file' unless Keystore.validate(json)
53
+ new json, password, path
54
+ end
55
+ end
56
+
57
+ attr_accessor :path, :address, :keystore
58
+
59
+ def initialize(keystore, password=nil, path=nil)
60
+ @keystore = Hashie.symbolize_keys keystore
61
+ @address = keystore[:address] ? Utils.decode_hex(keystore[:address]) : nil
62
+
63
+ @path = path
64
+ @locked = true
65
+
66
+ unlock(password) if password
67
+ end
68
+
69
+ ##
70
+ # Dump the keystore for later disk storage.
71
+ #
72
+ # The result inherits the entries `crypto` and `version` from `Keystore`,
73
+ # and adds `address` and `id` in accordance with the parameters
74
+ # `include_address` and `include_id`.
75
+ #
76
+ # If address or id are not known, they are not added, even if requested.
77
+ #
78
+ # @param include_address [Bool] flag denoting if the address should be
79
+ # included or not
80
+ # @param include_id [Bool] flag denoting if the id should be included or
81
+ # not
82
+ #
83
+ def dump(include_address=true, include_id=true)
84
+ h = {}
85
+ h[:crypto] = @keystore[:crypto]
86
+ h[:version] = @keystore[:version]
87
+
88
+ h[:address] = Utils.encode_hex address if include_address && address
89
+ h[:id] = uuid if include_id && uuid
90
+
91
+ JSON.dump(h)
92
+ end
93
+
94
+ ##
95
+ # Unlock the account with a password.
96
+ #
97
+ # If the account is already unlocked, nothing happens, even if the
98
+ # password is wrong.
99
+ #
100
+ # @raise [ValueError] (originating from `Keystore.decode_json`) if the
101
+ # password is wrong and the account is locked
102
+ #
103
+ def unlock(password)
104
+ if @locked
105
+ @privkey = Keystore.decode_json @keystore, password
106
+ @locked = false
107
+ address # get address such that it stays accessible after a subsequent lock
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Relock an unlocked account.
113
+ #
114
+ # This method sets `privkey` to `nil` (unlike `address` which is
115
+ # preserved).
116
+ #
117
+ def lock
118
+ @privkey = nil
119
+ @locked = true
120
+ end
121
+
122
+ def privkey
123
+ @locked ? nil : @privkey
124
+ end
125
+
126
+ def pubkey
127
+ @locked ? nil : PrivateKey.new(@privkey).to_pubkey
128
+ end
129
+
130
+ def address
131
+ unless @address
132
+ if @keystore[:address]
133
+ @address = Utils.decode_hex(@keystore[:address])
134
+ elsif !@locked
135
+ @address = PrivateKey.new(@privkey).to_address
136
+ else
137
+ return nil
138
+ end
139
+ end
140
+
141
+ @address
142
+ end
143
+
144
+ ##
145
+ # An optional unique identifier, formatted according to UUID version 4,
146
+ # or `nil` if the account does not have an id.
147
+ #
148
+ def uuid
149
+ @keystore[:id]
150
+ end
151
+
152
+ def uuid=(id)
153
+ if id
154
+ @keystore[:id] = id
155
+ else
156
+ @keystore.delete :id
157
+ end
158
+ end
159
+
160
+ ##
161
+ # Sign a Transaction with the private key of this account.
162
+ #
163
+ # If the account is unlocked, this is equivalent to
164
+ # `tx.sign(account.privkey)`.
165
+ #
166
+ # @param tx [Transaction] the transaction to sign
167
+ # @raise [ValueError] if the account is locked
168
+ #
169
+ def sign_tx(tx)
170
+ if privkey
171
+ logger.info "signing tx", tx: tx, account: self
172
+ tx.sign privkey
173
+ else
174
+ raise ValueError, "Locked account cannot sign tx"
175
+ end
176
+ end
177
+
178
+ def locked?
179
+ @locked
180
+ end
181
+
182
+ def to_s
183
+ addr = address ? Utils.encode_hex(address) : '?'
184
+ "<Account(address=#{addr}, id=#{uuid})>"
185
+ end
186
+
187
+ private
188
+
189
+ def logger
190
+ @logger ||= Logger.new('accounts')
191
+ end
192
+
193
+ end
194
+
195
+ end