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# This code supports verifying group implementations which have branches2# or conditional statements (like cmovs), by allowing each execution path3# to independently set assumptions on input or intermediary variables.4#5# The general approach is:6# * A constraint is a tuple of two sets of symbolic expressions:7# the first of which are required to evaluate to zero, the second of which8# are required to evaluate to nonzero.9# - A constraint is said to be conflicting if any of its nonzero expressions10# is in the ideal with basis the zero expressions (in other words: when the11# zero expressions imply that one of the nonzero expressions are zero).12# * There is a list of laws that describe the intended behaviour, including13# laws for addition and doubling. Each law is called with the symbolic point14# coordinates as arguments, and returns:15# - A constraint describing the assumptions under which it is applicable,16# called "assumeLaw"17# - A constraint describing the requirements of the law, called "require"18# * Implementations are transliterated into functions that operate as well on19# algebraic input points, and are called once per combination of branches20# executed. Each execution returns:21# - A constraint describing the assumptions this implementation requires22# (such as Z1=1), called "assumeFormula"23# - A constraint describing the assumptions this specific branch requires,24# but which is by construction guaranteed to cover the entire space by25# merging the results from all branches, called "assumeBranch"26# - The result of the computation27# * All combinations of laws with implementation branches are tried, and:28# - If the combination of assumeLaw, assumeFormula, and assumeBranch results29# in a conflict, it means this law does not apply to this branch, and it is30# skipped.31# - For others, we try to prove the require constraints hold, assuming the32# information in assumeLaw + assumeFormula + assumeBranch, and if this does33# not succeed, we fail.34# + To prove an expression is zero, we check whether it belongs to the35# ideal with the assumed zero expressions as basis. This test is exact.36# + To prove an expression is nonzero, we check whether each of its37# factors is contained in the set of nonzero assumptions' factors.38# This test is not exact, so various combinations of original and39# reduced expressions' factors are tried.40# - If we succeed, we print out the assumptions from assumeFormula that41# weren't implied by assumeLaw already. Those from assumeBranch are skipped,42# as we assume that all constraints in it are complementary with each other.43#44# Based on the sage verification scripts used in the Explicit-Formulas Database45# by Tanja Lange and others, see https://hyperelliptic.org/EFD47class fastfrac:48"""Fractions over rings."""50def __init__(self,R,top,bot=1):51"""Construct a fractional, given a ring, a numerator, and denominator."""52self.R = R53if parent(top) == ZZ or parent(top) == R:54self.top = R(top)55self.bot = R(bot)56elif top.__class__ == fastfrac:57self.top = top.top58self.bot = top.bot * bot59else:60self.top = R(numerator(top))61self.bot = R(denominator(top)) * bot63def iszero(self,I):64"""Return whether this fraction is zero given an ideal."""65return self.top in I and self.bot not in I67def reduce(self,assumeZero):68zero = self.R.ideal(list(map(numerator, assumeZero)))69return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))71def __add__(self,other):72"""Add two fractions."""73if parent(other) == ZZ:74return fastfrac(self.R,self.top + self.bot * other,self.bot)75if other.__class__ == fastfrac:76return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)77return NotImplemented79def __sub__(self,other):80"""Subtract two fractions."""81if parent(other) == ZZ:82return fastfrac(self.R,self.top - self.bot * other,self.bot)83if other.__class__ == fastfrac:84return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)85return NotImplemented87def __neg__(self):88"""Return the negation of a fraction."""89return fastfrac(self.R,-self.top,self.bot)91def __mul__(self,other):92"""Multiply two fractions."""93if parent(other) == ZZ:94return fastfrac(self.R,self.top * other,self.bot)95if other.__class__ == fastfrac:96return fastfrac(self.R,self.top * other.top,self.bot * other.bot)97return NotImplemented99def __rmul__(self,other):100"""Multiply something else with a fraction."""101return self.__mul__(other)103def __truediv__(self,other):104"""Divide two fractions."""105if parent(other) == ZZ:106return fastfrac(self.R,self.top,self.bot * other)107if other.__class__ == fastfrac:108return fastfrac(self.R,self.top * other.bot,self.bot * other.top)109return NotImplemented111# Compatibility wrapper for Sage versions based on Python 2112def __div__(self,other):113"""Divide two fractions."""114return self.__truediv__(other)116def __pow__(self,other):117"""Compute a power of a fraction."""118if parent(other) == ZZ:119if other < 0:120# Negative powers require flipping top and bottom121return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))122else:123return fastfrac(self.R,self.top ^ other,self.bot ^ other)124return NotImplemented126def __str__(self):127return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"128def __repr__(self):129return "%s" % self131def numerator(self):132return self.top134class constraints:135"""A set of constraints, consisting of zero and nonzero expressions.137Constraints can either be used to express knowledge or a requirement.139Both the fields zero and nonzero are maps from expressions to description140strings. The expressions that are the keys in zero are required to be zero,141and the expressions that are the keys in nonzero are required to be nonzero.143Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in144nonzero could be multiplied into a single key. This is often much less145efficient to work with though, so we keep them separate inside the146constraints. This allows higher-level code to do fast checks on the individual147nonzero elements, or combine them if needed for stronger checks.149We can't multiply the different zero elements, as it would suffice for one of150the factors to be zero, instead of all of them. Instead, the zero elements are151typically combined into an ideal first.152"""154def __init__(self, **kwargs):155if 'zero' in kwargs:156self.zero = dict(kwargs['zero'])157else:158self.zero = dict()159if 'nonzero' in kwargs:160self.nonzero = dict(kwargs['nonzero'])161else:162self.nonzero = dict()164def negate(self):165return constraints(zero=self.nonzero, nonzero=self.zero)167def map(self, fun):168return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()})170def __add__(self, other):171zero = self.zero.copy()172zero.update(other.zero)173nonzero = self.nonzero.copy()174nonzero.update(other.nonzero)175return constraints(zero=zero, nonzero=nonzero)177def __str__(self):178return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)180def __repr__(self):181return "%s" % self183def normalize_factor(p):184"""Normalizes the sign of primitive polynomials (as returned by factor())186This function ensures that the polynomial has a positive leading coefficient.188This is necessary because recent sage versions (starting with v9.3 or v9.4,189we don't know) are inconsistent about the placement of the minus sign in190polynomial factorizations:191```192sage: R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')193sage: R((-2 * (bx - ax)) ^ 1).factor()194(-2) * (bx - ax)195sage: R((-2 * (bx - ax)) ^ 2).factor()196(4) * (-bx + ax)^2197sage: R((-2 * (bx - ax)) ^ 3).factor()198(8) * (-bx + ax)^3199```200"""201# Assert p is not 0 and that its non-zero coefficients are coprime.202# (We could just work with the primitive part p/p.content() but we want to be203# aware if factor() does not return a primitive part in future sage versions.)204assert p.content() == 1205# Ensure that the first non-zero coefficient is positive.206return p if p.lc() > 0 else -p208def conflicts(R, con):209"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""210zero = R.ideal(list(map(numerator, con.zero)))211if 1 in zero:212return True213# First a cheap check whether any of the individual nonzero terms conflict on214# their own.215for nonzero in con.nonzero:216if nonzero.iszero(zero):217return True218# It can be the case that entries in the nonzero set do not individually219# conflict with the zero set, but their combination does. For example, knowing220# that either x or y is zero is equivalent to having x*y in the zero set.221# Having x or y individually in the nonzero set is not a conflict, but both222# simultaneously is, so that is the right thing to check for.223if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):224return True225return False228def get_nonzero_set(R, assume):229"""Calculate a simple set of nonzero expressions"""230zero = R.ideal(list(map(numerator, assume.zero)))231nonzero = set()232for nz in map(numerator, assume.nonzero):233for (f,n) in nz.factor():234nonzero.add(normalize_factor(f))235rnz = zero.reduce(nz)236for (f,n) in rnz.factor():237nonzero.add(normalize_factor(f))238return nonzero241def prove_nonzero(R, exprs, assume):242"""Check whether an expression is provably nonzero, given assumptions"""243zero = R.ideal(list(map(numerator, assume.zero)))244nonzero = get_nonzero_set(R, assume)245expl = set()246ok = True247for expr in exprs:248if numerator(expr) in zero:249return (False, [exprs[expr]])250allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
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.