py3port: Zcash and Monero-related changes

This commit is contained in:
The MMGen Project 2018-10-31 18:12:07 +00:00
commit 53189f4469
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
12 changed files with 118 additions and 36 deletions

View file

@ -99,11 +99,11 @@ class AddrGeneratorZcashZ(AddrGenerator):
vk_width = 97
def zhash256(self,s,t):
s = list(map(ord,s+'\0'*32))
s = bytearray(s + bytes(32))
s[0] |= 0xc0
s[32] = t
from mmgen.sha256 import Sha256
return Sha256(list(map(chr,s)),preprocess=False).digest()
return Sha256(s,preprocess=False).digest()
def to_addr(self,pubhex): # pubhex is really privhex
key = unhexlify(pubhex)
@ -120,12 +120,12 @@ class AddrGeneratorZcashZ(AddrGenerator):
def to_viewkey(self,pubhex): # pubhex is really privhex
key = unhexlify(pubhex)
assert len(key) == 32,'{}: incorrect privkey length'.format(len(key))
vk = list(map(ord,self.zhash256(key,0)+self.zhash256(key,1)))
vk = bytearray(self.zhash256(key,0)+self.zhash256(key,1))
vk[32] &= 0xf8
vk[63] &= 0x7f
vk[63] |= 0x40
from mmgen.protocol import _b58chk_encode
ret = _b58chk_encode(g.proto.addr_ver_num['viewkey'][0] + hexlify(''.join(map(chr,vk))))
ret = _b58chk_encode(g.proto.addr_ver_num['viewkey'][0] + hexlify(vk))
assert len(ret) == self.vk_width,'Invalid Zcash view key length'
return ZcashViewKey(ret)
@ -142,13 +142,11 @@ class AddrGeneratorMonero(AddrGenerator):
def to_addr(self,sk_hex): # sk_hex instead of pubhex
# ed25519ll, a low-level ctypes wrapper for Ed25519 digital signatures by
# Daniel Holth <dholth@fastmail.fm> - http://bitbucket.org/dholth/ed25519ll/
try:
assert not opt.use_internal_ed25519_mod
from ed25519ll.djbec import scalarmult,edwards,encodepoint,B
except:
from mmgen.ed25519 import scalarmult,edwards,encodepoint,B
if opt.use_old_ed25519:
from mmgen.ed25519 import edwards,encodepoint,B,scalarmult
else:
from mmgen.ed25519ll_djbec import scalarmult
from mmgen.ed25519 import edwards,encodepoint,B
# Source and license for scalarmultbase function:
# https://github.com/bigreddmachine/MoneroPy/blob/master/moneropy/crypto/ed25519.py

View file

@ -2,15 +2,10 @@
# Source: https://ed25519.cr.yp.to/python/ed25519.py
# Date accessed: 2 Nov. 2016
import hashlib
b = 256
q = 2**255 - 19
l = 2**252 + 27742317777372353535851937790883648493
def H(m):
return hashlib.sha512(m).digest()
def expmod(b, e, m):
if e == 0: return 1
t = expmod(b, e//2, m)**2 % m
@ -54,4 +49,4 @@ def encodepoint(P):
x = P[0]
y = P[1]
bits = [(y >> i) & 1 for i in range(b-1)] + [x & 1]
return b''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b//8)])
return bytes([sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b//8)])

85
mmgen/ed25519ll_djbec.py Normal file
View file

@ -0,0 +1,85 @@
# Ported to Python 3 (added floor division) from ed25519ll package:
# https://pypi.org/project/ed25519ll/
# This module is adapted from that package's pure-Python fallback
# implementation. All functions and vars not required by MMGen have
# been removed.
#
# ed25519ll, a low-level ctypes wrapper for Ed25519 digital signatures by
# Daniel Holth <dholth@fastmail.fm> - http://bitbucket.org/dholth/ed25519ll/
# This wrapper also contains a reasonably performant pure-Python fallback.
# Unlike the reference implementation, the Python implementation does not
# contain protection against timing attacks.
#
# Ed25519 digital signatures
# Based on http://ed25519.cr.yp.to/python/ed25519.py
# See also http://ed25519.cr.yp.to/software.html
# Adapted by Ron Garret
# Sped up considerably using coordinate transforms found on:
# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
# Specifically add-2008-hwcd-4 and dbl-2008-hwcd
q = 2**255 - 19
def expmod(b,e,m):
if e == 0: return 1
t = expmod(b,e//2,m)**2 % m
if e & 1: t = (t*b) % m
return t
# Can probably get some extra speedup here by replacing this with
# an extended-euclidean, but performance seems OK without that
def inv(x):
return expmod(x,q-2,q)
# Faster (!) version based on:
# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
def xpt_add(pt1, pt2):
(X1, Y1, Z1, T1) = pt1
(X2, Y2, Z2, T2) = pt2
A = ((Y1-X1)*(Y2+X2)) % q
B = ((Y1+X1)*(Y2-X2)) % q
C = (Z1*2*T2) % q
D = (T1*2*Z2) % q
E = (D+C) % q
F = (B-A) % q
G = (B+A) % q
H = (D-C) % q
X3 = (E*F) % q
Y3 = (G*H) % q
Z3 = (F*G) % q
T3 = (E*H) % q
return (X3, Y3, Z3, T3)
def xpt_double (pt):
(X1, Y1, Z1, _) = pt
A = (X1*X1)
B = (Y1*Y1)
C = (2*Z1*Z1)
D = (-A) % q
J = (X1+Y1) % q
E = (J*J-A-B) % q
G = (D+B) % q
F = (G-C) % q
H = (D-B) % q
X3 = (E*F) % q
Y3 = (G*H) % q
Z3 = (F*G) % q
T3 = (E*H) % q
return (X3, Y3, Z3, T3)
def pt_xform (pt):
(x, y) = pt
return (x, y, 1, (x*y)%q)
def pt_unxform (pt):
(x, y, z, _) = pt
return ((x*inv(z))%q, (y*inv(z))%q)
def xpt_mult (pt, n):
if n==0: return pt_xform((0,1))
_ = xpt_double(xpt_mult(pt, n>>1))
return xpt_add(_, pt) if n&1 else _
def scalarmult(pt, e):
return pt_unxform(xpt_mult(pt_xform(pt), e))

View file

@ -53,8 +53,8 @@ opts_data = lambda: {
-c, --print-checksum Print address list checksum and exit
-d, --outdir= d Output files to directory 'd' instead of working dir
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
-E, --use-internal-ed25519-mod Use (slow) internal ed25519 module for Monero
address generation, even if ed25519ll is installed
-E, --use-old-ed25519 Use original (and slow) ed25519 module for Monero
address generation instead of ed25519ll_djbec
-i, --in-fmt= f Input is from wallet format 'f' (see FMT CODES below)
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
'f' at offset 'o' (comma-separated)
@ -123,8 +123,8 @@ addr_type = MAT(opt.type or g.proto.dfl_mmtype,errmsg=errmsg)
if len(cmd_args) < 1: opts.usage()
if opt.use_internal_ed25519_mod:
msg('Using (slow) internal ed25519 module by user request')
if opt.use_old_ed25519:
msg('Using old (slow) ed25519 module by user request')
idxs = AddrIdxList(fmt_str=cmd_args.pop())

View file

@ -141,7 +141,7 @@ Type '{pn} help <command> for help on a particular command
)
}
cmd_args = opts.init(opts_data,add_opts=['hidden_incog_input_params','in_fmt','use_internal_ed25519_mod'])
cmd_args = opts.init(opts_data,add_opts=['hidden_incog_input_params','in_fmt','use_old_ed25519'])
if len(cmd_args) < 1: opts.usage()

View file

@ -397,6 +397,7 @@ class BTCAmt(Decimal,Hilite,InitErrors):
class BCHAmt(BTCAmt): pass
class B2XAmt(BTCAmt): pass
class LTCAmt(BTCAmt): max_amt = 84000000
class XMRAmt(BTCAmt): min_coin_unit = Decimal('0.000000000001')
from mmgen.altcoins.eth.obj import ETHAmt,ETHNonce

View file

@ -402,10 +402,7 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen):
@classmethod
def preprocess_key(cls,hexpriv,pubkey_type): # reduce key
try:
from ed25519ll.djbec import l
except:
from mmgen.ed25519 import l
from mmgen.ed25519 import l
n = int(hexlify(unhexlify(hexpriv)[::-1]),16) % l
return hexlify(unhexlify('{:064x}'.format(n))[::-1])

View file

@ -47,7 +47,7 @@ class Sha256(object):
n,nPrime = 2,0
while nPrime < 64:
if isPrime(n):
k[nPrime] = getFractionalBits(math.pow(n, 1.0 / 3))
k[nPrime] = getFractionalBits(math.pow(n, 1 / 3))
nPrime += 1
n += 1
@ -73,21 +73,21 @@ class Sha256(object):
return hexlify(self.digest())
def bytesToWords(self):
assert type(self.M) in (str,list)
assert type(self.M) in (bytes,bytearray,list)
words = [0] * (len(self.M) // 4 + len(self.M) % 4)
b = 0
for i in range(len(self.M)):
words[b >> 5] |= ord(self.M[i]) << (24 - b % 32)
words[b >> 5] |= self.M[i] << (24 - b % 32)
b += 8
self.M = words
def wordsToBytes(self):
assert type(self.M) == list and len(self.M) == 8
self.M = ''.join([chr((self.M[b >> 5] >> (24 - b % 32)) & 0xff) for b in range(0,len(self.M)*32,8)])
self.M = bytes([(self.M[b >> 5] >> (24 - b % 32) & 0xff) for b in range(0,len(self.M)*32,8)])
def preprocessBlock(self):
def lshift(a,b): return (a << b) & 0xffffffff
assert type(self.M) == str
assert type(self.M) in (bytes,bytearray)
l = len(self.M) * 8
self.bytesToWords()
last_idx = lshift((l + 64 >> 9),4) + 15

View file

@ -624,14 +624,15 @@ def monero_wallet_ops(infile,op,blockheight=None,addrs=None):
gmsg('\n{} wallet{} {}ed'.format(dl,suf(dl),m[op][0].lower()))
if op == 'sync':
col1_w = max(list(map(len,bals))) + 1
fs = '{:%s} {:18} {:18}' % col1_w
msg('\n'+fs.format('Wallet',' Balance',' Unlocked Balance'))
fs = '{:%s} {} {}' % col1_w
msg('\n'+fs.format('Wallet','Balance ','Unlocked Balance '))
tbals = [Decimal('0'),Decimal('0')]
from mmgen.obj import XMRAmt
for bal in bals:
for i in (0,1): tbals[i] += bals[bal][i]
msg(fs.format(bal+':',*bals[bal]))
msg(fs.format(bal+':',*[XMRAmt(b).fmt(fs='5.12',color=True) for b in bals[bal]]))
msg(fs.format('-'*col1_w,'-'*18,'-'*18))
msg(fs.format('TOTAL:',*tbals))
msg(fs.format('TOTAL:',*[XMRAmt(b).fmt(fs='5.12',color=True) for b in tbals]))
os.environ['LANG'] = 'C'
import pexpect

View file

@ -193,7 +193,7 @@ t_monero=(
"mmgen-walletgen -q -r0 -p1 -Llabel --outdir $TMPDIR -o words"
"$mmgen_keygen -q --accept-defaults --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $monero_addrs"
'cs1=$(mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys)'
"$mmgen_keygen -q --use-internal-ed25519-mod --accept-defaults --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $monero_addrs"
"$mmgen_keygen -q --use-old-ed25519 --accept-defaults --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $monero_addrs"
'cs2=$(mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys)'
'[ "$cs1" == "$cs2" ] || false'
"$mmgen_tool -q --accept-defaults --outdir $TMPDIR keyaddrlist2monerowallets $TMPDIR/*-XMR*.akeys addrs=23"

View file

@ -115,6 +115,7 @@ setup(
'mmgen.common',
'mmgen.crypto',
'mmgen.ed25519',
'mmgen.ed25519ll_djbec',
'mmgen.exception',
'mmgen.filename',
'mmgen.globalvars',

View file

@ -1069,6 +1069,8 @@ cmd_group['ref_alt'] = (
('ref_addrfile_gen_zec', 'generate address file (ZEC-T)'),
('ref_addrfile_gen_zec_z','generate address file (ZEC-Z)'),
('ref_addrfile_gen_xmr', 'generate address file (XMR)'),
# we test the old ed25519 library in test-release.sh, so skip this
# ('ref_addrfile_gen_xmr_old','generate address file (XMR - old (slow) ed25519 library)'),
('ref_keyaddrfile_gen_eth', 'generate key-address file (ETH)'),
('ref_keyaddrfile_gen_etc', 'generate key-address file (ETC)'),
@ -2609,6 +2611,8 @@ class MMGenTestSuite(object):
def ref_addrfile_gen_xmr(self,name):
self.ref_altcoin_addrgen(name,coin='XMR',mmtype='monero')
def ref_addrfile_gen_xmr_old(self,name):
self.ref_altcoin_addrgen(name,coin='XMR',mmtype='monero',add_args=['--use-old-ed25519'])
def ref_keyaddrfile_gen_eth(self,name):
self.ref_altcoin_addrgen(name,coin='ETH',mmtype='ethereum',gen_what='key')