public async Task AddNewFiltersAsync(IEnumerable <FilterModel> filters, CancellationToken cancel) { var successAny = false; foreach (var filter in filters) { var success = false; using (await IndexLock.LockAsync(cancel).ConfigureAwait(false)) { success = TryProcessFilter(filter, enqueue: true); } successAny = successAny || success; if (success) { NewFilter?.Invoke(this, filter); // Event always outside the lock. } } if (successAny) { AbandonedTasks.AddAndClearCompleted(TryCommitToFileAsync(TimeSpan.FromSeconds(3), cancel)); } }
public async Task AddNewFiltersAsync(IEnumerable <FilterModel> filters, CancellationToken cancel) { foreach (var filter in filters) { using (await IndexLock.LockAsync()) { ProcessFilter(filter, enqueue: true); } NewFilter?.Invoke(this, filter); // Event always outside the lock. } _ = TryCommitToFileAsync(TimeSpan.FromSeconds(3), cancel); }
public void Synchronize(TimeSpan requestInterval) { Guard.NotNull(nameof(requestInterval), requestInterval); Interlocked.Exchange(ref _running, 1); Task.Run(async() => { FilterModel bestKnownFilter = null; try { while (IsRunning) { try { // If stop was requested return. if (IsRunning == false) { return; } using (await IndexLock.LockAsync()) { bestKnownFilter = Index.Last(); } var filters = await WasabiClient.GetFiltersAsync(bestKnownFilter.BlockHash, 1000); if (!filters.Any()) { continue; } using (await IndexLock.LockAsync()) { var filtersList = filters.ToList(); // performance for (int i = 0; i < filtersList.Count; i++) { var filterModel = FilterModel.FromLine(filtersList[i], bestKnownFilter.BlockHeight + i + 1); Index.Add(filterModel); NewFilter?.Invoke(this, filterModel); } if (filtersList.Count == 1) // minor optimization { await File.AppendAllLinesAsync(IndexFilePath, new[] { Index.Last().ToLine() }); } else { await File.WriteAllLinesAsync(IndexFilePath, Index.Select(x => x.ToLine())); } Logger.LogInfo <IndexDownloader>($"Downloaded filters for blocks from {bestKnownFilter.BlockHeight.Value + 1} to {Index.Last().BlockHeight}."); } continue; } catch (HttpRequestException ex) when(ex.Message.StartsWith(HttpStatusCode.NotFound.ToReasonString())) { // Reorg happened var reorgedHash = bestKnownFilter.BlockHash; Logger.LogInfo <IndexDownloader>($"REORG Invalid Block: {reorgedHash}"); // 1. Rollback index using (await IndexLock.LockAsync()) { Index.RemoveAt(Index.Count - 1); } Reorged?.Invoke(this, reorgedHash); // 2. Serialize Index. (Remove last line.) var lines = File.ReadAllLines(IndexFilePath); File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Skip the last valid block. continue; } catch (Exception ex) { Logger.LogError <IndexDownloader>(ex); } finally { await Task.Delay(requestInterval); // Ask for new index in every requestInterval. } } } finally { if (IsStopping) { Interlocked.Exchange(ref _running, 3); } } }); }
private void OnNewFilter(FilterModel filter) => NewFilter?.Invoke(this, filter);
public void Synchronize(TimeSpan requestInterval) { Guard.NotNull(nameof(requestInterval), requestInterval); Interlocked.Exchange(ref _running, 1); Task.Run(async() => { try { while (IsRunning) { try { // If stop was requested return. if (IsRunning == false) { return; } FilterModel bestKnownFilter; using (await IndexLock.LockAsync()) { bestKnownFilter = Index.Last(); } var response = await Client.SendAsync(HttpMethod.Get, $"/api/v1/btc/blockchain/filters?bestKnownBlockHash={bestKnownFilter.BlockHash}&count=1000"); if (response.StatusCode == HttpStatusCode.NoContent) { continue; } if (response.StatusCode == HttpStatusCode.OK) { var filters = await response.Content.ReadAsJsonAsync <List <string> >(); using (await IndexLock.LockAsync()) { for (int i = 0; i < filters.Count; i++) { var filterModel = FilterModel.FromLine(filters[i], bestKnownFilter.BlockHeight + i + 1); Index.Add(filterModel); NewFilter?.Invoke(this, filterModel); } if (filters.Count == 1) // minor optimization { await File.AppendAllLinesAsync(IndexFilePath, new[] { Index.Last().ToLine() }); } else { await File.WriteAllLinesAsync(IndexFilePath, Index.Select(x => x.ToLine())); } Logger.LogInfo <IndexDownloader>($"Downloaded filters for blocks from {bestKnownFilter.BlockHeight.Value + 1} to {Index.Last().BlockHeight}."); } continue; } else if (response.StatusCode == HttpStatusCode.NotFound) { // Reorg happened var reorgedHash = bestKnownFilter.BlockHash; Logger.LogInfo <IndexDownloader>($"REORG Invalid Block: {reorgedHash}"); // 1. Rollback index using (await IndexLock.LockAsync()) { Index.RemoveAt(Index.Count - 1); } Reorged?.Invoke(this, reorgedHash); // 2. Serialize Index. (Remove last line.) var lines = File.ReadAllLines(IndexFilePath); File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray()); // 3. Skip the last valid block. continue; } else { var error = await response.Content.ReadAsStringAsync(); throw new HttpRequestException($"{response.StatusCode.ToReasonString()}: {error}"); } } catch (Exception ex) { Logger.LogError <IndexDownloader>(ex); } finally { await Task.Delay(requestInterval); // Ask for new index in every requestInterval. } } } finally { if (IsStopping) { Interlocked.Exchange(ref _running, 3); } } }); }