code.vegaprotocol.io/vega@v0.79.0/wallet/service/service.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package service
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  	"strings"
    24  
    25  	v1 "code.vegaprotocol.io/vega/wallet/service/v1"
    26  	v2 "code.vegaprotocol.io/vega/wallet/service/v2"
    27  
    28  	"github.com/julienschmidt/httprouter"
    29  	"github.com/rs/cors"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  type JSONRPCErr struct {
    34  	Err     string   `json:"error"`
    35  	Details []string `json:"details,omitempty"`
    36  }
    37  
    38  type HeaderError struct {
    39  	Key string   `json:"header"`
    40  	Val []string `json:"value"`
    41  }
    42  
    43  type Service struct {
    44  	*httprouter.Router
    45  
    46  	log *zap.Logger
    47  
    48  	server *http.Server
    49  
    50  	apiV1 *v1.API
    51  	apiV2 *v2.API
    52  }
    53  
    54  func NewService(log *zap.Logger, cfg *Config, apiV1 *v1.API, apiV2 *v2.API) *Service {
    55  	s := &Service{
    56  		Router: httprouter.New(),
    57  		log:    log,
    58  		apiV1:  apiV1,
    59  		apiV2:  apiV2,
    60  	}
    61  
    62  	s.server = &http.Server{
    63  		Addr:    fmt.Sprintf("%s:%v", cfg.Server.Host, cfg.Server.Port),
    64  		Handler: cors.AllowAll().Handler(s),
    65  	}
    66  
    67  	// V1
    68  	s.handleV1(http.MethodPost, "/api/v1/auth/token", s.apiV1.Login)
    69  	s.handleV1(http.MethodDelete, "/api/v1/auth/token", s.apiV1.Revoke)
    70  
    71  	s.handleV1(http.MethodGet, "/api/v1/network", s.apiV1.GetNetwork)
    72  	s.handleV1(http.MethodGet, "/api/v1/network/chainid", s.apiV1.GetNetworkChainID)
    73  
    74  	s.handleV1(http.MethodPost, "/api/v1/wallets", s.apiV1.CreateWallet)
    75  	s.handleV1(http.MethodPost, "/api/v1/wallets/import", s.apiV1.ImportWallet)
    76  
    77  	s.handleV1(http.MethodGet, "/api/v1/keys", s.apiV1.ListPublicKeys)
    78  	s.handleV1(http.MethodPost, "/api/v1/keys", s.apiV1.GenerateKeyPair)
    79  	s.handleV1(http.MethodGet, "/api/v1/keys/:keyid", s.apiV1.GetPublicKey)
    80  	s.handleV1(http.MethodPut, "/api/v1/keys/:keyid/taint", s.apiV1.TaintKey)
    81  	s.handleV1(http.MethodPut, "/api/v1/keys/:keyid/metadata", s.apiV1.UpdateMeta)
    82  
    83  	s.handleV1(http.MethodPost, "/api/v1/command", s.apiV1.SignTx)
    84  	s.handleV1(http.MethodPost, "/api/v1/command/sync", s.apiV1.SignTxSync)
    85  	s.handleV1(http.MethodPost, "/api/v1/command/check", s.apiV1.CheckTx)
    86  	s.handleV1(http.MethodPost, "/api/v1/command/commit", s.apiV1.SignTxCommit)
    87  	s.handleV1(http.MethodPost, "/api/v1/sign", s.apiV1.SignAny)
    88  	s.handleV1(http.MethodPost, "/api/v1/verify", s.apiV1.VerifyAny)
    89  
    90  	s.handleV1(http.MethodGet, "/api/v1/version", s.apiV1.Version)
    91  	s.handleV1(http.MethodGet, "/api/v1/status", s.apiV1.Health)
    92  
    93  	// V2
    94  	s.Handle(http.MethodGet, "/api/v2/health", s.apiV2.CheckHealth)
    95  	s.Handle(http.MethodGet, "/api/v2/methods", s.apiV2.ListMethods)
    96  	s.Handle(http.MethodPost, "/api/v2/requests", s.apiV2.HandleRequest)
    97  
    98  	return s
    99  }
   100  
   101  func (s *Service) Start() error {
   102  	return s.server.ListenAndServe()
   103  }
   104  
   105  func (s *Service) Stop(ctx context.Context) error {
   106  	return s.server.Shutdown(ctx)
   107  }
   108  
   109  func (s *Service) handleV1(method string, path string, handle httprouter.Handle) {
   110  	loggedEndpoint := func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
   111  		for k, h := range r.Header {
   112  			for _, v := range h {
   113  				if len([]rune(v)) != len(v) {
   114  					s.badHeaderResp(w, HeaderError{
   115  						Key: k,
   116  						Val: h,
   117  					})
   118  					return
   119  				}
   120  			}
   121  		}
   122  		s.log.Info(fmt.Sprintf("Entering %s %s", method, path))
   123  		handle(w, r, p)
   124  		s.log.Info(fmt.Sprintf("Leaving %s %s", method, path))
   125  	}
   126  	s.Handle(method, path, loggedEndpoint)
   127  }
   128  
   129  func (h HeaderError) Error() string {
   130  	return fmt.Sprintf("header %s contains invalid characters: %s", h.Key, strings.Join(h.Val, ", "))
   131  }
   132  
   133  func (h HeaderError) MarshalJSON() ([]byte, error) {
   134  	details := make([]string, 0, len(h.Val)+1)
   135  	details = append(details, h.Key)
   136  	return json.Marshal(JSONRPCErr{
   137  		Err:     h.Error(),
   138  		Details: append(details, h.Val...),
   139  	})
   140  }
   141  
   142  func (h *HeaderError) UnmarshalJSON(data []byte) error {
   143  	w := JSONRPCErr{}
   144  	if err := json.Unmarshal(data, &w); err != nil {
   145  		return err
   146  	}
   147  	if len(w.Details) == 0 {
   148  		return nil
   149  	}
   150  	h.Key = w.Details[0]
   151  	if len(w.Details) > 1 {
   152  		h.Val = w.Details[1:]
   153  	}
   154  	return nil
   155  }
   156  
   157  func (s *Service) badHeaderResp(w http.ResponseWriter, herr HeaderError) {
   158  	w.Header().Set("Content-Type", "application/json")
   159  	w.WriteHeader(http.StatusBadRequest)
   160  	buf, err := json.Marshal(herr)
   161  	if err != nil {
   162  		s.log.Error("couldn't marshal errors", zap.String("error", err.Error()))
   163  		return
   164  	}
   165  	if _, err := w.Write(buf); err != nil {
   166  		s.log.Error("couldn't write errors", zap.String("error", err.Error()))
   167  		return
   168  	}
   169  	s.log.Info(fmt.Sprintf("%d %s", http.StatusBadRequest, http.StatusText(http.StatusBadRequest)))
   170  }