Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1251* (only after ReceiveKey). Decoys are skipped. Optional associated authenticated data (AAD) is1252* expected in the first received packet, no matter if that is a decoy or not. */1253std::vector<uint8_t> ReceivePacket(std::span<const std::byte> aad = {})1254{1255std::vector<uint8_t> contents;1256// Loop as long as there are ignored packets that are to be skipped.1257while (true) {1258// When processing a packet, at least enough bytes for its length descriptor must be received.1259BOOST_REQUIRE(m_received.size() >= BIP324Cipher::LENGTH_LEN);1260// Decrypt the content length.1261size_t size = m_cipher.DecryptLength(MakeByteSpan(std::span{m_received}.first(BIP324Cipher::LENGTH_LEN)));1262// Check that the full packet is in the receive buffer.1263BOOST_REQUIRE(m_received.size() >= size + BIP324Cipher::EXPANSION);1264// Decrypt the packet contents.1265contents.resize(size);1266bool ignore{false};1267bool ret = m_cipher.Decrypt(1268/*input=*/MakeByteSpan(1269std::span{m_received}.first(size + BIP324Cipher::EXPANSION).subspan(BIP324Cipher::LENGTH_LEN)),1270/*aad=*/aad,1271/*ignore=*/ignore,1272/*contents=*/MakeWritableByteSpan(contents));1273BOOST_CHECK(ret);1274// Don't expect AAD in further packets.1275aad = {};1276// Strip the processed packet's bytes off the front of the receive buffer.1277m_received.erase(m_received.begin(), m_received.begin() + size + BIP324Cipher::EXPANSION);1278// Stop if the ignore bit is not set on this packet.1279if (!ignore) break;1280}1281return contents;1282}1284/** Expect garbage and garbage terminator to have been received, and process them (only after1285* ReceiveKey). */1286void ReceiveGarbage()1287{1288// Figure out the garbage length.1289size_t garblen;1290for (garblen = 0; garblen <= V2Transport::MAX_GARBAGE_LEN; ++garblen) {1291BOOST_REQUIRE(m_received.size() >= garblen + BIP324Cipher::GARBAGE_TERMINATOR_LEN);1292auto term_span = MakeByteSpan(std::span{m_received}.subspan(garblen, BIP324Cipher::GARBAGE_TERMINATOR_LEN));1293if (std::ranges::equal(term_span, m_cipher.GetReceiveGarbageTerminator())) break;1294}1295// Copy the garbage to a buffer.1296m_recv_garbage.assign(m_received.begin(), m_received.begin() + garblen);1297// Strip garbage + garbage terminator off the front of the receive buffer.1298m_received.erase(m_received.begin(), m_received.begin() + garblen + BIP324Cipher::GARBAGE_TERMINATOR_LEN);1299}1301/** Expect version packet to have been received, and process it (only after ReceiveKey). */1302void ReceiveVersion()1303{1304auto contents = ReceivePacket(/*aad=*/MakeByteSpan(m_recv_garbage));1305// Version packets from real BIP324 peers are expected to be empty, despite the fact that1306// this class supports *sending* non-empty version packets (to test that BIP324 peers1307// correctly ignore version packet contents).1308BOOST_CHECK(contents.empty());1309}1311/** Expect application packet to have been received, with specified short id and payload.1312* (only after ReceiveKey). */1313void ReceiveMessage(uint8_t short_id, std::span<const uint8_t> payload)1314{1315auto ret = ReceivePacket();1316BOOST_CHECK(ret.size() == payload.size() + 1);1317BOOST_CHECK(ret[0] == short_id);1318BOOST_CHECK(std::ranges::equal(std::span{ret}.subspan(1), payload));1319}1321/** Expect application packet to have been received, with specified 12-char message type and1322* payload (only after ReceiveKey). */1323void ReceiveMessage(const std::string& m_type, std::span<const uint8_t> payload)1324{1325auto ret = ReceivePacket();1326BOOST_REQUIRE(ret.size() == payload.size() + 1 + CMessageHeader::MESSAGE_TYPE_SIZE);1327BOOST_CHECK(ret[0] == 0);1328for (unsigned i = 0; i < 12; ++i) {1329if (i < m_type.size()) {1330BOOST_CHECK(ret[1 + i] == m_type[i]);1331} else {1332BOOST_CHECK(ret[1 + i] == 0);1333}1334}1335BOOST_CHECK(std::ranges::equal(std::span{ret}.subspan(1 + CMessageHeader::MESSAGE_TYPE_SIZE), payload));1336}1338/** Schedule an encrypted packet with specified message type and payload to be sent to1339* transport (only after ReceiveKey). */1340void SendMessage(std::string mtype, std::span<const uint8_t> payload)1341{1342// Construct contents consisting of 0x00 + 12-byte message type + payload.1343std::vector<uint8_t> contents(1 + CMessageHeader::MESSAGE_TYPE_SIZE + payload.size());1344std::copy(mtype.begin(), mtype.end(), contents.begin() + 1);1345std::copy(payload.begin(), payload.end(), contents.begin() + 1 + CMessageHeader::MESSAGE_TYPE_SIZE);1346// Send a packet with that as contents.1347SendPacket(contents);1348}1350/** Schedule an encrypted packet with specified short message id and payload to be sent to1351* transport (only after ReceiveKey). */1352void SendMessage(uint8_t short_id, std::span<const uint8_t> payload)1353{1354// Construct contents consisting of short_id + payload.1355std::vector<uint8_t> contents(1 + payload.size());1356contents[0] = short_id;1357std::copy(payload.begin(), payload.end(), contents.begin() + 1);1358// Send a packet with that as contents.1359SendPacket(contents);1360}1362/** Test whether the transport's session ID matches the session ID we expect. */1363void CompareSessionIDs() const1364{1365auto info = m_transport.GetInfo();1366BOOST_CHECK(info.session_id);1367BOOST_CHECK(uint256(MakeUCharSpan(m_cipher.GetSessionID())) == *info.session_id);1368}1370/** Introduce a bit error in the data scheduled to be sent. */1371void Damage()1372{1373m_to_send[m_rng.randrange(m_to_send.size())] ^= (uint8_t{1} << m_rng.randrange(8));1374}1375};1377} // namespace1379BOOST_AUTO_TEST_CASE(v2transport_test)1380{1381// A mostly normal scenario, testing a transport in initiator mode.1382for (int i = 0; i < 10; ++i) {1383V2TransportTester tester(m_rng, true);1384auto ret = tester.Interact();1385BOOST_REQUIRE(ret);1386BOOST_REQUIRE(ret->empty());1387tester.SendKey();1388tester.SendGarbage();1389tester.ReceiveKey();1390tester.SendGarbageTerm();1391tester.SendVersion();1392ret = tester.Interact();1393BOOST_REQUIRE(ret);1394BOOST_REQUIRE(ret->empty());1395tester.ReceiveGarbage();1396tester.ReceiveVersion();1397tester.CompareSessionIDs();1398auto msg_data_1 = m_rng.randbytes<uint8_t>(m_rng.randrange(100000));1399auto msg_data_2 = m_rng.randbytes<uint8_t>(m_rng.randrange(1000));1400tester.SendMessage(uint8_t(4), msg_data_1); // cmpctblock short id1401tester.SendMessage(0, {}); // Invalidly encoded message1402tester.SendMessage("tx", msg_data_2); // 12-character encoded message type1403// Message type containing 0x7F (DEL): outside the printable-ASCII range1404// accepted by the v1 transport, so must be rejected here as well.1405tester.SendMessage(std::string{"t\x7f"}, msg_data_2);1406// Message type containing 0x7E ('~'): at the boundary, still valid.1407tester.SendMessage("t~", msg_data_2);1408ret = tester.Interact();1409BOOST_REQUIRE(ret);1410BOOST_REQUIRE(ret->size() == 5);1411BOOST_REQUIRE((*ret)[0]);1412BOOST_CHECK((*ret)[0]->m_type == "cmpctblock");1413BOOST_CHECK(std::ranges::equal((*ret)[0]->m_recv, MakeByteSpan(msg_data_1)));1414BOOST_CHECK(!(*ret)[1]);1415BOOST_REQUIRE((*ret)[2]);1416BOOST_CHECK((*ret)[2]->m_type == "tx");1417BOOST_CHECK(std::ranges::equal((*ret)[2]->m_recv, MakeByteSpan(msg_data_2)));1418BOOST_CHECK(!(*ret)[3]);1419BOOST_REQUIRE((*ret)[4]);1420BOOST_CHECK((*ret)[4]->m_type == "t~");1421BOOST_CHECK(std::ranges::equal((*ret)[4]->m_recv, MakeByteSpan(msg_data_2)));1423// Then send a message with a bit error, expecting failure. It's possible this failure does1424// not occur immediately (when the length descriptor was modified), but it should come1425// eventually, and no messages can be delivered anymore.1426tester.SendMessage("bad", msg_data_1);1427tester.Damage();1428while (true) {1429ret = tester.Interact();1430if (!ret) break; // failure1431BOOST_CHECK(ret->size() == 0); // no message can be delivered1432// Send another message.1433auto msg_data_3 = m_rng.randbytes<uint8_t>(m_rng.randrange(10000));1434tester.SendMessage(uint8_t(12), msg_data_3); // getheaders short id1435}1436}1438// Normal scenario, with a transport in responder node.1439for (int i = 0; i < 10; ++i) {1440V2TransportTester tester(m_rng, false);1441tester.SendKey();1442tester.SendGarbage();1443auto ret = tester.Interact();1444BOOST_REQUIRE(ret);1445BOOST_REQUIRE(ret->empty());1446tester.ReceiveKey();1447tester.SendGarbageTerm();1448tester.SendVersion();1449ret = tester.Interact();1450BOOST_REQUIRE(ret);1451BOOST_REQUIRE(ret->empty());1452tester.ReceiveGarbage();1453tester.ReceiveVersion();1454tester.CompareSessionIDs();1455auto msg_data_1 = m_rng.randbytes<uint8_t>(m_rng.randrange(100000));1456auto msg_data_2 = m_rng.randbytes<uint8_t>(m_rng.randrange(1000));1457tester.SendMessage(uint8_t(14), msg_data_1); // inv short id1458tester.SendMessage(uint8_t(19), msg_data_2); // pong short id1459ret = tester.Interact();1460BOOST_REQUIRE(ret);1461BOOST_REQUIRE(ret->size() == 2);1462BOOST_REQUIRE((*ret)[0]);1463BOOST_CHECK((*ret)[0]->m_type == "inv");1464BOOST_CHECK(std::ranges::equal((*ret)[0]->m_recv, MakeByteSpan(msg_data_1)));1465BOOST_REQUIRE((*ret)[1]);1466BOOST_CHECK((*ret)[1]->m_type == "pong");1467BOOST_CHECK(std::ranges::equal((*ret)[1]->m_recv, MakeByteSpan(msg_data_2)));1469// Then send a too-large message.1470auto msg_data_3 = m_rng.randbytes<uint8_t>(4005000);1471tester.SendMessage(uint8_t(11), msg_data_3); // getdata short id1472ret = tester.Interact();1473BOOST_CHECK(!ret);1474}1476// Various valid but unusual scenarios.1477for (int i = 0; i < 50; ++i) {1478/** Whether an initiator or responder is being tested. */1479bool initiator = m_rng.randbool();1480/** Use either 0 bytes or the maximum possible (4095 bytes) garbage length. */1481size_t garb_len = m_rng.randbool() ? 0 : V2Transport::MAX_GARBAGE_LEN;1482/** How many decoy packets to send before the version packet. */1483unsigned num_ignore_version = m_rng.randrange(10);1484/** What data to send in the version packet (ignored by BIP324 peers, but reserved for future extensions). */1485auto ver_data = m_rng.randbytes<uint8_t>(m_rng.randbool() ? 0 : m_rng.randrange(1000));1486/** Whether to immediately send key and garbage out (required for responders, optional otherwise). */1487bool send_immediately = !initiator || m_rng.randbool();1488/** How many decoy packets to send before the first and second real message. */1489unsigned num_decoys_1 = m_rng.randrange(1000), num_decoys_2 = m_rng.randrange(1000);1490V2TransportTester tester(m_rng, initiator);1491if (send_immediately) {1492tester.SendKey();1493tester.SendGarbage(garb_len);1494}1495auto ret = tester.Interact();1496BOOST_REQUIRE(ret);1497BOOST_REQUIRE(ret->empty());1498if (!send_immediately) {1499tester.SendKey();1500tester.SendGarbage(garb_len);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.