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 }