github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/peersdb/peerdb.go (about)

     1  package peersdb
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  	"hash/crc64"
    10  	"io"
    11  	"net"
    12  	"os"
    13  	"sort"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  	"time"
    18  
    19  	"github.com/piotrnar/gocoin/client/common"
    20  	"github.com/piotrnar/gocoin/lib/btc"
    21  	"github.com/piotrnar/gocoin/lib/others/qdb"
    22  	"github.com/piotrnar/gocoin/lib/others/sys"
    23  )
    24  
    25  const (
    26  	ExpireDeadPeerAfter   = (1 * 24 * time.Hour)
    27  	ExpireAlivePeerAfter  = (3 * 24 * time.Hour)
    28  	ExpireBannedPeerAfter = (7 * 24 * time.Hour)
    29  	MinPeersInDB          = 2500
    30  	MaxPeersInDB          = 70000
    31  	MaxPeersDeviation     = 2500
    32  	ExpirePeersPeriod     = (5 * time.Minute)
    33  )
    34  
    35  /*
    36  Serialized peer record (all values are LSB unless specified otherwise):
    37   [0:4] - Unix timestamp of when last the peer was seen
    38   [4:12] - Services
    39   [12:24] - IPv6 (network order)
    40   [24:28] - IPv4 (network order)
    41   [28:30] - TCP port (big endian)
    42   [30:34] - OPTIONAL:
    43   	highest bit: set to 1 of peer has been seen "alive"
    44  	low 31 bits: if present, unix timestamp of when the peer was banned divided by 2
    45   [35] - OPTIONAL flags
    46      bit(0) - Indicates BanReadon present (byte_len followed by the string)
    47      bit(1) - Indicates CameFromIP present (for IP4: len byte 4 followed by 4 bytes of IP)
    48      bit(2) - Agent string from the Version message
    49  	bits(2-7) - reserved
    50  
    51    Extra fields are always present int the order defined by the flags (from bit 0 to 7).
    52    Each extra field is one byte of length followed by the length bytes of data.
    53  
    54  */
    55  
    56  var (
    57  	PeerDB       *qdb.DB
    58  	proxyPeer    *PeerAddr // when this is not nil we should only connect to this single node
    59  	peerdb_mutex sync.Mutex
    60  
    61  	Testnet     bool
    62  	ConnectOnly string
    63  	Services    uint64 = 1
    64  
    65  	crctab = crc64.MakeTable(crc64.ISO)
    66  )
    67  
    68  type PeerAddr struct {
    69  	btc.NetAddr
    70  	Time       uint32 // When seen last time
    71  	Banned     uint32 // time when this address baned or zero if never
    72  	SeenAlive  bool
    73  	BanReason  string
    74  	CameFromIP []byte
    75  	NodeAgent  string
    76  
    77  	key_set bool // cached to avid key crc64 re-calculations
    78  	key_val uint64
    79  
    80  	// The fields below don't get saved, but are used internaly
    81  	Manual bool // Manually connected (from UI)
    82  	Friend bool // Connected from friends.txt
    83  
    84  	lastSaved int64 // update the record only once per minute
    85  }
    86  
    87  func DefaultTcpPort() uint16 {
    88  	if Testnet {
    89  		return 18333
    90  	} else {
    91  		return 8333
    92  	}
    93  }
    94  
    95  func Lock() {
    96  	peerdb_mutex.Lock()
    97  }
    98  
    99  func Unlock() {
   100  	peerdb_mutex.Unlock()
   101  }
   102  
   103  func read_extra_field(b *bytes.Buffer) []byte {
   104  	le, er := b.ReadByte()
   105  	if er != nil {
   106  		return nil
   107  	}
   108  	dat := make([]byte, int(le))
   109  	_, er = io.ReadFull(b, dat)
   110  	if er != nil {
   111  		return nil
   112  	}
   113  	return dat
   114  }
   115  
   116  func write_extra_field(b *bytes.Buffer, dat []byte) {
   117  	le := len(dat)
   118  	if le > 255 {
   119  		le = 255
   120  	}
   121  	b.WriteByte(byte(le))
   122  	b.Write(dat[:le])
   123  }
   124  
   125  func NewPeer(v []byte) (p *PeerAddr) {
   126  	p = new(PeerAddr)
   127  	if v == nil || len(v) < 30 {
   128  		p.Ip6[10], p.Ip6[11] = 0xff, 0xff
   129  		p.Time = uint32(time.Now().Unix())
   130  		return
   131  	}
   132  	p.Time = binary.LittleEndian.Uint32(v[0:4])
   133  	p.Services = binary.LittleEndian.Uint64(v[4:12])
   134  	copy(p.Ip6[:], v[12:24])
   135  	copy(p.Ip4[:], v[24:28])
   136  	p.Port = binary.BigEndian.Uint16(v[28:30])
   137  	if len(v) >= 34 {
   138  		xd := binary.LittleEndian.Uint32(v[30:34])
   139  		p.SeenAlive = (xd & 0x80000000) != 0
   140  		p.Banned = (xd & 0x7fffffff) << 1
   141  		if !p.SeenAlive && p.Banned > 1893452400 /*Year 2030*/ {
   142  			// Convert from the old DB - TODO: remove it at some point (now is 14th of July 2021)
   143  			p.Banned >>= 1
   144  		}
   145  		if len(v) >= 35 {
   146  			extra_fields := v[34]
   147  			if extra_fields != 0 {
   148  				buf := bytes.NewBuffer(v[35:])
   149  				for bit := 0; bit < 8; bit++ {
   150  					if (extra_fields & 0x01) != 0 {
   151  						dat := read_extra_field(buf)
   152  						if dat == nil {
   153  							break // error
   154  						}
   155  						switch bit {
   156  						case 0:
   157  							p.BanReason = string(dat)
   158  						case 1:
   159  							p.CameFromIP = dat
   160  						case 2:
   161  							p.NodeAgent = string(dat)
   162  						}
   163  					}
   164  					extra_fields >>= 1
   165  					if extra_fields == 0 {
   166  						break
   167  					}
   168  				}
   169  			}
   170  		}
   171  	}
   172  	return
   173  }
   174  
   175  func (p *PeerAddr) Bytes() (res []byte) {
   176  	var x_flags byte
   177  	if p.Banned != 0 && p.BanReason != "" {
   178  		x_flags |= 0x01
   179  	}
   180  	if p.CameFromIP != nil {
   181  		x_flags |= 0x02
   182  	}
   183  	if p.NodeAgent != "" {
   184  		x_flags |= 0x04
   185  	}
   186  	b := new(bytes.Buffer)
   187  	binary.Write(b, binary.LittleEndian, p.Time)
   188  	binary.Write(b, binary.LittleEndian, p.Services)
   189  	b.Write(p.Ip6[:])
   190  	b.Write(p.Ip4[:])
   191  	binary.Write(b, binary.BigEndian, p.Port)
   192  	if p.SeenAlive || x_flags != 0 {
   193  		xd := p.Banned >> 1
   194  		if p.SeenAlive {
   195  			xd |= 0x80000000
   196  		}
   197  		binary.Write(b, binary.LittleEndian, xd)
   198  	}
   199  	if x_flags != 0 {
   200  		b.WriteByte(x_flags)
   201  	}
   202  	if (x_flags & 0x01) != 0 {
   203  		write_extra_field(b, []byte(p.BanReason))
   204  	}
   205  	if (x_flags & 0x02) != 0 {
   206  		write_extra_field(b, p.CameFromIP)
   207  	}
   208  	if (x_flags & 0x04) != 0 {
   209  		write_extra_field(b, []byte(p.NodeAgent))
   210  	}
   211  	res = b.Bytes()
   212  	return
   213  }
   214  
   215  func (p *PeerAddr) UniqID() uint64 {
   216  	if !p.key_set {
   217  		h := crc64.New(crctab)
   218  		h.Write(p.Ip6[:])
   219  		h.Write(p.Ip4[:])
   220  		h.Write([]byte{byte(p.Port >> 8), byte(p.Port)})
   221  		p.key_set = true
   222  		p.key_val = h.Sum64()
   223  	}
   224  	return p.key_val
   225  }
   226  
   227  func NewAddrFromString(ipstr string, force_default_port bool) (p *PeerAddr, e error) {
   228  	port := DefaultTcpPort()
   229  	x := strings.Index(ipstr, ":")
   230  	if x != -1 {
   231  		if !force_default_port {
   232  			v, er := strconv.ParseUint(ipstr[x+1:], 10, 32)
   233  			if er != nil {
   234  				e = er
   235  				return
   236  			}
   237  			if v > 0xffff {
   238  				e = errors.New("Port number too big")
   239  				return
   240  			}
   241  			port = uint16(v)
   242  		}
   243  		ipstr = ipstr[:x] // remove port number
   244  	}
   245  	ipa, er := net.ResolveIPAddr("ip", ipstr)
   246  	if er == nil {
   247  		if ipa == nil || len(ipa.IP) != 4 && len(ipa.IP) != 16 {
   248  			e = errors.New("peerdb.NewAddrFromString(" + ipstr + ") - address error")
   249  		} else {
   250  			p = NewPeer(nil)
   251  			p.Services = Services
   252  			p.Port = port
   253  			if len(ipa.IP) == 4 {
   254  				copy(p.Ip4[:], ipa.IP[:])
   255  			} else {
   256  				copy(p.Ip4[:], ipa.IP[12:16])
   257  				copy(p.Ip6[:], ipa.IP[:12])
   258  			}
   259  			if dbp := PeerDB.Get(qdb.KeyType(p.UniqID())); dbp != nil {
   260  				p = NewPeer(dbp) // if we already had it, take the previous record
   261  			}
   262  		}
   263  	} else {
   264  		e = errors.New("peerdb.NewAddrFromString(" + ipstr + ") - " + er.Error())
   265  	}
   266  	return
   267  }
   268  
   269  func NewIncommingConnection(ipstr string, force_default_port bool) (p *PeerAddr, e error) {
   270  	p, e = NewAddrFromString(ipstr, force_default_port)
   271  	if e != nil {
   272  		return
   273  	}
   274  
   275  	if sys.IsIPBlocked(p.Ip4[:]) {
   276  		e = errors.New(ipstr + " is blocked")
   277  		return
   278  	}
   279  
   280  	if p.Banned != 0 {
   281  		e = errors.New(p.Ip() + " is banned")
   282  		// If the peer is banned but still trying to connect, update the time so it won't be expiring
   283  		now := time.Now().Unix()
   284  		p.Time = uint32(now)
   285  		if now-int64(p.Time) >= 60 { // do not update more often than once per minute
   286  			p.Time = uint32(now)
   287  			p.Save()
   288  		}
   289  		p = nil
   290  	}
   291  	return
   292  }
   293  
   294  func DeleteFromIP(ip []byte) int {
   295  	var ks []qdb.KeyType
   296  	peerdb_mutex.Lock()
   297  	PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 {
   298  		p := NewPeer(v)
   299  		if p.CameFromIP != nil && bytes.Equal(ip, p.CameFromIP) {
   300  			ks = append(ks, k)
   301  		}
   302  		return 0
   303  	})
   304  	for _, k := range ks {
   305  		PeerDB.Del(k)
   306  	}
   307  	peerdb_mutex.Unlock()
   308  	return len(ks)
   309  }
   310  
   311  func ExpirePeers() {
   312  	peerdb_mutex.Lock()
   313  	defer peerdb_mutex.Unlock()
   314  	if PeerDB.Count() > 11*MinPeersInDB/10 {
   315  		common.CountSafe("PeersExpireNeeded")
   316  		now := time.Now()
   317  		expire_dead_before_time := uint32(now.Add(-ExpireDeadPeerAfter).Unix())
   318  		expire_alive_before_time := uint32(now.Add(-ExpireAlivePeerAfter).Unix())
   319  		expire_banned_before_time := uint32(now.Add(-ExpireBannedPeerAfter).Unix())
   320  		recs := make(manyPeers, PeerDB.Count())
   321  		var i, c_dead1, c_dead2, c_seen_alive, c_banned int
   322  		PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 {
   323  			if i >= len(recs) {
   324  				println("ERROR: PeersDB grew since we checked its size. Please report!")
   325  				return 0
   326  			}
   327  			recs[i] = NewPeer(v)
   328  			i++
   329  			return 0
   330  		})
   331  		if i < len(recs) {
   332  			println("ERROR: PeersDB shrunk since we checked its size. Please report!")
   333  			recs = recs[:i]
   334  		}
   335  		sort.Sort(recs)
   336  		for i = len(recs) - 1; i > MinPeersInDB; i-- {
   337  			var delit bool
   338  			rec := recs[i]
   339  			if !rec.SeenAlive {
   340  				if PeerDB.Count() > MaxPeersInDB-MaxPeersDeviation {
   341  					// if DB is full, we delete all the oldest never-alive records
   342  					delit = true
   343  					c_dead1++
   344  				} else {
   345  					// otherwise we only delete those older than 24 hours (ExpireDeadPeerAfter)
   346  					if rec.Time < expire_dead_before_time {
   347  						delit = true
   348  						c_dead2++
   349  					} else {
   350  						break
   351  					}
   352  				}
   353  			} else if rec.Time < expire_alive_before_time {
   354  				if rec.Banned == 0 {
   355  					delit = true
   356  					c_seen_alive++
   357  				} else if rec.Banned < expire_banned_before_time {
   358  					delit = true
   359  					c_banned++
   360  				}
   361  			}
   362  			if delit {
   363  				PeerDB.Del(qdb.KeyType(rec.UniqID()))
   364  				if PeerDB.Count() <= MinPeersInDB {
   365  					break
   366  				}
   367  			}
   368  		}
   369  		common.CounterMutex.Lock()
   370  		if c_dead1 > 0 {
   371  			common.CountAdd("PeersExpiredDead1", uint64(c_dead1))
   372  		}
   373  		if c_dead2 > 0 {
   374  			common.CountAdd("PeersExpiredDead2", uint64(c_dead2))
   375  		}
   376  		if c_seen_alive > 0 {
   377  			common.CountAdd("PeersExpiredAlive", uint64(c_seen_alive))
   378  		}
   379  		if c_banned > 0 {
   380  			common.CountAdd("PeersExpiredBanned", uint64(c_banned))
   381  		}
   382  		common.CounterMutex.Unlock()
   383  		PeerDB.Defrag(false)
   384  	} else {
   385  		common.CountSafe("PeersExpireNone")
   386  	}
   387  }
   388  
   389  func (p *PeerAddr) Save() {
   390  	if p.Banned > p.Time {
   391  		p.lastSaved = int64(p.Banned)
   392  	} else {
   393  		p.lastSaved = int64(p.Time)
   394  	}
   395  	peerdb_mutex.Lock()
   396  	PeerDB.Put(qdb.KeyType(p.UniqID()), p.Bytes())
   397  	//PeerDB.Sync()
   398  	peerdb_mutex.Unlock()
   399  }
   400  
   401  func (p *PeerAddr) Ban(reason string) {
   402  	now := time.Now().Unix()
   403  	p.Banned = uint32(now)
   404  	if p.Banned == 0 || p.BanReason == "" && reason != "" || now-p.lastSaved >= 60 {
   405  		p.BanReason = reason
   406  		p.Save()
   407  	}
   408  }
   409  
   410  func (p *PeerAddr) Alive() {
   411  	now := time.Now().Unix()
   412  	p.Time = uint32(now)
   413  	if !p.SeenAlive || now-p.lastSaved >= 60 {
   414  		p.SeenAlive = true
   415  		p.Save()
   416  	}
   417  }
   418  
   419  func (p *PeerAddr) Dead() {
   420  	peerdb_mutex.Lock()
   421  	if !p.SeenAlive && p.Banned == 0 && PeerDB.Count() > MinPeersInDB {
   422  		PeerDB.Del(qdb.KeyType(p.UniqID()))
   423  		peerdb_mutex.Unlock()
   424  		return
   425  	}
   426  	peerdb_mutex.Unlock()
   427  	p.Time = uint32(time.Now().Unix() - 5*60) // make it last alive 5 minutes ago
   428  	p.Save()
   429  }
   430  
   431  func (p *PeerAddr) Ip() string {
   432  	return fmt.Sprintf("%d.%d.%d.%d:%d", p.Ip4[0], p.Ip4[1], p.Ip4[2], p.Ip4[3], p.Port)
   433  }
   434  
   435  func secs_to_str(t int) string {
   436  	if t < 0 {
   437  		return fmt.Sprint(t)
   438  	}
   439  	if t < 120 {
   440  		return fmt.Sprintf("%d sec", t)
   441  	}
   442  	if t < 5*3600 {
   443  		return fmt.Sprintf("%.2f min", float64(t)/60.0)
   444  	}
   445  	if t < 2*86400 {
   446  		return fmt.Sprintf("%.2f hrs", float64(t)/3600.0)
   447  	}
   448  	return fmt.Sprintf("%.2f dys", float64(t)/(86400.0))
   449  }
   450  
   451  func (p *PeerAddr) String() (s string) {
   452  	s = fmt.Sprintf("%21s  ", p.Ip())
   453  	if p.Services == 0xffffffffffffffff {
   454  		s += "Srv:ALL"
   455  	} else {
   456  		s += fmt.Sprintf("Srv:%3x", p.Services)
   457  	}
   458  
   459  	now := uint32(time.Now().Unix())
   460  	if p.SeenAlive {
   461  		s += "  ALI"
   462  	} else {
   463  		s += "     "
   464  	}
   465  	s += " " + secs_to_str(int(now)-int(p.Time))
   466  
   467  	if p.Banned != 0 {
   468  		s += "  BAN"
   469  		if p.BanReason != "" {
   470  			s += " (" + p.BanReason + ")"
   471  		}
   472  		s += " " + secs_to_str(int(now)-int(p.Banned))
   473  	}
   474  
   475  	if p.NodeAgent != "" {
   476  		s += "  [" + p.NodeAgent + "]"
   477  	}
   478  
   479  	if p.CameFromIP != nil {
   480  		s += "  from "
   481  		if len(p.CameFromIP) == 4 {
   482  			s += fmt.Sprintf("%d.%d.%d.%d", p.CameFromIP[0], p.CameFromIP[1], p.CameFromIP[2], p.CameFromIP[3])
   483  		} else {
   484  			s += hex.EncodeToString(p.CameFromIP)
   485  		}
   486  	}
   487  
   488  	return
   489  }
   490  
   491  type manyPeers []*PeerAddr
   492  
   493  func (mp manyPeers) Len() int {
   494  	return len(mp)
   495  }
   496  
   497  func (mp manyPeers) Less(i, j int) bool {
   498  	return mp[i].Time > mp[j].Time
   499  }
   500  
   501  func (mp manyPeers) Swap(i, j int) {
   502  	mp[i], mp[j] = mp[j], mp[i]
   503  }
   504  
   505  // GetRecentPeersExt fetches a given number of best (most recenty seen) peers.
   506  func GetRecentPeers(limit uint, sort_result bool, ignorePeer func(*PeerAddr) bool) (res manyPeers) {
   507  	if proxyPeer != nil {
   508  		if ignorePeer == nil || !ignorePeer(proxyPeer) {
   509  			return manyPeers{proxyPeer}
   510  		}
   511  		return manyPeers{}
   512  	}
   513  	res = make(manyPeers, 0)
   514  	peerdb_mutex.Lock()
   515  	PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 {
   516  		ad := NewPeer(v)
   517  		if sys.ValidIp4(ad.Ip4[:]) && !sys.IsIPBlocked(ad.Ip4[:]) {
   518  			if ignorePeer == nil || !ignorePeer(ad) {
   519  				res = append(res, ad)
   520  				if !sort_result && len(res) >= int(limit) {
   521  					return qdb.BR_ABORT
   522  				}
   523  			}
   524  		}
   525  		return 0
   526  	})
   527  	peerdb_mutex.Unlock()
   528  	if sort_result && len(res) > 0 {
   529  		sort.Sort(res)
   530  		if int(limit) < len(res) {
   531  			res = res[:int(limit)]
   532  		}
   533  	}
   534  	return
   535  }
   536  
   537  func initSeeds(seeds []string, port uint16) {
   538  	for i := range seeds {
   539  		ad, er := net.LookupHost(seeds[i])
   540  		if er == nil {
   541  			//println(len(ad), "addrs from", seeds[i])
   542  			for j := range ad {
   543  				ip := net.ParseIP(ad[j])
   544  				if ip != nil && len(ip) == 16 {
   545  					p := NewPeer(nil)
   546  					p.Services = 0xFFFFFFFFFFFFFFFF
   547  					copy(p.Ip6[:], ip[:12])
   548  					copy(p.Ip4[:], ip[12:16])
   549  					p.Port = port
   550  					if dbp := PeerDB.Get(qdb.KeyType(p.UniqID())); dbp != nil {
   551  						_p := NewPeer(dbp)
   552  						_p.Time = p.Time
   553  						_p.Save() // if we already had it, only update the time field
   554  					} else {
   555  						p.Save()
   556  					}
   557  				}
   558  			}
   559  		} else {
   560  			println("initSeeds LookupHost", seeds[i], "-", er.Error())
   561  		}
   562  	}
   563  }
   564  
   565  // InitPeers should be called from the main thread.
   566  func InitPeers(dir string) {
   567  	PeerDB, _ = qdb.NewDB(dir+"peers3", true)
   568  
   569  	if ConnectOnly != "" {
   570  		x := strings.Index(ConnectOnly, ":")
   571  		if x == -1 {
   572  			ConnectOnly = fmt.Sprint(ConnectOnly, ":", DefaultTcpPort())
   573  		}
   574  		oa, e := net.ResolveTCPAddr("tcp4", ConnectOnly)
   575  		if e != nil {
   576  			println(e.Error(), ConnectOnly)
   577  			os.Exit(1)
   578  		}
   579  		proxyPeer = NewPeer(nil)
   580  		proxyPeer.Services = Services
   581  		copy(proxyPeer.Ip4[:], oa.IP[12:16])
   582  		proxyPeer.Port = uint16(oa.Port)
   583  		fmt.Printf("Connect to bitcoin network via %d.%d.%d.%d:%d\n",
   584  			proxyPeer.Ip4[0], proxyPeer.Ip4[1], proxyPeer.Ip4[2], proxyPeer.Ip4[3], proxyPeer.Port)
   585  	} else if PeerDB.Count() < MinPeersInDB {
   586  		go func() {
   587  			if !Testnet {
   588  				initSeeds([]string{
   589  					"seed.bitcoin.sipa.be",
   590  					"dnsseed.bluematt.me",
   591  					"dnsseed.bitcoin.dashjr.org",
   592  					"seed.bitcoinstats.com",
   593  					"seed.bitcoin.jonasschnelli.ch",
   594  					"seed.btc.petertodd.org",
   595  					"seed.bitcoin.sprovoost.nl",
   596  					"seed.bitnodes.io",
   597  					"dnsseed.emzy.de",
   598  					"seed.bitcoin.wiz.biz",
   599  				}, 8333)
   600  			} else {
   601  				initSeeds([]string{
   602  					"testnet-seed.bitcoin.jonasschnelli.ch",
   603  					"seed.tbtc.petertodd.org",
   604  					"seed.testnet.bitcoin.sprovoost.nl",
   605  					"testnet-seed.bluematt.me",
   606  				}, 18333)
   607  			}
   608  		}()
   609  	}
   610  }
   611  
   612  func ClosePeerDB() {
   613  	if PeerDB != nil {
   614  		fmt.Println("Closing peer DB")
   615  		PeerDB.Sync()
   616  		PeerDB.Defrag(true)
   617  		PeerDB.Close()
   618  		PeerDB = nil
   619  	}
   620  }