From 645eacea6f2b63bbaf9df2852a433aecfdeff76f Mon Sep 17 00:00:00 2001 From: Varun Srinivasan Date: Sun, 16 Jul 2023 14:36:57 -0700 Subject: [PATCH] refactor(hubble): order and comment cli options for clarity (#1154) --- apps/hubble/src/cli.ts | 233 +++++++++++++++++-------------- apps/hubble/www/docs/docs/cli.md | 63 +++++---- 2 files changed, 169 insertions(+), 127 deletions(-) diff --git a/apps/hubble/src/cli.ts b/apps/hubble/src/cli.ts index 99c01738..44c88bc2 100644 --- a/apps/hubble/src/cli.ts +++ b/apps/hubble/src/cli.ts @@ -51,58 +51,67 @@ const parseNumber = (string: string) => { const app = new Command(); app.name("hub").description("Farcaster Hub").version(APP_VERSION); +/*////////////////////////////////////////////////////////////// + START COMMAND +//////////////////////////////////////////////////////////////*/ + app .command("start") .description("Start a Hub") - .option("-e, --eth-rpc-url ", "RPC URL of a Goerli Ethereum Node (or comma separated list of URLs)") - .option("-l, --l2-rpc-url ", "RPC URL of a Goerli Optimism Node (or comma separated list of URLs)") - .option("-m, --eth-mainnet-rpc-url ", "RPC URL of a mainnet Ethereum Node (or comma separated list of URLs)") - .option("--rank-rpcs", "Rank the RPCs by latency/stability and use the fastest one (default: disabled)") - .option("-c, --config ", "Path to a config file with options") - .option("--fir-address
", "The address of the Farcaster ID Registry contract") - .option("--fnr-address
", "The address of the Farcaster Name Registry contract") - .option("--first-block ", "The block number to begin syncing events from Farcaster contracts", parseNumber) - .option("--fname-server-url ", "The URL for the FName registry server") - .option( - "--chunk-size ", - "The number of blocks to batch when syncing historical events from Farcaster contracts. (default: 10000)", - parseNumber, - ) - .option("-b, --bootstrap ", "A list of peer multiaddrs to bootstrap libp2p") - .option("-a, --allowed-peers ", "An allow-list of peer ids permitted to connect to the hub") - .option("--ip ", 'The IP address libp2p should listen on. (default: "127.0.0.1")') - .option( - "--announce-ip ", - "The IP address libp2p should announce to other peers. If not provided, the IP address will be fetched from an external service", - ) - .option( - "--announce-server-name ", - 'The name of the server to announce to peers. This is useful if you have SSL/TLS enabled. (default: "none")', - ) - .option("-g, --gossip-port ", "The tcp port libp2p should gossip over. (default: 2282)") - .option("-r, --rpc-port ", "The tcp port that the rpc server should listen on. (default: 2283)") - .option( - "--rpc-auth ", - "Enable Auth for RPC submit methods with the username and password. (default: disabled)", - ) - .option( - "--rpc-rate-limit ", - "Impose a Per IP rate limit per minute. Set to -1 for no rate limits (default: 20k/min)", - ) + + // Hubble Options + .option("-n --network ", "ID of the Farcaster Network (default: 3 (devnet))", parseNetwork) + .option("-i, --id ", "Path to the PeerId file.") + .option("-c, --config ", "Path to the config file.") + .option("--db-name ", "The name of the RocksDB instance. (default: rocks.hub._default)") .option("--admin-server-enabled", "Enable the admin server. (default: disabled)") .option("--admin-server-host ", "The host the admin server should listen on. (default: '127.0.0.1')") - .option("--db-name ", "The name of the RocksDB instance") - .option("--profile-sync", "Profile the sync. Will sync the node and exit. (default: disabled)") - .option("--rebuild-sync-trie", "Rebuilds the sync trie before starting") - .option("--resync-eth-events", "Resyncs events from the Farcaster contracts before starting") - .option("--resync-name-events", "Resyncs events from the FName registry server before starting") - .option("--commit-lock-timeout ", "Commit lock timeout in milliseconds (default: 500)", parseNumber) - .option("--commit-lock-max-pending ", "Commit lock max pending jobs (default: 1000)", parseNumber) - .option("-i, --id ", "Path to the PeerId file") - .option("-n --network ", "Farcaster network ID", parseNetwork) - .option("--gossip-metrics-enabled", "Enable gossip network tracing and metrics. (default: disabled)") .option("--process-file-prefix ", 'Prefix for file to which hub process number is written. (default: "")') + + // Ethereum Options + .option("-m, --eth-mainnet-rpc-url ", "RPC URL of a Mainnet ETH Node (or comma separated list of URLs)") + .option("-e, --eth-rpc-url ", "RPC URL of a Goerli ETH Node (or comma separated list of URLs)") + .option("-l, --l2-rpc-url ", "RPC URL of a Goerli Optimism Node (or comma separated list of URLs)") + .option("--rank-rpcs", "Rank the RPCs by latency/stability and use the fastest one (default: disabled)") + .option("--fname-server-url ", `The URL for the FName registry server (default: ${DEFAULT_FNAME_SERVER_URL}`) + .option("--fir-address
", "The address of the Farcaster ID Registry contract") + .option("--first-block ", "The block number to begin syncing events from Farcaster contracts", parseNumber) + + // Networking Options + .option("-a, --allowed-peers ", "Only peer with specific peer ids. (default: all peers allowed)") + .option("-b, --bootstrap ", "Peers to bootstrap gossip and sync from. (default: none)") + .option("-g, --gossip-port ", `Port to use for gossip (default: ${DEFAULT_GOSSIP_PORT})`) + .option("-r, --rpc-port ", `Port to use for gRPC (default: ${DEFAULT_RPC_PORT})`) + .option("--ip ", 'IP address to listen on (default: "127.0.0.1")') + .option("--announce-ip ", "Public IP address announced to peers (default: fetched with external service)") + .option( + "--announce-server-name ", + 'Server name announced to peers, useful if SSL/TLS enabled. (default: "none")', + ) .option("--direct-peers ", "A list of peers for libp2p to directly peer with (default: [])") + .option( + "--rpc-rate-limit ", + "RPC rate limit for peers specified in rpm. Set to -1 for none. (default: 20k/min)", + ) + + // Debugging options + .option("--gossip-metrics-enabled", "Generate tracing and metrics for the gossip network. (default: disabled)") + .option("--profile-sync", "Profile a full hub sync and exit. (default: disabled)") + .option("--rebuild-sync-trie", "Rebuild the sync trie before starting (default: disabled)") + .option("--resync-eth-events", "Resync events from the Farcaster contracts before starting (default: disabled)") + .option("--resync-name-events", "Resync events from the Fname server before starting (default: disabled)") + .option( + "--chunk-size ", + `The number of blocks to batch when syncing historical events from Farcaster contracts. (default: ${DEFAULT_CHUNK_SIZE})`, + parseNumber, + ) + .option("--commit-lock-timeout ", "Rocks DB commit lock timeout in milliseconds (default: 500)", parseNumber) + .option("--commit-lock-max-pending ", "Rocks DB commit lock max pending jobs (default: 1000)", parseNumber) + .option("--rpc-auth ", "Require username-password auth for RPC submit. (default: disabled)") + + // To be deprecated + .option("--fnr-address
", "The address of the Farcaster Name Registry contract") + .action(async (cliOptions) => { const teardown = async (hub: Hub) => { await hub.stop(); @@ -424,6 +433,10 @@ app }); }); +/*////////////////////////////////////////////////////////////// + IDENTITY COMMAND +//////////////////////////////////////////////////////////////*/ + /** Write a given PeerId to a file */ const writePeerId = async (peerId: PeerId, filepath: string) => { const directory = dirname(filepath); @@ -447,7 +460,7 @@ const createIdCommand = new Command("create") .description( "Create a new peerId and write it to a file.\n\nNote: This command will always overwrite the default PeerId file.", ) - .option("-O, --output ", "Path to where the generated PeerId/s should be stored", DEFAULT_PEER_ID_DIR) + .option("-O, --output ", "Path to where the generated PeerIds should be stored", DEFAULT_PEER_ID_DIR) .option("-N, --count ", "Number of PeerIds to generate", parseNumber, 1) .action(async (options) => { for (let i = 0; i < options.count; i++) { @@ -465,40 +478,6 @@ const createIdCommand = new Command("create") exit(0); }); -app - .command("dbreset") - .description("Completely remove the database") - .option("--db-name ", "The name of the RocksDB instance") - .option("-c, --config ", "Path to a config file with options") - .action(async (cliOptions) => { - const hubConfig = cliOptions.config ? (await import(resolve(cliOptions.config))).Config : DefaultConfig; - const rocksDBName = cliOptions.dbName ?? hubConfig.dbName ?? ""; - - if (!rocksDBName) throw new Error("No RocksDB name provided."); - - const rocksDB = new RocksDB(rocksDBName); - const fallback = () => { - fs.rmSync(rocksDB.location, { recursive: true, force: true }); - }; - - const dbResult = await ResultAsync.fromPromise(rocksDB.open(), (e) => e as Error); - if (dbResult.isErr()) { - logger.warn({ rocksDBName }, "Failed to open RocksDB, falling back to rm"); - fallback(); - } else { - const clearResult = await ResultAsync.fromPromise(rocksDB.clear(), (e) => e as Error); - if (clearResult.isErr()) { - logger.warn({ rocksDBName }, "Failed to open RocksDB, falling back to rm"); - fallback(); - } - - await rocksDB.close(); - } - - logger.info({ rocksDBName }, "Database cleared."); - exit(0); - }); - const verifyIdCommand = new Command("verify") .description("Verify a peerId file") .option("-I, --id ", "Path to the PeerId file", DEFAULT_PEER_ID_LOCATION) @@ -514,28 +493,9 @@ app .addCommand(createIdCommand) .addCommand(verifyIdCommand); -const storageProfileCommand = new Command("storage") - .description("Profile the storage layout of the hub, accounting for all the storage") - .option("--db-name ", "The name of the RocksDB instance") - .option("-c, --config ", "Path to a config file with options") - .action(async (cliOptions) => { - const hubConfig = cliOptions.config ? (await import(resolve(cliOptions.config))).Config : DefaultConfig; - const rocksDBName = cliOptions.dbName ?? hubConfig.dbName ?? ""; - const rocksDB = new RocksDB(rocksDBName); - - if (!rocksDBName) throw new Error("No RocksDB name provided."); - const dbResult = await ResultAsync.fromPromise(rocksDB.open(), (e) => e as Error); - if (dbResult.isErr()) { - logger.warn({ rocksDBName }, "Failed to open RocksDB. The Hub needs to be stopped to run this command."); - } else { - await profileStorageUsed(rocksDB); - } - - await rocksDB.close(); - exit(0); - }); - -app.command("profile").description("Profile various resources used by the hub").addCommand(storageProfileCommand); +/*////////////////////////////////////////////////////////////// + STATUS COMMAND +//////////////////////////////////////////////////////////////*/ app .command("status") @@ -612,6 +572,75 @@ app exit(0); }); +/*////////////////////////////////////////////////////////////// + PROFILE COMMAND +//////////////////////////////////////////////////////////////*/ + +const storageProfileCommand = new Command("storage") + .description("Profile the storage layout of the hub, accounting for all the storage") + .option("--db-name ", "The name of the RocksDB instance") + .option("-c, --config ", "Path to a config file with options") + .action(async (cliOptions) => { + const hubConfig = cliOptions.config ? (await import(resolve(cliOptions.config))).Config : DefaultConfig; + const rocksDBName = cliOptions.dbName ?? hubConfig.dbName ?? ""; + const rocksDB = new RocksDB(rocksDBName); + + if (!rocksDBName) throw new Error("No RocksDB name provided."); + const dbResult = await ResultAsync.fromPromise(rocksDB.open(), (e) => e as Error); + if (dbResult.isErr()) { + logger.warn({ rocksDBName }, "Failed to open RocksDB. The Hub needs to be stopped to run this command."); + } else { + await profileStorageUsed(rocksDB); + } + + await rocksDB.close(); + exit(0); + }); + +app.command("profile").description("Profile various resources used by the hub").addCommand(storageProfileCommand); + +/*////////////////////////////////////////////////////////////// + DBRESET COMMAND +//////////////////////////////////////////////////////////////*/ + +app + .command("dbreset") + .description("Completely remove the database") + .option("--db-name ", "The name of the RocksDB instance") + .option("-c, --config ", "Path to a config file with options") + .action(async (cliOptions) => { + const hubConfig = cliOptions.config ? (await import(resolve(cliOptions.config))).Config : DefaultConfig; + const rocksDBName = cliOptions.dbName ?? hubConfig.dbName ?? ""; + + if (!rocksDBName) throw new Error("No RocksDB name provided."); + + const rocksDB = new RocksDB(rocksDBName); + const fallback = () => { + fs.rmSync(rocksDB.location, { recursive: true, force: true }); + }; + + const dbResult = await ResultAsync.fromPromise(rocksDB.open(), (e) => e as Error); + if (dbResult.isErr()) { + logger.warn({ rocksDBName }, "Failed to open RocksDB, falling back to rm"); + fallback(); + } else { + const clearResult = await ResultAsync.fromPromise(rocksDB.clear(), (e) => e as Error); + if (clearResult.isErr()) { + logger.warn({ rocksDBName }, "Failed to open RocksDB, falling back to rm"); + fallback(); + } + + await rocksDB.close(); + } + + logger.info({ rocksDBName }, "Database cleared."); + exit(0); + }); + +/*////////////////////////////////////////////////////////////// + CONSOLE COMMAND +//////////////////////////////////////////////////////////////*/ + app .command("console") .description("Start a REPL console") diff --git a/apps/hubble/www/docs/docs/cli.md b/apps/hubble/www/docs/docs/cli.md index f0c35ff2..c8103eab 100644 --- a/apps/hubble/www/docs/docs/cli.md +++ b/apps/hubble/www/docs/docs/cli.md @@ -25,34 +25,47 @@ Usage: yarn start [options] Start a Hub -Options: - -e, --eth-rpc-url RPC URL of a Goerli Ethereum Node - -c, --config Path to a config file with options - --fir-address
The address of the Farcaster ID Registry contract - --fnr-address
The address of the Farcaster Name Registry contract - --first-block The block number to begin syncing events from Farcaster contracts - --fname-server-url The URL for the FName registry server - --chunk-size The number of blocks to batch when syncing historical events from Farcaster contracts. (default: 10000) - -b, --bootstrap A list of peer multiaddrs to bootstrap libp2p - -a, --allowed-peers An allow-list of peer ids permitted to connect to the hub - --ip The IP address libp2p should listen on. (default: "127.0.0.1") - --announce-ip The IP address libp2p should announce to other peers. If not provided, the IP address will be fetched from an external service - --announce-server-name The name of the server to announce to peers. This is useful if you have SSL/TLS enabled. (default: "none") - -g, --gossip-port The tcp port libp2p should gossip over. (default: 2282) - -r, --rpc-port The tcp port that the rpc server should listen on. (default: 2283) - --rpc-auth Enable Auth for RPC submit methods with the username and password. (default: disabled) - --rpc-rate-limit Impose a Per IP rate limit per minute. Set to -1 for no rate limits (default: 20k/min) +Hubble Options: + -n --network ID of the Farcaster Network (default: 3 (devnet)) + -i, --id Path to the PeerId file. + -c, --config Path to the config file. + --db-name The name of the RocksDB instance. (default: rocks.hub._default) --admin-server-enabled Enable the admin server. (default: disabled) --admin-server-host The host the admin server should listen on. (default: '127.0.0.1') - --db-name The name of the RocksDB instance - --rebuild-sync-trie Rebuilds the sync trie before starting - --resync-eth-events Resyncs events from the Farcaster contracts before starting - --resync-name-events Resyncs events from the FName registry server before starting - --commit-lock-timeout Commit lock timeout in milliseconds (default: 500) - --commit-lock-max-pending Commit lock max pending jobs (default: 1000) - -i, --id Path to the PeerId file - -n --network Farcaster network ID --process-file-prefix Prefix for file to which hub process number is written. (default: "") + +Ethereum Options: + -m, --eth-mainnet-rpc-url RPC URL of a Mainnet ETH Node (or comma separated list of URLs) + -e, --eth-rpc-url RPC URL of a Goerli ETH Node (or comma separated list of URLs) + -l, --l2-rpc-url RPC URL of a Goerli Optimism Node (or comma separated list of URLs) + --rank-rpcs Rank the RPCs by latency/stability and use the fastest one (default: disabled) + --fname-server-url The URL for the FName registry server (default: https://fnames.farcaster.xyz + --fir-address
The address of the Farcaster ID Registry contract + --first-block The block number to begin syncing events from Farcaster contracts + +Networking Options: + -a, --allowed-peers Only peer with specific peer ids. (default: all peers allowed) + -b, --bootstrap Peers to bootstrap gossip and sync from. (default: none) + -g, --gossip-port Port to use for gossip (default: 2282) + -r, --rpc-port Port to use for gRPC (default: 2283) + --ip IP address to listen on (default: "127.0.0.1") + --announce-ip Public IP address announced to peers (default: fetched with external service) + --announce-server-name Server name announced to peers, useful if SSL/TLS enabled. (default: "none") + --direct-peers A list of peers for libp2p to directly peer with (default: []) + --rpc-rate-limit RPC rate limit for peers specified in rpm. Set to -1 for none. (default: 20k/min) + +Debugging Options: + --gossip-metrics-enabled Generate tracing and metrics for the gossip network. (default: disabled) + --profile-sync Profile a full hub sync and exit. (default: disabled) + --rebuild-sync-trie Rebuild the sync trie before starting (default: disabled) + --resync-eth-events Resync events from the Farcaster contracts before starting (default: disabled) + --resync-name-events Resync events from the Fname server before starting (default: disabled) + --chunk-size The number of blocks to batch when syncing historical events from Farcaster contracts. (default: 10000) + --commit-lock-timeout Rocks DB commit lock timeout in milliseconds (default: 500) + --commit-lock-max-pending Rocks DB commit lock max pending jobs (default: 1000) + --rpc-auth Require username-password auth for RPC submit. (default: disabled) + + --fnr-address
The address of the Farcaster Name Registry contract -h, --help display help for command ```