diff --git a/src/GWallet.Backend.Tests/StratumParsing.fs b/src/GWallet.Backend.Tests/StratumParsing.fs index b8794b676..1163fa5cf 100644 --- a/src/GWallet.Backend.Tests/StratumParsing.fs +++ b/src/GWallet.Backend.Tests/StratumParsing.fs @@ -41,13 +41,13 @@ type StratumParsing() = StratumClient.Deserialize errorResponse |> ignore ) - Assert.That(ex.ErrorCode, Is.EqualTo(-32603)) + Assert.That(ex.ErrorCode, Is.EqualTo(Some -32603)) [] member __.``can deserialize error result with string error``() = - Assert.Throws ( - fun () -> - StratumClient.Deserialize """{"error":"bad tx_hash","id":0,"jsonrpc":"2.0"}""" - |> ignore - ) - |> ignore + let ex = Assert.Throws (fun () -> + StratumClient.Deserialize """{"error":"bad tx_hash","id":0,"jsonrpc":"2.0"}""" + |> ignore + ) + + Assert.That(ex.ErrorCode, Is.EqualTo None) diff --git a/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs b/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs index 14bca3c32..594725962 100644 --- a/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs +++ b/src/GWallet.Backend/UtxoCoin/ElectrumClient.fs @@ -29,7 +29,7 @@ module ElectrumClient = stratumClient.ServerVersion CLIENT_NAME_SENT_TO_STRATUM_SERVER_WHEN_HELLO PROTOCOL_VERSION_SUPPORTED with | :? ElectrumServerReturningErrorException as ex -> - if (ex.ErrorCode = 1 && ex.Message.StartsWith "unsupported protocol version" && + if (ex.ErrorCode = Some 1 && ex.Message.StartsWith "unsupported protocol version" && ex.Message.EndsWith (PROTOCOL_VERSION_SUPPORTED.ToString())) then // FIXME: even if this ex is already handled to ignore the server, we should report to sentry as WARN diff --git a/src/GWallet.Backend/UtxoCoin/StratumClient.fs b/src/GWallet.Backend/UtxoCoin/StratumClient.fs index fa0ec3b65..643da0812 100644 --- a/src/GWallet.Backend/UtxoCoin/StratumClient.fs +++ b/src/GWallet.Backend/UtxoCoin/StratumClient.fs @@ -1,6 +1,7 @@ namespace GWallet.Backend.UtxoCoin open System +open System.ComponentModel open Newtonsoft.Json @@ -81,6 +82,12 @@ type ErrorResult = Id: int; Error: ErrorInnerResult; } + +type ErrorResultWithStringError = + { + Id: int + Error: string + } type RpcErrorCode = // see https://gitlab.com/nblockchain/geewallet/issues/110 @@ -98,13 +105,13 @@ type RpcErrorCode = type public ElectrumServerReturningImproperJsonResponseException(message: string, innerEx: Exception) = inherit ServerMisconfiguredException (message, innerEx) -type public ElectrumServerReturningErrorInJsonResponseException(message: string, code: int) = +type public ElectrumServerReturningErrorInJsonResponseException(message: string, code: Option) = inherit CommunicationUnsuccessfulException(message) - member val ErrorCode: int = + member val ErrorCode: Option = code with get -type public ElectrumServerReturningErrorException(message: string, code: int, +type public ElectrumServerReturningErrorException(message: string, code: Option, originalRequest: string, originalResponse: string) = inherit ElectrumServerReturningErrorInJsonResponseException(message, code) @@ -114,7 +121,7 @@ type public ElectrumServerReturningErrorException(message: string, code: int, member val OriginalResponse: string = originalResponse with get -type public ElectrumServerReturningInternalErrorException(message: string, code: int, +type public ElectrumServerReturningInternalErrorException(message: string, code: Option, originalRequest: string, originalResponse: string) = inherit ElectrumServerReturningErrorException(message, code, originalRequest, originalResponse) @@ -137,13 +144,13 @@ type StratumClient (jsonRpcClient: JsonRpcTcpClient) = return (StratumClient.Deserialize<'R> rawResponse, rawResponse) with | :? ElectrumServerReturningErrorInJsonResponseException as ex -> - if ex.ErrorCode = int RpcErrorCode.InternalError then + if ex.ErrorCode = (RpcErrorCode.InternalError |> int |> Some) then return raise(ElectrumServerReturningInternalErrorException(ex.Message, ex.ErrorCode, jsonRequest, rawResponse)) - if ex.ErrorCode = int RpcErrorCode.UnknownMethod then + if ex.ErrorCode = (RpcErrorCode.UnknownMethod |> int |> Some) then return raise <| ServerMisconfiguredException(ex.Message, ex) - if ex.ErrorCode = int RpcErrorCode.ServerBusy then + if ex.ErrorCode = (RpcErrorCode.ServerBusy |> int |> Some) then return raise <| ServerUnavailabilityException(ex.Message, ex) - if ex.ErrorCode = int RpcErrorCode.ExcessiveResourceUsage then + if ex.ErrorCode = (RpcErrorCode.ExcessiveResourceUsage |> int |> Some) then return raise <| ServerUnavailabilityException(ex.Message, ex) return raise(ElectrumServerReturningErrorException(ex.Message, ex.ErrorCode, jsonRequest, rawResponse)) @@ -151,17 +158,34 @@ type StratumClient (jsonRpcClient: JsonRpcTcpClient) = static member private DeserializeInternal<'T> (result: string): 'T = let resultTrimmed = result.Trim() - let maybeError = + + let maybeError: Choice = + let raiseDeserializationError (ex: Exception) = + raise <| Exception(SPrintF2 "Failed deserializing JSON response (to check for error) '%s' to type '%s'" + resultTrimmed typedefof<'T>.FullName, ex) try JsonConvert.DeserializeObject(resultTrimmed, Marshalling.PascalCase2LowercasePlusUnderscoreConversionSettings) + |> Choice1Of2 with - | ex -> raise <| Exception(SPrintF2 "Failed deserializing JSON response (to check for error) '%s' to type '%s'" - resultTrimmed typedefof<'T>.FullName, ex) - - if (not (Object.ReferenceEquals(maybeError, null))) && (not (Object.ReferenceEquals(maybeError.Error, null))) then - raise(ElectrumServerReturningErrorInJsonResponseException(maybeError.Error.Message, maybeError.Error.Code)) - + | :? JsonSerializationException -> + try + JsonConvert.DeserializeObject(resultTrimmed, + Marshalling.PascalCase2LowercasePlusUnderscoreConversionSettings) + |> Choice2Of2 + with + | ex -> + raiseDeserializationError ex + | ex -> + raiseDeserializationError ex + + match maybeError with + | Choice1Of2 errorResult when (not (Object.ReferenceEquals(errorResult, null))) && (not (Object.ReferenceEquals(errorResult.Error, null))) -> + raise <| ElectrumServerReturningErrorInJsonResponseException(errorResult.Error.Message, Some errorResult.Error.Code) + | Choice2Of2 errorResultWithStringError when (not (Object.ReferenceEquals(errorResultWithStringError, null))) && (not (String.IsNullOrWhiteSpace errorResultWithStringError.Error)) -> + raise <| ElectrumServerReturningErrorInJsonResponseException(errorResultWithStringError.Error, None) + | _ -> () + let failedDeserMsg = SPrintF2 "Failed deserializing JSON response '%s' to type '%s'" resultTrimmed typedefof<'T>.FullName let deserializedValue =