Remove dependencies on pysha3 and ethereum modules
- pysha3: Monero and Ethereum use the old pre-SHA3 version of the keccak hash function, which is not supported by hashlib.sha3. The pysha3 package supports the function but is not portable. Therefore, use the pure-Python implementation mmgen.keccak as a fallback, making the pysha3 package optional. Use of mmgen.keccak may be forced with --use-internal-keccak-module mmgen.keccak was ported from the Python 2 implementation at https://github.com/ctz/keccak - ethereum (pyethereum): Installation of this package presents numerous problems due to poor maintenance and many superfluous dependencies. Therefore, use stripped-down and modified local versions of ethereum.transactions and ethereum.utils as fallbacks, making the ethereum package optional. The local pyethereum.utils uses mmgen.keccak as a fallback for pysha3's keccak implementation.
This commit is contained in:
parent
281e1f3ffb
commit
a7126ede03
17 changed files with 1083 additions and 74 deletions
|
|
@ -78,14 +78,27 @@ class AddrGeneratorBech32(AddrGenerator):
|
|||
raise NotImplementedError('Segwit redeem script not supported by this address type')
|
||||
|
||||
class AddrGeneratorEthereum(AddrGenerator):
|
||||
|
||||
def __init__(self,addr_type):
|
||||
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
from sha3 import keccak_256
|
||||
except:
|
||||
from mmgen.keccak import keccak_256
|
||||
self.keccak_256 = keccak_256
|
||||
|
||||
from mmgen.protocol import hash256
|
||||
self.hash256 = hash256
|
||||
|
||||
return AddrGenerator.__init__(addr_type)
|
||||
|
||||
def to_addr(self,pubhex):
|
||||
assert type(pubhex) == PubKey
|
||||
import sha3
|
||||
return CoinAddr(sha3.keccak_256(bytes.fromhex(pubhex[2:])).hexdigest()[24:])
|
||||
return CoinAddr(self.keccak_256(bytes.fromhex(pubhex[2:])).hexdigest()[24:])
|
||||
|
||||
def to_wallet_passwd(self,sk_hex):
|
||||
from mmgen.protocol import hash256
|
||||
return WalletPassword(hash256(sk_hex)[:32])
|
||||
return WalletPassword(self.hash256(sk_hex)[:32])
|
||||
|
||||
def to_segwit_redeem_script(self,pubhex):
|
||||
raise NotImplementedError('Segwit redeem script not supported by this address type')
|
||||
|
|
@ -132,6 +145,31 @@ class AddrGeneratorZcashZ(AddrGenerator):
|
|||
|
||||
class AddrGeneratorMonero(AddrGenerator):
|
||||
|
||||
def __init__(self,addr_type):
|
||||
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
from sha3 import keccak_256
|
||||
except:
|
||||
from mmgen.keccak import keccak_256
|
||||
self.keccak_256 = keccak_256
|
||||
|
||||
from mmgen.protocol import hash256
|
||||
self.hash256 = hash256
|
||||
|
||||
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
|
||||
|
||||
self.edwards = edwards
|
||||
self.encodepoint = encodepoint
|
||||
self.scalarmult = scalarmult
|
||||
self.B = B
|
||||
|
||||
return AddrGenerator.__init__(addr_type)
|
||||
|
||||
def b58enc(self,addr_bytes):
|
||||
enc = baseconv.fromhex
|
||||
l = len(addr_bytes)
|
||||
|
|
@ -141,42 +179,33 @@ class AddrGeneratorMonero(AddrGenerator):
|
|||
|
||||
def to_addr(self,sk_hex): # sk_hex instead of pubhex
|
||||
|
||||
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
|
||||
# Copyright (c) 2014-2016, The Monero Project
|
||||
# All rights reserved.
|
||||
def scalarmultbase(e):
|
||||
if e == 0: return [0, 1]
|
||||
Q = scalarmult(B, e//2)
|
||||
Q = edwards(Q, Q)
|
||||
if e & 1: Q = edwards(Q, B)
|
||||
Q = self.scalarmult(self.B, e//2)
|
||||
Q = self.edwards(Q, Q)
|
||||
if e & 1: Q = self.edwards(Q, self.B)
|
||||
return Q
|
||||
|
||||
def hex2int_le(hexstr):
|
||||
return int((bytes.fromhex(hexstr)[::-1]).hex(),16)
|
||||
|
||||
vk_hex = self.to_viewkey(sk_hex)
|
||||
pk_str = encodepoint(scalarmultbase(hex2int_le(sk_hex)))
|
||||
pvk_str = encodepoint(scalarmultbase(hex2int_le(vk_hex)))
|
||||
pk_str = self.encodepoint(scalarmultbase(hex2int_le(sk_hex)))
|
||||
pvk_str = self.encodepoint(scalarmultbase(hex2int_le(vk_hex)))
|
||||
addr_p1 = bytes.fromhex(g.proto.addr_ver_num['monero'][0]) + pk_str + pvk_str
|
||||
|
||||
import sha3
|
||||
return CoinAddr(self.b58enc(addr_p1 + sha3.keccak_256(addr_p1).digest()[:4]))
|
||||
return CoinAddr(self.b58enc(addr_p1 + self.keccak_256(addr_p1).digest()[:4]))
|
||||
|
||||
def to_wallet_passwd(self,sk_hex):
|
||||
from mmgen.protocol import hash256
|
||||
return WalletPassword(hash256(sk_hex)[:32])
|
||||
return WalletPassword(self.hash256(sk_hex)[:32])
|
||||
|
||||
def to_viewkey(self,sk_hex):
|
||||
assert len(sk_hex) == 64,'{}: incorrect privkey length'.format(len(sk_hex))
|
||||
import sha3
|
||||
return MoneroViewKey(g.proto.preprocess_key(sha3.keccak_256(bytes.fromhex(sk_hex)).hexdigest(),None))
|
||||
return MoneroViewKey(g.proto.preprocess_key(self.keccak_256(bytes.fromhex(sk_hex)).hexdigest(),None))
|
||||
|
||||
def to_segwit_redeem_script(self,sk_hex):
|
||||
raise NotImplementedError('Monero addresses incompatible with Segwit')
|
||||
|
|
|
|||
|
|
@ -20,14 +20,19 @@
|
|||
altcoins.eth.contract: Ethereum contract and token classes for the MMGen suite
|
||||
"""
|
||||
|
||||
from sha3 import keccak_256
|
||||
from decimal import Decimal
|
||||
import rlp
|
||||
|
||||
from mmgen.globalvars import g
|
||||
from mmgen.common import *
|
||||
from mmgen.obj import MMGenObject,TokenAddr,CoinTxID,ETHAmt
|
||||
from mmgen.util import msg,msg_r,pmsg,pdie
|
||||
from mmgen.util import msg,pmsg
|
||||
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
from sha3 import keccak_256
|
||||
except:
|
||||
from mmgen.keccak import keccak_256
|
||||
|
||||
def parse_abi(s):
|
||||
return [s[:8]] + [s[8+x*64:8+(x+1)*64] for x in range(len(s[8:])//64)]
|
||||
|
|
@ -103,7 +108,10 @@ class Token(MMGenObject): # ERC20
|
|||
'data': bytes.fromhex(data) }
|
||||
|
||||
def txsign(self,tx_in,key,from_addr,chain_id=None):
|
||||
from ethereum.transactions import Transaction
|
||||
|
||||
try: from ethereum.transactions import Transaction
|
||||
except: from mmgen.altcoins.eth.pyethereum.transactions import Transaction
|
||||
|
||||
if chain_id is None:
|
||||
chain_id_method = ('parity_chainId','eth_chainId')['eth_chainId' in g.rpch.caps]
|
||||
chain_id = int(g.rpch.request(chain_id_method),16)
|
||||
|
|
|
|||
0
mmgen/altcoins/eth/pyethereum/__init__.py
Executable file
0
mmgen/altcoins/eth/pyethereum/__init__.py
Executable file
215
mmgen/altcoins/eth/pyethereum/transactions.py
Normal file
215
mmgen/altcoins/eth/pyethereum/transactions.py
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#
|
||||
# Adapted from: # https://github.com/ethereum/pyethereum/blob/master/ethereum/transactions.py
|
||||
#
|
||||
import rlp
|
||||
from rlp.sedes import big_endian_int,binary
|
||||
from mmgen.altcoins.eth.pyethereum.utils import (
|
||||
str_to_bytes,encode_hex,ascii_chr,big_endian_to_int,TT256,mk_contract_address,
|
||||
ecsign,ecrecover_to_pub,normalize_key )
|
||||
import mmgen.altcoins.eth.pyethereum.utils as utils
|
||||
|
||||
class InvalidTransaction(Exception): pass
|
||||
class opcodes(object):
|
||||
GTXCOST = 21000 # TX BASE GAS COST
|
||||
GTXDATAZERO = 4 # TX DATA ZERO BYTE GAS COST
|
||||
GTXDATANONZERO = 68 # TX DATA NON ZERO BYTE GAS COST
|
||||
|
||||
# in the yellow paper it is specified that s should be smaller than
|
||||
# secpk1n (eq.205)
|
||||
secpk1n = 115792089237316195423570985008687907852837564279074904382605163141518161494337
|
||||
null_address = b'\xff' * 20
|
||||
|
||||
class Transaction(rlp.Serializable):
|
||||
|
||||
"""
|
||||
A transaction is stored as:
|
||||
[nonce, gasprice, startgas, to, value, data, v, r, s]
|
||||
|
||||
nonce is the number of transactions already sent by that account, encoded
|
||||
in binary form (eg. 0 -> '', 7 -> '\x07', 1000 -> '\x03\xd8').
|
||||
|
||||
(v,r,s) is the raw Electrum-style signature of the transaction without the
|
||||
signature made with the private key corresponding to the sending account,
|
||||
with 0 <= v <= 3. From an Electrum-style signature (65 bytes) it is
|
||||
possible to extract the public key, and thereby the address, directly.
|
||||
|
||||
A valid transaction is one where:
|
||||
(i) the signature is well-formed (ie. 0 <= v <= 3, 0 <= r < P, 0 <= s < N,
|
||||
0 <= r < P - N if v >= 2), and
|
||||
(ii) the sending account has enough funds to pay the fee and the value.
|
||||
"""
|
||||
|
||||
fields = [
|
||||
('nonce', big_endian_int),
|
||||
('gasprice', big_endian_int),
|
||||
('startgas', big_endian_int),
|
||||
('to', utils.address),
|
||||
('value', big_endian_int),
|
||||
('data', binary),
|
||||
('v', big_endian_int),
|
||||
('r', big_endian_int),
|
||||
('s', big_endian_int),
|
||||
]
|
||||
|
||||
_sender = None
|
||||
|
||||
def __init__(self, nonce, gasprice, startgas, to, value, data, v=0, r=0, s=0):
|
||||
# self.data = None
|
||||
|
||||
to = utils.normalize_address(to, allow_blank=True)
|
||||
|
||||
super(
|
||||
Transaction,
|
||||
self).__init__(
|
||||
nonce,
|
||||
gasprice,
|
||||
startgas,
|
||||
to,
|
||||
value,
|
||||
data,
|
||||
v,
|
||||
r,
|
||||
s)
|
||||
|
||||
if self.gasprice >= TT256 or self.startgas >= TT256 or \
|
||||
self.value >= TT256 or self.nonce >= TT256:
|
||||
raise InvalidTransaction("Values way too high!")
|
||||
|
||||
@property
|
||||
def sender(self):
|
||||
if not self._sender:
|
||||
# Determine sender
|
||||
if self.r == 0 and self.s == 0:
|
||||
self._sender = null_address
|
||||
else:
|
||||
if self.v in (27, 28):
|
||||
vee = self.v
|
||||
sighash = utils.sha3(rlp.encode(unsigned_tx_from_tx(self), UnsignedTransaction))
|
||||
|
||||
elif self.v >= 37:
|
||||
vee = self.v - self.network_id * 2 - 8
|
||||
assert vee in (27, 28)
|
||||
rlpdata = rlp.encode(rlp.infer_sedes(self).serialize(self)[:-3] + [self.network_id, '', ''])
|
||||
sighash = utils.sha3(rlpdata)
|
||||
else:
|
||||
raise InvalidTransaction("Invalid V value")
|
||||
if self.r >= secpk1n or self.s >= secpk1n or self.r == 0 or self.s == 0:
|
||||
raise InvalidTransaction("Invalid signature values!")
|
||||
pub = ecrecover_to_pub(sighash, vee, self.r, self.s)
|
||||
if pub == b'\x00' * 64:
|
||||
raise InvalidTransaction(
|
||||
"Invalid signature (zero privkey cannot sign)")
|
||||
self._sender = utils.sha3(pub)[-20:]
|
||||
return self._sender
|
||||
|
||||
@property
|
||||
def network_id(self):
|
||||
if self.r == 0 and self.s == 0:
|
||||
return self.v
|
||||
elif self.v in (27, 28):
|
||||
return None
|
||||
else:
|
||||
return ((self.v - 1) // 2) - 17
|
||||
|
||||
@sender.setter
|
||||
def sender(self, value):
|
||||
self._sender = value
|
||||
|
||||
def sign(self, key, network_id=None):
|
||||
"""Sign this transaction with a private key.
|
||||
|
||||
A potentially already existing signature would be overridden.
|
||||
"""
|
||||
if network_id is None:
|
||||
rawhash = utils.sha3(rlp.encode(unsigned_tx_from_tx(self), UnsignedTransaction))
|
||||
else:
|
||||
assert 1 <= network_id < 2**63 - 18
|
||||
rlpdata = rlp.encode(rlp.infer_sedes(self).serialize(self)[:-3] + [network_id, b'', b''])
|
||||
rawhash = utils.sha3(rlpdata)
|
||||
|
||||
key = normalize_key(key)
|
||||
|
||||
v, r, s = ecsign(rawhash, key)
|
||||
if network_id is not None:
|
||||
v += 8 + network_id * 2
|
||||
|
||||
ret = self.copy(
|
||||
v=v, r=r, s=s
|
||||
)
|
||||
ret._sender = utils.privtoaddr(key)
|
||||
return ret
|
||||
|
||||
@property
|
||||
def hash(self):
|
||||
return utils.sha3(rlp.encode(self))
|
||||
|
||||
def to_dict(self):
|
||||
d = {}
|
||||
for name, _ in self.__class__._meta.fields:
|
||||
d[name] = getattr(self, name)
|
||||
if name in ('to', 'data'):
|
||||
d[name] = '0x' + encode_hex(d[name])
|
||||
d['sender'] = '0x' + encode_hex(self.sender)
|
||||
d['hash'] = '0x' + encode_hex(self.hash)
|
||||
return d
|
||||
|
||||
@property
|
||||
def intrinsic_gas_used(self):
|
||||
num_zero_bytes = str_to_bytes(self.data).count(ascii_chr(0))
|
||||
num_non_zero_bytes = len(self.data) - num_zero_bytes
|
||||
return (opcodes.GTXCOST
|
||||
# + (0 if self.to else opcodes.CREATE[3])
|
||||
+ opcodes.GTXDATAZERO * num_zero_bytes
|
||||
+ opcodes.GTXDATANONZERO * num_non_zero_bytes)
|
||||
|
||||
@property
|
||||
def creates(self):
|
||||
"returns the address of a contract created by this tx"
|
||||
if self.to in (b'', '\0' * 20):
|
||||
return mk_contract_address(self.sender, self.nonce)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and self.hash == other.hash
|
||||
|
||||
def __lt__(self, other):
|
||||
return isinstance(other, self.__class__) and self.hash < other.hash
|
||||
|
||||
def __hash__(self):
|
||||
return utils.big_endian_to_int(self.hash)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Transaction(%s)>' % encode_hex(self.hash)[:4]
|
||||
|
||||
def __structlog__(self):
|
||||
return encode_hex(self.hash)
|
||||
|
||||
# This method should be called for block numbers >= HOMESTEAD_FORK_BLKNUM only.
|
||||
# The >= operator is replaced by > because the integer division N/2 always produces the value
|
||||
# which is by 0.5 less than the real N/2
|
||||
def check_low_s_metropolis(self):
|
||||
if self.s > secpk1n // 2:
|
||||
raise InvalidTransaction("Invalid signature S value!")
|
||||
|
||||
def check_low_s_homestead(self):
|
||||
if self.s > secpk1n // 2 or self.s == 0:
|
||||
raise InvalidTransaction("Invalid signature S value!")
|
||||
|
||||
|
||||
class UnsignedTransaction(rlp.Serializable):
|
||||
fields = [
|
||||
(field, sedes) for field, sedes in Transaction._meta.fields
|
||||
if field not in "vrs"
|
||||
]
|
||||
|
||||
def unsigned_tx_from_tx(tx):
|
||||
return UnsignedTransaction(
|
||||
nonce=tx.nonce,
|
||||
gasprice=tx.gasprice,
|
||||
startgas=tx.startgas,
|
||||
to=tx.to,
|
||||
value=tx.value,
|
||||
data=tx.data,
|
||||
)
|
||||
397
mmgen/altcoins/eth/pyethereum/utils.py
Normal file
397
mmgen/altcoins/eth/pyethereum/utils.py
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
#
|
||||
# Adapted from: https://github.com/ethereum/pyethereum/blob/master/ethereum/utils.py
|
||||
#
|
||||
|
||||
from py_ecc.secp256k1 import privtopub, ecdsa_raw_sign, ecdsa_raw_recover
|
||||
import rlp
|
||||
from rlp.sedes import big_endian_int, BigEndianInt, Binary
|
||||
from eth_utils import encode_hex as encode_hex_0x
|
||||
from eth_utils import decode_hex, int_to_big_endian, big_endian_to_int
|
||||
from rlp.utils import ALL_BYTES
|
||||
|
||||
from mmgen.globalvars import g
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
import sha3 as _sha3
|
||||
def sha3_256(x): return _sha3.keccak_256(x).digest()
|
||||
except:
|
||||
from mmgen.keccak import keccak_256
|
||||
def sha3_256(x):
|
||||
return keccak_256(x).digest()
|
||||
|
||||
|
||||
class Memoize:
|
||||
def __init__(self, fn):
|
||||
self.fn = fn
|
||||
self.memo = {}
|
||||
def __call__(self, *args):
|
||||
if args not in self.memo:
|
||||
self.memo[args] = self.fn(*args)
|
||||
return self.memo[args]
|
||||
|
||||
TT256 = 2 ** 256
|
||||
TT256M1 = 2 ** 256 - 1
|
||||
TT255 = 2 ** 255
|
||||
SECP256K1P = 2**256 - 4294968273
|
||||
|
||||
def is_numeric(x): return isinstance(x, int)
|
||||
|
||||
def is_string(x): return isinstance(x, bytes)
|
||||
|
||||
def to_string(value):
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
return bytes(value, 'utf-8')
|
||||
if isinstance(value, int):
|
||||
return bytes(str(value), 'utf-8')
|
||||
|
||||
def int_to_bytes(value):
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
return int_to_big_endian(value)
|
||||
|
||||
def to_string_for_regexp(value):
|
||||
return str(to_string(value), 'utf-8')
|
||||
unicode = str
|
||||
|
||||
def bytearray_to_bytestr(value):
|
||||
return bytes(value)
|
||||
|
||||
def encode_int32(v):
|
||||
return v.to_bytes(32, byteorder='big')
|
||||
|
||||
def bytes_to_int(value):
|
||||
return int.from_bytes(value, byteorder='big')
|
||||
|
||||
def str_to_bytes(value):
|
||||
if isinstance(value, bytearray):
|
||||
value = bytes(value)
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
return bytes(value, 'utf-8')
|
||||
|
||||
def ascii_chr(n):
|
||||
return ALL_BYTES[n]
|
||||
|
||||
def encode_hex(n):
|
||||
if isinstance(n, str):
|
||||
return encode_hex(n.encode('ascii'))
|
||||
return encode_hex_0x(n)[2:]
|
||||
|
||||
def ecrecover_to_pub(rawhash, v, r, s):
|
||||
result = ecdsa_raw_recover(rawhash, (v, r, s))
|
||||
if result:
|
||||
x, y = result
|
||||
pub = encode_int32(x) + encode_int32(y)
|
||||
else:
|
||||
raise ValueError('Invalid VRS')
|
||||
assert len(pub) == 64
|
||||
return pub
|
||||
|
||||
|
||||
def ecsign(rawhash, key):
|
||||
return ecdsa_raw_sign(rawhash, key)
|
||||
|
||||
|
||||
def mk_contract_address(sender, nonce):
|
||||
return sha3(rlp.encode([normalize_address(sender), nonce]))[12:]
|
||||
|
||||
|
||||
def mk_metropolis_contract_address(sender, initcode):
|
||||
return sha3(normalize_address(sender) + initcode)[12:]
|
||||
|
||||
|
||||
def safe_ord(value):
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
else:
|
||||
return ord(value)
|
||||
|
||||
# decorator
|
||||
|
||||
|
||||
def flatten(li):
|
||||
o = []
|
||||
for l in li:
|
||||
o.extend(l)
|
||||
return o
|
||||
|
||||
|
||||
def bytearray_to_int(arr):
|
||||
o = 0
|
||||
for a in arr:
|
||||
o = (o << 8) + a
|
||||
return o
|
||||
|
||||
|
||||
def int_to_32bytearray(i):
|
||||
o = [0] * 32
|
||||
for x in range(32):
|
||||
o[31 - x] = i & 0xff
|
||||
i >>= 8
|
||||
return o
|
||||
|
||||
# sha3_count = [0]
|
||||
|
||||
|
||||
def sha3(seed):
|
||||
return sha3_256(to_string(seed))
|
||||
|
||||
|
||||
assert encode_hex(
|
||||
sha3(b'')) == 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
|
||||
|
||||
|
||||
@Memoize
|
||||
def privtoaddr(k):
|
||||
k = normalize_key(k)
|
||||
x, y = privtopub(k)
|
||||
return sha3(encode_int32(x) + encode_int32(y))[12:]
|
||||
|
||||
|
||||
def normalize_address(x, allow_blank=False):
|
||||
if is_numeric(x):
|
||||
return int_to_addr(x)
|
||||
if allow_blank and x in {'', b''}:
|
||||
return b''
|
||||
if len(x) in (42, 50) and x[:2] in {'0x', b'0x'}:
|
||||
x = x[2:]
|
||||
if len(x) in (40, 48):
|
||||
x = decode_hex(x)
|
||||
if len(x) == 24:
|
||||
assert len(x) == 24 and sha3(x[:20])[:4] == x[-4:]
|
||||
x = x[:20]
|
||||
if len(x) != 20:
|
||||
raise Exception("Invalid address format: %r" % x)
|
||||
return x
|
||||
|
||||
|
||||
def normalize_key(key):
|
||||
if is_numeric(key):
|
||||
o = encode_int32(key)
|
||||
elif len(key) == 32:
|
||||
o = key
|
||||
elif len(key) == 64:
|
||||
o = decode_hex(key)
|
||||
elif len(key) == 66 and key[:2] == '0x':
|
||||
o = decode_hex(key[2:])
|
||||
else:
|
||||
raise Exception("Invalid key format: %r" % key)
|
||||
if o == b'\x00' * 32:
|
||||
raise Exception("Zero privkey invalid")
|
||||
return o
|
||||
|
||||
|
||||
def zpad(x, l):
|
||||
""" Left zero pad value `x` at least to length `l`.
|
||||
|
||||
>>> zpad('', 1)
|
||||
'\x00'
|
||||
>>> zpad('\xca\xfe', 4)
|
||||
'\x00\x00\xca\xfe'
|
||||
>>> zpad('\xff', 1)
|
||||
'\xff'
|
||||
>>> zpad('\xca\xfe', 2)
|
||||
'\xca\xfe'
|
||||
"""
|
||||
return b'\x00' * max(0, l - len(x)) + x
|
||||
|
||||
|
||||
def rzpad(value, total_length):
|
||||
""" Right zero pad value `x` at least to length `l`.
|
||||
|
||||
>>> zpad('', 1)
|
||||
'\x00'
|
||||
>>> zpad('\xca\xfe', 4)
|
||||
'\xca\xfe\x00\x00'
|
||||
>>> zpad('\xff', 1)
|
||||
'\xff'
|
||||
>>> zpad('\xca\xfe', 2)
|
||||
'\xca\xfe'
|
||||
"""
|
||||
return value + b'\x00' * max(0, total_length - len(value))
|
||||
|
||||
|
||||
def int_to_addr(x):
|
||||
o = [b''] * 20
|
||||
for i in range(20):
|
||||
o[19 - i] = ascii_chr(x & 0xff)
|
||||
x >>= 8
|
||||
return b''.join(o)
|
||||
|
||||
|
||||
def coerce_addr_to_bin(x):
|
||||
if is_numeric(x):
|
||||
return encode_hex(zpad(big_endian_int.serialize(x), 20))
|
||||
elif len(x) == 40 or len(x) == 0:
|
||||
return decode_hex(x)
|
||||
else:
|
||||
return zpad(x, 20)[-20:]
|
||||
|
||||
|
||||
def coerce_addr_to_hex(x):
|
||||
if is_numeric(x):
|
||||
return encode_hex(zpad(big_endian_int.serialize(x), 20))
|
||||
elif len(x) == 40 or len(x) == 0:
|
||||
return x
|
||||
else:
|
||||
return encode_hex(zpad(x, 20)[-20:])
|
||||
|
||||
|
||||
def coerce_to_int(x):
|
||||
if is_numeric(x):
|
||||
return x
|
||||
elif len(x) == 40:
|
||||
return big_endian_to_int(decode_hex(x))
|
||||
else:
|
||||
return big_endian_to_int(x)
|
||||
|
||||
|
||||
def coerce_to_bytes(x):
|
||||
if is_numeric(x):
|
||||
return big_endian_int.serialize(x)
|
||||
elif len(x) == 40:
|
||||
return decode_hex(x)
|
||||
else:
|
||||
return x
|
||||
|
||||
|
||||
def parse_int_or_hex(s):
|
||||
if is_numeric(s):
|
||||
return s
|
||||
elif s[:2] in (b'0x', '0x'):
|
||||
s = to_string(s)
|
||||
tail = (b'0' if len(s) % 2 else b'') + s[2:]
|
||||
return big_endian_to_int(decode_hex(tail))
|
||||
else:
|
||||
return int(s)
|
||||
|
||||
|
||||
def ceil32(x):
|
||||
return x if x % 32 == 0 else x + 32 - (x % 32)
|
||||
|
||||
|
||||
def to_signed(i):
|
||||
return i if i < TT255 else i - TT256
|
||||
|
||||
|
||||
def sha3rlp(x):
|
||||
return sha3(rlp.encode(x))
|
||||
|
||||
|
||||
# Format encoders/decoders for bin, addr, int
|
||||
|
||||
|
||||
def decode_bin(v):
|
||||
"""decodes a bytearray from serialization"""
|
||||
if not is_string(v):
|
||||
raise Exception("Value must be binary, not RLP array")
|
||||
return v
|
||||
|
||||
|
||||
def decode_addr(v):
|
||||
"""decodes an address from serialization"""
|
||||
if len(v) not in [0, 20]:
|
||||
raise Exception("Serialized addresses must be empty or 20 bytes long!")
|
||||
return encode_hex(v)
|
||||
|
||||
|
||||
def decode_int(v):
|
||||
"""decodes and integer from serialization"""
|
||||
if len(v) > 0 and (v[0] == b'\x00' or v[0] == 0):
|
||||
raise Exception("No leading zero bytes allowed for integers")
|
||||
return big_endian_to_int(v)
|
||||
|
||||
|
||||
def decode_int256(v):
|
||||
return big_endian_to_int(v)
|
||||
|
||||
|
||||
def encode_bin(v):
|
||||
"""encodes a bytearray into serialization"""
|
||||
return v
|
||||
|
||||
|
||||
def encode_root(v):
|
||||
"""encodes a trie root into serialization"""
|
||||
return v
|
||||
|
||||
|
||||
def encode_int(v):
|
||||
"""encodes an integer into serialization"""
|
||||
if not is_numeric(v) or v < 0 or v >= TT256:
|
||||
raise Exception("Integer invalid or out of range: %r" % v)
|
||||
return int_to_big_endian(v)
|
||||
|
||||
|
||||
def encode_int256(v):
|
||||
return zpad(int_to_big_endian(v), 256)
|
||||
|
||||
|
||||
def scan_bin(v):
|
||||
if v[:2] in ('0x', b'0x'):
|
||||
return decode_hex(v[2:])
|
||||
else:
|
||||
return decode_hex(v)
|
||||
|
||||
|
||||
def scan_int(v):
|
||||
if v[:2] in ('0x', b'0x'):
|
||||
return big_endian_to_int(decode_hex(v[2:]))
|
||||
else:
|
||||
return int(v)
|
||||
|
||||
|
||||
def int_to_hex(x):
|
||||
o = encode_hex(encode_int(x))
|
||||
return '0x' + (o[1:] if (len(o) > 0 and o[0] == b'0') else o)
|
||||
|
||||
|
||||
def remove_0x_head(s):
|
||||
return s[2:] if s[:2] in (b'0x', '0x') else s
|
||||
|
||||
|
||||
def parse_as_bin(s):
|
||||
return decode_hex(s[2:] if s[:2] == '0x' else s)
|
||||
|
||||
|
||||
def parse_as_int(s):
|
||||
return s if is_numeric(s) else int(
|
||||
'0' + s[2:], 16) if s[:2] == '0x' else int(s)
|
||||
|
||||
|
||||
def dump_state(trie):
|
||||
res = ''
|
||||
for k, v in list(trie.to_dict().items()):
|
||||
res += '%r:%r\n' % (encode_hex(k), encode_hex(v))
|
||||
return res
|
||||
|
||||
|
||||
class Denoms():
|
||||
|
||||
def __init__(self):
|
||||
self.wei = 1
|
||||
self.babbage = 10 ** 3
|
||||
self.ada = 10 ** 3
|
||||
self.kwei = 10 ** 6
|
||||
self.lovelace = 10 ** 6
|
||||
self.mwei = 10 ** 6
|
||||
self.shannon = 10 ** 9
|
||||
self.gwei = 10 ** 9
|
||||
self.szabo = 10 ** 12
|
||||
self.finney = 10 ** 15
|
||||
self.mether = 10 ** 15
|
||||
self.ether = 10 ** 18
|
||||
self.turing = 2 ** 256 - 1
|
||||
|
||||
|
||||
denoms = Denoms()
|
||||
|
||||
|
||||
address = Binary.fixed_length(20, allow_empty=True)
|
||||
int20 = BigEndianInt(20)
|
||||
int32 = BigEndianInt(32)
|
||||
int256 = BigEndianInt(256)
|
||||
hash32 = Binary.fixed_length(32)
|
||||
trie_root = Binary.fixed_length(32, allow_empty=True)
|
||||
|
|
@ -90,7 +90,10 @@ class EthereumMMGenTX(MMGenTX):
|
|||
# hex data if signed, json if unsigned: see create_raw()
|
||||
def check_txfile_hex_data(self):
|
||||
if self.check_sigs():
|
||||
from ethereum.transactions import Transaction
|
||||
|
||||
try: from ethereum.transactions import Transaction
|
||||
except: from mmgen.altcoins.eth.pyethereum.transactions import Transaction
|
||||
|
||||
import rlp
|
||||
etx = rlp.decode(bytes.fromhex(self.hex),Transaction)
|
||||
d = etx.to_dict() # ==> hex values have '0x' prefix, 0 is '0x'
|
||||
|
|
@ -284,7 +287,9 @@ class EthereumMMGenTX(MMGenTX):
|
|||
'nonce': d['nonce'],
|
||||
'data': bytes.fromhex(d['data'])}
|
||||
|
||||
from ethereum.transactions import Transaction
|
||||
try: from ethereum.transactions import Transaction
|
||||
except: from mmgen.altcoins.eth.pyethereum.transactions import Transaction
|
||||
|
||||
etx = Transaction(**d_in).sign(wif,d['chainId'])
|
||||
assert etx.sender.hex() == d['from'],(
|
||||
'Sender address recovered from signature does not match true sender')
|
||||
|
|
|
|||
|
|
@ -79,8 +79,12 @@ class g(object):
|
|||
testnet = False
|
||||
regtest = False
|
||||
accept_defaults = False
|
||||
use_internal_keccak_module = False
|
||||
|
||||
chain = None # set by first call to rpc_init()
|
||||
chains = 'mainnet','testnet','regtest'
|
||||
|
||||
# rpc:
|
||||
rpc_host = ''
|
||||
rpc_port = 0
|
||||
rpc_user = ''
|
||||
|
|
@ -88,6 +92,7 @@ class g(object):
|
|||
rpc_fail_on_command = ''
|
||||
rpch = None # global RPC handle
|
||||
|
||||
# regtest:
|
||||
bob = False
|
||||
alice = False
|
||||
|
||||
|
|
@ -117,12 +122,20 @@ class g(object):
|
|||
data_dir_root,data_dir,cfg_file = None,None,None
|
||||
daemon_data_dir = '' # set by user or protocol
|
||||
|
||||
# User opt sets global var:
|
||||
# global var sets user opt:
|
||||
global_sets_opt = ( 'minconf','seed_len','hash_preset','usr_randchars','debug',
|
||||
'quiet','tx_confs','tx_fee_adj','key_generator' )
|
||||
|
||||
# user opt sets global var:
|
||||
opt_sets_global = ( 'use_internal_keccak_module', )
|
||||
|
||||
# 'long' opts - opt sets global var
|
||||
common_opts = (
|
||||
'color','no_license','rpc_host','rpc_port','testnet','rpc_user','rpc_password',
|
||||
'daemon_data_dir','force_256_color','regtest','coin','bob','alice',
|
||||
'accept_defaults','token'
|
||||
)
|
||||
# opts initialized to None by opts.init() if not set by user
|
||||
required_opts = (
|
||||
'quiet','verbose','debug','outdir','echo_passphrase','passwd_file','stdout',
|
||||
'show_hash_presets','label','keep_passphrase','keep_hash_preset','yes',
|
||||
|
|
@ -179,10 +192,6 @@ class g(object):
|
|||
max_tx_file_size = 100000
|
||||
max_input_size = 1024 * 1024
|
||||
|
||||
# Global var sets user opt:
|
||||
global_sets_opt = ['minconf','seed_len','hash_preset','usr_randchars','debug',
|
||||
'quiet','tx_confs','tx_fee_adj','key_generator']
|
||||
|
||||
passwd_max_tries = 5
|
||||
|
||||
max_urandchars = 80
|
||||
|
|
|
|||
310
mmgen/keccak.py
Normal file
310
mmgen/keccak.py
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Python 2 code from here: https://github.com/ctz/keccak
|
||||
# Ported to python3 by the MMGen Project
|
||||
#
|
||||
# This is the old, pre-SHA3 version of Keccak used by Ethereum, which is not supported
|
||||
# by hashlib.sha3
|
||||
|
||||
from math import log
|
||||
from operator import xor
|
||||
from copy import deepcopy
|
||||
from functools import reduce
|
||||
|
||||
# The Keccak-f round constants.
|
||||
RoundConstants = [
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
|
||||
0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
|
||||
0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
|
||||
0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
|
||||
0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
|
||||
]
|
||||
|
||||
RotationConstants = [
|
||||
[ 0, 1, 62, 28, 27, ],
|
||||
[ 36, 44, 6, 55, 20, ],
|
||||
[ 3, 10, 43, 25, 39, ],
|
||||
[ 41, 45, 15, 21, 8, ],
|
||||
[ 18, 2, 61, 56, 14, ]
|
||||
]
|
||||
|
||||
Masks = [(1 << i) - 1 for i in range(65)]
|
||||
|
||||
def bits2bytes(x):
|
||||
return (int(x) + 7) // 8
|
||||
|
||||
def rol(value, left, bits):
|
||||
"""
|
||||
Circularly rotate 'value' to the left,
|
||||
treating it as a quantity of the given size in bits.
|
||||
"""
|
||||
top = value >> (bits - left)
|
||||
bot = (value & Masks[bits - left]) << left
|
||||
return bot | top
|
||||
|
||||
def ror(value, right, bits):
|
||||
"""
|
||||
Circularly rotate 'value' to the right,
|
||||
treating it as a quantity of the given size in bits.
|
||||
"""
|
||||
top = value >> right
|
||||
bot = (value & Masks[right]) << (bits - right)
|
||||
return bot | top
|
||||
|
||||
def multirate_padding(used_bytes, align_bytes):
|
||||
"""
|
||||
The Keccak padding function.
|
||||
"""
|
||||
padlen = align_bytes - used_bytes
|
||||
if padlen == 0:
|
||||
padlen = align_bytes
|
||||
# note: padding done in 'internal bit ordering', wherein LSB is leftmost
|
||||
if padlen == 1:
|
||||
return [0x81]
|
||||
else:
|
||||
return [0x01] + ([0x00] * (padlen - 2)) + [0x80]
|
||||
|
||||
def keccak_f(state):
|
||||
"""
|
||||
This is Keccak-f permutation. It operates on and
|
||||
mutates the passed-in KeccakState. It returns nothing.
|
||||
"""
|
||||
def round(A, RC):
|
||||
W, H = state.W, state.H
|
||||
rangeW, rangeH = state.rangeW, state.rangeH
|
||||
lanew = state.lanew
|
||||
zero = state.zero
|
||||
|
||||
# theta
|
||||
C = [reduce(xor, A[x]) for x in rangeW]
|
||||
D = [0] * W
|
||||
for x in rangeW:
|
||||
D[x] = C[(x - 1) % W] ^ rol(C[(x + 1) % W], 1, lanew)
|
||||
for y in rangeH:
|
||||
A[x][y] ^= D[x]
|
||||
|
||||
# rho and pi
|
||||
B = zero()
|
||||
for x in rangeW:
|
||||
for y in rangeH:
|
||||
B[y % W][(2 * x + 3 * y) % H] = rol(A[x][y], RotationConstants[y][x], lanew)
|
||||
|
||||
# chi
|
||||
for x in rangeW:
|
||||
for y in rangeH:
|
||||
A[x][y] = B[x][y] ^ ((~ B[(x + 1) % W][y]) & B[(x + 2) % W][y])
|
||||
|
||||
# iota
|
||||
A[0][0] ^= RC
|
||||
|
||||
l = int(log(state.lanew, 2))
|
||||
nr = 12 + 2 * l
|
||||
|
||||
for ir in range(nr):
|
||||
round(state.s, RoundConstants[ir])
|
||||
|
||||
class KeccakState(object):
|
||||
"""
|
||||
A keccak state container.
|
||||
|
||||
The state is stored as a 5x5 table of integers.
|
||||
"""
|
||||
W = 5
|
||||
H = 5
|
||||
|
||||
rangeW = range(W)
|
||||
rangeH = range(H)
|
||||
|
||||
@staticmethod
|
||||
def zero():
|
||||
"""
|
||||
Returns an zero state table.
|
||||
"""
|
||||
return [[0] * KeccakState.W for x in KeccakState.rangeH]
|
||||
|
||||
@staticmethod
|
||||
def format(st):
|
||||
"""
|
||||
Formats the given state as hex, in natural byte order.
|
||||
"""
|
||||
rows = []
|
||||
def fmt(x): return '%016x' % x
|
||||
for y in KeccakState.rangeH:
|
||||
row = []
|
||||
for x in rangeW:
|
||||
row.append(fmt(st[x][y]))
|
||||
rows.append(' '.join(row))
|
||||
return '\n'.join(rows)
|
||||
|
||||
@staticmethod
|
||||
def lane2bytes(s, w):
|
||||
"""
|
||||
Converts the lane s to a sequence of byte values,
|
||||
assuming a lane is w bits.
|
||||
"""
|
||||
return [(s >> b) & 0xff for b in range(0, w, 8)]
|
||||
|
||||
@staticmethod
|
||||
def bytes2lane(bb):
|
||||
"""
|
||||
Converts a sequence of byte values to a lane.
|
||||
"""
|
||||
r = 0
|
||||
for b in reversed(bb):
|
||||
r = r << 8 | b
|
||||
return r
|
||||
|
||||
def __init__(self, bitrate, b):
|
||||
self.bitrate = bitrate
|
||||
self.b = b
|
||||
|
||||
# only byte-aligned
|
||||
assert self.bitrate % 8 == 0
|
||||
self.bitrate_bytes = bits2bytes(self.bitrate)
|
||||
|
||||
assert self.b % 25 == 0
|
||||
self.lanew = self.b // 25
|
||||
|
||||
self.s = KeccakState.zero()
|
||||
|
||||
def __str__(self):
|
||||
return KeccakState.format(self.s)
|
||||
|
||||
def absorb(self, bb):
|
||||
"""
|
||||
Mixes in the given bitrate-length string to the state.
|
||||
"""
|
||||
assert len(bb) == self.bitrate_bytes
|
||||
|
||||
bb += [0] * bits2bytes(self.b - self.bitrate)
|
||||
i = 0
|
||||
|
||||
for y in self.rangeH:
|
||||
for x in self.rangeW:
|
||||
self.s[x][y] ^= KeccakState.bytes2lane(bb[i:i + 8])
|
||||
i += 8
|
||||
|
||||
def squeeze(self):
|
||||
"""
|
||||
Returns the bitrate-length prefix of the state to be output.
|
||||
"""
|
||||
return self.get_bytes()[:self.bitrate_bytes]
|
||||
|
||||
def get_bytes(self):
|
||||
"""
|
||||
Convert whole state to a byte string.
|
||||
"""
|
||||
out = [0] * bits2bytes(self.b)
|
||||
i = 0
|
||||
for y in self.rangeH:
|
||||
for x in self.rangeW:
|
||||
v = KeccakState.lane2bytes(self.s[x][y], self.lanew)
|
||||
out[i:i+8] = v
|
||||
i += 8
|
||||
return out
|
||||
|
||||
def set_bytes(self, bb):
|
||||
"""
|
||||
Set whole state from byte string, which is assumed
|
||||
to be the correct length.
|
||||
"""
|
||||
i = 0
|
||||
for y in self.rangeH:
|
||||
for x in self.rangeW:
|
||||
self.s[x][y] = KeccakState.bytes2lane(bb[i:i+8])
|
||||
i += 8
|
||||
|
||||
class KeccakSponge(object):
|
||||
def __init__(self, bitrate, width, padfn, permfn):
|
||||
self.state = KeccakState(bitrate, width)
|
||||
self.padfn = padfn
|
||||
self.permfn = permfn
|
||||
self.buffer = []
|
||||
|
||||
def copy(self):
|
||||
return deepcopy(self)
|
||||
|
||||
def absorb_block(self, bb):
|
||||
assert len(bb) == self.state.bitrate_bytes
|
||||
self.state.absorb(bb)
|
||||
self.permfn(self.state)
|
||||
|
||||
def absorb(self, s):
|
||||
self.buffer += bytes(s)
|
||||
|
||||
while len(self.buffer) >= self.state.bitrate_bytes:
|
||||
self.absorb_block(self.buffer[:self.state.bitrate_bytes])
|
||||
self.buffer = self.buffer[self.state.bitrate_bytes:]
|
||||
|
||||
def absorb_final(self):
|
||||
padded = self.buffer + self.padfn(len(self.buffer), self.state.bitrate_bytes)
|
||||
self.absorb_block(padded)
|
||||
self.buffer = []
|
||||
|
||||
def squeeze_once(self):
|
||||
rc = self.state.squeeze()
|
||||
self.permfn(self.state)
|
||||
return rc
|
||||
|
||||
def squeeze(self, l):
|
||||
Z = self.squeeze_once()
|
||||
while len(Z) < l:
|
||||
Z += self.squeeze_once()
|
||||
return Z[:l]
|
||||
|
||||
class KeccakHash(object):
|
||||
"""
|
||||
The Keccak hash function, with a hashlib-compatible interface.
|
||||
"""
|
||||
def __init__(self, bitrate_bits, capacity_bits, output_bits):
|
||||
# our in-absorption sponge. this is never given padding
|
||||
assert bitrate_bits + capacity_bits in (25, 50, 100, 200, 400, 800, 1600)
|
||||
self.sponge = KeccakSponge(bitrate_bits, bitrate_bits + capacity_bits,
|
||||
multirate_padding,
|
||||
keccak_f)
|
||||
|
||||
# hashlib interface members
|
||||
assert output_bits % 8 == 0
|
||||
self.digest_size = bits2bytes(output_bits)
|
||||
self.block_size = bits2bytes(bitrate_bits)
|
||||
|
||||
def __repr__(self):
|
||||
inf = (self.sponge.state.bitrate,
|
||||
self.sponge.state.b - self.sponge.state.bitrate,
|
||||
self.digest_size * 8)
|
||||
return '<KeccakHash with r=%d, c=%d, image=%d>' % inf
|
||||
|
||||
def copy(self):
|
||||
return deepcopy(self)
|
||||
|
||||
def update(self, s):
|
||||
self.sponge.absorb(s)
|
||||
|
||||
def digest(self):
|
||||
finalised = self.sponge.copy()
|
||||
finalised.absorb_final()
|
||||
digest = finalised.squeeze(self.digest_size)
|
||||
return bytes(digest)
|
||||
|
||||
def hexdigest(self):
|
||||
return self.digest().hex()
|
||||
|
||||
@staticmethod
|
||||
def preset(bitrate_bits, capacity_bits, output_bits):
|
||||
"""
|
||||
Returns a factory function for the given bitrate, sponge capacity and output length.
|
||||
The function accepts an optional initial input, ala hashlib.
|
||||
"""
|
||||
def create(initial_input = None):
|
||||
h = KeccakHash(bitrate_bits, capacity_bits, output_bits)
|
||||
if initial_input is not None:
|
||||
h.update(initial_input)
|
||||
return h
|
||||
return create
|
||||
|
||||
# SHA3 parameter presets
|
||||
keccak_224 = KeccakHash.preset(1152, 448, 224)
|
||||
keccak_256 = KeccakHash.preset(1088, 512, 256)
|
||||
keccak_384 = KeccakHash.preset(832, 768, 384)
|
||||
keccak_512 = KeccakHash.preset(576, 1024, 512)
|
||||
|
|
@ -35,7 +35,7 @@ if sys.argv[0].split('-')[-1] == 'keygen':
|
|||
else:
|
||||
gen_what = 'addresses'
|
||||
gen_desc = 'addresses'
|
||||
opt_filter = 'hbcdeEiHOKlpzPqrStv-'
|
||||
opt_filter = 'hbcdeEiHOkKlpzPqrStv-'
|
||||
note_addrkey = ''
|
||||
note_secp256k1 = """
|
||||
If available, the secp256k1 library will be used for address generation.
|
||||
|
|
@ -59,6 +59,7 @@ opts_data = lambda: {
|
|||
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
|
||||
'f' at offset 'o' (comma-separated)
|
||||
-O, --old-incog-fmt Specify old-format incognito input
|
||||
-k, --use-internal-keccak-module Force use of the internal keccak module
|
||||
-K, --key-generator=m Use method 'm' for public key generation
|
||||
Options: {kgs} (default: {kg})
|
||||
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ opts_data = lambda: {
|
|||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-h, --help Print this help message
|
||||
--, --longhelp Print help message for long options (common options)
|
||||
-k, --use-internal-keccak-module Force use of the internal keccak module
|
||||
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ opts_data = lambda: {
|
|||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-D, --tx-id Display transaction ID and exit
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-E, --use-internal-keccak-module Force use of the internal keccak module
|
||||
-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)
|
||||
|
|
|
|||
|
|
@ -228,8 +228,9 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False):
|
|||
usage_txt = opts_data['usage']
|
||||
|
||||
# Transfer uopts into opt, setting program's opts + required opts to None if not set by user
|
||||
for o in tuple([s.rstrip('=') for s in long_opts] + add_opts + skipped_opts) + \
|
||||
g.required_opts + g.common_opts:
|
||||
for o in ( tuple([s.rstrip('=') for s in long_opts] + add_opts + skipped_opts)
|
||||
+ g.required_opts
|
||||
+ g.common_opts ):
|
||||
setattr(opt,o,uopts[o] if o in uopts else None)
|
||||
|
||||
if opt.version: Die(0,"""
|
||||
|
|
@ -255,9 +256,11 @@ def init(opts_f,add_opts=[],opt_filter=None,parse_only=False):
|
|||
override_from_env()
|
||||
|
||||
# User opt sets global var - do these here, before opt is set from g.global_sets_opt
|
||||
for k in g.common_opts:
|
||||
val = getattr(opt,k)
|
||||
if val != None: setattr(g,k,set_for_type(val,getattr(g,k),'--'+k))
|
||||
for k in (g.common_opts + g.opt_sets_global):
|
||||
if hasattr(opt,k):
|
||||
val = getattr(opt,k)
|
||||
if val != None:
|
||||
setattr(g,k,set_for_type(val,getattr(g,k),'--'+k))
|
||||
|
||||
if g.regtest: g.testnet = True # These are equivalent for now
|
||||
|
||||
|
|
|
|||
|
|
@ -415,8 +415,14 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen):
|
|||
assert len(addr) == cls.addr_width,'Incorrect width'
|
||||
|
||||
ret = b58dec(addr)
|
||||
import sha3
|
||||
chk = sha3.keccak_256(bytes.fromhex(ret)[:-4]).hexdigest()[:8]
|
||||
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
from sha3 import keccak_256
|
||||
except:
|
||||
from mmgen.keccak import keccak_256
|
||||
|
||||
chk = keccak_256(bytes.fromhex(ret)[:-4]).hexdigest()[:8]
|
||||
assert chk == ret[-8:],'{}: incorrect checksum. Correct value: {}'.format(ret[-8:],chk)
|
||||
|
||||
return { 'hex': ret, 'format': 'monero' } if return_dict else True
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ scrambletest_py='test/scrambletest.py'
|
|||
mmgen_tool='cmds/mmgen-tool'
|
||||
mmgen_keygen='cmds/mmgen-keygen'
|
||||
python='python3'
|
||||
rounds=100 rounds_mid=250 rounds_max=500
|
||||
rounds=100 rounds_min=20 rounds_mid=250 rounds_max=500
|
||||
monero_addrs='3,99,2,22-24,101-104'
|
||||
|
||||
dfl_tests='obj unit sha2 alts monero eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_tn ltc_rt tool tool2 gen'
|
||||
dfl_tests='obj unit hash alts monero eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_tn ltc_rt tool tool2 gen'
|
||||
add_tests='autosign_minimal autosign_live'
|
||||
|
||||
PROGNAME=$(basename $0)
|
||||
|
|
@ -47,7 +47,7 @@ do
|
|||
echo " AVAILABLE TESTS:"
|
||||
echo " obj - data objects"
|
||||
echo " unit - unit tests"
|
||||
echo " sha2 - MMGen sha2 implementation"
|
||||
echo " hash - internal hash function implementations"
|
||||
echo " alts - operations for all supported gen-only altcoins"
|
||||
echo " monero - operations for Monero"
|
||||
echo " eth - operations for Ethereum"
|
||||
|
|
@ -78,7 +78,7 @@ do
|
|||
gentest_py="$python $gentest_py"
|
||||
mmgen_tool="$python $mmgen_tool"
|
||||
mmgen_keygen="$python $mmgen_keygen" ;&
|
||||
f) rounds=10 rounds_mid=25 rounds_max=50 monero_addrs='3,23' ;;
|
||||
f) rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 monero_addrs='3,23' ;;
|
||||
i) INSTALL=1 ;;
|
||||
I) INSTALL_ONLY=1 ;;
|
||||
l) echo -e "Default tests:\n $dfl_tests"
|
||||
|
|
@ -169,12 +169,13 @@ s_unit='Running unit'
|
|||
t_unit=("$unit_tests_py")
|
||||
f_unit='Unit tests run complete'
|
||||
|
||||
i_sha2='MMGen SHA2 implementation'
|
||||
s_sha2='Testing SHA2 implementation'
|
||||
t_sha2=(
|
||||
"$python test/sha2test.py sha256 $rounds_max"
|
||||
"$python test/sha2test.py sha512 $rounds_max")
|
||||
f_sha2='SHA2 test complete'
|
||||
i_hash='Internal hash function implementations'
|
||||
s_hash='Testing internal hash function implementations'
|
||||
t_hash=(
|
||||
"$python test/hashfunc.py sha256 $rounds_max"
|
||||
"$python test/hashfunc.py sha512 $rounds_max"
|
||||
"$python test/hashfunc.py keccak $rounds_max")
|
||||
f_hash='Hash function tests complete'
|
||||
|
||||
i_alts='Gen-only altcoin'
|
||||
s_alts='The following tests will test generation operations for all supported altcoins'
|
||||
|
|
@ -190,7 +191,11 @@ t_alts=(
|
|||
"$gentest_py --coin=ltc --type=segwit 2 $rounds"
|
||||
"$gentest_py --coin=ltc --type=bech32 2 $rounds"
|
||||
"$gentest_py --coin=etc 2 $rounds"
|
||||
"$gentest_py --coin=etc --use-internal-keccak-module 2 $rounds_min"
|
||||
"$gentest_py --coin=eth 2 $rounds"
|
||||
"$gentest_py --coin=eth --use-internal-keccak-module 2 $rounds_min"
|
||||
"$gentest_py --coin=xmr 2 $rounds"
|
||||
"$gentest_py --coin=xmr --use-internal-keccak-module 2 $rounds_min"
|
||||
"$gentest_py --coin=zec 2 $rounds"
|
||||
"$gentest_py --coin=zec --type=zcash_z 2 $rounds_mid"
|
||||
|
||||
|
|
@ -228,7 +233,7 @@ s_monero='Testing key-address file generation and wallet creation and sync opera
|
|||
s_monero='The monerod (mainnet) daemon must be running for the following tests'
|
||||
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"
|
||||
"$mmgen_keygen -q --accept-defaults --use-internal-keccak-module --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-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)'
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -128,6 +128,7 @@ setup(
|
|||
'mmgen.exception',
|
||||
'mmgen.filename',
|
||||
'mmgen.globalvars',
|
||||
'mmgen.keccak',
|
||||
'mmgen.license',
|
||||
'mmgen.mn_electrum',
|
||||
'mmgen.mn_tirosh',
|
||||
|
|
@ -151,6 +152,9 @@ setup(
|
|||
'mmgen.altcoins.eth.obj',
|
||||
'mmgen.altcoins.eth.tx',
|
||||
'mmgen.altcoins.eth.tw',
|
||||
'mmgen.altcoins.eth.pyethereum.__init__',
|
||||
'mmgen.altcoins.eth.pyethereum.transactions',
|
||||
'mmgen.altcoins.eth.pyethereum.utils',
|
||||
|
||||
'mmgen.main',
|
||||
'mmgen.main_addrgen',
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ opts_data = lambda: {
|
|||
'options': """
|
||||
-h, --help Print this help message
|
||||
-a, --all Test all supported coins for external generator 'ext'
|
||||
-k, --use-internal-keccak-module Force use of the internal keccak module
|
||||
--, --longhelp Print help message for long options (common options)
|
||||
-q, --quiet Produce quieter output
|
||||
-t, --type=t Specify address type (valid options: 'compressed','segwit','zcash_z')
|
||||
|
|
@ -122,7 +123,7 @@ def init_external_prog():
|
|||
addr_type = MMGenAddrType('Z')
|
||||
elif test_support('pyethereum'):
|
||||
try:
|
||||
import ethereum.utils as eth
|
||||
import mmgen.altcoins.eth.pyethereum.utils as eth
|
||||
except:
|
||||
raise ImportError("Unable to import 'ethereum' module. Is pyethereum installed?")
|
||||
ext_sec2addr = pyethereum_sec2addr
|
||||
|
|
|
|||
|
|
@ -16,22 +16,21 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
test/sha2test.py: Test MMGen's SHA256 and SHA512 implementations
|
||||
test/hashfunc.py: Test internal implementations of SHA256, SHA512 and Keccak256
|
||||
"""
|
||||
|
||||
import sys,os
|
||||
from mmgen.sha2 import Sha256,Sha512
|
||||
from mmgen.util import die
|
||||
|
||||
assert len(sys.argv) in (2,3),"Test takes 1 or 2 arguments: test name, plus optional rounds count"
|
||||
test = sys.argv[1].capitalize()
|
||||
assert test in ('Sha256','Sha512'), "Valid choices for test are 'sha256' or 'sha512'"
|
||||
assert test in ('Sha256','Sha512','Keccak'), "Valid choices for test are 'sha256','sha512' or 'keccak'"
|
||||
random_rounds = int(sys.argv[2]) if len(sys.argv) == 3 else 500
|
||||
|
||||
def msg(s): sys.stderr.write(s)
|
||||
def green(s): return '\033[32;1m' + s + '\033[0m'
|
||||
|
||||
class TestSha2(object):
|
||||
class TestHashFunc(object):
|
||||
|
||||
def test_constants(self):
|
||||
msg('Testing generated constants: ')
|
||||
|
|
@ -45,8 +44,7 @@ class TestSha2(object):
|
|||
msg('OK\n')
|
||||
|
||||
def compare_hashes(self,dlen,data):
|
||||
import hashlib
|
||||
sha2_ref = getattr(hashlib,self.desc)(data).hexdigest()
|
||||
sha2_ref = getattr(self.hashlib,self.desc)(data).hexdigest()
|
||||
ret = self.t_cls(data).hexdigest()
|
||||
if ret != sha2_ref:
|
||||
m ='\nHashes do not match!\nReference {d}: {}\nMMGen {d}: {}'
|
||||
|
|
@ -81,9 +79,40 @@ class TestSha2(object):
|
|||
self.compare_hashes(dlen,os.urandom(dlen))
|
||||
msg('OK\n')
|
||||
|
||||
class TestKeccak(TestHashFunc):
|
||||
desc = 'keccak_256'
|
||||
def __init__(self):
|
||||
from mmgen.keccak import keccak_256
|
||||
import sha3
|
||||
self.t_cls = keccak_256
|
||||
self.hashlib = sha3
|
||||
|
||||
def test_constants(self): pass
|
||||
|
||||
class TestSha2(TestHashFunc):
|
||||
|
||||
def __init__(self):
|
||||
from mmgen.sha2 import Sha256,Sha512
|
||||
import hashlib
|
||||
self.t_cls = { 'sha256':Sha256, 'sha512':Sha512 }[self.desc]
|
||||
self.hashlib = hashlib
|
||||
|
||||
class TestSha256(TestSha2):
|
||||
desc = 'sha256'
|
||||
H_ref = (
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 )
|
||||
K_ref = (
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 )
|
||||
|
||||
class TestSha512(TestSha2):
|
||||
desc = 'sha512'
|
||||
t_cls = Sha512
|
||||
H_ref = (
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 )
|
||||
|
|
@ -105,23 +134,8 @@ class TestSha512(TestSha2):
|
|||
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
|
||||
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 )
|
||||
|
||||
class TestSha256(TestSha2):
|
||||
desc = 'sha256'
|
||||
t_cls = Sha256
|
||||
H_ref = (
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 )
|
||||
K_ref = (
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 )
|
||||
|
||||
msg(green('Testing MMGen implementation of {}\n'.format(test)))
|
||||
t = globals()['Test'+test]()
|
||||
msg(green('Testing internal implementation of {}\n'.format(t.desc)))
|
||||
t.test_constants()
|
||||
t.test_ref()
|
||||
t.test_random(random_rounds)
|
||||
Loading…
Add table
Add a link
Reference in a new issue