Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/bips.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ BIPs that are implemented by Bitcoin Core:
* [`BIP 390`](https://github.com/bitcoin/bips/blob/master/bip-0390.mediawiki): MuSig2 Descriptor parsing is implemented in **v30.0** ([PR 31244](https://github.com/bitcoin/bitcoin/pull/31244)) and signing in **v31.0** ([PR 29675](https://github.com/bitcoin/bitcoin/pull/29675))
* [`BIP 431`](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki): transactions with nVersion=3 are standard and treated as Topologically Restricted Until Confirmation as of **v28.0** ([PR 29496](https://github.com/bitcoin/bitcoin/pull/29496)).
* [`BIP 433`](https://github.com/bitcoin/bips/blob/master/bip-0433.mediawiki): Spending of Pay to Anchor (P2A) outputs is standard as of **v28.0** ([PR 30352](https://github.com/bitcoin/bitcoin/pull/30352)).
* [`BIP 434`](https://github.com/bitcoin/bips/blob/master/bip-0434.md): Peer Feature Negotiation as of **v32.0** ([PR 35221](https://github.com/bitcoin/bitcoin/pull/35221)).
2 changes: 2 additions & 0 deletions src/bip324.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <pubkey.h>
#include <span.h>

static constexpr unsigned BIP324_SHORTIDS_IMPLEMENTED{38};

/** The BIP324 packet cipher, encapsulating its key derivation, stream cipher, and AEAD. */
class BIP324Cipher
{
Expand Down
11 changes: 5 additions & 6 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ namespace {
* Only message types that are actually implemented in this codebase need to be listed, as other
* messages get ignored anyway - whether we know how to decode them or not.
*/
const std::array<std::string, 33> V2_MESSAGE_IDS = {
const std::array<std::string, BIP324_SHORTIDS_IMPLEMENTED> V2_MESSAGE_IDS = {
"", // 12 bytes follow encoding the message type like in V1
NetMsgType::ADDR,
NetMsgType::BLOCK,
Expand Down Expand Up @@ -952,11 +952,10 @@ const std::array<std::string, 33> V2_MESSAGE_IDS = {
NetMsgType::GETCFCHECKPT,
NetMsgType::CFCHECKPT,
NetMsgType::ADDRV2,
// Unimplemented message types that are assigned in BIP324:
"",
"",
"",
""
"", "", "", // Unimplemented message types 29-31
"", "", "", "", // Unimplemented message types 32-35
"", // Unimplemented message type 36
NetMsgType::FEATURE,
};

class V2MessageMap
Expand Down
7 changes: 7 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,13 @@ class CNode
return m_conn_type == ConnectionType::PRIVATE_BROADCAST;
}

/** Protocol version advertised in our VERSION message.
* Private broadcast connections use a fixed version to maximise anonymity. */
int AdvertisedVersion() const
{
return IsPrivateBroadcastConn() ? WTXID_RELAY_VERSION : PROTOCOL_VERSION;
}

bool IsInboundConn() const {
return m_conn_type == ConnectionType::INBOUND;
}
Expand Down
59 changes: 56 additions & 3 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,15 @@ class PeerManagerImpl final : public PeerManager
{
m_connman.PushMessage(&node, NetMsg::Make(std::move(msg_type), std::forward<Args>(args)...));
}
template <typename... Args>
void MakeAndPushFeature(CNode& node, std::string_view feature_id, Args&&... args) const
{
if (!Assume(feature_id.size() >= 4 && feature_id.size() <= MAX_FEATUREID_LENGTH)) return;
std::vector<unsigned char> feature_data;
VectorWriter{feature_data, 0, std::forward<Args>(args)...};
if (!Assume(feature_data.size() <= MAX_FEATUREDATA_LENGTH)) return;
MakeAndPushMessage(node, NetMsgType::FEATURE, feature_id, std::move(feature_data));
}

/** Send a version message to a peer */
void PushNodeVersion(CNode& pnode, const Peer& peer);
Expand Down Expand Up @@ -1578,7 +1587,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
MakeAndPushMessage(
pnode,
NetMsgType::VERSION,
PROTOCOL_VERSION,
pnode.AdvertisedVersion(),
my_services,
my_time,
// your_services + CNetAddr::V1(your_addr) is the pre-version-31402 serialization of your_addr (without nTime)
Expand All @@ -1592,7 +1601,7 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)

LogDebug(
BCLog::NET, "send version message: version=%d, blocks=%d%s, txrelay=%d, peer=%d\n",
PROTOCOL_VERSION, my_height,
pnode.AdvertisedVersion(), my_height,
fLogIPs ? strprintf(", them=%s", your_addr.ToStringAddrPort()) : "",
my_tx_relay, pnode.GetId());
}
Expand Down Expand Up @@ -3679,7 +3688,7 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
}

// Change version
const int greatest_common_version = std::min(nVersion, PROTOCOL_VERSION);
const int greatest_common_version = std::min(nVersion, pfrom.AdvertisedVersion());
pfrom.SetCommonVersion(greatest_common_version);
pfrom.nVersion = nVersion;

Expand Down Expand Up @@ -3754,6 +3763,11 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
}
}

if (greatest_common_version >= FEATURE_VERSION) {
// announce supported features
// MakeAndPushFeature(pfrom, NetMsgFeature::FOO, uint32_t{1});
}

MakeAndPushMessage(pfrom, NetMsgType::VERACK);

// Potentially mark this peer as a preferred download peer.
Expand Down Expand Up @@ -3969,6 +3983,45 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
return;
}

if (msg_type == NetMsgType::FEATURE) {
if (pfrom.fSuccessfullyConnected) {
// Disconnect peers that send a FEATURE message after VERACK.
LogDebug(BCLog::NET, "feature received after verack, %s", pfrom.DisconnectMsg());
pfrom.fDisconnect = true;
return;
} else if (pfrom.GetCommonVersion() < FEATURE_VERSION) {
// Disconnect peers that send a FEATURE message without valid version negotiation.
LogDebug(BCLog::NET, "feature received with incompatible version %d, %s", pfrom.GetCommonVersion(), pfrom.DisconnectMsg());
pfrom.fDisconnect = true;
return;
}

std::string feature_id;
DataStream feature_data;
try {
vRecv >> LIMITED_STRING(feature_id, MAX_FEATUREID_LENGTH);
std::vector<unsigned char> feature_data_vec;
vRecv >> LIMITED_VECTOR(feature_data_vec, MAX_FEATUREDATA_LENGTH);
feature_data = DataStream(feature_data_vec);
} catch (const std::exception&) {
feature_id.clear(); // use empty feature_id as error indicator
}
if (feature_id.size() < 4 || !vRecv.empty()) {
LogDebug(BCLog::NET, "invalid feature payload, %s", pfrom.DisconnectMsg());
pfrom.fDisconnect = true;
return;
}

// if (feature_id == NetMsgFeature::FOO) {
// ...
// return;
// }

// ignore unknown feature_id
LogDebug(BCLog::NET, "unknown feature advertised: %s", SanitizeString(feature_id));
return;
}

// Received from a peer demonstrating readiness to announce transactions via reconciliations.
// This feature negotiation must happen between VERSION and VERACK to avoid relay problems
// from switching announcement protocols after the connection is up.
Expand Down
21 changes: 16 additions & 5 deletions src/node/caches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,27 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
{
size_t total_cache{CalculateDbCacheBytes(args)};

// Allocate proportional to usage pattern benefit:
// - txindex (10%): serves getrawtransaction RPCs with mostly unique,
// non-repetitive lookups across the entire blockchain.
// - blockfilterindex (5%): serves BIP 157 light clients that repeatedly
// query recent blocks, benefiting from LevelDB cache, but the
// working set for a typical 2-week offline gap is ~200kiB, well within 5%
// of the total cache.
// - txospenderindex (5%): serves gettxspendingprevout RPCs with very
// specific, rarely repeated outpoint queries.
// - coinstatsindex: intentionally not included here, since usage pattern
// does not seem to suggest it would be necessary to cache.
IndexCacheSizes index_sizes;
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
total_cache -= index_sizes.tx_index;
index_sizes.txospender_index = std::min(total_cache / 8, args.GetBoolArg("-txospenderindex", DEFAULT_TXOSPENDERINDEX) ? MAX_TXOSPENDER_INDEX_CACHE : 0);
total_cache -= index_sizes.txospender_index;
index_sizes.tx_index = std::min(total_cache * 10 / 100, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
index_sizes.txospender_index = std::min(total_cache * 5 / 100, args.GetBoolArg("-txospenderindex", DEFAULT_TXOSPENDERINDEX) ? MAX_TXOSPENDER_INDEX_CACHE : 0);
if (n_indexes > 0) {
size_t max_cache = std::min(total_cache / 8, MAX_FILTER_INDEX_CACHE);
size_t max_cache = std::min(total_cache * 5 / 100, MAX_FILTER_INDEX_CACHE);
index_sizes.filter_index = max_cache / n_indexes;
total_cache -= index_sizes.filter_index * n_indexes;
}
total_cache -= index_sizes.tx_index;
total_cache -= index_sizes.txospender_index;
return {index_sizes, kernel::CacheSizes{total_cache}};
}

Expand Down
5 changes: 4 additions & 1 deletion src/node/protocol_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,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;
Expand All @@ -35,4 +35,7 @@ static const int INVALID_CB_NO_BAN_VERSION = 70015;
//! "wtxidrelay" message type for wtxid-based relay starts with this version
static const int WTXID_RELAY_VERSION = 70016;

//! "feature" message type for feature negotiation starts with this version
static const int FEATURE_VERSION = 70017;

#endif // BITCOIN_NODE_PROTOCOL_VERSION_H
12 changes: 12 additions & 0 deletions src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ inline constexpr const char* WTXIDRELAY{"wtxidrelay"};
* txreconciliation, as described by BIP 330.
*/
inline constexpr const char* SENDTXRCNCL{"sendtxrcncl"};
/**
* BIP 434 Peer feature negotiation
*/
inline constexpr const char* FEATURE{"feature"};
}; // namespace NetMsgType

/** All known message types (see above). Keep this in the same order as the list of messages above. */
Expand Down Expand Up @@ -303,8 +307,16 @@ inline const std::array ALL_NET_MESSAGE_TYPES{std::to_array<std::string>({
NetMsgType::CFCHECKPT,
NetMsgType::WTXIDRELAY,
NetMsgType::SENDTXRCNCL,
NetMsgType::FEATURE,
})};

static constexpr size_t MAX_FEATUREID_LENGTH{80};
static constexpr size_t MAX_FEATUREDATA_LENGTH{512};

namespace NetMsgFeature {
//inline constexpr std::string_view FOO{"BIP-FOO"};
}

/** nServices flags */
enum ServiceFlags : uint64_t {
// NOTE: When adding here, be sure to update serviceFlagToStr too
Expand Down
49 changes: 48 additions & 1 deletion src/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <set>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -493,6 +494,7 @@ static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&
#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
#define COMPACTSIZE(obj) Using<CompactSizeFormatter<true>>(obj)
#define LIMITED_STRING(obj,n) Using<LimitedStringFormatter<n>>(obj)
#define LIMITED_VECTOR(obj,n) Using<LimitedVectorFormatter<n>>(obj)

/** Serialization wrapper class for integers in VarInt format. */
template<VarIntMode Mode>
Expand Down Expand Up @@ -705,6 +707,12 @@ struct VectorFormatter
template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str);
template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str);

/**
* string_view
*/
template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string_view<C>& str);
template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string_view<C>& str) = delete;

/**
* prevector
*/
Expand Down Expand Up @@ -783,6 +791,35 @@ struct DefaultFormatter
static void Unser(Stream& s, T& t) { Unserialize(s, t); }
};

/**
* Limited vector formatter. Throws an error if a vector is oversized.
*/

template<size_t Limit, class Formatter = DefaultFormatter>
struct LimitedVectorFormatter
{
template<typename Stream, typename V>
void Unser(Stream& s, V& v)
{
Formatter formatter;
v.clear();
size_t size = ReadCompactSize(s);
if (size > Limit) {
throw std::ios_base::failure("Vector length limit exceeded");
}
v.reserve(size);
for (size_t i = 0; i < size; ++i) {
v.emplace_back();
formatter.Unser(s, v.back());
}
}

template<typename Stream, typename V>
void Ser(Stream& s, const V& v)
{
VectorFormatter<Formatter>{}.Ser(s, v);
}
};



Expand All @@ -807,7 +844,17 @@ void Unserialize(Stream& is, std::basic_string<C>& str)
is.read(MakeWritableByteSpan(str));
}


/**
* string_view
*/
template<typename Stream, typename C>
void Serialize(Stream& os, const std::basic_string_view<C>& str)
{
WriteCompactSize(os, str.size());
if (!str.empty()) {
os.write(MakeByteSpan(str));
}
}

/**
* prevector
Expand Down
2 changes: 1 addition & 1 deletion src/test/feerounder_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include <set>

BOOST_AUTO_TEST_SUITE(fee_rounder_tests)
BOOST_AUTO_TEST_SUITE(feerounder_tests)

BOOST_AUTO_TEST_CASE(FeeRounder)
{
Expand Down
3 changes: 2 additions & 1 deletion src/test/net_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <addrman.h>
#include <bip324.h>
#include <chainparams.h>
#include <clientversion.h>
#include <common/args.h>
Expand Down Expand Up @@ -1568,7 +1569,7 @@ BOOST_AUTO_TEST_CASE(v2transport_test)
tester.CompareSessionIDs();
auto msg_data_1 = m_rng.randbytes<uint8_t>(4000000); // test that receiving 4M payload works
auto msg_data_2 = m_rng.randbytes<uint8_t>(4000000); // test that sending 4M payload works
tester.SendMessage(uint8_t(m_rng.randrange(223) + 33), {}); // unknown short id
tester.SendMessage(uint8_t(m_rng.randrange(256 - BIP324_SHORTIDS_IMPLEMENTED) + BIP324_SHORTIDS_IMPLEMENTED), {}); // unknown short id
tester.SendMessage(uint8_t(2), msg_data_1); // "block" short id
tester.AddMessage("blocktxn", msg_data_2); // schedule blocktxn to be sent to us
ret = tester.Interact();
Expand Down
35 changes: 35 additions & 0 deletions src/test/serialize_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <test/util/setup_common.h>
#include <util/strencodings.h>

#include <algorithm>
#include <cstdint>
#include <string>

Expand Down Expand Up @@ -241,6 +242,40 @@ BOOST_AUTO_TEST_CASE(noncanonical)
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
}

BOOST_AUTO_TEST_CASE(string_view)
{
const std::string_view sv{"hello, world"};
DataStream ss;
ss << sv;
std::string s;
ss >> s;
BOOST_CHECK_EQUAL(sv, s);
}

BOOST_AUTO_TEST_CASE(limited_vector)
{
const std::vector<int> v = {1,2,3,4,-5,-6,-7,-8,-9,-10,10000,20000,-30000};

auto check = [&]<size_t N>() {
DataStream ss;
ss << v;
try {
std::vector<int> r;
ss >> LIMITED_VECTOR(r, N);
BOOST_CHECK_LE(r.size(), N);
BOOST_CHECK(std::ranges::equal(r, v));
} catch (const std::ios_base::failure&) {
BOOST_CHECK_GT(v.size(), N);
}
};
check.operator()<0>();
check.operator()<10>();
check.operator()<12>();
check.operator()<13>();
check.operator()<14>();
check.operator()<100>();
}

BOOST_AUTO_TEST_CASE(class_methods)
{
int intval(100);
Expand Down
2 changes: 1 addition & 1 deletion src/test/util/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void ConnmanTestMsg::Handshake(CNode& node,
FlushSendBuffer(node); // Drop the verack message added by SendMessages.
if (node.fDisconnect) return;
assert(node.nVersion == version);
assert(node.GetCommonVersion() == std::min(version, PROTOCOL_VERSION));
assert(node.GetCommonVersion() == std::min(version, node.AdvertisedVersion()));
CNodeStateStats statestats;
assert(peerman.GetNodeStateStats(node.GetId(), statestats));
assert(statestats.m_relay_txs == (relay_txs && !node.IsBlockOnlyConn()));
Expand Down
Loading
Loading