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  }