Skip to content

Commit

Permalink
Updated ECC example
Browse files Browse the repository at this point in the history
  • Loading branch information
vbuterin committed May 31, 2017
1 parent aa655e2 commit 3ec98d0
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 63 deletions.
6 changes: 3 additions & 3 deletions examples/ecc/ecrecover.se
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# that I created. It may possible to achieve a further 20-50% savings by applying
# that version.

extern modint.type: [add:[int256,int256,int256,int256,int256,int256]:int256[], decompose:[int256[]]:int256[], double:[int256,int256,int256]:int256[], exp:[int256,int256,int256]:int256, mul:[int256,int256,int256,int256]:int256[], recover_y:[int256,int256]:int256]
extern modexp.se: [exp:[int256,int256,int256]:int256]
extern modint.type: [add:[uint256,uint256,uint256,uint256,uint256,uint256]:int256[], decompose:[int256[]]:int256[], double:[uint256,uint256,uint256]:int256[], exp:[uint256,uint256,uint256]:int256, mul:[uint256,uint256,uint256,uint256]:int256[], recover_y:[uint256,int256]:int256]
extern modexp.se: [exp:[uint256,uint256,uint256]:int256]

data JACOBIAN_ARITH
data EXP
Expand All @@ -17,7 +17,7 @@ def init():

event PubkeyTripleLogEvent(x:uint256, y:uint256, z:uint256)

def ecrecover(h, v, r, s):
def ecrecover(h: uint256, v: uint256, r: uint256, s: uint256):
h = mod(h, N)
r = mod(r, P)
s = mod(s, N)
Expand Down
10 changes: 5 additions & 5 deletions examples/ecc/jacobian_arith.se
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
inset('modint.type')

def double(m_ax, m_ay, m_az):
def double(m_ax: uint256, m_ay: uint256, m_az: uint256):
if !m_ay:
return([0, 0, 0]:arr)
with P_______P = -4294968273:
Expand All @@ -18,7 +18,7 @@ def double(m_ax, m_ay, m_az):
~mstore(~msize(), m_nz)
return((~msize() - 96):arr)

def add(m_ax, m_ay, m_az, m_bx, m_by, m_bz):
def add(m_ax: uint256, m_ay: uint256, m_az: uint256, m_bx: uint256, m_by: uint256, m_bz:uint256):
if !m_ay:
return([m_bx, m_by, m_bz]:arr)
if !m_by:
Expand Down Expand Up @@ -48,7 +48,7 @@ def add(m_ax, m_ay, m_az, m_bx, m_by, m_bz):
return((~msize() - 96):arr)


def mul(m_bx, m_by, m_bz, n):
def mul(m_bx: uint256, m_by: uint256, m_bz: uint256, n: uint256):
n = mod(n, -432420386565659656852420866394968145599)
if !m_bx * !m_by + !n: # Constant-gas version of !axn and !ayn or !n
return([0, 0, 1]:arr)
Expand Down Expand Up @@ -100,7 +100,7 @@ def mul(m_bx, m_by, m_bz, n):
return(o:arr)


def recover_y(x, y_bit):
def recover_y(x: uint256, y_bit):
N = -432420386565659656852420866394968145599
P = -4294968273
xcubed = mulmod(mulmod(x, x, P), x, P)
Expand All @@ -109,7 +109,7 @@ def recover_y(x, y_bit):
return(beta * y_is_positive + (P - beta) * (1 - y_is_positive))


def exp(b, e, m):
def exp(b: uint256, e: uint256, m: uint256):
with o = 1:
with bit = 2 ^ 255:
while gt(bit, 0):
Expand Down
2 changes: 1 addition & 1 deletion examples/ecc/modexp.se
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def exp(b, e, m):
def exp(b: uint256, e: uint256, m:uint256):
with o = 1:
with bit = 2 ^ 255:
while gt(bit, 0):
Expand Down
4 changes: 2 additions & 2 deletions examples/ecc/ringsig.se
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TOTALLY NOT TESTED AND LIKELY BROKEN AT THIS POINT; AWAITING A TEST SUITE

extern modint.type: [add:[int256,int256,int256,int256,int256,int256]:int256[], decompose:[int256[]]:int256[], double:[int256,int256,int256]:int256[], exp:[int256,int256,int256]:int256, mul:[int256,int256,int256,int256]:int256[], recover_y:[int256,int256]:int256]
extern modint.type: [add:[uint256,uint256,uint256,uint256,uint256,uint256]:int256[], decompose:[int256[]]:int256[], double:[uint256,uint256,uint256]:int256[], exp:[uint256,uint256,uint256]:int256, mul:[uint256,uint256,uint256,uint256]:int256[], recover_y:[uint256,int256]:int256]
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
P = -4294968273
Expand Down Expand Up @@ -37,7 +37,7 @@ event PubkeyLogEvent(x:uint256, y:uint256)

event PubkeyTripleLogEvent(x:uint256, y:uint256, z:uint256)

def hash_pubkey_to_pubkey(pub:arr):
def hash_pubkey_to_pubkey(pub:uint256[]):
x = sha3(pub:arr)
while 1:
xcubed = mulmod(mulmod(x, x, P), x, P)
Expand Down
14 changes: 7 additions & 7 deletions examples/ecc/substitutes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def signed(o):
if not isinstance(o, (list, tuple)):
return o - 2**256 if o >= 2**255 else o
return map(lambda x: x - 2**256 if x >= 2**255 else x, o)
return list(map(lambda x: x - 2**256 if x >= 2**255 else x, o))


def hamming_weight(n):
Expand Down Expand Up @@ -40,7 +40,7 @@ def modexp_substitute(base, exp, mod):
def ecrecover_substitute(z, v, r, s):
P, A, B, N, Gx, Gy = b.P, b.A, b.B, b.N, b.Gx, b.Gy
x = r
beta = pow(x*x*x+A*x+B, (P + 1) / 4, P)
beta = pow(x*x*x+A*x+B, (P + 1) // 4, P)
y = beta if v % 2 ^ beta % 2 else (P - beta)
Gz = b.jacobian_multiply((Gx, Gy, 1), (N - z) % N)
XY = b.jacobian_multiply((x, y, 1), s)
Expand All @@ -53,7 +53,7 @@ def recover_y(x, y_bit):
N = 2**256-432420386565659656852420866394968145599
P = 2**256-4294968273
xcubed = x**3 % P
beta = pow((x**3 + 7) % P, (P + 1) / 4, P)
beta = pow((x**3 + 7) % P, (P + 1) // 4, P)
y_is_positive = y_bit ^ (beta % 2) ^ 1
return(beta * y_is_positive + (P - beta) * (1 - y_is_positive))

Expand All @@ -76,15 +76,15 @@ def decompose(Q):
return([ox, oy])

def hash_array(arr):
o = ''
o = b''
for x in arr:
if isinstance(x, (int, long)):
if isinstance(x, int):
x = utils.zpad(utils.encode_int(x), 32)
o += x
return utils.big_endian_to_int(utils.sha3(o))

def hash_value(x):
if isinstance(x, (int, long)):
if isinstance(x, int):
x = utils.zpad(utils.encode_int(x), 32)
return utils.big_endian_to_int(utils.sha3(x))

Expand Down Expand Up @@ -140,7 +140,7 @@ def ringsig_sign_substitute(msghash, priv, pub_xs, pub_ys):
return (e[0]["left"], s, I[0], I[1] % 2)

def bit(bytez, i):
return (ord(bytez[i // 8]) / 2**(i % 8)) % 2
return (ord(bytez[i // 8]) // 2**(i % 8)) % 2

def ringsig_verify_substitute(msghash, x0, s, Ix, Iy, pub_xs, pub_ys):
# Number of pubkeys
Expand Down
83 changes: 38 additions & 45 deletions examples/ecc/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import random
import sys
import math
from ethereum import tester as t
from ethereum.tools import tester as t
from ethereum import utils
import substitutes
import time
Expand All @@ -18,78 +18,73 @@
def neg_point(p):
return [p[0], b.P - p[1], p[2]]

s = t.state()
s.block.gas_limit = 100000000
t.gas_limit = 3000000
s = t.Chain()
s.head_state.gas_limit = 10**9

tests = sys.argv[1:]

if '--log' in tests:
t.set_logging_level(int((tests+[1])[tests.index('--log') + 1]))

if '--modexp' in tests or not len(tests):
c = s.abi_contract('jacobian_arith.se')
print "Starting modexp tests"
c = s.contract('jacobian_arith.se', language='serpent')
print("Starting modexp tests")

for i in range(0, len(vals) - 2, 3):
o1 = substitutes.modexp_substitute(vals[i], vals[i+1], vals[i+2])
o2 = c.exp(vals[i], vals[i+1], vals[i+2], profiling=1)
print "gas", o2["gas"], "time", o2["time"]
assert o1 == o2["output"], (o1, o2)
o2 = c.exp(vals[i], vals[i+1], vals[i+2])
assert o1 == o2

if '--double' in tests or not len(tests):
print "Starting doubling tests"
c = s.abi_contract('jacobian_arith.se')
print("Starting doubling tests")
c = s.contract('jacobian_arith.se', language='serpent')
for i in range(5):
print 'trying doubling test', vals[i]
print('trying doubling test', vals[i])
P = b.to_jacobian(b.privtopub(vals[i]))
o1 = substitutes.jacobian_double_substitute(*list(P))
o2 = c.double(*(list(P)), profiling=2)
print "gas", o2["gas"], "time", o2["time"], "ops", o2["ops"]
assert o1 == o2["output"], (o1, o2)
o2 = c.double(*(list(P)))
assert o1 == o2, (o1, o2)

if '--add' in tests or not len(tests):
print "Starting addition tests"
c = s.abi_contract('jacobian_arith.se')
print("Starting addition tests")
c = s.contract('jacobian_arith.se', language='serpent')
for i in range(5):
print 'trying addition test', vals[i * 2], vals[i * 2 + 1]
print('trying addition test', vals[i * 2], vals[i * 2 + 1])
P = b.to_jacobian(b.privtopub(vals[i * 2]))
Q = b.to_jacobian(b.privtopub(vals[i * 2 + 1]))
o1 = substitutes.jacobian_add_substitute(*(list(P) + list(Q)))
o2 = c.add(*(list(P) + list(Q)), profiling=2)
print "gas", o2["gas"], "time", o2["time"], "ops", o2["ops"]
assert o1 == o2["output"], (o1, o2)
o2 = c.add(*(list(P) + list(Q)))
assert o1 == o2

if '--mul' in tests or not len(tests):
print "Starting multiplication tests"
c = s.abi_contract('jacobian_arith.se')
print("Starting multiplication tests")
c = s.contract('jacobian_arith.se', language='serpent')
for i in range(5):
print 'trying multiplication test', vals[i * 2], vals[i * 2 + 1]
print('trying multiplication test', vals[i * 2], vals[i * 2 + 1])
P = b.to_jacobian(b.privtopub(vals[i * 2]))
q = vals[i * 2 + 1]
o1 = substitutes.jacobian_mul_substitute(*(list(P) + [q]))
a = time.time()
print list(P) + [q]
o2 = c.mul(*(list(P) + [q]), profiling=1)
print "gas", o2["gas"], "time", o2["time"]
assert o1 == o2["output"], (o1, o2)
print(list(P) + [q])
o2 = c.mul(*(list(P) + [q]))
assert o1 == o2


if '--ecrecover' in tests or not len(tests):
c = s.abi_contract('ecrecover.se')
print "Starting ecrecover tests"
c = s.contract('ecrecover.se', language='serpent')
print("Starting ecrecover tests")

for i in range(5):
print 'trying ecrecover_test', vals[i*2], vals[i*2+1]
print('trying ecrecover_test', vals[i*2], vals[i*2+1])
k = vals[i*2]
h = vals[i*2+1]
V, R, S = b.ecdsa_raw_sign(b.encode(h, 256, 32), k)
aa = time.time()
o1 = substitutes.ecrecover_substitute(h, V, R, S)
print 'Native execution time:', time.time() - aa
print('Native execution time:', time.time() - aa)
a = time.time()
o2 = c.ecrecover(h, V, R, S)
print 'time', time.time() - a, 'gas', s.block.get_receipts()[-1].gas_used - s.block.get_receipts()[-2].gas_used
print('time', time.time() - a, 'gas', s.head_state.receipts[-1].gas_used - s.head_state.receipts[-2].gas_used)
assert o1 == o2, (o1, o2)

# Explicit tests
Expand All @@ -107,14 +102,14 @@ def neg_point(p):
assert o1 == o2, (o1, o2)

if '--ringsig' in tests or not len(tests):
print "Starting ringsig tests"
c = s.abi_contract('ringsig.se')
print("Starting ringsig tests")
c = s.contract('ringsig.se', language='serpent')
for L in range(2, 6):
privs = vals[:L]
my_priv = vals[1]
pubs = map(b.privtopub, privs)
pubs = list(map(b.privtopub, privs))
for pub in pubs:
assert map(substitutes.signed, list(substitutes.hash_to_pubkey(list(pub)))) == \
assert list(map(substitutes.signed, list(substitutes.hash_to_pubkey(list(pub))))) == \
c.hash_pubkey_to_pubkey(list(pub))
pub_xs = [x[0] for x in pubs]
pub_ys = []
Expand All @@ -127,11 +122,9 @@ def neg_point(p):
x0, s_vals, Ix, Iy = substitutes.ringsig_sign_substitute(msghash, my_priv, pub_xs, pub_ys)
t1 = time.time()
assert substitutes.ringsig_verify_substitute(msghash, x0, s_vals, Ix, Iy, pub_xs, pub_ys)
print 'Native execution time: ', time.time() - t1
ogl = t.gas_limit
t.gas_limit = 10000000
o = c.verify(msghash, x0, s_vals, Ix, Iy, pub_xs, pub_ys, profiling=1)
assert o["output"]
print 'number of pubkeys', L, 'gas', o["gas"], 'time', o["time"], \
'totalgas', s.block.get_receipts()[-1].gas_used - s.block.get_receipts()[-2].gas_used
t.gas_limit = ogl
print('Native execution time: ', time.time() - t1)
t2 = time.time()
o = c.verify(msghash, x0, s_vals, Ix, Iy, pub_xs, pub_ys, startgas=10**7)
assert o
print('number of pubkeys', L, \
'totalgas', s.head_state.receipts[-1].gas_used - s.head_state.receipts[-2].gas_used, 'EVM verification time:', time.time() - t2)

0 comments on commit 3ec98d0

Please sign in to comment.