Skip to content

Commit e02e0f3

Browse files
Merge #7082: feat: add protocol version-based v2 p2p short ID negotiation
ddeca21 refactor: use message type as key in V2_NEW_SHORT_ID_VERSIONS (UdjinM6) 14a085c docs: add release notes for v2 short ID version negotiation (UdjinM6) 2373ab3 test: add unit tests for v2 short ID version negotiation (UdjinM6) 11f9a41 feat: add protocol version-based v2 p2p short ID negotiation (UdjinM6) Pull request description: ## Issue being fixed or feature implemented Implement backwards-compatible version negotiation for BIP324 v2 P2P short message IDs, allowing new short IDs to be added incrementally without breaking compatibility with older peers. ## What was done? - Implement backwards-compatible version negotiation for BIP324 v2 P2P short message IDs - Bump `PROTOCOL_VERSION` to 70240 and add `platformban` message to the list of short message IDs for nodes with protocol version 70240+ - Add unit tests - Add release notes ## How Has This Been Tested? Run tests ## Breaking Changes None Backwards Compatibility: - Old nodes (v70238) receive PLATFORMBAN with long encoding (13 bytes) - New nodes (v70240) use short encoding (1 byte) when both support it - Automatic per-connection negotiation based on protocol version - No network-wide activation needed - MIN_MASTERNODE_PROTO_VERSION unchanged (70238) - masternodes can upgrade gradually since the version negotiation handles mixed versions ## Checklist: - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK ddeca21 kwvg: utACK ddeca21 Tree-SHA512: 9e55c0261914cfed3cdcf10110915ce66884cc2ab1a6bd07af6fbc9339240e82bbd77ce6542811e104901fbd61ec18df957ae42dec723db4cecb11c90efa5ac2
2 parents 0dc3016 + ddeca21 commit e02e0f3

File tree

7 files changed

+161
-7
lines changed

7 files changed

+161
-7
lines changed

doc/release-notes-7082.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# v2 P2P Short ID Version Negotiation
2+
3+
The network protocol version has been updated to `70240` (`PLATFORMBAN_V2_SHORT_ID_VERSION`).
4+
5+
The `PLATFORMBAN` message has been added to the v2 P2P short ID mapping (short ID 168). When communicating with peers supporting version 70240+, this message uses 1-byte encoding instead of 13-byte encoding, reducing bandwidth.
6+
7+
The v2 transport layer now automatically negotiates short ID support per connection. Compatible peers use compact encoding, while older v2 peers automatically fall back to long encoding. This enables gradual rollout of new short IDs without breaking backwards compatibility.

src/net.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,7 @@ const std::array<std::string, 33> V2_BITCOIN_IDS = {
931931
* by a protocol upgrade, the old message is no longer supported by the client
932932
* and a new slot wasn't already allotted for the message.
933933
*/
934-
const std::array<std::string, 40> V2_DASH_IDS = {
934+
const std::array<std::string, 41> V2_DASH_IDS = {
935935
NetMsgType::SPORK,
936936
NetMsgType::GETSPORKS,
937937
NetMsgType::SENDDSQUEUE,
@@ -971,9 +971,39 @@ const std::array<std::string, 40> V2_DASH_IDS = {
971971
NetMsgType::SENDHEADERS2,
972972
NetMsgType::HEADERS2,
973973
NetMsgType::GETQUORUMROTATIONINFO,
974-
NetMsgType::QUORUMROTATIONINFO
974+
NetMsgType::QUORUMROTATIONINFO,
975+
NetMsgType::PLATFORMBAN
975976
};
976977

978+
/** Explicit version requirements for Dash v2 short IDs added after baseline.
979+
*
980+
* Maps message type to the minimum protocol version required to use its short ID.
981+
* Only contains messages that were added AFTER the initial v2 implementation.
982+
* Messages not in this map are baseline messages available at BIP324_DASH_BASELINE_VERSION.
983+
*
984+
* IMPORTANT:
985+
* - Never remove entries (historical compatibility)
986+
* - Add new messages as they are introduced
987+
*/
988+
static const std::map<std::string, int> V2_NEW_SHORT_ID_VERSIONS = {
989+
{NetMsgType::PLATFORMBAN, PLATFORMBAN_V2_SHORT_ID_VERSION},
990+
};
991+
992+
/** Get the minimum protocol version required for a message's short ID.
993+
*
994+
* @param message_type The message type to check
995+
* @return Minimum protocol version required
996+
*/
997+
int GetMessageMinVersion(const std::string& message_type)
998+
{
999+
auto it = V2_NEW_SHORT_ID_VERSIONS.find(message_type);
1000+
if (it != V2_NEW_SHORT_ID_VERSIONS.end()) {
1001+
return it->second;
1002+
}
1003+
// Not in map means it's a baseline message
1004+
return BIP324_DASH_BASELINE_VERSION;
1005+
}
1006+
9771007
/** A complete set of short IDs
9781008
*
9791009
* Bitcoin takes up short IDs up to 128 (lower half) while Dash can take
@@ -1551,11 +1581,14 @@ bool V2Transport::SetMessageToSend(CSerializedNetMsg& msg) noexcept
15511581
// Construct contents (encoding message type + payload).
15521582
std::vector<uint8_t> contents;
15531583
auto short_message_id = V2_MESSAGE_MAP(msg.m_type);
1554-
if (short_message_id) {
1584+
1585+
if (short_message_id.has_value() && m_peer_version >= GetMessageMinVersion(msg.m_type)) {
1586+
// Use short encoding (1 byte)
15551587
contents.resize(1 + msg.data.size());
15561588
contents[0] = *short_message_id;
15571589
std::copy(msg.data.begin(), msg.data.end(), contents.begin() + 1);
15581590
} else {
1591+
// Use long encoding (13 bytes for message type)
15591592
// Initialize with zeroes, and then write the message type string starting at offset 1.
15601593
// This means contents[0] and the unused positions in contents[1..13] remain 0x00.
15611594
contents.resize(1 + CMessageHeader::COMMAND_SIZE + msg.data.size(), 0);
@@ -1571,6 +1604,13 @@ bool V2Transport::SetMessageToSend(CSerializedNetMsg& msg) noexcept
15711604
return true;
15721605
}
15731606

1607+
void V2Transport::SetPeerVersion(int version) noexcept
1608+
{
1609+
AssertLockNotHeld(m_send_mutex);
1610+
LOCK(m_send_mutex);
1611+
m_peer_version = version;
1612+
}
1613+
15741614
Transport::BytesToSend V2Transport::GetBytesToSend(bool have_next_message) const noexcept
15751615
{
15761616
AssertLockNotHeld(m_send_mutex);

src/net.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,13 @@ class Transport {
389389
/** Return the memory usage of this transport attributable to buffered data to send. */
390390
virtual size_t GetSendMemoryUsage() const noexcept = 0;
391391

392+
/** Set the peer's protocol version (used for v2 short ID negotiation).
393+
*
394+
* This is a no-op for V1 transport. V2 transport uses it to determine which
395+
* short IDs are supported by the peer.
396+
*/
397+
virtual void SetPeerVersion(int version) noexcept {}
398+
392399
// 3. Miscellaneous functions.
393400

394401
/** Whether upon disconnections, a reconnect with V1 is warranted. */
@@ -645,6 +652,8 @@ class V2Transport final : public Transport
645652
SendState m_send_state GUARDED_BY(m_send_mutex);
646653
/** Whether we've sent at least 24 bytes (which would trigger disconnect for V1 peers). */
647654
bool m_sent_v1_header_worth GUARDED_BY(m_send_mutex) {false};
655+
/** Peer's protocol version for encoding decisions (e.g., v2 short ID negotiation). */
656+
int m_peer_version GUARDED_BY(m_send_mutex) {INIT_PROTO_VERSION};
648657

649658
/** Change the receive state. */
650659
void SetReceiveState(RecvState recv_state) noexcept EXCLUSIVE_LOCKS_REQUIRED(m_recv_mutex);
@@ -690,6 +699,7 @@ class V2Transport final : public Transport
690699
BytesToSend GetBytesToSend(bool have_next_message) const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_send_mutex);
691700
void MarkBytesSent(size_t bytes_sent) noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_send_mutex);
692701
size_t GetSendMemoryUsage() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_send_mutex);
702+
void SetPeerVersion(int version) noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_send_mutex);
693703

694704
// Miscellaneous functions.
695705
bool ShouldReconnectV1() const noexcept override EXCLUSIVE_LOCKS_REQUIRED(!m_recv_mutex, !m_send_mutex);
@@ -1006,6 +1016,8 @@ class CNode
10061016
{
10071017
Assume(m_greatest_common_version == INIT_PROTO_VERSION);
10081018
m_greatest_common_version = greatest_common_version;
1019+
// Also update transport's peer version for v2 short ID negotiation
1020+
m_transport->SetPeerVersion(greatest_common_version);
10091021
}
10101022
int GetCommonVersion() const
10111023
{

src/test/net_tests.cpp

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,11 @@ class V2TransportTester
10541054
V2TransportTester(bool test_initiator) :
10551055
m_transport(0, test_initiator, SER_NETWORK, INIT_PROTO_VERSION),
10561056
m_cipher{GenerateRandomTestKey(), MakeByteSpan(InsecureRand256())},
1057-
m_test_initiator(test_initiator) {}
1057+
m_test_initiator(test_initiator)
1058+
{
1059+
// Set peer version for v2 short ID negotiation
1060+
m_transport.SetPeerVersion(PROTOCOL_VERSION);
1061+
}
10581062

10591063
/** Data type returned by Interact:
10601064
*
@@ -1121,6 +1125,9 @@ class V2TransportTester
11211125
/** Expose the cipher. */
11221126
BIP324Cipher& GetCipher() { return m_cipher; }
11231127

1128+
/** Expose the transport. */
1129+
V2Transport& GetTransport() { return m_transport; }
1130+
11241131
/** Schedule bytes to be sent to the transport. */
11251132
void Send(Span<const uint8_t> data)
11261133
{
@@ -1575,4 +1582,84 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
15751582
}
15761583
}
15771584

1585+
BOOST_AUTO_TEST_CASE(v2_short_id_version_negotiation)
1586+
{
1587+
// Test that v2 short ID encoding respects peer protocol version.
1588+
// This ensures backwards compatibility when adding new short IDs.
1589+
1590+
// Test 1: Baseline messages (short IDs 0-167) work with any v2-capable peer
1591+
{
1592+
V2TransportTester tester(true);
1593+
tester.GetTransport().SetPeerVersion(BIP324_DASH_BASELINE_VERSION); // v70235
1594+
1595+
auto ret = tester.Interact();
1596+
BOOST_REQUIRE(ret && ret->empty());
1597+
tester.SendKey();
1598+
tester.SendGarbage();
1599+
tester.ReceiveKey();
1600+
tester.SendGarbageTerm();
1601+
tester.SendVersion();
1602+
ret = tester.Interact();
1603+
BOOST_REQUIRE(ret && ret->empty());
1604+
tester.ReceiveGarbage();
1605+
tester.ReceiveVersion();
1606+
1607+
// SPORK (short ID 128) should use short encoding
1608+
auto msg_data = g_insecure_rand_ctx.randbytes<uint8_t>(100);
1609+
tester.AddMessage("spork", msg_data);
1610+
ret = tester.Interact();
1611+
BOOST_REQUIRE(ret && ret->empty());
1612+
tester.ReceiveMessage(uint8_t(128), msg_data);
1613+
}
1614+
1615+
// Test 2: New short IDs (168+) require version negotiation
1616+
{
1617+
// Old peer (v70238) - doesn't know about PLATFORMBAN short ID 168
1618+
V2TransportTester tester_old(true);
1619+
tester_old.GetTransport().SetPeerVersion(70238);
1620+
1621+
auto ret = tester_old.Interact();
1622+
BOOST_REQUIRE(ret && ret->empty());
1623+
tester_old.SendKey();
1624+
tester_old.SendGarbage();
1625+
tester_old.ReceiveKey();
1626+
tester_old.SendGarbageTerm();
1627+
tester_old.SendVersion();
1628+
ret = tester_old.Interact();
1629+
BOOST_REQUIRE(ret && ret->empty());
1630+
tester_old.ReceiveGarbage();
1631+
tester_old.ReceiveVersion();
1632+
1633+
// Old peer gets long encoding for PLATFORMBAN
1634+
auto msg_data_old = g_insecure_rand_ctx.randbytes<uint8_t>(100);
1635+
tester_old.AddMessage("platformban", msg_data_old);
1636+
ret = tester_old.Interact();
1637+
BOOST_REQUIRE(ret && ret->empty());
1638+
tester_old.ReceiveMessage("platformban", msg_data_old); // long encoding
1639+
1640+
// New peer (v70240) - knows about PLATFORMBAN short ID 168
1641+
V2TransportTester tester_new(true);
1642+
// Uses PROTOCOL_VERSION (70240) by default
1643+
1644+
ret = tester_new.Interact();
1645+
BOOST_REQUIRE(ret && ret->empty());
1646+
tester_new.SendKey();
1647+
tester_new.SendGarbage();
1648+
tester_new.ReceiveKey();
1649+
tester_new.SendGarbageTerm();
1650+
tester_new.SendVersion();
1651+
ret = tester_new.Interact();
1652+
BOOST_REQUIRE(ret && ret->empty());
1653+
tester_new.ReceiveGarbage();
1654+
tester_new.ReceiveVersion();
1655+
1656+
// New peer gets short encoding for PLATFORMBAN
1657+
auto msg_data_new = g_insecure_rand_ctx.randbytes<uint8_t>(100);
1658+
tester_new.AddMessage("platformban", msg_data_new);
1659+
ret = tester_new.Interact();
1660+
BOOST_REQUIRE(ret && ret->empty());
1661+
tester_new.ReceiveMessage(uint8_t(168), msg_data_new); // short encoding
1662+
}
1663+
}
1664+
15781665
BOOST_AUTO_TEST_SUITE_END()

src/version.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
* network protocol versioning
1111
*/
1212

13-
static const int PROTOCOL_VERSION = 70239;
13+
14+
static const int PROTOCOL_VERSION = 70240;
1415

1516
//! initial proto version, to be increased after version/verack negotiation
1617
static const int INIT_PROTO_VERSION = 209;
@@ -48,6 +49,9 @@ static const int DSQ_INV_VERSION = 70234;
4849
//! Maximum header count for HEADRES2 message was increased from 2000 to 8000 in this version
4950
static const int INCREASE_MAX_HEADERS2_VERSION = 70235;
5051

52+
//! BIP324 v2 transport with Dash short IDs (128-167) introduced
53+
static const int BIP324_DASH_BASELINE_VERSION = 70235;
54+
5155
//! Behavior of QRINFO is changed in this protocol version
5256
static const int EFFICIENT_QRINFO_VERSION = 70236;
5357

@@ -60,6 +64,9 @@ static const int PLATFORM_BAN_VERSION = 70238;
6064
//! Ban of re-propagation of old QFCOMMIT enforcement
6165
static const int QFCOMMIT_STALE_REPROP_BAN_VERSION = 70239;
6266

67+
//! PLATFORMBAN added to v2 short IDs (short ID 168)
68+
static const int PLATFORMBAN_V2_SHORT_ID_VERSION = 70240;
69+
6370
// Make sure that none of the values above collide with `ADDRV2_FORMAT`.
6471

6572
#endif // BITCOIN_VERSION_H

test/functional/test_framework/p2p.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@
100100
# The minimum P2P version that this test framework supports
101101
MIN_P2P_VERSION_SUPPORTED = 60001
102102
# The P2P version that this test framework implements and sends in its `version` message
103-
# Version 70238 introduced platform PoSe ban (dip-0031)
104-
P2P_VERSION = 70238
103+
# Version 70240 introduced PLATFORMBAN to v2 short IDs
104+
P2P_VERSION = 70240
105105
# The services that this test framework offers in its `version` message
106106
P2P_SERVICES = NODE_NETWORK | NODE_HEADERS_COMPRESSED
107107
# The P2P user agent string that this test framework sends in its `version` message

test/functional/test_framework/v2_p2p.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
165: b"headers2",
9191
166: b"getqrinfo",
9292
167: b"qrinfo",
93+
168: b"platformban",
9394
}
9495

9596
# Dictionary which contains short message type ID for the P2P message

0 commit comments

Comments
 (0)