Merge bitcoin-core/secp256k1#1068: sage: Fix incompatibility with sage 9.4
ebb1beea7832207e5c8c5112d250fd216259ef41 sage: Ensure that constraints are always fastfracs (Tim Ruffing) d8d54859ed138a8ed9a8486d847155211c9f4a7d ci: Run sage prover on CI (Tim Ruffing) 77cfa98dbc40f9494048447b8a302867235300da sage: Normalize sign of polynomial factors in prover (Tim Ruffing) eae75869cfbbbcb69d881bc5d313bd94c6155655 sage: Exit with non-zero status in case of failures (Tim Ruffing) b54d843eac905993eba2314a89a2d7f155445eaa sage: Fix printing of errors (Tim Ruffing) e108d0039c36483dffe4be00815c1b6d65ef5751 sage: Fix incompatibility with sage 9.4 (Tim Ruffing) Pull request description: ACKs for top commit: sipa: ACK ebb1beea7832207e5c8c5112d250fd216259ef41 jonasnick: ACK ebb1beea7832207e5c8c5112d250fd216259ef41 Tree-SHA512: 7a4732fd31d925d3dff471911183acc465ddcadbb5c88c46995502df61a913433c7639cb52fad3db72373b7cc47b9b0f063f7f5d5f8189c9ef998955e409479f
This commit is contained in:
commit
85b00a1c65
@ -322,3 +322,10 @@ task:
|
|||||||
test_script:
|
test_script:
|
||||||
- ./ci/cirrus.sh
|
- ./ci/cirrus.sh
|
||||||
<< : *CAT_LOGS
|
<< : *CAT_LOGS
|
||||||
|
|
||||||
|
task:
|
||||||
|
name: "sage prover"
|
||||||
|
<< : *LINUX_CONTAINER
|
||||||
|
test_script:
|
||||||
|
- cd sage
|
||||||
|
- sage prove_group_implementations.sage
|
||||||
|
@ -19,7 +19,8 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \
|
|||||||
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
|
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
|
||||||
gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
|
gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
|
||||||
gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
|
gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
|
||||||
wine gcc-mingw-w64-x86-64
|
wine gcc-mingw-w64-x86-64 \
|
||||||
|
sagemath
|
||||||
|
|
||||||
# Run a dummy command in wine to make it set up configuration
|
# Run a dummy command in wine to make it set up configuration
|
||||||
RUN wine64-stable xcopy || true
|
RUN wine64-stable xcopy || true
|
||||||
|
@ -164,6 +164,9 @@ class constraints:
|
|||||||
def negate(self):
|
def negate(self):
|
||||||
return constraints(zero=self.nonzero, nonzero=self.zero)
|
return constraints(zero=self.nonzero, nonzero=self.zero)
|
||||||
|
|
||||||
|
def map(self, fun):
|
||||||
|
return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()})
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
zero = self.zero.copy()
|
zero = self.zero.copy()
|
||||||
zero.update(other.zero)
|
zero.update(other.zero)
|
||||||
@ -177,6 +180,30 @@ class constraints:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s" % self
|
return "%s" % self
|
||||||
|
|
||||||
|
def normalize_factor(p):
|
||||||
|
"""Normalizes the sign of primitive polynomials (as returned by factor())
|
||||||
|
|
||||||
|
This function ensures that the polynomial has a positive leading coefficient.
|
||||||
|
|
||||||
|
This is necessary because recent sage versions (starting with v9.3 or v9.4,
|
||||||
|
we don't know) are inconsistent about the placement of the minus sign in
|
||||||
|
polynomial factorizations:
|
||||||
|
```
|
||||||
|
sage: R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
|
||||||
|
sage: R((-2 * (bx - ax)) ^ 1).factor()
|
||||||
|
(-2) * (bx - ax)
|
||||||
|
sage: R((-2 * (bx - ax)) ^ 2).factor()
|
||||||
|
(4) * (-bx + ax)^2
|
||||||
|
sage: R((-2 * (bx - ax)) ^ 3).factor()
|
||||||
|
(8) * (-bx + ax)^3
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
# Assert p is not 0 and that its non-zero coeffients are coprime.
|
||||||
|
# (We could just work with the primitive part p/p.content() but we want to be
|
||||||
|
# aware if factor() does not return a primitive part in future sage versions.)
|
||||||
|
assert p.content() == 1
|
||||||
|
# Ensure that the first non-zero coefficient is positive.
|
||||||
|
return p if p.lc() > 0 else -p
|
||||||
|
|
||||||
def conflicts(R, con):
|
def conflicts(R, con):
|
||||||
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
|
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
|
||||||
@ -204,10 +231,10 @@ def get_nonzero_set(R, assume):
|
|||||||
nonzero = set()
|
nonzero = set()
|
||||||
for nz in map(numerator, assume.nonzero):
|
for nz in map(numerator, assume.nonzero):
|
||||||
for (f,n) in nz.factor():
|
for (f,n) in nz.factor():
|
||||||
nonzero.add(f)
|
nonzero.add(normalize_factor(f))
|
||||||
rnz = zero.reduce(nz)
|
rnz = zero.reduce(nz)
|
||||||
for (f,n) in rnz.factor():
|
for (f,n) in rnz.factor():
|
||||||
nonzero.add(f)
|
nonzero.add(normalize_factor(f))
|
||||||
return nonzero
|
return nonzero
|
||||||
|
|
||||||
|
|
||||||
@ -222,27 +249,27 @@ def prove_nonzero(R, exprs, assume):
|
|||||||
return (False, [exprs[expr]])
|
return (False, [exprs[expr]])
|
||||||
allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
|
allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
|
||||||
for (f, n) in allexprs.factor():
|
for (f, n) in allexprs.factor():
|
||||||
if f not in nonzero:
|
if normalize_factor(f) not in nonzero:
|
||||||
ok = False
|
ok = False
|
||||||
if ok:
|
if ok:
|
||||||
return (True, None)
|
return (True, None)
|
||||||
ok = True
|
ok = True
|
||||||
for (f, n) in zero.reduce(numerator(allexprs)).factor():
|
for (f, n) in zero.reduce(allexprs).factor():
|
||||||
if f not in nonzero:
|
if normalize_factor(f) not in nonzero:
|
||||||
ok = False
|
ok = False
|
||||||
if ok:
|
if ok:
|
||||||
return (True, None)
|
return (True, None)
|
||||||
ok = True
|
ok = True
|
||||||
for expr in exprs:
|
for expr in exprs:
|
||||||
for (f,n) in numerator(expr).factor():
|
for (f,n) in numerator(expr).factor():
|
||||||
if f not in nonzero:
|
if normalize_factor(f) not in nonzero:
|
||||||
ok = False
|
ok = False
|
||||||
if ok:
|
if ok:
|
||||||
return (True, None)
|
return (True, None)
|
||||||
ok = True
|
ok = True
|
||||||
for expr in exprs:
|
for expr in exprs:
|
||||||
for (f,n) in zero.reduce(numerator(expr)).factor():
|
for (f,n) in zero.reduce(numerator(expr)).factor():
|
||||||
if f not in nonzero:
|
if normalize_factor(f) not in nonzero:
|
||||||
expl.add(exprs[expr])
|
expl.add(exprs[expr])
|
||||||
if expl:
|
if expl:
|
||||||
return (False, list(expl))
|
return (False, list(expl))
|
||||||
@ -254,7 +281,7 @@ def prove_zero(R, exprs, assume):
|
|||||||
"""Check whether all of the passed expressions are provably zero, given assumptions"""
|
"""Check whether all of the passed expressions are provably zero, given assumptions"""
|
||||||
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
|
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
|
||||||
if not r:
|
if not r:
|
||||||
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
|
return (False, list(map(lambda x: "Possibly zero denominator: %s" % x, e)))
|
||||||
zero = R.ideal(list(map(numerator, assume.zero)))
|
zero = R.ideal(list(map(numerator, assume.zero)))
|
||||||
nonzero = prod(x for x in assume.nonzero)
|
nonzero = prod(x for x in assume.nonzero)
|
||||||
expl = []
|
expl = []
|
||||||
@ -279,8 +306,8 @@ def describe_extra(R, assume, assumeExtra):
|
|||||||
if base not in zero:
|
if base not in zero:
|
||||||
add = []
|
add = []
|
||||||
for (f, n) in numerator(base).factor():
|
for (f, n) in numerator(base).factor():
|
||||||
if f not in nonzero:
|
if normalize_factor(f) not in nonzero:
|
||||||
add += ["%s" % f]
|
add += ["%s" % normalize_factor(f)]
|
||||||
if add:
|
if add:
|
||||||
ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
|
ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
|
||||||
# Iterate over the extra nonzero expressions
|
# Iterate over the extra nonzero expressions
|
||||||
@ -288,8 +315,8 @@ def describe_extra(R, assume, assumeExtra):
|
|||||||
nzr = zeroextra.reduce(numerator(nz))
|
nzr = zeroextra.reduce(numerator(nz))
|
||||||
if nzr not in zeroextra:
|
if nzr not in zeroextra:
|
||||||
for (f,n) in nzr.factor():
|
for (f,n) in nzr.factor():
|
||||||
if zeroextra.reduce(f) not in nonzero:
|
if normalize_factor(zeroextra.reduce(f)) not in nonzero:
|
||||||
ret.add("%s != 0" % zeroextra.reduce(f))
|
ret.add("%s != 0" % normalize_factor(zeroextra.reduce(f)))
|
||||||
return ", ".join(x for x in ret)
|
return ", ".join(x for x in ret)
|
||||||
|
|
||||||
|
|
||||||
@ -299,22 +326,21 @@ def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
|
|||||||
|
|
||||||
if conflicts(R, assume):
|
if conflicts(R, assume):
|
||||||
# This formula does not apply
|
# This formula does not apply
|
||||||
return None
|
return (True, None)
|
||||||
|
|
||||||
describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
|
describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
|
||||||
|
if describe != "":
|
||||||
|
describe = " (assuming " + describe + ")"
|
||||||
|
|
||||||
ok, msg = prove_zero(R, require.zero, assume)
|
ok, msg = prove_zero(R, require.zero, assume)
|
||||||
if not ok:
|
if not ok:
|
||||||
return "FAIL, %s fails (assuming %s)" % (str(msg), describe)
|
return (False, "FAIL, %s fails%s" % (str(msg), describe))
|
||||||
|
|
||||||
res, expl = prove_nonzero(R, require.nonzero, assume)
|
res, expl = prove_nonzero(R, require.nonzero, assume)
|
||||||
if not res:
|
if not res:
|
||||||
return "FAIL, %s fails (assuming %s)" % (str(expl), describe)
|
return (False, "FAIL, %s fails%s" % (str(expl), describe))
|
||||||
|
|
||||||
if describe != "":
|
return (True, "OK%s" % describe)
|
||||||
return "OK (assuming %s)" % describe
|
|
||||||
else:
|
|
||||||
return "OK"
|
|
||||||
|
|
||||||
|
|
||||||
def concrete_verify(c):
|
def concrete_verify(c):
|
||||||
|
@ -292,15 +292,18 @@ def formula_secp256k1_gej_add_ge_old(branch, a, b):
|
|||||||
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
|
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
|
success = True
|
||||||
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
|
success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
|
||||||
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
|
success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
|
||||||
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
|
success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
|
||||||
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)
|
success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
|
||||||
|
success = success & (not check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old))
|
||||||
|
|
||||||
if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
|
if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
|
||||||
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
|
success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
|
||||||
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
|
success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
|
||||||
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
|
success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
|
||||||
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
|
success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
|
||||||
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)
|
success = success & (not check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43))
|
||||||
|
|
||||||
|
sys.exit(int(not success))
|
||||||
|
@ -184,6 +184,7 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
|
|||||||
if r:
|
if r:
|
||||||
points.append(point)
|
points.append(point)
|
||||||
|
|
||||||
|
ret = True
|
||||||
for za in range(1, p):
|
for za in range(1, p):
|
||||||
for zb in range(1, p):
|
for zb in range(1, p):
|
||||||
for pa in points:
|
for pa in points:
|
||||||
@ -211,8 +212,11 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
|
|||||||
match = True
|
match = True
|
||||||
r, e = concrete_verify(require)
|
r, e = concrete_verify(require)
|
||||||
if not r:
|
if not r:
|
||||||
|
ret = False
|
||||||
print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
|
print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
|
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
|
||||||
@ -244,15 +248,21 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
|
|||||||
|
|
||||||
print("Formula " + name + ":")
|
print("Formula " + name + ":")
|
||||||
count = 0
|
count = 0
|
||||||
|
ret = True
|
||||||
for branch in range(branches):
|
for branch in range(branches):
|
||||||
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
|
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
|
||||||
|
assumeBranch = assumeBranch.map(lift)
|
||||||
|
assumeFormula = assumeFormula.map(lift)
|
||||||
pC.X = lift(pC.X)
|
pC.X = lift(pC.X)
|
||||||
pC.Y = lift(pC.Y)
|
pC.Y = lift(pC.Y)
|
||||||
pC.Z = lift(pC.Z)
|
pC.Z = lift(pC.Z)
|
||||||
pC.Infinity = lift(pC.Infinity)
|
pC.Infinity = lift(pC.Infinity)
|
||||||
|
|
||||||
for key in laws_jacobian_weierstrass:
|
for key in laws_jacobian_weierstrass:
|
||||||
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
|
success, msg = check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC)
|
||||||
|
if not success:
|
||||||
|
ret = False
|
||||||
|
res[key].append((msg, branch))
|
||||||
|
|
||||||
for key in res:
|
for key in res:
|
||||||
print(" %s:" % key)
|
print(" %s:" % key)
|
||||||
@ -262,3 +272,4 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
|
|||||||
print(" branch %i: %s" % (x[1], x[0]))
|
print(" branch %i: %s" % (x[1], x[0]))
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
return ret
|
||||||
|
Loading…
x
Reference in New Issue
Block a user