Use "async" + add unit test.
MODIFY
WalletWasabi.Packager/BitnodesApi.cs
WalletWasabi.Packager/BitnodesApi.cs
@@ -6,29 +6,64 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Threading.Tasks;
namespace WalletWasabi.Packager
{
+ /// <summary>
+ /// Implementation for Bitnodes API.
+ /// </summary>
+ /// <seealso href="https://bitnodes.io/api/"/>
public class BitnodesApi
{
- public static void PrintOnions(TextWriter textWriter)
+ public BitnodesApi(TextWriter textWriter)
{
- PrintOnions(textWriter, null);
+ TextWriter = textWriter;
}
- public static void PrintOnions(TextWriter textWriter, HashSet<string> currentOnions)
+ private TextWriter TextWriter { get; }
+
+ /// <summary>
+ /// Finds all Bitcoin nodes with ".onion" in their names, sorts by node names and writes to <see cref="TextWriter"/>.
+ /// </summary>
+ /// <returns></returns>
+ public async Task PrintOnionsAsync()
+ {
+ await PrintOnionsAsync(currentOnions: null);
+ }
+
+ /// <summary>
+ /// Finds all Bitcoin nodes with ".onion" in their names, sorts by node names and writes to <see cref="TextWriter"/>.
+ /// </summary>
+ /// <remarks>Allows to pre-define set of Bitcoin nodes with ".onion" in their names.</remarks>
+ /// <param name="currentOnions">Set of Bitcoin node names containing ".onion".</param>
+ /// <returns></returns>
+ public async Task PrintOnionsAsync(HashSet<string>? currentOnions)
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://bitnodes.21.co/api/v1/");
- using var response = httpClient.GetAsync("snapshots/latest/", HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
- if (response.StatusCode != HttpStatusCode.OK)
+ // List all reachable nodes from a snapshot.
+ // See https://bitnodes.io/api/#list-nodes.
+ using var httpResponse = await httpClient.GetAsync("snapshots/latest/", HttpCompletionOption.ResponseContentRead);
+
+ if (httpResponse.StatusCode != HttpStatusCode.OK)
{
- throw new HttpRequestException(response.StatusCode.ToString());
+ throw new HttpRequestException(httpResponse.StatusCode.ToString());
}
- var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
- JObject json = (JObject)JsonConvert.DeserializeObject(responseString);
+ string response = await httpResponse.Content.ReadAsStringAsync();
+
+ ProcessResponse(response, currentOnions);
+ }
+
+ /// <summary>
+ /// Internal method. Use only in tests.
+ /// </summary>
+ /// <param name="currentOnions">Set of Bitcoin node names containing ".onion".</param>
+ public void ProcessResponse(string response, HashSet<string>? currentOnions)
+ {
+ JObject json = (JObject)JsonConvert.DeserializeObject(response);
var onions = new List<string>();
foreach (JProperty node in json["nodes"])
@@ -58,7 +93,7 @@ namespace WalletWasabi.Packager
foreach (var onion in onions.OrderBy(x => x))
{
- textWriter.WriteLine(onion);
+ TextWriter.WriteLine(onion);
}
}
}

MODIFY
WalletWasabi.Packager/Program.cs
WalletWasabi.Packager/Program.cs
using NSubsys;
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
-using System.Net;
-using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
-using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
@@ -59,7 +55,7 @@ namespace WalletWasabi.Packager
/// <summary>
/// Main entry point.
/// </summary>
- private static void Main(string[] args)
+ private static async Task Main(string[] args)
{
var argsProcessor = new ArgsProcessor(args);
@@ -73,14 +69,21 @@ namespace WalletWasabi.Packager
// If I want a list of up to date onions run it with '--getonions'.
if (argsProcessor.IsGetOnionsMode())
{
- BitnodesApi.PrintOnions(Console.Out);
+ var api = new BitnodesApi(Console.Out);
+ await api.PrintOnionsAsync();
+
return;
}
// If I want a list of up to date onions run it with '--reduceonions'.
if (argsProcessor.IsReduceOnionsMode())
{
- ReduceOnions();
+ string onionFilePath = Path.Combine(LibraryProjectDirectory, "OnionSeeds", "MainOnionSeeds.txt");
+ var currentOnions = File.ReadAllLines(onionFilePath).ToHashSet();
+
+ var api = new BitnodesApi(Console.Out);
+ await api.PrintOnionsAsync(currentOnions);
+
return;
}
@@ -118,13 +121,6 @@ namespace WalletWasabi.Packager
}
}
- private static void ReduceOnions()
- {
- var onionFile = Path.Combine(LibraryProjectDirectory, "OnionSeeds", "MainOnionSeeds.txt");
- var currentOnions = File.ReadAllLines(onionFile).ToHashSet();
- BitnodesApi.PrintOnions(Console.Out, currentOnions);
- }
-
private static void CreateDigests()
{
var tempDir = "DigestTempDir";

ADD
WalletWasabi.Tests/UnitTests/Packager/BitnodesApiTests.cs
+using System;
+using System.IO;
+using WalletWasabi.Packager;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Packager
+{
+ /// <summary>
+ /// Tests for <see cref="BitnodesApi"/>.
+ /// </summary>
+ public class BitnodesApiTests
+ {
+ [Fact]
+ public void ProcessResponseTest()
+ {
+ var stringWriter = new StringWriter();
+ var api = new BitnodesApi(stringWriter);
+
+ string response = @"
+ {
+ ""timestamp"": 1597669414,
+ ""total_nodes"": 10407,
+ ""latest_height"": 644147,
+ ""nodes"": {
+ ""tg4uwrjmtr2jlbjy.onion:8333"": [
+ 70015,
+ ""/Satoshi:0.19.0.1/"",
+ 1597459201,
+ 1033,
+ 0,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ null,
+ ""TOR"",
+ ""Tor network""
+ ],
+ ""nesxfmano25clfvn.onion:8333"": [
+ 70015,
+ ""/Satoshi:0.20.0/"",
+ 1597596093,
+ 1037,
+ 644124,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ null,
+ ""TOR"",
+ ""Tor network""
+ ],
+ ""185.25.48.184:8333"": [
+ 70015,
+ ""/Satoshi:0.19.0.1/"",
+ 1590754138,
+ 1037,
+ 644147,
+ ""4832-10449.bacloud.info"",
+ null,
+ ""LT"",
+ 56,
+ 24,
+ ""Europe/Vilnius"",
+ ""AS61272"",
+ ""Informacines sistemos ir technologijos, UAB""
+ ]
+ }
+ }";
+
+ api.ProcessResponse(response, null);
+
+ Assert.Equal($"nesxfmano25clfvn.onion:8333{Environment.NewLine}tg4uwrjmtr2jlbjy.onion:8333{Environment.NewLine}", stringWriter.ToString());
+ }
+ }
+}
\ No newline at end of file

Use "async" + add unit test.