code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_import_network.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 api 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "regexp" 23 "strings" 24 25 vgfs "code.vegaprotocol.io/vega/libs/fs" 26 "code.vegaprotocol.io/vega/libs/jsonrpc" 27 "code.vegaprotocol.io/vega/paths" 28 "code.vegaprotocol.io/vega/wallet/network" 29 30 "github.com/mitchellh/mapstructure" 31 ) 32 33 var ( 34 ErrInvalidNetworkSource = errors.New("invalid network source") 35 36 githubToml = regexp.MustCompile(`(http[s]?://)(github\.com).*(.toml)$`) 37 ) 38 39 type AdminImportNetworkParams struct { 40 Name string `json:"name"` 41 URL string `json:"url"` 42 Overwrite bool `json:"overwrite"` 43 } 44 45 type AdminImportNetworkResult struct { 46 Name string `json:"name"` 47 } 48 49 type AdminImportNetwork struct { 50 networkStore NetworkStore 51 } 52 53 type Reader func(uri string, net interface{}) error 54 55 type Readers struct { 56 ReadFromFile Reader 57 ReadFromURL Reader 58 } 59 60 func NewReaders() Readers { 61 return Readers{ 62 ReadFromFile: paths.ReadStructuredFile, 63 ReadFromURL: paths.FetchStructuredFile, 64 } 65 } 66 67 func (h *AdminImportNetwork) Handle(_ context.Context, rawParams jsonrpc.Params) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 68 params, err := validateImportNetworkParams(rawParams) 69 if err != nil { 70 return nil, InvalidParams(err) 71 } 72 73 net, err := readImportNetworkSource(params) 74 if errors.Is(err, ErrInvalidNetworkSource) { 75 return nil, InvalidParams(err) 76 } 77 if err != nil { 78 return nil, InternalError(err) 79 } 80 81 if len(params.Name) != 0 { 82 net.Name = params.Name 83 } 84 85 if len(net.Name) == 0 { 86 return nil, InvalidParams(ErrNetworkNameIsRequired) 87 } 88 89 if exist, err := h.networkStore.NetworkExists(net.Name); err != nil { 90 return nil, InternalError(fmt.Errorf("could not verify the network existence: %w", err)) 91 } else if exist && !params.Overwrite { 92 return nil, InvalidParams(ErrNetworkAlreadyExists) 93 } 94 95 if err := h.networkStore.SaveNetwork(net); err != nil { 96 return nil, InternalError(err) 97 } 98 99 return AdminImportNetworkResult{ 100 Name: net.Name, 101 }, nil 102 } 103 104 // urlPreCheck looks for basic user errors in the given URL. For example if a github 105 // URL is supplied instead of a link to the raw-file-contents. 106 func urlPreCheck(url string) error { 107 m := githubToml.FindString(url) 108 if len(m) == 0 { 109 return nil 110 } 111 112 // make a suggestion 113 suggestion := strings.Replace(url, "github.com", "raw.githubusercontent.com", 1) 114 suggestion = strings.Replace(suggestion, "/blob/", "/", 1) 115 return fmt.Errorf("this URL leads to a Github page and not the network configuration, did you mean %s", suggestion) 116 } 117 118 func validateImportNetworkParams(rawParams jsonrpc.Params) (AdminImportNetworkParams, error) { 119 if rawParams == nil { 120 return AdminImportNetworkParams{}, ErrParamsRequired 121 } 122 123 params := AdminImportNetworkParams{} 124 if err := mapstructure.Decode(rawParams, ¶ms); err != nil { 125 return AdminImportNetworkParams{}, ErrParamsDoNotMatch 126 } 127 128 if params.URL == "" { 129 return AdminImportNetworkParams{}, ErrNetworkSourceIsRequired 130 } 131 132 if err := urlPreCheck(params.URL); err != nil { 133 return AdminImportNetworkParams{}, err 134 } 135 return params, nil 136 } 137 138 // readImportNetworkSource parse the network file given by the source in the params 139 // into a `Network` which can then be saved to disk. 140 func readImportNetworkSource(params AdminImportNetworkParams) (*network.Network, error) { 141 net := &network.Network{} 142 rs := NewReaders() 143 144 s, filePath, isFile := strings.Cut(params.URL, FileSchemePrefix) 145 if isFile && len(s) == 0 { 146 exists, err := vgfs.FileExists(filePath) 147 if err != nil { 148 return nil, fmt.Errorf("could not check file's existence at %q: %w", filePath, err) 149 } 150 if !exists { 151 return nil, fmt.Errorf("the network source file does not exist: %w", ErrInvalidNetworkSource) 152 } 153 154 err = rs.ReadFromFile(filePath, net) 155 if err == paths.ErrEmptyFile { 156 return nil, fmt.Errorf("network source file is empty: %w", ErrInvalidNetworkSource) 157 } 158 if err != nil { 159 return nil, fmt.Errorf("could not read the network configuration at %q: %w", filePath, err) 160 } 161 return net, nil 162 } 163 164 if len(params.URL) != 0 { 165 err := rs.ReadFromURL(params.URL, net) 166 if err == paths.ErrEmptyResponse { 167 return nil, fmt.Errorf("network source url points to an empty file: %w", ErrInvalidNetworkSource) 168 } 169 if err != nil { 170 return nil, fmt.Errorf("could not fetch the network configuration from %q: %w", params.URL, err) 171 } 172 return net, nil 173 } 174 175 return net, nil 176 } 177 178 func NewAdminImportNetwork( 179 networkStore NetworkStore, 180 ) *AdminImportNetwork { 181 return &AdminImportNetwork{ 182 networkStore: networkStore, 183 } 184 }