github.com/ledgerwatch/erigon-lib@v1.0.0/downloader/snaptype/files.go (about)

     1  /*
     2     Copyright 2021 Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package snaptype
    18  
    19  import (
    20  	"encoding/hex"
    21  	"errors"
    22  	"fmt"
    23  	"github.com/anacrolix/torrent/metainfo"
    24  	"os"
    25  	"path/filepath"
    26  	"strconv"
    27  	"strings"
    28  
    29  	"github.com/ledgerwatch/erigon-lib/common/dir"
    30  	"golang.org/x/exp/slices"
    31  )
    32  
    33  type Type int
    34  
    35  const (
    36  	Headers Type = iota
    37  	Bodies
    38  	Transactions
    39  	BorEvents
    40  	BorSpans
    41  	NumberOfTypes
    42  )
    43  
    44  func (ft Type) String() string {
    45  	switch ft {
    46  	case Headers:
    47  		return "headers"
    48  	case Bodies:
    49  		return "bodies"
    50  	case Transactions:
    51  		return "transactions"
    52  	case BorEvents:
    53  		return "borevents"
    54  	case BorSpans:
    55  		return "borspans"
    56  	default:
    57  		panic(fmt.Sprintf("unknown file type: %d", ft))
    58  	}
    59  }
    60  
    61  func ParseFileType(s string) (Type, bool) {
    62  	switch s {
    63  	case "headers":
    64  		return Headers, true
    65  	case "bodies":
    66  		return Bodies, true
    67  	case "transactions":
    68  		return Transactions, true
    69  	case "borevents":
    70  		return BorEvents, true
    71  	case "borspans":
    72  		return BorSpans, true
    73  	default:
    74  		return NumberOfTypes, false
    75  	}
    76  }
    77  
    78  type IdxType string
    79  
    80  const (
    81  	Transactions2Block IdxType = "transactions-to-block"
    82  )
    83  
    84  func (it IdxType) String() string { return string(it) }
    85  
    86  var AllSnapshotTypes = []Type{Headers, Bodies, Transactions}
    87  
    88  var (
    89  	ErrInvalidFileName = fmt.Errorf("invalid compressed file name")
    90  )
    91  
    92  func FileName(from, to uint64, fileType string) string {
    93  	return fmt.Sprintf("v1-%06d-%06d-%s", from/1_000, to/1_000, fileType)
    94  }
    95  func SegmentFileName(from, to uint64, t Type) string   { return FileName(from, to, t.String()) + ".seg" }
    96  func DatFileName(from, to uint64, fType string) string { return FileName(from, to, fType) + ".dat" }
    97  func IdxFileName(from, to uint64, fType string) string { return FileName(from, to, fType) + ".idx" }
    98  
    99  func FilterExt(in []FileInfo, expectExt string) (out []FileInfo) {
   100  	for _, f := range in {
   101  		if f.Ext != expectExt { // filter out only compressed files
   102  			continue
   103  		}
   104  		out = append(out, f)
   105  	}
   106  	return out
   107  }
   108  func FilesWithExt(dir, expectExt string) ([]FileInfo, error) {
   109  	files, err := ParseDir(dir)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return FilterExt(files, expectExt), nil
   114  }
   115  
   116  func IsCorrectFileName(name string) bool {
   117  	parts := strings.Split(name, "-")
   118  	return len(parts) == 4 && parts[3] != "v1"
   119  }
   120  
   121  func IsCorrectHistoryFileName(name string) bool {
   122  	parts := strings.Split(name, ".")
   123  	return len(parts) == 3
   124  }
   125  
   126  func ParseFileName(dir, fileName string) (res FileInfo, ok bool) {
   127  	ext := filepath.Ext(fileName)
   128  	onlyName := fileName[:len(fileName)-len(ext)]
   129  	parts := strings.Split(onlyName, "-")
   130  	if len(parts) < 4 {
   131  		return res, ok
   132  	}
   133  	version := parts[0]
   134  	_ = version
   135  	from, err := strconv.ParseUint(parts[1], 10, 64)
   136  	if err != nil {
   137  		return
   138  	}
   139  	to, err := strconv.ParseUint(parts[2], 10, 64)
   140  	if err != nil {
   141  		return
   142  	}
   143  	ft, ok := ParseFileType(parts[3])
   144  	if !ok {
   145  		return res, ok
   146  	}
   147  	return FileInfo{From: from * 1_000, To: to * 1_000, Path: filepath.Join(dir, fileName), T: ft, Ext: ext}, ok
   148  }
   149  
   150  const Erigon3SeedableSteps = 32
   151  const Erigon2SegmentSize = 500_000
   152  const Erigon2MinSegmentSize = 1_000
   153  
   154  // FileInfo - parsed file metadata
   155  type FileInfo struct {
   156  	Version   uint8
   157  	From, To  uint64
   158  	Path, Ext string
   159  	T         Type
   160  }
   161  
   162  func (f FileInfo) TorrentFileExists() bool { return dir.FileExist(f.Path + ".torrent") }
   163  func (f FileInfo) Seedable() bool          { return f.To-f.From == Erigon2SegmentSize }
   164  func (f FileInfo) NeedTorrentFile() bool   { return f.Seedable() && !f.TorrentFileExists() }
   165  
   166  func IdxFiles(dir string) (res []FileInfo, err error) { return FilesWithExt(dir, ".idx") }
   167  func Segments(dir string) (res []FileInfo, err error) { return FilesWithExt(dir, ".seg") }
   168  func TmpFiles(dir string) (res []string, err error) {
   169  	files, err := os.ReadDir(dir)
   170  	if err != nil {
   171  		if errors.Is(err, os.ErrNotExist) {
   172  			return []string{}, nil
   173  		}
   174  		return nil, err
   175  	}
   176  	for _, f := range files {
   177  		if f.IsDir() || len(f.Name()) < 3 {
   178  			continue
   179  		}
   180  		if filepath.Ext(f.Name()) != ".tmp" {
   181  			continue
   182  		}
   183  		res = append(res, filepath.Join(dir, f.Name()))
   184  	}
   185  	return res, nil
   186  }
   187  
   188  // ParseDir - reading dir (
   189  func ParseDir(dir string) (res []FileInfo, err error) {
   190  	files, err := os.ReadDir(dir)
   191  	if err != nil {
   192  		if errors.Is(err, os.ErrNotExist) {
   193  			return []FileInfo{}, nil
   194  		}
   195  		return nil, err
   196  	}
   197  	for _, f := range files {
   198  		fileInfo, err := f.Info()
   199  		if err != nil {
   200  			return nil, err
   201  		}
   202  		if f.IsDir() || fileInfo.Size() == 0 || len(f.Name()) < 3 {
   203  			continue
   204  		}
   205  
   206  		meta, ok := ParseFileName(dir, f.Name())
   207  		if !ok {
   208  			continue
   209  		}
   210  		res = append(res, meta)
   211  	}
   212  	slices.SortFunc(res, func(i, j FileInfo) bool {
   213  		if i.Version != j.Version {
   214  			return i.Version < j.Version
   215  		}
   216  		if i.From != j.From {
   217  			return i.From < j.From
   218  		}
   219  		if i.To != j.To {
   220  			return i.To < j.To
   221  		}
   222  		if i.T != j.T {
   223  			return i.T < j.T
   224  		}
   225  		return i.Ext < j.Ext
   226  	})
   227  
   228  	return res, nil
   229  }
   230  
   231  func Hex2InfoHash(in string) (infoHash metainfo.Hash) {
   232  	inHex, err := hex.DecodeString(in)
   233  	if err != nil {
   234  		panic(err)
   235  	}
   236  	copy(infoHash[:], inHex)
   237  	return infoHash
   238  }