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.
251return _BinNode(node0.ins, node0.arg1 + (1 << (node0.arg1.bit_length() - 1)),252node0.arg2)253return _BinNode(_Instruction.MATCH, 2, node0)254return _BinNode(_Instruction.JUMP, node0, node1)256@staticmethod257def make_default(val: int, sub: "_BinNode") -> "_BinNode":258"""259Construct a _BinNode that corresponds to the specified subprogram, with the specified260default value. It exploits shortcuts that are possible in the encoding, and will use261either a DEFAULT or a RETURN instruction."""262assert val is not None and val > 0263if sub.ins == _Instruction.END:264return _BinNode(_Instruction.RETURN, val)265if sub.ins in (_Instruction.RETURN, _Instruction.DEFAULT):266return sub267return _BinNode(_Instruction.DEFAULT, val, sub)269@total_ordering270class ASMap:271"""272A class whose objects represent a mapping from subnets to ASNs.274Internally the mapping is stored as a binary trie, but can be converted275from/to a list of ASNEntry objects, and from/to the binary asmap file format.277In the trie representation, nodes are represented as bare lists for efficiency278and ease of manipulation:279- [0] means an unassigned subnet (no ASN mapping for it is present)280- [int] means a subnet mapped entirely to the specified ASN.281- [node,node] means a subnet whose lower half and upper half have different282- mappings, represented by new trie nodes.283"""285def update(self, prefix: list[bool], asn: int) -> None:286"""Update this ASMap object to map prefix to the specified asn."""287assert asn == 0 or _CODER_ASN.can_encode(asn)289def recurse(node: list, offset: int) -> None:290if offset == len(prefix):291# Reached the end of prefix; overwrite this node.292node.clear()293node.append(asn)294return295if len(node) == 1:296# Need to descend into a leaf node; split it up.297oldasn = node[0]298node.clear()299node.append([oldasn])300node.append([oldasn])301# Descend into the node.302recurse(node[prefix[offset]], offset + 1)303# If the result is two identical leaf children, merge them.304if len(node[0]) == 1 and len(node[1]) == 1 and node[0] == node[1]:305oldasn = node[0][0]306node.clear()307node.append(oldasn)308recurse(self._trie, 0)310def update_multi(self, entries: list[tuple[list[bool], int]]) -> None:311"""Apply multiple update operations, where longer prefixes take precedence."""312entries.sort(key=lambda entry: len(entry[0]))313for prefix, asn in entries:314self.update(prefix, asn)316def _set_trie(self, trie) -> None:317"""Set trie directly. Internal use only."""318def recurse(node: list) -> None:319if len(node) < 2:320return321recurse(node[0])322recurse(node[1])323if len(node[0]) == 2:324return325if node[0] == node[1]:326if len(node[0]) == 0:327node.clear()328else:329asn = node[0][0]330node.clear()331node.append(asn)332recurse(trie)333self._trie = trie335def __init__(self, entries: Optional[Iterable[ASNEntry]] = None) -> None:336"""Construct an ASMap object from an optional list of entries."""337self._trie = [0]338if entries is not None:339def entry_key(entry):340"""Sort function that places shorter prefixes first."""341prefix, asn = entry342return len(prefix), prefix, asn343for prefix, asn in sorted(entries, key=entry_key):344self.update(prefix, asn)346def lookup(self, prefix: list[bool]) -> Optional[int]:347"""Look up a prefix. Returns ASN, or 0 if unassigned, or None if indeterminate."""348node = self._trie349for bit in prefix:350if len(node) == 1:351break352node = node[bit]353if len(node) == 1:354return node[0]355return None357def _to_entries_flat(self, fill: bool = False) -> list[ASNEntry]:358"""Convert an ASMap object to a list of non-overlapping (prefix, asn) objects."""359prefix : list[bool] = []361def recurse(node: list) -> list[ASNEntry]:362ret = []363if len(node) == 1:364if node[0] > 0:365ret = [(list(prefix), node[0])]366elif len(node) == 2:367prefix.append(False)368ret = recurse(node[0])369prefix[-1] = True370ret += recurse(node[1])371prefix.pop()372if fill and len(ret) > 1:373asns = set(x[1] for x in ret)374if len(asns) == 1:375ret = [(list(prefix), list(asns)[0])]376return ret377return recurse(self._trie)379def _to_entries_minimal(self, fill: bool = False) -> list[ASNEntry]:380"""Convert a trie to a minimal list of ASNEntry objects, exploiting overlap."""381prefix : list[bool] = []383def recurse(node: list) -> (tuple[dict[Optional[int], list[ASNEntry]], bool]):384if len(node) == 1 and node[0] == 0:385return {None if fill else 0: []}, True386if len(node) == 1:387return {node[0]: [], None: [(list(prefix), node[0])]}, False388ret: dict[Optional[int], list[ASNEntry]] = {}389prefix.append(False)390left, lhole = recurse(node[0])391prefix[-1] = True392right, rhole = recurse(node[1])393prefix.pop()394hole = not fill and (lhole or rhole)395def candidate(ctx: Optional[int], res0: Optional[list[ASNEntry]],396res1: Optional[list[ASNEntry]]):397if res0 is not None and res1 is not None:398if ctx not in ret or len(res0) + len(res1) < len(ret[ctx]):399ret[ctx] = res0 + res1400for ctx in set(left) | set(right):401candidate(ctx, left.get(ctx), right.get(ctx))402candidate(ctx, left.get(None), right.get(ctx))403candidate(ctx, left.get(ctx), right.get(None))404if not hole:405for ctx in list(ret):406if ctx is not None:407candidate(None, [(list(prefix), ctx)], ret[ctx])408if None in ret:409ret = {ctx:entries for ctx, entries in ret.items()410if ctx is None or len(entries) < len(ret[None])}411if hole:412ret = {ctx:entries for ctx, entries in ret.items() if ctx is None or ctx == 0}413return ret, hole414res, _ = recurse(self._trie)415return res[0] if 0 in res else res[None]417def __str__(self) -> str:418"""Convert this ASMap object to a string containing Python code constructing it."""419return f"ASMap({self._trie})"421def to_entries(self, overlapping: bool = True, fill: bool = False) -> list[ASNEntry]:422"""423Convert the mappings in this ASMap object to a list of ASNEntry objects.425Arguments:426overlapping: Permit the subnets in the resulting ASNEntry to overlap.427Setting this can result in a shorter list.428fill: Permit the resulting ASNEntry objects to cover subnets that429are unassigned in this ASMap object. Setting this can430result in a shorter list.431"""432if overlapping:433return self._to_entries_minimal(fill)434return self._to_entries_flat(fill)436@staticmethod437def from_random(num_leaves: int = 10, max_asn: int = 6,438unassigned_prob: float = 0.5) -> "ASMap":439"""440Construct a random ASMap object, with specified:441- Number of leaves in its trie (at least 1)442- Maximum ASN value (at least 1)443- Probability for leaf nodes to be unassigned445The number of leaves in the resulting object may be less than what is446requested. This method is mostly intended for testing.447"""448assert num_leaves >= 1449assert max_asn >= 1 or unassigned_prob == 1450assert _CODER_ASN.can_encode(max_asn)451assert 0.0 <= unassigned_prob <= 1.0452trie: list = []453leaves = [trie]454ret = ASMap()455for i in range(1, num_leaves):456idx = random.randrange(i)457leaf = leaves[idx]458lastleaf = leaves.pop()459if idx + 1 < i:460leaves[idx] = lastleaf461leaf.append([])462leaf.append([])463leaves.append(leaf[0])464leaves.append(leaf[1])465for leaf in leaves:466if random.random() >= unassigned_prob:467leaf.append(random.randrange(1, max_asn + 1))468else:469leaf.append(0)470#pylint: disable=protected-access471ret._set_trie(trie)472return ret474def _to_binnode(self, fill: bool = False) -> _BinNode:475"""Convert a trie to a _BinNode object."""476def recurse(node: list) -> tuple[dict[Optional[int], _BinNode], bool]:477if len(node) == 1 and node[0] == 0:478return {(None if fill else 0): _BinNode.make_end()}, True479if len(node) == 1:480return {None: _BinNode.make_leaf(node[0]), node[0]: _BinNode.make_end()}, False481ret: dict[Optional[int], _BinNode] = {}482left, lhole = recurse(node[0])483right, rhole = recurse(node[1])484hole = (lhole or rhole) and not fill486def candidate(ctx: Optional[int], arg1, arg2, func: Callable):487if arg1 is not None and arg2 is not None:488cand = func(arg1, arg2)489if ctx not in ret or cand.size < ret[ctx].size:490ret[ctx] = cand492union = set(left) | set(right)493sorted_union = sorted(union, key=lambda x: (x is None, x))494for ctx in sorted_union:495candidate(ctx, left.get(ctx), right.get(ctx), _BinNode.make_branch)496candidate(ctx, left.get(None), right.get(ctx), _BinNode.make_branch)497candidate(ctx, left.get(ctx), right.get(None), _BinNode.make_branch)498if not hole:499for ctx in sorted(set(ret) - set([None])):500candidate(None, ctx, ret[ctx], _BinNode.make_default)
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.