1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
use clap::{App, Arg, SubCommand};

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new("Beacon Node")
        .visible_aliases(&["b", "bn", "beacon", "beacon_node"])
        .version(crate_version!())
        .author("Sigma Prime <contact@sigmaprime.io>")
        .about("Eth 2.0 Client")
        /*
         * Configuration directory locations.
         */
        .arg(
            Arg::with_name("datadir")
                .long("datadir")
                .value_name("DIR")
                .help("Data directory for keys and databases.")
                .takes_value(true)
                .global(true)
        )
        .arg(
            Arg::with_name("network-dir")
                .long("network-dir")
                .value_name("DIR")
                .help("Data directory for network keys.")
                .takes_value(true)
                .global(true)
        )
        /*
         * Network parameters.
         */
        .arg(
            Arg::with_name("port-bump")
                .long("port-bump")
                .short("b")
                .value_name("INCREMENT")
                .help("Sets all listening TCP/UDP ports to default values, but with each port increased by \
                      INCREMENT. Useful when starting multiple nodes on a single machine. Using increments \
                      in multiples of 10 is recommended.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("listen-address")
                .long("listen-address")
                .value_name("ADDRESS")
                .help("The address lighthouse will listen for UDP and TCP connections. (default 127.0.0.1).")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("port")
                .long("port")
                .value_name("PORT")
                .help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.")
                .conflicts_with("port-bump")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("maxpeers")
                .long("maxpeers")
                .help("The maximum number of peers (default 10).")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("boot-nodes")
                .long("boot-nodes")
                .allow_hyphen_values(true)
                .value_name("ENR-LIST")
                .help("One or more comma-delimited base64-encoded ENR's to bootstrap the p2p network.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("discovery-port")
                .long("disc-port")
                .value_name("PORT")
                .help("The discovery UDP port.")
                .conflicts_with("port-bump")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("discovery-address")
                .long("discovery-address")
                .value_name("ADDRESS")
                .help("The IP address to broadcast to other peers on how to reach this node.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("topics")
                .long("topics")
                .value_name("STRING")
                .help("One or more comma-delimited gossipsub topic strings to subscribe to.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("libp2p-addresses")
                .long("libp2p-addresses")
                .value_name("MULTIADDR")
                .help("One or more comma-delimited multiaddrs to manually connect to a libp2p peer without an ENR.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("p2p-priv-key")
                .long("p2p-priv-key")
                .value_name("HEX")
                .help("A secp256k1 secret key, represented as ASCII-encoded hex bytes (with or without 0x prefix).")
                .takes_value(true),
        )
        /*
         * gRPC parameters.
         */
        .arg(
            Arg::with_name("no-grpc")
                .long("no-grpc")
                .help("Disable the gRPC server.")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("rpc-address")
                .long("rpc-address")
                .value_name("ADDRESS")
                .help("Listen address for RPC endpoint.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("rpc-port")
                .long("rpc-port")
                .value_name("PORT")
                .help("Listen port for RPC endpoint.")
                .conflicts_with("port-bump")
                .takes_value(true),
        )
        /* REST API related arguments */
        .arg(
            Arg::with_name("no-api")
                .long("no-api")
                .help("Disable RESTful HTTP API server.")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("api-address")
                .long("api-address")
                .value_name("ADDRESS")
                .help("Set the listen address for the RESTful HTTP API server.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("api-port")
                .long("api-port")
                .value_name("PORT")
                .help("Set the listen TCP port for the RESTful HTTP API server.")
                .conflicts_with("port-bump")
                .takes_value(true),
        )
        /* Websocket related arguments */
        .arg(
            Arg::with_name("no-ws")
                .long("no-ws")
                .help("Disable websocket server.")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("ws-address")
                .long("ws-address")
                .value_name("ADDRESS")
                .help("Set the listen address for the websocket server.")
                .conflicts_with_all(&["no-ws"])
                .takes_value(true),
        )
        .arg(
            Arg::with_name("ws-port")
                .long("ws-port")
                .value_name("PORT")
                .help("Set the listen TCP port for the websocket server.")
                .conflicts_with_all(&["no-ws", "port-bump"])
                .takes_value(true),
        )

        /*
         * Eth1 Integration
         */
        .arg(
            Arg::with_name("dummy-eth1")
                .long("dummy-eth1")
                .help("If present, uses an eth1 backend that generates static dummy data.\
                      Identical to the method used at the 2019 Canada interop.")
        )
        .arg(
            Arg::with_name("eth1-endpoint")
                .long("eth1-endpoint")
                .value_name("HTTP-ENDPOINT")
                .help("Specifies the server for a web3 connection to the Eth1 chain.")
                .takes_value(true)
                .default_value("http://localhost:8545")
        )
        .arg(
            Arg::with_name("eth1-follow")
                .long("eth1-follow")
                .value_name("BLOCK_COUNT")
                .help("Specifies how many blocks we should cache behind the eth1 head. A larger number means a smaller cache.")
                .takes_value(true)
                // TODO: set this higher once we're not using testnets all the time.
                .default_value("0")
        )
        .arg(
            Arg::with_name("deposit-contract")
                .long("deposit-contract")
                .short("e")
                .value_name("DEPOSIT-CONTRACT")
                .help("Specifies the deposit contract address on the Eth1 chain.")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("deposit-contract-deploy")
                .long("deposit-contract-deploy")
                .value_name("BLOCK_NUMBER")
                .help("Specifies the block number that the deposit contract was deployed at.")
                .takes_value(true)
                // TODO: set this higher once we're not using testnets all the time.
                .default_value("0")
        )
        /*
         * The "testnet" sub-command.
         *
         * Allows for creating a new datadir with testnet-specific configs.
         */
        .subcommand(SubCommand::with_name("testnet")
            .about("Create a new Lighthouse datadir using a testnet strategy.")
            .arg(
                Arg::with_name("eth2-config")
                    .long("eth2-config")
                    .value_name("TOML_FILE")
                    .help("A existing eth2_spec TOML file (e.g., eth2_spec.toml).")
                    .takes_value(true)
                    .conflicts_with("spec")
            )
            .arg(
                Arg::with_name("client-config")
                    .long("client-config")
                    .value_name("TOML_FILE")
                    .help("An existing beacon_node TOML file (e.g., beacon_node.toml).")
                    .takes_value(true)
            )
            .arg(
                Arg::with_name("random-datadir")
                    .long("random-datadir")
                    .short("r")
                    .help("If present, append a random string to the datadir path. Useful for fast development \
                          iteration.")
            )
            .arg(
                Arg::with_name("force")
                    .long("force")
                    .short("f")
                    .help("If present, will create new config and database files and move the any existing to a \
                           backup directory.")
                    .conflicts_with("random-datadir")
            )
            .arg(
                Arg::with_name("slot-time")
                    .long("slot-time")
                    .short("t")
                    .value_name("MILLISECONDS")
                    .help("Defines the slot time when creating a new testnet.")
            )
            /*
             * `boostrap`
             *
             * Start a new node by downloading genesis and network info from another node via the
             * HTTP API.
             */
            .subcommand(SubCommand::with_name("bootstrap")
                .about("Connects to the given HTTP server, downloads a genesis state and attempts to peer with it.")
                .arg(Arg::with_name("server")
                    .value_name("HTTP_SERVER")
                    .required(true)
                    .default_value("http://localhost:5052")
                    .help("A HTTP server, with a http:// prefix"))
                .arg(Arg::with_name("libp2p-port")
                    .short("p")
                    .long("port")
                    .value_name("TCP_PORT")
                    .help("A libp2p listen port used to peer with the bootstrap server. This flag is useful \
                           when port-fowarding is used: you may connect using a different port than \
                           the one the server is immediately listening on."))
            )
            /*
             * `recent`
             *
             * Start a new node, with a specified number of validators with a genesis time in the last
             * 30-minutes.
             */
            .subcommand(SubCommand::with_name("recent")
                .about("Creates a new genesis state where the genesis time was at the previous \
                       MINUTES boundary (e.g., when MINUTES == 30; 12:00, 12:30, 13:00, etc.)")
                .arg(Arg::with_name("validator_count")
                    .value_name("VALIDATOR_COUNT")
                    .required(true)
                    .help("The number of validators in the genesis state"))
                .arg(Arg::with_name("minutes")
                    .long("minutes")
                    .short("m")
                    .value_name("MINUTES")
                    .required(true)
                    .default_value("15")
                    .help("The maximum number of minutes that will have elapsed before genesis"))
            )
            /*
             * `quick`
             *
             * Start a new node, specifying the number of validators and genesis time
             */
            .subcommand(SubCommand::with_name("quick")
                .about("Creates a new genesis state from the specified validator count and genesis time. \
                        Compatible with the `quick-start genesis` defined in the eth2.0-pm repo.")
                .arg(Arg::with_name("validator_count")
                    .value_name("VALIDATOR_COUNT")
                    .required(true)
                    .help("The number of validators in the genesis state"))
                .arg(Arg::with_name("genesis_time")
                    .value_name("UNIX_EPOCH_SECONDS")
                    .required(true)
                    .help("The genesis time for the given state."))
            )
            /*
             * `yaml`
             *
             * Start a new node, using a genesis state loaded from a YAML file
             */
            .subcommand(SubCommand::with_name("file")
                .about("Creates a new datadir where the genesis state is read from file. May fail to parse \
                       a file that was generated to a different spec than that specified by --spec.")
                .arg(Arg::with_name("format")
                    .value_name("FORMAT")
                    .required(true)
                    .possible_values(&["ssz"])
                    .help("The encoding of the state in the file."))
                .arg(Arg::with_name("file")
                    .value_name("FILE")
                    .required(true)
                    .help("A file from which to read the state"))
            )
            /*
             * `prysm`
             *
             * Connect to the Prysmatic Labs testnet.
             */
            .subcommand(SubCommand::with_name("prysm")
                .about("Connect to the Prysmatic Labs testnet on Goerli.")
            )
        )
}