diff --git a/apps/hubble/.config/hub.config.ts b/apps/hubble/.config/hub.config.ts index 2eaa1bcc..83b76f84 100644 --- a/apps/hubble/.config/hub.config.ts +++ b/apps/hubble/.config/hub.config.ts @@ -43,4 +43,6 @@ export const Config = { network: DEFAULT_NETWORK, /** Start the admin server? */ adminServerEnabled: false, + /** The admin server bind host */ + adminServerHost: '127.0.0.1', }; diff --git a/apps/hubble/src/cli.ts b/apps/hubble/src/cli.ts index ce8215f1..b9461f88 100644 --- a/apps/hubble/src/cli.ts +++ b/apps/hubble/src/cli.ts @@ -57,6 +57,7 @@ app 'Enable Auth for RPC submit methods with the username and password. (default: disabled)' ) .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('--db-reset', 'Clears the database before starting') .option('--rebuild-sync-trie', 'Rebuilds the sync trie before starting') @@ -202,6 +203,7 @@ app resetDB: cliOptions.dbReset ?? hubConfig.dbReset, rebuildSyncTrie, adminServerEnabled: cliOptions.adminServerEnabled ?? hubConfig.adminServerEnabled, + adminServerHost: cliOptions.adminServerHost ?? hubConfig.adminServerHost, }; const hub = new Hub(options); diff --git a/apps/hubble/src/hubble.ts b/apps/hubble/src/hubble.ts index c918e435..7aa33387 100644 --- a/apps/hubble/src/hubble.ts +++ b/apps/hubble/src/hubble.ts @@ -120,6 +120,9 @@ export interface HubOptions { /** Enables the Admin Server */ adminServerEnabled?: boolean; + /** Host for the Admin Server to bind to */ + adminServerHost?: string; + /** * Only allows the Hub to connect to and advertise local IP addresses * @@ -162,10 +165,10 @@ export class Hub implements HubInterface { private pruneEventsJobScheduler: PruneEventsJobScheduler; private updateNameRegistryEventExpiryJobQueue: UpdateNameRegistryEventExpiryJobQueue; - private updateNameRegistryEventExpiryJobWorker: UpdateNameRegistryEventExpiryJobWorker; + private updateNameRegistryEventExpiryJobWorker?: UpdateNameRegistryEventExpiryJobWorker; engine: Engine; - ethRegistryProvider: EthEventsProvider; + ethRegistryProvider?: EthEventsProvider; constructor(options: HubOptions) { this.options = options; @@ -179,14 +182,18 @@ export class Hub implements HubInterface { // Create the ETH registry provider, which will fetch ETH events and push them into the engine. // Defaults to Goerli testnet, which is currently used for Production Farcaster Hubs. - this.ethRegistryProvider = EthEventsProvider.build( - this, - options.ethRpcUrl ?? '', - options.idRegistryAddress ?? GoerliEthConstants.IdRegistryAddress, - options.nameRegistryAddress ?? GoerliEthConstants.NameRegistryAddress, - options.firstBlock ?? GoerliEthConstants.FirstBlock, - options.chunkSize ?? GoerliEthConstants.ChunkSize - ); + if (options.ethRpcUrl) { + this.ethRegistryProvider = EthEventsProvider.build( + this, + options.ethRpcUrl, + options.idRegistryAddress ?? GoerliEthConstants.IdRegistryAddress, + options.nameRegistryAddress ?? GoerliEthConstants.NameRegistryAddress, + options.firstBlock ?? GoerliEthConstants.FirstBlock, + options.chunkSize ?? GoerliEthConstants.ChunkSize + ); + } else { + log.warn('No ETH RPC URL provided, not syncing with ETH contract events'); + } // Setup job queues this.revokeSignerJobQueue = new RevokeSignerJobQueue(this.rocksDB); @@ -197,11 +204,14 @@ export class Hub implements HubInterface { this.pruneMessagesJobScheduler = new PruneMessagesJobScheduler(this.engine); this.periodSyncJobScheduler = new PeriodicSyncJobScheduler(this, this.syncEngine); this.pruneEventsJobScheduler = new PruneEventsJobScheduler(this.engine); - this.updateNameRegistryEventExpiryJobWorker = new UpdateNameRegistryEventExpiryJobWorker( - this.updateNameRegistryEventExpiryJobQueue, - this.rocksDB, - this.ethRegistryProvider - ); + + if (this.ethRegistryProvider) { + this.updateNameRegistryEventExpiryJobWorker = new UpdateNameRegistryEventExpiryJobWorker( + this.updateNameRegistryEventExpiryJobQueue, + this.rocksDB, + this.ethRegistryProvider + ); + } } get rpcAddress() { @@ -274,7 +284,9 @@ export class Hub implements HubInterface { } // Start the ETH registry provider first - await this.ethRegistryProvider.start(); + if (this.ethRegistryProvider) { + await this.ethRegistryProvider.start(); + } // Start the sync engine await this.syncEngine.initialize(this.options.rebuildSyncTrie ?? false); @@ -290,7 +302,7 @@ export class Hub implements HubInterface { // Start the RPC server await this.rpcServer.start(this.options.rpcPort ? this.options.rpcPort : 0); if (this.options.adminServerEnabled) { - await this.adminServer.start(); + await this.adminServer.start(this.options.adminServerHost ?? '127.0.0.1'); } this.registerEventHandlers(); @@ -347,7 +359,9 @@ export class Hub implements HubInterface { await this.syncEngine.stop(); // Stop the ETH registry provider - await this.ethRegistryProvider.stop(); + if (this.ethRegistryProvider) { + await this.ethRegistryProvider.stop(); + } // Close the DB, which will flush all data to disk. Just before we close, though, write that // we've cleanly shutdown. diff --git a/apps/hubble/src/rpc/adminServer.ts b/apps/hubble/src/rpc/adminServer.ts index 65263086..1454033b 100644 --- a/apps/hubble/src/rpc/adminServer.ts +++ b/apps/hubble/src/rpc/adminServer.ts @@ -36,7 +36,7 @@ export default class AdminServer { this.grpcServer.addService(AdminServiceService, this.getImpl()); } - async start(): HubAsyncResult { + async start(host = '127.0.0.1'): HubAsyncResult { // Create a unix socket server net.createServer(() => { log.info('Admin server socket connected'); @@ -44,13 +44,13 @@ export default class AdminServer { return new Promise((resolve) => { // The Admin server is only available on localhost - this.grpcServer.bindAsync(`127.0.0.1:${ADMIN_SERVER_PORT}`, ServerCredentials.createInsecure(), (e, port) => { + this.grpcServer.bindAsync(`${host}:${ADMIN_SERVER_PORT}`, ServerCredentials.createInsecure(), (e, port) => { if (e) { log.error(`Failed to bind admin server to socket: ${e}`); resolve(err(new HubError('unavailable.network_failure', `Failed to bind admin server to socket: ${e}`))); } else { this.grpcServer.start(); - log.info({ port }, 'Starting Admin server'); + log.info({ host, port }, 'Starting Admin server'); resolve(ok(undefined)); } }); diff --git a/apps/hubble/src/storage/engine/index.ts b/apps/hubble/src/storage/engine/index.ts index 5499848c..cbf18a80 100644 --- a/apps/hubble/src/storage/engine/index.ts +++ b/apps/hubble/src/storage/engine/index.ts @@ -74,6 +74,8 @@ class Engine { } else { resolve(err(new HubError(errCode, errMessage))); } + } else { + logger.warn({ id }, 'validation worker promise.response not found'); } }); } else { @@ -592,14 +594,12 @@ class Engine { // 6. Check message body and envelope if (this._validationWorker) { - const promise = new Promise>((resolve) => { + return new Promise>((resolve) => { const id = this._validationWorkerJobId++; this._validationWorkerPromiseMap.set(id, resolve); this._validationWorker?.postMessage({ id, message }); }); - - return promise; } else { return validations.validateMessage(message); }