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.
1// Copyright (c) 2019-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <util/asmap.h>7#include <hash.h>8#include <streams.h>9#include <uint256.h>10#include <util/check.h>11#include <util/fs.h>12#include <util/log.h>14#include <bit>15#include <cstddef>16#include <cstdio>17#include <span>18#include <string>19#include <utility>20#include <vector>22/*23* ASMap (Autonomous System Map) Implementation24*25* Provides a compressed mapping from IP address prefixes to Autonomous System Numbers (ASNs).26* Uses a binary trie structure encoded as bytecode instructions that are interpreted27* at runtime to find the ASN for a given IP address.28*29* The format of the asmap data is a bit-packed binary format where the entire mapping30* is treated as a continuous sequence of bits. Instructions and their arguments are31* encoded using variable numbers of bits and concatenated together without regard for32* byte boundaries. The bits are stored in bytes using little-endian bit ordering.33*34* The data structure internally represents the mapping as a binary trie where:35* - Unassigned subnets (no ASN mapping present) map to 036* - Subnets mapped entirely to one ASN become leaf nodes37* - Subnets whose lower and upper halves have different mappings branch into subtrees38*39* The encoding uses variable-length integers and four instruction types (RETURN, JUMP,40* MATCH, DEFAULT) to efficiently represent the trie.41*/43namespace {45// Indicates decoding errors or invalid data46constexpr uint32_t INVALID = 0xFFFFFFFF;48/**49* Extract a single bit from byte array using little-endian bit ordering (LSB first).50* Used for ASMap data.51*/52inline bool ConsumeBitLE(size_t& bitpos, std::span<const std::byte> bytes) noexcept53{54const bool bit = (std::to_integer<uint8_t>(bytes[bitpos / 8]) >> (bitpos % 8)) & 1;55++bitpos;56return bit;57}59/**60* Extract a single bit from byte array using big-endian bit ordering (MSB first).61* Used for IP addresses to match network byte order conventions.62*/63inline bool ConsumeBitBE(uint8_t& bitpos, std::span<const std::byte> bytes) noexcept64{65const bool bit = (std::to_integer<uint8_t>(bytes[bitpos / 8]) >> (7 - (bitpos % 8))) & 1;66++bitpos;67return bit;68}70/**71* Variable-length integer decoder using a custom encoding scheme.72*73* The encoding is easiest to describe using an example. Let's say minval=100 and74* bit_sizes=[4,2,2,3]. In that case:75* - x in [100..115]: encoded as [0] + [4-bit BE encoding of (x-100)]76* - x in [116..119]: encoded as [1,0] + [2-bit BE encoding of (x-116)]77* - x in [120..123]: encoded as [1,1,0] + [2-bit BE encoding of (x-120)]78* - x in [124..131]: encoded as [1,1,1] + [3-bit BE encoding of (x-124)]79*80* In general, every number is encoded as:81* - First, k "1"-bits, where k is the class the number falls in82* - Then, a "0"-bit, unless k is the highest class83* - Lastly, bit_sizes[k] bits encoding in big endian the position within that class84*/85uint32_t DecodeBits(size_t& bitpos, const std::span<const std::byte> data, uint8_t minval, const std::span<const uint8_t> bit_sizes)86{87uint32_t val = minval; // Start with minimum encodable value88bool bit;89for (auto bit_sizes_it = bit_sizes.begin(); bit_sizes_it != bit_sizes.end(); ++bit_sizes_it) {90// Read continuation bit to determine if we're in this class91if (bit_sizes_it + 1 != bit_sizes.end()) { // Unless we're in the last class92if (bitpos >= data.size() * 8) break;93bit = ConsumeBitLE(bitpos, data);94} else {95bit = 0; // Last class has no continuation bit96}97if (bit) {98// If the value will not fit in this class, subtract its range from val,99// emit a "1" bit and continue with the next class100val += (1 << *bit_sizes_it); // Add size of this class101} else {102// Decode the position within this class in big endian103for (int b = 0; b < *bit_sizes_it; b++) {104if (bitpos >= data.size() * 8) return INVALID; // Reached EOF in mantissa105bit = ConsumeBitLE(bitpos, data);106val += bit << (*bit_sizes_it - 1 - b); // Big-endian within the class107}108return val;109}110}111return INVALID; // Reached EOF in exponent112}114/**115* Instruction Set116*117* The instruction set is designed to efficiently encode a binary trie118* that maps IP prefixes to ASNs. Each instruction type serves a specific119* role in trie traversal and evaluation.120*/121enum class Instruction : uint32_t122{123// A return instruction, encoded as [0], returns a constant ASN.124// It is followed by an integer using the ASN encoding.125RETURN = 0,126// A jump instruction, encoded as [1,0], inspects the next unused bit in the input127// and either continues execution (if 0), or skips a specified number of bits (if 1).128// It is followed by an integer using jump encoding.129JUMP = 1,130// A match instruction, encoded as [1,1,0], inspects 1 or more of the next unused bits131// in the input. If they all match, execution continues. If not, the default ASN is returned132// (or 0 if unset). The match value encodes both the pattern and its length.133MATCH = 2,134// A default instruction, encoded as [1,1,1], sets the default variable to its argument,135// and continues execution. It is followed by an integer in ASN encoding.136DEFAULT = 3,137};139// Instruction type encoding: RETURN=[0], JUMP=[1,0], MATCH=[1,1,0], DEFAULT=[1,1,1]140constexpr uint8_t TYPE_BIT_SIZES[]{0, 0, 1};141Instruction DecodeType(size_t& bitpos, const std::span<const std::byte> data)142{143return Instruction(DecodeBits(bitpos, data, 0, TYPE_BIT_SIZES));144}146// ASN encoding: Can encode ASNs from 1 to ~16.7 million.147// Uses variable-length encoding optimized for real-world ASN distribution.148// ASN 0 is reserved and used if there isn't a match.149constexpr uint8_t ASN_BIT_SIZES[]{15, 16, 17, 18, 19, 20, 21, 22, 23, 24};150uint32_t DecodeASN(size_t& bitpos, const std::span<const std::byte> data)151{152return DecodeBits(bitpos, data, 1, ASN_BIT_SIZES);153}155// MATCH argument: Values in [2, 511]. The highest set bit determines the match length156// n ∈ [1,8]; the lower n-1 bits are the pattern to compare.157constexpr uint8_t MATCH_BIT_SIZES[]{1, 2, 3, 4, 5, 6, 7, 8};158uint32_t DecodeMatch(size_t& bitpos, const std::span<const std::byte> data)159{160return DecodeBits(bitpos, data, 2, MATCH_BIT_SIZES);161}163// JUMP offset: Minimum value 17. Variable-length coded and may be large164// for skipping big subtrees.165constexpr uint8_t JUMP_BIT_SIZES[]{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};166uint32_t DecodeJump(size_t& bitpos, const std::span<const std::byte> data)167{168return DecodeBits(bitpos, data, 17, JUMP_BIT_SIZES);169}171} // anonymous namespace173/**174* Execute the ASMap bytecode to find the ASN for an IP175*176* This function interprets the asmap bytecode and uses bits from the IP177* address to navigate through the encoded trie structure, ultimately178* returning an ASN value.179*/180uint32_t Interpret(const std::span<const std::byte> asmap, const std::span<const std::byte> ip)181{182size_t pos{0};183const size_t endpos{asmap.size() * 8};184uint8_t ip_bit{0};185const uint8_t ip_bits_end = ip.size() * 8;186uint32_t default_asn = 0;187while (pos < endpos) {188Instruction opcode = DecodeType(pos, asmap);189if (opcode == Instruction::RETURN) {190// Found leaf node - return the ASN191uint32_t asn = DecodeASN(pos, asmap);192if (asn == INVALID) break; // ASN straddles EOF193return asn;194} else if (opcode == Instruction::JUMP) {195// Binary branch: if IP bit is 1, jump forward; else continue196uint32_t jump = DecodeJump(pos, asmap);197if (jump == INVALID) break; // Jump offset straddles EOF198if (ip_bit == ip_bits_end) break; // No input bits left199if (int64_t{jump} >= static_cast<int64_t>(endpos - pos)) break; // Jumping past EOF200if (ConsumeBitBE(ip_bit, ip)) { // Check next IP bit (big-endian)201pos += jump; // Bit = 1: skip to right subtree202}203// Bit = 0: fall through to left subtree204} else if (opcode == Instruction::MATCH) {205// Compare multiple IP bits against a pattern206// The match value encodes both length and pattern:207// - highest set bit position determines length (bit_width - 1)208// - lower bits contain the pattern to compare209uint32_t match = DecodeMatch(pos, asmap);210if (match == INVALID) break; // Match bits straddle EOF211int matchlen = std::bit_width(match) - 1; // An n-bit value matches n-1 input bits212if ((ip_bits_end - ip_bit) < matchlen) break; // Not enough input bits213for (int bit = 0; bit < matchlen; bit++) {214if (ConsumeBitBE(ip_bit, ip) != ((match >> (matchlen - 1 - bit)) & 1)) {215return default_asn; // Pattern mismatch - use default216}217}218// Pattern matched - continue execution219} else if (opcode == Instruction::DEFAULT) {220// Update the default ASN for subsequent MATCH failures221default_asn = DecodeASN(pos, asmap);222if (default_asn == INVALID) break; // ASN straddles EOF223} else {224break; // Instruction straddles EOF225}226}227// Reached EOF without RETURN, or aborted (see any of the breaks above)228// - should have been caught by SanityCheckAsmap below229assert(false);230return 0; // 0 is not a valid ASN231}233/**234* Validates ASMap structure by simulating all possible execution paths.235* Ensures well-formed bytecode, valid jumps, and proper termination.236*/237bool SanityCheckAsmap(const std::span<const std::byte> asmap, int bits)238{239size_t pos{0};240const size_t endpos{asmap.size() * 8};241std::vector<std::pair<uint32_t, int>> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left)242jumps.reserve(bits);243Instruction prevopcode = Instruction::JUMP;244bool had_incomplete_match = false; // Track <8 bit matches for efficiency check246while (pos != endpos) {247// There was a jump into the middle of the previous instruction248if (!jumps.empty() && pos >= jumps.back().first) return false;250Instruction opcode = DecodeType(pos, asmap);
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.