git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/p2pool/p2p/server.go (about)

     1  package p2p
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
    10  	"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/mainchain"
    11  	"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
    12  	p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
    13  	"git.gammaspectra.live/P2Pool/consensus/v3/types"
    14  	"git.gammaspectra.live/P2Pool/consensus/v3/utils"
    15  	unsafeRandom "math/rand/v2"
    16  	"net"
    17  	"net/netip"
    18  	"slices"
    19  	"sync"
    20  	"sync/atomic"
    21  	"time"
    22  	"unsafe"
    23  )
    24  
    25  type P2PoolInterface interface {
    26  	sidechain.ConsensusProvider
    27  	SideChain() *sidechain.SideChain
    28  	MainChain() *mainchain.MainChain
    29  	GetChainMainTip() *sidechain.ChainMain
    30  	GetMinerDataTip() *p2pooltypes.MinerData
    31  	ClientRPC() *client.Client
    32  }
    33  
    34  type PeerListEntry struct {
    35  	AddressPort       netip.AddrPort
    36  	FailedConnections atomic.Uint32
    37  	LastSeenTimestamp atomic.Uint64
    38  }
    39  
    40  type PeerList []*PeerListEntry
    41  
    42  func (l PeerList) Get(addr netip.Addr) *PeerListEntry {
    43  	if i := slices.IndexFunc(l, func(entry *PeerListEntry) bool {
    44  		return entry.AddressPort.Addr().Compare(addr) == 0
    45  	}); i != -1 {
    46  		return l[i]
    47  	}
    48  	return nil
    49  }
    50  func (l PeerList) Delete(addr netip.Addr) PeerList {
    51  	ret := l
    52  	for i := slices.IndexFunc(ret, func(entry *PeerListEntry) bool {
    53  		return entry.AddressPort.Addr().Compare(addr) == 0
    54  	}); i != -1; i = slices.IndexFunc(ret, func(entry *PeerListEntry) bool {
    55  		return entry.AddressPort.Addr().Compare(addr) == 0
    56  	}) {
    57  		ret = slices.Delete(ret, i, i+1)
    58  	}
    59  	return ret
    60  }
    61  
    62  type BanEntry struct {
    63  	Expiration uint64
    64  	Error      error
    65  }
    66  
    67  type Server struct {
    68  	p2pool P2PoolInterface
    69  
    70  	peerId             uint64
    71  	versionInformation p2pooltypes.PeerVersionInformation
    72  
    73  	listenAddress      netip.AddrPort
    74  	externalListenPort uint16
    75  
    76  	useIPv4 bool
    77  	useIPv6 bool
    78  
    79  	ipv6AddrsLock         sync.RWMutex
    80  	ipv6OutgoingAddresses []netip.Addr
    81  
    82  	close    atomic.Bool
    83  	listener *net.TCPListener
    84  
    85  	fastestPeer *Client
    86  
    87  	MaxOutgoingPeers uint32
    88  	MaxIncomingPeers uint32
    89  
    90  	NumOutgoingConnections atomic.Int32
    91  	NumIncomingConnections atomic.Int32
    92  
    93  	PendingOutgoingConnections *utils.CircularBuffer[string]
    94  
    95  	peerList       PeerList
    96  	peerListLock   sync.RWMutex
    97  	moneroPeerList PeerList
    98  
    99  	bansLock sync.RWMutex
   100  	bans     map[[16]byte]BanEntry
   101  
   102  	clientsLock sync.RWMutex
   103  	clients     []*Client
   104  
   105  	cachedBlocksLock sync.RWMutex
   106  	cachedBlocks     map[types.Hash]*sidechain.PoolBlock
   107  
   108  	ctx context.Context
   109  }
   110  
   111  func NewServer(p2pool P2PoolInterface, listenAddress string, externalListenPort uint16, maxOutgoingPeers, maxIncomingPeers uint32, useIPv4, useIPv6 bool, ctx context.Context) (*Server, error) {
   112  	peerId := make([]byte, int(unsafe.Sizeof(uint64(0))))
   113  	_, err := rand.Read(peerId)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	addrPort, err := netip.ParseAddrPort(listenAddress)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	s := &Server{
   124  		p2pool:             p2pool,
   125  		listenAddress:      addrPort,
   126  		externalListenPort: externalListenPort,
   127  		peerId:             binary.LittleEndian.Uint64(peerId),
   128  		MaxOutgoingPeers:   min(maxOutgoingPeers, 450),
   129  		MaxIncomingPeers:   min(maxIncomingPeers, 450),
   130  		cachedBlocks:       make(map[types.Hash]*sidechain.PoolBlock, p2pool.Consensus().ChainWindowSize*3),
   131  		versionInformation: p2pooltypes.PeerVersionInformation{
   132  			SoftwareId:      p2pooltypes.CurrentSoftwareId,
   133  			SoftwareVersion: p2pooltypes.CurrentSoftwareVersion,
   134  			Protocol:        p2pooltypes.SupportedProtocolVersion,
   135  		},
   136  		useIPv4: useIPv4,
   137  		useIPv6: useIPv6,
   138  		ctx:     ctx,
   139  		bans:    make(map[[16]byte]BanEntry),
   140  	}
   141  
   142  	s.PendingOutgoingConnections = utils.NewCircularBuffer[string](int(s.MaxOutgoingPeers))
   143  	s.RefreshOutgoingIPv6()
   144  
   145  	return s, nil
   146  }
   147  
   148  func (s *Server) RefreshOutgoingIPv6() {
   149  	utils.Logf("P2PServer", "Refreshing outgoing IPv6")
   150  	addrs, _ := utils.GetOutboundIPv6()
   151  	s.ipv6AddrsLock.Lock()
   152  	defer s.ipv6AddrsLock.Unlock()
   153  	s.ipv6OutgoingAddresses = addrs
   154  	for _, a := range addrs {
   155  		utils.Logf("P2PServer", "Outgoing IPv6: %s", a.String())
   156  	}
   157  }
   158  
   159  func (s *Server) GetOutgoingIPv6() []netip.Addr {
   160  	s.ipv6AddrsLock.RLock()
   161  	defer s.ipv6AddrsLock.RUnlock()
   162  	return s.ipv6OutgoingAddresses
   163  }
   164  
   165  func (s *Server) ListenPort() uint16 {
   166  	return s.listenAddress.Port()
   167  }
   168  
   169  func (s *Server) ExternalListenPort() uint16 {
   170  	if s.externalListenPort != 0 {
   171  		return s.externalListenPort
   172  	} else {
   173  		return s.listenAddress.Port()
   174  	}
   175  }
   176  
   177  func (s *Server) AddToPeerList(addressPort netip.AddrPort) {
   178  	if addressPort.Addr().IsLoopback() {
   179  		return
   180  	}
   181  	addr := addressPort.Addr().Unmap()
   182  
   183  	if !s.useIPv4 && addr.Is4() {
   184  		return
   185  	} else if !s.useIPv6 && addr.Is6() {
   186  		return
   187  	}
   188  
   189  	s.peerListLock.Lock()
   190  	defer s.peerListLock.Unlock()
   191  	if e := s.peerList.Get(addr); e == nil {
   192  		if ok, _ := s.IsBanned(addr); ok {
   193  			return
   194  		}
   195  		e = &PeerListEntry{
   196  			AddressPort: netip.AddrPortFrom(addr, addressPort.Port()),
   197  		}
   198  		e.LastSeenTimestamp.Store(uint64(time.Now().Unix()))
   199  		s.peerList = append(s.peerList, e)
   200  	} else {
   201  		e.LastSeenTimestamp.Store(uint64(time.Now().Unix()))
   202  	}
   203  }
   204  
   205  func (s *Server) UpdateInPeerList(addressPort netip.AddrPort) {
   206  	if addressPort.Addr().IsLoopback() {
   207  		return
   208  	}
   209  	addr := addressPort.Addr().Unmap()
   210  
   211  	if !s.useIPv4 && addr.Is4() {
   212  		return
   213  	} else if !s.useIPv6 && addr.Is6() {
   214  		return
   215  	}
   216  	s.peerListLock.Lock()
   217  	defer s.peerListLock.Unlock()
   218  	if e := s.peerList.Get(addr); e == nil {
   219  		if ok, _ := s.IsBanned(addr); ok {
   220  			return
   221  		}
   222  		e = &PeerListEntry{
   223  			AddressPort: netip.AddrPortFrom(addr, addressPort.Port()),
   224  		}
   225  		e.LastSeenTimestamp.Store(uint64(time.Now().Unix()))
   226  		s.peerList = append(s.peerList, e)
   227  	} else {
   228  		e.FailedConnections.Store(0)
   229  		e.LastSeenTimestamp.Store(uint64(time.Now().Unix()))
   230  	}
   231  }
   232  
   233  func (s *Server) PeerList() PeerList {
   234  	s.peerListLock.RLock()
   235  	defer s.peerListLock.RUnlock()
   236  
   237  	return slices.Clone(s.peerList)
   238  }
   239  
   240  func (s *Server) RemoveFromPeerList(ip netip.Addr) {
   241  	ip = ip.Unmap()
   242  	s.peerListLock.Lock()
   243  	defer s.peerListLock.Unlock()
   244  	for i := len(s.peerList) - 1; i >= 0; i-- {
   245  		a := s.peerList[i]
   246  		if a.AddressPort.Addr().Compare(ip) == 0 {
   247  			s.peerList = slices.Delete(s.peerList, i, i+1)
   248  			return
   249  		}
   250  	}
   251  }
   252  
   253  func (s *Server) GetFastestClient() *Client {
   254  	var client *Client
   255  	var ping uint64
   256  	for _, c := range s.Clients() {
   257  		p := c.PingDuration.Load()
   258  		if c.IsGood() && p != 0 && (ping == 0 || p < ping) {
   259  			client = c
   260  			ping = p
   261  		}
   262  	}
   263  
   264  	return client
   265  }
   266  
   267  func (s *Server) UpdatePeerList() {
   268  	curTime := uint64(time.Now().Unix())
   269  	for _, c := range s.Clients() {
   270  		if c.IsGood() && curTime >= c.NextOutgoingPeerListRequestTimestamp.Load() {
   271  			c.SendPeerListRequest()
   272  		}
   273  	}
   274  }
   275  
   276  func (s *Server) CleanupBanList() {
   277  	s.bansLock.Lock()
   278  	defer s.bansLock.Unlock()
   279  
   280  	currentTime := uint64(time.Now().Unix())
   281  
   282  	for k, b := range s.bans {
   283  		if currentTime >= b.Expiration {
   284  			delete(s.bans, k)
   285  		}
   286  	}
   287  }
   288  
   289  func (s *Server) UpdateClientConnections() {
   290  
   291  	currentTime := uint64(time.Now().Unix())
   292  	lastUpdated := uint64(s.SideChain().LastUpdated().Unix())
   293  
   294  	var hasGoodPeers bool
   295  	var fastestPeer *Client
   296  
   297  	connectedClients := s.Clients()
   298  
   299  	connectedPeers := make([]netip.Addr, 0, len(connectedClients))
   300  
   301  	for _, client := range connectedClients {
   302  		timeout := uint64(10)
   303  		if client.HandshakeComplete.Load() {
   304  			timeout = 300
   305  		}
   306  
   307  		lastAlive := client.LastActiveTimestamp.Load()
   308  
   309  		if currentTime >= (lastAlive + timeout) {
   310  			idleTime := currentTime - lastAlive
   311  			utils.Logf("P2PServer", "peer %s has been idle for %d seconds, disconnecting", client.AddressPort, idleTime)
   312  			client.Close()
   313  			continue
   314  		}
   315  
   316  		if client.HandshakeComplete.Load() && client.LastBroadcastTimestamp.Load() > 0 {
   317  			// - Side chain is at least 15 minutes newer (last_updated >= client->m_lastBroadcastTimestamp + 900)
   318  			// - It's been at least 10 seconds since side chain updated (cur_time >= last_updated + 10)
   319  			// - It's been at least 10 seconds since the last block request (peer is not syncing)
   320  			// - Peer should have sent a broadcast by now
   321  
   322  			if lastUpdated > 0 && (currentTime >= max(lastUpdated, client.LastBlockRequestTimestamp.Load())+10) && (lastUpdated >= client.LastBroadcastTimestamp.Load()+900) {
   323  				dt := lastUpdated - client.LastBroadcastTimestamp.Load()
   324  				client.Ban(DefaultBanTime, fmt.Errorf("not broadcasting blocks (last update %d seconds ago)", dt))
   325  				client.Close()
   326  				continue
   327  			}
   328  		}
   329  
   330  		connectedPeers = append(connectedPeers, client.AddressPort.Addr().Unmap())
   331  		if client.IsGood() {
   332  			hasGoodPeers = true
   333  			if client.PingDuration.Load() >= 0 && (fastestPeer == nil || fastestPeer.PingDuration.Load() > client.PingDuration.Load()) {
   334  				fastestPeer = client
   335  			}
   336  		}
   337  	}
   338  
   339  	deletedPeers := 0
   340  	peerList := s.PeerList()
   341  	peerList2 := slices.Clone(peerList)
   342  	for _, p := range peerList {
   343  		if slices.ContainsFunc(connectedPeers, func(addr netip.Addr) bool {
   344  			return p.AddressPort.Addr().Compare(addr) == 0
   345  		}) {
   346  			p.LastSeenTimestamp.Store(currentTime)
   347  		}
   348  		if (p.LastSeenTimestamp.Load() + 3600) < currentTime {
   349  			peerList2 = peerList2.Delete(p.AddressPort.Addr())
   350  			deletedPeers++
   351  		}
   352  	}
   353  	peerList = peerList2
   354  
   355  	if deletedPeers > 0 {
   356  		func() {
   357  			s.peerListLock.Lock()
   358  			defer s.peerListLock.Unlock()
   359  			s.peerList = slices.Clone(peerList)
   360  		}()
   361  	}
   362  
   363  	N := int(s.MaxOutgoingPeers)
   364  
   365  	// Special case: when we can't find p2pool peers, scan through monerod peers (try 25 peers at a time)
   366  	if !hasGoodPeers && len(s.moneroPeerList) > 0 {
   367  		utils.Logf("P2PServer", "Scanning monerod peers, %d left", len(s.moneroPeerList))
   368  		for i := 0; i < 25 && len(s.moneroPeerList) > 0; i++ {
   369  			peerList = append(peerList, s.moneroPeerList[len(s.moneroPeerList)-1])
   370  			s.moneroPeerList = s.moneroPeerList[:len(s.moneroPeerList)-1]
   371  		}
   372  		N = len(peerList)
   373  	}
   374  
   375  	var wg sync.WaitGroup
   376  	attempts := 0
   377  
   378  	for i := s.NumOutgoingConnections.Load() - s.NumIncomingConnections.Load(); int(i) < N && len(peerList) > 0; {
   379  		k := unsafeRandom.IntN(len(peerList)) % len(peerList)
   380  		peer := peerList[k]
   381  
   382  		if !slices.ContainsFunc(connectedPeers, func(addr netip.Addr) bool {
   383  			return peer.AddressPort.Addr().Compare(addr) == 0
   384  		}) {
   385  			wg.Add(1)
   386  			attempts++
   387  			go func() {
   388  				defer wg.Done()
   389  
   390  				if s.NumOutgoingConnections.Load() >= int32(s.MaxOutgoingPeers) {
   391  					return
   392  				}
   393  				if err := s.Connect(peer.AddressPort); err != nil {
   394  					utils.Logf("P2PServer", "Connection to %s rejected (%s)", peer.AddressPort.String(), err.Error())
   395  				}
   396  			}()
   397  			i++
   398  		}
   399  
   400  		peerList = slices.Delete(peerList, k, k+1)
   401  	}
   402  
   403  	wg.Wait()
   404  
   405  	if attempts == 0 && !hasGoodPeers && len(s.moneroPeerList) == 0 {
   406  		utils.Logf("P2PServer", "No connections to other p2pool nodes, check your monerod/p2pool/network/firewall setup!")
   407  		if moneroPeerList, err := s.p2pool.ClientRPC().GetPeerList(); err == nil {
   408  			s.moneroPeerList = make(PeerList, 0, len(moneroPeerList.WhiteList))
   409  			for _, p := range moneroPeerList.WhiteList {
   410  				addr, err := netip.ParseAddr(p.Host)
   411  				if err != nil {
   412  					continue
   413  				}
   414  				e := &PeerListEntry{
   415  					AddressPort: netip.AddrPortFrom(addr, s.Consensus().DefaultPort()),
   416  				}
   417  				e.LastSeenTimestamp.Store(uint64(p.LastSeen))
   418  				if ok, _ := s.IsBanned(addr); !ok {
   419  					if !s.useIPv4 && addr.Is4() {
   420  						continue
   421  					} else if !s.useIPv6 && addr.Is6() {
   422  						continue
   423  					}
   424  					s.moneroPeerList = append(s.moneroPeerList, e)
   425  				}
   426  			}
   427  			slices.SortFunc(s.moneroPeerList, func(a, b *PeerListEntry) int {
   428  				aValue, bValue := a.LastSeenTimestamp.Load(), b.LastSeenTimestamp.Load()
   429  				if aValue < bValue {
   430  					return -1
   431  				} else if aValue > bValue {
   432  					return 1
   433  				}
   434  				return 0
   435  			})
   436  			utils.Logf("P2PServer", "monerod peer list loaded (%d peers)", len(s.moneroPeerList))
   437  		}
   438  	}
   439  }
   440  
   441  func (s *Server) AddCachedBlock(block *sidechain.PoolBlock) {
   442  	s.cachedBlocksLock.Lock()
   443  	defer s.cachedBlocksLock.Unlock()
   444  
   445  	if s.cachedBlocks == nil {
   446  		return
   447  	}
   448  
   449  	s.cachedBlocks[block.SideTemplateId(s.p2pool.Consensus())] = block
   450  }
   451  
   452  func (s *Server) ClearCachedBlocks() {
   453  	s.cachedBlocksLock.Lock()
   454  	defer s.cachedBlocksLock.Unlock()
   455  
   456  	s.cachedBlocks = nil
   457  }
   458  
   459  func (s *Server) GetCachedBlock(hash types.Hash) *sidechain.PoolBlock {
   460  	s.cachedBlocksLock.RLock()
   461  	defer s.cachedBlocksLock.RUnlock()
   462  
   463  	return s.cachedBlocks[hash]
   464  }
   465  
   466  func (s *Server) DownloadMissingBlocks() {
   467  	clientList := s.Clients()
   468  
   469  	if len(clientList) == 0 {
   470  		return
   471  	}
   472  
   473  	s.cachedBlocksLock.RLock()
   474  	defer s.cachedBlocksLock.RUnlock()
   475  
   476  	for {
   477  		obtained := false
   478  		for _, h := range s.SideChain().GetMissingBlocks() {
   479  			if b, ok := s.cachedBlocks[h]; ok {
   480  				if _, err, _ := s.SideChain().AddPoolBlockExternal(b); err == nil {
   481  					obtained = true
   482  					continue
   483  				}
   484  			}
   485  
   486  			clientList[unsafeRandom.IntN(len(clientList))].SendUniqueBlockRequest(h)
   487  		}
   488  		if !obtained {
   489  			break
   490  		}
   491  	}
   492  }
   493  
   494  func (s *Server) Listen() (err error) {
   495  	var listener net.Listener
   496  	var ok bool
   497  	if listener, err = (&net.ListenConfig{}).Listen(s.ctx, "tcp", s.listenAddress.String()); err != nil {
   498  		return err
   499  	} else if s.listener, ok = listener.(*net.TCPListener); !ok {
   500  		return errors.New("not a tcp listener")
   501  	} else {
   502  		defer s.listener.Close()
   503  
   504  		var wg sync.WaitGroup
   505  		wg.Add(1)
   506  		go func() {
   507  			defer wg.Done()
   508  			for range utils.ContextTick(s.ctx, time.Second*5) {
   509  				s.UpdatePeerList()
   510  				s.UpdateClientConnections()
   511  			}
   512  		}()
   513  		wg.Add(1)
   514  		go func() {
   515  			defer wg.Done()
   516  			for range utils.ContextTick(s.ctx, time.Second) {
   517  				if s.SideChain().PreCalcFinished() {
   518  					s.ClearCachedBlocks()
   519  					break
   520  				}
   521  
   522  				s.DownloadMissingBlocks()
   523  			}
   524  			// Slow down updates for missing blocks after sync
   525  			for range utils.ContextTick(s.ctx, time.Minute*2) {
   526  				s.DownloadMissingBlocks()
   527  			}
   528  		}()
   529  		wg.Add(1)
   530  		go func() {
   531  			defer wg.Done()
   532  			for range utils.ContextTick(s.ctx, time.Hour) {
   533  				s.RefreshOutgoingIPv6()
   534  			}
   535  		}()
   536  		wg.Add(1)
   537  		go func() {
   538  			defer wg.Done()
   539  			for range utils.ContextTick(s.ctx, time.Minute*5) {
   540  				s.CleanupBanList()
   541  			}
   542  		}()
   543  
   544  		for !s.close.Load() {
   545  			if conn, err := s.listener.AcceptTCP(); err != nil {
   546  				return err
   547  			} else {
   548  				if err = func() error {
   549  					if uint32(s.NumIncomingConnections.Load()) > s.MaxIncomingPeers {
   550  						return errors.New("incoming connections limit was reached")
   551  					}
   552  					if addrPort, err := netip.ParseAddrPort(conn.RemoteAddr().String()); err != nil {
   553  						return err
   554  					} else if !addrPort.Addr().IsLoopback() {
   555  						if clients := s.GetAddressConnected(addrPort.Addr()); !addrPort.Addr().IsLoopback() && len(clients) != 0 {
   556  							return errors.New("peer is already connected as " + clients[0].AddressPort.String())
   557  						}
   558  
   559  						addr := addrPort.Addr().Unmap()
   560  
   561  						if !s.useIPv4 && addr.Is4() {
   562  							return errors.New("peer is IPv4 but we do not allow it")
   563  						} else if !s.useIPv6 && addr.Is6() {
   564  							return errors.New("peer is IPv6 but we do not allow it")
   565  						}
   566  
   567  						if ok, b := s.IsBanned(addr); ok {
   568  							return fmt.Errorf("peer is banned: %w", b.Error)
   569  						}
   570  					}
   571  
   572  					return nil
   573  				}(); err != nil {
   574  					go func() {
   575  						defer conn.Close()
   576  						utils.Logf("P2PServer", "Connection from %s rejected (%s)", conn.RemoteAddr().String(), err.Error())
   577  					}()
   578  					continue
   579  				}
   580  
   581  				func() {
   582  					utils.Logf("P2PServer", "Incoming connection from %s", conn.RemoteAddr().String())
   583  
   584  					s.clientsLock.Lock()
   585  					defer s.clientsLock.Unlock()
   586  					client := NewClient(s, conn)
   587  					client.IsIncomingConnection = true
   588  					s.clients = append(s.clients, client)
   589  					s.NumIncomingConnections.Add(1)
   590  					go client.OnConnection()
   591  				}()
   592  			}
   593  
   594  		}
   595  
   596  		wg.Wait()
   597  	}
   598  
   599  	return nil
   600  }
   601  
   602  func (s *Server) GetAddressConnectedPrefix(prefix netip.Prefix) (result []*Client) {
   603  	s.clientsLock.RLock()
   604  	defer s.clientsLock.RUnlock()
   605  	for _, c := range s.clients {
   606  		if prefix.Contains(c.AddressPort.Addr()) {
   607  			result = append(result, c)
   608  		}
   609  	}
   610  	return result
   611  }
   612  
   613  func (s *Server) GetAddressConnected(addr netip.Addr) (result []*Client) {
   614  	s.clientsLock.RLock()
   615  	defer s.clientsLock.RUnlock()
   616  	for _, c := range s.clients {
   617  		if c.AddressPort.Addr().Compare(addr) == 0 {
   618  			result = append(result, c)
   619  		}
   620  	}
   621  	return result
   622  }
   623  
   624  func (s *Server) DirectConnect(addrPort netip.AddrPort) (*Client, error) {
   625  	addr := addrPort.Addr().Unmap()
   626  	if !s.useIPv4 && addr.Is4() {
   627  		return nil, errors.New("peer is IPv4 but we do not allow it")
   628  	} else if !s.useIPv6 && addr.Is6() {
   629  		return nil, errors.New("peer is IPv6 but we do not allow it")
   630  	}
   631  
   632  	if clients := s.GetAddressConnected(addrPort.Addr()); !addrPort.Addr().IsLoopback() && len(clients) != 0 {
   633  		return nil, errors.New("peer is already connected as " + clients[0].AddressPort.String())
   634  	}
   635  
   636  	if !s.PendingOutgoingConnections.PushUnique(addrPort.Addr().String()) {
   637  		return nil, errors.New("peer is already attempting connection")
   638  	}
   639  
   640  	s.NumOutgoingConnections.Add(1)
   641  
   642  	var localAddr net.Addr
   643  
   644  	//select IPv6 outgoing address
   645  	if addr.Is6() {
   646  		addrs := s.GetOutgoingIPv6()
   647  		if len(addrs) > 1 {
   648  			a := addrs[unsafeRandom.IntN(len(addrs))]
   649  			localAddr = &net.TCPAddr{IP: a.AsSlice(), Zone: a.Zone()}
   650  		} else if len(addrs) == 1 {
   651  			localAddr = &net.TCPAddr{IP: addrs[0].AsSlice(), Zone: addrs[0].Zone()}
   652  		}
   653  	}
   654  
   655  	if localAddr != nil {
   656  		utils.Logf("P2PServer", "Outgoing connection to %s using %s", addrPort.String(), localAddr.String())
   657  	} else {
   658  		utils.Logf("P2PServer", "Outgoing connection to %s", addrPort.String())
   659  	}
   660  
   661  	if conn, err := (&net.Dialer{Timeout: time.Second * 5, LocalAddr: localAddr}).DialContext(s.ctx, "tcp", addrPort.String()); err != nil {
   662  		s.NumOutgoingConnections.Add(-1)
   663  		s.PendingOutgoingConnections.Replace(addrPort.Addr().String(), "")
   664  		if p := s.PeerList().Get(addrPort.Addr()); p != nil {
   665  			if p.FailedConnections.Add(1) >= 10 {
   666  				s.RemoveFromPeerList(addrPort.Addr())
   667  			}
   668  		}
   669  		return nil, err
   670  	} else if tcpConn, ok := conn.(*net.TCPConn); !ok {
   671  		s.NumOutgoingConnections.Add(-1)
   672  		s.PendingOutgoingConnections.Replace(addrPort.Addr().String(), "")
   673  		if p := s.PeerList().Get(addrPort.Addr()); p != nil {
   674  			if p.FailedConnections.Add(1) >= 10 {
   675  				s.RemoveFromPeerList(addrPort.Addr())
   676  			}
   677  		}
   678  		return nil, errors.New("not a tcp connection")
   679  	} else {
   680  		s.clientsLock.Lock()
   681  		defer s.clientsLock.Unlock()
   682  		client := NewClient(s, tcpConn)
   683  		s.clients = append(s.clients, client)
   684  		go client.OnConnection()
   685  		return client, nil
   686  	}
   687  }
   688  
   689  func (s *Server) Connect(addrPort netip.AddrPort) error {
   690  	if ok, b := s.IsBanned(addrPort.Addr()); ok {
   691  		return fmt.Errorf("peer is banned: %w", b.Error)
   692  	}
   693  
   694  	_, err := s.DirectConnect(addrPort)
   695  	return err
   696  }
   697  
   698  func (s *Server) Clients() []*Client {
   699  	s.clientsLock.RLock()
   700  	defer s.clientsLock.RUnlock()
   701  	return slices.Clone(s.clients)
   702  }
   703  
   704  func (s *Server) IsBanned(ip netip.Addr) (bool, *BanEntry) {
   705  	if ip.IsLoopback() {
   706  		return false, nil
   707  	}
   708  	ip = ip.Unmap()
   709  	var prefix netip.Prefix
   710  	if ip.Is6() {
   711  		//ban the /64
   712  		prefix, _ = ip.Prefix(64)
   713  	} else if ip.Is4() {
   714  		//ban only a single ip, /32
   715  		prefix, _ = ip.Prefix(32)
   716  	}
   717  
   718  	if !prefix.IsValid() {
   719  		return false, nil
   720  	}
   721  
   722  	k := prefix.Addr().As16()
   723  
   724  	if b, ok := func() (entry BanEntry, ok bool) {
   725  		s.bansLock.RLock()
   726  		defer s.bansLock.RUnlock()
   727  		entry, ok = s.bans[k]
   728  		return entry, ok
   729  	}(); ok == false {
   730  		return false, nil
   731  	} else if uint64(time.Now().Unix()) >= b.Expiration {
   732  		return false, nil
   733  	} else {
   734  		return true, &b
   735  	}
   736  }
   737  
   738  func (s *Server) Ban(ip netip.Addr, duration time.Duration, err error) {
   739  	if ok, _ := s.IsBanned(ip); ok {
   740  		return
   741  	}
   742  
   743  	utils.Logf("P2PServer", "Banned %s for %s: %s", ip.String(), duration.String(), err.Error())
   744  	if !ip.IsLoopback() {
   745  		ip = ip.Unmap()
   746  		var prefix netip.Prefix
   747  		if ip.Is6() {
   748  			//ban the /64
   749  			prefix, _ = ip.Prefix(64)
   750  		} else if ip.Is4() {
   751  			//ban only a single ip, /32
   752  			prefix, _ = ip.Prefix(32)
   753  		}
   754  
   755  		if prefix.IsValid() {
   756  			func() {
   757  				s.bansLock.Lock()
   758  				defer s.bansLock.Unlock()
   759  				s.bans[prefix.Addr().As16()] = BanEntry{
   760  					Error:      err,
   761  					Expiration: uint64(time.Now().Unix()) + uint64(duration.Seconds()),
   762  				}
   763  			}()
   764  			for _, c := range s.GetAddressConnectedPrefix(prefix) {
   765  				c.Close()
   766  			}
   767  		}
   768  	}
   769  
   770  }
   771  
   772  func (s *Server) Close() {
   773  	if !s.close.Swap(true) && s.listener != nil {
   774  		s.clientsLock.Lock()
   775  		defer s.clientsLock.Unlock()
   776  		for _, c := range s.clients {
   777  			c.Connection.Close()
   778  		}
   779  		s.listener.Close()
   780  	}
   781  }
   782  
   783  func (s *Server) VersionInformation() *p2pooltypes.PeerVersionInformation {
   784  	return &s.versionInformation
   785  }
   786  
   787  func (s *Server) PeerId() uint64 {
   788  	return s.peerId
   789  }
   790  
   791  func (s *Server) SideChain() *sidechain.SideChain {
   792  	return s.p2pool.SideChain()
   793  }
   794  
   795  func (s *Server) MainChain() *mainchain.MainChain {
   796  	return s.p2pool.MainChain()
   797  }
   798  
   799  func (s *Server) Broadcast(block *sidechain.PoolBlock) {
   800  	var message, prunedMessage, compactMessage *ClientMessage
   801  	if block != nil {
   802  		// Full block broadcast
   803  		buffer := make([]byte, 4, block.BufferLength()+4)
   804  		blockData, err := block.AppendBinaryFlags(buffer, false, false)
   805  		if err != nil {
   806  			utils.Panicf("[P2PServer] Tried to broadcast block %s at height %d but received error: %s", block.SideTemplateId(s.Consensus()), block.Side.Height, err)
   807  			return
   808  		}
   809  		binary.LittleEndian.PutUint32(blockData, uint32(len(blockData)-4))
   810  		message = &ClientMessage{
   811  			MessageId: MessageBlockBroadcast,
   812  			Buffer:    blockData,
   813  		}
   814  
   815  		// Pruned block broadcast
   816  		prunedBuffer := make([]byte, 4, block.BufferLength()+4)
   817  		prunedBlockData, _ := block.AppendBinaryFlags(prunedBuffer, true, false)
   818  		binary.LittleEndian.PutUint32(prunedBlockData, uint32(len(prunedBlockData)-4))
   819  		prunedMessage = &ClientMessage{
   820  			MessageId: MessageBlockBroadcast,
   821  			Buffer:    prunedBlockData,
   822  		}
   823  
   824  		// Compact block broadcast
   825  		compactBuffer := make([]byte, 4, block.BufferLength()+4)
   826  		compactBlockData, _ := block.AppendBinaryFlags(compactBuffer, true, true)
   827  		binary.LittleEndian.PutUint32(compactBlockData, uint32(len(compactBlockData)-4))
   828  		if len(compactBlockData) >= len(prunedBlockData) {
   829  			//do not send compact if it ends up larger due to some reason, like parent missing or mismatch in transactions
   830  			compactMessage = prunedMessage
   831  		} else {
   832  			compactMessage = &ClientMessage{
   833  				MessageId: MessageBlockBroadcastCompact,
   834  				Buffer:    compactBlockData,
   835  			}
   836  		}
   837  	} else {
   838  		message = &ClientMessage{
   839  			MessageId: MessageBlockBroadcast,
   840  			Buffer:    binary.LittleEndian.AppendUint32(nil, 0),
   841  		}
   842  		prunedMessage, compactMessage = message, message
   843  	}
   844  
   845  	if !s.versionInformation.SupportsFeature(p2pooltypes.FeaturePrunedBroadcast) {
   846  		prunedMessage = message
   847  	}
   848  
   849  	if !s.versionInformation.SupportsFeature(p2pooltypes.FeatureCompactBroadcast) {
   850  		compactMessage = prunedMessage
   851  	}
   852  
   853  	supportsNotify := s.versionInformation.SupportsFeature(p2pooltypes.FeatureBlockNotify)
   854  
   855  	blockTemplateId := block.SideTemplateId(s.Consensus())
   856  
   857  	go func() {
   858  		for _, c := range s.Clients() {
   859  			if c.IsGood() {
   860  				if !func() (sent bool) {
   861  					broadcastedHashes := c.BroadcastedHashes.Slice()
   862  
   863  					// has peer not broadcasted block parent to us?
   864  					if slices.Index(broadcastedHashes, block.Side.Parent) == -1 {
   865  						return false
   866  					}
   867  					// has peer not broadcasted block uncles to us?
   868  					for _, uncleHash := range block.Side.Uncles {
   869  						if slices.Index(broadcastedHashes, uncleHash) == -1 {
   870  							return false
   871  						}
   872  					}
   873  
   874  					// has peer broadcasted this block to us?
   875  					if slices.Index(broadcastedHashes, blockTemplateId) != -1 &&
   876  						supportsNotify && c.VersionInformation.SupportsFeature(p2pooltypes.FeatureBlockNotify) {
   877  						c.SendBlockNotify(blockTemplateId)
   878  						return true
   879  					}
   880  
   881  					if c.VersionInformation.SupportsFeature(p2pooltypes.FeatureCompactBroadcast) {
   882  						c.SendMessage(compactMessage)
   883  						return true
   884  					} else if c.VersionInformation.SupportsFeature(p2pooltypes.FeaturePrunedBroadcast) {
   885  						c.SendMessage(prunedMessage)
   886  						return true
   887  					}
   888  					return false
   889  				}() {
   890  					//fallback
   891  					c.SendMessage(message)
   892  				}
   893  			}
   894  		}
   895  	}()
   896  }
   897  
   898  func (s *Server) HandshakeConsensusId() types.Hash {
   899  	return s.p2pool.Consensus().Id
   900  }
   901  
   902  func (s *Server) Consensus() *sidechain.Consensus {
   903  	return s.p2pool.Consensus()
   904  }