Inside Bitcoin
Opening the selected file…
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.
1// Copyright (c) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <netaddress.h>8#include <crypto/common.h>9#include <crypto/sha3.h>10#include <hash.h>11#include <prevector.h>12#include <tinyformat.h>13#include <util/strencodings.h>14#include <util/string.h>16#include <algorithm>17#include <array>18#include <cstdint>19#include <ios>20#include <iterator>21#include <string_view>22#include <tuple>24using util::ContainsNUL;25using util::HasPrefix;27CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const28{29switch (m_net) {30case NET_IPV4:31return BIP155Network::IPV4;32case NET_IPV6:33return BIP155Network::IPV6;34case NET_ONION:35return BIP155Network::TORV3;36case NET_I2P:37return BIP155Network::I2P;38case NET_CJDNS:39return BIP155Network::CJDNS;40case NET_INTERNAL: // should have been handled before calling this function41case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE42case NET_MAX: // m_net is never and should not be set to NET_MAX43assert(false);44} // no default case, so the compiler can warn about missing cases46assert(false);47}49bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size)50{51switch (possible_bip155_net) {52case BIP155Network::IPV4:53if (address_size == ADDR_IPV4_SIZE) {54m_net = NET_IPV4;55return true;56}57throw std::ios_base::failure(58strprintf("BIP155 IPv4 address with length %u (should be %u)", address_size,59ADDR_IPV4_SIZE));60case BIP155Network::IPV6:61if (address_size == ADDR_IPV6_SIZE) {62m_net = NET_IPV6;63return true;64}65throw std::ios_base::failure(66strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size,67ADDR_IPV6_SIZE));68case BIP155Network::TORV3:69if (address_size == ADDR_TORV3_SIZE) {70m_net = NET_ONION;71return true;72}73throw std::ios_base::failure(74strprintf("BIP155 TORv3 address with length %u (should be %u)", address_size,75ADDR_TORV3_SIZE));76case BIP155Network::I2P:77if (address_size == ADDR_I2P_SIZE) {78m_net = NET_I2P;79return true;80}81throw std::ios_base::failure(82strprintf("BIP155 I2P address with length %u (should be %u)", address_size,83ADDR_I2P_SIZE));84case BIP155Network::CJDNS:85if (address_size == ADDR_CJDNS_SIZE) {86m_net = NET_CJDNS;87return true;88}89throw std::ios_base::failure(90strprintf("BIP155 CJDNS address with length %u (should be %u)", address_size,91ADDR_CJDNS_SIZE));92}94// Don't throw on addresses with unknown network ids (maybe from the future).95// Instead silently drop them and have the unserialization code consume96// subsequent ones which may be known to us.97return false;98}100/**101* Construct an unspecified IPv6 network address (::/128).102*103* @note This address is considered invalid by CNetAddr::IsValid()104*/105CNetAddr::CNetAddr() = default;107void CNetAddr::SetIP(const CNetAddr& ipIn)108{109// Size check.110switch (ipIn.m_net) {111case NET_IPV4:112assert(ipIn.m_addr.size() == ADDR_IPV4_SIZE);113break;114case NET_IPV6:115assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE);116break;117case NET_ONION:118assert(ipIn.m_addr.size() == ADDR_TORV3_SIZE);119break;120case NET_I2P:121assert(ipIn.m_addr.size() == ADDR_I2P_SIZE);122break;123case NET_CJDNS:124assert(ipIn.m_addr.size() == ADDR_CJDNS_SIZE);125break;126case NET_INTERNAL:127assert(ipIn.m_addr.size() == ADDR_INTERNAL_SIZE);128break;129case NET_UNROUTABLE:130case NET_MAX:131assert(false);132} // no default case, so the compiler can warn about missing cases134m_net = ipIn.m_net;135m_addr = ipIn.m_addr;136}138void CNetAddr::SetLegacyIPv6(std::span<const uint8_t> ipv6)139{140assert(ipv6.size() == ADDR_IPV6_SIZE);142size_t skip{0};144if (HasPrefix(ipv6, IPV4_IN_IPV6_PREFIX)) {145// IPv4-in-IPv6146m_net = NET_IPV4;147skip = sizeof(IPV4_IN_IPV6_PREFIX);148} else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) {149// TORv2-in-IPv6 (unsupported). Unserialize as !IsValid(), thus ignoring them.150// Mimic a default-constructed CNetAddr object which is !IsValid() and thus151// will not be gossiped, but continue reading next addresses from the stream.152m_net = NET_IPV6;153m_addr.assign(ADDR_IPV6_SIZE, 0x0);154return;155} else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) {156// Internal-in-IPv6157m_net = NET_INTERNAL;158skip = sizeof(INTERNAL_IN_IPV6_PREFIX);159} else {160// IPv6161m_net = NET_IPV6;162}164m_addr.assign(ipv6.begin() + skip, ipv6.end());165}167/**168* Create an "internal" address that represents a name or FQDN. AddrMan uses169* these fake addresses to keep track of which DNS seeds were used.170* @returns Whether or not the operation was successful.171* @see NET_INTERNAL, INTERNAL_IN_IPV6_PREFIX, CNetAddr::IsInternal(), CNetAddr::IsRFC4193()172*/173bool CNetAddr::SetInternal(const std::string &name)174{175if (name.empty()) {176return false;177}178m_net = NET_INTERNAL;179unsigned char hash[32] = {};180CSHA256().Write((const unsigned char*)name.data(), name.size()).Finalize(hash);181m_addr.assign(hash, hash + ADDR_INTERNAL_SIZE);182return true;183}185namespace torv3 {186// https://gitlab.torproject.org/tpo/core/torspec/-/tree/main/spec/rend-spec187static constexpr size_t CHECKSUM_LEN = 2;188static const unsigned char VERSION[] = {3};189static constexpr size_t TOTAL_LEN = ADDR_TORV3_SIZE + CHECKSUM_LEN + sizeof(VERSION);191static void Checksum(std::span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKSUM_LEN])192{193// TORv3 CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]194static const unsigned char prefix[] = ".onion checksum";195static constexpr size_t prefix_len = 15;197SHA3_256 hasher;199hasher.Write(std::span{prefix}.first(prefix_len));200hasher.Write(addr_pubkey);201hasher.Write(VERSION);203uint8_t checksum_full[SHA3_256::OUTPUT_SIZE];205hasher.Finalize(checksum_full);207memcpy(checksum, checksum_full, sizeof(checksum));208}210}; // namespace torv3212bool CNetAddr::SetSpecial(std::string_view addr)213{214if (ContainsNUL(addr)) {215return false;216}218if (SetTor(addr)) {219return true;220}222if (SetI2P(addr)) {223return true;224}226return false;227}229bool CNetAddr::SetTor(std::string_view addr)230{231if (!addr.ends_with(".onion")) return false;232addr.remove_suffix(6);233auto input = DecodeBase32(addr);235if (!input) {236return false;237}239if (input->size() == torv3::TOTAL_LEN) {240std::span<const uint8_t> input_pubkey{input->data(), ADDR_TORV3_SIZE};241std::span<const uint8_t> input_checksum{input->data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};242std::span<const uint8_t> input_version{input->data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};244if (!std::ranges::equal(input_version, torv3::VERSION)) {245return false;246}248uint8_t calculated_checksum[torv3::CHECKSUM_LEN];249torv3::Checksum(input_pubkey, calculated_checksum);