2022-11-01 11:22:00 +00:00
package p2p
import (
2022-11-07 18:38:29 +00:00
"bufio"
2022-11-01 11:22:00 +00:00
"crypto/rand"
"encoding/binary"
2022-11-07 14:58:02 +00:00
"encoding/hex"
"errors"
"fmt"
2022-11-03 11:32:07 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
2023-03-11 18:08:04 +00:00
p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types"
2022-11-01 11:22:00 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
2022-11-06 16:01:36 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
2022-11-01 11:22:00 +00:00
"golang.org/x/exp/slices"
2022-11-07 18:38:29 +00:00
"io"
2022-11-01 11:22:00 +00:00
"log"
2022-12-02 13:34:18 +00:00
unsafeRandom "math/rand"
2022-11-01 11:22:00 +00:00
"net"
"net/netip"
2022-12-09 12:13:00 +00:00
"sync"
2022-11-01 11:22:00 +00:00
"sync/atomic"
"time"
2022-11-03 11:32:07 +00:00
"unsafe"
2022-11-01 11:22:00 +00:00
)
2022-11-03 11:32:07 +00:00
const DefaultBanTime = time . Second * 600
2022-11-06 16:01:36 +00:00
const PeerListResponseMaxPeers = 16
2022-11-03 11:32:07 +00:00
2023-04-21 13:41:50 +00:00
const MaxBufferSize = 128 * 1024
var smallBufferPool = sync . Pool {
New : func ( ) any {
return make ( [ ] byte , 16384 )
} ,
}
func getBuffer ( length int ) [ ] byte {
if length <= 16384 {
return smallBufferPool . Get ( ) . ( [ ] byte )
}
return make ( [ ] byte , length )
}
func returnBuffer ( x [ ] byte ) {
if len ( x ) <= 16384 {
smallBufferPool . Put ( x )
}
}
2022-12-09 12:13:00 +00:00
2022-11-01 11:22:00 +00:00
type Client struct {
2022-12-11 12:31:43 +00:00
// Peer general static-ish information
2022-12-11 16:27:46 +00:00
PeerId atomic . Uint64
2023-03-11 18:08:04 +00:00
VersionInformation p2pooltypes . PeerVersionInformation
2022-12-11 12:31:43 +00:00
IsIncomingConnection bool
ConnectionTime time . Time
ListenPort atomic . Uint32
AddressPort netip . AddrPort
// Peer general dynamic-ish information
BroadcastMaxHeight atomic . Uint64
PingDuration atomic . Uint64
// Internal values
Owner * Server
Connection * net . TCPConn
Closed atomic . Bool
2023-04-19 17:18:35 +00:00
banErrorLock sync . Mutex
banError error
2023-04-19 13:16:56 +00:00
LastBroadcastTimestamp atomic . Uint64
LastBlockRequestTimestamp atomic . Uint64
2022-12-11 12:31:43 +00:00
LastIncomingPeerListRequestTime time . Time
LastActiveTimestamp atomic . Uint64
LastPeerListRequestTimestamp atomic . Uint64
NextOutgoingPeerListRequestTimestamp atomic . Uint64
//State properties
2023-04-19 19:48:30 +00:00
HandshakeComplete atomic . Bool
SentHandshakeSolution atomic . Bool
2022-11-03 11:32:07 +00:00
2023-04-19 19:48:30 +00:00
LastKnownTip atomic . Pointer [ sidechain . PoolBlock ]
2023-04-19 17:18:35 +00:00
2022-12-04 00:46:22 +00:00
BroadcastedHashes * utils . CircularBuffer [ types . Hash ]
2022-12-09 12:13:00 +00:00
RequestedHashes * utils . CircularBuffer [ types . Hash ]
2022-12-03 22:43:18 +00:00
2022-12-18 14:53:32 +00:00
blockPendingRequests chan types . Hash
2022-11-03 11:32:07 +00:00
2022-11-07 14:58:02 +00:00
expectedMessage MessageId
2022-11-01 11:22:00 +00:00
2022-11-07 14:58:02 +00:00
handshakeChallenge HandshakeChallenge
2022-11-06 10:58:19 +00:00
2022-11-07 14:58:02 +00:00
closeChannel chan struct { }
2022-12-09 12:13:00 +00:00
sendLock sync . Mutex
2022-11-01 11:22:00 +00:00
}
2022-12-09 12:13:00 +00:00
func NewClient ( owner * Server , conn * net . TCPConn ) * Client {
2022-11-01 11:22:00 +00:00
c := & Client {
2022-12-18 14:53:32 +00:00
Owner : owner ,
Connection : conn ,
ConnectionTime : time . Now ( ) ,
AddressPort : netip . MustParseAddrPort ( conn . RemoteAddr ( ) . String ( ) ) ,
expectedMessage : MessageHandshakeChallenge ,
closeChannel : make ( chan struct { } ) ,
BroadcastedHashes : utils . NewCircularBuffer [ types . Hash ] ( 8 ) ,
RequestedHashes : utils . NewCircularBuffer [ types . Hash ] ( 16 ) ,
blockPendingRequests : make ( chan types . Hash , 100 ) , //allow max 100 pending block requests at the same time
2022-11-01 11:22:00 +00:00
}
2022-12-11 12:31:43 +00:00
c . LastActiveTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2022-11-01 11:22:00 +00:00
return c
}
2023-04-19 17:18:35 +00:00
func ( c * Client ) BanError ( ) error {
c . banErrorLock . Lock ( )
c . banErrorLock . Unlock ( )
return c . banError
}
2023-04-23 15:21:10 +00:00
func ( c * Client ) SetError ( err error ) {
c . banErrorLock . Lock ( )
c . banErrorLock . Unlock ( )
if c . banError == nil {
c . banError = err
}
}
2022-11-07 14:58:02 +00:00
func ( c * Client ) Ban ( duration time . Duration , err error ) {
2023-04-19 17:18:35 +00:00
2023-04-23 15:21:10 +00:00
c . SetError ( err )
2022-11-07 14:58:02 +00:00
c . Owner . Ban ( c . AddressPort . Addr ( ) , duration , err )
2023-04-19 13:16:56 +00:00
c . Owner . RemoveFromPeerList ( c . AddressPort . Addr ( ) )
2022-11-03 11:32:07 +00:00
c . Close ( )
}
func ( c * Client ) OnAfterHandshake ( ) {
c . SendListenPort ( )
c . SendBlockRequest ( types . ZeroHash )
2022-12-14 18:31:59 +00:00
2023-04-19 13:16:56 +00:00
c . LastBroadcastTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2022-11-03 11:32:07 +00:00
}
2022-12-18 14:53:32 +00:00
func ( c * Client ) getNextBlockRequest ( ) ( id types . Hash , ok bool ) {
select {
case id = <- c . blockPendingRequests :
return id , true
default :
return types . ZeroHash , false
}
}
2022-11-03 11:32:07 +00:00
func ( c * Client ) SendListenPort ( ) {
2023-04-23 14:46:47 +00:00
c . SendMessage ( & ClientMessage {
MessageId : MessageListenPort ,
Buffer : binary . LittleEndian . AppendUint32 ( nil , uint32 ( c . Owner . ExternalListenPort ( ) ) ) ,
} )
2022-11-03 11:32:07 +00:00
}
2023-04-19 21:32:58 +00:00
func ( c * Client ) SendMissingBlockRequestAtRandom ( hash types . Hash , allowedClients [ ] * Client ) [ ] * Client {
if hash == types . ZeroHash || c . Owner . SideChain ( ) . GetPoolBlockByTemplateId ( hash ) != nil {
return allowedClients
}
if b := c . Owner . GetCachedBlock ( hash ) ; b != nil {
log . Printf ( "[P2PClient] Using cached block for id = %s" , hash . String ( ) )
if _ , err , _ := c . Owner . SideChain ( ) . AddPoolBlockExternal ( b ) ; err == nil {
return allowedClients
}
}
if len ( allowedClients ) == 0 {
allowedClients = append ( allowedClients , c )
}
for len ( allowedClients ) > 0 {
k := unsafeRandom . Intn ( len ( allowedClients ) ) % len ( allowedClients )
client := allowedClients [ k ]
if client . IsGood ( ) && len ( client . blockPendingRequests ) < 20 {
client . SendBlockRequest ( hash )
break
} else {
allowedClients = slices . Delete ( allowedClients , k , k + 1 )
}
}
return allowedClients
}
2022-12-04 14:15:33 +00:00
func ( c * Client ) SendMissingBlockRequest ( hash types . Hash ) {
2023-03-10 21:03:12 +00:00
if hash == types . ZeroHash || c . Owner . SideChain ( ) . GetPoolBlockByTemplateId ( hash ) != nil {
2022-12-09 12:13:00 +00:00
return
}
2023-03-10 15:45:22 +00:00
if b := c . Owner . GetCachedBlock ( hash ) ; b != nil {
log . Printf ( "[P2PClient] Using cached block for id = %s" , hash . String ( ) )
2023-04-19 18:24:55 +00:00
if missingBlocks , err , _ := c . Owner . SideChain ( ) . AddPoolBlockExternal ( b ) ; err == nil {
2023-03-10 15:45:22 +00:00
for _ , id := range missingBlocks {
c . SendMissingBlockRequest ( id )
}
return
}
}
2022-12-09 12:13:00 +00:00
// do not re-request hashes that have been requested
if ! c . RequestedHashes . PushUnique ( hash ) {
return
}
// If the initial sync is not finished yet, try to ask the fastest peer too
if ! c . Owner . SideChain ( ) . PreCalcFinished ( ) {
2022-12-04 14:15:33 +00:00
fastest := c . Owner . GetFastestClient ( )
if fastest != nil && c != fastest && ! c . Owner . SideChain ( ) . PreCalcFinished ( ) {
//send towards the fastest peer as well
2022-12-09 12:13:00 +00:00
fastest . SendMissingBlockRequest ( hash )
2022-12-04 14:15:33 +00:00
}
2022-12-09 12:13:00 +00:00
}
2022-12-04 14:15:33 +00:00
c . SendBlockRequest ( hash )
}
2022-12-12 15:58:45 +00:00
func ( c * Client ) SendUniqueBlockRequest ( hash types . Hash ) {
if hash == types . ZeroHash {
return
}
// do not re-request hashes that have been requested
if ! c . RequestedHashes . PushUnique ( hash ) {
return
}
c . SendBlockRequest ( hash )
}
2022-12-18 14:53:32 +00:00
func ( c * Client ) SendBlockRequest ( id types . Hash ) {
2023-04-19 21:32:58 +00:00
if len ( c . blockPendingRequests ) < 80 {
c . blockPendingRequests <- id
c . SendMessage ( & ClientMessage {
MessageId : MessageBlockRequest ,
Buffer : id [ : ] ,
} )
}
2022-11-03 11:32:07 +00:00
}
func ( c * Client ) SendBlockResponse ( block * sidechain . PoolBlock ) {
if block != nil {
2023-04-21 13:41:50 +00:00
blockData , err := block . MarshalBinary ( )
if err != nil {
log . Printf ( "[P2PClient] Peer %s tried to respond with a block but received error, disconnecting: %s" , c . AddressPort , err )
c . Close ( )
return
}
2022-11-06 16:01:36 +00:00
c . SendMessage ( & ClientMessage {
MessageId : MessageBlockResponse ,
2022-11-07 22:59:52 +00:00
Buffer : append ( binary . LittleEndian . AppendUint32 ( make ( [ ] byte , 0 , len ( blockData ) + 4 ) , uint32 ( len ( blockData ) ) ) , blockData ... ) ,
2022-11-06 16:01:36 +00:00
} )
2022-11-03 11:32:07 +00:00
} else {
2022-11-06 16:01:36 +00:00
c . SendMessage ( & ClientMessage {
MessageId : MessageBlockResponse ,
2022-11-07 22:59:52 +00:00
Buffer : binary . LittleEndian . AppendUint32 ( nil , 0 ) ,
2022-11-06 16:01:36 +00:00
} )
2022-11-06 10:58:19 +00:00
}
}
2023-04-19 21:32:58 +00:00
func ( c * Client ) SendInternalFastTemplateHeaderSyncRequest ( hash types . Hash ) {
buf := make ( [ ] byte , 0 , binary . MaxVarintLen64 * 2 + types . HashSize )
buf = binary . AppendUvarint ( buf , uint64 ( InternalMessageFastTemplateHeaderSyncRequest ) )
buf = binary . AppendUvarint ( buf , uint64 ( types . HashSize ) )
buf = append ( buf , hash [ : ] ... )
c . SendMessage ( & ClientMessage {
MessageId : MessageInternal ,
Buffer : buf ,
} )
}
func ( c * Client ) SendInternalFastTemplateHeaderSyncResponse ( hashes ... types . Hash ) {
buf := make ( [ ] byte , 0 , binary . MaxVarintLen64 * 2 + 8 + len ( hashes ) * types . HashSize )
buf = binary . AppendUvarint ( buf , uint64 ( InternalMessageFastTemplateHeaderSyncResponse ) )
buf = binary . AppendUvarint ( buf , uint64 ( 8 + len ( hashes ) * types . HashSize ) )
buf = binary . LittleEndian . AppendUint64 ( buf , uint64 ( len ( hashes ) ) )
for _ , h := range hashes {
buf = append ( buf , h [ : ] ... )
}
c . SendMessage ( & ClientMessage {
MessageId : MessageInternal ,
Buffer : buf ,
} )
}
2022-11-03 11:32:07 +00:00
func ( c * Client ) SendPeerListRequest ( ) {
2022-12-11 12:31:43 +00:00
c . NextOutgoingPeerListRequestTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) + 60 + ( unsafeRandom . Uint64 ( ) % 61 ) )
2022-11-06 16:01:36 +00:00
c . SendMessage ( & ClientMessage {
MessageId : MessagePeerListRequest ,
} )
2022-12-11 12:31:43 +00:00
c . LastPeerListRequestTimestamp . Store ( uint64 ( time . Now ( ) . UnixMicro ( ) ) )
2023-03-10 15:45:22 +00:00
//log.Printf("[P2PClient] Sending PEER_LIST_REQUEST to %s", c.AddressPort.String())
2022-11-06 16:01:36 +00:00
}
func ( c * Client ) SendPeerListResponse ( list [ ] netip . AddrPort ) {
if len ( list ) > PeerListResponseMaxPeers {
return
}
2022-11-07 22:59:52 +00:00
buf := make ( [ ] byte , 0 , 1 + len ( list ) * ( 1 + 16 + 2 ) )
2022-11-06 16:01:36 +00:00
buf = append ( buf , byte ( len ( list ) ) )
for i := range list {
2023-03-05 14:06:49 +00:00
//TODO: check ipv4 gets sent properly
2023-03-11 18:08:04 +00:00
if list [ i ] . Addr ( ) . Is6 ( ) && ! p2pooltypes . IsPeerVersionInformation ( list [ i ] ) {
2022-11-06 16:01:36 +00:00
buf = append ( buf , 1 )
} else {
buf = append ( buf , 0 )
}
ip := list [ i ] . Addr ( ) . As16 ( )
buf = append ( buf , ip [ : ] ... )
buf = binary . LittleEndian . AppendUint16 ( buf , list [ i ] . Port ( ) )
}
c . SendMessage ( & ClientMessage {
MessageId : MessagePeerListResponse ,
2022-11-07 22:59:52 +00:00
Buffer : buf ,
2022-11-06 16:01:36 +00:00
} )
2022-11-03 11:32:07 +00:00
}
2022-12-02 13:34:18 +00:00
func ( c * Client ) IsGood ( ) bool {
return c . HandshakeComplete . Load ( ) && c . ListenPort . Load ( ) > 0
}
2022-11-01 11:22:00 +00:00
func ( c * Client ) OnConnection ( ) {
2022-12-11 12:31:43 +00:00
c . LastActiveTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2022-11-01 11:22:00 +00:00
c . sendHandshakeChallenge ( )
2022-11-03 11:32:07 +00:00
2022-12-09 12:13:00 +00:00
var messageIdBuf [ 1 ] byte
var messageId MessageId
2022-11-03 11:32:07 +00:00
for ! c . Closed . Load ( ) {
2022-12-09 12:13:00 +00:00
if _ , err := io . ReadFull ( c , messageIdBuf [ : ] ) ; err != nil {
2023-04-19 15:24:36 +00:00
c . Close ( )
2022-11-03 11:32:07 +00:00
return
}
2022-12-09 12:13:00 +00:00
messageId = MessageId ( messageIdBuf [ 0 ] )
2022-11-07 18:38:29 +00:00
if ! c . HandshakeComplete . Load ( ) && messageId != c . expectedMessage {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "unexpected pre-handshake message: got %d, expected %d" , messageId , c . expectedMessage ) )
2022-11-03 11:32:07 +00:00
return
}
switch messageId {
case MessageHandshakeChallenge :
2022-11-07 18:38:29 +00:00
if c . HandshakeComplete . Load ( ) {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , errors . New ( "got HANDSHAKE_CHALLENGE but handshake is complete" ) )
2022-11-03 11:32:07 +00:00
return
}
var challenge HandshakeChallenge
var peerId uint64
if err := binary . Read ( c , binary . LittleEndian , & challenge ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
if err := binary . Read ( c , binary . LittleEndian , & peerId ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
if peerId == c . Owner . PeerId ( ) {
//tried to connect to self
c . Close ( )
return
}
2022-12-11 16:27:46 +00:00
c . PeerId . Store ( peerId )
2022-11-03 11:32:07 +00:00
2023-04-23 14:46:47 +00:00
if ok , otherClient := func ( ) ( bool , * Client ) {
2022-11-03 11:32:07 +00:00
c . Owner . clientsLock . RLock ( )
defer c . Owner . clientsLock . RUnlock ( )
for _ , client := range c . Owner . clients {
2022-12-11 16:27:46 +00:00
if client != c && client . PeerId . Load ( ) == peerId {
2023-04-23 14:46:47 +00:00
return true , client
2022-11-03 11:32:07 +00:00
}
}
2023-04-23 14:46:47 +00:00
return false , nil
} ( ) ; ok {
2023-04-23 15:21:10 +00:00
c . HandshakeComplete . Store ( true )
2023-04-23 17:34:52 +00:00
c . SetError ( fmt . Errorf ( "already connected as %s (%d)" , otherClient . AddressPort , otherClient . PeerId . Load ( ) ) )
2022-11-03 11:32:07 +00:00
//same peer
2023-04-23 14:46:47 +00:00
log . Printf ( "[P2PClient] Connected to other same peer: %s (%d) is also %s (%d)" , c . AddressPort , c . PeerId . Load ( ) , otherClient . AddressPort , otherClient . PeerId . Load ( ) )
2022-11-03 11:32:07 +00:00
c . Close ( )
return
}
c . sendHandshakeSolution ( challenge )
2022-11-07 14:58:02 +00:00
c . expectedMessage = MessageHandshakeSolution
2022-11-03 11:32:07 +00:00
2023-04-19 19:48:30 +00:00
if c . HandshakeComplete . Load ( ) && c . SentHandshakeSolution . Load ( ) {
c . OnAfterHandshake ( )
}
2022-11-03 11:32:07 +00:00
case MessageHandshakeSolution :
2022-11-07 18:38:29 +00:00
if c . HandshakeComplete . Load ( ) {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , errors . New ( "got HANDSHAKE_SOLUTION but handshake is complete" ) )
2022-11-03 11:32:07 +00:00
return
}
var challengeHash types . Hash
var solution uint64
if err := binary . Read ( c , binary . LittleEndian , & challengeHash ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
if err := binary . Read ( c , binary . LittleEndian , & solution ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
if c . IsIncomingConnection {
2022-11-07 14:58:02 +00:00
if hash , ok := CalculateChallengeHash ( c . handshakeChallenge , c . Owner . Consensus ( ) . Id ( ) , solution ) ; ! ok {
2022-11-03 11:32:07 +00:00
//not enough PoW
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "not enough PoW on HANDSHAKE_SOLUTION, challenge = %s, solution = %d, calculated hash = %s, expected hash = %s" , hex . EncodeToString ( c . handshakeChallenge [ : ] ) , solution , hash . String ( ) , challengeHash . String ( ) ) )
2022-11-03 11:32:07 +00:00
return
} else if hash != challengeHash {
//wrong hash
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "wrong hash HANDSHAKE_SOLUTION, challenge = %s, solution = %d, calculated hash = %s, expected hash = %s" , hex . EncodeToString ( c . handshakeChallenge [ : ] ) , solution , hash . String ( ) , challengeHash . String ( ) ) )
2022-11-03 11:32:07 +00:00
return
}
} else {
2022-11-07 14:58:02 +00:00
if hash , _ := CalculateChallengeHash ( c . handshakeChallenge , c . Owner . Consensus ( ) . Id ( ) , solution ) ; hash != challengeHash {
2022-11-03 11:32:07 +00:00
//wrong hash
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "wrong hash HANDSHAKE_SOLUTION, challenge = %s, solution = %d, calculated hash = %s, expected hash = %s" , hex . EncodeToString ( c . handshakeChallenge [ : ] ) , solution , hash . String ( ) , challengeHash . String ( ) ) )
2022-11-03 11:32:07 +00:00
return
}
}
2022-11-07 18:38:29 +00:00
c . HandshakeComplete . Store ( true )
2022-11-03 11:32:07 +00:00
2023-04-19 19:48:30 +00:00
if c . HandshakeComplete . Load ( ) && c . SentHandshakeSolution . Load ( ) {
c . OnAfterHandshake ( )
}
2022-11-03 11:32:07 +00:00
case MessageListenPort :
2022-12-02 13:34:18 +00:00
if c . ListenPort . Load ( ) != 0 {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , errors . New ( "got LISTEN_PORT but we already received it" ) )
2022-11-03 11:32:07 +00:00
return
}
2022-12-02 13:34:18 +00:00
var listenPort uint32
if err := binary . Read ( c , binary . LittleEndian , & listenPort ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
2022-12-02 13:34:18 +00:00
if listenPort == 0 || listenPort >= 65536 {
c . Ban ( DefaultBanTime , fmt . Errorf ( "listen port out of range: %d" , listenPort ) )
2022-11-03 11:32:07 +00:00
return
}
2022-12-02 13:34:18 +00:00
c . ListenPort . Store ( listenPort )
2023-04-19 13:16:56 +00:00
c . Owner . UpdateInPeerList ( netip . AddrPortFrom ( c . AddressPort . Addr ( ) , uint16 ( c . ListenPort . Load ( ) ) ) )
2022-11-03 11:32:07 +00:00
case MessageBlockRequest :
2023-04-19 13:16:56 +00:00
c . LastBlockRequestTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2022-11-03 11:32:07 +00:00
var templateId types . Hash
if err := binary . Read ( c , binary . LittleEndian , & templateId ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
}
2022-12-09 12:13:00 +00:00
var block * sidechain . PoolBlock
//if empty, return chain tip
if templateId == types . ZeroHash {
2023-04-22 01:43:42 +00:00
// Don't return stale chain tip
if block = c . Owner . SideChain ( ) . GetChainTip ( ) ; block != nil && ( block . Main . Coinbase . GenHeight + 2 ) < c . Owner . MainChain ( ) . GetMinerDataTip ( ) . Height {
block = nil
}
2022-12-09 12:13:00 +00:00
} else {
block = c . Owner . SideChain ( ) . GetPoolBlockByTemplateId ( templateId )
2023-04-19 18:24:55 +00:00
if block == nil {
log . Printf ( "[P2PClient] Peer %s tip requested id = %s, got nil" , c . AddressPort . String ( ) , templateId )
} else {
log . Printf ( "[P2PClient] Peer %s tip requested id = %s, got height = %d, main height = %d" , c . AddressPort . String ( ) , templateId , block . Side . Height , block . Main . Coinbase . GenHeight )
}
2022-12-09 12:13:00 +00:00
}
2022-11-03 11:32:07 +00:00
2022-12-09 12:13:00 +00:00
c . SendBlockResponse ( block )
2022-11-03 11:32:07 +00:00
case MessageBlockResponse :
2022-12-09 12:13:00 +00:00
block := & sidechain . PoolBlock {
LocalTimestamp : uint64 ( time . Now ( ) . Unix ( ) ) ,
}
2022-11-03 11:32:07 +00:00
2022-12-18 14:53:32 +00:00
expectedBlockId , ok := c . getNextBlockRequest ( )
if ! ok {
2022-12-14 18:31:59 +00:00
c . Ban ( DefaultBanTime , errors . New ( "unexpected BLOCK_RESPONSE" ) )
return
}
2022-11-03 11:32:07 +00:00
var blockSize uint32
if err := binary . Read ( c , binary . LittleEndian , & blockSize ) ; err != nil {
//TODO warn
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
2022-11-06 10:58:19 +00:00
} else if blockSize == 0 {
//NOT found
//TODO log
2023-04-19 17:35:13 +00:00
break
2022-11-03 11:32:07 +00:00
} else {
2023-04-16 17:25:24 +00:00
if err = block . FromReader ( c . Owner . Consensus ( ) , c . Owner . SideChain ( ) . DerivationCache ( ) , bufio . NewReader ( io . LimitReader ( c , int64 ( blockSize ) ) ) ) ; err != nil {
2022-11-03 11:32:07 +00:00
//TODO warn
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
2022-11-06 10:58:19 +00:00
} else {
2023-03-27 12:38:41 +00:00
isChainTipBlockRequest := expectedBlockId == types . ZeroHash
2023-04-19 18:24:55 +00:00
tipHash := types . HashFromBytes ( block . CoinbaseExtra ( sidechain . SideTemplateId ) )
2023-03-27 12:38:41 +00:00
if isChainTipBlockRequest {
2023-04-19 19:48:30 +00:00
c . LastKnownTip . Store ( block )
2023-04-19 17:18:35 +00:00
log . Printf ( "[P2PClient] Peer %s tip is at id = %s, height = %d, main height = %d" , c . AddressPort . String ( ) , tipHash , block . Side . Height , block . Main . Coinbase . GenHeight )
2023-03-10 15:45:22 +00:00
peerHeight := block . Main . Coinbase . GenHeight
ourHeight := c . Owner . MainChain ( ) . GetMinerDataTip ( ) . Height
if ( peerHeight + 2 ) < ourHeight {
c . Ban ( DefaultBanTime , fmt . Errorf ( "mining on top of a stale block (mainchain peer height %d, expected >= %d)" , peerHeight , ourHeight ) )
return
}
2022-12-18 14:53:32 +00:00
2022-11-06 16:01:36 +00:00
c . SendPeerListRequest ( )
2022-11-06 10:58:19 +00:00
}
2023-04-19 18:24:55 +00:00
if missingBlocks , err , ban := c . Owner . SideChain ( ) . AddPoolBlockExternal ( block ) ; err != nil {
if ban {
c . Ban ( DefaultBanTime , err )
} else {
log . Printf ( "[P2PClient] Peer %s error adding block id = %s, height = %d, main height = %d, timestamp = %d" , c . AddressPort . String ( ) , tipHash , block . Side . Height , block . Main . Coinbase . GenHeight , block . Main . Timestamp )
}
2022-12-09 12:13:00 +00:00
return
} else {
2022-12-18 14:53:32 +00:00
if ! isChainTipBlockRequest && expectedBlockId != block . SideTemplateId ( c . Owner . SideChain ( ) . Consensus ( ) ) {
c . Ban ( DefaultBanTime , fmt . Errorf ( "expected block id = %s, got %s" , expectedBlockId . String ( ) , block . SideTemplateId ( c . Owner . SideChain ( ) . Consensus ( ) ) . String ( ) ) )
return
}
2022-12-09 12:13:00 +00:00
for _ , id := range missingBlocks {
c . SendMissingBlockRequest ( id )
2022-11-06 16:01:36 +00:00
}
2022-12-09 12:13:00 +00:00
}
2022-11-06 16:01:36 +00:00
}
2022-11-03 11:32:07 +00:00
}
2022-12-03 22:43:18 +00:00
case MessageBlockBroadcast , MessageBlockBroadcastCompact :
2022-12-09 12:13:00 +00:00
block := & sidechain . PoolBlock {
LocalTimestamp : uint64 ( time . Now ( ) . Unix ( ) ) ,
}
2022-11-03 11:32:07 +00:00
var blockSize uint32
if err := binary . Read ( c , binary . LittleEndian , & blockSize ) ; err != nil {
//TODO warn
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-03 11:32:07 +00:00
return
2022-11-06 10:58:19 +00:00
} else if blockSize == 0 {
//NOT found
//TODO log
2023-04-19 17:35:13 +00:00
break
2022-12-03 22:43:18 +00:00
} else if messageId == MessageBlockBroadcastCompact {
2023-04-16 17:25:24 +00:00
if err = block . FromCompactReader ( c . Owner . Consensus ( ) , c . Owner . SideChain ( ) . DerivationCache ( ) , bufio . NewReader ( io . LimitReader ( c , int64 ( blockSize ) ) ) ) ; err != nil {
2022-12-03 22:43:18 +00:00
//TODO warn
c . Ban ( DefaultBanTime , err )
return
}
} else {
2023-04-16 17:25:24 +00:00
if err = block . FromReader ( c . Owner . Consensus ( ) , c . Owner . SideChain ( ) . DerivationCache ( ) , bufio . NewReader ( io . LimitReader ( c , int64 ( blockSize ) ) ) ) ; err != nil {
2022-12-03 22:43:18 +00:00
//TODO warn
c . Ban ( DefaultBanTime , err )
return
}
2022-11-03 11:32:07 +00:00
}
2022-12-03 22:43:18 +00:00
2022-12-11 12:31:43 +00:00
//Atomic max, not necessary as no external writers exist
topHeight := utils . Max ( c . BroadcastMaxHeight . Load ( ) , block . Side . Height )
for {
if oldHeight := c . BroadcastMaxHeight . Swap ( topHeight ) ; oldHeight <= topHeight {
break
} else {
topHeight = oldHeight
}
}
2023-04-19 17:18:35 +00:00
tipHash := types . HashFromBytes ( block . CoinbaseExtra ( sidechain . SideTemplateId ) )
c . BroadcastedHashes . Push ( tipHash )
2022-12-03 22:43:18 +00:00
2023-04-19 13:16:56 +00:00
c . LastBroadcastTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2023-04-19 19:48:30 +00:00
c . LastKnownTip . Store ( block )
2023-04-19 17:18:35 +00:00
2022-12-09 12:13:00 +00:00
if missingBlocks , err := c . Owner . SideChain ( ) . PreprocessBlock ( block ) ; err != nil {
for _ , id := range missingBlocks {
c . SendMissingBlockRequest ( id )
}
//TODO: ban here, but sort blocks properly, maybe a queue to re-try?
return
} else {
2023-03-09 15:18:42 +00:00
ourMinerData := c . Owner . MainChain ( ) . GetMinerDataTip ( )
if block . Main . PreviousId != ourMinerData . PrevId {
// This peer is mining on top of a different Monero block, investigate it
peerHeight := block . Main . Coinbase . GenHeight
ourHeight := ourMinerData . Height
if peerHeight < ourHeight {
if ( ourHeight - peerHeight ) < 5 {
elapsedTime := time . Now ( ) . Sub ( ourMinerData . TimeReceived )
if ( ourHeight - peerHeight ) > 1 || elapsedTime > ( time . Second * 10 ) {
log . Printf ( "[P2PClient] Peer %s broadcasted a stale block (%d ms late, mainchain height %d, expected >= %d), ignoring it" , c . AddressPort . String ( ) , elapsedTime . Milliseconds ( ) , peerHeight , ourHeight )
}
} else {
c . Ban ( DefaultBanTime , fmt . Errorf ( "broadcasted an unreasonably stale block (mainchain height %d, expected >= %d)" , peerHeight , ourHeight ) )
return
}
} else if peerHeight > ourHeight {
if peerHeight >= ( ourHeight + 2 ) {
log . Printf ( "[P2PClient] Peer %s is ahead on mainchain (mainchain height %d, your height %d). Is monerod stuck or lagging?" , c . AddressPort . String ( ) , peerHeight , ourHeight )
}
} else {
log . Printf ( "[P2PClient] Peer %s is mining on an alternative mainchain tip (mainchain height %d, previous_id = %s)" , c . AddressPort . String ( ) , peerHeight , block . Main . PreviousId )
}
}
2022-12-09 12:13:00 +00:00
block . WantBroadcast . Store ( true )
2023-04-19 18:24:55 +00:00
if missingBlocks , err , ban := c . Owner . SideChain ( ) . AddPoolBlockExternal ( block ) ; err != nil {
if ban {
c . Ban ( DefaultBanTime , err )
} else {
log . Printf ( "[P2PClient] Peer %s error adding block id = %s, height = %d, main height = %d, timestamp = %d" , c . AddressPort . String ( ) , tipHash , block . Side . Height , block . Main . Coinbase . GenHeight , block . Main . Timestamp )
}
2022-11-06 10:58:19 +00:00
return
} else {
2022-12-09 12:13:00 +00:00
for _ , id := range missingBlocks {
c . SendMissingBlockRequest ( id )
2022-11-06 10:58:19 +00:00
}
}
2022-12-09 12:13:00 +00:00
}
2022-11-03 11:32:07 +00:00
case MessagePeerListRequest :
2022-12-02 13:34:18 +00:00
connectedPeerList := c . Owner . Clients ( )
entriesToSend := make ( [ ] netip . AddrPort , 0 , PeerListResponseMaxPeers )
// Send every 4th peer on average, selected at random
2023-04-19 19:48:30 +00:00
peersToSendTarget := utils . Min ( PeerListResponseMaxPeers , utils . Max ( len ( connectedPeerList ) / 4 , 1 ) )
2022-12-02 13:34:18 +00:00
n := 0
for _ , peer := range connectedPeerList {
2023-04-23 14:46:47 +00:00
if peer . AddressPort . Addr ( ) . IsLoopback ( ) || peer . AddressPort . Addr ( ) . IsPrivate ( ) || ! peer . IsGood ( ) || peer . AddressPort . Addr ( ) . Compare ( c . AddressPort . Addr ( ) ) == 0 {
2022-12-02 13:34:18 +00:00
continue
}
n ++
// Use https://en.wikipedia.org/wiki/Reservoir_sampling algorithm
if len ( entriesToSend ) < peersToSendTarget {
entriesToSend = append ( entriesToSend , peer . AddressPort )
}
k := unsafeRandom . Intn ( n )
if k < peersToSendTarget {
entriesToSend [ k ] = peer . AddressPort
}
}
2022-12-02 11:33:37 +00:00
if c . LastIncomingPeerListRequestTime . IsZero ( ) {
//first, send version / protocol information
2022-12-02 13:34:18 +00:00
if len ( entriesToSend ) == 0 {
entriesToSend = append ( entriesToSend , c . Owner . versionInformation . ToAddrPort ( ) )
} else {
entriesToSend [ 0 ] = c . Owner . versionInformation . ToAddrPort ( )
}
2022-12-02 11:33:37 +00:00
}
2022-12-02 13:34:18 +00:00
2023-04-19 19:56:49 +00:00
lastLen := len ( entriesToSend )
if lastLen < PeerListResponseMaxPeers {
//improvement from normal p2pool: pad response with other peers from peer list, not connected
peerList := c . Owner . PeerList ( )
for i := lastLen ; i < PeerListResponseMaxPeers ; i ++ {
k := unsafeRandom . Intn ( len ( peerList ) ) % len ( peerList )
peer := peerList [ k ]
if ! slices . ContainsFunc ( entriesToSend , func ( addrPort netip . AddrPort ) bool {
return addrPort . Addr ( ) . Compare ( peer . AddressPort . Addr ( ) ) == 0
} ) {
entriesToSend = append ( entriesToSend , peer . AddressPort )
}
}
}
2023-04-23 14:46:47 +00:00
var hasIpv6 bool
for _ , e := range entriesToSend {
if e . Addr ( ) . Is6 ( ) {
hasIpv6 = true
break
}
}
//include one ipv6, if existent
if ! hasIpv6 {
peerList := c . Owner . PeerList ( )
unsafeRandom . Shuffle ( len ( peerList ) , func ( i , j int ) {
peerList [ i ] = peerList [ j ]
} )
for _ , p := range c . Owner . PeerList ( ) {
if p . AddressPort . Addr ( ) . Is4In6 ( ) || p . AddressPort . Addr ( ) . Is6 ( ) {
if len ( entriesToSend ) < PeerListResponseMaxPeers {
entriesToSend = append ( entriesToSend , p . AddressPort )
} else {
entriesToSend [ len ( entriesToSend ) - 1 ] = p . AddressPort
}
break
}
}
}
2022-12-02 13:34:18 +00:00
c . LastIncomingPeerListRequestTime = time . Now ( )
c . SendPeerListResponse ( entriesToSend )
2022-11-03 11:32:07 +00:00
case MessagePeerListResponse :
2022-11-06 16:01:36 +00:00
if numPeers , err := c . ReadByte ( ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-06 16:01:36 +00:00
return
} else if numPeers > PeerListResponseMaxPeers {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "too many peers on PEER_LIST_RESPONSE num_peers = %d" , numPeers ) )
2022-11-06 16:01:36 +00:00
return
} else {
2023-04-19 18:24:55 +00:00
firstPeerResponse := c . PingDuration . Swap ( uint64 ( utils . Max ( time . Now ( ) . Sub ( time . UnixMicro ( int64 ( c . LastPeerListRequestTimestamp . Load ( ) ) ) ) , 0 ) ) ) == 0
2022-11-06 16:01:36 +00:00
var rawIp [ 16 ] byte
var port uint16
2023-04-19 18:24:55 +00:00
if firstPeerResponse {
log . Printf ( "[P2PClient] Peer %s initial PEER_LIST_RESPONSE: num_peers %d" , c . AddressPort . String ( ) , numPeers )
}
2022-11-06 16:01:36 +00:00
for i := uint8 ( 0 ) ; i < numPeers ; i ++ {
if isV6 , err := c . ReadByte ( ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-06 16:01:36 +00:00
return
} else {
if _ , err = c . Read ( rawIp [ : ] ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-06 16:01:36 +00:00
return
} else if err = binary . Read ( c , binary . LittleEndian , & port ) ; err != nil {
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , err )
2022-11-06 16:01:36 +00:00
return
}
2022-12-02 11:33:37 +00:00
2022-11-08 10:46:35 +00:00
if isV6 == 0 {
2022-12-02 11:33:37 +00:00
if rawIp [ 12 ] == 0 || rawIp [ 12 ] >= 224 {
// Ignore 0.0.0.0/8 (special-purpose range for "this network") and 224.0.0.0/3 (IP multicast and reserved ranges)
// Check for protocol version message
if binary . LittleEndian . Uint32 ( rawIp [ 12 : ] ) == 0xFFFFFFFF && port == 0xFFFF {
2023-03-11 18:08:04 +00:00
c . VersionInformation . Protocol = p2pooltypes . ProtocolVersion ( binary . LittleEndian . Uint32 ( rawIp [ 0 : ] ) )
c . VersionInformation . SoftwareVersion = p2pooltypes . SoftwareVersion ( binary . LittleEndian . Uint32 ( rawIp [ 4 : ] ) )
c . VersionInformation . SoftwareId = p2pooltypes . SoftwareId ( binary . LittleEndian . Uint32 ( rawIp [ 8 : ] ) )
2023-04-19 20:05:22 +00:00
log . Printf ( "[P2PClient] Peer %s version information: %s" , c . AddressPort . String ( ) , c . VersionInformation . String ( ) )
2023-04-19 21:32:58 +00:00
c . afterInitialProtocolExchange ( )
2022-12-02 11:33:37 +00:00
}
continue
}
2022-11-06 16:01:36 +00:00
copy ( rawIp [ : ] , make ( [ ] byte , 10 ) )
rawIp [ 10 ] , rawIp [ 11 ] = 0xFF , 0xFF
2023-04-23 14:46:47 +00:00
} else {
log . Printf ( "Got IPv6 from peer %s: %s" , c . AddressPort , netip . AddrFrom16 ( rawIp ) . String ( ) )
2022-11-06 16:01:36 +00:00
}
2023-04-23 14:46:47 +00:00
2022-11-08 10:46:35 +00:00
c . Owner . AddToPeerList ( netip . AddrPortFrom ( netip . AddrFrom16 ( rawIp ) . Unmap ( ) , port ) )
2022-11-06 16:01:36 +00:00
}
}
}
2023-04-19 21:32:58 +00:00
case MessageInternal :
internalMessageId , err := binary . ReadUvarint ( c )
if err != nil {
c . Ban ( DefaultBanTime , err )
return
}
messageSize , err := binary . ReadUvarint ( c )
if err != nil {
c . Ban ( DefaultBanTime , err )
return
}
reader := io . LimitReader ( c , int64 ( messageSize ) )
switch InternalMessageId ( internalMessageId ) {
case InternalMessageFastTemplateHeaderSyncRequest :
c . LastBlockRequestTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
var fromTemplateId types . Hash
if err := binary . Read ( reader , binary . LittleEndian , & fromTemplateId ) ; err != nil {
c . Ban ( DefaultBanTime , err )
return
}
log . Printf ( "[P2PClient] Peer %s: received InternalMessageFastTemplateHeaderSyncRequest for %s" , c . AddressPort , fromTemplateId )
//TODO: this could just be a sample of like 16 blocks
var blocks , uncles sidechain . UniquePoolBlockSlice
if fromTemplateId == types . ZeroHash {
tip := c . Owner . SideChain ( ) . GetChainTip ( )
if tip != nil {
blocks , uncles = c . Owner . SideChain ( ) . GetPoolBlocksFromTip ( tip . SideTemplateId ( c . Owner . Consensus ( ) ) )
}
} else {
blocks , uncles = c . Owner . SideChain ( ) . GetPoolBlocksFromTip ( fromTemplateId )
}
hashes := make ( [ ] types . Hash , 0 , len ( blocks ) + len ( uncles ) )
for _ , b := range blocks {
hashes = append ( hashes , b . SideTemplateId ( c . Owner . Consensus ( ) ) )
}
for _ , u := range uncles {
hashes = append ( hashes , u . SideTemplateId ( c . Owner . Consensus ( ) ) )
}
if uint64 ( len ( hashes ) ) > c . Owner . Consensus ( ) . ChainWindowSize * 3 {
hashes = hashes [ : c . Owner . Consensus ( ) . ChainWindowSize * 3 ]
}
//shuffle to have random chances of faster sync
unsafeRandom . Shuffle ( len ( hashes ) , func ( i , j int ) {
hashes [ i ] , hashes [ j ] = hashes [ j ] , hashes [ i ]
} )
2023-04-23 15:11:55 +00:00
maxLen := ( MaxBufferSize - 128 ) / types . HashSize
if len ( hashes ) > maxLen {
hashes = hashes [ : maxLen ]
}
2023-04-19 21:32:58 +00:00
c . SendInternalFastTemplateHeaderSyncResponse ( hashes ... )
case InternalMessageFastTemplateHeaderSyncResponse :
var hashLen uint64
if err = binary . Read ( c , binary . LittleEndian , & hashLen ) ; err != nil {
c . Ban ( DefaultBanTime , err )
return
}
if hashLen > c . Owner . Consensus ( ) . ChainWindowSize * 3 {
c . Ban ( DefaultBanTime , errors . New ( "size error" ) )
return
}
log . Printf ( "[P2PClient] Peer %s: received InternalMessageFastTemplateHeaderSyncResponse with size %d" , c . AddressPort , hashLen )
var hash types . Hash
clients := c . Owner . Clients ( )
for len ( clients ) < 64 {
clients = c . Owner . Clients ( )
time . Sleep ( time . Second * 1 )
}
for i := uint64 ( 0 ) ; i < hashLen ; i ++ {
if err := binary . Read ( reader , binary . LittleEndian , & hash ) ; err != nil {
c . Ban ( DefaultBanTime , err )
return
}
clients = c . SendMissingBlockRequestAtRandom ( hash , clients )
}
default :
c . Ban ( DefaultBanTime , fmt . Errorf ( "unknown InternalMessageId %d" , internalMessageId ) )
return
}
2022-11-03 11:32:07 +00:00
default :
2022-11-07 14:58:02 +00:00
c . Ban ( DefaultBanTime , fmt . Errorf ( "unknown MessageId %d" , messageId ) )
2022-11-06 16:01:36 +00:00
return
2022-11-03 11:32:07 +00:00
}
2022-12-11 12:31:43 +00:00
c . LastActiveTimestamp . Store ( uint64 ( time . Now ( ) . Unix ( ) ) )
2022-11-03 11:32:07 +00:00
}
2022-11-01 11:22:00 +00:00
}
2023-04-19 21:32:58 +00:00
func ( c * Client ) afterInitialProtocolExchange ( ) {
if c . VersionInformation . SupportsFeature ( p2pooltypes . InternalFeatureFastTemplateHeaderSync ) {
if tip := c . LastKnownTip . Load ( ) ; tip != nil {
c . SendInternalFastTemplateHeaderSyncRequest ( tip . SideTemplateId ( c . Owner . Consensus ( ) ) )
} else {
c . SendInternalFastTemplateHeaderSyncRequest ( types . ZeroHash )
}
}
}
2022-11-01 11:22:00 +00:00
func ( c * Client ) sendHandshakeChallenge ( ) {
2022-11-07 14:58:02 +00:00
if _ , err := rand . Read ( c . handshakeChallenge [ : ] ) ; err != nil {
2022-11-01 11:22:00 +00:00
log . Printf ( "[P2PServer] Unable to generate handshake challenge for %s" , c . AddressPort . String ( ) )
c . Close ( )
return
}
2022-11-06 16:01:36 +00:00
var buf [ HandshakeChallengeSize + int ( unsafe . Sizeof ( uint64 ( 0 ) ) ) ] byte
2022-11-07 14:58:02 +00:00
copy ( buf [ : ] , c . handshakeChallenge [ : ] )
2022-11-06 16:01:36 +00:00
binary . LittleEndian . PutUint64 ( buf [ HandshakeChallengeSize : ] , c . Owner . PeerId ( ) )
2022-11-01 11:22:00 +00:00
2022-11-06 16:01:36 +00:00
c . SendMessage ( & ClientMessage {
MessageId : MessageHandshakeChallenge ,
2022-11-07 22:59:52 +00:00
Buffer : buf [ : ] ,
2022-11-06 16:01:36 +00:00
} )
2022-11-01 11:22:00 +00:00
}
func ( c * Client ) sendHandshakeSolution ( challenge HandshakeChallenge ) {
stop := & c . Closed
if c . IsIncomingConnection {
stop = & atomic . Bool { }
stop . Store ( true )
}
2023-04-19 19:48:30 +00:00
if solution , hash , ok := FindChallengeSolution ( challenge , c . Owner . Consensus ( ) . Id ( ) , stop ) ; ok || c . IsIncomingConnection {
2022-11-01 11:22:00 +00:00
2022-11-06 16:01:36 +00:00
var buf [ HandshakeChallengeSize + types . HashSize ] byte
copy ( buf [ : ] , hash [ : ] )
binary . LittleEndian . PutUint64 ( buf [ types . HashSize : ] , solution )
c . SendMessage ( & ClientMessage {
MessageId : MessageHandshakeSolution ,
2022-11-07 22:59:52 +00:00
Buffer : buf [ : ] ,
2022-11-06 16:01:36 +00:00
} )
2023-04-19 19:48:30 +00:00
c . SentHandshakeSolution . Store ( true )
2022-11-01 11:22:00 +00:00
}
}
2022-11-03 11:32:07 +00:00
// Read reads from underlying connection, on error it will Close
func ( c * Client ) Read ( buf [ ] byte ) ( n int , err error ) {
2022-12-09 12:13:00 +00:00
if n , err = c . Connection . Read ( buf ) ; err != nil {
2022-11-03 11:32:07 +00:00
c . Close ( )
}
return
}
2022-11-06 16:01:36 +00:00
type ClientMessage struct {
MessageId MessageId
2022-11-07 22:59:52 +00:00
Buffer [ ] byte
2022-11-06 16:01:36 +00:00
}
func ( c * Client ) SendMessage ( message * ClientMessage ) {
2022-11-07 14:58:02 +00:00
if ! c . Closed . Load ( ) {
2023-04-21 13:41:50 +00:00
bufLen := len ( message . Buffer ) + 1
if bufLen > MaxBufferSize {
log . Printf ( "[P2PClient] Peer %s tried to send more than %d bytes, sent %d, disconnecting" , c . AddressPort , MaxBufferSize , len ( message . Buffer ) + 1 )
c . Close ( )
return
}
buf := getBuffer ( bufLen )
defer returnBuffer ( buf )
buf [ 0 ] = byte ( message . MessageId )
copy ( buf [ 1 : ] , message . Buffer )
2022-12-09 12:13:00 +00:00
//c.sendLock.Lock()
//defer c.sendLock.Unlock()
2022-12-19 11:02:42 +00:00
if err := c . Connection . SetWriteDeadline ( time . Now ( ) . Add ( time . Second * 5 ) ) ; err != nil {
c . Close ( )
2023-04-21 13:41:50 +00:00
} else if _ , err = c . Connection . Write ( buf [ : bufLen ] ) ; err != nil {
2022-12-11 12:02:15 +00:00
c . Close ( )
}
2022-12-09 12:13:00 +00:00
//_, _ = c.Write(message.Buffer)
2022-11-07 14:58:02 +00:00
}
2022-11-06 16:01:36 +00:00
}
2022-11-03 11:32:07 +00:00
// ReadByte reads from underlying connection, on error it will Close
func ( c * Client ) ReadByte ( ) ( b byte , err error ) {
var buf [ 1 ] byte
if _ , err = c . Connection . Read ( buf [ : ] ) ; err != nil && c . Closed . Load ( ) {
c . Close ( )
}
return buf [ 0 ] , err
}
2023-04-19 17:18:35 +00:00
func ( c * Client ) Close ( ) bool {
2022-11-01 11:22:00 +00:00
if c . Closed . Swap ( true ) {
2023-04-19 17:18:35 +00:00
return false
2022-11-01 11:22:00 +00:00
}
2023-04-19 13:16:56 +00:00
if ! c . HandshakeComplete . Load ( ) {
c . Ban ( DefaultBanTime , errors . New ( "disconnected before finishing handshake" ) )
}
2022-12-09 12:13:00 +00:00
c . Owner . clientsLock . Lock ( )
defer c . Owner . clientsLock . Unlock ( )
2023-04-19 13:16:56 +00:00
if c . Owner . fastestPeer == c {
c . Owner . fastestPeer = nil
}
2022-12-09 12:13:00 +00:00
if i := slices . Index ( c . Owner . clients , c ) ; i != - 1 {
2022-12-06 08:59:57 +00:00
c . Owner . clients = slices . Delete ( c . Owner . clients , i , i + 1 )
2022-11-07 14:58:02 +00:00
if c . IsIncomingConnection {
c . Owner . NumIncomingConnections . Add ( - 1 )
} else {
c . Owner . NumOutgoingConnections . Add ( - 1 )
2023-04-19 17:18:35 +00:00
c . Owner . PendingOutgoingConnections . Replace ( c . AddressPort . Addr ( ) . String ( ) , "" )
2022-11-07 14:58:02 +00:00
}
2022-11-01 11:22:00 +00:00
}
2022-11-03 11:32:07 +00:00
_ = c . Connection . Close ( )
2022-11-07 14:58:02 +00:00
close ( c . closeChannel )
2023-04-19 18:24:55 +00:00
2023-04-19 19:48:30 +00:00
log . Printf ( "[P2PClient] Peer %s connection closed" , c . AddressPort . String ( ) )
2023-04-19 17:18:35 +00:00
return true
2022-11-01 11:22:00 +00:00
}