code.vegaprotocol.io/vega@v0.79.0/wallet/network/store/v1/file_store.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 v1 17 18 import ( 19 "errors" 20 "fmt" 21 "io/fs" 22 "os" 23 "path/filepath" 24 "sort" 25 "strings" 26 27 vgfs "code.vegaprotocol.io/vega/libs/fs" 28 "code.vegaprotocol.io/vega/paths" 29 "code.vegaprotocol.io/vega/wallet/api" 30 "code.vegaprotocol.io/vega/wallet/network" 31 ) 32 33 const fileExt = ".toml" 34 35 var ( 36 ErrNetworkNameCannotBeEmpty = errors.New("the network name cannot be empty") 37 ErrNetworkNameCannotStartWithDot = errors.New("the network name cannot start with a `.`") 38 ErrNetworkNameCannotContainSlash = errors.New("the network name cannot contain `\\` nor `/`") 39 ) 40 41 type FileStore struct { 42 networksHome string 43 } 44 45 type networkFileContentV1 struct { 46 FileVersion uint32 `json:"fileVersion"` 47 API network.APIConfig `json:"api"` 48 TokenDApp struct { 49 URL string `json:"url"` 50 } `json:"tokenDApp"` 51 Console struct { 52 URL string `json:"url"` 53 } `json:"console"` 54 } 55 56 type networkFileContentV2 struct { 57 FileVersion uint32 `json:"fileVersion"` 58 API network.APIConfig `json:"api"` 59 Apps network.AppsConfig `json:"apps"` 60 Metadata []network.Metadata `json:"metadata"` 61 } 62 63 func InitialiseStore(vegaPaths paths.Paths) (*FileStore, error) { 64 networksHome, err := vegaPaths.CreateConfigDirFor(paths.WalletServiceNetworksConfigHome) 65 if err != nil { 66 return nil, fmt.Errorf("couldn't get config path for %s: %w", paths.WalletServiceNetworksConfigHome, err) 67 } 68 69 return &FileStore{ 70 networksHome: networksHome, 71 }, nil 72 } 73 74 func (s *FileStore) ListNetworks() ([]string, error) { 75 networksParentDir, networksDir := filepath.Split(s.networksHome) 76 entries, err := fs.ReadDir(os.DirFS(networksParentDir), networksDir) 77 if err != nil { 78 return nil, fmt.Errorf("couldn't read directory at %s: %w", s.networksHome, err) 79 } 80 networks := []string{} 81 for _, entry := range entries { 82 if err := ensureValidNetworkName(entry.Name()); err != nil { 83 continue 84 } 85 86 if strings.HasSuffix(entry.Name(), ".toml") { 87 networks = append(networks, s.fileNameToName(entry.Name())) 88 } 89 } 90 sort.Strings(networks) 91 return networks, nil 92 } 93 94 func (s *FileStore) GetNetworksPath() string { 95 return s.networksHome 96 } 97 98 func (s *FileStore) GetNetworkPath(name string) string { 99 return s.nameToFilePath(name) 100 } 101 102 func (s *FileStore) NetworkExists(name string) (bool, error) { 103 if err := ensureValidNetworkName(name); err != nil { 104 return false, err 105 } 106 107 return vgfs.FileExists(s.GetNetworkPath(name)) 108 } 109 110 func (s *FileStore) GetNetwork(name string) (*network.Network, error) { 111 if err := ensureValidNetworkName(name); err != nil { 112 return nil, err 113 } 114 115 versionedFile := struct { 116 FileVersion int32 `json:"fileVersion"` 117 }{} 118 119 if err := paths.ReadStructuredFile(s.nameToFilePath(name), &versionedFile); err != nil { 120 return nil, fmt.Errorf("couldn't read network configuration file: %w", err) 121 } 122 123 if versionedFile.FileVersion == 2 { 124 nfc := &networkFileContentV2{} 125 if err := paths.ReadStructuredFile(s.nameToFilePath(name), &nfc); err != nil { 126 return nil, fmt.Errorf("couldn't read network configuration file (v2): %w", err) 127 } 128 129 return &network.Network{ 130 Name: name, 131 Metadata: nfc.Metadata, 132 API: nfc.API, 133 Apps: network.AppsConfig{ 134 Console: nfc.Apps.Console, 135 Governance: nfc.Apps.Governance, 136 Explorer: nfc.Apps.Explorer, 137 }, 138 }, nil 139 } 140 141 nfc := &networkFileContentV1{} 142 if err := paths.ReadStructuredFile(s.nameToFilePath(name), &nfc); err != nil { 143 return nil, fmt.Errorf("couldn't read network configuration file (v1): %w", err) 144 } 145 146 return &network.Network{ 147 Name: name, 148 API: nfc.API, 149 Apps: network.AppsConfig{ 150 Console: nfc.Console.URL, 151 Governance: nfc.TokenDApp.URL, 152 }, 153 }, nil 154 } 155 156 func (s *FileStore) SaveNetwork(net *network.Network) error { 157 if err := ensureValidNetworkName(net.Name); err != nil { 158 return err 159 } 160 161 nfc := &networkFileContentV2{ 162 FileVersion: 2, 163 API: net.API, 164 Apps: net.Apps, 165 Metadata: net.Metadata, 166 } 167 if err := paths.WriteStructuredFile(s.nameToFilePath(net.Name), nfc); err != nil { 168 return fmt.Errorf("couldn't write network configuration file: %w", err) 169 } 170 return nil 171 } 172 173 func (s *FileStore) DeleteNetwork(name string) error { 174 if err := ensureValidNetworkName(name); err != nil { 175 return err 176 } 177 path := s.GetNetworkPath(name) 178 return os.Remove(path) 179 } 180 181 func (s *FileStore) RenameNetwork(currentName, newName string) error { 182 if err := ensureValidNetworkName(currentName); err != nil { 183 return err 184 } 185 186 if err := ensureValidNetworkName(newName); err != nil { 187 return err 188 } 189 190 currentNetworkPath := s.nameToFilePath(currentName) 191 192 if exists, err := vgfs.PathExists(currentNetworkPath); err != nil { 193 return fmt.Errorf("could not verify the path at %s: %w", currentNetworkPath, err) 194 } else if !exists { 195 return api.ErrNetworkDoesNotExist 196 } 197 198 newNetworkPath := s.nameToFilePath(newName) 199 200 if err := os.Rename(currentNetworkPath, newNetworkPath); err != nil { 201 return fmt.Errorf("could not rename the network %q to %q at %s: %w", currentName, newName, s.networksHome, err) 202 } 203 204 return nil 205 } 206 207 func (s *FileStore) nameToFilePath(network string) string { 208 return filepath.Join(s.networksHome, network+fileExt) 209 } 210 211 func (s *FileStore) fileNameToName(fileName string) string { 212 return fileName[:len(fileName)-len(fileExt)] 213 } 214 215 func ensureValidNetworkName(name string) error { 216 if name == "" { 217 return ErrNetworkNameCannotBeEmpty 218 } 219 if name[0] == '.' { 220 return ErrNetworkNameCannotStartWithDot 221 } 222 223 if strings.ContainsAny(name, "\\/") { 224 return ErrNetworkNameCannotContainSlash 225 } 226 227 return nil 228 }