github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/persistence/persistence.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package persistence
     8  
     9  import (
    10  	"encoding/hex"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  
    18  	"github.com/hechain20/hechain/common/chaincode"
    19  	"github.com/hechain20/hechain/common/flogging"
    20  	"github.com/hechain20/hechain/common/util"
    21  
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  var logger = flogging.MustGetLogger("chaincode.persistence")
    26  
    27  // IOReadWriter defines the interface needed for reading, writing, removing, and
    28  // checking for existence of a specified file
    29  type IOReadWriter interface {
    30  	ReadDir(string) ([]os.FileInfo, error)
    31  	ReadFile(string) ([]byte, error)
    32  	Remove(name string) error
    33  	WriteFile(string, string, []byte) error
    34  	MakeDir(string, os.FileMode) error
    35  	Exists(path string) (bool, error)
    36  }
    37  
    38  // FilesystemIO is the production implementation of the IOWriter interface
    39  type FilesystemIO struct{}
    40  
    41  // WriteFile writes a file to the filesystem; it does so atomically
    42  // by first writing to a temp file and then renaming the file so that
    43  // if the operation crashes midway we're not stuck with a bad package
    44  func (f *FilesystemIO) WriteFile(path, name string, data []byte) error {
    45  	if path == "" {
    46  		return errors.New("empty path not allowed")
    47  	}
    48  	tmpFile, err := ioutil.TempFile(path, ".ccpackage.")
    49  	if err != nil {
    50  		return errors.Wrapf(err, "error creating temp file in directory '%s'", path)
    51  	}
    52  	defer os.Remove(tmpFile.Name())
    53  
    54  	if n, err := tmpFile.Write(data); err != nil || n != len(data) {
    55  		if err == nil {
    56  			err = errors.Errorf(
    57  				"failed to write the entire content of the file, expected %d, wrote %d",
    58  				len(data), n)
    59  		}
    60  		return errors.Wrapf(err, "error writing to temp file '%s'", tmpFile.Name())
    61  	}
    62  
    63  	if err := tmpFile.Close(); err != nil {
    64  		return errors.Wrapf(err, "error closing temp file '%s'", tmpFile.Name())
    65  	}
    66  
    67  	if err := os.Rename(tmpFile.Name(), filepath.Join(path, name)); err != nil {
    68  		return errors.Wrapf(err, "error renaming temp file '%s'", tmpFile.Name())
    69  	}
    70  
    71  	return nil
    72  }
    73  
    74  // Remove removes a file from the filesystem - used for rolling back an in-flight
    75  // Save operation upon a failure
    76  func (f *FilesystemIO) Remove(name string) error {
    77  	return os.Remove(name)
    78  }
    79  
    80  // ReadFile reads a file from the filesystem
    81  func (f *FilesystemIO) ReadFile(filename string) ([]byte, error) {
    82  	return ioutil.ReadFile(filename)
    83  }
    84  
    85  // ReadDir reads a directory from the filesystem
    86  func (f *FilesystemIO) ReadDir(dirname string) ([]os.FileInfo, error) {
    87  	return ioutil.ReadDir(dirname)
    88  }
    89  
    90  // MakeDir makes a directory on the filesystem (and any
    91  // necessary parent directories).
    92  func (f *FilesystemIO) MakeDir(dirname string, mode os.FileMode) error {
    93  	return os.MkdirAll(dirname, mode)
    94  }
    95  
    96  // Exists checks whether a file exists
    97  func (*FilesystemIO) Exists(path string) (bool, error) {
    98  	_, err := os.Stat(path)
    99  	if err == nil {
   100  		return true, nil
   101  	}
   102  	if os.IsNotExist(err) {
   103  		return false, nil
   104  	}
   105  
   106  	return false, errors.Wrapf(err, "could not determine whether file '%s' exists", path)
   107  }
   108  
   109  // Store holds the information needed for persisting a chaincode install package
   110  type Store struct {
   111  	Path       string
   112  	ReadWriter IOReadWriter
   113  }
   114  
   115  // NewStore creates a new chaincode persistence store using
   116  // the provided path on the filesystem.
   117  func NewStore(path string) *Store {
   118  	store := &Store{
   119  		Path:       path,
   120  		ReadWriter: &FilesystemIO{},
   121  	}
   122  	store.Initialize()
   123  	return store
   124  }
   125  
   126  // Initialize checks for the existence of the _lifecycle chaincodes
   127  // directory and creates it if it has not yet been created.
   128  func (s *Store) Initialize() {
   129  	var (
   130  		exists bool
   131  		err    error
   132  	)
   133  	if exists, err = s.ReadWriter.Exists(s.Path); exists {
   134  		return
   135  	}
   136  	if err != nil {
   137  		panic(fmt.Sprintf("Initialization of chaincode store failed: %s", err))
   138  	}
   139  	if err = s.ReadWriter.MakeDir(s.Path, 0o750); err != nil {
   140  		panic(fmt.Sprintf("Could not create _lifecycle chaincodes install path: %s", err))
   141  	}
   142  }
   143  
   144  // Save persists chaincode install package bytes. It returns
   145  // the hash of the chaincode install package
   146  func (s *Store) Save(label string, ccInstallPkg []byte) (string, error) {
   147  	packageID := PackageID(label, ccInstallPkg)
   148  
   149  	ccInstallPkgFileName := CCFileName(packageID)
   150  	ccInstallPkgFilePath := filepath.Join(s.Path, ccInstallPkgFileName)
   151  
   152  	if exists, _ := s.ReadWriter.Exists(ccInstallPkgFilePath); exists {
   153  		// chaincode install package was already installed
   154  		return packageID, nil
   155  	}
   156  
   157  	if err := s.ReadWriter.WriteFile(s.Path, ccInstallPkgFileName, ccInstallPkg); err != nil {
   158  		err = errors.Wrapf(err, "error writing chaincode install package to %s", ccInstallPkgFilePath)
   159  		logger.Error(err.Error())
   160  		return "", err
   161  	}
   162  
   163  	return packageID, nil
   164  }
   165  
   166  // Load loads a persisted chaincode install package bytes with
   167  // the given packageID.
   168  func (s *Store) Load(packageID string) ([]byte, error) {
   169  	ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
   170  
   171  	exists, err := s.ReadWriter.Exists(ccInstallPkgPath)
   172  	if err != nil {
   173  		return nil, errors.Wrapf(err, "could not determine whether chaincode install package '%s' exists", packageID)
   174  	}
   175  	if !exists {
   176  		return nil, &CodePackageNotFoundErr{
   177  			PackageID: packageID,
   178  		}
   179  	}
   180  
   181  	ccInstallPkg, err := s.ReadWriter.ReadFile(ccInstallPkgPath)
   182  	if err != nil {
   183  		err = errors.Wrapf(err, "error reading chaincode install package at %s", ccInstallPkgPath)
   184  		return nil, err
   185  	}
   186  
   187  	return ccInstallPkg, nil
   188  }
   189  
   190  // Delete deletes a persisted chaincode.  Note, there is no locking,
   191  // so this should only be performed if the chaincode has already
   192  // been marked built.
   193  func (s *Store) Delete(packageID string) error {
   194  	ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
   195  	return s.ReadWriter.Remove(ccInstallPkgPath)
   196  }
   197  
   198  // CodePackageNotFoundErr is the error returned when a code package cannot
   199  // be found in the persistence store
   200  type CodePackageNotFoundErr struct {
   201  	PackageID string
   202  }
   203  
   204  func (e CodePackageNotFoundErr) Error() string {
   205  	return fmt.Sprintf("chaincode install package '%s' not found", e.PackageID)
   206  }
   207  
   208  // ListInstalledChaincodes returns an array with information about the
   209  // chaincodes installed in the persistence store
   210  func (s *Store) ListInstalledChaincodes() ([]chaincode.InstalledChaincode, error) {
   211  	files, err := s.ReadWriter.ReadDir(s.Path)
   212  	if err != nil {
   213  		return nil, errors.Wrapf(err, "error reading chaincode directory at %s", s.Path)
   214  	}
   215  
   216  	installedChaincodes := []chaincode.InstalledChaincode{}
   217  	for _, file := range files {
   218  		if instCC, isInstCC := installedChaincodeFromFilename(file.Name()); isInstCC {
   219  			installedChaincodes = append(installedChaincodes, instCC)
   220  		}
   221  	}
   222  	return installedChaincodes, nil
   223  }
   224  
   225  // GetChaincodeInstallPath returns the path where chaincodes
   226  // are installed
   227  func (s *Store) GetChaincodeInstallPath() string {
   228  	return s.Path
   229  }
   230  
   231  // PackageID returns the package ID with the label and hash of the chaincode install package
   232  func PackageID(label string, ccInstallPkg []byte) string {
   233  	hash := util.ComputeSHA256(ccInstallPkg)
   234  	return packageID(label, hash)
   235  }
   236  
   237  func packageID(label string, hash []byte) string {
   238  	return fmt.Sprintf("%s:%x", label, hash)
   239  }
   240  
   241  func CCFileName(packageID string) string {
   242  	return strings.Replace(packageID, ":", ".", 1) + ".tar.gz"
   243  }
   244  
   245  var packageFileMatcher = regexp.MustCompile("^(.+)[.]([0-9a-f]{64})[.]tar[.]gz$")
   246  
   247  func installedChaincodeFromFilename(fileName string) (chaincode.InstalledChaincode, bool) {
   248  	matches := packageFileMatcher.FindStringSubmatch(fileName)
   249  	if len(matches) == 3 {
   250  		label := matches[1]
   251  		hash, _ := hex.DecodeString(matches[2])
   252  		packageID := packageID(label, hash)
   253  
   254  		return chaincode.InstalledChaincode{
   255  			Label:     label,
   256  			Hash:      hash,
   257  			PackageID: packageID,
   258  		}, true
   259  	}
   260  
   261  	return chaincode.InstalledChaincode{}, false
   262  }