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

This commit is contained in:
Kleissner
2021-12-12 01:06:55 +01:00
parent ea31af1f6e
commit 0cd4519688
5 changed files with 56 additions and 18 deletions

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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

16
Ping.go
View File

@@ -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
}
}

View File

@@ -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:]