Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knocte/64be73089a73f36a6734f37d862b4a09 to your computer and use it in GitHub Desktop.
Save knocte/64be73089a73f36a6734f37d862b4a09 to your computer and use it in GitHub Desktop.
From bbcbfd03efe82fcd6b1c834ce2c1f1699a3a8cf1 Mon Sep 17 00:00:00 2001
From: "Andres G. Aragoneses" <knocte@gmail.com>
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