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.
501if None in ret:502ret = {ctx:enc for ctx, enc in ret.items()503if ctx is None or enc.size < ret[None].size}504if hole:505ret = {ctx:enc for ctx, enc in ret.items() if ctx is None or ctx == 0}506return ret, hole507res, _ = recurse(self._trie)508return res[0] if 0 in res else res[None]510@staticmethod511def _from_binnode(binnode: _BinNode) -> "ASMap":512"""Construct an ASMap object from a _BinNode. Internal use only."""513def recurse(node: _BinNode, default: int) -> list:514if node.ins == _Instruction.RETURN:515return [node.arg1]516if node.ins == _Instruction.JUMP:517return [recurse(node.arg1, default), recurse(node.arg2, default)]518if node.ins == _Instruction.MATCH:519val = node.arg1520sub = recurse(node.arg2, default)521while val >= 2:522bit = val & 1523val >>= 1524if bit:525sub = [[default], sub]526else:527sub = [sub, [default]]528return sub529assert node.ins == _Instruction.DEFAULT530return recurse(node.arg2, node.arg1)531ret = ASMap()532if binnode.ins != _Instruction.END:533#pylint: disable=protected-access534ret._set_trie(recurse(binnode, 0))535return ret537def to_binary(self, fill: bool = False) -> bytes:538"""539Convert this ASMap object to binary.541Argument:542fill: permit the resulting binary encoder to contain mappers for543unassigned subnets in this ASMap object. Doing so may544reduce the size of the encoding.545Returns:546A bytes object with the encoding of this ASMap object.547"""548bits: list[int] = []550def recurse(node: _BinNode) -> None:551_CODER_INS.encode(node.ins.value, bits)552if node.ins == _Instruction.RETURN:553_CODER_ASN.encode(node.arg1, bits)554elif node.ins == _Instruction.JUMP:555_CODER_JUMP.encode(node.arg1.size, bits)556recurse(node.arg1)557recurse(node.arg2)558elif node.ins == _Instruction.DEFAULT:559_CODER_ASN.encode(node.arg1, bits)560recurse(node.arg2)561else:562assert node.ins == _Instruction.MATCH563_CODER_MATCH.encode(node.arg1, bits)564recurse(node.arg2)566binnode = self._to_binnode(fill)567if binnode.ins != _Instruction.END:568recurse(binnode)570val = 0571nbits = 0572ret = []573for bit in bits:574val += (bit << nbits)575nbits += 1576if nbits == 8:577ret.append(val)578val = 0579nbits = 0580if nbits:581ret.append(val)582return bytes(ret)584@staticmethod585def from_binary(bindata: bytes) -> Optional["ASMap"]:586"""Decode an ASMap object from the provided binary encoding."""588bits: list[int] = []589for byte in bindata:590bits.extend((byte >> i) & 1 for i in range(8))592def recurse(bitpos: int) -> tuple[_BinNode, int]:593insval, bitpos = _CODER_INS.decode(bits, bitpos)594ins = _Instruction(insval)595if ins == _Instruction.RETURN:596asn, bitpos = _CODER_ASN.decode(bits, bitpos)597return _BinNode(ins, asn), bitpos598if ins == _Instruction.JUMP:599jump, bitpos = _CODER_JUMP.decode(bits, bitpos)600left, bitpos1 = recurse(bitpos)601if bitpos1 != bitpos + jump:602raise ValueError("Inconsistent jump")603right, bitpos = recurse(bitpos1)604return _BinNode(ins, left, right), bitpos605if ins == _Instruction.MATCH:606match, bitpos = _CODER_MATCH.decode(bits, bitpos)607sub, bitpos = recurse(bitpos)608return _BinNode(ins, match, sub), bitpos609assert ins == _Instruction.DEFAULT610asn, bitpos = _CODER_ASN.decode(bits, bitpos)611sub, bitpos = recurse(bitpos)612return _BinNode(ins, asn, sub), bitpos614if len(bits) == 0:615binnode = _BinNode(_Instruction.END)616else:617try:618binnode, bitpos = recurse(0)619except (ValueError, IndexError):620return None621if bitpos < len(bits) - 7:622return None623if not all(bit == 0 for bit in bits[bitpos:]):624return None626return ASMap._from_binnode(binnode)628def __lt__(self, other: "ASMap") -> bool:629return self._trie < other._trie631def __eq__(self, other: object) -> bool:632if isinstance(other, ASMap):633return self._trie == other._trie634return False636def extends(self, req: "ASMap") -> bool:637"""Determine whether this matches req for all subranges where req is assigned."""638def recurse(actual: list, require: list) -> bool:639if len(require) == 1 and require[0] == 0:640return True641if len(require) == 1:642if len(actual) == 1:643return bool(require[0] == actual[0])644return recurse(actual[0], require) and recurse(actual[1], require)645if len(actual) == 2:646return recurse(actual[0], require[0]) and recurse(actual[1], require[1])647return recurse(actual, require[0]) and recurse(actual, require[1])648assert isinstance(req, ASMap)649#pylint: disable=protected-access650return recurse(self._trie, req._trie)652def diff(self, other: "ASMap") -> list[ASNDiff]:653"""Compute the diff from self to other."""654prefix: list[bool] = []655ret: list[ASNDiff] = []657def recurse(old_node: list, new_node: list):658if len(old_node) == 1 and len(new_node) == 1:659if old_node[0] != new_node[0]:660ret.append((list(prefix), old_node[0], new_node[0]))661else:662old_left: list = old_node if len(old_node) == 1 else old_node[0]663old_right: list = old_node if len(old_node) == 1 else old_node[1]664new_left: list = new_node if len(new_node) == 1 else new_node[0]665new_right: list = new_node if len(new_node) == 1 else new_node[1]666prefix.append(False)667recurse(old_left, new_left)668prefix[-1] = True669recurse(old_right, new_right)670prefix.pop()671assert isinstance(other, ASMap)672#pylint: disable=protected-access673recurse(self._trie, other._trie)674return ret676def __copy__(self) -> "ASMap":677"""Construct a copy of this ASMap object. Its state will not be shared."""678ret = ASMap()679#pylint: disable=protected-access680ret._set_trie(copy.deepcopy(self._trie))681return ret683def __deepcopy__(self, _) -> "ASMap":684# ASMap objects do not allow sharing of the _trie member, so we don't need the memoization.685return self.__copy__()688class TestASMap(unittest.TestCase):689"""Unit tests for this module."""691def test_ipv6_prefix_roundtrips(self) -> None:692"""Test that random IPv6 network ranges roundtrip through prefix encoding."""693for _ in range(20):694net_bits = random.getrandbits(128)695for prefix_len in range(0, 129):696masked_bits = (net_bits >> (128 - prefix_len)) << (128 - prefix_len)697net = ipaddress.IPv6Network((masked_bits.to_bytes(16, 'big'), prefix_len))698prefix = net_to_prefix(net)699self.assertTrue(len(prefix) <= 128)700net2 = prefix_to_net(prefix)701self.assertEqual(net, net2)703def test_ipv4_prefix_roundtrips(self) -> None:704"""Test that random IPv4 network ranges roundtrip through prefix encoding."""705for _ in range(100):706net_bits = random.getrandbits(32)707for prefix_len in range(0, 33):708masked_bits = (net_bits >> (32 - prefix_len)) << (32 - prefix_len)709net = ipaddress.IPv4Network((masked_bits.to_bytes(4, 'big'), prefix_len))710prefix = net_to_prefix(net)711self.assertTrue(32 <= len(prefix) <= 128)712net2 = prefix_to_net(prefix)713self.assertEqual(net, net2)715def test_asmap_roundtrips(self) -> None:716"""Test case that verifies random ASMap objects roundtrip to/from entries/binary."""717# Iterate over the number of leaves the random test ASMap objects have.718for leaves in range(1, 20):719# Iterate over the number of bits in the AS numbers used.720for asnbits in range(0, 24):721# Iterate over the probability that leaves are unassigned.722for pct in range(101):723# Construct a random ASMap object according to the above parameters.724asmap = ASMap.from_random(num_leaves=leaves, max_asn=1 + (1 << asnbits),725unassigned_prob=0.01 * pct)726# Run tests for to_entries and construction from those entries, both727# for overlapping and non-overlapping ones.728for overlapping in [False, True]:729entries = asmap.to_entries(overlapping=overlapping, fill=False)730random.shuffle(entries)731asmap2 = ASMap(entries)732assert asmap2 is not None733self.assertEqual(asmap2, asmap)734entries = asmap.to_entries(overlapping=overlapping, fill=True)735random.shuffle(entries)736asmap2 = ASMap(entries)737assert asmap2 is not None738self.assertTrue(asmap2.extends(asmap))740# Run tests for to_binary and construction from binary.741enc = asmap.to_binary(fill=False)742asmap3 = ASMap.from_binary(enc)743assert asmap3 is not None744self.assertEqual(asmap3, asmap)745enc = asmap.to_binary(fill=True)746asmap3 = ASMap.from_binary(enc)747assert asmap3 is not None748self.assertTrue(asmap3.extends(asmap))750def test_patching(self) -> None:
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.