Created
August 21, 2019 10:35
-
-
Save knocte/64be73089a73f36a6734f37d862b4a09 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From bbcbfd03efe82fcd6b1c834ce2c1f1699a3a8cf1 Mon Sep 17 00:00:00 2001 | |
From: "Andres G. Aragoneses" <[email protected]> | |
Date: Wed, 21 Aug 2019 17:02:03 +0800 | |
Subject: [PATCH] Backend(,Tests),Frontend: merge server cache with server | |
reference JSON | |
As a follow-up to previous recent commit [1], this way we only have one | |
format, moving forward, where we store info about servers (their domain | |
name, port, etc), and the connection statistics for them. | |
The makefile target 'update-servers' now not only updates the JSON file | |
from external sources (electrum's github, bitcoin-eye website) but also | |
tries to connect to every single one of them with the FaultTPClient, | |
with a new mode called "Exhaustive", to gather updated stats about them. | |
[1] d948d53ab57bd5e6ad16d6204592ffec18eb5c0d | |
--- | |
scripts/make.fsx | 8 +- | |
.../AsyncCancellation.fs | 63 +- | |
.../ElectrumIntegrationTests.fs | 24 +- | |
src/GWallet.Backend.Tests/FaultTolerance.fs | 490 ++++-- | |
.../ParallelizationAndOptimization.fs | 138 +- | |
src/GWallet.Backend.Tests/ServerReference.fs | 348 ++-- | |
src/GWallet.Backend/Account.fs | 8 +- | |
src/GWallet.Backend/Caching.fs | 129 +- | |
src/GWallet.Backend/CachingTypes.fs | 20 + | |
src/GWallet.Backend/Ether/EtherAccount.fs | 10 +- | |
src/GWallet.Backend/Ether/EtherServer.fs | 110 +- | |
.../FaultTolerantParallelClient.fs | 175 +- | |
src/GWallet.Backend/GWallet.Backend.fsproj | 1 + | |
src/GWallet.Backend/Server.fs | 117 +- | |
src/GWallet.Backend/ServerManager.fs | 91 +- | |
.../UtxoCoin/ElectrumClient.fs | 4 +- | |
.../UtxoCoin/ElectrumServer.fs | 4 +- | |
.../UtxoCoin/UtxoCoinAccount.fs | 106 +- | |
src/GWallet.Backend/servers.json | 1542 +++++++++-------- | |
src/GWallet.Frontend.Console/Program.fs | 14 +- | |
.../UserInteraction.fs | 4 +- | |
21 files changed, 2107 insertions(+), 1299 deletions(-) | |
create mode 100644 src/GWallet.Backend/CachingTypes.fs | |
diff --git a/scripts/make.fsx b/scripts/make.fsx | |
index c407b2e..3696071 100755 | |
--- a/scripts/make.fsx | |
+++ b/scripts/make.fsx | |
@@ -264,8 +264,12 @@ match maybeTarget with | |
| Some "update-servers" -> | |
let buildConfig = MakeAll() | |
Directory.SetCurrentDirectory (GetPathToBackend()) | |
- let proc = RunFrontend buildConfig (Some "--update-servers-file") | |
- Environment.Exit proc.ExitCode | |
+ let proc1 = RunFrontend buildConfig (Some "--update-servers-file") | |
+ if proc1.ExitCode <> 0 then | |
+ Environment.Exit proc1.ExitCode | |
+ else | |
+ let proc2 = RunFrontend buildConfig (Some "--update-servers-stats") | |
+ Environment.Exit proc2.ExitCode | |
| Some(someOtherTarget) -> | |
Console.Error.WriteLine("Unrecognized target: " + someOtherTarget) | |
diff --git a/src/GWallet.Backend.Tests/AsyncCancellation.fs b/src/GWallet.Backend.Tests/AsyncCancellation.fs | |
index fa16910..4485f48 100644 | |
--- a/src/GWallet.Backend.Tests/AsyncCancellation.fs | |
+++ b/src/GWallet.Backend.Tests/AsyncCancellation.fs | |
@@ -17,13 +17,16 @@ type AsyncCancellation() = | |
{ | |
Details = | |
{ | |
- NetworkPath = serverId | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = serverId | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = job | |
} | |
- let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ -> ()) | |
+ let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ _ -> ()) | |
[<Test>] | |
member __.``slower funcs get cancelled after consistent results have been gathered``() = | |
@@ -46,9 +49,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 2u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -80,9 +86,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 1u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -128,9 +137,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 1u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -169,9 +181,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 1u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -212,9 +227,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 1u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -278,9 +296,12 @@ type AsyncCancellation() = | |
let number_of_parallel_jobs_allowed = uint32 allFuncs.Length | |
let NUMBER_OF_CONSISTENT_RESULTS = 2u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = number_of_parallel_jobs_allowed | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
diff --git a/src/GWallet.Backend.Tests/ElectrumIntegrationTests.fs b/src/GWallet.Backend.Tests/ElectrumIntegrationTests.fs | |
index 6ed9fd7..4a18c0d 100644 | |
--- a/src/GWallet.Backend.Tests/ElectrumIntegrationTests.fs | |
+++ b/src/GWallet.Backend.Tests/ElectrumIntegrationTests.fs | |
@@ -15,24 +15,24 @@ type ElectrumServerUnitTests() = | |
[<Test>] | |
member __.``filters electrum BTC servers``() = | |
for electrumServer in ElectrumServerSeedList.DefaultBtcList do | |
- Assert.That (electrumServer.ConnectionType.Encrypted, Is.EqualTo false, | |
+ Assert.That (electrumServer.ServerInfo.ConnectionType.Encrypted, Is.EqualTo false, | |
sprintf "BTC servers list should be filtered against only-TLS compatible servers, but %s was found" | |
- electrumServer.NetworkPath) | |
+ electrumServer.ServerInfo.NetworkPath) | |
- Assert.That (electrumServer.NetworkPath, Is.Not.StringEnding ".onion", | |
+ Assert.That (electrumServer.ServerInfo.NetworkPath, Is.Not.StringEnding ".onion", | |
sprintf "BTC servers list should be filtered against onion servers, but %s was found" | |
- electrumServer.NetworkPath) | |
+ electrumServer.ServerInfo.NetworkPath) | |
[<Test>] | |
member __.``filters electrum LTC servers``() = | |
for electrumServer in ElectrumServerSeedList.DefaultLtcList do | |
- Assert.That (electrumServer.ConnectionType.Encrypted, Is.EqualTo false, | |
+ Assert.That (electrumServer.ServerInfo.ConnectionType.Encrypted, Is.EqualTo false, | |
sprintf "BTC servers list should be filtered against only-TLS compatible servers, but %s was found" | |
- electrumServer.NetworkPath) | |
+ electrumServer.ServerInfo.NetworkPath) | |
- Assert.That (electrumServer.NetworkPath, Is.Not.StringEnding ".onion", | |
+ Assert.That (electrumServer.ServerInfo.NetworkPath, Is.Not.StringEnding ".onion", | |
sprintf "BTC servers list should be filtered against onion servers, but %s was found" | |
- electrumServer.NetworkPath) | |
+ electrumServer.ServerInfo.NetworkPath) | |
[<TestFixture>] | |
[<Ignore ("Seems we have general issues reaching electrum servers these days, probably related to DDOS attack on them")>] | |
@@ -67,7 +67,7 @@ type ElectrumIntegrationTests() = | |
assertion result | |
- Console.WriteLine (sprintf "%A server %s is reachable" currency server.NetworkPath) | |
+ Console.WriteLine (sprintf "%A server %s is reachable" currency server.ServerInfo.NetworkPath) | |
Some electrumServer | |
with | |
| :? CommunicationUnsuccessfulException as ex -> | |
@@ -78,7 +78,7 @@ type ElectrumIntegrationTests() = | |
Console.Error.WriteLine (sprintf "%s -> %A server %s is unreachable" exDescription | |
currency | |
- server.NetworkPath) | |
+ server.ServerInfo.NetworkPath) | |
None | |
match maybeFilter with | |
@@ -149,12 +149,12 @@ type ElectrumIntegrationTests() = | |
let btcNonRebelServers = | |
List.filter | |
- (fun server -> rebelBtcServerHostnames.All(fun rebel -> server.NetworkPath <> rebel)) | |
+ (fun server -> rebelBtcServerHostnames.All(fun rebel -> server.ServerInfo.NetworkPath <> rebel)) | |
ElectrumServerSeedList.DefaultBtcList | |
let btcRebelServers = | |
List.filter | |
- (fun server -> rebelBtcServerHostnames.Any(fun rebel -> server.NetworkPath = rebel)) | |
+ (fun server -> rebelBtcServerHostnames.Any(fun rebel -> server.ServerInfo.NetworkPath = rebel)) | |
ElectrumServerSeedList.DefaultBtcList | |
let UtxosAssertion (utxos: array<BlockchainScripthashListUnspentInnerResult>) = | |
diff --git a/src/GWallet.Backend.Tests/FaultTolerance.fs b/src/GWallet.Backend.Tests/FaultTolerance.fs | |
index 69cb869..60aaf3c 100644 | |
--- a/src/GWallet.Backend.Tests/FaultTolerance.fs | |
+++ b/src/GWallet.Backend.Tests/FaultTolerance.fs | |
@@ -20,26 +20,35 @@ type FaultTolerance() = | |
let one_consistent_result_because_this_test_doesnt_test_consistency = 1u | |
let not_more_than_one_parallel_job_because_this_test_doesnt_test_parallelization = 1u | |
let test_does_not_involve_retries = 0u | |
- let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ -> ()) | |
+ let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ _ -> ()) | |
// yes, the default one is the fast one because it's the one with no filters, just sorting | |
- let default_mode_as_it_is_irrelevant_for_this_test = Mode.Fast | |
+ let default_result_selection_mode_as_it_is_irrelevant_for_this_test maybeConsistencyConfig = | |
+ let consistencyConfig = | |
+ match maybeConsistencyConfig with | |
+ | None -> SpecificNumberOfConsistentResponsesRequired | |
+ one_consistent_result_because_this_test_doesnt_test_consistency | |
+ | Some specificConsistencyConfig -> specificConsistencyConfig | |
+ Selective | |
+ { | |
+ ServerSelectionMode = ServerSelectionMode.Fast | |
+ ReportUncancelledJobs = false | |
+ ConsistencyConfig = consistencyConfig | |
+ } | |
+ | |
let some_fault_with_no_last_successful_comm_because_irrelevant_for_this_test = | |
Fault ({ TypeFullName = typeof<Exception>.FullName; Message = "some err" },None) | |
let some_successful_irrelevant_date: Status = LastSuccessfulCommunication DateTime.UtcNow | |
- let defaultSettingsForNoConsistencyNoParallelismAndNoRetries() = | |
+ let dummy_date_for_cache = DateTime.Now | |
+ | |
+ let defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig = | |
{ | |
NumberOfParallelJobsAllowed = not_more_than_one_parallel_job_because_this_test_doesnt_test_parallelization | |
- ConsistencyConfig = | |
- SpecificNumberOfConsistentResponsesRequired one_consistent_result_because_this_test_doesnt_test_consistency | |
NumberOfRetries = test_does_not_involve_retries | |
NumberOfRetriesForInconsistency = test_does_not_involve_retries | |
- Mode = default_mode_as_it_is_irrelevant_for_this_test | |
- | |
- // this setting below is not being tested | |
- ShouldReportUncancelledJobs = false | |
+ ResultSelectionMode = default_result_selection_mode_as_it_is_irrelevant_for_this_test consistencyConfig | |
} | |
let defaultFaultTolerantParallelClient = | |
@@ -51,8 +60,11 @@ type FaultTolerance() = | |
{ | |
Details = | |
{ | |
- NetworkPath = serverId | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = serverId | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = job | |
@@ -66,7 +78,7 @@ type FaultTolerance() = | |
let func = serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob" aJob | |
let dataRetreived = | |
defaultFaultTolerantParallelClient.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
Assert.That(dataRetreived, Is.EqualTo(someResult)) | |
@@ -81,7 +93,7 @@ type FaultTolerance() = | |
let func1,func2 = serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob1" aJob1, | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob2" aJob2 | |
let dataRetreived = defaultFaultTolerantParallelClient.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ func1; func2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -92,7 +104,7 @@ type FaultTolerance() = | |
let client = defaultFaultTolerantParallelClient | |
Assert.Throws<ArgumentException>( | |
fun _ -> client.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
List.Empty | |
|> Async.RunSynchronously |> ignore | |
) |> ignore | |
@@ -108,7 +120,7 @@ type FaultTolerance() = | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob2" aJob2 | |
let dataRetreived = | |
defaultFaultTolerantParallelClient.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func1; func2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func1; func2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
Assert.That(dataRetreived, Is.EqualTo(someResult)) | |
@@ -129,7 +141,7 @@ type FaultTolerance() = | |
(FaultTolerantParallelClient<ServerDetails,SomeException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test) | |
.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func1; func2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func1; func2 ] | |
|> Async.RunSynchronously | |
Some(result) | |
with | |
@@ -156,7 +168,7 @@ type FaultTolerance() = | |
(FaultTolerantParallelClient<ServerDetails, SomeException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test) | |
.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func1; func2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func1; func2 ] | |
|> Async.RunSynchronously | |
Assert.That(result, Is.EqualTo(someResult)) | |
@@ -174,7 +186,7 @@ type FaultTolerance() = | |
(FaultTolerantParallelClient<ServerDetails, SomeException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test) | |
.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func1; func2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func1; func2 ] | |
|> Async.RunSynchronously | |
|> ignore ) | |
@@ -195,7 +207,7 @@ type FaultTolerance() = | |
(FaultTolerantParallelClient<ServerDetails, SomeInnerException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test) | |
.Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func1; func2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func1; func2 ] | |
|> Async.RunSynchronously | |
Assert.That(result, Is.EqualTo(someResult)) | |
@@ -225,9 +237,9 @@ type FaultTolerance() = | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aConsistentJobA" aConsistentJobA, | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aConsistentJobB" aConsistentJobB | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = | |
- SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe | |
+ |> Some | |
+ let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg | |
let consistencyGuardClient = | |
FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -258,8 +270,8 @@ type FaultTolerance() = | |
[<Test>] | |
member __.``consistency precondition > 0``() = | |
- let invalidSettings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired 0u; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired 0u |> Some | |
+ let invalidSettings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg | |
let dummyArg = () | |
let dummyServers = | |
[ serverWithNoHistoryInfoBecauseIrrelevantToThisTest "dummyServerName" (async { return () }) ] | |
@@ -273,9 +285,9 @@ type FaultTolerance() = | |
[<Test>] | |
member __.``consistency precondition > funcs``() = | |
let numberOfConsistentResponsesToBeConsideredSafe = 3u | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = | |
- SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe; } | |
+ let consistencyConfig = | |
+ SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe |> Some | |
+ let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
let someResult = 1 | |
@@ -300,8 +312,9 @@ type FaultTolerance() = | |
[<Test>] | |
member __.``if consistency is not found, throws inconsistency exception``() = | |
let numberOfConsistentResponsesToBeConsideredSafe = 3u | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe; } | |
+ let consistencyConfig = | |
+ SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe |> Some | |
+ let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
let mostConsistentResult = 1 | |
let someOtherResultA = 2 | |
@@ -337,9 +350,9 @@ type FaultTolerance() = | |
let someBalance = 1.0m | |
let someBalanceMatchFunc someBalanceRetreived = | |
someBalanceRetreived = someBalance | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = | |
- OneServerConsistentWithCacheOrTwoServers someBalanceMatchFunc } | |
+ | |
+ let consistencyConfig = OneServerConsistentWithCacheOrTwoServers someBalanceMatchFunc |> Some | |
+ let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
let otherBalance = 2.0m | |
let yetAnotherBalance = 3.0m | |
@@ -375,9 +388,9 @@ type FaultTolerance() = | |
let someBalance = 1.0m | |
let someBalanceMatchFunc someBalanceRetreived = | |
someBalanceRetreived = someBalance | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = | |
- OneServerConsistentWithCacheOrTwoServers someBalanceMatchFunc } | |
+ | |
+ let consistencyConfig = OneServerConsistentWithCacheOrTwoServers someBalanceMatchFunc |> Some | |
+ let settings = defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
let newBalance = 2.0m | |
let wrongBalance = 3.0m | |
@@ -433,7 +446,7 @@ type FaultTolerance() = | |
let func1,func2 = serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob1" aJob1, | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob2" aJob2 | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
+ let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries None with | |
NumberOfRetries = 1u | |
NumberOfRetriesForInconsistency = 0u } | |
@@ -478,11 +491,14 @@ type FaultTolerance() = | |
let aJob4 = | |
async { return mostConsistentResult } | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- ConsistencyConfig = | |
- SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe; | |
- NumberOfRetries = 0u; | |
- NumberOfRetriesForInconsistency = 1u } | |
+ let consistencyCfg = | |
+ SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesToBeConsideredSafe |> Some | |
+ let settings = | |
+ { | |
+ defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfRetries = 0u | |
+ NumberOfRetriesForInconsistency = 1u | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -521,13 +537,15 @@ type FaultTolerance() = | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "job2" job2 | |
serverWithNoHistoryInfoBecauseIrrelevantToThisTest "job3" job3 ] | |
- let settings = { defaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = uint32 funcs.Length | |
- ConsistencyConfig = | |
- AverageBetweenResponses (uint32 funcs.Length, | |
- (fun (list:List<int>) -> | |
- list.Sum() / list.Length | |
- )); } | |
+ let consistencyCfg = AverageBetweenResponses (uint32 funcs.Length, | |
+ (fun (list:List<int>) -> | |
+ list.Sum() / list.Length | |
+ )) |> Some | |
+ let settings = | |
+ { | |
+ defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = uint32 funcs.Length | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -547,25 +565,34 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = | |
+ Some ({ Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails,DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -574,7 +601,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -588,24 +615,32 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault; TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = fault; TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -614,7 +649,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -627,25 +662,32 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -654,7 +696,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -668,24 +710,31 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -694,7 +743,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -709,17 +758,24 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some ({ Status = fault; TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = async { return someResult2 } | |
@@ -727,17 +783,35 @@ type FaultTolerance() = | |
let server3 = { | |
Details = | |
{ | |
- NetworkPath = "server3" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server3" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult3 } | |
} | |
+ | |
+ let defaultSettings = FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None | |
+ let settings = | |
+ match defaultSettings.ResultSelectionMode with | |
+ | Selective selSettings -> | |
+ { | |
+ defaultSettings with | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ selSettings with | |
+ ServerSelectionMode = ServerSelectionMode.Analysis | |
+ } | |
+ } | |
+ | _ -> failwith "default settings should be selective! :-?" | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server1; server2; server3 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -746,8 +820,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server3; server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -762,30 +835,42 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeSpecificException } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeSpecificException } | |
} | |
let server3 = { | |
Details = | |
{ | |
- NetworkPath = "server3" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 3.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server3" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 3.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult3 } | |
} | |
@@ -793,17 +878,36 @@ type FaultTolerance() = | |
let server4 = { | |
Details = | |
{ | |
- NetworkPath = "server4" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = fault | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server4" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = fault | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult4 } | |
} | |
+ | |
+ | |
+ let defaultSettings = FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None | |
+ let settings = | |
+ match defaultSettings.ResultSelectionMode with | |
+ | Selective selSettings -> | |
+ { | |
+ defaultSettings with | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ selSettings with | |
+ ServerSelectionMode = ServerSelectionMode.Analysis | |
+ } | |
+ } | |
+ | _ -> failwith "default settings should be selective! :-?" | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server1; server2; server3; server4 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -812,8 +916,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server4; server3; server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -829,30 +932,42 @@ type FaultTolerance() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeSpecificException } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeSpecificException } | |
} | |
let server3 = { | |
Details = | |
{ | |
- NetworkPath = "server3" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 3.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server3" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 3.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeSpecificException } | |
} | |
@@ -860,27 +975,50 @@ type FaultTolerance() = | |
let server4 = { | |
Details = | |
{ | |
- NetworkPath = "server4" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 4.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server4" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 4.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult4 } | |
} | |
let server5 = { | |
Details = | |
{ | |
- NetworkPath = "server5" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 5.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server5" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 5.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult5 } | |
} | |
+ | |
+ let defaultSettings = FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None | |
+ let settings = | |
+ match defaultSettings.ResultSelectionMode with | |
+ | Selective selSettings -> | |
+ { | |
+ defaultSettings with | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ selSettings with | |
+ ServerSelectionMode = ServerSelectionMode.Analysis | |
+ } | |
+ } | |
+ | _ -> failwith "default settings should be selective! :-?" | |
+ | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server1; server2; server3; server4; server5 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -889,8 +1027,7 @@ type FaultTolerance() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server5; server4; server3; server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -906,20 +1043,20 @@ type FaultTolerance() = | |
let mutable someFlag = false | |
let mutable someTimeStamp = None | |
- let saveServerLastStat (serverDetails: ServerDetails, historyInfo): unit = | |
- Assert.That(serverDetails.NetworkPath, Is.EqualTo serverId) | |
- match historyInfo.Status with | |
- | Fault _ -> | |
+ let saveServerLastStat (isServer: ServerDetails->bool) (historyFact: HistoryFact): unit = | |
+ Assert.That(isServer func.Details, Is.EqualTo true) | |
+ match historyFact.Fault with | |
+ | Some _ -> | |
failwith "assertion failed" | |
| _ -> | |
() | |
- Assert.That(historyInfo.TimeSpan, Is.GreaterThan TimeSpan.Zero) | |
+ Assert.That(historyFact.TimeSpan, Is.GreaterThan TimeSpan.Zero) | |
someFlag <- true | |
let dataRetreived = | |
(FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
saveServerLastStat).Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ func ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) [ func ] | |
|> Async.RunSynchronously | |
Assert.That(someFlag, Is.EqualTo true) | |
@@ -940,31 +1077,74 @@ type FaultTolerance() = | |
let mutable someTotalCounter = 0 | |
let mutable someTimeStamp = None | |
let lockObj = Object() | |
- let saveServerLastStat (serverDetails: ServerDetails, historyInfo): unit = | |
+ let saveServerLastStat (isServer: ServerDetails->bool) (historyFact: HistoryFact): unit = | |
lock lockObj (fun _ -> | |
- match historyInfo.Status with | |
- | Fault (fault,_) -> | |
- Assert.That(serverDetails.NetworkPath, Is.EqualTo failingServerName) | |
- Assert.That(fault.TypeFullName, Is.EqualTo typeof<SomeSpecificException>.FullName) | |
+ match historyFact.Fault with | |
+ | Some ex -> | |
+ Assert.That(isServer server1.Details, Is.EqualTo true) | |
+ Assert.That(isServer server2.Details, Is.EqualTo false) | |
+ Assert.That(ex.TypeFullName, Is.EqualTo typeof<SomeSpecificException>.FullName) | |
| _ -> | |
- Assert.That(serverDetails.NetworkPath, Is.Not.EqualTo failingServerName) | |
+ Assert.That(isServer server1.Details, Is.EqualTo false) | |
+ Assert.That(isServer server2.Details, Is.EqualTo true) | |
someNonFailingCounter <- someNonFailingCounter + 1 | |
- Assert.That(historyInfo.TimeSpan, Is.GreaterThan TimeSpan.Zero) | |
+ Assert.That(historyFact.TimeSpan, Is.GreaterThan TimeSpan.Zero) | |
someTotalCounter <- someTotalCounter + 1 | |
) | |
let dataRetreived = | |
(FaultTolerantParallelClient<ServerDetails, SomeSpecificException> | |
saveServerLastStat).Query | |
- (defaultSettingsForNoConsistencyNoParallelismAndNoRetries()) [ server1; server2 ] | |
+ (defaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
+ [ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(someTotalCounter, Is.EqualTo 2) | |
Assert.That(someNonFailingCounter, Is.EqualTo 1) | |
- member private __.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() = | |
- defaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
+ [<Test>] | |
+ member __.``calls all jobs in exhaustive mode``() = | |
+ let someResult = 1 | |
+ let mutable aJob1Called = false | |
+ let aJob1 = | |
+ async { aJob1Called <- true; return someResult } | |
+ let mutable aJob2Called = false | |
+ let aJob2 = | |
+ async { aJob2Called <- true; return someResult } | |
+ let func1,func2 = serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob1" aJob1, | |
+ serverWithNoHistoryInfoBecauseIrrelevantToThisTest "aJob2" aJob2 | |
+ | |
+ let settings = | |
+ { | |
+ NumberOfParallelJobsAllowed = not_more_than_one_parallel_job_because_this_test_doesnt_test_parallelization | |
+ NumberOfRetries = test_does_not_involve_retries | |
+ NumberOfRetriesForInconsistency = test_does_not_involve_retries | |
+ ResultSelectionMode = ResultSelectionMode.Exhaustive | |
+ } | |
+ let dataRetreived1 = defaultFaultTolerantParallelClient.Query | |
+ settings | |
+ [ func1; func2 ] | |
+ |> Async.RunSynchronously | |
+ Assert.That(dataRetreived1, Is.TypeOf<int>()) | |
+ Assert.That(dataRetreived1, Is.EqualTo someResult) | |
+ Assert.That(aJob1Called, Is.EqualTo true) | |
+ Assert.That(aJob2Called, Is.EqualTo true) | |
+ | |
+ aJob1Called <- false | |
+ aJob2Called <- false | |
+ // different order | |
+ let dataRetreived2 = defaultFaultTolerantParallelClient.Query | |
+ settings | |
+ [ func2; func1 ] | |
+ |> Async.RunSynchronously | |
+ Assert.That(dataRetreived2, Is.TypeOf<int>()) | |
+ Assert.That(dataRetreived2, Is.EqualTo someResult) | |
+ Assert.That(aJob1Called, Is.EqualTo true) | |
+ Assert.That(aJob2Called, Is.EqualTo true) | |
+ | |
+ member private __.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig = | |
+ defaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
- static member DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() = | |
- FaultTolerance().DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
+ static member DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig = | |
+ FaultTolerance().DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig | |
diff --git a/src/GWallet.Backend.Tests/ParallelizationAndOptimization.fs b/src/GWallet.Backend.Tests/ParallelizationAndOptimization.fs | |
index b14e1c5..b698a19 100644 | |
--- a/src/GWallet.Backend.Tests/ParallelizationAndOptimization.fs | |
+++ b/src/GWallet.Backend.Tests/ParallelizationAndOptimization.fs | |
@@ -17,13 +17,23 @@ type ParallelizationAndOptimization() = | |
let dummy_connection_type = { Encrypted = false; Protocol = Http } | |
let serverWithNoHistoryInfoBecauseIrrelevantToThisTest serverId job = | |
{ | |
- Details = { NetworkPath = serverId; ConnectionType = dummy_connection_type; CommunicationHistory = None } | |
+ Details = | |
+ { | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = serverId | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = None | |
+ } | |
Retrieval = job | |
} | |
- let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ -> ()) | |
+ let dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test = (fun _ _ -> ()) | |
+ | |
+ let dummy_date_for_cache = DateTime.Now | |
// yes, the default one is the fast one because it's the one with no filters, just sorting | |
- let default_mode_as_it_is_irrelevant_for_this_test = Mode.Fast | |
+ let default_mode_as_it_is_irrelevant_for_this_test = ServerSelectionMode.Fast | |
let some_fault_with_no_last_successful_comm_because_irrelevant_for_this_test = | |
Fault ({ TypeFullName = typeof<Exception>.FullName; Message = "some err" },None) | |
@@ -35,9 +45,12 @@ type ParallelizationAndOptimization() = | |
// because this test doesn't deal with inconsistencies | |
let NUMBER_OF_CONSISTENT_RESULTS = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyCfg = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyCfg with | |
+ NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED | |
+ } | |
let mutable job1Done = false | |
let aJob1 = async { | |
@@ -79,9 +92,12 @@ type ParallelizationAndOptimization() = | |
// because this test doesn't deal with inconsistencies | |
let NUMBER_OF_CONSISTENT_RESULTS = 1u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
+ let consistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig with | |
+ NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED | |
+ } | |
let aJob1: Async<int> = async { | |
return raise SomeExceptionDuringParallelWork | |
@@ -116,10 +132,12 @@ type ParallelizationAndOptimization() = | |
let NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED = 2u | |
let NUMBER_OF_CONSISTENT_RESULTS = 2u | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED; | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS; } | |
- | |
+ let consistencyConfig = SpecificNumberOfConsistentResponsesRequired NUMBER_OF_CONSISTENT_RESULTS |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig with | |
+ NumberOfParallelJobsAllowed = NUMBER_OF_PARALLEL_JOBS_TO_BE_TESTED | |
+ } | |
let aJob1 = | |
async { return 0 } | |
let aJob2 = async { | |
@@ -183,13 +201,12 @@ type ParallelizationAndOptimization() = | |
[<Test>] | |
member __.``using an average func encourages you (via throwing an exception) to use parallelism``() = | |
- let settings = { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() with | |
- NumberOfParallelJobsAllowed = 1u | |
- ConsistencyConfig = | |
- AverageBetweenResponses (2u, | |
- (fun _ -> | |
- failwith "unreachable" | |
- )); } | |
+ let consistencyConfig = AverageBetweenResponses (2u, (fun _ -> failwith "unreachable")) |> Some | |
+ let settings = | |
+ { | |
+ FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries consistencyConfig with | |
+ NumberOfParallelJobsAllowed = 1u | |
+ } | |
let client = FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test | |
@@ -209,26 +226,34 @@ type ParallelizationAndOptimization() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult1 } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server1; server2 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -237,7 +262,7 @@ type ParallelizationAndOptimization() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, DummyIrrelevantToThisTestException> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries()) | |
+ (FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None) | |
[ server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -271,36 +296,62 @@ type ParallelizationAndOptimization() = | |
let server1 = { | |
Details = | |
{ | |
- NetworkPath = "server1" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 1.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server1" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 1.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return raise SomeExceptionDuringParallelWork } | |
} | |
let server2 = { | |
Details = | |
{ | |
- NetworkPath = "server2" | |
- ConnectionType = dummy_connection_type | |
- CommunicationHistory = Some { Status = some_successful_irrelevant_date | |
- TimeSpan = TimeSpan.FromSeconds 2.0 } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server2" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
+ CommunicationHistory = Some({ Status = some_successful_irrelevant_date | |
+ TimeSpan = TimeSpan.FromSeconds 2.0 }, | |
+ dummy_date_for_cache) | |
} | |
Retrieval = async { return someResult2 } | |
} | |
let server3 = { | |
Details = | |
{ | |
- NetworkPath = "server3" | |
- ConnectionType = dummy_connection_type | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "server3" | |
+ ConnectionType = dummy_connection_type | |
+ } | |
CommunicationHistory = None | |
} | |
Retrieval = async { return someResult3 } | |
} | |
+ | |
+ let defaultSettings = FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries None | |
+ let settings = | |
+ match defaultSettings.ResultSelectionMode with | |
+ | Selective selSettings -> | |
+ { | |
+ defaultSettings with | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ selSettings with | |
+ ServerSelectionMode = ServerSelectionMode.Analysis | |
+ } | |
+ } | |
+ | _ -> failwith "default settings should be selective! :-?" | |
+ | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server1; server2; server3 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
@@ -309,8 +360,7 @@ type ParallelizationAndOptimization() = | |
// same but different order | |
let dataRetreived = (FaultTolerantParallelClient<ServerDetails, SomeExceptionDuringParallelWork> | |
dummy_func_to_not_save_server_because_it_is_irrelevant_for_this_test).Query | |
- { FaultTolerance.DefaultSettingsForNoConsistencyNoParallelismAndNoRetries() | |
- with Mode = Mode.Analysis } | |
+ settings | |
[ server3; server2; server1 ] | |
|> Async.RunSynchronously | |
Assert.That(dataRetreived, Is.TypeOf<int>()) | |
diff --git a/src/GWallet.Backend.Tests/ServerReference.fs b/src/GWallet.Backend.Tests/ServerReference.fs | |
index 989d11e..3fddee3 100644 | |
--- a/src/GWallet.Backend.Tests/ServerReference.fs | |
+++ b/src/GWallet.Backend.Tests/ServerReference.fs | |
@@ -15,34 +15,40 @@ type ServerReference() = | |
let some_connection_type_irrelevant_for_this_test = { Encrypted = false; Protocol = Http } | |
let CreateHistoryInfo(lastSuccessfulCommunication: DateTime) = | |
- { | |
+ ({ | |
Status = LastSuccessfulCommunication lastSuccessfulCommunication | |
//irrelevant for this test | |
TimeSpan = TimeSpan.Zero | |
- } |> Some | |
+ },dummy_now) |> Some | |
[<Test>] | |
member __.``order of servers is kept if non-hostname details are same``() = | |
let serverWithHighestPriority = | |
{ | |
- NetworkPath = "dlm8yerwlcifs" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "dlm8yerwlcifs" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = None | |
} | |
let serverWithLowestPriority = | |
{ | |
- NetworkPath = "eliuh4midkndk" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
- CommunicationHistory = None | |
- } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "eliuh4midkndk" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = None | |
+ } | |
let servers1 = Map.empty.Add | |
(dummy_currency_because_irrelevant_for_this_test, | |
seq { yield serverWithHighestPriority; yield serverWithLowestPriority }) | |
let serverDetails = ServerRegistry.Serialize servers1 | |
- let serverAPos = serverDetails.IndexOf serverWithHighestPriority.NetworkPath | |
- let serverBPos = serverDetails.IndexOf serverWithLowestPriority.NetworkPath | |
+ let serverAPos = serverDetails.IndexOf serverWithHighestPriority.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetails.IndexOf serverWithLowestPriority.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -55,8 +61,8 @@ type ServerReference() = | |
seq { yield serverWithLowestPriority; yield serverWithHighestPriority }) | |
let serverDetailsReverse = ServerRegistry.Serialize servers2 | |
- let serverAPos = serverDetailsReverse.IndexOf serverWithHighestPriority.NetworkPath | |
- let serverBPos = serverDetailsReverse.IndexOf serverWithLowestPriority.NetworkPath | |
+ let serverAPos = serverDetailsReverse.IndexOf serverWithHighestPriority.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetailsReverse.IndexOf serverWithLowestPriority.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -68,23 +74,29 @@ type ServerReference() = | |
member __.``order of servers depends on last successful conn``() = | |
let serverWithOldestConnection = | |
{ | |
- NetworkPath = "dlm8yerwlcifs" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "dlm8yerwlcifs" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo (DateTime.Now - TimeSpan.FromDays 10.0) | |
} | |
let serverWithMostRecentConnection = | |
{ | |
- NetworkPath = "eliuh4midkndk" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
- CommunicationHistory = CreateHistoryInfo DateTime.Now | |
- } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "eliuh4midkndk" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = CreateHistoryInfo DateTime.Now | |
+ } | |
let servers1 = Map.empty.Add | |
(dummy_currency_because_irrelevant_for_this_test, | |
seq { yield serverWithOldestConnection; yield serverWithMostRecentConnection }) | |
let serverDetails = ServerRegistry.Serialize servers1 | |
- let serverAPos = serverDetails.IndexOf serverWithOldestConnection.NetworkPath | |
- let serverBPos = serverDetails.IndexOf serverWithMostRecentConnection.NetworkPath | |
+ let serverAPos = serverDetails.IndexOf serverWithOldestConnection.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetails.IndexOf serverWithMostRecentConnection.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -97,8 +109,8 @@ type ServerReference() = | |
seq { yield serverWithMostRecentConnection; yield serverWithOldestConnection }) | |
let serverDetailsReverse = ServerRegistry.Serialize servers2 | |
- let serverAPos = serverDetailsReverse.IndexOf serverWithOldestConnection.NetworkPath | |
- let serverBPos = serverDetailsReverse.IndexOf serverWithMostRecentConnection.NetworkPath | |
+ let serverAPos = serverDetailsReverse.IndexOf serverWithOldestConnection.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetailsReverse.IndexOf serverWithMostRecentConnection.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -109,8 +121,11 @@ type ServerReference() = | |
let serverWithNoLastConnection = | |
{ | |
- NetworkPath = "dlm8yerwlcifs" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "dlm8yerwlcifs" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = None | |
} | |
@@ -119,8 +134,8 @@ type ServerReference() = | |
seq { yield serverWithNoLastConnection; yield serverWithMostRecentConnection }) | |
let serverDetails3 = ServerRegistry.Serialize servers3 | |
- let serverAPos = serverDetails.IndexOf serverWithNoLastConnection.NetworkPath | |
- let serverBPos = serverDetails.IndexOf serverWithMostRecentConnection.NetworkPath | |
+ let serverAPos = serverDetails.IndexOf serverWithNoLastConnection.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetails.IndexOf serverWithMostRecentConnection.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -133,8 +148,8 @@ type ServerReference() = | |
seq { yield serverWithMostRecentConnection; yield serverWithNoLastConnection }) | |
let serverDetails3Rev = ServerRegistry.Serialize servers4 | |
- let serverAPos = serverDetails3Rev.IndexOf serverWithNoLastConnection.NetworkPath | |
- let serverBPos = serverDetails3Rev.IndexOf serverWithMostRecentConnection.NetworkPath | |
+ let serverAPos = serverDetails3Rev.IndexOf serverWithNoLastConnection.ServerInfo.NetworkPath | |
+ let serverBPos = serverDetails3Rev.IndexOf serverWithMostRecentConnection.ServerInfo.NetworkPath | |
Assert.That(serverAPos, Is.Not.LessThan 0) | |
@@ -147,10 +162,13 @@ type ServerReference() = | |
let now = DateTime.UtcNow | |
let serverWithSomeRecentConnection = | |
{ | |
- NetworkPath = "eliuh4midkndk" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
- CommunicationHistory = CreateHistoryInfo now | |
- } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "eliuh4midkndk" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = CreateHistoryInfo now | |
+ } | |
let servers = Map.empty.Add | |
(dummy_currency_because_irrelevant_for_this_test, | |
seq { yield serverWithSomeRecentConnection }) | |
@@ -176,10 +194,13 @@ type ServerReference() = | |
let now = DateTime.UtcNow | |
let serverWithSomeRecentConnection = | |
{ | |
- NetworkPath = "eliuh4midkndk" | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
- CommunicationHistory = CreateHistoryInfo DateTime.UtcNow | |
- } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "eliuh4midkndk" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = CreateHistoryInfo DateTime.UtcNow | |
+ } | |
let servers = Map.empty.Add | |
(dummy_currency_because_irrelevant_for_this_test, | |
seq { yield serverWithSomeRecentConnection }) | |
@@ -193,9 +214,12 @@ type ServerReference() = | |
let port = 50001u | |
let serverWithSomeRecentConnection = | |
{ | |
- NetworkPath = "eliuh4midkndk" | |
- ConnectionType = { Encrypted = false; Protocol = Tcp port } | |
- CommunicationHistory = None | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "eliuh4midkndk" | |
+ ConnectionType = { Encrypted = false; Protocol = Tcp port } | |
+ } | |
+ CommunicationHistory = None | |
} | |
let servers = Map.empty.Add | |
(dummy_currency_because_irrelevant_for_this_test, | |
@@ -210,22 +234,29 @@ type ServerReference() = | |
let tcpServerNetworkPath = "tcp" | |
let tcpServerWithNoHistory = | |
{ | |
- NetworkPath = tcpServerNetworkPath | |
- ConnectionType = { Encrypted = false; Protocol = Tcp 50001u } | |
- CommunicationHistory = None | |
- } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = tcpServerNetworkPath | |
+ ConnectionType = { Encrypted = false; Protocol = Tcp 50001u } | |
+ } | |
+ CommunicationHistory = None | |
+ } | |
let timeSpanForHttpServer = TimeSpan.FromSeconds 1.0 | |
let httpServerNetworkPath = "http" | |
let lastSuccessfulCommunication = DateTime.UtcNow | |
let httpSuccessfulServer = | |
{ | |
- NetworkPath = httpServerNetworkPath | |
- ConnectionType = { Encrypted = false; Protocol = Http } | |
- CommunicationHistory = Some({ | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = httpServerNetworkPath | |
+ ConnectionType = { Encrypted = false; Protocol = Http } | |
+ } | |
+ CommunicationHistory = Some({ | |
Status = LastSuccessfulCommunication lastSuccessfulCommunication | |
TimeSpan = timeSpanForHttpServer | |
- }) | |
+ }, | |
+ dummy_now) | |
} | |
let httpsServerNetworkPath1 = "https1" | |
@@ -233,22 +264,30 @@ type ServerReference() = | |
let exInfo = { TypeFullName = "SomeNamespace.SomeException" ; Message = "argh" } | |
let httpsFailureServer1 = | |
{ | |
- NetworkPath = httpsServerNetworkPath1 | |
- ConnectionType = { Encrypted = true; Protocol = Http } | |
- CommunicationHistory = Some({ | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = httpsServerNetworkPath1 | |
+ ConnectionType = { Encrypted = true; Protocol = Http } | |
+ } | |
+ CommunicationHistory = Some({ | |
Status = Fault (exInfo, None) | |
TimeSpan = timeSpanForHttpsServer | |
- }) | |
+ }, | |
+ dummy_now) | |
} | |
let httpsServerNetworkPath2 = "https2" | |
let httpsFailureServer2 = | |
{ | |
- NetworkPath = httpsServerNetworkPath2 | |
- ConnectionType = { Encrypted = true; Protocol = Http } | |
- CommunicationHistory = Some({ | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = httpsServerNetworkPath2 | |
+ ConnectionType = { Encrypted = true; Protocol = Http } | |
+ } | |
+ CommunicationHistory = Some({ | |
Status = Fault (exInfo, Some lastSuccessfulCommunication) | |
TimeSpan = timeSpanForHttpsServer | |
- }) | |
+ }, | |
+ dummy_now) | |
} | |
let servers = Map.empty.Add | |
@@ -266,25 +305,25 @@ type ServerReference() = | |
|> List.ofSeq | |
Assert.That(deserializedServers.Length, Is.EqualTo 4) | |
- let tcpServers = Seq.filter (fun server -> server.NetworkPath = tcpServerNetworkPath) | |
+ let tcpServers = Seq.filter (fun server -> server.ServerInfo.NetworkPath = tcpServerNetworkPath) | |
deserializedServers | |
|> List.ofSeq | |
Assert.That(tcpServers.Length, Is.EqualTo 1) | |
let tcpServer = tcpServers.[0] | |
- Assert.That(tcpServer.NetworkPath, Is.EqualTo tcpServerNetworkPath) | |
- Assert.That(tcpServer.ConnectionType.Encrypted, Is.EqualTo false) | |
+ Assert.That(tcpServer.ServerInfo.NetworkPath, Is.EqualTo tcpServerNetworkPath) | |
+ Assert.That(tcpServer.ServerInfo.ConnectionType.Encrypted, Is.EqualTo false) | |
Assert.That(tcpServer.CommunicationHistory, Is.EqualTo None) | |
- let httpServers = Seq.filter (fun server -> server.NetworkPath = httpServerNetworkPath) | |
+ let httpServers = Seq.filter (fun server -> server.ServerInfo.NetworkPath = httpServerNetworkPath) | |
deserializedServers | |
|> List.ofSeq | |
Assert.That(httpServers.Length, Is.EqualTo 1) | |
let httpServer = httpServers.[0] | |
- Assert.That(httpServer.NetworkPath, Is.EqualTo httpServerNetworkPath) | |
- Assert.That(httpServer.ConnectionType.Encrypted, Is.EqualTo false) | |
+ Assert.That(httpServer.ServerInfo.NetworkPath, Is.EqualTo httpServerNetworkPath) | |
+ Assert.That(httpServer.ServerInfo.ConnectionType.Encrypted, Is.EqualTo false) | |
match httpServer.CommunicationHistory with | |
| None -> Assert.Fail "http server should have some historyinfo" | |
- | Some historyInfo -> | |
+ | Some (historyInfo,_) -> | |
Assert.That(historyInfo.TimeSpan, Is.EqualTo timeSpanForHttpServer) | |
match historyInfo.Status with | |
| Fault _ -> | |
@@ -292,16 +331,16 @@ type ServerReference() = | |
| LastSuccessfulCommunication lsc -> | |
Assert.That(lsc, Is.EqualTo lastSuccessfulCommunication) | |
- let https1Servers = Seq.filter (fun server -> server.NetworkPath = httpsServerNetworkPath1) | |
+ let https1Servers = Seq.filter (fun server -> server.ServerInfo.NetworkPath = httpsServerNetworkPath1) | |
deserializedServers | |
|> List.ofSeq | |
Assert.That(https1Servers.Length, Is.EqualTo 1) | |
let httpsServer1 = https1Servers.[0] | |
- Assert.That(httpsServer1.NetworkPath, Is.EqualTo httpsServerNetworkPath1) | |
- Assert.That(httpsServer1.ConnectionType.Encrypted, Is.EqualTo true) | |
+ Assert.That(httpsServer1.ServerInfo.NetworkPath, Is.EqualTo httpsServerNetworkPath1) | |
+ Assert.That(httpsServer1.ServerInfo.ConnectionType.Encrypted, Is.EqualTo true) | |
match httpsServer1.CommunicationHistory with | |
| None -> Assert.Fail "https server should have some historyinfo" | |
- | Some historyInfo -> | |
+ | Some (historyInfo,_) -> | |
Assert.That(historyInfo.TimeSpan, Is.EqualTo timeSpanForHttpsServer) | |
match historyInfo.Status with | |
| Fault (fault, maybeLsc) -> | |
@@ -311,16 +350,16 @@ type ServerReference() = | |
| _ -> | |
Assert.Fail "https server should be fault, not successful" | |
- let https2Servers = Seq.filter (fun server -> server.NetworkPath = httpsServerNetworkPath2) | |
+ let https2Servers = Seq.filter (fun server -> server.ServerInfo.NetworkPath = httpsServerNetworkPath2) | |
deserializedServers | |
|> List.ofSeq | |
Assert.That(https2Servers.Length, Is.EqualTo 1) | |
let httpsServer2 = https2Servers.[0] | |
- Assert.That(httpsServer2.NetworkPath, Is.EqualTo httpsServerNetworkPath2) | |
- Assert.That(httpsServer2.ConnectionType.Encrypted, Is.EqualTo true) | |
+ Assert.That(httpsServer2.ServerInfo.NetworkPath, Is.EqualTo httpsServerNetworkPath2) | |
+ Assert.That(httpsServer2.ServerInfo.ConnectionType.Encrypted, Is.EqualTo true) | |
match httpsServer2.CommunicationHistory with | |
| None -> Assert.Fail "https server should have some historyinfo" | |
- | Some historyInfo -> | |
+ | Some (historyInfo,_) -> | |
Assert.That(historyInfo.TimeSpan, Is.EqualTo timeSpanForHttpsServer) | |
match historyInfo.Status with | |
| Fault (fault, maybeLsc) -> | |
@@ -335,14 +374,20 @@ type ServerReference() = | |
let sameRandomHostname = "xfoihror3uo3wmio" | |
let serverA = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = None | |
} | |
let serverB = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo dummy_now | |
} | |
let servers = Map.empty.Add | |
@@ -356,91 +401,154 @@ type ServerReference() = | |
Assert.That(deserializedServers.Length, Is.EqualTo 1) | |
[<Test>] | |
- member __.``when removing duplicate servers, the ones with history and most up to date, stay``() = | |
+ member __.``non-duplicate servers are not removed``() = | |
let sameRandomHostname = "xfoihror3uo3wmio" | |
let serverA = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "A" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = None | |
} | |
let serverB = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
- CommunicationHistory = CreateHistoryInfo dummy_now | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = "B" | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = None | |
} | |
+ | |
let servers = Map.empty.Add | |
- (dummy_currency_because_irrelevant_for_this_test, | |
- seq { yield serverA; yield serverB }) | |
+ (dummy_currency_because_irrelevant_for_this_test, seq { yield serverA; yield serverB }) | |
let serverDetails = ServerRegistry.Serialize servers | |
let deserializedServers = | |
((ServerRegistry.Deserialize serverDetails).TryFind dummy_currency_because_irrelevant_for_this_test).Value | |
|> List.ofSeq | |
+ Assert.That(deserializedServers.Length, Is.EqualTo 2) | |
+ | |
+ member private __.SerializeAndDeserialize (serverA: ServerDetails) (serverB: ServerDetails): List<ServerDetails> = | |
+ let servers = seq { yield serverA; yield serverB } | |
+ let serverRanking = Map.empty.Add (dummy_currency_because_irrelevant_for_this_test, servers) | |
+ let serverDetails = ServerRegistry.Serialize serverRanking | |
+ ((ServerRegistry.Deserialize serverDetails).TryFind dummy_currency_because_irrelevant_for_this_test).Value | |
+ |> List.ofSeq | |
+ | |
+ member private __.Merge (serverA: ServerDetails) (serverB: ServerDetails): List<ServerDetails> = | |
+ let serverRankingA = | |
+ Map.empty.Add (dummy_currency_because_irrelevant_for_this_test, seq { yield serverA }) | |
+ let serverRankingB = | |
+ Map.empty.Add (dummy_currency_because_irrelevant_for_this_test, seq { yield serverB }) | |
+ let mergedServerRanking = ServerRegistry.Merge serverRankingA serverRankingB | |
+ ((ServerRegistry.Merge serverRankingA serverRankingB).TryFind dummy_currency_because_irrelevant_for_this_test) | |
+ .Value | |
+ |> List.ofSeq | |
+ | |
+ [<Test>] | |
+ member self.``when removing duplicate servers, the ones with history and most up to date, stay (I)``() = | |
+ let sameRandomHostname = "xfoihror3uo3wmio" | |
+ let serverA = | |
+ { | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = None | |
+ } | |
+ let serverB = | |
+ { | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
+ CommunicationHistory = CreateHistoryInfo dummy_now | |
+ } | |
+ let deserializedServers = self.SerializeAndDeserialize serverA serverB | |
+ | |
Assert.That(deserializedServers.Length, Is.EqualTo 1) | |
Assert.That(deserializedServers.[0].CommunicationHistory, Is.Not.EqualTo None) | |
+ let mergedServers = self.Merge serverA serverB | |
+ Assert.That(mergedServers.Length, Is.EqualTo 1) | |
+ Assert.That(mergedServers.[0].CommunicationHistory, Is.Not.EqualTo None) | |
+ | |
+ [<Test>] | |
+ member self.``when removing duplicate servers, the ones with history and most up to date, stay (II)``() = | |
+ | |
let olderDate = dummy_now - TimeSpan.FromDays 1.0 | |
let sameRandomHostname = "xfoihror3uo3wmio" | |
let serverA = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo dummy_now | |
} | |
let serverB = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo olderDate | |
} | |
- let servers = Map.empty.Add | |
- (dummy_currency_because_irrelevant_for_this_test, | |
- seq { yield serverA; yield serverB }) | |
- let serverDetails = ServerRegistry.Serialize servers | |
- let deserializedServers = | |
- ((ServerRegistry.Deserialize serverDetails).TryFind dummy_currency_because_irrelevant_for_this_test).Value | |
- |> List.ofSeq | |
+ let deserializedServers = self.SerializeAndDeserialize serverA serverB | |
+ let mergedServers = self.Merge serverA serverB | |
Assert.That(deserializedServers.Length, Is.EqualTo 1) | |
- match deserializedServers.[0].CommunicationHistory with | |
- | None -> | |
- Assert.Fail "should have some history since no server stored had None on it #1" | |
- | Some history -> | |
- match history.Status with | |
- | Fault(_,_) -> Assert.Fail "should have status since both servers inserted had it #1" | |
- | LastSuccessfulCommunication lsc -> | |
- Assert.That(lsc, Is.EqualTo dummy_now) | |
+ Assert.That(mergedServers.Length, Is.EqualTo 1) | |
+ match deserializedServers.[0].CommunicationHistory,mergedServers.[0].CommunicationHistory with | |
+ | Some (dHistory,_), Some (mHistory,_) -> | |
+ match dHistory.Status,mHistory.Status with | |
+ | LastSuccessfulCommunication dLsc,LastSuccessfulCommunication mLsc -> | |
+ Assert.That(dLsc, Is.EqualTo dummy_now) | |
+ Assert.That(mLsc, Is.EqualTo dummy_now) | |
+ | _ -> Assert.Fail "both deserialized and merged should have status since both servers inserted had it #1" | |
+ | _ -> | |
+ Assert.Fail "both deserialized and merged should have some history since no server stored had None on it #1" | |
+ [<Test>] | |
+ member self.``when removing duplicate servers, the ones with history and most up to date, stay (III)``() = | |
let olderDate = dummy_now - TimeSpan.FromDays 1.0 | |
let sameRandomHostname = "xfoihror3uo3wmio" | |
let serverA = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo olderDate | |
} | |
let serverB = | |
{ | |
- NetworkPath = sameRandomHostname | |
- ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = sameRandomHostname | |
+ ConnectionType = some_connection_type_irrelevant_for_this_test | |
+ } | |
CommunicationHistory = CreateHistoryInfo dummy_now | |
} | |
- let servers = Map.empty.Add | |
- (dummy_currency_because_irrelevant_for_this_test, | |
- seq { yield serverA; yield serverB }) | |
- let serverDetails = ServerRegistry.Serialize servers | |
- let deserializedServers = | |
- ((ServerRegistry.Deserialize serverDetails).TryFind dummy_currency_because_irrelevant_for_this_test).Value | |
- |> List.ofSeq | |
+ let deserializedServers = self.SerializeAndDeserialize serverA serverB | |
+ let mergedServers = self.Merge serverA serverB | |
Assert.That(deserializedServers.Length, Is.EqualTo 1) | |
- match deserializedServers.[0].CommunicationHistory with | |
- | None -> | |
- Assert.Fail "should have some history since no server stored had None on it #2" | |
- | Some history -> | |
- match history.Status with | |
- | Fault(_,_) -> Assert.Fail "should have status since both servers inserted had it #2" | |
- | LastSuccessfulCommunication lsc -> | |
- Assert.That(lsc, Is.EqualTo dummy_now) | |
+ Assert.That(mergedServers.Length, Is.EqualTo 1) | |
+ match deserializedServers.[0].CommunicationHistory,mergedServers.[0].CommunicationHistory with | |
+ | Some (dHistory, _), Some (mHistory, _) -> | |
+ match dHistory.Status,mHistory.Status with | |
+ | LastSuccessfulCommunication dLsc,LastSuccessfulCommunication mLsc -> | |
+ Assert.That(dLsc, Is.EqualTo dummy_now) | |
+ Assert.That(mLsc, Is.EqualTo dummy_now) | |
+ | _ -> Assert.Fail "both deserialized and merged should have status since both servers inserted had it #1" | |
+ | _ -> | |
+ Assert.Fail "both deserialized and merged should have some history since no server stored had None on it #2" | |
diff --git a/src/GWallet.Backend/Account.fs b/src/GWallet.Backend/Account.fs | |
index 6d94c9e..726f70a 100644 | |
--- a/src/GWallet.Backend/Account.fs | |
+++ b/src/GWallet.Backend/Account.fs | |
@@ -9,7 +9,7 @@ open System.Threading.Tasks | |
module Account = | |
let private GetShowableBalanceInternal (account: IAccount) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<Option<decimal>> = | |
match account with | |
@@ -24,7 +24,9 @@ module Account = | |
account.Currency | |
Ether.Account.GetShowableBalance account mode cancelSourceOption | |
- let GetShowableBalance (account: IAccount) (mode: Mode) (cancelSourceOption: Option<CancellationTokenSource>) | |
+ let GetShowableBalance (account: IAccount) | |
+ (mode: ServerSelectionMode) | |
+ (cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<MaybeCached<decimal>> = | |
async { | |
if Config.NoNetworkBalanceForDebuggingPurposes then | |
@@ -116,7 +118,7 @@ module Account = | |
for accountFile in Config.GetAccountFiles [currency] AccountKind.Archived do | |
let account = ArchivedAccount(currency, accountFile, fromConfigAccountFileToPublicAddressFunc) | |
- let maybeBalanceJob = GetShowableBalanceInternal account Mode.Fast | |
+ let maybeBalanceJob = GetShowableBalanceInternal account ServerSelectionMode.Fast | |
yield async { | |
let! maybeBalance = maybeBalanceJob cancelSourceOption | |
let positiveBalance = | |
diff --git a/src/GWallet.Backend/Caching.fs b/src/GWallet.Backend/Caching.fs | |
index 5bd0542..6fbb080 100644 | |
--- a/src/GWallet.Backend/Caching.fs | |
+++ b/src/GWallet.Backend/Caching.fs | |
@@ -5,24 +5,6 @@ open System.IO | |
open System.Linq | |
open System.Net.Http | |
-type CachedValue<'T> = ('T*DateTime) | |
-type NotFresh<'T> = | |
- NotAvailable | Cached of CachedValue<'T> | |
-type MaybeCached<'T> = | |
- NotFresh of NotFresh<'T> | Fresh of 'T | |
- | |
-type PublicAddress = string | |
-type private DietCurrency = string | |
-type private ServerIdentifier = string | |
- | |
-type DietCache = | |
- { | |
- UsdPrice: Map<DietCurrency,decimal>; | |
- Addresses: Map<PublicAddress,List<DietCurrency>>; | |
- Balances: Map<DietCurrency,decimal>; | |
- } | |
- | |
-type ServerRanking = Map<ServerIdentifier,HistoryInfo*DateTime> | |
type CachedNetworkData = | |
{ | |
@@ -98,7 +80,8 @@ module Caching = | |
let private defaultCacheFiles = | |
{ | |
CachedNetworkData = FileInfo(Path.Combine(GetCacheDir().FullName, "networkdata.json")) | |
- ServerStats = FileInfo(Path.Combine(GetCacheDir().FullName, "stats.json")) | |
+ ServerStats = FileInfo(Path.Combine(GetCacheDir().FullName, | |
+ ServerRegistry.ServersEmbeddedResourceFileName)) | |
} | |
let public ImportFromJson<'T> (cacheData: string): 'T = | |
@@ -238,22 +221,40 @@ module Caching = | |
| Some files -> files | |
| None -> defaultCacheFiles | |
- let firstRun,initialSessionCachedNetworkData,initialServerStats = LoadFromDisk cacheFiles | |
- let mutable sessionCachedNetworkData = initialSessionCachedNetworkData | |
- let mutable sessionServerRanking = initialServerStats | |
- | |
let SaveNetworkDataToDisk (newCachedData: CachedNetworkData) = | |
let networkDataInJson = Marshalling.Serialize newCachedData | |
// it is assumed that SaveToDisk is being run under a lock() block | |
File.WriteAllText (cacheFiles.CachedNetworkData.FullName, networkDataInJson) | |
+ // we return back the rankings because the serialization process could remove dupes (and deserialization time | |
+ // is basically negligible, i.e. took 15 milliseconds max in my MacBook in Debug mode) | |
let SaveServerRankingsToDisk (serverStats: ServerRanking) = | |
let serverStatsInJson = Marshalling.Serialize serverStats | |
// it is assumed that SaveToDisk is being run under a lock() block | |
File.WriteAllText (cacheFiles.ServerStats.FullName, serverStatsInJson) | |
+ match LoadFromDiskInternal<ServerRanking> cacheFiles.ServerStats with | |
+ | None -> failwith "should return something after having saved it" | |
+ | Some cleansedServerStats -> cleansedServerStats | |
+ | |
+ let InitServers (lastServerStats: ServerRanking) = | |
+ let mergedServers = ServerRegistry.MergeWithBaseline lastServerStats | |
+ let mergedAndSaved = SaveServerRankingsToDisk mergedServers | |
+ for KeyValue(currency,servers) in mergedAndSaved do | |
+ for server in servers do | |
+ if server.CommunicationHistory.IsNone then | |
+ Console.Error.WriteLine (sprintf "WARNING: no history stats about %A server %s" | |
+ currency server.ServerInfo.NetworkPath) | |
+ mergedServers | |
+ | |
+ let firstRun,initialSessionCachedNetworkData,lastServerStats = LoadFromDisk cacheFiles | |
+ let initialServerStats = InitServers lastServerStats | |
+ | |
+ let mutable sessionCachedNetworkData = initialSessionCachedNetworkData | |
+ let mutable sessionServerRanking = initialServerStats | |
+ | |
let GetSumOfAllTransactions (trans: Map<Currency,Map<PublicAddress,Map<string,CachedValue<decimal>>>>) | |
currency address: decimal = | |
let now = DateTime.UtcNow | |
@@ -492,50 +493,54 @@ module Caching = | |
if transactionCurrency <> feeCurrency && (not Config.EthTokenEstimationCouldBeBuggyAsInNotAccurate) then | |
self.StoreTransactionRecord address feeCurrency txId feeAmount | |
- member self.SaveServerLastStat (server: ServerDetails, historyInfo): unit = | |
+ member self.SaveServerLastStat (serverMatchFunc: ServerDetails->bool) | |
+ (stat: HistoryFact): unit = | |
lock cacheFiles.ServerStats (fun _ -> | |
- let previousLastSuccessfulCommunication = | |
- match sessionServerRanking.TryFind server.NetworkPath with | |
- | None -> None | |
- | Some (prevHistoryInfo,_) -> | |
- if WeirdNullCheckToDetectVersionConflicts prevHistoryInfo || | |
- WeirdNullCheckToDetectVersionConflicts prevHistoryInfo.Status then | |
- Console.Error.WriteLine droppedCachedMsgWarning | |
- None | |
- else | |
- match prevHistoryInfo.Status with | |
- | LastSuccessfulCommunication lsc -> Some lsc | |
- | Fault (_, maybeLsc) -> maybeLsc | |
- | |
- let newHistoryInfo = | |
- match previousLastSuccessfulCommunication with | |
- | None -> historyInfo | |
- | Some lsc -> | |
- match historyInfo.Status with | |
- | LastSuccessfulCommunication newLsc -> historyInfo | |
- // _ because the FaultParallelClient cannot know the last successful communication in this case | |
- | Fault (fault, _) -> | |
- { | |
- TimeSpan = historyInfo.TimeSpan | |
- Status = Fault(fault, Some lsc) | |
- } | |
+ let currency,serverInfo,previousLastSuccessfulCommunication = | |
+ match ServerRegistry.TryFindValue sessionServerRanking serverMatchFunc with | |
+ | None -> | |
+ failwith "Merge&Save didn't happen before launching the FaultTolerantPClient?" | |
+ | Some (currency,server) -> | |
+ match server.CommunicationHistory with | |
+ | None -> currency,server.ServerInfo,None | |
+ | Some (prevHistoryInfo,_) -> | |
+ match prevHistoryInfo.Status with | |
+ | LastSuccessfulCommunication lsc -> currency,server.ServerInfo,Some lsc | |
+ | Fault (_, maybeLsc) -> currency,server.ServerInfo,maybeLsc | |
+ | |
+ let now = DateTime.Now | |
+ let newHistoryInfo: CachedValue<HistoryInfo> = | |
+ match stat.Fault with | |
+ | None -> | |
+ ({ TimeSpan = stat.TimeSpan; Status = LastSuccessfulCommunication now }, now) | |
+ | Some exInfo -> | |
+ ({ TimeSpan = stat.TimeSpan; Status = Fault(exInfo, previousLastSuccessfulCommunication) }, now) | |
- let newCachedValue = | |
- sessionServerRanking.Add(server.NetworkPath, (newHistoryInfo, DateTime.UtcNow)) | |
+ let newServerDetails = | |
+ { | |
+ ServerInfo = serverInfo | |
+ CommunicationHistory = Some newHistoryInfo | |
+ } | |
+ let serversForCurrency = | |
+ match sessionServerRanking.TryFind currency with | |
+ | None -> Seq.empty | |
+ | Some servers -> servers | |
- sessionServerRanking <- newCachedValue | |
+ let newServersForCurrency = | |
+ Seq.append (seq { yield newServerDetails }) serversForCurrency | |
- SaveServerRankingsToDisk newCachedValue | |
+ let newServerList = sessionServerRanking.Add(currency, newServersForCurrency) | |
+ | |
+ let newCachedValue = SaveServerRankingsToDisk newServerList | |
+ sessionServerRanking <- newCachedValue | |
) | |
- member self.RetreiveLastServerHistory (serverId: string): Option<HistoryInfo> = | |
+ member self.GetServers (currency: Currency): seq<ServerDetails> = | |
lock cacheFiles.ServerStats (fun _ -> | |
- match sessionServerRanking.TryFind serverId with | |
+ match sessionServerRanking.TryFind currency with | |
| None -> | |
- if Config.DebugLog then | |
- Console.Error.WriteLine (sprintf "WARNING: no history stats about %s" serverId) | |
- None | |
- | Some (historyInfo,_) -> Some historyInfo | |
+ failwithf "Initialization of servers' cache failed? currency %A not found" currency | |
+ | Some servers -> servers | |
) | |
member self.BootstrapServerStatsFromTrustedSource(): Async<unit> = | |
@@ -567,7 +572,7 @@ module Caching = | |
let username = "knocte" | |
let projName = "geewallet" | |
let githubBaseUrl,gitlabBaseUrl = "https://raw.githubusercontent.com","https://gitlab.com" | |
- let pathToFile = "src/GWallet.Backend/lastServerStats.json" | |
+ let pathToFile = sprintf "src/GWallet.Backend/%s" ServerRegistry.ServersEmbeddedResourceFileName | |
let knocteGitHub = | |
sprintf "%s/%s/%s/%s/%s" | |
@@ -589,8 +594,8 @@ module Caching = | |
| Some lastServerStatsInJson -> | |
let lastServerStats = ImportFromJson<ServerRanking> lastServerStatsInJson | |
lock cacheFiles.ServerStats (fun _ -> | |
- sessionServerRanking <- lastServerStats | |
- SaveServerRankingsToDisk lastServerStats | |
+ let savedServerStats = SaveServerRankingsToDisk lastServerStats | |
+ sessionServerRanking <- savedServerStats | |
) | |
} | |
diff --git a/src/GWallet.Backend/CachingTypes.fs b/src/GWallet.Backend/CachingTypes.fs | |
new file mode 100644 | |
index 0000000..4970e7e | |
--- /dev/null | |
+++ b/src/GWallet.Backend/CachingTypes.fs | |
@@ -0,0 +1,20 @@ | |
+namespace GWallet.Backend | |
+ | |
+open System | |
+ | |
+type CachedValue<'T> = ('T*DateTime) | |
+type NotFresh<'T> = | |
+ NotAvailable | Cached of CachedValue<'T> | |
+type MaybeCached<'T> = | |
+ NotFresh of NotFresh<'T> | Fresh of 'T | |
+ | |
+type PublicAddress = string | |
+type private DietCurrency = string | |
+type private ServerIdentifier = string | |
+ | |
+type DietCache = | |
+ { | |
+ UsdPrice: Map<DietCurrency,decimal>; | |
+ Addresses: Map<PublicAddress,List<DietCurrency>>; | |
+ Balances: Map<DietCurrency,decimal>; | |
+ } | |
diff --git a/src/GWallet.Backend/Ether/EtherAccount.fs b/src/GWallet.Backend/Ether/EtherAccount.fs | |
index 047c987..e53956a 100644 | |
--- a/src/GWallet.Backend/Ether/EtherAccount.fs | |
+++ b/src/GWallet.Backend/Ether/EtherAccount.fs | |
@@ -36,7 +36,7 @@ module internal Account = | |
publicAddress | |
let private GetBalance (account: IAccount) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(balType: BalanceType) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
= async { | |
@@ -50,7 +50,9 @@ module internal Account = | |
return balance | |
} | |
- let private GetBalanceFromServer (account: IAccount) (balType: BalanceType) (mode: Mode) | |
+ let private GetBalanceFromServer (account: IAccount) | |
+ (balType: BalanceType) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<Option<decimal>> = | |
async { | |
@@ -66,14 +68,14 @@ module internal Account = | |
} | |
let internal GetShowableBalance (account: IAccount) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<Option<decimal>> = | |
let getBalanceWithoutCaching(maybeUnconfirmedBalanceTaskAlreadyStarted: Option<Task<Option<decimal>>>) | |
: Async<Option<decimal>> = | |
async { | |
let! confirmed = GetBalanceFromServer account BalanceType.Confirmed mode cancelSourceOption | |
- if mode = Mode.Fast then | |
+ if mode = ServerSelectionMode.Fast then | |
return confirmed | |
else | |
let! unconfirmed = | |
diff --git a/src/GWallet.Backend/Ether/EtherServer.fs b/src/GWallet.Backend/Ether/EtherServer.fs | |
index 2c62e0a..5cf9178 100644 | |
--- a/src/GWallet.Backend/Ether/EtherServer.fs | |
+++ b/src/GWallet.Backend/Ether/EtherServer.fs | |
@@ -42,7 +42,7 @@ module Web3ServerSeedList = | |
//let private etcWeb3CommonWealthMantis = SomeWeb3("https://etc-mantis.callisto.network") | |
// -------------------------------------------------------------------------------------- | |
- let private GetWeb3Servers (currency: Currency): List<ServerDetails> = | |
+ let private GetEtherServers (currency: Currency): List<ServerDetails> = | |
let baseCurrency = | |
if currency = Currency.ETC || currency = Currency.ETH then | |
currency | |
@@ -50,26 +50,26 @@ module Web3ServerSeedList = | |
Currency.ETH | |
else | |
failwithf "Assertion failed: Ether currency %A not supported?" currency | |
- ServerRegistry.GetServers baseCurrency |> List.ofSeq | |
+ Caching.Instance.GetServers baseCurrency |> List.ofSeq | |
let Randomize currency = | |
- let serverList = GetWeb3Servers currency | |
+ let serverList = GetEtherServers currency | |
Shuffler.Unsort serverList | |
module Server = | |
let private Web3Server (serverDetails: ServerDetails) = | |
- match serverDetails.ConnectionType with | |
+ match serverDetails.ServerInfo.ConnectionType with | |
| { Protocol = Tcp _ ; Encrypted = _ } -> | |
- failwithf "Ether server of TCP connection type?: %s" serverDetails.NetworkPath | |
+ failwithf "Ether server of TCP connection type?: %s" serverDetails.ServerInfo.NetworkPath | |
| { Protocol = Http ; Encrypted = encrypted } -> | |
let protocol = | |
if encrypted then | |
"https" | |
else | |
"http" | |
- let uri = sprintf "%s://%s" protocol serverDetails.NetworkPath | |
+ let uri = sprintf "%s://%s" protocol serverDetails.ServerInfo.NetworkPath | |
SomeWeb3 uri | |
let HttpRequestExceptionMatchesErrorCode (ex: Http.HttpRequestException) (errorCode: int): bool = | |
@@ -248,21 +248,32 @@ module Server = | |
let private NumberOfParallelJobsForMode mode = | |
match mode with | |
- | Mode.Fast -> 5u | |
- | Mode.Analysis -> 3u | |
+ | ServerSelectionMode.Fast -> 5u | |
+ | ServerSelectionMode.Analysis -> 3u | |
let private FaultTolerantParallelClientInnerSettings (numberOfConsistentResponsesRequired: uint32) | |
- (mode: Mode) = | |
+ (mode: ServerSelectionMode) | |
+ maybeConsistencyConfig = | |
+ | |
+ let consistencyConfig = | |
+ match maybeConsistencyConfig with | |
+ | None -> SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired | |
+ | Some specificConsistencyConfig -> specificConsistencyConfig | |
+ | |
{ | |
NumberOfParallelJobsAllowed = NumberOfParallelJobsForMode mode | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired; | |
NumberOfRetries = Config.NUMBER_OF_RETRIES_TO_SAME_SERVERS; | |
NumberOfRetriesForInconsistency = Config.NUMBER_OF_RETRIES_TO_SAME_SERVERS; | |
- Mode = mode | |
- ShouldReportUncancelledJobs = true | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ ServerSelectionMode = mode | |
+ ConsistencyConfig = consistencyConfig | |
+ ReportUncancelledJobs = true | |
+ } | |
} | |
- let private FaultTolerantParallelClientDefaultSettings (currency: Currency) (mode: Mode) = | |
+ let private FaultTolerantParallelClientDefaultSettings (currency: Currency) (mode: ServerSelectionMode) = | |
let numberOfConsistentResponsesRequired = | |
if not Networking.Tls12Support then | |
1u | |
@@ -272,19 +283,17 @@ module Server = | |
mode | |
let private FaultTolerantParallelClientSettingsForBalanceCheck (currency: Currency) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cacheMatchFunc: decimal->bool) = | |
- let defaultSettings = FaultTolerantParallelClientDefaultSettings currency mode | |
- if mode = Mode.Fast then | |
- { | |
- defaultSettings with | |
- ConsistencyConfig = OneServerConsistentWithCacheOrTwoServers cacheMatchFunc | |
- } | |
- else | |
- defaultSettings | |
+ let consistencyConfig = | |
+ if mode = ServerSelectionMode.Fast then | |
+ Some (OneServerConsistentWithCacheOrTwoServers cacheMatchFunc) | |
+ else | |
+ None | |
+ FaultTolerantParallelClientDefaultSettings currency mode consistencyConfig | |
let private FaultTolerantParallelClientSettingsForBroadcast () = | |
- FaultTolerantParallelClientInnerSettings 1u Mode.Fast | |
+ FaultTolerantParallelClientInnerSettings 1u ServerSelectionMode.Fast None | |
let private NUMBER_OF_CONSISTENT_RESPONSES_TO_TRUST_ETH_SERVER_RESULTS = 2 | |
let private NUMBER_OF_ALLOWED_PARALLEL_CLIENT_QUERY_JOBS = 3 | |
@@ -320,34 +329,35 @@ module Server = | |
let msg = sprintf "%s: %s" (ex.GetType().FullName) ex.Message | |
return raise <| ServerDiscardedException(msg, ex) | |
| ex -> | |
- return raise <| Exception(sprintf "Some problem when connecting to %s" server.NetworkPath, ex) | |
+ return raise <| Exception(sprintf "Some problem when connecting to '%s'" | |
+ server.ServerInfo.NetworkPath, ex) | |
} | |
- // FIXME: seems there's some code duplication between this function and UtxoAccount's GetRandomizedFuncs function | |
- let private GetRandomizedFuncs<'R> (currency: Currency) | |
- (web3Func: SomeWeb3->Async<'R>) | |
- : List<Server<ServerDetails,'R>> = | |
+ // FIXME: seems there's some code duplication between this function and UtxoCoinAccount.fs's GetServerFuncs function | |
+ // and room for simplification to not pass a new ad-hoc delegate? | |
+ let GetServerFuncs<'R> (web3Func: SomeWeb3->Async<'R>) | |
+ (etherServers: seq<ServerDetails>) | |
+ : seq<Server<ServerDetails,'R>> = | |
let Web3ServerToGenericServer (web3ClientFunc: SomeWeb3->Async<'R>) | |
(etherServer: ServerDetails) | |
: Server<ServerDetails,'R> = | |
- let lastDetailsForServer = | |
- match Caching.Instance.RetreiveLastServerHistory etherServer.NetworkPath with | |
- | None -> etherServer | |
- | Some historyInCache -> | |
- { etherServer with | |
- CommunicationHistory = Some historyInCache } | |
- | |
{ | |
- Details = lastDetailsForServer | |
- Retrieval = Web3ServerToRetrievalFunc lastDetailsForServer web3ClientFunc | |
+ Details = etherServer | |
+ Retrieval = Web3ServerToRetrievalFunc etherServer web3ClientFunc | |
} | |
- let web3servers = Web3ServerSeedList.Randomize currency |> List.ofSeq | |
let serverFuncs = | |
- List.map (Web3ServerToGenericServer web3Func) | |
- web3servers | |
+ Seq.map (Web3ServerToGenericServer web3Func) | |
+ etherServers | |
serverFuncs | |
+ let private GetRandomizedFuncs<'R> (currency: Currency) | |
+ (web3Func: SomeWeb3->Async<'R>) | |
+ : List<Server<ServerDetails,'R>> = | |
+ let etherServers = Web3ServerSeedList.Randomize currency | |
+ GetServerFuncs web3Func etherServers | |
+ |> List.ofSeq | |
+ | |
let GetTransactionCount (currency: Currency) (address: string) | |
: Async<HexBigInteger> = | |
async { | |
@@ -361,7 +371,7 @@ module Server = | |
} | |
GetRandomizedFuncs currency web3Func | |
return! faultTolerantEtherClient.Query | |
- (FaultTolerantParallelClientDefaultSettings currency Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings currency ServerSelectionMode.Fast None) | |
web3Funcs | |
} | |
@@ -413,7 +423,10 @@ module Server = | |
| None -> false | |
| Some balance -> someBalanceRetreived = balance | |
- let GetEtherBalance (currency: Currency) (address: string) (balType: BalanceType) (mode: Mode) | |
+ let GetEtherBalance (currency: Currency) | |
+ (address: string) | |
+ (balType: BalanceType) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<decimal> = | |
async { | |
@@ -473,7 +486,7 @@ module Server = | |
let GetTokenBalance (currency: Currency) | |
(address: string) | |
(balType: BalanceType) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<decimal> = | |
async { | |
@@ -523,7 +536,7 @@ module Server = | |
} | |
GetRandomizedFuncs account.Currency web3Func | |
return! faultTolerantEtherClient.Query | |
- (FaultTolerantParallelClientDefaultSettings baseCurrency Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings baseCurrency ServerSelectionMode.Fast None) | |
web3Funcs | |
} | |
@@ -546,8 +559,9 @@ module Server = | |
GetRandomizedFuncs currency web3Func | |
let minResponsesRequired = 2u | |
return! faultTolerantEtherClient.Query | |
- { FaultTolerantParallelClientDefaultSettings currency Mode.Fast with | |
- ConsistencyConfig = AverageBetweenResponses (minResponsesRequired, AverageGasPrice) } | |
+ (FaultTolerantParallelClientDefaultSettings | |
+ currency ServerSelectionMode.Fast | |
+ (Some (AverageBetweenResponses (minResponsesRequired, AverageGasPrice)))) | |
web3Funcs | |
} | |
@@ -601,7 +615,7 @@ module Server = | |
} | |
GetRandomizedFuncs currency web3Func | |
return! faultTolerantEtherClient.Query | |
- (FaultTolerantParallelClientDefaultSettings currency Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings currency ServerSelectionMode.Fast None) | |
web3Funcs | |
} | |
@@ -625,7 +639,7 @@ module Server = | |
} | |
GetRandomizedFuncs baseCurrency web3Func | |
return! faultTolerantEtherClient.Query | |
- (FaultTolerantParallelClientDefaultSettings baseCurrency Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings baseCurrency ServerSelectionMode.Fast None) | |
web3Funcs | |
} | |
diff --git a/src/GWallet.Backend/FaultTolerantParallelClient.fs b/src/GWallet.Backend/FaultTolerantParallelClient.fs | |
index 4a05fbf..a7c0e20 100644 | |
--- a/src/GWallet.Backend/FaultTolerantParallelClient.fs | |
+++ b/src/GWallet.Backend/FaultTolerantParallelClient.fs | |
@@ -54,18 +54,27 @@ type ConsistencySettings<'R> = | |
| SpecificNumberOfConsistentResponsesRequired of uint32 | |
| AverageBetweenResponses of (uint32 * (List<'R> -> 'R)) | |
-type Mode = | |
+type ServerSelectionMode = | |
| Fast | |
| Analysis | |
+type ResultSelectionSettings<'R> = | |
+ { | |
+ ServerSelectionMode: ServerSelectionMode | |
+ ReportUncancelledJobs: bool | |
+ ConsistencyConfig: ConsistencySettings<'R> | |
+ } | |
+ | |
+type ResultSelectionMode<'R> = | |
+ | Selective of ResultSelectionSettings<'R> | |
+ | Exhaustive | |
+ | |
type FaultTolerantParallelClientSettings<'R> = | |
{ | |
NumberOfParallelJobsAllowed: uint32; | |
- ConsistencyConfig: ConsistencySettings<'R>; | |
NumberOfRetries: uint32; | |
NumberOfRetriesForInconsistency: uint32; | |
- Mode: Mode | |
- ShouldReportUncancelledJobs: bool | |
+ ResultSelectionMode: ResultSelectionMode<'R> | |
} | |
type Result<'Value, 'Err> = | |
@@ -125,7 +134,7 @@ type Runner<'Resource,'Ex when 'Resource: equality and 'Ex :> Exception> = | |
} | |
type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicationHistory and 'E :> Exception> | |
- (updateServer: 'K*HistoryInfo -> unit) = | |
+ (updateServer: ('K->bool)->HistoryFact->unit) = | |
do | |
if typeof<'E> = typeof<Exception> then | |
raise (ArgumentException("'E cannot be System.Exception, use a derived one", "'E")) | |
@@ -146,7 +155,8 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
jobs | |
|> List.map (fun job -> Async.StartAsTask(job, ?cancellationToken = Some token)) | |
- let rec WhenSomeInternal (consistencySettings: ConsistencySettings<'R>) | |
+ let rec WhenSomeInternal (consistencySettings: Option<ConsistencySettings<'R>>) | |
+ (initialServerCount: uint32) | |
(tasks: List<Task<NonParallelResults<'K,'R,'E>>>) | |
(resultsSoFar: List<'R>) | |
(failedFuncsSoFar: ExceptionsSoFar<'K,'R,'E>) | |
@@ -172,42 +182,80 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
resultsSoFar,restOfTasks | |
let newFailedFuncs = List.append failedFuncsSoFar failuresOfTask | |
- let returnWithConsistencyOf number cacheMatchFunc = async { | |
+ let returnWithConsistencyOf (minNumberOfConsistentResultsRequired: Option<uint32>) cacheMatchFunc = async { | |
let resultsSortedByCount = MeasureConsistency newResults | |
match resultsSortedByCount with | |
| [] -> | |
- return! WhenSomeInternal consistencySettings newRestOfTasks newResults newFailedFuncs | |
+ return! WhenSomeInternal consistencySettings | |
+ initialServerCount | |
+ newRestOfTasks newResults newFailedFuncs | |
| (mostConsistentResult,maxNumberOfConsistentResultsObtained)::_ -> | |
- if cacheMatchFunc mostConsistentResult || (maxNumberOfConsistentResultsObtained = int number) then | |
+ match minNumberOfConsistentResultsRequired,cacheMatchFunc with | |
+ | None, None -> | |
return ConsistentResult mostConsistentResult | |
- else | |
- return! WhenSomeInternal consistencySettings newRestOfTasks newResults newFailedFuncs | |
+ | Some number, Some cacheMatch -> | |
+ if cacheMatch mostConsistentResult || (maxNumberOfConsistentResultsObtained = int number) then | |
+ return ConsistentResult mostConsistentResult | |
+ else | |
+ return! WhenSomeInternal consistencySettings | |
+ initialServerCount | |
+ newRestOfTasks | |
+ newResults | |
+ newFailedFuncs | |
+ | _ -> return failwith "should be either both None or both Some!" | |
} | |
match consistencySettings with | |
- | AverageBetweenResponses (minimumNumberOfResponses,averageFunc) -> | |
+ | Some (AverageBetweenResponses (minimumNumberOfResponses,averageFunc)) -> | |
if (newResults.Length >= int minimumNumberOfResponses) then | |
return AverageResult (averageFunc newResults) | |
else | |
- return! WhenSomeInternal consistencySettings newRestOfTasks newResults newFailedFuncs | |
- | SpecificNumberOfConsistentResponsesRequired number -> | |
- return! returnWithConsistencyOf number (fun _ -> false) | |
- | OneServerConsistentWithCacheOrTwoServers cacheMatchFunc -> | |
- return! returnWithConsistencyOf 2u cacheMatchFunc | |
+ return! WhenSomeInternal consistencySettings | |
+ initialServerCount | |
+ newRestOfTasks | |
+ newResults | |
+ newFailedFuncs | |
+ | Some (SpecificNumberOfConsistentResponsesRequired number) -> | |
+ return! returnWithConsistencyOf (Some number) ((fun _ -> false) |> Some) | |
+ | Some (OneServerConsistentWithCacheOrTwoServers cacheMatchFunc) -> | |
+ return! returnWithConsistencyOf (Some 2u) (Some cacheMatchFunc) | |
+ | None -> | |
+ if newRestOfTasks.Length = 0 then | |
+ | |
+ if Config.DebugLog then | |
+ Console.WriteLine "100% done (for this currency)" | |
+ return! returnWithConsistencyOf None None | |
+ | |
+ else | |
+ if Config.DebugLog && | |
+ | |
+ // even when all funcs have been finished, we still have newRestOfTasks.Length==1 | |
+ // because of the way ConcatenateNonParallelFuncs works with empty([]) servers var | |
+ not (newFailedFuncs.Length + newResults.Length = int initialServerCount) then | |
+ | |
+ Console.WriteLine(sprintf "%f%% done (for this currency)" | |
+ (100.*(float (newFailedFuncs.Length+newResults.Length))/(float initialServerCount))) | |
+ | |
+ return! WhenSomeInternal consistencySettings | |
+ initialServerCount | |
+ newRestOfTasks | |
+ newResults | |
+ newFailedFuncs | |
} | |
// at the time of writing this, I only found a Task.WhenAny() equivalent function in the asyncF# world, called | |
// "Async.WhenAny" in TomasP's tryJoinads source code, however it seemed a bit complex for me to wrap my head around | |
// it (and I couldn't just consume it and call it a day, I had to modify it to be "WhenSome" instead of "WhenAny", | |
// as in when N>1), so I decided to write my own, using Tasks to make sure I would not spawn duplicate jobs | |
- let WhenSome (consistencySettings: ConsistencySettings<'R>) | |
+ let WhenSome (consistencySettings: Option<ConsistencySettings<'R>>) | |
+ (initialServerCount: uint32) | |
(jobs: List<Async<NonParallelResults<'K,'R,'E>>>) | |
(resultsSoFar: List<'R>) | |
(failedFuncsSoFar: ExceptionsSoFar<'K,'R,'E>) | |
(cancellationSource: CancellationTokenSource) | |
: Async<FinalResult<'K,'T,'R,'E>> = | |
let tasks = LaunchAsyncJobs jobs cancellationSource | |
- WhenSomeInternal consistencySettings tasks resultsSoFar failedFuncsSoFar | |
+ WhenSomeInternal consistencySettings initialServerCount tasks resultsSoFar failedFuncsSoFar | |
let rec ConcatenateNonParallelFuncs (failuresSoFar: ExceptionsSoFar<'K,'R,'E>) | |
(shouldReportUncancelledJobs: bool) | |
@@ -227,12 +275,8 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
match runResult with | |
| Success result -> | |
- let history = | |
- { | |
- TimeSpan = stopwatch.Elapsed | |
- Status = LastSuccessfulCommunication DateTime.UtcNow | |
- } | |
- updateServer (head.Details, history) | |
+ let historyFact = { TimeSpan = stopwatch.Elapsed; Fault = None } | |
+ updateServer (fun server -> server = head.Details) historyFact | |
let tailAsync = | |
ConcatenateNonParallelFuncs failuresSoFar shouldReportUncancelledJobs cancelledInternally tail | |
return failuresSoFar,SuccessfulFirstResult(result,tailAsync) | |
@@ -243,12 +287,8 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
TypeFullName = ex.GetType().FullName | |
Message = ex.Message | |
} | |
- let history = | |
- { | |
- TimeSpan = stopwatch.Elapsed | |
- Status = Fault(exInfo, None) | |
- } | |
- updateServer (head.Details, history) | |
+ let historyFact = { TimeSpan = stopwatch.Elapsed; Fault = (Some exInfo) } | |
+ updateServer (fun server -> server = head.Details) historyFact | |
let newFailures = (head,ex)::failuresSoFar | |
return! ConcatenateNonParallelFuncs newFailures shouldReportUncancelledJobs cancelledInternally tail | |
} | |
@@ -269,6 +309,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
let rec QueryInternalImplementation | |
(settings: FaultTolerantParallelClientSettings<'R>) | |
+ (initialFuncCount: uint32) | |
(funcs: List<Server<'K,'R>>) | |
(resultsSoFar: List<'R>) | |
(failedFuncsSoFar: ExceptionsSoFar<'K,'R,'E>) | |
@@ -283,19 +324,22 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
let howManyFuncs = uint32 funcs.Length | |
let numberOfParallelJobsAllowed = int settings.NumberOfParallelJobsAllowed | |
- match settings.ConsistencyConfig with | |
- | SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired -> | |
- if numberOfConsistentResponsesRequired < 1u then | |
- return raise <| ArgumentException("must be higher than zero", "numberOfConsistentResponsesRequired") | |
- if (howManyFuncs < numberOfConsistentResponsesRequired) then | |
- return raise(ArgumentException("number of funcs must be equal or higher than numberOfConsistentResponsesRequired", | |
- "funcs")) | |
- | AverageBetweenResponses(minimumNumberOfResponses,averageFunc) -> | |
- if (int minimumNumberOfResponses > numberOfParallelJobsAllowed) then | |
- return raise(ArgumentException("numberOfParallelJobsAllowed should be equal or higher than minimumNumberOfResponses for the averageFunc", | |
- "settings")) | |
- | OneServerConsistentWithCacheOrTwoServers _ -> | |
- () | |
+ match settings.ResultSelectionMode with | |
+ | Selective resultSelectionSettings -> | |
+ match resultSelectionSettings.ConsistencyConfig with | |
+ | SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired -> | |
+ if numberOfConsistentResponsesRequired < 1u then | |
+ return raise <| ArgumentException("must be higher than zero", "numberOfConsistentResponsesRequired") | |
+ if (howManyFuncs < numberOfConsistentResponsesRequired) then | |
+ return raise(ArgumentException("number of funcs must be equal or higher than numberOfConsistentResponsesRequired", | |
+ "funcs")) | |
+ | AverageBetweenResponses(minimumNumberOfResponses,averageFunc) -> | |
+ if (int minimumNumberOfResponses > numberOfParallelJobsAllowed) then | |
+ return raise(ArgumentException("numberOfParallelJobsAllowed should be equal or higher than minimumNumberOfResponses for the averageFunc", | |
+ "settings")) | |
+ | OneServerConsistentWithCacheOrTwoServers _ -> | |
+ () | |
+ | _ -> () | |
let funcsToRunInParallel,restOfFuncs = | |
if (howManyFuncs > settings.NumberOfParallelJobsAllowed) then | |
@@ -303,6 +347,12 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
else | |
funcs |> Seq.ofList, Seq.empty | |
+ let shouldReportUncancelledJobs = | |
+ match settings.ResultSelectionMode with | |
+ | Exhaustive -> false | |
+ | Selective subSettings -> | |
+ subSettings.ReportUncancelledJobs | |
+ | |
// each bucket can be run in parallel, each bucket contains 1 or more funcs that cannot be run in parallel | |
// e.g. if we have funcs A, B, C, D and numberOfParallelJobsAllowed=2, then we have funcBucket1(A,B) and | |
// funcBucket2(C,D), then fb1&fb2 are started at the same time (A&C start at the same time), and B | |
@@ -311,7 +361,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
Seq.splitInto numberOfParallelJobsAllowed funcs | |
|> Seq.map List.ofArray | |
|> Seq.map | |
- (ConcatenateNonParallelFuncs List.empty settings.ShouldReportUncancelledJobs cancelledInternally) | |
+ (ConcatenateNonParallelFuncs List.empty shouldReportUncancelledJobs cancelledInternally) | |
|> List.ofSeq | |
let lengthOfBucketsSanityCheck = Math.Min(funcs.Length, numberOfParallelJobsAllowed) | |
@@ -319,8 +369,12 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
return failwithf "Assertion failed, splitInto didn't work as expected? got %d, should be %d" | |
funcBuckets.Length lengthOfBucketsSanityCheck | |
+ let consistencyConfig = | |
+ match settings.ResultSelectionMode with | |
+ | Exhaustive -> None | |
+ | Selective subSettings -> Some subSettings.ConsistencyConfig | |
let! result = | |
- WhenSome settings.ConsistencyConfig funcBuckets resultsSoFar failedFuncsSoFar cancellationSource | |
+ WhenSome consistencyConfig initialFuncCount funcBuckets resultsSoFar failedFuncsSoFar cancellationSource | |
match result with | |
| AverageResult averageResult -> | |
CancelAndDispose cancellationSource cancelledInternally | |
@@ -338,6 +392,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
else | |
return! QueryInternalImplementation | |
settings | |
+ initialFuncCount | |
failedFuncs | |
allResultsSoFar | |
List.Empty | |
@@ -351,13 +406,13 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
// HACK: we do this as a quick fix wrt new OneServerConsistentWithCacheOrTwoServers setting, but we should | |
// (TODO) rather throw a specific overload of ResultInconsistencyException about this mode being used | |
let wrappedSettings = | |
- match settings.ConsistencyConfig with | |
- | OneServerConsistentWithCacheOrTwoServers _ -> | |
- SpecificNumberOfConsistentResponsesRequired 2u | |
- | _ -> settings.ConsistencyConfig | |
+ match consistencyConfig with | |
+ | Some (OneServerConsistentWithCacheOrTwoServers _) -> | |
+ Some (SpecificNumberOfConsistentResponsesRequired 2u) | |
+ | _ -> consistencyConfig | |
match wrappedSettings with | |
- | SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired -> | |
+ | Some (SpecificNumberOfConsistentResponsesRequired numberOfConsistentResponsesRequired) -> | |
let resultsOrderedByCount = MeasureConsistency allResultsSoFar | |
match resultsOrderedByCount with | |
| [] -> | |
@@ -371,6 +426,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
else | |
return! QueryInternalImplementation | |
settings | |
+ initialFuncCount | |
funcs | |
List.Empty | |
List.Empty | |
@@ -378,7 +434,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
(retriesForInconsistency + 1u) | |
cancellationSource | |
cancelledInternally | |
- | AverageBetweenResponses(minimumNumberOfResponses,averageFunc) -> | |
+ | Some(AverageBetweenResponses(minimumNumberOfResponses,averageFunc)) -> | |
if (retries = settings.NumberOfRetries) then | |
let firstEx = failedFuncsWithTheirExceptions.First() |> snd | |
CancelAndDispose cancellationSource cancelledInternally | |
@@ -386,6 +442,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
else | |
return! QueryInternalImplementation | |
settings | |
+ initialFuncCount | |
failedFuncs | |
allResultsSoFar | |
failedFuncsWithTheirExceptions | |
@@ -398,7 +455,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
} | |
- let OrderServers (servers: List<Server<'K,'R>>) (mode: Mode): List<Server<'K,'R>> = | |
+ let SortServers (servers: List<Server<'K,'R>>) (mode: ServerSelectionMode): List<Server<'K,'R>> = | |
let workingServers = List.filter (fun server -> | |
match server.Details.CommunicationHistory with | |
| None -> | |
@@ -453,7 +510,7 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
) | |
faultyServers | |
- if mode = Mode.Fast then | |
+ if mode = ServerSelectionMode.Fast then | |
List.append sortedWorkingServers (List.append serversWithNoHistoryServers sortedFaultyServers) | |
else | |
let intersectionOffset = 3u | |
@@ -481,9 +538,17 @@ type FaultTolerantParallelClient<'K,'E when 'K: equality and 'K :> ICommunicatio | |
let cancelledInternally = MutableStateCapsule<Option<DateTime>> None | |
+ let initialServerCount = uint32 servers.Length | |
+ let maybeSortedServers = | |
+ match settings.ResultSelectionMode with | |
+ | Exhaustive -> servers | |
+ | Selective selSettings -> | |
+ SortServers servers selSettings.ServerSelectionMode | |
+ | |
QueryInternalImplementation | |
settings | |
- (OrderServers servers settings.Mode) | |
+ initialServerCount | |
+ maybeSortedServers | |
List.Empty | |
List.Empty | |
0u | |
diff --git a/src/GWallet.Backend/GWallet.Backend.fsproj b/src/GWallet.Backend/GWallet.Backend.fsproj | |
index d01bb8e..3c69b29 100644 | |
--- a/src/GWallet.Backend/GWallet.Backend.fsproj | |
+++ b/src/GWallet.Backend/GWallet.Backend.fsproj | |
@@ -60,6 +60,7 @@ <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.mic | |
<Compile Include="IBlockchainFeeInfo.fs" /> | |
<Compile Include="TransferAmount.fs" /> | |
<Compile Include="Infrastructure.fs" /> | |
+ <Compile Include="CachingTypes.fs" /> | |
<Compile Include="Server.fs" /> | |
<Compile Include="Caching.fs" /> | |
<Compile Include="Transaction.fs" /> | |
diff --git a/src/GWallet.Backend/Server.fs b/src/GWallet.Backend/Server.fs | |
index 4ef799c..48fd9e9 100644 | |
--- a/src/GWallet.Backend/Server.fs | |
+++ b/src/GWallet.Backend/Server.fs | |
@@ -29,34 +29,65 @@ type ConnectionType = | |
type ICommunicationHistory = | |
abstract member CommunicationHistory: Option<HistoryInfo> with get | |
-[<CustomEquality; NoComparison>] | |
-type ServerDetails = | |
+type HistoryFact = | |
+ { | |
+ TimeSpan: TimeSpan | |
+ Fault: Option<ExceptionInfo> | |
+ } | |
+ | |
+type ServerInfo = | |
{ | |
NetworkPath: string | |
ConnectionType: ConnectionType | |
- CommunicationHistory: Option<HistoryInfo> | |
} | |
- override self.Equals yObj = | |
+ | |
+[<CustomEquality; NoComparison>] | |
+type ServerDetails = | |
+ { | |
+ ServerInfo: ServerInfo | |
+ CommunicationHistory: Option<CachedValue<HistoryInfo>> | |
+ } | |
+ member private self.EqualsInternal (yObj: obj) = | |
match yObj with | |
| :? ServerDetails as y -> | |
- self.NetworkPath.Equals y.NetworkPath | |
+ self.ServerInfo.Equals y.ServerInfo | |
| _ -> false | |
+ override self.Equals yObj = | |
+ self.EqualsInternal yObj | |
override self.GetHashCode () = | |
- self.NetworkPath.GetHashCode() | |
+ self.ServerInfo.GetHashCode() | |
interface ICommunicationHistory with | |
- member self.CommunicationHistory with get() = self.CommunicationHistory | |
+ member self.CommunicationHistory | |
+ with get() = | |
+ match self.CommunicationHistory with | |
+ | None -> None | |
+ | Some (h,_) -> Some h | |
+ | |
+type ServerRanking = Map<Currency,seq<ServerDetails>> | |
module ServerRegistry = | |
let ServersEmbeddedResourceFileName = "servers.json" | |
- let Serialize(servers: Map<Currency,seq<ServerDetails>>): string = | |
+ let internal TryFindValue (map: ServerRanking) (serverPredicate: ServerDetails -> bool) | |
+ : Option<Currency*ServerDetails> = | |
+ let rec tryFind currencyAndServers server = | |
+ match currencyAndServers with | |
+ | [] -> None | |
+ | (currency, servers)::tail -> | |
+ match Seq.tryFind serverPredicate servers with | |
+ | None -> tryFind tail server | |
+ | Some foundServer -> Some (currency, foundServer) | |
+ let listMap = Map.toList map | |
+ tryFind listMap serverPredicate | |
+ | |
+ let internal RemoveDupes (servers: seq<ServerDetails>) = | |
let rec removeDupesInternal (servers: seq<ServerDetails>) (serversMap: Map<string,ServerDetails>) = | |
match Seq.tryHead servers with | |
| None -> Seq.empty | |
| Some server -> | |
let tail = Seq.tail servers | |
- match serversMap.TryGetValue server.NetworkPath with | |
+ match serversMap.TryGetValue server.ServerInfo.NetworkPath with | |
| false,_ -> | |
removeDupesInternal tail serversMap | |
| true,serverInMap -> | |
@@ -64,7 +95,7 @@ module ServerRegistry = | |
match server.CommunicationHistory,serverInMap.CommunicationHistory with | |
| None,_ -> serverInMap | |
| _,None -> server | |
- | Some commHistory,Some commHistoryInMap -> | |
+ | Some (commHistory,_),Some (commHistoryInMap,_) -> | |
match commHistory.Status,commHistoryInMap.Status with | |
| Fault(_,None),_ -> serverInMap | |
| _,Fault(_,None) -> server | |
@@ -76,39 +107,63 @@ module ServerRegistry = | |
server | |
else | |
serverInMap | |
- let newMap = serversMap.Remove serverToAppend.NetworkPath | |
+ let newMap = serversMap.Remove serverToAppend.ServerInfo.NetworkPath | |
Seq.append (seq { yield serverToAppend }) (removeDupesInternal tail newMap) | |
- let removeDupes (servers: seq<ServerDetails>) = | |
- removeDupesInternal servers (servers |> Seq.map (fun server -> server.NetworkPath,server) |> Map.ofSeq) | |
+ removeDupesInternal servers | |
+ (servers |> Seq.map (fun server -> server.ServerInfo.NetworkPath,server) |> Map.ofSeq) | |
+ | |
+ let internal Sort (servers: seq<ServerDetails>): seq<ServerDetails> = | |
+ Seq.sortByDescending (fun server -> | |
+ match server.CommunicationHistory with | |
+ | None -> None | |
+ | Some (history,_) -> | |
+ match history.Status with | |
+ | Fault (_,lsc) -> lsc | |
+ | LastSuccessfulCommunication lsc -> | |
+ Some lsc | |
+ ) servers | |
- let sort (servers: seq<ServerDetails>) = | |
- Seq.sortByDescending (fun server -> | |
- match server.CommunicationHistory with | |
- | None -> None | |
- | Some history -> | |
- match history.Status with | |
- | Fault (_,lsc) -> lsc | |
- | LastSuccessfulCommunication lsc -> | |
- Some lsc | |
- ) servers | |
+ let Serialize(servers: ServerRanking): string = | |
let rearrangedServers = | |
servers | |
|> Map.toSeq | |
- |> Seq.map (fun (currency, servers) -> currency, servers |> removeDupes |> sort) | |
+ |> Seq.map (fun (currency, servers) -> currency, servers |> RemoveDupes |> Sort) | |
|> Map.ofSeq | |
Marshalling.Serialize rearrangedServers | |
- let Deserialize(json: string): Map<Currency,seq<ServerDetails>> = | |
+ let Deserialize(json: string): ServerRanking = | |
Marshalling.Deserialize json | |
- let internal servers = Deserialize (Config.ExtractEmbeddedResourceFileContents ServersEmbeddedResourceFileName) | |
- | |
- let GetServers currency = | |
- match servers.TryFind currency with | |
- | Some currencyServers -> currencyServers | |
- | _ -> failwithf "No servers found in resource file for %A?" currency | |
+ let Merge (ranking1: ServerRanking) (ranking2: ServerRanking): ServerRanking = | |
+ let allKeys = | |
+ seq { | |
+ for KeyValue(key, _) in ranking1 do | |
+ yield key | |
+ for KeyValue(key, _) in ranking2 do | |
+ yield key | |
+ } |> Set.ofSeq | |
+ | |
+ seq { | |
+ for currency in allKeys do | |
+ let allServersFrom1 = | |
+ match ranking1.TryFind currency with | |
+ | None -> Seq.empty | |
+ | Some servers -> servers | |
+ let allServersFrom2 = | |
+ match ranking2.TryFind currency with | |
+ | None -> Seq.empty | |
+ | Some servers -> | |
+ servers | |
+ yield currency,((Seq.append allServersFrom1 allServersFrom2) |> RemoveDupes |> Sort) | |
+ } |> Map.ofSeq | |
+ | |
+ let private ServersRankingBaseline = | |
+ Deserialize (Config.ExtractEmbeddedResourceFileContents ServersEmbeddedResourceFileName) | |
+ | |
+ let MergeWithBaseline (ranking: ServerRanking): ServerRanking = | |
+ Merge ranking ServersRankingBaseline | |
[<CustomEquality; NoComparison>] | |
type Server<'K,'R when 'K: equality and 'K :> ICommunicationHistory> = | |
diff --git a/src/GWallet.Backend/ServerManager.fs b/src/GWallet.Backend/ServerManager.fs | |
index 060713b..a400e4d 100644 | |
--- a/src/GWallet.Backend/ServerManager.fs | |
+++ b/src/GWallet.Backend/ServerManager.fs | |
@@ -16,8 +16,11 @@ module ServerManager = | |
| None -> failwith "filtering for non-ssl electrum servers didn't work?" | |
| Some unencryptedPort -> | |
{ | |
- NetworkPath = es.Fqdn | |
- ConnectionType = { Encrypted = false; Protocol = Tcp unencryptedPort } | |
+ ServerInfo = | |
+ { | |
+ NetworkPath = es.Fqdn | |
+ ConnectionType = { Encrypted = false; Protocol = Tcp unencryptedPort } | |
+ } | |
CommunicationHistory = None | |
} | |
@@ -51,7 +54,7 @@ module ServerManager = | |
|> Seq.map fromElectrumServerToGenericServerDetails | |
|> Seq.append baseLineLtcServers | |
- for currency,servers in baseLineServers |> Map.toSeq do | |
+ for KeyValue(currency,servers) in baseLineServers do | |
Console.WriteLine (sprintf "%i %A servers from baseline JSON file" (servers.Count()) currency) | |
match currency with | |
@@ -73,5 +76,85 @@ module ServerManager = | |
Console.WriteLine "OUTPUT:" | |
let filteredOutServers = ServerRegistry.Deserialize allServersJson | |
- for currency,servers in filteredOutServers |> Map.toSeq do | |
+ for KeyValue(currency,servers) in filteredOutServers do | |
Console.WriteLine (sprintf "%i %A servers total" (servers.Count()) currency) | |
+ | |
+ let private tester = | |
+ FaultTolerantParallelClient<ServerDetails,CommunicationUnsuccessfulException> | |
+ Caching.Instance.SaveServerLastStat | |
+ | |
+ let private testingSettings = | |
+ { | |
+ NumberOfParallelJobsAllowed = 4u | |
+ NumberOfRetries = 1u | |
+ NumberOfRetriesForInconsistency = 1u | |
+ ResultSelectionMode = Exhaustive | |
+ } | |
+ | |
+ let private GetDummyBalanceAction (currency: Currency) servers = | |
+ | |
+ let retrievalFuncs = | |
+ if (currency.IsUtxo()) then | |
+ let scriptHash = | |
+ match currency with | |
+ | Currency.BTC -> | |
+ // probably a satoshi address because it was used in blockheight 2 and is unspent yet | |
+ let SATOSHI_ADDRESS = "1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1" | |
+ // funny that it almost begins with "1HoDL" | |
+ UtxoCoin.Account.GetElectrumScriptHashFromPublicAddress currency SATOSHI_ADDRESS | |
+ | Currency.LTC -> | |
+ // https://medium.com/@SatoshiLite/satoshilite-1e2dad89a017 | |
+ let LTC_GENESIS_BLOCK_ADDRESS = "Ler4HNAEfwYhBmGXcFP2Po1NpRUEiK8km2" | |
+ UtxoCoin.Account.GetElectrumScriptHashFromPublicAddress currency LTC_GENESIS_BLOCK_ADDRESS | |
+ | _ -> | |
+ failwithf "Currency %A not UTXO?" currency | |
+ let utxoFunc electrumServer = | |
+ async { | |
+ let! bal = UtxoCoin.ElectrumClient.GetBalance scriptHash electrumServer | |
+ return bal.Confirmed |> decimal | |
+ } | |
+ UtxoCoin.Account.GetServerFuncs utxoFunc servers |> Some | |
+ | |
+ elif currency.IsEther() then | |
+ let ETH_GENESISBLOCK_ADDRESS = "0x0000000000000000000000000000000000000000" | |
+ | |
+ let web3Func (web3: Ether.SomeWeb3): Async<decimal> = | |
+ async { | |
+ let! balance = Async.AwaitTask (web3.Eth.GetBalance.SendRequestAsync ETH_GENESISBLOCK_ADDRESS) | |
+ return balance.Value |> decimal | |
+ } | |
+ | |
+ Ether.Server.GetServerFuncs web3Func servers |> Some | |
+ | |
+ else | |
+ None | |
+ | |
+ match retrievalFuncs with | |
+ | Some queryFuncs -> | |
+ async { | |
+ try | |
+ let! _ = tester.Query testingSettings | |
+ (queryFuncs |> List.ofSeq) | |
+ return () | |
+ with | |
+ | :? NoneAvailableException -> | |
+ return () | |
+ } |> Some | |
+ | _ -> | |
+ None | |
+ | |
+ | |
+ let UpdateServersStats () = | |
+ let jobs = seq { | |
+ for currency in Currency.GetAll() do | |
+ | |
+ // because ETH tokens use ETH servers | |
+ if not (currency.IsEthToken()) then | |
+ let serversForSpecificCurrency = Caching.Instance.GetServers currency | |
+ match GetDummyBalanceAction currency serversForSpecificCurrency with | |
+ | None -> () | |
+ | Some job -> yield job | |
+ } | |
+ Async.Parallel jobs | |
+ |> Async.RunSynchronously | |
+ |> ignore | |
diff --git a/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs b/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs | |
index 2534d63..110f262 100644 | |
--- a/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs | |
+++ b/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs | |
@@ -44,11 +44,11 @@ module ElectrumClient = | |
} | |
let StratumServer (electrumServer: ServerDetails): Async<StratumClient> = | |
- match electrumServer.ConnectionType with | |
+ match electrumServer.ServerInfo.ConnectionType with | |
| { Encrypted = true; Protocol = _ } -> failwith "Incompatibility filter for non-encryption didn't work?" | |
| { Encrypted = false; Protocol = Http } -> failwith "HTTP server for UtxoCoin?" | |
| { Encrypted = false; Protocol = Tcp port } -> | |
- Init electrumServer.NetworkPath port | |
+ Init electrumServer.ServerInfo.NetworkPath port | |
let GetBalance (scriptHash: string) (stratumServer: Async<StratumClient>) = async { | |
// FIXME: we should rather implement this method in terms of: | |
diff --git a/src/GWallet.Backend/UtxoCoin/ElectrumServer.fs b/src/GWallet.Backend/UtxoCoin/ElectrumServer.fs | |
index 0b8eb92..64c40c6 100644 | |
--- a/src/GWallet.Backend/UtxoCoin/ElectrumServer.fs | |
+++ b/src/GWallet.Backend/UtxoCoin/ElectrumServer.fs | |
@@ -145,11 +145,11 @@ module ElectrumServerSeedList = | |
|> Seq.filter FilterCompatibleServer | |
let DefaultBtcList = | |
- ServerRegistry.GetServers Currency.BTC | |
+ Caching.Instance.GetServers Currency.BTC | |
|> List.ofSeq | |
let DefaultLtcList = | |
- ServerRegistry.GetServers Currency.LTC | |
+ Caching.Instance.GetServers Currency.LTC | |
|> List.ofSeq | |
let Randomize currency = | |
diff --git a/src/GWallet.Backend/UtxoCoin/UtxoCoinAccount.fs b/src/GWallet.Backend/UtxoCoin/UtxoCoinAccount.fs | |
index 6e1dbb4..b6a3917 100644 | |
--- a/src/GWallet.Backend/UtxoCoin/UtxoCoinAccount.fs | |
+++ b/src/GWallet.Backend/UtxoCoin/UtxoCoinAccount.fs | |
@@ -54,35 +54,41 @@ module Account = | |
let private NumberOfParallelJobsForMode mode = | |
match mode with | |
- | Mode.Fast -> 8u | |
- | Mode.Analysis -> 5u | |
+ | ServerSelectionMode.Fast -> 8u | |
+ | ServerSelectionMode.Analysis -> 5u | |
+ | |
+ let private FaultTolerantParallelClientDefaultSettings (mode: ServerSelectionMode) | |
+ maybeConsistencyConfig = | |
+ let consistencyConfig = | |
+ match maybeConsistencyConfig with | |
+ | None -> SpecificNumberOfConsistentResponsesRequired 2u | |
+ | Some specificConsistencyConfig -> specificConsistencyConfig | |
- let private FaultTolerantParallelClientDefaultSettings(mode: Mode) = | |
{ | |
NumberOfParallelJobsAllowed = NumberOfParallelJobsForMode mode | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired 2u; | |
NumberOfRetries = Config.NUMBER_OF_RETRIES_TO_SAME_SERVERS; | |
NumberOfRetriesForInconsistency = Config.NUMBER_OF_RETRIES_TO_SAME_SERVERS; | |
- Mode = mode | |
- ShouldReportUncancelledJobs = (not Config.NewUtxoTcpClientDisabled) | |
+ ResultSelectionMode = | |
+ Selective | |
+ { | |
+ ServerSelectionMode = mode | |
+ ConsistencyConfig = consistencyConfig | |
+ ReportUncancelledJobs = (not Config.NewUtxoTcpClientDisabled) | |
+ } | |
} | |
let private FaultTolerantParallelClientSettingsForBroadcast() = | |
- { | |
- FaultTolerantParallelClientDefaultSettings Mode.Fast with | |
- ConsistencyConfig = SpecificNumberOfConsistentResponsesRequired 1u; | |
- } | |
+ FaultTolerantParallelClientDefaultSettings ServerSelectionMode.Fast | |
+ (Some (SpecificNumberOfConsistentResponsesRequired 1u)) | |
- let private FaultTolerantParallelClientSettingsForBalanceCheck (mode: Mode) | |
+ let private FaultTolerantParallelClientSettingsForBalanceCheck (mode: ServerSelectionMode) | |
cacheMatchFunc = | |
- let defaultSettings = FaultTolerantParallelClientDefaultSettings mode | |
- if mode = Mode.Fast then | |
- { | |
- defaultSettings with | |
- ConsistencyConfig = OneServerConsistentWithCacheOrTwoServers cacheMatchFunc | |
- } | |
- else | |
- defaultSettings | |
+ let consistencyConfig = | |
+ if mode = ServerSelectionMode.Fast then | |
+ Some (OneServerConsistentWithCacheOrTwoServers cacheMatchFunc) | |
+ else | |
+ None | |
+ FaultTolerantParallelClientDefaultSettings mode consistencyConfig | |
let private faultTolerantElectrumClient = | |
FaultTolerantParallelClient<ServerDetails,ServerDiscardedException> Caching.Instance.SaveServerLastStat | |
@@ -122,12 +128,11 @@ module Account = | |
let privateKey = Key.Parse(privateKey, GetNetwork currency) | |
GetPublicAddressFromPublicKey currency privateKey.PubKey | |
- // FIXME: there should be a way to simplify this function to not need to pass a new ad-hoc delegate | |
- // (maybe make it more similar to old EtherServer.fs' PlumbingCall() in stable branch[1]?) | |
- // [1] https://gitlab.com/knocte/geewallet/blob/stable/src/GWallet.Backend/EtherServer.fs | |
- let private GetRandomizedFuncs<'R> (currency: Currency) | |
- (electrumClientFunc: Async<StratumClient>->Async<'R>) | |
- : List<Server<ServerDetails,'R>> = | |
+ // FIXME: seems there's some code duplication between this function and EtherServer.fs's GetServerFuncs function | |
+ // and room for simplification to not pass a new ad-hoc delegate? | |
+ let GetServerFuncs<'R> (electrumClientFunc: Async<StratumClient>->Async<'R>) | |
+ (electrumServers: seq<ServerDetails>) | |
+ : seq<Server<ServerDetails,'R>> = | |
let ElectrumServerToRetrievalFunc (server: ServerDetails) | |
(electrumClientFunc: Async<StratumClient>->Async<'R>) | |
@@ -142,27 +147,26 @@ module Account = | |
let msg = sprintf "%s: %s" (ex.GetType().FullName) ex.Message | |
return raise <| ServerDiscardedException(msg, ex) | |
| ex -> | |
- return raise <| Exception(sprintf "Some problem when connecting to %s" server.NetworkPath, ex) | |
+ return raise <| Exception(sprintf "Some problem when connecting to %s" server.ServerInfo.NetworkPath, ex) | |
} | |
- | |
let ElectrumServerToGenericServer (electrumClientFunc: Async<StratumClient>->Async<'R>) | |
(electrumServer: ServerDetails) | |
: Server<ServerDetails,'R> = | |
- let lastDetailsForServer = | |
- match Caching.Instance.RetreiveLastServerHistory electrumServer.NetworkPath with | |
- | None -> electrumServer | |
- | Some historyInCache -> | |
- { electrumServer with | |
- CommunicationHistory = Some historyInCache } | |
- | |
- { Details = lastDetailsForServer | |
- Retrieval = ElectrumServerToRetrievalFunc lastDetailsForServer electrumClientFunc } | |
- | |
- let randomizedElectrumServers = ElectrumServerSeedList.Randomize currency |> List.ofSeq | |
- let randomizedServers = | |
- List.map (ElectrumServerToGenericServer electrumClientFunc) | |
- randomizedElectrumServers | |
- randomizedServers | |
+ { Details = electrumServer | |
+ Retrieval = ElectrumServerToRetrievalFunc electrumServer electrumClientFunc } | |
+ | |
+ let serverFuncs = | |
+ Seq.map (ElectrumServerToGenericServer electrumClientFunc) | |
+ electrumServers | |
+ serverFuncs | |
+ | |
+ let private GetRandomizedFuncs<'R> (currency: Currency) | |
+ (electrumClientFunc: Async<StratumClient>->Async<'R>) | |
+ : List<Server<ServerDetails,'R>> = | |
+ | |
+ let electrumServers = ElectrumServerSeedList.Randomize currency | |
+ GetServerFuncs electrumClientFunc electrumServers | |
+ |> List.ofSeq | |
let private BalanceToShow (balances: BlockchainScripthahsGetBalanceInnerResult) = | |
let unconfirmedPlusConfirmed = balances.Unconfirmed + balances.Confirmed | |
@@ -180,7 +184,9 @@ module Account = | |
| Some balance -> | |
BalanceToShow someBalancesRetreived = balance | |
- let private GetBalances (account: IUtxoAccount) (mode: Mode) (cancelSourceOption: Option<CancellationTokenSource>) | |
+ let private GetBalances (account: IUtxoAccount) | |
+ (mode: ServerSelectionMode) | |
+ (cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<BlockchainScripthahsGetBalanceInnerResult> = | |
let scriptHashHex = GetElectrumScriptHashFromPublicAddress account.Currency account.PublicAddress | |
@@ -198,7 +204,7 @@ module Account = | |
balanceJob | |
let private GetBalancesFromServer (account: IUtxoAccount) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<Option<BlockchainScripthahsGetBalanceInnerResult>> = | |
async { | |
@@ -214,7 +220,7 @@ module Account = | |
} | |
let internal GetShowableBalance (account: IUtxoAccount) | |
- (mode: Mode) | |
+ (mode: ServerSelectionMode) | |
(cancelSourceOption: Option<CancellationTokenSource>) | |
: Async<Option<decimal>> = | |
async { | |
@@ -303,7 +309,7 @@ module Account = | |
|> ElectrumClient.GetUnspentTransactionOutputs | |
let! utxos = | |
faultTolerantElectrumClient.Query | |
- (FaultTolerantParallelClientDefaultSettings Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings ServerSelectionMode.Fast None) | |
(GetRandomizedFuncs account.Currency job) | |
if not (utxos.Any()) then | |
@@ -328,7 +334,7 @@ module Account = | |
let job = ElectrumClient.GetBlockchainTransaction utxo.TransactionId | |
let! transRaw = | |
faultTolerantElectrumClient.Query | |
- (FaultTolerantParallelClientDefaultSettings Mode.Fast) | |
+ (FaultTolerantParallelClientDefaultSettings ServerSelectionMode.Fast None) | |
(GetRandomizedFuncs account.Currency job) | |
let transaction = Transaction.Parse(transRaw, GetNetwork amount.Currency) | |
let txOut = transaction.Outputs.[utxo.OutputIndex] | |
@@ -359,8 +365,10 @@ module Account = | |
let! btcPerKiloByteForFastTrans = | |
faultTolerantElectrumClient.Query | |
- { FaultTolerantParallelClientDefaultSettings Mode.Fast with | |
- ConsistencyConfig = AverageBetweenResponses (minResponsesRequired, averageFee) } | |
+ (FaultTolerantParallelClientDefaultSettings | |
+ ServerSelectionMode.Fast | |
+ (Some (AverageBetweenResponses (minResponsesRequired, averageFee)))) | |
+ | |
(GetRandomizedFuncs account.Currency estimateFeeJob) | |
let feeRate = | |
diff --git a/src/GWallet.Backend/servers.json b/src/GWallet.Backend/servers.json | |
index ca0ce71..f362022 100644 | |
--- a/src/GWallet.Backend/servers.json | |
+++ b/src/GWallet.Backend/servers.json | |
@@ -4,794 +4,916 @@ | |
"Value": { | |
"BTC": [ | |
{ | |
- "NetworkPath": "E-X.not.fyi", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "E-X.not.fyi", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "VPS.hsmiths.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "VPS.hsmiths.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "b.ooze.cc", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "b.ooze.cc", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "bitcoin.corgi.party", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "bitcoin.corgi.party", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "bitcoins.sk", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "bitcoins.sk", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "btc.cihar.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "btc.cihar.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "btc.xskyx.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "btc.xskyx.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "currentlane.lovebitco.in", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "currentlane.lovebitco.in", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "daedalus.bauerj.eu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "daedalus.bauerj.eu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.jochen-hoenicke.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.jochen-hoenicke.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "e-1.claudioboxx.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "e-1.claudioboxx.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "e.keff.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "e.keff.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum-server.ninja", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum-server.ninja", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.eff.ro", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.eff.ro", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.festivaldelhumor.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.festivaldelhumor.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.hsmiths.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.hsmiths.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.leblancnet.us", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.leblancnet.us", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.qtornado.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.qtornado.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.villocq.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.villocq.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum2.eff.ro", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum2.eff.ro", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum2.villocq.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum2.villocq.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.bot.nu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.bot.nu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.ddns.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.ddns.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.ml", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.ml", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.soon.it", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.soon.it", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "elx01.knas.systems", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "elx01.knas.systems", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "enode.duckdns.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "enode.duckdns.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "fedaykin.goip.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "fedaykin.goip.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "fn.48.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "fn.48.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "helicarrier.bauerj.eu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "helicarrier.bauerj.eu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "icarus.tetradrachm.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "icarus.tetradrachm.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.emzy.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.emzy.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ndnd.selfhost.eu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "ndnd.selfhost.eu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "orannis.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "orannis.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "tardis.bauerj.eu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "tardis.bauerj.eu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "tomscryptos.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "tomscryptos.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ulrichard.ch", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "ulrichard.ch", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "vmd27610.contaboserver.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "vmd27610.contaboserver.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "vmd30612.contaboserver.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "vmd30612.contaboserver.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "yuio.top", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "yuio.top", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "bitcoin.dragon.zone", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "bitcoin.dragon.zone", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ecdsa.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "ecdsa.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "e2.keff.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "e2.keff.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "fortress.qtornado.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "fortress.qtornado.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "green-gold.westeurope.cloudapp.azure.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 56001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "green-gold.westeurope.cloudapp.azure.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 56001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.erbium.eu", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.erbium.eu", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "btc.jochen-hoenicke.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "btc.jochen-hoenicke.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "dedi.jochen-hoenicke.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "dedi.jochen-hoenicke.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx-core.1209k.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx-core.1209k.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "b6.1209k.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "b6.1209k.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "b.1209k.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "b.1209k.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "185.64.116.15", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "185.64.116.15", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "kirsche.emzy.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "kirsche.emzy.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "52.1.56.181", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "52.1.56.181", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.coineuskal.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.coineuskal.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "cashyes.zapto.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "cashyes.zapto.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum3.hachre.de", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum3.hachre.de", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.nmdps.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.nmdps.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "erbium1.sytes.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "erbium1.sytes.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "oneweek.duckdns.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "oneweek.duckdns.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "us.electrum.be", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "us.electrum.be", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
@@ -799,157 +921,181 @@ | |
], | |
"LTC": [ | |
{ | |
- "NetworkPath": "backup.electrum-ltc.org", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "backup.electrum-ltc.org", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "e-1.claudioboxx.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "e-1.claudioboxx.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "e-3.claudioboxx.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "e-3.claudioboxx.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum-ltc.bysh.me", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum-ltc.bysh.me", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum-ltc.villocq.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 60001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum-ltc.villocq.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 60001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum-ltc.wilv.in", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum-ltc.wilv.in", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.leblancnet.us", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.leblancnet.us", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrum.ltc.xurious.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrum.ltc.xurious.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ltc01.knas.systems", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "ltc01.knas.systems", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "node.ispol.sk", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50003 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "node.ispol.sk", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50003 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ltc.rentonisk.com", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 50001 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "ltc.rentonisk.com", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 50001 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "electrumx.nmdps.net", | |
- "ConnectionType": { | |
- "Encrypted": false, | |
- "Protocol": { | |
- "Case": "Tcp", | |
- "Fields": [ | |
- 9433 | |
- ] | |
+ "ServerInfo": { | |
+ "NetworkPath": "electrumx.nmdps.net", | |
+ "ConnectionType": { | |
+ "Encrypted": false, | |
+ "Protocol": { | |
+ "Case": "Tcp", | |
+ "Fields": [ | |
+ 9433 | |
+ ] | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
@@ -957,91 +1103,109 @@ | |
], | |
"ETH": [ | |
{ | |
- "NetworkPath": "cloudflare-eth.com/", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "cloudflare-eth.com/", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "eth-mainnet.alchemyapi.io/jsonrpc/-vPGIFwUyjlMRF9beTLXiGQUK6Nf3k8z", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "eth-mainnet.alchemyapi.io/jsonrpc/-vPGIFwUyjlMRF9beTLXiGQUK6Nf3k8z", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "o70075sme1.execute-api.us-east-1.amazonaws.com/latest/eth", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "o70075sme1.execute-api.us-east-1.amazonaws.com/latest/eth", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "mainnet.infura.io/mew", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "mainnet.infura.io/mew", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "api.dev.blockscale.net/dev/parity", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "api.dev.blockscale.net/dev/parity", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "mew.giveth.io", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "mew.giveth.io", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "api.myetherapi.com/eth", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "api.myetherapi.com/eth", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "mainnet.infura.io/v3/c02fff6b5daa434d8422b8ece54c7286", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "mainnet.infura.io/v3/c02fff6b5daa434d8422b8ece54c7286", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "mainnet.infura.io/mycrypto", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "mainnet.infura.io/mycrypto", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
@@ -1049,101 +1213,121 @@ | |
], | |
"ETC": [ | |
{ | |
- "NetworkPath": "www.ethercluster.com/etc", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "www.ethercluster.com/etc", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "ethereumclassic.network", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "ethereumclassic.network", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "web3.gastracker.io", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "web3.gastracker.io", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "node.classicexplorer.org/", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "node.classicexplorer.org/", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "etc-parity.callisto.network", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "etc-parity.callisto.network", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "etcrpc.viperid.online", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "etcrpc.viperid.online", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "etc-parity.0xinfra.com", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "etc-parity.0xinfra.com", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "etc-geth.0xinfra.com", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "etc-geth.0xinfra.com", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "mewapi.epool.io", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "mewapi.epool.io", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
}, | |
{ | |
- "NetworkPath": "cry.epool.io", | |
- "ConnectionType": { | |
- "Encrypted": true, | |
- "Protocol": { | |
- "Case": "Http" | |
+ "ServerInfo": { | |
+ "NetworkPath": "cry.epool.io", | |
+ "ConnectionType": { | |
+ "Encrypted": true, | |
+ "Protocol": { | |
+ "Case": "Http" | |
+ } | |
} | |
}, | |
"CommunicationHistory": null | |
diff --git a/src/GWallet.Frontend.Console/Program.fs b/src/GWallet.Frontend.Console/Program.fs | |
index a7b010c..6e0f2f8 100644 | |
--- a/src/GWallet.Frontend.Console/Program.fs | |
+++ b/src/GWallet.Frontend.Console/Program.fs | |
@@ -178,7 +178,7 @@ let ArchiveAccount() = | |
UserInteraction.PressAnyKeyToContinue() | |
| :? NormalAccount as normalAccount -> | |
let balance = | |
- Account.GetShowableBalance account Mode.Fast None | |
+ Account.GetShowableBalance account ServerSelectionMode.Fast None | |
|> Async.RunSynchronously | |
match balance with | |
| NotFresh(NotAvailable) -> | |
@@ -311,16 +311,22 @@ let NormalStartWithNoParameters () = | |
exitCode | |
-let UpdateServers () = | |
+let UpdateServersFile () = | |
ServerManager.UpdateServersFile() | |
0 | |
+let UpdateServersStats () = | |
+ ServerManager.UpdateServersStats() | |
+ 0 | |
+ | |
[<EntryPoint>] | |
let main argv = | |
match argv.Length with | |
| 0 -> | |
NormalStartWithNoParameters() | |
- | 1 when argv.[0] = "--update-servers" -> | |
- UpdateServers() | |
+ | 1 when argv.[0] = "--update-servers-file" -> | |
+ UpdateServersFile() | |
+ | 1 when argv.[0] = "--update-servers-stats" -> | |
+ UpdateServersStats() | |
| _ -> | |
failwith "Arguments not recognized" | |
diff --git a/src/GWallet.Frontend.Console/UserInteraction.fs b/src/GWallet.Frontend.Console/UserInteraction.fs | |
index 4f285f8..8d4a3a7 100644 | |
--- a/src/GWallet.Frontend.Console/UserInteraction.fs | |
+++ b/src/GWallet.Frontend.Console/UserInteraction.fs | |
@@ -215,7 +215,7 @@ module UserInteraction = | |
// opposed to the other frontends) because it doesn't have automatic balance refresh (it's this | |
// operation the one that should only use Analysis mode). If we used Mode.Fast here, then the console | |
// frontend would never re-discover slow/failing servers or even ones with no history | |
- let mode = Mode.Analysis | |
+ let mode = ServerSelectionMode.Analysis | |
let! balance = Account.GetShowableBalance account mode None | |
return (account,balance) | |
@@ -488,7 +488,7 @@ module UserInteraction = | |
AskParticularAmountOption currentBalance amountOption | |
let showableBalance = | |
- Account.GetShowableBalance account Mode.Fast None | |
+ Account.GetShowableBalance account ServerSelectionMode.Fast None | |
|> Async.RunSynchronously | |
match showableBalance with | |
-- | |
2.21.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment