Added modular square root-based sequential PoW
This commit is contained in:
parent
e4e711f971
commit
2d231e2921
|
@ -0,0 +1,139 @@
|
|||
import time
|
||||
|
||||
def legendre_symbol(a, p):
|
||||
"""
|
||||
Legendre symbol
|
||||
Define if a is a quadratic residue modulo odd prime
|
||||
http://en.wikipedia.org/wiki/Legendre_symbol
|
||||
"""
|
||||
ls = pow(a, (p - 1)/2, p)
|
||||
if ls == p - 1:
|
||||
return -1
|
||||
return ls
|
||||
|
||||
def prime_mod_sqrt(a, p):
|
||||
"""
|
||||
Square root modulo prime number
|
||||
Solve the equation
|
||||
x^2 = a mod p
|
||||
and return list of x solution
|
||||
http://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
|
||||
"""
|
||||
a %= p
|
||||
|
||||
# Simple case
|
||||
if a == 0:
|
||||
return [0]
|
||||
if p == 2:
|
||||
return [a]
|
||||
|
||||
# Check solution existence on odd prime
|
||||
if legendre_symbol(a, p) != 1:
|
||||
return []
|
||||
|
||||
# Simple case
|
||||
if p % 4 == 3:
|
||||
x = pow(a, (p + 1)/4, p)
|
||||
return [x, p-x]
|
||||
|
||||
# Factor p-1 on the form q * 2^s (with Q odd)
|
||||
q, s = p - 1, 0
|
||||
while q % 2 == 0:
|
||||
s += 1
|
||||
q //= 2
|
||||
|
||||
# Select a z which is a quadratic non resudue modulo p
|
||||
z = 1
|
||||
while legendre_symbol(z, p) != -1:
|
||||
z += 1
|
||||
c = pow(z, q, p)
|
||||
|
||||
# Search for a solution
|
||||
x = pow(a, (q + 1)/2, p)
|
||||
t = pow(a, q, p)
|
||||
m = s
|
||||
while t != 1:
|
||||
# Find the lowest i such that t^(2^i) = 1
|
||||
i, e = 0, 2
|
||||
for i in xrange(1, m):
|
||||
if pow(t, e, p) == 1:
|
||||
break
|
||||
e *= 2
|
||||
|
||||
# Update next value to iterate
|
||||
b = pow(c, 2**(m - i - 1), p)
|
||||
x = (x * b) % p
|
||||
t = (t * b * b) % p
|
||||
c = (b * b) % p
|
||||
m = i
|
||||
|
||||
return [x, p-x]
|
||||
|
||||
def inv(a, n):
|
||||
if a == 0:
|
||||
return 0
|
||||
lm, hm = 1, 0
|
||||
low, high = a % n, n
|
||||
while low > 1:
|
||||
r = high//low
|
||||
nm, new = hm-lm*r, high-low*r
|
||||
lm, low, hm, high = nm, new, lm, low
|
||||
return lm % n
|
||||
|
||||
# Pre-compute (i) a list of primes
|
||||
# (ii) a list of -1 legendre bases for each prime
|
||||
# (iii) the inverse for each base
|
||||
LENPRIMES = 1000
|
||||
primes = []
|
||||
r = 2**31 - 1
|
||||
for i in range(LENPRIMES):
|
||||
r += 2
|
||||
while pow(2, r, r) != 2: r += 2
|
||||
primes.append(r)
|
||||
bases = [None] * LENPRIMES
|
||||
invbases = [None] * LENPRIMES
|
||||
for i in range(LENPRIMES):
|
||||
b = 2
|
||||
while legendre_symbol(b, primes[i]) == 1:
|
||||
b += 1
|
||||
bases[i] = b
|
||||
invbases[i] = inv(b, primes[i])
|
||||
|
||||
# Compute the PoW
|
||||
def forward(val, rounds=10**6):
|
||||
t1 = time.time()
|
||||
for i in range(rounds):
|
||||
# Select a prime
|
||||
p = primes[i % LENPRIMES]
|
||||
# Make sure the value we're working on is a
|
||||
# quadratic residue. If it's not, do a spooky
|
||||
# transform (ie. multiply by a known
|
||||
# non-residue) to make sure that it is
|
||||
if legendre_symbol(val, p) != 1:
|
||||
val = (val * invbases[i % LENPRIMES]) % p
|
||||
mul_by_base = 1
|
||||
else:
|
||||
mul_by_base = 0
|
||||
# Take advantage of the fact that two square
|
||||
# roots exist to hide whether or not the spooky
|
||||
# transform was done in the result so that we
|
||||
# can invert it when verifying
|
||||
val = sorted(prime_mod_sqrt(val, p))[mul_by_base]
|
||||
print time.time() - t1
|
||||
return val
|
||||
|
||||
def backward(val, rounds=10**6):
|
||||
t1 = time.time()
|
||||
for i in range(rounds-1, -1, -1):
|
||||
# Select a prime
|
||||
p = primes[i % LENPRIMES]
|
||||
# Extract the info about whether or not the
|
||||
# spooky transform was done
|
||||
mul_by_base = val * 2 > p
|
||||
# Square the value (ie. invert the square root)
|
||||
val = pow(val, 2, p)
|
||||
# Undo the spooky transform if needed
|
||||
if mul_by_base:
|
||||
val = (val * bases[i % LENPRIMES]) % p
|
||||
print time.time() - t1
|
||||
return val
|
Loading…
Reference in New Issue