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 }