github.com/ledgerwatch/erigon-lib@v1.0.0/downloader/downloadercfg/downloadercfg.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 downloadercfg
    18  
    19  import (
    20  	"io/ioutil"
    21  	"net"
    22  	"net/url"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  
    27  	"github.com/anacrolix/dht/v2"
    28  	lg "github.com/anacrolix/log"
    29  	"github.com/anacrolix/torrent"
    30  	"github.com/c2h5oh/datasize"
    31  	"github.com/ledgerwatch/erigon-lib/common"
    32  	"github.com/ledgerwatch/erigon-lib/common/datadir"
    33  	"github.com/ledgerwatch/erigon-lib/common/dir"
    34  	"github.com/ledgerwatch/log/v3"
    35  	"golang.org/x/time/rate"
    36  )
    37  
    38  // DefaultPieceSize - Erigon serves many big files, bigger pieces will reduce
    39  // amount of network announcements, but can't go over 2Mb
    40  // see https://wiki.theory.org/BitTorrentSpecification#Metainfo_File_Structure
    41  const DefaultPieceSize = 2 * 1024 * 1024
    42  
    43  // DefaultNetworkChunkSize - how much data request per 1 network call to peer.
    44  // default: 16Kb
    45  const DefaultNetworkChunkSize = 512 * 1024
    46  
    47  type Cfg struct {
    48  	ClientConfig  *torrent.ClientConfig
    49  	SnapDir       string
    50  	DownloadSlots int
    51  	WebSeedUrls   []*url.URL
    52  	WebSeedFiles  []string
    53  }
    54  
    55  func Default() *torrent.ClientConfig {
    56  	torrentConfig := torrent.NewDefaultClientConfig()
    57  
    58  	// enable dht
    59  	torrentConfig.NoDHT = true
    60  	//torrentConfig.DisableTrackers = true
    61  	//torrentConfig.DisableWebtorrent = true
    62  
    63  	// Reduce defaults - to avoid peers with very bad geography
    64  	//torrentConfig.MinDialTimeout = 1 * time.Second      // default: 3sec
    65  	//torrentConfig.NominalDialTimeout = 10 * time.Second // default: 20sec
    66  	//torrentConfig.HandshakesTimeout = 1 * time.Second   // default: 4sec
    67  
    68  	// see: https://en.wikipedia.org/wiki/TCP_half-open
    69  	//torrentConfig.TotalHalfOpenConns = 100     // default: 100
    70  	//torrentConfig.HalfOpenConnsPerTorrent = 25 // default: 25
    71  	//torrentConfig.TorrentPeersHighWater = 500 // default: 500
    72  	//torrentConfig.TorrentPeersLowWater = 50   // default: 50
    73  
    74  	torrentConfig.Seed = true
    75  	torrentConfig.UpnpID = torrentConfig.UpnpID + "leecher"
    76  
    77  	return torrentConfig
    78  }
    79  
    80  func New(dataDir datadir.Dirs, version string, verbosity lg.Level, downloadRate, uploadRate datasize.ByteSize, port, connsPerFile, downloadSlots int, staticPeers []string, webseeds string) (*Cfg, error) {
    81  	torrentConfig := Default()
    82  	torrentConfig.DataDir = dataDir.Snap // `DataDir` of torrent-client-lib is different from Erigon's `DataDir`. Just same naming.
    83  
    84  	torrentConfig.ExtendedHandshakeClientVersion = version
    85  
    86  	// We would-like to reduce amount of goroutines in Erigon, so reducing next params
    87  	torrentConfig.EstablishedConnsPerTorrent = connsPerFile // default: 50
    88  
    89  	torrentConfig.ListenPort = port
    90  	// check if ipv6 is enabled
    91  	torrentConfig.DisableIPv6 = !getIpv6Enabled()
    92  
    93  	// rates are divided by 2 - I don't know why it works, maybe bug inside torrent lib accounting
    94  	torrentConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(uploadRate.Bytes()), 2*DefaultNetworkChunkSize) // default: unlimited
    95  	if downloadRate.Bytes() < 500_000_000 {
    96  		b := 2 * DefaultNetworkChunkSize
    97  		if downloadRate.Bytes() > DefaultNetworkChunkSize {
    98  			b = int(2 * downloadRate.Bytes())
    99  		}
   100  		torrentConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(downloadRate.Bytes()), b) // default: unlimited
   101  	}
   102  
   103  	// debug
   104  	//	torrentConfig.Debug = false
   105  	torrentConfig.Logger.WithFilterLevel(verbosity)
   106  	torrentConfig.Logger.Handlers = []lg.Handler{adapterHandler{}}
   107  
   108  	if len(staticPeers) > 0 {
   109  		torrentConfig.NoDHT = false
   110  		//defaultNodes := torrentConfig.DhtStartingNodes
   111  		torrentConfig.DhtStartingNodes = func(network string) dht.StartingNodesGetter {
   112  			return func() ([]dht.Addr, error) {
   113  				addrs, err := dht.GlobalBootstrapAddrs(network)
   114  				if err != nil {
   115  					return nil, err
   116  				}
   117  
   118  				for _, seed := range staticPeers {
   119  					if network == "udp" {
   120  						var addr *net.UDPAddr
   121  						addr, err := net.ResolveUDPAddr(network, seed+":80")
   122  						if err != nil {
   123  							log.Warn("[downloader] Cannot UDP resolve address", "network", network, "addr", seed)
   124  							continue
   125  						}
   126  						addrs = append(addrs, dht.NewAddr(addr))
   127  					}
   128  					if network == "tcp" {
   129  						var addr *net.TCPAddr
   130  						addr, err := net.ResolveTCPAddr(network, seed+":80")
   131  						if err != nil {
   132  							log.Warn("[downloader] Cannot TCP resolve address", "network", network, "addr", seed)
   133  							continue
   134  						}
   135  						addrs = append(addrs, dht.NewAddr(addr))
   136  					}
   137  				}
   138  				return addrs, nil
   139  			}
   140  		}
   141  		//staticPeers
   142  	}
   143  
   144  	webseedUrlsOrFiles := common.CliString2Array(webseeds)
   145  	webseedUrls := make([]*url.URL, 0, len(webseedUrlsOrFiles))
   146  	webseedFiles := make([]string, 0, len(webseedUrlsOrFiles))
   147  	for _, webseed := range webseedUrlsOrFiles {
   148  		uri, err := url.ParseRequestURI(webseed)
   149  		if err != nil {
   150  			if strings.HasSuffix(webseed, ".toml") && dir.FileExist(webseed) {
   151  				webseedFiles = append(webseedFiles, webseed)
   152  			}
   153  			continue
   154  		}
   155  		webseedUrls = append(webseedUrls, uri)
   156  	}
   157  	localCfgFile := filepath.Join(dataDir.DataDir, "webseeds.toml") // datadir/webseeds.toml allowed
   158  	if dir.FileExist(localCfgFile) {
   159  		webseedFiles = append(webseedFiles, localCfgFile)
   160  	}
   161  
   162  	return &Cfg{SnapDir: torrentConfig.DataDir,
   163  		ClientConfig: torrentConfig, DownloadSlots: downloadSlots,
   164  		WebSeedUrls: webseedUrls, WebSeedFiles: webseedFiles,
   165  	}, nil
   166  }
   167  
   168  func getIpv6Enabled() bool {
   169  	if runtime.GOOS == "linux" {
   170  		file, err := ioutil.ReadFile("/sys/module/ipv6/parameters/disable")
   171  		if err != nil {
   172  			log.Warn("could not read /sys/module/ipv6/parameters/disable for ipv6 detection")
   173  			return false
   174  		}
   175  		fileContent := strings.TrimSpace(string(file))
   176  		return fileContent != "0"
   177  	}
   178  
   179  	// TODO hotfix: for platforms other than linux disable ipv6
   180  	return false
   181  }