From 5750431fd808d9fe0c58be5435b9846e7eaf7ed7 Mon Sep 17 00:00:00 2001 From: dreinix Date: Thu, 16 May 2024 13:04:39 -0400 Subject: [PATCH] Return status code in the relay --- provider/provider.go | 31 +++++++-------- provider/provider_test.go | 44 ++++++++++----------- provider/relay.go | 11 +----- relayer/relayer.go | 19 +++++----- relayer/relayer_test.go | 80 +++++++++++++++++++-------------------- 5 files changed, 90 insertions(+), 95 deletions(-) diff --git a/provider/provider.go b/provider/provider.go index d39f03f..a6dd59b 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -53,7 +53,7 @@ var ( const ( // 202 means the response was accepted but we don't know if it actually succeeded - defaultStatusCode = 202 + DefaultStatusCode = 202 // Use this contante to avoid the use of the hardcoded string result // result is the field present in a successful response resultText = "result" @@ -824,29 +824,30 @@ func (p *Provider) DispatchWithCtx(ctx context.Context, appPublicKey, chain stri } // Relay does request to be relayed to a target blockchain -func (p *Provider) Relay(rpcURL string, input *RelayInput, options *RelayRequestOptions) (*RelayOutput, *RelayOutputErr) { +func (p *Provider) Relay(rpcURL string, input *RelayInput, options *RelayRequestOptions) (*RelayOutput, int, error) { return p.RelayWithCtx(context.Background(), rpcURL, input, options) } // RelayWithCtx does request to be relayed to a target blockchain -func (p *Provider) RelayWithCtx(ctx context.Context, rpcURL string, input *RelayInput, options *RelayRequestOptions) (*RelayOutput, *RelayOutputErr) { +func (p *Provider) RelayWithCtx(ctx context.Context, rpcURL string, input *RelayInput, options *RelayRequestOptions) (*RelayOutput, int, error) { rawOutput, reqErr := p.doPostRequest(ctx, rpcURL, input, ClientRelayRoute, http.Header{}) defer closeOrLog(rawOutput) + // this is the status from the request, if the request fail this will be returned as the final status statusCode := extractStatusFromRequest(rawOutput, reqErr) if reqErr != nil && !errors.Is(reqErr, errOnRelayRequest) { - return nil, &RelayOutputErr{Error: reqErr, StatusCode: statusCode} + return nil, statusCode, reqErr } bodyBytes, err := io.ReadAll(rawOutput.Body) if err != nil { - return nil, &RelayOutputErr{Error: err, StatusCode: statusCode} + return nil, statusCode, err } if errors.Is(reqErr, errOnRelayRequest) { - return nil, &RelayOutputErr{Error: parseRelayErrorOutput(bodyBytes, input.Proof.ServicerPubKey), StatusCode: statusCode} + return nil, statusCode, parseRelayErrorOutput(bodyBytes, input.Proof.ServicerPubKey) } // The statusCode will be overwritten based on the response @@ -854,7 +855,7 @@ func (p *Provider) RelayWithCtx(ctx context.Context, rpcURL string, input *Relay } func extractStatusFromRequest(rawOutput *http.Response, reqErr error) int { - statusCode := defaultStatusCode + statusCode := DefaultStatusCode if reqErr != nil { for key, status := range errorStatusCodesMap { @@ -889,30 +890,30 @@ func extractStatusFromResponse(response string) int { return code } } - return defaultStatusCode + return DefaultStatusCode } -func parseRelaySuccesfulOutput(bodyBytes []byte) (*RelayOutput, *RelayOutputErr) { +func parseRelaySuccesfulOutput(bodyBytes []byte) (*RelayOutput, int, error) { output := RelayOutput{} - + status := DefaultStatusCode err := json.Unmarshal(bodyBytes, &output) if err != nil { - return nil, &RelayOutputErr{Error: err} + return nil, DefaultStatusCode, err } // Check if there's explicitly a result field, if there's on mark it as success, otherwise check what's the potential status. // for REST chain that doesn't return result in any of the call will be defaulted to 202 in extractStatusFromResponse if strings.Contains(output.Response, resultText) { - output.StatusCode = http.StatusOK + status = http.StatusOK } else { - output.StatusCode = extractStatusFromResponse(output.Response) + status = extractStatusFromResponse(output.Response) } if !json.Valid([]byte(output.Response)) { - return nil, &RelayOutputErr{Error: ErrNonJSONResponse, StatusCode: output.StatusCode} + return nil, status, ErrNonJSONResponse } - return &output, nil + return &output, status, nil } func parseRelayErrorOutput(bodyBytes []byte, servicerPubKey string) error { diff --git a/provider/provider_test.go b/provider/provider_test.go index affea89..c7c028f 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -768,30 +768,30 @@ func TestProvider_Relay(t *testing.T) { mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusOK, "samples/client_relay.json") - relay, err := provider.Relay("https://dummy.com", &RelayInput{}, nil) + relay, _, err := provider.Relay("https://dummy.com", &RelayInput{}, nil) c.Nil(err) c.NotEmpty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusInternalServerError, "samples/client_relay.json") - relay, err = provider.Relay("https://dummy.com", &RelayInput{}, nil) - c.Equal(Err5xxOnConnection, err.Error) - c.Equal(http.StatusInternalServerError, err.StatusCode) - c.False(IsErrorCode(EmptyPayloadDataError, err.Error)) + relay, statusCode, err := provider.Relay("https://dummy.com", &RelayInput{}, nil) + c.Equal(Err5xxOnConnection, err) + c.Equal(http.StatusInternalServerError, statusCode) + c.False(IsErrorCode(EmptyPayloadDataError, err)) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusBadRequest, "samples/client_relay_error.json") - relay, err = provider.Relay("https://dummy.com", &RelayInput{Proof: &RelayProof{ServicerPubKey: "PJOG"}}, nil) - c.Equal("Request failed with code: 25, codespace: pocketcore and message: the payload data of the relay request is empty\nWith ServicerPubKey: PJOG", err.Error.Error()) - c.True(IsErrorCode(EmptyPayloadDataError, err.Error)) - c.Equal(http.StatusInternalServerError, err.StatusCode) + relay, statusCode, err = provider.Relay("https://dummy.com", &RelayInput{Proof: &RelayProof{ServicerPubKey: "PJOG"}}, nil) + c.Equal("Request failed with code: 25, codespace: pocketcore and message: the payload data of the relay request is empty\nWith ServicerPubKey: PJOG", err.Error()) + c.True(IsErrorCode(EmptyPayloadDataError, err)) + c.Equal(http.StatusInternalServerError, statusCode) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusOK, "samples/client_relay_non_json.json") - relay, err = provider.Relay("https://dummy.com", &RelayInput{}, nil) - c.Equal(ErrNonJSONResponse, err.Error) + relay, _, err = provider.Relay("https://dummy.com", &RelayInput{}, nil) + c.Equal(ErrNonJSONResponse, err) c.Empty(relay) } @@ -805,29 +805,29 @@ func TestProvider_RelayWithCtx(t *testing.T) { mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusOK, "samples/client_relay.json") - relay, err := provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) + relay, statusCode, err := provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) c.Nil(err) c.NotEmpty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusInternalServerError, "samples/client_relay.json") - relay, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) - c.Equal(Err5xxOnConnection, err.Error) - c.Equal(http.StatusInternalServerError, err.StatusCode) - c.False(IsErrorCode(EmptyPayloadDataError, err.Error)) + relay, statusCode, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) + c.Equal(Err5xxOnConnection, err) + c.Equal(http.StatusInternalServerError, statusCode) + c.False(IsErrorCode(EmptyPayloadDataError, err)) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusBadRequest, "samples/client_relay_error.json") - relay, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{Proof: &RelayProof{ServicerPubKey: "PJOG"}}, nil) - c.Equal("Request failed with code: 25, codespace: pocketcore and message: the payload data of the relay request is empty\nWith ServicerPubKey: PJOG", err.Error.Error()) - c.True(IsErrorCode(EmptyPayloadDataError, err.Error)) - c.Equal(http.StatusInternalServerError, err.StatusCode) + relay, statusCode, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{Proof: &RelayProof{ServicerPubKey: "PJOG"}}, nil) + c.Equal("Request failed with code: 25, codespace: pocketcore and message: the payload data of the relay request is empty\nWith ServicerPubKey: PJOG", err.Error()) + c.True(IsErrorCode(EmptyPayloadDataError, err)) + c.Equal(http.StatusInternalServerError, statusCode) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", ClientRelayRoute), http.StatusOK, "samples/client_relay_non_json.json") - relay, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) - c.Equal(ErrNonJSONResponse, err.Error) + relay, _, err = provider.RelayWithCtx(context.Background(), "https://dummy.com", &RelayInput{}, nil) + c.Equal(ErrNonJSONResponse, err) c.Empty(relay) } diff --git a/provider/relay.go b/provider/relay.go index 9075adf..e42c886 100644 --- a/provider/relay.go +++ b/provider/relay.go @@ -13,15 +13,8 @@ type RelayInput struct { // RelayOutput represents the Relay RPC output type RelayOutput struct { - Response string `json:"response"` - Signature string `json:"signature"` - StatusCode int `json:"statusCode"` -} - -// RelayOutputErr represents the RPC output error -type RelayOutputErr struct { - Error error `json:"error"` - StatusCode int `json:"statusCode"` + Response string `json:"response"` + Signature string `json:"signature"` } // RelayMeta represents metadata of a relay diff --git a/relayer/relayer.go b/relayer/relayer.go index 68e0deb..5ee45b8 100644 --- a/relayer/relayer.go +++ b/relayer/relayer.go @@ -10,6 +10,7 @@ import ( "errors" "math" "math/big" + "net/http" "golang.org/x/crypto/sha3" @@ -35,7 +36,7 @@ var ( // Provider interface representing provider functions necessary for Relayer Package type Provider interface { - RelayWithCtx(ctx context.Context, rpcURL string, input *provider.RelayInput, options *provider.RelayRequestOptions) (*provider.RelayOutput, *provider.RelayOutputErr) + RelayWithCtx(ctx context.Context, rpcURL string, input *provider.RelayInput, options *provider.RelayRequestOptions) (*provider.RelayOutput, int, error) } // Signer interface representing signer functions necessary for Relayer Package @@ -172,37 +173,37 @@ func (r *Relayer) buildRelay( } // Relay does relay request with given input -func (r *Relayer) Relay(input *Input, options *provider.RelayRequestOptions) (*Output, *provider.RelayOutputErr) { +func (r *Relayer) Relay(input *Input, options *provider.RelayRequestOptions) (*Output, int, error) { return r.RelayWithCtx(context.Background(), input, options) } // RelayWithCtx does relay request with given input -func (r *Relayer) RelayWithCtx(ctx context.Context, input *Input, options *provider.RelayRequestOptions) (*Output, *provider.RelayOutputErr) { +func (r *Relayer) RelayWithCtx(ctx context.Context, input *Input, options *provider.RelayRequestOptions) (*Output, int, error) { err := r.validateRelayRequest(input) if err != nil { - return nil, &provider.RelayOutputErr{Error: err} + return nil, http.StatusBadRequest, err } node, err := getNode(input) if err != nil { - return nil, &provider.RelayOutputErr{Error: err} + return nil, provider.DefaultStatusCode, err } relayInput, err := r.buildRelay(node, input, options) if err != nil { - return nil, &provider.RelayOutputErr{Error: err} + return nil, provider.DefaultStatusCode, err } - relayOutput, relayErr := r.provider.RelayWithCtx(ctx, node.ServiceURL, relayInput, options) + relayOutput, statusCode, relayErr := r.provider.RelayWithCtx(ctx, node.ServiceURL, relayInput, options) if relayErr != nil { - return nil, relayErr + return nil, statusCode, relayErr } return &Output{ RelayOutput: relayOutput, Proof: relayInput.Proof, Node: node, - }, nil + }, statusCode, nil } // GetRandomSessionNode returns a random node from given session diff --git a/relayer/relayer_test.go b/relayer/relayer_test.go index 586ae0b..39864a4 100644 --- a/relayer/relayer_test.go +++ b/relayer/relayer_test.go @@ -22,8 +22,8 @@ func TestRelayer_Relay(t *testing.T) { relayer := NewRelayer(nil, nil) input := &Input{} - relay, err := relayer.Relay(input, nil) - c.Equal(ErrNoSigner, err.Error) + relay, _, err := relayer.Relay(input, nil) + c.Equal(ErrNoSigner, err) c.Empty(relay) signer, signerErrr := signer.NewRandomSigner() @@ -31,39 +31,39 @@ func TestRelayer_Relay(t *testing.T) { relayer.signer = signer - relay, err = relayer.Relay(input, nil) - c.Equal(ErrNoProvider, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrNoProvider, err) c.Empty(relay) relayer.provider = provider.NewProvider("https://dummy.com", []string{"https://dummy.com"}) - relay, err = relayer.Relay(input, nil) - c.Equal(ErrNoSession, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrNoSession, err) c.Empty(relay) input.Session = &provider.Session{} - relay, err = relayer.Relay(input, nil) - c.Equal(ErrNoPocketAAT, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrNoPocketAAT, err) c.Empty(relay) input.PocketAAT = &provider.PocketAAT{} - relay, err = relayer.Relay(input, nil) - c.Equal(ErrSessionHasNoNodes, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrSessionHasNoNodes, err) c.Empty(relay) input.Session.Nodes = []provider.Node{{PublicKey: "AOG"}} - relay, err = relayer.Relay(input, nil) - c.Equal(ErrNoSessionHeader, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrNoSessionHeader, err) c.Empty(relay) input.Session.Header = provider.SessionHeader{Chain: "chain"} input.Node = &provider.Node{PublicKey: "PJOG"} - relay, err = relayer.Relay(input, nil) - c.Equal(ErrNodeNotInSession, err.Error) + relay, _, err = relayer.Relay(input, nil) + c.Equal(ErrNodeNotInSession, err) c.Empty(relay) input.Node = nil @@ -71,9 +71,9 @@ func TestRelayer_Relay(t *testing.T) { mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), http.StatusInternalServerError, "../provider/samples/client_relay.json") - relay, err = relayer.Relay(input, nil) + relay, _, err = relayer.Relay(input, nil) c.NotNil(err) - c.Equal(provider.Err5xxOnConnection, err.Error) + c.Equal(provider.Err5xxOnConnection, err) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), @@ -81,20 +81,20 @@ func TestRelayer_Relay(t *testing.T) { var error *provider.RelayError - relay, err = relayer.Relay(input, nil) - c.ErrorAs(err.Error, &error) + relay, _, err = relayer.Relay(input, nil) + c.ErrorAs(err, &error) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), http.StatusOK, "../provider/samples/client_relay.json") - relay, err = relayer.Relay(input, nil) + relay, _, err = relayer.Relay(input, nil) c.Nil(err) c.NotEmpty(relay) input.Node = &provider.Node{PublicKey: "AOG"} - relay, err = relayer.Relay(input, nil) + relay, _, err = relayer.Relay(input, nil) c.Nil(err) c.NotEmpty(relay) } @@ -108,8 +108,8 @@ func TestRelayer_RelayWithCtx(t *testing.T) { relayer := NewRelayer(nil, nil) input := &Input{} - relay, err := relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNoSigner, err.Error) + relay, _, err := relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNoSigner, err) c.Empty(relay) signer, signerErr := signer.NewRandomSigner() @@ -117,39 +117,39 @@ func TestRelayer_RelayWithCtx(t *testing.T) { relayer.signer = signer - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNoProvider, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNoProvider, err) c.Empty(relay) relayer.provider = provider.NewProvider("https://dummy.com", []string{"https://dummy.com"}) - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNoSession, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNoSession, err) c.Empty(relay) input.Session = &provider.Session{} - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNoPocketAAT, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNoPocketAAT, err) c.Empty(relay) input.PocketAAT = &provider.PocketAAT{} - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrSessionHasNoNodes, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrSessionHasNoNodes, err) c.Empty(relay) input.Session.Nodes = []provider.Node{{PublicKey: "AOG"}} - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNoSessionHeader, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNoSessionHeader, err) c.Empty(relay) input.Session.Header = provider.SessionHeader{Chain: "chain"} input.Node = &provider.Node{PublicKey: "PJOG"} - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(ErrNodeNotInSession, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(ErrNodeNotInSession, err) c.Empty(relay) input.Node = nil @@ -157,8 +157,8 @@ func TestRelayer_RelayWithCtx(t *testing.T) { mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), http.StatusInternalServerError, "../provider/samples/client_relay.json") - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.Equal(provider.Err5xxOnConnection, err.Error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.Equal(provider.Err5xxOnConnection, err) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), @@ -166,20 +166,20 @@ func TestRelayer_RelayWithCtx(t *testing.T) { var error *provider.RelayError - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) - c.ErrorAs(err.Error, &error) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) + c.ErrorAs(err, &error) c.Empty(relay) mock.AddMockedResponseFromFile(http.MethodPost, fmt.Sprintf("%s%s", "https://dummy.com", provider.ClientRelayRoute), http.StatusOK, "../provider/samples/client_relay.json") - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) c.Nil(err) c.NotEmpty(relay) input.Node = &provider.Node{PublicKey: "AOG"} - relay, err = relayer.RelayWithCtx(context.Background(), input, nil) + relay, _, err = relayer.RelayWithCtx(context.Background(), input, nil) c.Nil(err) c.NotEmpty(relay) }