protected virtual void SetupJobUpdates() { if (poolConfig.ExternalStratum) { return; } var enableStreaming = extraPoolConfig?.EnableDaemonWebsocketStreaming == true; if (enableStreaming && !poolConfig.Daemons.Any(x => x.Extra.SafeExtensionDataAs <EthereumDaemonEndpointConfigExtra>()?.PortWs.HasValue == true)) { logger.Warn(() => $"[{LogCat}] '{nameof(EthereumPoolConfigExtra.EnableDaemonWebsocketStreaming).ToLowerCamelCase()}' enabled but not a single daemon found with a configured websocket port ('{nameof(EthereumDaemonEndpointConfigExtra.PortWs).ToLowerCamelCase()}'). Falling back to polling."); enableStreaming = false; } if (enableStreaming) { // collect ports var wsDaemons = poolConfig.Daemons .Where(x => x.Extra.SafeExtensionDataAs <EthereumDaemonEndpointConfigExtra>()?.PortWs.HasValue == true) .ToDictionary(x => x, x => x.Extra.SafeExtensionDataAs <EthereumDaemonEndpointConfigExtra>().PortWs.Value); logger.Info(() => $"[{LogCat}] Subscribing to WebSocket push-updates from {string.Join(", ", wsDaemons.Keys.Select(x=> x.Host).Distinct())}"); // stream pending blocks var pendingBlockObs = daemon.WebsocketSubscribe(wsDaemons, EC.ParitySubscribe, new[] { (object)EC.GetBlockByNumber, new[] { "pending", (object)true } }) .Select(data => { try { var psp = DeserializeRequest(data).ParamsAs <PubSubParams <Block> >(); return(psp?.Result); } catch (Exception ex) { logger.Info(() => $"[{LogCat}] Error deserializing pending block: {ex.Message}"); } return(null); }); // stream work updates var getWorkObs = daemon.WebsocketSubscribe(wsDaemons, EC.ParitySubscribe, new[] { (object)EC.GetWork }) .Select(data => { try { var psp = DeserializeRequest(data).ParamsAs <PubSubParams <string[]> >(); return(psp?.Result); } catch (Exception ex) { logger.Info(() => $"[{LogCat}] Error deserializing pending block: {ex.Message}"); } return(null); }); Jobs = Observable.CombineLatest( pendingBlockObs.Where(x => x != null), getWorkObs.Where(x => x != null), AssembleBlockTemplate) .Select(UpdateJob) .Do(isNew => { if (isNew) { logger.Info(() => $"[{LogCat}] New block {currentJob.BlockTemplate.Height} detected"); } }) .Where(isNew => isNew) .Select(_ => GetJobParamsForStratum(true)) .Publish() .RefCount(); } else { Jobs = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) .Select(_ => Observable.FromAsync(UpdateJobAsync)) .Concat() .Do(isNew => { if (isNew) { logger.Info(() => $"[{LogCat}] New block {currentJob.BlockTemplate.Height} detected"); } }) .Where(isNew => isNew) .Select(_ => GetJobParamsForStratum(true)) .Publish() .RefCount(); } }