From 0cd4519688a59882267111813547f599281b6075 Mon Sep 17 00:00:00 2001 From: Kleissner Date: Sun, 12 Dec 2021 01:06:55 +0100 Subject: [PATCH] Send Announcement instead of ping in case blockchain info was not refreshed within a certain threshold. This helps to keep the global blockchain cache active, with no additional overhead. Close #78 --- Message Send.go | 20 +++++++++++++++++ Network.go | 3 +++ Peer ID.go | 32 ++++++++++++++------------- Ping.go | 16 ++++++++++++-- protocol/Message Encoding Response.go | 3 ++- 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/Message Send.go b/Message Send.go index a71ac15..d217e5e 100644 --- a/Message Send.go +++ b/Message Send.go @@ -26,6 +26,26 @@ func (peer *PeerInfo) pingConnection(connection *Connection) { } } +// pingConnectionAnnouncement sends an empty announcement via a particular connection. +// It has the same effect as ping, but returns the blockchain version and height of the other peer in the Response message, which may be useful for keeping the global blockchain cache up to date. +func (peer *PeerInfo) pingConnectionAnnouncement(connection *Connection) { + _, blockchainHeight, blockchainVersion := UserBlockchain.Header() + packets := protocol.EncodeAnnouncement(false, false, nil, nil, nil, FeatureSupport(), blockchainHeight, blockchainVersion, userAgent) + if len(packets) != 1 { + return + } + + raw := &protocol.PacketRaw{Command: protocol.CommandAnnouncement, Payload: packets[0], Sequence: networks.Sequences.NewSequence(peer.PublicKey, &peer.messageSequence, nil).SequenceNumber} + Filters.MessageOutAnnouncement(peer.PublicKey, peer, raw, false, nil, nil, nil) + + err := peer.sendConnection(raw, connection) + connection.LastPingOut = time.Now() + + if (connection.Status == ConnectionActive || connection.Status == ConnectionRedundant) && IsNetworkErrorFatal(err) { + peer.invalidateActiveConnection(connection) + } +} + // Ping sends a ping. This function exists only for debugging purposes, it should not be used normally. // This ping is not used for uptime detection and the LastPingOut time in connections is not set. func (peer *PeerInfo) Ping() { diff --git a/Network.go b/Network.go index 5f77d86..824863e 100644 --- a/Network.go +++ b/Network.go @@ -180,6 +180,7 @@ func (nets *Networks) packetWorker() { isBlockchainUpdate := peer.BlockchainHeight != announce.BlockchainHeight || peer.BlockchainVersion != announce.BlockchainVersion peer.BlockchainHeight = announce.BlockchainHeight peer.BlockchainVersion = announce.BlockchainVersion + peer.blockchainLastRefresh = time.Now() Filters.MessageIn(peer, raw, announce) @@ -215,6 +216,7 @@ func (nets *Networks) packetWorker() { isBlockchainUpdate := peer.BlockchainHeight != response.BlockchainHeight || peer.BlockchainVersion != response.BlockchainVersion peer.BlockchainHeight = response.BlockchainHeight peer.BlockchainVersion = response.BlockchainVersion + peer.blockchainLastRefresh = time.Now() Filters.MessageIn(peer, raw, response) @@ -235,6 +237,7 @@ func (nets *Networks) packetWorker() { isBlockchainUpdate := peer.BlockchainHeight != announce.BlockchainHeight || peer.BlockchainVersion != announce.BlockchainVersion peer.BlockchainHeight = announce.BlockchainHeight peer.BlockchainVersion = announce.BlockchainVersion + peer.blockchainLastRefresh = time.Now() Filters.MessageIn(peer, raw, announce) diff --git a/Peer ID.go b/Peer ID.go index d9f9b72..1f7f83f 100644 --- a/Peer ID.go +++ b/Peer ID.go @@ -13,6 +13,7 @@ import ( "net" "os" "sync" + "time" "github.com/PeernetOfficial/core/btcec" "github.com/PeernetOfficial/core/dht" @@ -87,21 +88,22 @@ func SelfUserAgent() string { // PeerInfo stores information about a single remote peer type PeerInfo struct { - PublicKey *btcec.PublicKey // Public key - NodeID []byte // Node ID in Kademlia network = blake3(Public Key). - connectionActive []*Connection // List of active established connections to the peer. - connectionInactive []*Connection // List of former connections that are no longer valid. They may be removed after a while. - connectionLatest *Connection // Latest valid connection. - sync.RWMutex // Mutex for access to list of connections. - messageSequence uint32 // Sequence number. Increased with every message. - IsRootPeer bool // Whether the peer is a trusted root peer. - UserAgent string // User Agent reported by remote peer. Empty if no Announcement/Response message was yet received. - Features uint8 // Feature bit array. 0 = IPv4_LISTEN, 1 = IPv6_LISTEN, 1 = FIREWALL - isVirtual bool // Whether it is a virtual peer for establishing a connection. - targetAddresses []*peerAddress // Virtual peer: Addresses to send any replies. - traversePeer *PeerInfo // Virtual peer: Same field as in connection. - BlockchainHeight uint64 // Blockchain height - BlockchainVersion uint64 // Blockchain version + PublicKey *btcec.PublicKey // Public key + NodeID []byte // Node ID in Kademlia network = blake3(Public Key). + connectionActive []*Connection // List of active established connections to the peer. + connectionInactive []*Connection // List of former connections that are no longer valid. They may be removed after a while. + connectionLatest *Connection // Latest valid connection. + sync.RWMutex // Mutex for access to list of connections. + messageSequence uint32 // Sequence number. Increased with every message. + IsRootPeer bool // Whether the peer is a trusted root peer. + UserAgent string // User Agent reported by remote peer. Empty if no Announcement/Response message was yet received. + Features uint8 // Feature bit array. 0 = IPv4_LISTEN, 1 = IPv6_LISTEN, 1 = FIREWALL + isVirtual bool // Whether it is a virtual peer for establishing a connection. + targetAddresses []*peerAddress // Virtual peer: Addresses to send any replies. + traversePeer *PeerInfo // Virtual peer: Same field as in connection. + BlockchainHeight uint64 // Blockchain height + BlockchainVersion uint64 // Blockchain version + blockchainLastRefresh time.Time // Last refresh of the blockchain info. // statistics StatsPacketSent uint64 // Count of packets sent diff --git a/Ping.go b/Ping.go index 9484f28..6b3b1e2 100644 --- a/Ping.go +++ b/Ping.go @@ -6,11 +6,17 @@ Author: Peter Kleissner package core -import "time" +import ( + "time" +) // pingTime is the time in seconds to send out ping messages const pingTime = 10 +// thresholdBlockchainRefresh is the threshold to refresh the blockchain information by sending an Announcement (and expecting the Response message). +// This helps for keeping the global blockchain cache up to date. +const thresholdBlockchainRefresh = 60 * time.Second + // connectionInvalidate is the threshold in seconds to invalidate formerly active connections that no longer receive incoming packets. const connectionInvalidate = 22 @@ -25,6 +31,7 @@ func autoPingAll() { thresholdInvalidate2 := time.Now().Add(-connectionInvalidate * time.Second * 4) thresholdPingOut1 := time.Now().Add(-pingTime * time.Second) thresholdPingOut2 := time.Now().Add(-pingTime * time.Second * 4) + thresholdBlockchainRefresh := time.Now().Add(-thresholdBlockchainRefresh) for _, peer := range PeerlistGet() { // first handle active connections @@ -43,7 +50,12 @@ func autoPingAll() { } if connection.LastPacketIn.Before(thresholdPing) && connection.LastPingOut.Before(thresholdPing) { - peer.pingConnection(connection) + if connection.Status == ConnectionActive && peer.blockchainLastRefresh.Before(thresholdBlockchainRefresh) { + peer.pingConnectionAnnouncement(connection) + } else { + // just a regular ping otherwise + peer.pingConnection(connection) + } continue } } diff --git a/protocol/Message Encoding Response.go b/protocol/Message Encoding Response.go index f6a3b02..f132fd2 100644 --- a/protocol/Message Encoding Response.go +++ b/protocol/Message Encoding Response.go @@ -110,7 +110,8 @@ func DecodeResponse(msg *MessageRaw) (result *MessageResponse, err error) { read += 6 if countPeerResponses == 0 && countEmbeddedFiles == 0 && countHashesNotFound == 0 { - return nil, errors.New("response: empty") + // Empty responses are allowed. They can be useful as quasi-pings to get the latest blockchain info of the peer. + return } data := msg.Payload[read:]