Skip to content

Commit

Permalink
refactor(SPV-1061): arc requests moved to a single package (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-4chain authored Oct 4, 2024
1 parent e3bff84 commit 62ba56c
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 254 deletions.
9 changes: 3 additions & 6 deletions engine/chain/chain_service.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package chain

import (
"github.com/bitcoin-sv/spv-wallet/engine/chain/internal/policy"
"github.com/bitcoin-sv/spv-wallet/engine/chain/internal/query"
"github.com/bitcoin-sv/spv-wallet/engine/chain/internal/arc"
"github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/go-resty/resty/v2"
"github.com/rs/zerolog"
)

type chainService struct {
QueryService
PolicyService
ARCService
}

// NewChainService creates a new chain service.
func NewChainService(logger zerolog.Logger, httpClient *resty.Client, arcCfg chainmodels.ARCConfig) Service {
return &chainService{
query.NewQueryService(logger.With().Str("chain", "query").Logger(), httpClient, arcCfg),
policy.NewPolicyService(logger.With().Str("chain", "policy").Logger(), httpClient, arcCfg),
arc.NewARCService(logger.With().Str("chain", "arc").Logger(), httpClient, arcCfg),
}
}
11 changes: 3 additions & 8 deletions engine/chain/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@ import (
"github.com/bitcoin-sv/spv-wallet/engine/chain/models"
)

// QueryService for querying transactions.
type QueryService interface {
// ARCService for querying ARC server.
type ARCService interface {
QueryTransaction(ctx context.Context, txID string) (*chainmodels.TXInfo, error)
}

// PolicyService for querying policy.
type PolicyService interface {
GetPolicy(ctx context.Context) (*chainmodels.Policy, error)
}

// Service related to the chain.
type Service interface {
QueryService
PolicyService
ARCService
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package query_test
package arc_test

import (
"fmt"
"net/http"
"time"

chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/go-resty/resty/v2"
"github.com/jarcoal/httpmock"
)
Expand Down Expand Up @@ -74,5 +75,32 @@ func arcMockActivate(applyTimeout bool) *resty.Client {
}`),
)

transport.RegisterResponder("GET", fmt.Sprintf("%s/v1/policy", arcURL), responder(http.StatusOK, `{
"policy": {
"maxscriptsizepolicy": 100000000,
"maxtxsigopscountspolicy": 4294967295,
"maxtxsizepolicy": 100000000,
"miningFee": {
"bytes": 1000,
"satoshis": 1
}
},
"timestamp": "2024-10-02T07:36:33.589144918Z"
}`),
)

transport.RegisterResponder("GET", arcURL+wrongButReachable, responder(http.StatusNotFound, `{
"message": "no matching operation was found"
}`),
)

return client
}

func arcCfg(url, token string) chainmodels.ARCConfig {
return chainmodels.ARCConfig{
URL: url,
Token: token,
DeploymentID: "spv-wallet-test-arc-connection",
}
}
36 changes: 36 additions & 0 deletions engine/chain/internal/arc/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package arc

import (
"context"
"fmt"
"net/http"

chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

// GetPolicy requests ARC server for the policy
func (s *Service) GetPolicy(ctx context.Context) (*chainmodels.Policy, error) {
result := &chainmodels.Policy{}
arcErr := &chainmodels.ArcError{}
req := s.prepareARCRequest(ctx).
SetResult(result).
SetError(arcErr)

response, err := req.Get(fmt.Sprintf("%s/v1/policy", s.arcCfg.URL))

if err != nil {
return nil, s.wrapRequestError(err)
}

switch response.StatusCode() {
case http.StatusOK:
return result, nil
case http.StatusUnauthorized, http.StatusForbidden:
return nil, s.wrapARCError(spverrors.ErrARCUnauthorized, arcErr)
case http.StatusNotFound:
return nil, spverrors.ErrARCUnreachable
default:
return nil, s.wrapARCError(spverrors.ErrARCUnsupportedStatusCode, arcErr)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package policy_test
package arc_test

import (
"context"
"testing"
"time"

"github.com/bitcoin-sv/spv-wallet/engine/chain"
chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
"github.com/bitcoin-sv/spv-wallet/engine/tester"
"github.com/bitcoin-sv/spv-wallet/models/bsv"
Expand Down Expand Up @@ -102,11 +101,3 @@ func TestPolicyServiceTimeouts(t *testing.T) {
require.Nil(t, txInfo)
})
}

func arcCfg(url, token string) chainmodels.ARCConfig {
return chainmodels.ARCConfig{
URL: url,
Token: token,
DeploymentID: "spv-wallet-test-arc-connection",
}
}
42 changes: 42 additions & 0 deletions engine/chain/internal/arc/query_transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package arc

import (
"context"
"fmt"
"net/http"

"github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
)

// QueryTransaction a transaction.
func (s *Service) QueryTransaction(ctx context.Context, txID string) (*chainmodels.TXInfo, error) {
result := &chainmodels.TXInfo{}
arcErr := &chainmodels.ArcError{}
req := s.prepareARCRequest(ctx).
SetResult(result).
SetError(arcErr)

response, err := req.Get(fmt.Sprintf("%s/v1/tx/%s", s.arcCfg.URL, txID))

if err != nil {
return nil, s.wrapRequestError(err)
}

switch response.StatusCode() {
case http.StatusOK:
return result, nil
case http.StatusUnauthorized, http.StatusForbidden:
return nil, s.wrapARCError(spverrors.ErrARCUnauthorized, arcErr)
case http.StatusNotFound:
if !arcErr.IsEmpty() {
// ARC returns 404 when transaction is not found
return nil, nil // By convention, nil is returned when transaction is not found
}
return nil, spverrors.ErrARCUnreachable
case http.StatusConflict:
return nil, s.wrapARCError(spverrors.ErrARCGenericError, arcErr)
default:
return nil, s.wrapARCError(spverrors.ErrARCUnsupportedStatusCode, arcErr)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package query_test
package arc_test

import (
"context"
"testing"
"time"

"github.com/bitcoin-sv/spv-wallet/engine/chain"
chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
"github.com/bitcoin-sv/spv-wallet/engine/tester"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -121,11 +120,3 @@ func TestQueryServiceTimeouts(t *testing.T) {
require.Nil(t, txInfo)
})
}

func arcCfg(url, token string) chainmodels.ARCConfig {
return chainmodels.ARCConfig{
URL: url,
Token: token,
DeploymentID: "spv-wallet-test-arc-connection",
}
}
43 changes: 43 additions & 0 deletions engine/chain/internal/arc/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package arc

import (
"context"
"errors"
"net"

chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/bitcoin-sv/spv-wallet/engine/spverrors"
"github.com/bitcoin-sv/spv-wallet/models"
"github.com/go-resty/resty/v2"
)

func (s *Service) prepareARCRequest(ctx context.Context) *resty.Request {
req := s.httpClient.R().
SetContext(ctx).
SetHeader("Content-Type", "application/json")

if s.arcCfg.Token != "" {
req.SetHeader("Authorization", s.arcCfg.Token)
}

if s.arcCfg.DeploymentID != "" {
req.SetHeader("XDeployment-ID", s.arcCfg.DeploymentID)
}

return req
}

func (s *Service) wrapRequestError(err error) error {
var e net.Error
if errors.As(err, &e) {
return spverrors.ErrARCUnreachable.Wrap(e)
}
return spverrors.ErrInternal.Wrap(err)
}

func (s *Service) wrapARCError(baseError models.SPVError, errResult *chainmodels.ArcError) error {
if errResult == nil || errResult.IsEmpty() {
return baseError
}
return baseError.Wrap(errResult)
}
23 changes: 23 additions & 0 deletions engine/chain/internal/arc/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package arc

import (
chainmodels "github.com/bitcoin-sv/spv-wallet/engine/chain/models"
"github.com/go-resty/resty/v2"
"github.com/rs/zerolog"
)

// Service for arc requests.
type Service struct {
logger zerolog.Logger
httpClient *resty.Client
arcCfg chainmodels.ARCConfig
}

// NewARCService creates a new arc service.
func NewARCService(logger zerolog.Logger, httpClient *resty.Client, arcCfg chainmodels.ARCConfig) *Service {
return &Service{
logger: logger,
httpClient: httpClient,
arcCfg: arcCfg,
}
}
57 changes: 0 additions & 57 deletions engine/chain/internal/policy/arc_mock_test.go

This file was deleted.

Loading

0 comments on commit 62ba56c

Please sign in to comment.