Skip to content

Commit

Permalink
Backend(UtxoCoin): fix deserialization error
Browse files Browse the repository at this point in the history
This commit fixes the deserialization error happening
when returned error is a simple string instead of an object
containing errorCode and errorMsg.

The error originally happened when I passed uint256
as txId to GetBlockchainTransaction electrum request
when it should've been a string.

Co-authored-by: Mersho <[email protected]>
  • Loading branch information
aarani and Mersho committed Oct 19, 2023
1 parent 3ca8098 commit 7d91cb9
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 23 deletions.
14 changes: 7 additions & 7 deletions src/GWallet.Backend.Tests/StratumParsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ type StratumParsing() =
StratumClient.Deserialize<BlockchainScriptHashGetBalanceResult> errorResponse
|> ignore<BlockchainScriptHashGetBalanceResult>
)
Assert.That(ex.ErrorCode, Is.EqualTo(-32603))
Assert.That(ex.ErrorCode, Is.EqualTo(Some -32603))

[<Test>]
member __.``can deserialize error result with string error``() =
Assert.Throws<ElectrumServerReturningErrorInJsonResponseException> (
fun () ->
StratumClient.Deserialize<BlockchainTransactionGetResult> """{"error":"bad tx_hash","id":0,"jsonrpc":"2.0"}"""
|> ignore
)
|> ignore
let ex = Assert.Throws<ElectrumServerReturningErrorInJsonResponseException> (fun () ->
StratumClient.Deserialize<BlockchainTransactionGetResult> """{"error":"bad tx_hash","id":0,"jsonrpc":"2.0"}"""
|> ignore<BlockchainTransactionGetResult>
)

Assert.That(ex.ErrorCode, Is.EqualTo None)
2 changes: 1 addition & 1 deletion src/GWallet.Backend/UtxoCoin/ElectrumClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 39 additions & 15 deletions src/GWallet.Backend/UtxoCoin/StratumClient.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace GWallet.Backend.UtxoCoin

open System
open System.ComponentModel

open Newtonsoft.Json

Expand Down Expand Up @@ -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
Expand All @@ -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<int>) =
inherit CommunicationUnsuccessfulException(message)

member val ErrorCode: int =
member val ErrorCode: Option<int> =
code with get

type public ElectrumServerReturningErrorException(message: string, code: int,
type public ElectrumServerReturningErrorException(message: string, code: Option<int>,
originalRequest: string, originalResponse: string) =
inherit ElectrumServerReturningErrorInJsonResponseException(message, code)

Expand All @@ -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<int>,
originalRequest: string, originalResponse: string) =
inherit ElectrumServerReturningErrorException(message, code, originalRequest, originalResponse)

Expand All @@ -137,31 +144,48 @@ 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))
}

static member private DeserializeInternal<'T> (result: string): 'T =
let resultTrimmed = result.Trim()
let maybeError =

let maybeError: Choice<ErrorResult, ErrorResultWithStringError> =
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<ErrorResult>(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<ErrorResultWithStringError>(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 =
Expand Down

0 comments on commit 7d91cb9

Please sign in to comment.