protected virtual void SetupJobUpdates() { if (!poolConfig.EnableInternalStratum) { return; } jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout)); var sources = new List <IObservable <bool> >(); var cancelTimeout = new List <IObservable <bool> >(); // collect ports var zmq = poolConfig.Daemons .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs <BitcoinDaemonEndpointConfigExtra>()?.ZmqBlockNotifySocket)) .ToDictionary(x => x, x => x.Extra.SafeExtensionDataAs <BitcoinDaemonEndpointConfigExtra>().ZmqBlockNotifySocket); if (zmq.Count > 0) { logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) .Select(frames => { try { // second frame contains the block hash as binary data if (frames.Length > 1) { var hash = frames[1].ToHexString(); return(hash); } } finally { frames.Dispose(); } return(null); }) .Where(x => x != null) .DistinctUntilChanged() .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) .Concat() .Publish() .RefCount(); sources.Add(newJobsPubSub); cancelTimeout.Add(newJobsPubSub); } if (poolConfig.BlockRefreshInterval > 0) { // periodically update block-template from daemon var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) .Concat() .Where(isNew => isNew) .Publish() .RefCount(); sources.Add(newJobsPolled); cancelTimeout.Add(newJobsPolled); } else { // poll for the first successful update after which polling is suspended forever var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval)) .Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling"))) .Concat() .Where(isNew => isNew) .Take(1) .Publish() .RefCount(); sources.Add(newJobsPolled); cancelTimeout.Add(newJobsPolled); } // if there haven't been any new jobs for a while, force an update var cancelRebroadcast = cancelTimeout.Count > 0 ? cancelTimeout.Count > 1 ? Observable.Merge(cancelTimeout) : cancelTimeout.First() : Observable.Never <bool>(); sources.Add(Observable.Timer(jobRebroadcastTimeout) .TakeUntil(cancelRebroadcast) // cancel timeout if an actual new job has been detected .Do(_ => logger.Debug(() => $"[{LogCat}] No new blocks for {jobRebroadcastTimeout.TotalSeconds} seconds - updating transactions & rebroadcasting work")) .Select(x => Observable.FromAsync(() => UpdateJob(true))) .Concat() .Repeat()); Jobs = Observable.Merge(sources) .Select(GetJobParamsForStratum); }