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#!/usr/bin/env python32# Copyright (c) 2020 Pieter Wuille3# Distributed under the MIT software license, see the accompanying4# file LICENSE or http://www.opensource.org/licenses/mit-license.php.6"""Native Python (slow) reimplementation of libminisketch' algorithms."""8import random9import unittest11# Irreducible polynomials over GF(2) to use (represented as integers).12#13# Most fields can be defined by multiple such polynomials. Minisketch uses the one with the minimal14# number of nonzero coefficients, and tie-breaking by picking the lexicographically first among15# those.16#17# All polynomials for degrees 2 through 64 (inclusive) are given.18GF2_MODULI = [19None, None,202**2 + 2**1 + 1,212**3 + 2**1 + 1,222**4 + 2**1 + 1,232**5 + 2**2 + 1,242**6 + 2**1 + 1,252**7 + 2**1 + 1,262**8 + 2**4 + 2**3 + 2**1 + 1,272**9 + 2**1 + 1,282**10 + 2**3 + 1,292**11 + 2**2 + 1,302**12 + 2**3 + 1,312**13 + 2**4 + 2**3 + 2**1 + 1,322**14 + 2**5 + 1,332**15 + 2**1 + 1,342**16 + 2**5 + 2**3 + 2**1 + 1,352**17 + 2**3 + 1,362**18 + 2**3 + 1,372**19 + 2**5 + 2**2 + 2**1 + 1,382**20 + 2**3 + 1,392**21 + 2**2 + 1,402**22 + 2**1 + 1,412**23 + 2**5 + 1,422**24 + 2**4 + 2**3 + 2**1 + 1,432**25 + 2**3 + 1,442**26 + 2**4 + 2**3 + 2**1 + 1,452**27 + 2**5 + 2**2 + 2**1 + 1,462**28 + 2**1 + 1,472**29 + 2**2 + 1,482**30 + 2**1 + 1,492**31 + 2**3 + 1,502**32 + 2**7 + 2**3 + 2**2 + 1,512**33 + 2**10 + 1,522**34 + 2**7 + 1,532**35 + 2**2 + 1,542**36 + 2**9 + 1,552**37 + 2**6 + 2**4 + 2**1 + 1,562**38 + 2**6 + 2**5 + 2**1 + 1,572**39 + 2**4 + 1,582**40 + 2**5 + 2**4 + 2**3 + 1,592**41 + 2**3 + 1,602**42 + 2**7 + 1,612**43 + 2**6 + 2**4 + 2**3 + 1,622**44 + 2**5 + 1,632**45 + 2**4 + 2**3 + 2**1 + 1,642**46 + 2**1 + 1,652**47 + 2**5 + 1,662**48 + 2**5 + 2**3 + 2**2 + 1,672**49 + 2**9 + 1,682**50 + 2**4 + 2**3 + 2**2 + 1,692**51 + 2**6 + 2**3 + 2**1 + 1,702**52 + 2**3 + 1,712**53 + 2**6 + 2**2 + 2**1 + 1,722**54 + 2**9 + 1,732**55 + 2**7 + 1,742**56 + 2**7 + 2**4 + 2**2 + 1,752**57 + 2**4 + 1,762**58 + 2**19 + 1,772**59 + 2**7 + 2**4 + 2**2 + 1,782**60 + 2**1 + 1,792**61 + 2**5 + 2**2 + 2**1 + 1,802**62 + 2**29 + 1,812**63 + 2**1 + 1,822**64 + 2**4 + 2**3 + 2**1 + 183]85class GF2Ops:86"""Class to perform GF(2^field_size) operations on elements represented as integers.88Given that elements are represented as integers, addition is simply xor, and not89exposed here.90"""92def __init__(self, field_size):93"""Construct a GF2Ops object for the specified field size."""94self.field_size = field_size95self._modulus = GF2_MODULI[field_size]96assert self._modulus is not None98def mul2(self, x):99"""Multiply x by 2 in GF(2^field_size)."""100x <<= 1101if x >> self.field_size:102x ^= self._modulus103return x105def mul(self, x, y):106"""Multiply x by y in GF(2^field_size)."""107ret = 0108while y:109if y & 1:110ret ^= x111y >>= 1112x = self.mul2(x)113return ret115def sqr(self, x):116"""Square x in GF(2^field_size)."""117return self.mul(x, x)119def inv(self, x):120"""Compute the inverse of x in GF(2^field_size)."""121assert x != 0122# Use the extended polynomial Euclidean GCD algorithm on (modulus, x), over GF(2).123# See https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor.124t1, t2 = 0, 1125r1, r2 = self._modulus, x126r1l, r2l = self.field_size + 1, r2.bit_length()127while r2:128q = r1l - r2l129r1 ^= r2 << q130t1 ^= t2 << q131r1l = r1.bit_length()132if r1 < r2:133t1, t2 = t2, t1134r1, r2 = r2, r1135r1l, r2l = r2l, r1l136assert r1 == 1137return t1139class TestGF2Ops(unittest.TestCase):140"""Test class for basic arithmetic properties of GF2Ops."""142def field_size_test(self, field_size):143"""Test operations for given field_size."""145gf = GF2Ops(field_size)146for i in range(100):147x = random.randrange(1 << field_size)148y = random.randrange(1 << field_size)149x2 = gf.mul2(x)150xy = gf.mul(x, y)151self.assertEqual(x2, gf.mul(x, 2)) # mul2(x) == x*2152self.assertEqual(x2, gf.mul(2, x)) # mul2(x) == 2*x153self.assertEqual(xy == 0, x == 0 or y == 0)154self.assertEqual(xy == x, y == 1 or x == 0)155self.assertEqual(xy == y, x == 1 or y == 0)156self.assertEqual(xy, gf.mul(y, x)) # x*y == y*x157if i < 10:158xp = x159for _ in range(field_size):160xp = gf.sqr(xp)161self.assertEqual(xp, x) # x^(2^field_size) == x162if y != 0:163yi = gf.inv(y)164self.assertEqual(y == yi, y == 1) # y==1/x iff y==1165self.assertEqual(gf.mul(y, yi), 1) # y*(1/y) == 1166yii = gf.inv(yi)167self.assertEqual(y, yii) # 1/(1/y) == y168if x != 0:169xi = gf.inv(x)170xyi = gf.inv(xy)171self.assertEqual(xyi, gf.mul(xi, yi)) # (1/x)*(1/y) == 1/(x*y)173def test(self):174"""Run tests."""175for field_size in range(2, 65):176self.field_size_test(field_size)178# The operations below operate on polynomials over GF(2^field_size), represented as lists of179# integers:180#181# [a, b, c, ...] = a + b*x + c*x^2 + ...182#183# As an invariant, there are never any trailing zeroes in the list representation.184#185# Examples:186# * [] = 0187# * [3] = 3188# * [0, 1] = x189# * [2, 0, 5] = 5*x^2 + 2191def poly_monic(poly, gf):192"""Return a monic version of the polynomial poly."""193# Multiply every coefficient with the inverse of the top coefficient.194inv = gf.inv(poly[-1])195return [gf.mul(inv, v) for v in poly]197def poly_divmod(poly, mod, gf):198"""Return the polynomial (quotient, remainder) of poly divided by mod."""199assert len(mod) > 0 and mod[-1] == 1 # Require monic mod.200if len(poly) < len(mod):201return ([], poly)202val = list(poly)203div = [0 for _ in range(len(val) - len(mod) + 1)]204while len(val) >= len(mod):205term = val[-1]206div[len(val) - len(mod)] = term207# If the highest coefficient in val is nonzero, subtract a multiple of mod from it.208val.pop()209if term != 0:210for x in range(len(mod) - 1):211val[1 + x - len(mod)] ^= gf.mul(term, mod[x])212# Prune trailing zero coefficients.213while len(val) > 0 and val[-1] == 0:214val.pop()215return div, val217def poly_gcd(a, b, gf):218"""Return the polynomial GCD of a and b."""219if len(a) < len(b):220a, b = b, a221# Use Euclid's algorithm to find the GCD of a and b.222# see https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclid's_algorithm.223while len(b) > 0:224b = poly_monic(b, gf)225(_, b), a = poly_divmod(a, b, gf), b226return a228def poly_sqr(poly, gf):229"""Return the square of polynomial poly."""230if len(poly) == 0:231return []232# In characteristic-2 fields, thanks to Frobenius' endomorphism ((a + b)^2 = a^2 + b^2),233# squaring a polynomial is easy: square all the coefficients and interleave with zeroes.234# E.g., (3 + 5*x + 17*x^2)^2 = 3^2 + (5*x)^2 + (17*x^2)^2.235# See https://en.wikipedia.org/wiki/Frobenius_endomorphism.236return [0 if i & 1 else gf.sqr(poly[i // 2]) for i in range(2 * len(poly) - 1)]238def poly_tracemod(poly, param, gf):239"""Compute y + y^2 + y^4 + ... + y^(2^(field_size-1)) mod poly, where y = param*x."""240out = [0, param]241for _ in range(gf.field_size - 1):242# In each loop iteration i, we start with out = y + y^2 + ... + y^(2^i). By squaring that we243# transform it into out = y^2 + y^4 + ... + y^(2^(i+1)).244out = poly_sqr(out, gf)245# Thus, we just need to add y again to it to get out = y + ... + y^(2^(i+1)).246while len(out) < 2:247out.append(0)248out[1] = param249# Finally take a modulus to keep the intermediary polynomials small.250_, out = poly_divmod(out, poly, gf)
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.