diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -719,6 +719,9 @@ // Store the next time we will consider a getavaaddr message from this peer std::chrono::seconds m_nextGetAvaAddr{0}; + // The version sent in the sendava message + std::atomic sendAvaVersion{0}; + /** * UNIX epoch time of the last block received from this peer that we had * not yet seen (e.g. not already received from another peer), that passed diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -114,6 +114,9 @@ */ static constexpr std::chrono::minutes AVALANCHE_AVAPROOFS_TIMEOUT{2}; +/** The version sent with the sendava message */ +static constexpr uint64_t AVALANCHE_SENDAVA_VERSION = 1; + struct DataRequestParameters { /** * Maximum number of in-flight data requests from a peer. It is not a hard @@ -3729,6 +3732,14 @@ } if (g_avalanche && isAvalancheEnabled(gArgs)) { + // Notify our peer that we have avalanche enabled and can serve + // avalanche addresses and proofs + if (pfrom.GetCommonVersion() >= SENDAVA_VERSION) { + m_connman.PushMessage(&pfrom, + msgMaker.Make(NetMsgType::SENDAVA, + AVALANCHE_SENDAVA_VERSION)); + } + if (g_avalanche->sendHello(&pfrom)) { LogPrint(BCLog::AVALANCHE, "Send avahello to peer %d\n", pfrom.GetId()); @@ -4919,6 +4930,24 @@ return; } + if (msg_type == NetMsgType::SENDAVA) { + if (pfrom.sendAvaVersion > 0) { + Misbehaving(pfrom, 100, "duplicated-sendava"); + return; + } + uint64_t version; + vRecv >> version; + + if (version == 0) { + Misbehaving(pfrom, 100, "invalid-sendava-version"); + return; + } + + pfrom.sendAvaVersion = version; + + return; + } + if (msg_type == NetMsgType::AVAHELLO) { if (pfrom.m_avalanche_state) { LogPrint( diff --git a/src/protocol.h b/src/protocol.h --- a/src/protocol.h +++ b/src/protocol.h @@ -321,6 +321,12 @@ */ extern const char *AVAPROOFSREQ; +/** + * Notify our peer that we have avalanche enabled and can serve + * avaaddr/avaproofs. + */ +extern const char *SENDAVA; + /** * Indicate if the message is used to transmit the content of a block. * These messages can be significantly larger than usual messages and therefore diff --git a/src/protocol.cpp b/src/protocol.cpp --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -55,6 +55,7 @@ const char *GETAVAPROOFS = "getavaproofs"; const char *AVAPROOFS = "avaproofs"; const char *AVAPROOFSREQ = "avaproofsreq"; +const char *SENDAVA = "sendava"; bool IsBlockLike(const std::string &strCommand) { return strCommand == NetMsgType::BLOCK || diff --git a/src/version.h b/src/version.h --- a/src/version.h +++ b/src/version.h @@ -8,7 +8,7 @@ /** * network protocol versioning */ -static const int PROTOCOL_VERSION = 70016; +static const int PROTOCOL_VERSION = 70017; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -40,6 +40,9 @@ //! send extra peer specific entropy in the version message static const int PEER_ENTROPY_VERSION = 70016; +//! "sendava" support starts with this version +static const int SENDAVA_VERSION = 70017; + // Make sure that none of the values above collide with // `ADDRV2_FORMAT`. diff --git a/test/functional/abc_p2p_sendava.py b/test/functional/abc_p2p_sendava.py new file mode 100755 --- /dev/null +++ b/test/functional/abc_p2p_sendava.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# Copyright (c) 2022 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test version message behavior""" + +from test_framework.messages import msg_sendava, msg_version +from test_framework.p2p import P2P_SUBVERSION, P2PInterface, p2p_lock +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +# Extracted from version.h +SENDAVA_VERSION = 70017 + + +class CustomVersionP2PInterface(P2PInterface): + def __init__(self, version): + super().__init__() + self.version = version + + def peer_connect_send_version(self, services): + # Send version message with custom version + vt = msg_version() + vt.nVersion = self.version + vt.strSubVer = P2P_SUBVERSION + vt.nServices = services + vt.addrTo.ip = self.dstaddr + vt.addrTo.port = self.dstport + vt.addrFrom.ip = "0.0.0.0" + vt.addrFrom.port = 0 + + # Will be sent right after connection_made + self.on_connection_send_msg = vt + + +class SendAvaTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + node = self.nodes[0] + + self.log.info("sendava message is only sent if avalanche is enabled") + + sendava_version_peer = CustomVersionP2PInterface(SENDAVA_VERSION) + node.add_p2p_connection(sendava_version_peer) + sendava_version_peer.sync_with_ping() + + with p2p_lock: + assert_equal( + sendava_version_peer.message_count.get( + "sendava", 0), 0) + + self.restart_node(0, extra_args=['-enableavalanche=1']) + + low_version_peer = CustomVersionP2PInterface(SENDAVA_VERSION - 1) + node.add_p2p_connection(low_version_peer) + low_version_peer.sync_with_ping() + + with p2p_lock: + assert_equal(low_version_peer.message_count.get("sendava", 0), 0) + + sendava_version_peer = CustomVersionP2PInterface(SENDAVA_VERSION) + node.add_p2p_connection(sendava_version_peer) + sendava_version_peer.wait_until( + lambda: sendava_version_peer.last_message.get("sendava")) + + duplicated_sendava_peer = CustomVersionP2PInterface(SENDAVA_VERSION) + node.add_p2p_connection(duplicated_sendava_peer) + duplicated_sendava_peer.send_message(msg_sendava()) + with node.assert_debug_log(["duplicated-sendava"]): + duplicated_sendava_peer.send_message(msg_sendava()) + duplicated_sendava_peer.wait_for_disconnect() + + invalid_version_peer = CustomVersionP2PInterface(SENDAVA_VERSION) + node.add_p2p_connection(invalid_version_peer) + with node.assert_debug_log(["invalid-sendava-version"]): + invalid_version_peer.send_message(msg_sendava(version=0)) + invalid_version_peer.wait_for_disconnect() + + +if __name__ == '__main__': + SendAvaTest().main() diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -2021,6 +2021,25 @@ self.filter_type, self.stop_hash) +class msg_sendava: + __slots__ = ("version") + msgtype = b"sendava" + + def __init__(self, version=1): + self.version = version + + def deserialize(self, f): + self.version = struct.unpack("