Reformat code with black

pycryptodomex
Michał Sałaban 2 years ago
parent be11c3591e
commit 102449f57f

@ -15,6 +15,7 @@ class Account(object):
:param index: the account's index within the wallet
:param label: optional account label as `str`
"""
index = None
wallet = None
label = None
@ -23,8 +24,8 @@ class Account(object):
self.index = index
self.label = label
self._backend = backend
self.incoming = PaymentManager(index, backend, 'in')
self.outgoing = PaymentManager(index, backend, 'out')
self.incoming = PaymentManager(index, backend, "in")
self.outgoing = PaymentManager(index, backend, "out")
def balances(self):
"""
@ -69,9 +70,15 @@ class Account(object):
"""
return self._backend.new_address(account=self.index, label=label)
def transfer(self, address, amount,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
def transfer(
self,
address,
amount,
priority=const.PRIO_NORMAL,
payment_id=None,
unlock_time=0,
relay=True,
):
"""
Sends a transfer. Returns a list of resulting transactions.
@ -95,11 +102,17 @@ class Account(object):
payment_id,
unlock_time,
account=self.index,
relay=relay)
def transfer_multiple(self, destinations,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
relay=relay,
)
def transfer_multiple(
self,
destinations,
priority=const.PRIO_NORMAL,
payment_id=None,
unlock_time=0,
relay=True,
):
"""
Sends a batch of transfers. Returns a list of resulting transactions.
@ -124,10 +137,18 @@ class Account(object):
payment_id,
unlock_time,
account=self.index,
relay=relay)
def sweep_all(self, address, priority=const.PRIO_NORMAL, payment_id=None,
subaddr_indices=None, unlock_time=0, relay=True):
relay=relay,
)
def sweep_all(
self,
address,
priority=const.PRIO_NORMAL,
payment_id=None,
subaddr_indices=None,
unlock_time=0,
relay=True,
):
"""
Sends all unlocked balance to an address. Returns a list of resulting transactions.
@ -153,4 +174,5 @@ class Account(object):
subaddr_indices,
unlock_time,
account=self.index,
relay=relay)
relay=relay,
)

@ -10,8 +10,13 @@ from . import const
from . import ed25519
from . import numbers
_ADDR_REGEX = re.compile(r'^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{95}$')
_IADDR_REGEX = re.compile(r'^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{106}$')
_ADDR_REGEX = re.compile(
r"^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{95}$"
)
_IADDR_REGEX = re.compile(
r"^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{106}$"
)
class BaseAddress(object):
label = None
@ -19,8 +24,10 @@ class BaseAddress(object):
def __init__(self, addr, label=None):
addr = addr.decode() if isinstance(addr, bytes) else str(addr)
if not _ADDR_REGEX.match(addr):
raise ValueError("Address must be 95 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=addr, len=len(addr)))
raise ValueError(
"Address must be 95 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=addr, len=len(addr))
)
self._decode(addr)
self.label = label or self.label
@ -48,9 +55,12 @@ class BaseAddress(object):
if checksum != keccak_256(self._decoded[:-4]).digest()[:4]:
raise ValueError("Invalid checksum in address {}".format(address))
if self._decoded[0] not in self._valid_netbytes:
raise ValueError("Invalid address netbyte {nb}. Allowed values are: {allowed}".format(
nb=self._decoded[0],
allowed=", ".join(map(lambda b: '%02x' % b, self._valid_netbytes))))
raise ValueError(
"Invalid address netbyte {nb}. Allowed values are: {allowed}".format(
nb=self._decoded[0],
allowed=", ".join(map(lambda b: "%02x" % b, self._valid_netbytes)),
)
)
def __repr__(self):
return base58.encode(hexlify(self._decoded))
@ -77,6 +87,7 @@ class Address(BaseAddress):
:param address: a Monero address as string-like object
:param label: a label for the address (defaults to `None`)
"""
_valid_netbytes = const.MASTERADDR_NETBYTES
def check_private_view_key(self, key):
@ -104,9 +115,17 @@ class Address(BaseAddress):
"""
payment_id = numbers.PaymentID(payment_id)
if not payment_id.is_short():
raise TypeError("Payment ID {0} has more than 64 bits and cannot be integrated".format(payment_id))
raise TypeError(
"Payment ID {0} has more than 64 bits and cannot be integrated".format(
payment_id
)
)
prefix = const.INTADDRR_NETBYTES[const.NETS.index(self.net)]
data = bytearray([prefix]) + self._decoded[1:65] + struct.pack('>Q', int(payment_id))
data = (
bytearray([prefix])
+ self._decoded[1:65]
+ struct.pack(">Q", int(payment_id))
)
checksum = bytearray(keccak_256(data).digest()[:4])
return IntegratedAddress(base58.encode(hexlify(data + checksum)))
@ -134,8 +153,10 @@ class IntegratedAddress(Address):
def __init__(self, address):
address = address.decode() if isinstance(address, bytes) else str(address)
if not _IADDR_REGEX.match(address):
raise ValueError("Integrated address must be 106 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=address, len=len(address)))
raise ValueError(
"Integrated address must be 106 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=address, len=len(address))
)
self._decode(address)
def payment_id(self):
@ -170,12 +191,20 @@ def address(addr, label=None):
return Address(addr, label=label)
elif netbyte in SubAddress._valid_netbytes:
return SubAddress(addr, label=label)
raise ValueError("Invalid address netbyte {nb:x}. Allowed values are: {allowed}".format(
nb=netbyte,
allowed=", ".join(map(
lambda b: '%02x' % b,
sorted(Address._valid_netbytes + SubAddress._valid_netbytes)))))
raise ValueError(
"Invalid address netbyte {nb:x}. Allowed values are: {allowed}".format(
nb=netbyte,
allowed=", ".join(
map(
lambda b: "%02x" % b,
sorted(Address._valid_netbytes + SubAddress._valid_netbytes),
)
),
)
)
elif _IADDR_REGEX.match(addr):
return IntegratedAddress(addr)
raise ValueError("Address must be either 95 or 106 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=addr, len=len(addr)))
raise ValueError(
"Address must be either 95 or 106 characters long base58-encoded string, "
"is {addr} ({len} chars length)".format(addr=addr, len=len(addr))
)

File diff suppressed because it is too large Load Diff

@ -1,5 +1,6 @@
from ... import exceptions
class RPCError(exceptions.BackendException):
pass
@ -11,5 +12,6 @@ class Unauthorized(RPCError):
class MethodNotFound(RPCError):
pass
class RestrictedRPC(RPCError):
pass
pass

@ -16,6 +16,7 @@ from .exceptions import RPCError, Unauthorized, MethodNotFound
_log = logging.getLogger(__name__)
class JSONRPCWallet(object):
"""
JSON RPC backend for Monero wallet (``monero-wallet-rpc``)
@ -30,122 +31,147 @@ class JSONRPCWallet(object):
:param verify_ssl_certs: verify ssl certs for request
:param proxy_url: a proxy to use
"""
_master_address = None
def __init__(self, protocol='http', host='127.0.0.1', port=18088, path='/json_rpc',
user='', password='', timeout=30, verify_ssl_certs=True, proxy_url=None):
self.url = '{protocol}://{host}:{port}/json_rpc'.format(
protocol=protocol,
host=host,
port=port)
def __init__(
self,
protocol="http",
host="127.0.0.1",
port=18088,
path="/json_rpc",
user="",
password="",
timeout=30,
verify_ssl_certs=True,
proxy_url=None,
):
self.url = "{protocol}://{host}:{port}/json_rpc".format(
protocol=protocol, host=host, port=port
)
_log.debug("JSONRPC wallet backend URL: {url}".format(url=self.url))
self.user = user
self.password = password
self.timeout = timeout
self.verify_ssl_certs = verify_ssl_certs
self.proxies = {protocol: proxy_url}
_log.debug("JSONRPC wallet backend auth: '{user}'/'{stars}'".format(
user=user, stars=('*' * len(password)) if password else ''))
_log.debug(
"JSONRPC wallet backend auth: '{user}'/'{stars}'".format(
user=user, stars=("*" * len(password)) if password else ""
)
)
def height(self):
return self.raw_request('getheight')['height']
return self.raw_request("getheight")["height"]
def spend_key(self):
return self.raw_request('query_key', {'key_type': 'spend_key'})['key']
return self.raw_request("query_key", {"key_type": "spend_key"})["key"]
def view_key(self):
return self.raw_request('query_key', {'key_type': 'view_key'})['key']
return self.raw_request("query_key", {"key_type": "view_key"})["key"]
def seed(self):
return Seed(self.raw_request('query_key', {'key_type': 'mnemonic'})['key'])
return Seed(self.raw_request("query_key", {"key_type": "mnemonic"})["key"])
def accounts(self):
accounts = []
_accounts = self.raw_request('get_accounts')
_accounts = self.raw_request("get_accounts")
idx = 0
self._master_address = Address(_accounts['subaddress_accounts'][0]['base_address'])
for _acc in _accounts['subaddress_accounts']:
assert idx == _acc['account_index']
accounts.append(Account(self, _acc['account_index'], label=_acc.get('label')))
self._master_address = Address(
_accounts["subaddress_accounts"][0]["base_address"]
)
for _acc in _accounts["subaddress_accounts"]:
assert idx == _acc["account_index"]
accounts.append(
Account(self, _acc["account_index"], label=_acc.get("label"))
)
idx += 1
return accounts
def new_account(self, label=None):
_account = self.raw_request('create_account', {'label': label})
_account = self.raw_request("create_account", {"label": label})
# NOTE: the following should re-read label by _account.get('label') but the RPC
# doesn't return that detail here
return Account(self, _account['account_index'], label=label), SubAddress(_account['address'])
return Account(self, _account["account_index"], label=label), SubAddress(
_account["address"]
)
def addresses(self, account=0, addr_indices=None):
qdata = {'account_index': account}
qdata = {"account_index": account}
if addr_indices:
qdata['address_index'] = addr_indices
_addresses = self.raw_request('getaddress', qdata)
addresses = [None] * (max(map(operator.itemgetter('address_index'), _addresses['addresses'])) + 1)
for _addr in _addresses['addresses']:
addresses[_addr['address_index']] = address(
_addr['address'],
label=_addr.get('label', None))
qdata["address_index"] = addr_indices
_addresses = self.raw_request("getaddress", qdata)
addresses = [None] * (
max(map(operator.itemgetter("address_index"), _addresses["addresses"])) + 1
)
for _addr in _addresses["addresses"]:
addresses[_addr["address_index"]] = address(
_addr["address"], label=_addr.get("label", None)
)
return addresses
def new_address(self, account=0, label=None):
_address = self.raw_request(
'create_address', {'account_index': account, 'label': label})
return SubAddress(_address['address']), _address['address_index']
"create_address", {"account_index": account, "label": label}
)
return SubAddress(_address["address"]), _address["address_index"]
def balances(self, account=0):
_balance = self.raw_request('getbalance', {'account_index': account})
return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance']))
_balance = self.raw_request("getbalance", {"account_index": account})
return (
from_atomic(_balance["balance"]),
from_atomic(_balance["unlocked_balance"]),
)
def transfers_in(self, account, pmtfilter):
params = {'account_index': account, 'pending': False}
method = 'get_transfers'
params = {"account_index": account, "pending": False}
method = "get_transfers"
if pmtfilter.tx_ids:
method = 'get_transfer_by_txid'
method = "get_transfer_by_txid"
if pmtfilter.unconfirmed:
params['in'] = pmtfilter.confirmed
params['out'] = False
params['pool'] = True
params["in"] = pmtfilter.confirmed
params["out"] = False
params["pool"] = True
else:
if pmtfilter.payment_ids:
method = 'get_bulk_payments'
params['payment_ids'] = list(map(str, pmtfilter.payment_ids))
method = "get_bulk_payments"
params["payment_ids"] = list(map(str, pmtfilter.payment_ids))
else:
params['in'] = pmtfilter.confirmed
params['out'] = False
params['pool'] = False
if method == 'get_transfers':
params["in"] = pmtfilter.confirmed
params["out"] = False
params["pool"] = False
if method == "get_transfers":
if pmtfilter.min_height:
# NOTE: the API uses (min, max] range which is confusing
params['min_height'] = pmtfilter.min_height - 1
params['filter_by_height'] = True
params["min_height"] = pmtfilter.min_height - 1
params["filter_by_height"] = True
if pmtfilter.max_height:
params['max_height'] = pmtfilter.max_height
params['filter_by_height'] = True
params["max_height"] = pmtfilter.max_height
params["filter_by_height"] = True
_pmts = self.raw_request(method, params)
pmts = _pmts.get('in', [])
elif method == 'get_transfer_by_txid':
pmts = _pmts.get("in", [])
elif method == "get_transfer_by_txid":
pmts = []
for txid in pmtfilter.tx_ids:
params['txid'] = txid
params["txid"] = txid
try:
_pmts = self.raw_request(method, params, squelch_error_logging=True)
except exceptions.TransactionNotFound:
continue
pmts.extend(_pmts['transfers'])
pmts.extend(_pmts["transfers"])
# Issue #71: incoming payments to self will have excess 'destinations' key. Remove.
for pmt in pmts:
try:
del pmt['destinations']
del pmt["destinations"]
except KeyError:
pass
else:
# NOTE: the API uses (min, max] range which is confusing
params['min_block_height'] = (pmtfilter.min_height or 1) - 1
params["min_block_height"] = (pmtfilter.min_height or 1) - 1
_pmts = self.raw_request(method, params)
pmts = _pmts.get('payments', [])
pmts = _pmts.get("payments", [])
if pmtfilter.unconfirmed:
pmts.extend(_pmts.get('pool', []))
pmts.extend(_pmts.get("pool", []))
return list(pmtfilter.filter(map(self._inpayment, pmts)))
def transfers_out(self, account, pmtfilter):
@ -154,45 +180,51 @@ class JSONRPCWallet(object):
for txid in pmtfilter.tx_ids:
try:
_pmts = self.raw_request(
'get_transfer_by_txid',
{'account_index': account, 'txid': txid},
squelch_error_logging=True)
"get_transfer_by_txid",
{"account_index": account, "txid": txid},
squelch_error_logging=True,
)
except exceptions.TransactionNotFound:
continue
pmts.extend(_pmts['transfers'])
pmts.extend(_pmts["transfers"])
else:
_pmts = self.raw_request('get_transfers', {
'account_index': account,
'in': False,
'out': pmtfilter.confirmed,
'pool': False,
'pending': pmtfilter.unconfirmed})
pmts = _pmts.get('out', [])
_pmts = self.raw_request(
"get_transfers",
{
"account_index": account,
"in": False,
"out": pmtfilter.confirmed,
"pool": False,
"pending": pmtfilter.unconfirmed,
},
)
pmts = _pmts.get("out", [])
if pmtfilter.unconfirmed:
pmts.extend(_pmts.get('pending', []))
pmts.extend(_pmts.get("pending", []))
return list(pmtfilter.filter(map(self._outpayment, pmts)))
def _paymentdict(self, data):
pid = data.get('payment_id', None)
laddr = data.get('address', None)
pid = data.get("payment_id", None)
laddr = data.get("address", None)
if laddr:
laddr = address(laddr)
result = {
'payment_id': None if pid is None else PaymentID(pid),
'amount': from_atomic(data['amount']),
'timestamp': datetime.fromtimestamp(data['timestamp']) if 'timestamp' in data else None,
'note': data.get('note', None),
'transaction': self._tx(data),
'local_address': laddr,
"payment_id": None if pid is None else PaymentID(pid),
"amount": from_atomic(data["amount"]),
"timestamp": datetime.fromtimestamp(data["timestamp"])
if "timestamp" in data
else None,
"note": data.get("note", None),
"transaction": self._tx(data),
"local_address": laddr,
}
if 'destinations' in data:
result['destinations'] = [
(address(x['address']), from_atomic(x['amount']))
for x in data.get('destinations')
if "destinations" in data:
result["destinations"] = [
(address(x["address"]), from_atomic(x["amount"]))
for x in data.get("destinations")
]
return result
def _inpayment(self, data):
return IncomingPayment(**self._paymentdict(data))
@ -200,123 +232,198 @@ class JSONRPCWallet(object):
return OutgoingPayment(**self._paymentdict(data))
def _tx(self, data):
return Transaction(**{
'hash': data.get('txid', data.get('tx_hash')),
'fee': from_atomic(data['fee']) if 'fee' in data else None,
'key': data.get('key'),
'height': data.get('height', data.get('block_height')) or None,
'timestamp': datetime.fromtimestamp(data['timestamp']) if 'timestamp' in data else None,
'blob': binascii.unhexlify(data.get('blob', '')),
'confirmations': data.get('confirmations', None)
})
return Transaction(
**{
"hash": data.get("txid", data.get("tx_hash")),
"fee": from_atomic(data["fee"]) if "fee" in data else None,
"key": data.get("key"),
"height": data.get("height", data.get("block_height")) or None,
"timestamp": datetime.fromtimestamp(data["timestamp"])
if "timestamp" in data
else None,
"blob": binascii.unhexlify(data.get("blob", "")),
"confirmations": data.get("confirmations", None),
}
)
def export_outputs(self):
return self.raw_request('export_outputs')['outputs_data_hex']
return self.raw_request("export_outputs")["outputs_data_hex"]
def import_outputs(self, outputs_hex):
return self.raw_request(
'import_outputs',
{'outputs_data_hex': outputs_hex})['num_imported']
return self.raw_request("import_outputs", {"outputs_data_hex": outputs_hex})[
"num_imported"
]
def export_key_images(self):
return self.raw_request('export_key_images')['signed_key_images']
return self.raw_request("export_key_images")["signed_key_images"]
def import_key_images(self, key_images):
_data = self.raw_request(
'import_key_images',
{'signed_key_images': key_images})
return (_data['height'], from_atomic(_data['spent']), from_atomic(_data['unspent']))
def transfer(self, destinations, priority,
payment_id=None, unlock_time=0, account=0,
relay=True):
_data = self.raw_request("import_key_images", {"signed_key_images": key_images})
return (
_data["height"],
from_atomic(_data["spent"]),
from_atomic(_data["unspent"]),
)
def transfer(
self,
destinations,
priority,
payment_id=None,
unlock_time=0,
account=0,
relay=True,
):
data = {
'account_index': account,
'destinations': list(map(
lambda dst: {'address': str(address(dst[0])), 'amount': to_atomic(dst[1])},
destinations)),
'priority': priority,
'unlock_time': 0,
'get_tx_keys': True,
'get_tx_hex': True,
'new_algorithm': True,
'do_not_relay': not relay,
"account_index": account,
"destinations": list(
map(
lambda dst: {
"address": str(address(dst[0])),
"amount": to_atomic(dst[1]),
},
destinations,
)
),
"priority": priority,
"unlock_time": 0,
"get_tx_keys": True,
"get_tx_hex": True,
"new_algorithm": True,
"do_not_relay": not relay,
}
if payment_id is not None:
data['payment_id'] = str(PaymentID(payment_id))
_transfers = self.raw_request('transfer_split', data)
_pertx = [dict(_tx) for _tx in map(
lambda vs: zip(('txid', 'amount', 'fee', 'key', 'blob', 'payment_id'), vs),
zip(*[_transfers[k] for k in (
'tx_hash_list', 'amount_list', 'fee_list', 'tx_key_list', 'tx_blob_list')]))]
data["payment_id"] = str(PaymentID(payment_id))
_transfers = self.raw_request("transfer_split", data)
_pertx = [
dict(_tx)
for _tx in map(
lambda vs: zip(
("txid", "amount", "fee", "key", "blob", "payment_id"), vs
),
zip(
*[
_transfers[k]
for k in (
"tx_hash_list",
"amount_list",
"fee_list",
"tx_key_list",
"tx_blob_list",
)
]
),
)
]
for d in _pertx:
d['payment_id'] = payment_id
d["payment_id"] = payment_id
return [self._tx(data) for data in _pertx]
def sweep_all(self, destination, priority, payment_id=None, subaddr_indices=None,
unlock_time=0, account=0, relay=True):
def sweep_all(
self,
destination,
priority,
payment_id=None,
subaddr_indices=None,
unlock_time=0,
account=0,
relay=True,
):
if not subaddr_indices:
# retrieve indices of all subaddresses with positive unlocked balance
bals = self.raw_request('get_balance', {'account_index': account})
bals = self.raw_request("get_balance", {"account_index": account})
subaddr_indices = []
for subaddr in bals['per_subaddress']:
if subaddr.get('unlocked_balance', 0):
subaddr_indices.append(subaddr['address_index'])
for subaddr in bals["per_subaddress"]:
if subaddr.get("unlocked_balance", 0):
subaddr_indices.append(subaddr["address_index"])
data = {
'account_index': account,
'address': str(address(destination)),
'subaddr_indices': list(subaddr_indices),
'priority': priority,
'unlock_time': 0,
'get_tx_keys': True,
'get_tx_hex': True,
'do_not_relay': not relay,
"account_index": account,
"address": str(address(destination)),
"subaddr_indices": list(subaddr_indices),
"priority": priority,
"unlock_time": 0,
"get_tx_keys": True,
"get_tx_hex": True,
"do_not_relay": not relay,
}
if payment_id is not None:
data['payment_id'] = str(PaymentID(payment_id))
_transfers = self.raw_request('sweep_all', data)
_pertx = [dict(_tx) for _tx in map(
lambda vs: zip(('txid', 'amount', 'fee', 'key', 'blob', 'payment_id'), vs),
zip(*[_transfers[k] for k in (
'tx_hash_list', 'amount_list', 'fee_list', 'tx_key_list', 'tx_blob_list')]))]
data["payment_id"] = str(PaymentID(payment_id))
_transfers = self.raw_request("sweep_all", data)
_pertx = [
dict(_tx)
for _tx in map(
lambda vs: zip(
("txid", "amount", "fee", "key", "blob", "payment_id"), vs
),
zip(
*[
_transfers[k]
for k in (
"tx_hash_list",
"amount_list",
"fee_list",
"tx_key_list",
"tx_blob_list",
)
]
),
)
]
for d in _pertx:
d['payment_id'] = payment_id
return list(zip(
d["payment_id"] = payment_id
return list(
zip(
[self._tx(data) for data in _pertx],
map(from_atomic, _transfers['amount_list'])))
map(from_atomic, _transfers["amount_list"]),
)
)
def raw_request(self, method, params=None, squelch_error_logging=False):
hdr = {'Content-Type': 'application/json'}
data = {'jsonrpc': '2.0', 'id': 0, 'method': method, 'params': params or {}}
_log.debug(u"Method: {method}\nParams:\n{params}".format(
method=method,
params=json.dumps(params, indent=2, sort_keys=True)))
hdr = {"Content-Type": "application/json"}
data = {"jsonrpc": "2.0", "id": 0, "method": method, "params": params or {}}
_log.debug(
"Method: {method}\nParams:\n{params}".format(
method=method, params=json.dumps(params, indent=2, sort_keys=True)
)
)
auth = requests.auth.HTTPDigestAuth(self.user, self.password)
rsp = requests.post(
self.url, headers=hdr, data=json.dumps(data), auth=auth,
timeout=self.timeout, verify=self.verify_ssl_certs, proxies=self.proxies)
self.url,
headers=hdr,
data=json.dumps(data),
auth=auth,
timeout=self.timeout,
verify=self.verify_ssl_certs,
proxies=self.proxies,
)
if rsp.status_code == 401:
raise Unauthorized("401 Unauthorized. Invalid RPC user name or password.")
elif rsp.status_code != 200:
raise RPCError("Invalid HTTP status {code} for method {method}.".format(
code=rsp.status_code,
method=method))
raise RPCError(
"Invalid HTTP status {code} for method {method}.".format(
code=rsp.status_code, method=method
)
)
result = rsp.json()
_ppresult = json.dumps(result, indent=2, sort_keys=True)
_log.debug(u"Result:\n{result}".format(result=_ppresult))
_log.debug("Result:\n{result}".format(result=_ppresult))
if 'error' in result:
err = result['error']
if "error" in result:
err = result["error"]
if not squelch_error_logging:
_log.error(u"JSON RPC error:\n{result}".format(result=_ppresult))
if err['code'] in _err2exc:
raise _err2exc[err['code']](err['message'])
_log.error("JSON RPC error:\n{result}".format(result=_ppresult))
if err["code"] in _err2exc:
raise _err2exc[err["code"]](err["message"])
else:
raise RPCError(
"Method '{method}' failed with RPC Error of unknown code {code}, "
"message: {message}".format(method=method, data=data, result=result, **err))
return result['result']
"message: {message}".format(
method=method, data=data, result=result, **err
)
)
return result["result"]
_err2exc = {
-2: exceptions.WrongAddress,
@ -332,6 +439,6 @@ _err2exc = {
-29: exceptions.WalletIsWatchOnly,
-37: exceptions.NotEnoughUnlockedMoney,
-38: exceptions.NoDaemonConnection,
-43: exceptions.WalletIsNotDeterministic, # https://github.com/monero-project/monero/pull/4653
-43: exceptions.WalletIsNotDeterministic, # https://github.com/monero-project/monero/pull/4653
-32601: MethodNotFound,
}

@ -13,6 +13,7 @@ class OfflineWallet(object):
"""
Offline backend for Monero wallet. Provides support for address generation.
"""
_address = None
_svk = None
_ssk = EMPTY_KEY
@ -43,7 +44,7 @@ class OfflineWallet(object):
def addresses(self, account=0, addr_indices=None):
if account == 0 and (addr_indices == [0] or addr_indices is None):
return [self._address]
raise WalletIsOffline() # pragma: no cover (this should never happen)
raise WalletIsOffline() # pragma: no cover (this should never happen)
def new_address(self, account=0, label=None):
raise WalletIsOffline()

@ -9,9 +9,11 @@
# + optimized
# + proper exceptions instead of returning errors as results
__alphabet = [ord(s) for s in '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz']
__alphabet = [
ord(s) for s in "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
]
__b58base = 58
__UINT64MAX = 2**64
__UINT64MAX = 2 ** 64
__encodedBlockSizes = [0, 2, 3, 5, 6, 7, 9, 10, 11]
__fullBlockSize = 8
__fullEncodedBlockSize = 11
@ -20,11 +22,11 @@ __fullEncodedBlockSize = 11
def _hexToBin(hex_):
if len(hex_) % 2 != 0:
raise ValueError("Hex string has invalid length: %d" % len(hex_))
return [int(hex_[i:i + 2], 16) for i in range(0, len(hex_), 2)]
return [int(hex_[i : i + 2], 16) for i in range(0, len(hex_), 2)]
def _binToHex(bin_):
return "".join('%02x' % int(b) for b in bin_)
return "".join("%02x" % int(b) for b in bin_)
def _uint8be_to_64(data):
@ -42,8 +44,8 @@ def _uint64_to_8be(num, size):
raise ValueError("Invalid input length: %d" % size)
res = [0] * size
twopow8 = 2**8
for i in range(size-1,-1,-1):
twopow8 = 2 ** 8
for i in range(size - 1, -1, -1):
res[i] = num % twopow8
num = num // twopow8
@ -62,14 +64,14 @@ def encode_block(data, buf, index):
while num > 0:
remainder = num % __b58base
num = num // __b58base
buf[index+i] = __alphabet[remainder]
buf[index + i] = __alphabet[remainder]
i -= 1
return buf
def encode(hex):
'''Encode hexadecimal string as base58 (ex: encoding a Monero address).'''
"""Encode hexadecimal string as base58 (ex: encoding a Monero address)."""
data = _hexToBin(hex)
l_data = len(data)
@ -78,17 +80,31 @@ def encode(hex):
full_block_count = l_data // __fullBlockSize
last_block_size = l_data % __fullBlockSize
res_size = full_block_count * __fullEncodedBlockSize + __encodedBlockSizes[last_block_size]
res_size = (
full_block_count * __fullEncodedBlockSize + __encodedBlockSizes[last_block_size]
)
res = bytearray([__alphabet[0]] * res_size)
for i in range(full_block_count):
res = encode_block(data[(i*__fullBlockSize):(i*__fullBlockSize+__fullBlockSize)], res, i * __fullEncodedBlockSize)
res = encode_block(
data[(i * __fullBlockSize) : (i * __fullBlockSize + __fullBlockSize)],
res,
i * __fullEncodedBlockSize,
)
if last_block_size > 0:
res = encode_block(data[(full_block_count*__fullBlockSize):(full_block_count*__fullBlockSize+last_block_size)], res, full_block_count * __fullEncodedBlockSize)
res = encode_block(
data[
(full_block_count * __fullBlockSize) : (
full_block_count * __fullBlockSize + last_block_size
)
],
res,
full_block_count * __fullEncodedBlockSize,
)
return bytes(res).decode('ascii')
return bytes(res).decode("ascii")
def decode_block(data, buf, index):
@ -103,30 +119,32 @@ def decode_block(data, buf, index):
res_num = 0
order = 1
for i in range(l_data-1, -1, -1):
for i in range(l_data - 1, -1, -1):
digit = __alphabet.index(data[i])
if digit < 0:
raise ValueError("Invalid symbol: %s" % data[i])
product = order * digit + res_num
if product > __UINT64MAX:
raise ValueError("Overflow: %d * %d + %d = %d" % (order, digit, res_num, product))
raise ValueError(
"Overflow: %d * %d + %d = %d" % (order, digit, res_num, product)
)
res_num = product
order = order * __b58base
if res_size < __fullBlockSize and 2**(8 * res_size) <= res_num:
if res_size < __fullBlockSize and 2 ** (8 * res_size) <= res_num:
raise ValueError("Overflow: %d doesn't fit in %d bit(s)" % (res_num, res_size))
tmp_buf = _uint64_to_8be(res_num, res_size)
buf[index:index + len(tmp_buf)] = tmp_buf
buf[index : index + len(tmp_buf)] = tmp_buf
return buf
def decode(enc):
'''Decode a base58 string (ex: a Monero address) into hexidecimal form.'''
enc = bytearray(enc, encoding='ascii')
"""Decode a base58 string (ex: a Monero address) into hexidecimal form."""
enc = bytearray(enc, encoding="ascii")
l_enc = len(enc)
if l_enc == 0:
@ -143,9 +161,25 @@ def decode(enc):
data = bytearray(data_size)
for i in range(full_block_count):
data = decode_block(enc[(i*__fullEncodedBlockSize):(i*__fullEncodedBlockSize+__fullEncodedBlockSize)], data, i * __fullBlockSize)
data = decode_block(
enc[
(i * __fullEncodedBlockSize) : (
i * __fullEncodedBlockSize + __fullEncodedBlockSize
)
],
data,
i * __fullBlockSize,
)
if last_block_size > 0:
data = decode_block(enc[(full_block_count*__fullEncodedBlockSize):(full_block_count*__fullEncodedBlockSize+last_block_size)], data, full_block_count * __fullBlockSize)
data = decode_block(
enc[
(full_block_count * __fullEncodedBlockSize) : (
full_block_count * __fullEncodedBlockSize + last_block_size
)
],
data,
full_block_count * __fullBlockSize,
)
return _binToHex(data)

@ -10,6 +10,7 @@ class Block(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
hash = None
height = None
timestamp = None
@ -24,11 +25,20 @@ class Block(object):
transactions = None
def __init__(self, **kwargs):
for k in ('hash', 'height', 'timestamp', 'version', 'difficulty', 'nonce', 'prev_hash', 'reward'):
for k in (
"hash",
"height",
"timestamp",
"version",
"difficulty",
"nonce",
"prev_hash",
"reward",
):
setattr(self, k, kwargs.get(k, getattr(self, k)))
self.orphan = kwargs.get('orphan', self.orphan)
self.transactions = kwargs.get('transactions', self.transactions or [])
self.blob = kwargs.get('blob', self.blob)
self.orphan = kwargs.get("orphan", self.orphan)
self.transactions = kwargs.get("transactions", self.transactions or [])
self.blob = kwargs.get("blob", self.blob)
def __eq__(self, other):
if isinstance(other, Block):
@ -45,5 +55,6 @@ class Block(object):
else:
raise ValueError(
"Only Transaction or hash strings may be used to test existence in Blocks, "
"got '{:s}'".format(tx))
return txid in map(operator.attrgetter('hash'), self.transactions)
"got '{:s}'".format(tx)
)
return txid in map(operator.attrgetter("hash"), self.transactions)

@ -2,6 +2,7 @@ import six
from .backends.jsonrpc import JSONRPCDaemon
class Daemon(object):
"""Monero daemon.
@ -11,9 +12,10 @@ class Daemon(object):
:param \\**kwargs: arguments to initialize a :class:`JSONRPCDaemon <monero.backends.jsonrpc.JSONRPCDaemon>`
instance if no backend is given
"""
def __init__(self, backend=None, **kwargs):
if backend and len(kwargs):
raise ValueError('backend already given, other arguments are extraneous')
raise ValueError("backend already given, other arguments are extraneous")
self._backend = backend if backend else JSONRPCDaemon(**kwargs)
@ -35,7 +37,7 @@ class Daemon(object):
:rtype: int
"""
return self._backend.info()['height']
return self._backend.info()["height"]
def send_transaction(self, tx, relay=True):
"""
@ -55,7 +57,6 @@ class Daemon(object):
"""
return self._backend.mempool()
def headers(self, start_height, end_height=None):
"""
Returns block headers for given height range.

@ -36,9 +36,9 @@ import six
import sys
if sys.version_info >= (3,): # pragma: no cover
if sys.version_info >= (3,): # pragma: no cover
intlist2bytes = bytes
else: # pragma: no cover
else: # pragma: no cover
range = xrange
def intlist2bytes(l):
@ -60,18 +60,18 @@ def pow2(x, p):
def inv(z):
# Adapted from curve25519_athlon.c in djb's Curve25519.
z2 = z * z % q # 2
z9 = pow2(z2, 2) * z % q # 9
z11 = z9 * z2 % q # 11
z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0
z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0
z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ...
z2 = z * z % q # 2
z9 = pow2(z2, 2) * z % q # 9
z11 = z9 * z2 % q # 11
z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0
z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0
z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ...
z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q
z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q
z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q
z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q
z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0
return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2
z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0
return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2
d = -121665 * inv(121666) % q
@ -86,16 +86,19 @@ def xrecover(y):
x = (x * I) % q
if x % 2 != 0:
x = q-x
x = q - x
return x
def compress(P):
zinv = inv(P[2])
return (P[0] * zinv % q, P[1] * zinv % q)
def decompress(P):
return (P[0], P[1], 1, P[0]*P[1] % q)
return (P[0], P[1], 1, P[0] * P[1] % q)
By = 4 * inv(5)
Bx = xrecover(By)
@ -109,18 +112,18 @@ def edwards_add(P, Q):
(x1, y1, z1, t1) = P
(x2, y2, z2, t2) = Q
a = (y1-x1)*(y2-x2) % q
b = (y1+x1)*(y2+x2) % q
c = t1*2*d*t2 % q
dd = z1*2*z2 % q
a = (y1 - x1) * (y2 - x2) % q
b = (y1 + x1) * (y2 + x2) % q
c = t1 * 2 * d * t2 % q
dd = z1 * 2 * z2 % q
e = b - a
f = dd - c
g = dd + c
h = b + a
x3 = e*f
y3 = g*h
t3 = e*h
z3 = f*g
x3 = e * f
y3 = g * h
t3 = e * h
z3 = f * g
return (x3 % q, y3 % q, z3 % q, t3 % q)
@ -130,18 +133,18 @@ def edwards_double(P):
# http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
(x1, y1, z1, t1) = P
a = x1*x1 % q
b = y1*y1 % q
c = 2*z1*z1 % q
a = x1 * x1 % q
b = y1 * y1 % q
c = 2 * z1 * z1 % q
# dd = -a
e = ((x1+y1)*(x1+y1) - a - b) % q
e = ((x1 + y1) * (x1 + y1) - a - b) % q
g = -a + b # dd + b
f = g - c
h = -a - b # dd - b
x3 = e*f
y3 = g*h
t3 = e*h
z3 = f*g
x3 = e * f
y3 = g * h
t3 = e * h
z3 = f * g
return (x3 % q, y3 % q, z3 % q, t3 % q)
@ -165,6 +168,8 @@ def make_Bpow():
for i in range(253):
Bpow.append(P)
P = edwards_double(P)
make_Bpow()
@ -185,10 +190,12 @@ def scalarmult_B(e):
def encodeint(y):
bits = [(y >> i) & 1 for i in range(b)]
return b''.join([
six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)]))
for i in range(b//8)
])
return b"".join(
[
six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)]))
for i in range(b // 8)
]
)
def encodepoint(P):
@ -197,10 +204,12 @@ def encodepoint(P):
x = (x * zi) % q
y = (y * zi) % q
bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
return b''.join([
six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)]))
for i in range(b // 8)
])
return b"".join(
[
six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)]))
for i in range(b // 8)
]
)
def bit(h, i):
@ -209,9 +218,11 @@ def bit(h, i):
def isoncurve(P):
(x, y, z, t) = P
return (z % q != 0 and
x*y % q == z*t % q and
(y*y - x*x - z*z - d*t*t) % q == 0)
return (
z % q != 0
and x * y % q == z * t % q
and (y * y - x * x - z * z - d * t * t) % q == 0
)
def decodeint(s):
@ -221,9 +232,9 @@ def decodeint(s):
def decodepoint(s):
y = sum(2 ** i * bit(s, i) for i in range(0, b - 1))
x = xrecover(y)
if x & 1 != bit(s, b-1):
if x & 1 != bit(s, b - 1):
x = q - x
P = (x, y, 1, (x*y) % q)
P = (x, y, 1, (x * y) % q)
if not isoncurve(P):
raise ValueError("decoding point that is not on curve")
return P
@ -234,5 +245,6 @@ def public_from_secret(k):
aB = scalarmult_B(keyInt)
return encodepoint(aB)
def public_from_secret_hex(hk):
return binascii.hexlify(public_from_secret(binascii.unhexlify(hk))).decode()

@ -1,67 +1,88 @@
class MoneroException(Exception):
pass
class BackendException(MoneroException):
pass
class NoDaemonConnection(BackendException):
pass
class DaemonIsBusy(BackendException):
pass
class AccountException(MoneroException):
pass
class WrongAddress(AccountException):
pass
class WrongPaymentId(AccountException):
pass
class NotEnoughMoney(AccountException):
pass
class NotEnoughUnlockedMoney(NotEnoughMoney):
pass
class AmountIsZero(AccountException):
pass
class TransactionNotPossible(AccountException):
pass
class TransactionBroadcastError(BackendException):
def __init__(self, message, details=None):
self.details = details
super(TransactionBroadcastError, self).__init__(message)
class TransactionNotFound(AccountException):
pass
class SignatureCheckFailed(MoneroException):
pass
class WalletIsNotDeterministic(MoneroException):
pass
class GenericTransferError(AccountException):
pass
class AccountIndexOutOfBound(AccountException):
pass
class AddressIndexOutOfBound(AccountException):
pass
class WalletIsWatchOnly(MoneroException):
pass
class TransactionIncomplete(MoneroException):
pass
class TransactionWithoutBlob(TransactionIncomplete):
pass
class TransactionWithoutJSON(TransactionIncomplete):
pass

@ -1,21 +1,25 @@
from decimal import Decimal
import six
PICONERO = Decimal('0.000000000001')
EMPTY_KEY = '0' * 64
PICONERO = Decimal("0.000000000001")
EMPTY_KEY = "0" * 64
def to_atomic(amount):
"""Convert Monero decimal to atomic integer of piconero."""
if not isinstance(amount, (Decimal, float) + six.integer_types):
raise ValueError("Amount '{}' doesn't have numeric type. Only Decimal, int, long and "
"float (not recommended) are accepted as amounts.")
return int(amount * 10**12)
raise ValueError(
"Amount '{}' doesn't have numeric type. Only Decimal, int, long and "
"float (not recommended) are accepted as amounts."
)
return int(amount * 10 ** 12)
def from_atomic(amount):
"""Convert atomic integer of piconero to Monero decimal."""
return (Decimal(amount) * PICONERO).quantize(PICONERO)
def as_monero(amount):
"""Return the amount rounded to maximal Monero precision."""
return Decimal(amount).quantize(PICONERO)
@ -31,18 +35,25 @@ class PaymentID(object):
:param payment_id: the payment ID as integer or hexadecimal string
"""
_payment_id = None
def __init__(self, payment_id):
if isinstance(payment_id, PaymentID):
payment_id = int(payment_id)
if isinstance(payment_id, six.text_type) or isinstance(payment_id, six.string_types):
if isinstance(payment_id, six.text_type) or isinstance(
payment_id, six.string_types
):
payment_id = int(payment_id, 16)
elif not isinstance(payment_id, six.integer_types):
raise TypeError("payment_id must be either int or hexadecimal str or bytes, "
"is {0}".format(type(payment_id)))
raise TypeError(
"payment_id must be either int or hexadecimal str or bytes, "
"is {0}".format(type(payment_id))
)
if payment_id.bit_length() > 256:
raise ValueError("payment_id {0} is more than 256 bits long".format(payment_id))
raise ValueError(
"payment_id {0} is more than 256 bits long".format(payment_id)
)
self._payment_id = payment_id
def is_short(self):

@ -42,6 +42,7 @@ import warnings
from . import base58, const, ed25519, wordlists
from .address import address
class Seed(object):
"""Creates a seed object either from local system randomness or an imported phrase.
@ -55,8 +56,8 @@ class Seed(object):
:rtype: :class:`Seed <monero.seed.Seed>`
"""
self.phrase = "" #13 or 25 word mnemonic word string
self.hex = "" # hexadecimal
self.phrase = "" # 13 or 25 word mnemonic word string
self.hex = "" # hexadecimal
self.word_list = wordlists.get_wordlist(wordlist)
@ -82,11 +83,15 @@ class Seed(object):
elif len(seed_split) == 1:
# single string, probably hex, but confirm
if not len(phrase_or_hex) % 8 == 0:
raise ValueError("Not valid hexadecimal: {hex}".format(hex=phrase_or_hex))
raise ValueError(
"Not valid hexadecimal: {hex}".format(hex=phrase_or_hex)
)
self.hex = phrase_or_hex
self._encode_seed()
else:
raise ValueError("Not valid mnemonic phrase or hex: {arg}".format(arg=phrase_or_hex))
raise ValueError(
"Not valid mnemonic phrase or hex: {arg}".format(arg=phrase_or_hex)
)
else:
self.hex = generate_hex()
self._encode_seed()
@ -96,13 +101,11 @@ class Seed(object):
return len(self.hex) == 32
def _encode_seed(self):
"""Convert hexadecimal string to mnemonic word representation with checksum.
"""
"""Convert hexadecimal string to mnemonic word representation with checksum."""
self.phrase = self.word_list.encode(self.hex)
def _decode_seed(self):
"""Calculate hexadecimal representation of the phrase.
"""
"""Calculate hexadecimal representation of the phrase."""
self.hex = self.word_list.decode(self.phrase)
def _validate_checksum(self):
@ -133,7 +136,11 @@ class Seed(object):
return self.sc_reduce(a)
def secret_view_key(self):
b = self._hex_seed_keccak() if self.is_mymonero() else unhexlify(self.secret_spend_key())
b = (
self._hex_seed_keccak()
if self.is_mymonero()
else unhexlify(self.secret_spend_key())
)
h = keccak_256()
h.update(b)
return self.sc_reduce(h.digest())
@ -158,12 +165,17 @@ class Seed(object):
:rtype: :class:`Address <monero.address.Address>`
"""
# backward compatibility
_net = net[:-3] if net.endswith('net') else net
_net = net[:-3] if net.endswith("net") else net
if net not in const.NETS:
raise ValueError(
"Invalid net argument '{:s}'. Must be one of monero.const.NET_*".format(net))
"Invalid net argument '{:s}'. Must be one of monero.const.NET_*".format(
net
)
)
netbyte = (18, 53, 24)[const.NETS.index(net)]
data = "{:x}{:s}{:s}".format(netbyte, self.public_spend_key(), self.public_view_key())
data = "{:x}{:s}{:s}".format(
netbyte, self.public_spend_key(), self.public_view_key()
)
h = keccak_256()
h.update(unhexlify(data))
checksum = h.hexdigest()

@ -13,6 +13,7 @@ from .. import ed25519
from .. import exceptions
from .extra import ExtraParser
class Payment(object):
"""
A payment base class, representing payment not associated with any
@ -21,28 +22,35 @@ class Payment(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
payment_id = None
amount = None
timestamp = None
transaction = None
local_address = None
note = ''
note = ""
_reprstr = "{} @ {} {:.12f} id={}"
def __init__(self, **kwargs):
self.amount = kwargs.pop('amount', self.amount)
self.timestamp = kwargs.pop('timestamp', self.timestamp)
self.payment_id = kwargs.pop('payment_id', self.payment_id)
self.transaction = kwargs.pop('transaction', self.transaction)
self.local_address = kwargs.pop('local_address', self.local_address)
self.note = kwargs.pop('note', self.note)
self.amount = kwargs.pop("amount", self.amount)
self.timestamp = kwargs.pop("timestamp", self.timestamp)
self.payment_id = kwargs.pop("payment_id", self.payment_id)
self.transaction = kwargs.pop("transaction", self.transaction)
self.local_address = kwargs.pop("local_address", self.local_address)
self.note = kwargs.pop("note", self.note)
if len(kwargs):
raise ValueError("Excessive arguments for {}: {}".format(type(self), kwargs))
raise ValueError(
"Excessive arguments for {}: {}".format(type(self), kwargs)
)
def __repr__(self):
return self._reprstr.format(
self.transaction.hash, self.transaction.height or 'pool', self.amount, self.payment_id)
self.transaction.hash,
self.transaction.height or "pool",
self.amount,
self.payment_id,
)
class IncomingPayment(Payment):
@ -50,6 +58,7 @@ class IncomingPayment(Payment):
An incoming payment (one that increases the balance of an
:class:`Account <monero.account.Account>`)
"""
_reprstr = "in: {} @ {} {:.12f} id={}"
@ -58,10 +67,11 @@ class OutgoingPayment(Payment):
An outgoing payment (one that decreases the balance of an
:class:`Account <monero.account.Account>`)
"""
destinations = None
def __init__(self, **kwargs):
self.destinations = kwargs.pop('destinations', [])
self.destinations = kwargs.pop("destinations", [])
super(OutgoingPayment, self).__init__(**kwargs)
_reprstr = "out: {} @ {} {:.12f} id={}"
@ -75,6 +85,7 @@ class Transaction(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
hash = None
fee = None
height = None
@ -90,29 +101,31 @@ class Transaction(object):
@property
def is_coinbase(self):
if self.json:
return "gen" in self.json['vin'][0]
return "gen" in self.json["vin"][0]
raise exceptions.TransactionWithoutJSON(
"Tx {:s} has no .json attribute and it cannot be determined "
"if it's coinbase tx.".format(self.hash))
"if it's coinbase tx.".format(self.hash)
)
@property
def size(self):
if not self.blob:
raise exceptions.TransactionWithoutBlob(
"Transaction has no blob, hence the size cannot be determined. "
"Perhaps the backend prunes transaction data?")
"Perhaps the backend prunes transaction data?"
)
return len(self.blob)
def __init__(self, **kwargs):
self.hash = kwargs.get('hash', self.hash)
self.fee = kwargs.get('fee', self.fee)
self.height = kwargs.get('height', self.height)
self.timestamp = kwargs.get('timestamp', self.timestamp)
self.key = kwargs.get('key', self.key)
self.blob = kwargs.get('blob', self.blob)
self.confirmations = kwargs.get('confirmations', self.confirmations)
self.output_indices = kwargs.get('output_indices', self.output_indices)
self.json = kwargs.get('json', self.json)
self.hash = kwargs.get("hash", self.hash)
self.fee = kwargs.get("fee", self.fee)
self.height = kwargs.get("height", self.height)
self.timestamp = kwargs.get("timestamp", self.timestamp)
self.key = kwargs.get("key", self.key)
self.blob = kwargs.get("blob", self.blob)
self.confirmations = kwargs.get("confirmations", self.confirmations)
self.output_indices = kwargs.get("output_indices", self.output_indices)
self.json = kwargs.get("json", self.json)
self.pubkeys = self.pubkeys or []
if self.json:
if "rct_signatures" in self.json:
@ -126,6 +139,7 @@ class Transaction(object):
for outputs directed to the wallet, provided that matching subaddresses have been
already generated.
"""
def _scan_pubkeys(svk, psk, stealth_address, amount, encamount):
for keyidx, tx_key in enumerate(self.pubkeys):
hsdata = b"".join(
@ -147,7 +161,8 @@ class Transaction(object):
k = ed25519.encodepoint(
ed25519.edwards_add(
ed25519.scalarmult_B(Hsint), ed25519.decodepoint(psk),
ed25519.scalarmult_B(Hsint),
ed25519.decodepoint(psk),
)
)
if k != stealth_address:
@ -158,22 +173,26 @@ class Transaction(object):
amount=amount,
timestamp=self.timestamp,
transaction=self,
local_address=addr)
local_address=addr,
)
amount_hs = sha3.keccak_256(b"amount" + Hs).digest()
xormask = amount_hs[:len(encamount)]
dec_amount = bytearray(a ^ b for a, b in zip(*map(bytearray, (encamount, xormask))))
xormask = amount_hs[: len(encamount)]
dec_amount = bytearray(
a ^ b for a, b in zip(*map(bytearray, (encamount, xormask)))
)
int_amount = struct.unpack("<Q", dec_amount)[0]
amount = from_atomic(int_amount)
return Payment(
amount=amount,
timestamp=self.timestamp,
transaction=self,
local_address=addr)
amount=amount,
timestamp=self.timestamp,
transaction=self,
local_address=addr,
)
if not self.json:
raise exceptions.TransactionWithoutJSON(
'Tx {:s} has no .json attribute'.format(self.hash))
"Tx {:s} has no .json attribute".format(self.hash)
)
if wallet:
ep = ExtraParser(self.json["extra"])
@ -182,29 +201,41 @@ class Transaction(object):
svk = binascii.unhexlify(wallet.view_key())
# fetch before loop to save on calls; cast to list to preserve over multiple iterations
addresses = list(
itertools.chain(*map(operator.methodcaller("addresses"), wallet.accounts)))
itertools.chain(
*map(operator.methodcaller("addresses"), wallet.accounts)
)
)
outs = []
for idx, vout in enumerate(self.json['vout']):
stealth_address = binascii.unhexlify(vout['target']['key'])
for idx, vout in enumerate(self.json["vout"]):
stealth_address = binascii.unhexlify(vout["target"]["key"])
encamount = None
if self.version == 2 and not self.is_coinbase:
encamount = binascii.unhexlify(
self.json["rct_signatures"]["ecdhInfo"][idx]["amount"]
)
payment = None
amount = from_atomic(vout['amount']) if self.version == 1 or self.is_coinbase else None
amount = (
from_atomic(vout["amount"])
if self.version == 1 or self.is_coinbase
else None
)
if wallet:
for addridx, addr in enumerate(addresses):
psk = binascii.unhexlify(addr.spend_key())
payment = _scan_pubkeys(svk, psk, stealth_address, amount, encamount)
payment = _scan_pubkeys(
svk, psk, stealth_address, amount, encamount
)
if payment:
break
outs.append(Output(
stealth_address=vout['target']['key'],
amount=payment.amount if payment else amount,
index=self.output_indices[idx] if self.output_indices else None,
transaction=self,
payment=payment))
outs.append(
Output(
stealth_address=vout["target"]["key"],
amount=payment.amount if payment else amount,
index=self.output_indices[idx] if self.output_indices else None,
transaction=self,
payment=payment,
)
)
return outs
def __repr__(self):
@ -220,6 +251,7 @@ class Output(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
stealth_address = None
amount = None
index = None
@ -227,22 +259,23 @@ class Output(object):
payment = None
def __init__(self, **kwargs):
self.stealth_address = kwargs.get('stealth_address', self.stealth_address)
self.amount = kwargs.get('amount', self.amount)
self.index = kwargs.get('index', self.index)
self.transaction = kwargs.get('transaction', self.transaction)
self.payment = kwargs.get('payment', self.payment)
self.stealth_address = kwargs.get("stealth_address", self.stealth_address)
self.amount = kwargs.get("amount", self.amount)
self.index = kwargs.get("index", self.index)
self.transaction = kwargs.get("transaction", self.transaction)
self.payment = kwargs.get("payment", self.payment)
def __repr__(self):
# Try to represent output as (index, amount) pair if applicable because there is no RPC
# daemon command to lookup outputs by their stealth_address ;(
# daemon command to lookup outputs by their stealth_address ;(
if self.stealth_address:
res = self.stealth_address
else:
res = "(index={},amount={})".format(self.index, self.amount)
if self.payment:
return "{:s}, {:.12f} to [{:s}]".format(
res, self.payment.amount, str(self.payment.local_address)[:6])
res, self.payment.amount, str(self.payment.local_address)[:6]
)
return res
def __eq__(self, other):
@ -252,10 +285,12 @@ class Output(object):
elif None not in (self.index, other.index, self.amount, other.amount):
return self.index == other.index and self.amount == other.amount
else:
raise TypeError('Given one-time outputs (%r,%r) are not comparable'.format(self, other))
raise TypeError(
"Given one-time outputs (%r,%r) are not comparable".format(self, other)
)
def __ne__(self, other):
return not(self == other)
return not (self == other)
class PaymentManager(object):
@ -266,6 +301,7 @@ class PaymentManager(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
account_idx = 0
backend = None
@ -275,14 +311,20 @@ class PaymentManager(object):
self.direction = direction
def __call__(self, **filterparams):
fetch = self.backend.transfers_in if self.direction == 'in' else self.backend.transfers_out
fetch = (
self.backend.transfers_in
if self.direction == "in"
else self.backend.transfers_out
)
return fetch(self.account_idx, PaymentFilter(**filterparams))
def _validate_tx_id(txid):
if not bool(re.compile('^[0-9a-f]{64}$').match(txid)):
raise ValueError("Transaction ID must be a 64-character hexadecimal string, not "
"'{}'".format(txid))
if not bool(re.compile("^[0-9a-f]{64}$").match(txid)):
raise ValueError(
"Transaction ID must be a 64-character hexadecimal string, not "
"'{}'".format(txid)
)
return txid
@ -292,8 +334,10 @@ class _ByHeight(object):
**WARNING:** Integer sorting is reversed here.
"""
def __init__(self, pmt):
self.pmt = pmt
def _cmp(self, other):
sh = self.pmt.transaction.height
oh = other.pmt.transaction.height
@ -304,16 +348,22 @@ class _ByHeight(object):
if oh is None:
return -1
return (sh > oh) - (sh < oh)
def __lt__(self, other):
return self._cmp(other) > 0
def __le__(self, other):
return self._cmp(other) >= 0
def __eq__(self, other):
return self._cmp(other) == 0
def __ge__(self, other):
return self._cmp(other) <= 0
def __gt__(self, other):
return self._cmp(other) < 0
def __ne__(self, other):
return self._cmp(other) != 0
@ -325,27 +375,35 @@ class PaymentFilter(object):
This class is not intended to be turned into objects by the user,
it is used by backends.
"""
def __init__(self, **filterparams):
self.min_height = filterparams.pop('min_height', None)
self.max_height = filterparams.pop('max_height', None)
self.unconfirmed = filterparams.pop('unconfirmed', False)
self.confirmed = filterparams.pop('confirmed', True)
_local_address = filterparams.pop('local_address', None)
_tx_id = filterparams.pop('tx_id', None)
_payment_id = filterparams.pop('payment_id', None)
self.min_height = filterparams.pop("min_height", None)
self.max_height = filterparams.pop("max_height", None)
self.unconfirmed = filterparams.pop("unconfirmed", False)
self.confirmed = filterparams.pop("confirmed", True)
_local_address = filterparams.pop("local_address", None)
_tx_id = filterparams.pop("tx_id", None)
_payment_id = filterparams.pop("payment_id", None)
if len(filterparams) > 0:
raise ValueError("Excessive arguments for payment query: {}".format(filterparams))
if self.unconfirmed and (self.min_height is not None or self.max_height is not None):
warnings.warn("Height filtering (min_height/max_height) has been requested while "
"also asking for unconfirmed transactions. These are mutually exclusive. "
"As mempool transactions have no height at all, they will be excluded "
"from the result.",
RuntimeWarning)
raise ValueError(
"Excessive arguments for payment query: {}".format(filterparams)
)
if self.unconfirmed and (
self.min_height is not None or self.max_height is not None
):
warnings.warn(
"Height filtering (min_height/max_height) has been requested while "
"also asking for unconfirmed transactions. These are mutually exclusive. "
"As mempool transactions have no height at all, they will be excluded "
"from the result.",
RuntimeWarning,
)
if _local_address is None:
self.local_addresses = []
else:
if isinstance(_local_address, six.string_types) \
or isinstance(_local_address, six.text_type):
if isinstance(_local_address, six.string_types) or isinstance(
_local_address, six.text_type
):
local_addresses = [_local_address]
else:
try:
@ -357,7 +415,9 @@ class PaymentFilter(object):
if _tx_id is None:
self.tx_ids = []
else:
if isinstance(_tx_id, six.string_types) or isinstance(_tx_id, six.text_type):
if isinstance(_tx_id, six.string_types) or isinstance(
_tx_id, six.text_type
):
tx_ids = [_tx_id]
else:
try:
@ -369,7 +429,9 @@ class PaymentFilter(object):
if _payment_id is None:
self.payment_ids = []
else:
if isinstance(_payment_id, six.string_types) or isinstance(_payment_id, six.text_type):
if isinstance(_payment_id, six.string_types) or isinstance(
_payment_id, six.text_type
):
payment_ids = [_payment_id]
else:
try:
@ -403,6 +465,4 @@ class PaymentFilter(object):
return True
def filter(self, payments):
return sorted(
filter(self.check, payments),
key=_ByHeight)
return sorted(filter(self.check, payments), key=_ByHeight)

@ -35,7 +35,7 @@ class ExtraParser(object):
return extra
def _pop_pubkey(self, extra):
key = bytes(bytearray(extra[:32])) # bytearray() is for py2 compatibility
key = bytes(bytearray(extra[:32])) # bytearray() is for py2 compatibility
if len(key) < 32:
raise ValueError(
"offset {:d}: only {:d} bytes of key data, expected 32".format(

@ -10,6 +10,7 @@ from . import ed25519
from . import numbers
from .transaction import Payment, PaymentManager
class Wallet(object):
"""
Monero wallet.
@ -29,15 +30,16 @@ class Wallet(object):
:param \\**kwargs: arguments to initialize a :class:`JSONRPCWallet <monero.backends.jsonrpc.JSONRPCWallet>`
instance if no backend is given
"""
accounts = None
def __init__(self, backend=None, **kwargs):
if backend and len(kwargs):
raise ValueError('backend already given, other arguments are extraneous')
raise ValueError("backend already given, other arguments are extraneous")
self._backend = backend if backend else JSONRPCWallet(**kwargs)
self.incoming = PaymentManager(0, self._backend, 'in')
self.outgoing = PaymentManager(0, self._backend, 'out')
self.incoming = PaymentManager(0, self._backend, "in")
self.outgoing = PaymentManager(0, self._backend, "out")
self.refresh()
def refresh(self):
@ -209,34 +211,47 @@ class Wallet(object):
:rtype: :class:`BaseAddress <monero.address.BaseAddress>`
"""
# ensure indexes are within uint32
if major < 0 or major >= 2**32:
raise ValueError('major index {} is outside uint32 range'.format(major))
if minor < 0 or minor >= 2**32:
raise ValueError('minor index {} is outside uint32 range'.format(minor))
if major < 0 or major >= 2 ** 32:
raise ValueError("major index {} is outside uint32 range".format(major))
if minor < 0 or minor >= 2 ** 32:
raise ValueError("minor index {} is outside uint32 range".format(minor))
master_address = self.address()
if major == minor == 0:
return master_address
master_svk = unhexlify(self.view_key())
master_psk = unhexlify(self.address().spend_key())
# m = Hs("SubAddr\0" || master_svk || major || minor)
hsdata = b''.join([
b'SubAddr\0', master_svk,
struct.pack('<I', major), struct.pack('<I', minor)])
hsdata = b"".join(
[
b"SubAddr\0",
master_svk,
struct.pack("<I", major),
struct.pack("<I", minor),
]
)
m = keccak_256(hsdata).digest()
# D = master_psk + m * B
D = ed25519.edwards_add(
ed25519.decodepoint(master_psk),
ed25519.scalarmult_B(ed25519.decodeint(m)))
ed25519.decodepoint(master_psk), ed25519.scalarmult_B(ed25519.decodeint(m))
)
# C = master_svk * D
C = ed25519.scalarmult(D, ed25519.decodeint(master_svk))
netbyte = bytearray([const.SUBADDR_NETBYTES[const.NETS.index(master_address.net)]])
netbyte = bytearray(
[const.SUBADDR_NETBYTES[const.NETS.index(master_address.net)]]
)
data = netbyte + ed25519.encodepoint(D) + ed25519.encodepoint(C)
checksum = keccak_256(data).digest()[:4]
return address.SubAddress(base58.encode(hexlify(data + checksum)))
def transfer(self, address, amount,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
def transfer(
self,
address,
amount,
priority=const.PRIO_NORMAL,
payment_id=None,
unlock_time=0,
relay=True,
):
"""
Sends a transfer from the default account. Returns a list of resulting transactions.
@ -255,16 +270,22 @@ class Wallet(object):
:rtype: list of :class:`Transaction <monero.transaction.Transaction>`
"""
return self.accounts[0].transfer(
address,
amount,
priority=priority,
payment_id=payment_id,
unlock_time=unlock_time,
relay=relay)
def transfer_multiple(self, destinations,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
address,
amount,
priority=priority,
payment_id=payment_id,
unlock_time=unlock_time,
relay=relay,
)
def transfer_multiple(
self,
destinations,
priority=const.PRIO_NORMAL,
payment_id=None,
unlock_time=0,
relay=True,
):
"""
Sends a batch of transfers from the default account. Returns a list of resulting
transactions and amounts.
@ -284,14 +305,22 @@ class Wallet(object):
[(:class:`Transaction <monero.transaction.Transaction>`, `Decimal`), ...]
"""
return self.accounts[0].transfer_multiple(
destinations,
priority=priority,
payment_id=payment_id,
unlock_time=unlock_time,
relay=relay)
def sweep_all(self, address, priority=const.PRIO_NORMAL, payment_id=None,
subaddr_indices=None, unlock_time=0, relay=True):
destinations,
priority=priority,
payment_id=payment_id,
unlock_time=unlock_time,
relay=relay,
)
def sweep_all(
self,
address,
priority=const.PRIO_NORMAL,
payment_id=None,
subaddr_indices=None,
unlock_time=0,
relay=True,
):
"""
Sends all unlocked balance from the default account to an address.
Returns a list of resulting transactions.
@ -317,4 +346,5 @@ class Wallet(object):
payment_id=payment_id,
subaddr_indices=subaddr_indices,
unlock_time=unlock_time,
relay=relay)
relay=relay,
)

@ -61,7 +61,7 @@ from .wordlist import Wordlist
class ChineseSimplified(Wordlist):
language_name = u"简体中文 (中国)",
language_name = (u"简体中文 (中国)",)
english_language_name = "Chinese (simplified)"
unique_prefix_length = 1
word_list = [
@ -1690,5 +1690,5 @@ class ChineseSimplified(Wordlist):
u"",
u"",
u"",
u""
u"",
]

@ -1689,5 +1689,5 @@ class Dutch(Wordlist):
"zwepen",
"zwiep",
"zwijmel",
"zworen"
"zworen",
]

@ -1690,5 +1690,5 @@ class English(Wordlist):
"zodiac",
"zombie",
"zones",
"zoom"
"zoom",
]

@ -1690,5 +1690,5 @@ class French(Wordlist):
"zeste",
"zinc",
"zone",
"zoom"
"zoom",
]

@ -59,6 +59,7 @@
from .wordlist import Wordlist
class German(Wordlist):
language_name = "Deutsch"
english_language_name = "German"
@ -1689,5 +1690,5 @@ class German(Wordlist):
u"Zugvogel",
u"Zündung",
u"Zweck",
u"Zyklop"
u"Zyklop",
]

@ -1690,5 +1690,5 @@ class Italian(Wordlist):
"zoccolo",
"zolfo",
"zombie",
"zucchero"
"zucchero",
]

@ -1690,5 +1690,5 @@ class Japanese(Wordlist):
u"ひさしぶり",
u"ひさん",
u"びじゅつかん",
u"ひしょ"
u"ひしょ",
]

@ -59,6 +59,7 @@
from .wordlist import Wordlist
class Lojban(Wordlist):
language_name = "Lojban"
english_language_name = "Lojban"

@ -1690,5 +1690,5 @@ class Portuguese(Wordlist):
"zefiro",
"zeloso",
"zenite",
"zumbi"
"zumbi",
]

@ -1690,5 +1690,5 @@ class Russian(Wordlist):
u"ясный",
u"яхта",
u"ячейка",
u"ящик"
u"ящик",
]

@ -1690,5 +1690,5 @@ class Spanish(Wordlist):
u"riqueza",
u"risa",
u"ritmo",
u"rito"
u"rito",
]

@ -14,18 +14,22 @@ _log = logging.getLogger(__name__)
class WordlistType(type):
def __new__(cls, name, bases, attrs):
if bases:
if 'language_name' not in attrs:
if "language_name" not in attrs:
raise TypeError("Missing language_name for {0}".format(name))
if 'unique_prefix_length' not in attrs:
if "unique_prefix_length" not in attrs:
raise TypeError("Missing 'unique_prefix_length' for {0}".format(name))
if 'word_list' not in attrs:
if "word_list" not in attrs:
raise TypeError("Missing 'word_list' for {0}".format(name))
if 'english_language_name' not in attrs:
_log.warn("No 'english_language_name' for {0} using '{1}'".format(name, language_name))
attrs['english_language_name'] = attrs['language_name']
if "english_language_name" not in attrs:
_log.warn(
"No 'english_language_name' for {0} using '{1}'".format(
name, language_name
)
)
attrs["english_language_name"] = attrs["language_name"]
if len(attrs['word_list']) != 1626:
if len(attrs["word_list"]) != 1626:
raise TypeError("Wrong word list length for {0}".format(name))
new_cls = super(WordlistType, cls).__new__(cls, name, bases, attrs)
@ -41,11 +45,10 @@ class Wordlist(with_metaclass(WordlistType)):
@classmethod
def encode(cls, hex):
"""Convert hexadecimal string to mnemonic word representation with checksum.
"""
"""Convert hexadecimal string to mnemonic word representation with checksum."""
out = []
for i in range(len(hex) // 8):
word = endian_swap(hex[8*i:8*i+8])
word = endian_swap(hex[8 * i : 8 * i + 8])
x = int(word, 16)
w1 = x % cls.n
w2 = (x // cls.n + w1) % cls.n
@ -57,16 +60,15 @@ class Wordlist(with_metaclass(WordlistType)):
@classmethod
def decode(cls, phrase):
"""Calculate hexadecimal representation of the phrase.
"""
"""Calculate hexadecimal representation of the phrase."""
phrase = phrase.split(" ")
out = ""
for i in range(len(phrase) // 3):
word1, word2, word3 = phrase[3*i:3*i+3]
word1, word2, word3 = phrase[3 * i : 3 * i + 3]
w1 = cls.word_list.index(word1)
w2 = cls.word_list.index(word2) % cls.n
w3 = cls.word_list.index(word3) % cls.n
x = w1 + cls.n *((w2 - w1) % cls.n) + cls.n * cls.n * ((w3 - w2) % cls.n)
x = w1 + cls.n * ((w2 - w1) % cls.n) + cls.n * cls.n * ((w3 - w2) % cls.n)
out += endian_swap("%08x" % x)
return out
@ -85,10 +87,10 @@ class Wordlist(with_metaclass(WordlistType)):
else:
# MyMonero format
phrase = phrase_split[:12]
wstr = "".join(word[:cls.unique_prefix_length] for word in phrase)
wstr = bytearray(wstr.encode('utf-8'))
z = ((crc32(wstr) & 0xffffffff) ^ 0xffffffff ) >> 0
z2 = ((z ^ 0xffffffff) >> 0) % len(phrase)
wstr = "".join(word[: cls.unique_prefix_length] for word in phrase)
wstr = bytearray(wstr.encode("utf-8"))
z = ((crc32(wstr) & 0xFFFFFFFF) ^ 0xFFFFFFFF) >> 0
z2 = ((z ^ 0xFFFFFFFF) >> 0) % len(phrase)
return phrase_split[z2]
@ -108,4 +110,4 @@ def endian_swap(word):
:rtype: str
"""
return "".join([word[i:i+2] for i in [6, 4, 2, 0]])
return "".join([word[i : i + 2] for i in [6, 4, 2, 0]])

@ -2,14 +2,15 @@ import json
import os
import unittest
class JSONTestCase(unittest.TestCase):
jsonrpc_url = 'http://127.0.0.1:18088/json_rpc'
jsonrpc_url = "http://127.0.0.1:18088/json_rpc"
data_subdir = None
def _read(self, *args):
path = os.path.join(os.path.dirname(__file__), 'data')
path = os.path.join(os.path.dirname(__file__), "data")
if self.data_subdir:
path = os.path.join(path, self.data_subdir)
path = os.path.join(path, *args)
with open(path, 'r') as fh:
with open(path, "r") as fh:
return json.loads(fh.read())

@ -93,7 +93,11 @@ class Tests(object):
self.assertTrue(a.check_private_view_key(self.svk))
self.assertFalse(a.check_private_view_key(self.psk))
self.assertFalse(a.check_private_view_key(self.pvk))
self.assertFalse(a.check_private_view_key('0000000000000000000000000000000000000000000000000000000000000000'))
self.assertFalse(
a.check_private_view_key(
"0000000000000000000000000000000000000000000000000000000000000000"
)
)
def test_check_private_spend_key(self):
a = Address(self.addr)
@ -101,7 +105,11 @@ class Tests(object):
self.assertFalse(a.check_private_spend_key(self.svk))
self.assertFalse(a.check_private_spend_key(self.psk))
self.assertFalse(a.check_private_spend_key(self.pvk))
self.assertFalse(a.check_private_spend_key('0000000000000000000000000000000000000000000000000000000000000000'))
self.assertFalse(
a.check_private_spend_key(
"0000000000000000000000000000000000000000000000000000000000000000"
)
)
def test_idempotence(self):
a = Address(self.addr)
@ -124,24 +132,26 @@ class Tests(object):
self.assertRaises(ValueError, Address, self.addr_invalid)
self.assertRaises(ValueError, Address, self.iaddr_invalid)
a = Address(self.addr)
self.assertRaises(TypeError, a.with_payment_id, 2**64+1)
self.assertRaises(TypeError, a.with_payment_id, "%x" % (2**64+1))
self.assertRaises(TypeError, a.with_payment_id, 2 ** 64 + 1)
self.assertRaises(TypeError, a.with_payment_id, "%x" % (2 ** 64 + 1))
s = SubAddress(self.subaddr)
self.assertRaises(TypeError, s.with_payment_id, 0)
self.assertRaises(ValueError, address, 'whatever')
self.assertRaises(ValueError, Address, 'whatever')
self.assertRaises(ValueError, SubAddress, 'whatever')
self.assertRaises(ValueError, IntegratedAddress, 'whatever')
self.assertRaises(ValueError, address, "whatever")
self.assertRaises(ValueError, Address, "whatever")
self.assertRaises(ValueError, SubAddress, "whatever")
self.assertRaises(ValueError, IntegratedAddress, "whatever")
# Aeon
self.assertRaises(
ValueError,
address,
'Wmtj8UAJhdrhbKvwyBJmLEUZKHcffv2VHNBaq6oTxJFwJjUj3QwMUSS32mddSX7vchbxXdmb4QuZA9TsN47441f61yAYLQYTo')
"Wmtj8UAJhdrhbKvwyBJmLEUZKHcffv2VHNBaq6oTxJFwJjUj3QwMUSS32mddSX7vchbxXdmb4QuZA9TsN47441f61yAYLQYTo",
)
# invalid netbyte
self.assertRaises(
ValueError,
address,
'Cf6RinMUztY5otm6NEFjg3UWBBkXK6Lh23wKrLFMEcCY7i3A6aPLH9i4QMCkf6CdWk8Q9N7yoJf7ANKgtQMuPM6JANXgCWs')
"Cf6RinMUztY5otm6NEFjg3UWBBkXK6Lh23wKrLFMEcCY7i3A6aPLH9i4QMCkf6CdWk8Q9N7yoJf7ANKgtQMuPM6JANXgCWs",
)
def test_type_mismatch(self):
self.assertRaises(ValueError, Address, self.iaddr)
@ -157,50 +167,50 @@ class Tests(object):
class AddressTestCase(Tests, unittest.TestCase):
addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
ssk = 'e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a'
svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
psk = '9f2a76d879aaf0670039dc8dbdca01f0ca26a2f6d93268e3674666bfdc5957e4'
pvk = '716cfc7da7e6ce366935c55747839a85be798037ab189c7dd0f10b7f1690cb78'
pid = '4a6f686e47616c74'
iaddr = '4HMcpBpe4ddJEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
subaddr = '84LooD7i35SFppgf4tQ453Vi3q5WexSUXaVgut69ro8MFnmHwuezAArEZTZyLr9fS6QotjqkSAxSF6d1aDgsPoX849izJ7m'
addr = "47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef"
ssk = "e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a"
svk = "6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901"
psk = "9f2a76d879aaf0670039dc8dbdca01f0ca26a2f6d93268e3674666bfdc5957e4"
pvk = "716cfc7da7e6ce366935c55747839a85be798037ab189c7dd0f10b7f1690cb78"
pid = "4a6f686e47616c74"
iaddr = "4HMcpBpe4ddJEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw"
subaddr = "84LooD7i35SFppgf4tQ453Vi3q5WexSUXaVgut69ro8MFnmHwuezAArEZTZyLr9fS6QotjqkSAxSF6d1aDgsPoX849izJ7m"
net = const.NET_MAIN
addr_invalid = '47ewoP19TN7JCEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
iaddr_invalid = '4HMcpBpe4ddJEEnFKUJHAYhGxkyTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
addr_invalid = "47ewoP19TN7JCEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef"
iaddr_invalid = "4HMcpBpe4ddJEEnFKUJHAYhGxkyTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw"
class TestnetAddressTestCase(Tests, unittest.TestCase):
addr = '9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
ssk = '4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201'
svk = '60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103'
psk = '7cf743dcfd23d452e9b2936caeb622c9849f1ff1ddfd62bfdfac64113c1a4e92'
pvk = 'e3924b14d99a9c088e5a45278d5218f2d053b1c03c480f00ed2ee3dce80806c4'
pid = '4a6f686e47616c74'
subaddr = 'BaU3yLuDqdcETYzeF7vFSVEKNR4sSGxBV1Evrw5yNBf2VMiuAwfDmiF3RHqLHkaA5A6RGiNNRUqvtaqhMtdjA1SQ1tnQV8D'
iaddr = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
addr = "9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N"
ssk = "4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201"
svk = "60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103"
psk = "7cf743dcfd23d452e9b2936caeb622c9849f1ff1ddfd62bfdfac64113c1a4e92"
pvk = "e3924b14d99a9c088e5a45278d5218f2d053b1c03c480f00ed2ee3dce80806c4"
pid = "4a6f686e47616c74"
subaddr = "BaU3yLuDqdcETYzeF7vFSVEKNR4sSGxBV1Evrw5yNBf2VMiuAwfDmiF3RHqLHkaA5A6RGiNNRUqvtaqhMtdjA1SQ1tnQV8D"
iaddr = "A7bzU6hSszTEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh"
net = const.NET_TEST
addr_invalid = '9wuKTHsxGiwEsMp3fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
iaddr_invalid = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi2oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
addr_invalid = "9wuKTHsxGiwEsMp3fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N"
iaddr_invalid = "A7bzU6hSszTEsMp2fYzJiVahyhU2aZi2oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh"
class StagenetAddressTestCase(Tests, unittest.TestCase):
addr = '52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
ssk = 'a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503'
svk = 'fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f'
psk = '180c1d7bbf7f2e11aa90d0f61bf49024370e01cd54f33f2d36bba0357c9c205f'
pvk = '94b66a81e646927b3da74392306f789c5024734b4ce6351ad74c4c7d7351b3ad'
pid = '4a6f686e47616c74'
subaddr = '7AeQwvrLtPeYoXVPRkEu8oEL7N9wnqHjYKwSvTf6YKbHgYmw6AJMsjggzVLo21egMK9qcoV1mxCTfP4FbaGb7JEMDfpLetk'
iaddr = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'
addr = "52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6"
ssk = "a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503"
svk = "fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f"
psk = "180c1d7bbf7f2e11aa90d0f61bf49024370e01cd54f33f2d36bba0357c9c205f"
pvk = "94b66a81e646927b3da74392306f789c5024734b4ce6351ad74c4c7d7351b3ad"
pid = "4a6f686e47616c74"
subaddr = "7AeQwvrLtPeYoXVPRkEu8oEL7N9wnqHjYKwSvTf6YKbHgYmw6AJMsjggzVLo21egMK9qcoV1mxCTfP4FbaGb7JEMDfpLetk"
iaddr = "5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS"
net = const.NET_STAGE
addr_invalid = '52jzuBBUMty3xPL3JsQxGP74LDuV6H1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
iaddr_invalid = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKppEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'
addr_invalid = "52jzuBBUMty3xPL3JsQxGP74LDuV6H1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6"
iaddr_invalid = "5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKppEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS"
class KnownBugsTest(unittest.TestCase):
def test_issue27(self):
addr = '41tjz19p4qc2gudqnwsdrhgcgxud8bgxy84ufe869nyw7ywbxw9s9gqbix7piu9d7qjvbjtrdnbubhcf663ydq3bsxj1brL'
addr = "41tjz19p4qc2gudqnwsdrhgcgxud8bgxy84ufe869nyw7ywbxw9s9gqbix7piu9d7qjvbjtrdnbubhcf663ydq3bsxj1brL"
self.assertRaises(ValueError, Address, addr)
self.assertRaises(ValueError, SubAddress, addr)
self.assertRaises(ValueError, IntegratedAddress, addr)

@ -5,19 +5,19 @@ from monero.base58 import decode, encode
class Base58EncodeTestCase(unittest.TestCase):
def test_encode_empty(self):
self.assertEqual(encode(''), '')
self.assertEqual(encode(""), "")
def test_encode_invalid_hex_length(self):
with self.assertRaises(ValueError) as cm:
encode('abcde')
self.assertEqual(str(cm.exception), 'Hex string has invalid length: 5')
encode("abcde")
self.assertEqual(str(cm.exception), "Hex string has invalid length: 5")
class Base58DecodeTestCase(unittest.TestCase):
def test_decode_empty(self):
self.assertEqual(decode(''), '')
self.assertEqual(decode(""), "")
def test_decode_invalid_length_block(self):
with self.assertRaises(ValueError) as cm:
decode('f')
self.assertEqual(str(cm.exception), 'Invalid encoded length: 1')
decode("f")
self.assertEqual(str(cm.exception), "Invalid encoded length: 1")

@ -10,35 +10,41 @@ from monero.transaction import Transaction
class BlockTestCase(unittest.TestCase):
def setUp(self):
self.tx1 = Transaction(
hash="7e5fea8470c5771315bab4b3c77493d2ff534f5201c7c6b2bab069cb7d21ce7b")
hash="7e5fea8470c5771315bab4b3c77493d2ff534f5201c7c6b2bab069cb7d21ce7b"
)
self.tx2 = Transaction(
hash="3a2f859dea9d2ad5ecec167719302d4e14e21beef9b74f9583183d8e965d9106")
hash="3a2f859dea9d2ad5ecec167719302d4e14e21beef9b74f9583183d8e965d9106"
)
self.tx3 = Transaction(
hash="bde2b5344b63cbe58ce1a724d0a2276aaa4266be5235d5e5fde969446c3e8de1")
hash="bde2b5344b63cbe58ce1a724d0a2276aaa4266be5235d5e5fde969446c3e8de1"
)
self.tx4 = Transaction(
hash="24fb42f9f324082658524b29b4cf946a9f5fcfa82194070e2f17c1875e15d5d0")
hash="24fb42f9f324082658524b29b4cf946a9f5fcfa82194070e2f17c1875e15d5d0"
)
self.block1 = Block(
hash="423cd4d170c53729cf25b4243ea576d1e901d86e26c06d6a7f79815f3fcb9a89",
height=451992,
difficulty=3590,
version= (11,12),
version=(11, 12),
nonce=140046906,
orphan=False,
prev_hash="51f6816891b6a7adedd0f1ad57a846eada1baac476421aa9d32d0630ce3dce41",
reward=from_atomic(15331952645334),
timestamp=datetime.fromtimestamp(1573646422),
transactions=[self.tx1, self.tx2, self.tx3, self.tx4])
transactions=[self.tx1, self.tx2, self.tx3, self.tx4],
)
self.block1_duplicate = Block(
hash="423cd4d170c53729cf25b4243ea576d1e901d86e26c06d6a7f79815f3fcb9a89",
height=451992,
difficulty=3590,
version= (11,12),
version=(11, 12),
nonce=140046906,
orphan=False,
prev_hash="51f6816891b6a7adedd0f1ad57a846eada1baac476421aa9d32d0630ce3dce41",
reward=from_atomic(15331952645334),
timestamp=datetime.fromtimestamp(1573646422),
transactions=[self.tx1, self.tx2, self.tx3, self.tx4])
transactions=[self.tx1, self.tx2, self.tx3, self.tx4],
)
def test_basic_ops(self):
self.assertIsNot(self.block1, self.block1_duplicate)

@ -1,8 +1,9 @@
import unittest
from monero import ed25519
class Ed25519TestCase(unittest.TestCase):
def test_comp_decomp(self):
pts = [(0,0), (1,1), (3,5), (5,3), (7,11), (11,7)]
pts = [(0, 0), (1, 1), (3, 5), (5, 3), (7, 11), (11, 7)]
for p in pts:
self.assertEqual(p, ed25519.compress(ed25519.decompress(p)))

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -4,47 +4,52 @@ import unittest
from monero.numbers import to_atomic, from_atomic, as_monero, PaymentID
class NumbersTestCase(unittest.TestCase):
def test_simple_numbers(self):
self.assertEqual(to_atomic(Decimal('0')), 0)
self.assertEqual(from_atomic(0), Decimal('0'))
self.assertEqual(to_atomic(Decimal('1')), 1000000000000)
self.assertEqual(from_atomic(1000000000000), Decimal('1'))
self.assertEqual(to_atomic(Decimal('0.000000000001')), 1)
self.assertEqual(from_atomic(1), Decimal('0.000000000001'))
self.assertEqual(to_atomic(Decimal("0")), 0)
self.assertEqual(from_atomic(0), Decimal("0"))
self.assertEqual(to_atomic(Decimal("1")), 1000000000000)
self.assertEqual(from_atomic(1000000000000), Decimal("1"))
self.assertEqual(to_atomic(Decimal("0.000000000001")), 1)
self.assertEqual(from_atomic(1), Decimal("0.000000000001"))
def test_numeric_types(self):
"Only check if conversion of given type succeeds or fails."
self.assertTrue(to_atomic(1))
self.assertTrue(to_atomic(1.0))
if hasattr(sys, 'maxint'): # Python 2.x
if hasattr(sys, "maxint"): # Python 2.x
self.assertTrue(to_atomic(sys.maxint + 1))
self.assertRaises(ValueError, to_atomic, '1')
self.assertRaises(ValueError, to_atomic, "1")
def test_rounding(self):
self.assertEqual(to_atomic(Decimal('1.0000000000004')), 1000000000000)
self.assertEqual(as_monero(Decimal('1.0000000000014')), Decimal('1.000000000001'))
self.assertEqual(to_atomic(Decimal("1.0000000000004")), 1000000000000)
self.assertEqual(
as_monero(Decimal("1.0000000000014")), Decimal("1.000000000001")
)
def test_payment_id(self):
pid = PaymentID('0')
pid = PaymentID("0")
self.assertTrue(pid.is_short())
self.assertEqual(pid, 0)
self.assertEqual(pid, '0000000000000000')
self.assertEqual(pid, "0000000000000000")
self.assertEqual(PaymentID(pid), pid)
self.assertNotEqual(pid, None)
pid = PaymentID('abcdef')
pid = PaymentID("abcdef")
self.assertTrue(pid.is_short())
self.assertEqual(pid, 0xabcdef)
self.assertEqual(pid, '0000000000abcdef')
self.assertEqual(pid, 0xABCDEF)
self.assertEqual(pid, "0000000000abcdef")
self.assertEqual(PaymentID(pid), pid)
pid = PaymentID('1234567812345678')
pid = PaymentID("1234567812345678")
self.assertTrue(pid.is_short())
self.assertEqual(pid, 0x1234567812345678)
self.assertEqual(pid, '1234567812345678')
self.assertEqual(pid, "1234567812345678")
self.assertEqual(PaymentID(pid), pid)
pid = PaymentID('a1234567812345678')
pid = PaymentID("a1234567812345678")
self.assertFalse(pid.is_short())
self.assertEqual(pid, 0xa1234567812345678)
self.assertEqual(pid, '00000000000000000000000000000000000000000000000a1234567812345678')
self.assertEqual(pid, 0xA1234567812345678)
self.assertEqual(
pid, "00000000000000000000000000000000000000000000000a1234567812345678"
)
self.assertEqual(PaymentID(pid), pid)
self.assertRaises(ValueError, PaymentID, 2**256+1)
self.assertRaises(ValueError, PaymentID, 2 ** 256 + 1)

@ -7,8 +7,8 @@ from .base import JSONTestCase
class OfflineTest(unittest.TestCase):
addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
addr = "47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef"
svk = "6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901"
def setUp(self):
self.wallet = Wallet(OfflineWallet(self.addr, view_key=self.svk))
@ -18,64 +18,72 @@ class OfflineTest(unittest.TestCase):
self.assertRaises(WalletIsOffline, self.wallet.new_account)
self.assertRaises(WalletIsOffline, self.wallet.new_address)
self.assertRaises(WalletIsOffline, self.wallet.export_outputs)
self.assertRaises(WalletIsOffline, self.wallet.import_outputs, '')
self.assertRaises(WalletIsOffline, self.wallet.import_outputs, "")
self.assertRaises(WalletIsOffline, self.wallet.export_key_images)
self.assertRaises(WalletIsOffline, self.wallet.import_key_images, '')
self.assertRaises(WalletIsOffline, self.wallet.import_key_images, "")
self.assertRaises(WalletIsOffline, self.wallet.balances)
self.assertRaises(WalletIsOffline, self.wallet.balance)
self.assertRaises(WalletIsOffline, self.wallet.incoming)
self.assertRaises(WalletIsOffline, self.wallet.outgoing)
self.assertRaises(WalletIsOffline, self.wallet.transfer, self.wallet.get_address(1,0), 1)
self.assertRaises(WalletIsOffline, self.wallet.transfer_multiple,
[(self.wallet.get_address(1,0), 1), (self.wallet.get_address(1,1), 2)])
self.assertRaises(
WalletIsOffline, self.wallet.transfer, self.wallet.get_address(1, 0), 1
)
self.assertRaises(
WalletIsOffline,
self.wallet.transfer_multiple,
[(self.wallet.get_address(1, 0), 1), (self.wallet.get_address(1, 1), 2)],
)
class SubaddrTest(object):
data_subdir = 'test_offline'
data_subdir = "test_offline"
def setUp(self):
self.wallet = Wallet(OfflineWallet(self.addr, view_key=self.svk, spend_key=self.ssk))
self.wallet = Wallet(
OfflineWallet(self.addr, view_key=self.svk, spend_key=self.ssk)
)
def test_keys(self):
self.assertEqual(self.wallet.spend_key(), self.ssk)
self.assertEqual(self.wallet.view_key(), self.svk)
self.assertEqual(25, len(self.wallet.seed().phrase.split(' ')))
self.assertEqual(25, len(self.wallet.seed().phrase.split(" ")))
def test_subaddresses(self):
major = 0
for acc in self._read('{}-subaddrs.json'.format(self.net)):
for acc in self._read("{}-subaddrs.json".format(self.net)):
minor = 0
for subaddr in acc:
self.assertEqual(
self.wallet.get_address(major, minor),
subaddr,
msg='major={}, minor={}'.format(major,minor))
self.wallet.get_address(major, minor),
subaddr,
msg="major={}, minor={}".format(major, minor),
)
minor += 1
major += 1
class AddressTestCase(SubaddrTest, JSONTestCase):
addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
ssk = 'e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a'
svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
net = 'mainnet'
addr = "47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef"
ssk = "e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a"
svk = "6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901"
net = "mainnet"
def test_subaddress_out_of_range(self):
self.assertRaises(ValueError, self.wallet.get_address, 0, -1)
self.assertRaises(ValueError, self.wallet.get_address, -1, 0)
self.assertRaises(ValueError, self.wallet.get_address, 1, 2**32)
self.assertRaises(ValueError, self.wallet.get_address, 2**32, 1)
self.assertRaises(ValueError, self.wallet.get_address, 1, 2 ** 32)
self.assertRaises(ValueError, self.wallet.get_address, 2 ** 32, 1)
class TestnetAddressTestCase(SubaddrTest, JSONTestCase):
addr = '9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
ssk = '4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201'
svk = '60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103'
net = 'testnet'
addr = "9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N"
ssk = "4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201"
svk = "60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103"
net = "testnet"
class StagenetAddressTestCase(SubaddrTest, JSONTestCase):
addr = '52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
ssk = 'a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503'
svk = 'fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f'
net = 'stagenet'
addr = "52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6"
ssk = "a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503"
svk = "fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f"
net = "stagenet"

@ -1,5 +1,6 @@
from decimal import Decimal
import json
try:
from unittest.mock import patch, Mock
except ImportError:
@ -15,6 +16,7 @@ from monero.wallet import Wallet
from .base import JSONTestCase
class OutputTestCase(JSONTestCase):
data_subdir = "test_outputs"
daemon_transactions_url = "http://127.0.0.1:38081/get_transactions"
@ -23,43 +25,66 @@ class OutputTestCase(JSONTestCase):
@responses.activate
def test_multiple_outputs(self):
daemon = Daemon(JSONRPCDaemon(host="127.0.0.1", port=38081))
responses.add(responses.POST, self.wallet_jsonrpc_url,
responses.add(
responses.POST,
self.wallet_jsonrpc_url,
json=self._read("test_multiple_outputs-wallet-00-get_accounts.json"),
status=200)
responses.add(responses.POST, self.wallet_jsonrpc_url,
status=200,
)
responses.add(
responses.POST,
self.wallet_jsonrpc_url,
json=self._read("test_multiple_outputs-wallet-01-query_key.json"),
status=200)
responses.add(responses.POST, self.wallet_jsonrpc_url,
status=200,
)
responses.add(
responses.POST,
self.wallet_jsonrpc_url,
json=self._read("test_multiple_outputs-wallet-02-addresses-account-0.json"),
status=200)
responses.add(responses.POST, self.wallet_jsonrpc_url,
status=200,
)
responses.add(
responses.POST,
self.wallet_jsonrpc_url,
json=self._read("test_multiple_outputs-wallet-02-addresses-account-1.json"),
status=200)
status=200,
)
wallet = Wallet(JSONRPCWallet(host="127.0.0.1", port=38083))
responses.add(responses.POST, self.daemon_transactions_url,
responses.add(
responses.POST,
self.daemon_transactions_url,
json=self._read("test_multiple_outputs-daemon-00-get_transactions.json"),
status=200)
status=200,
)
tx = daemon.transactions(
"f79a10256859058b3961254a35a97a3d4d5d40e080c6275a3f9779acde73ca8d")[0]
"f79a10256859058b3961254a35a97a3d4d5d40e080c6275a3f9779acde73ca8d"
)[0]
outs = tx.outputs(wallet=wallet)
self.assertEqual(len(outs), 5)
self.assertEqual(
outs[0].stealth_address,
"d3eb42322566c1d48685ee0d1ad7aed2ba6210291a785ec051d8b13ae797d202")
outs[0].stealth_address,
"d3eb42322566c1d48685ee0d1ad7aed2ba6210291a785ec051d8b13ae797d202",
)
self.assertEqual(
outs[1].stealth_address,
"5bda44d7953e27b84022399850b59ed87408facdf00bbd1a2d4fda4bf9ebf72f")
outs[1].stealth_address,
"5bda44d7953e27b84022399850b59ed87408facdf00bbd1a2d4fda4bf9ebf72f",
)
self.assertEqual(
outs[2].stealth_address,
"4c79c14d5d78696e72959a28a734ec192059ebabb931040b5a0714c67b507e76")
outs[2].stealth_address,
"4c79c14d5d78696e72959a28a734ec192059ebabb931040b5a0714c67b507e76",
)
self.assertEqual(
outs[3].stealth_address,
"64de2b358cdf96d498a9688edafcc0e25c60179e813304747524c876655a8e55")
outs[3].stealth_address,
"64de2b358cdf96d498a9688edafcc0e25c60179e813304747524c876655a8e55",
)
self.assertEqual(
outs[4].stealth_address,
"966240954892294091a48c599c6db2b028e265c67677ed113d2263a7538f9a43")
outs[4].stealth_address,
"966240954892294091a48c599c6db2b028e265c67677ed113d2263a7538f9a43",
)
self.assertIsNotNone(outs[0].payment)
self.assertIsNone(outs[1].payment) # FIXME: isn't that change we should recognize?
self.assertIsNone(
outs[1].payment
) # FIXME: isn't that change we should recognize?
self.assertIsNotNone(outs[2].payment)
self.assertIsNotNone(outs[3].payment)
self.assertIsNotNone(outs[4].payment)
@ -73,21 +98,26 @@ class OutputTestCase(JSONTestCase):
self.assertEqual(outs[3].amount, Decimal(2))
self.assertEqual(outs[4].amount, Decimal(8))
self.assertEqual(
outs[0].payment.local_address,
"76Qt2xMZ3m7b2tagubEgkvG81pwf9P3JYdxR65H2BEv8c79A9pCBTacEFv87tfdcqXRemBsZLFVGHTWbqBpkoBJENBoJJS9")
outs[0].payment.local_address,
"76Qt2xMZ3m7b2tagubEgkvG81pwf9P3JYdxR65H2BEv8c79A9pCBTacEFv87tfdcqXRemBsZLFVGHTWbqBpkoBJENBoJJS9",
)
self.assertEqual(
outs[2].payment.local_address,
"78zGgzb45TEL8uvRFjCayUjHS98RFry1f7P4PE4LU7oeLh42s9AtP8fYXVzWqUW4r3Nz4g3V64w9RSiV7o3zUbPZVs5DVaU")
outs[2].payment.local_address,
"78zGgzb45TEL8uvRFjCayUjHS98RFry1f7P4PE4LU7oeLh42s9AtP8fYXVzWqUW4r3Nz4g3V64w9RSiV7o3zUbPZVs5DVaU",
)
self.assertEqual(
outs[3].payment.local_address,
"73ndji4W2bu4WED87rJDVALMvUsZLLYstZsigbcGfb5YG9SuNyCSYk7Qbttez2mXciKtWRzRN9aYGJbF9TPBidNQNZppnFw")
outs[3].payment.local_address,
"73ndji4W2bu4WED87rJDVALMvUsZLLYstZsigbcGfb5YG9SuNyCSYk7Qbttez2mXciKtWRzRN9aYGJbF9TPBidNQNZppnFw",
)
self.assertEqual(
outs[4].payment.local_address,
"7BJxHKTa4p5USJ9Z5GY15ZARXL6Qe84qT3FnWkMbSJSoEj9ugGjnpQ1N9H1jqkjsTzLiN5VTbCP8f4MYYVPAcXhr36bHXzP")
outs[4].payment.local_address,
"7BJxHKTa4p5USJ9Z5GY15ZARXL6Qe84qT3FnWkMbSJSoEj9ugGjnpQ1N9H1jqkjsTzLiN5VTbCP8f4MYYVPAcXhr36bHXzP",
)
self.assertEqual(
repr(outs[0]),
"d3eb42322566c1d48685ee0d1ad7aed2ba6210291a785ec051d8b13ae797d202, 4.000000000000 "
"to [76Qt2x]")
repr(outs[0]),
"d3eb42322566c1d48685ee0d1ad7aed2ba6210291a785ec051d8b13ae797d202, 4.000000000000 "
"to [76Qt2x]",
)
def test_coinbase_no_own_output(self):
txdata = self._read("test_coinbase_no_own_output-26dcb5.json")
@ -95,11 +125,15 @@ class OutputTestCase(JSONTestCase):
hash="26dcb55c3c93a2176949fd9ec4e20a9d97ece7c420408d9353c390a909e9a7c1",
height=766459,
output_indices=txdata["output_indices"],
json=json.loads(txdata["as_json"]))
json=json.loads(txdata["as_json"]),
)
self.assertTrue(tx.is_coinbase)
wallet = Wallet(OfflineWallet(
address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj",
view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a"))
wallet = Wallet(
OfflineWallet(
address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj",
view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a",
)
)
outs = tx.outputs(wallet=wallet)
self.assertEqual(len(outs), 1)
self.assertIsNone(outs[0].payment)
@ -111,17 +145,22 @@ class OutputTestCase(JSONTestCase):
tx = Transaction(
hash="dc08610685b8a55dc7d64454ecbe12868e4e73c766e2d19ee092885a06fc092d",
height=518147,
json=txdata)
json=txdata,
)
self.assertTrue(tx.is_coinbase)
wallet = Wallet(OfflineWallet(
address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj",
view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a"))
wallet = Wallet(
OfflineWallet(
address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj",
view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a",
)
)
outs = tx.outputs(wallet=wallet)
self.assertEqual(len(outs), 1)
self.assertIsNotNone(outs[0].payment)
self.assertEqual(
outs[0].payment.local_address,
"56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj")
"56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj",
)
self.assertEqual(outs[0].amount, outs[0].payment.amount)
self.assertEqual(outs[0].payment.amount, Decimal("13.515927959357"))
@ -129,69 +168,144 @@ class OutputTestCase(JSONTestCase):
tx1 = Transaction(
hash="2634445086cc48b89f1cd241e89e6f37195008807264684d8fad4a16f479c45a",
height=2022660,
json=self._read("test_v1_tx-263444.json"))
json=self._read("test_v1_tx-263444.json"),
)
tx2 = Transaction(
hash="3586a81f051bcb265a45c99f11b19fc4b55bb2abb3332c515a8b88a559cd9f7b",
height=2022660,
json=self._read("test_v1_tx-3586a8.json"))
json=self._read("test_v1_tx-3586a8.json"),
)
outs1 = tx1.outputs()
self.assertEqual(len(outs1), 14)
self.assertEqual(outs1[0].stealth_address, "b1ef76960fe245f73131be22e9b548e861f93b727ab8a2a3ff64d86521512382")
self.assertEqual(
outs1[0].stealth_address,
"b1ef76960fe245f73131be22e9b548e861f93b727ab8a2a3ff64d86521512382",
)
self.assertEqual(outs1[0].amount, Decimal("0.000000000300"))
self.assertEqual(outs1[1].stealth_address, "dcd66bbcb6e72602dd876e1dad65a3464a2bd831f09ec7c8131147315152e29b")
self.assertEqual(
outs1[1].stealth_address,
"dcd66bbcb6e72602dd876e1dad65a3464a2bd831f09ec7c8131147315152e29b",
)
self.assertEqual(outs1[1].amount, Decimal("0.000008000000"))
self.assertEqual(outs1[2].stealth_address, "71efdb68dfd33f5c89a5fa8312ec6e346681f6f60fb406e9426231a5f230351a")
self.assertEqual(
outs1[2].stealth_address,
"71efdb68dfd33f5c89a5fa8312ec6e346681f6f60fb406e9426231a5f230351a",
)
self.assertEqual(outs1[2].amount, Decimal("0.007000000000"))
self.assertEqual(outs1[3].stealth_address, "499fb727f61f2ce0fbc3419b309601f2cbf672eeef2cc827aef423b0b70e2529")
self.assertEqual(
outs1[3].stealth_address,
"499fb727f61f2ce0fbc3419b309601f2cbf672eeef2cc827aef423b0b70e2529",
)
self.assertEqual(outs1[3].amount, Decimal("0.000000010000"))
self.assertEqual(outs1[4].stealth_address, "297ef9bb654dd6e26472a4f07f037eddb3f8b458cf4315e2cc40d9fd725e28b9")
self.assertEqual(
outs1[4].stealth_address,
"297ef9bb654dd6e26472a4f07f037eddb3f8b458cf4315e2cc40d9fd725e28b9",
)
self.assertEqual(outs1[4].amount, Decimal("0.000000500000"))
self.assertEqual(outs1[5].stealth_address, "b2bf18a500afe1775305b19d16d0d5afec0f72096b9f15cca6604d7f5ad6e5f8")
self.assertEqual(
outs1[5].stealth_address,
"b2bf18a500afe1775305b19d16d0d5afec0f72096b9f15cca6604d7f5ad6e5f8",
)
self.assertEqual(outs1[5].amount, Decimal("0.300000000000"))
self.assertEqual(outs1[6].stealth_address, "f7a95b33912077e3aca425270f76be13af503919b6230368a591e1053b3c7436")
self.assertEqual(
outs1[6].stealth_address,
"f7a95b33912077e3aca425270f76be13af503919b6230368a591e1053b3c7436",
)
self.assertEqual(outs1[6].amount, Decimal("5.000000000000"))
self.assertEqual(outs1[7].stealth_address, "1e93e243a865b71e14fe4df6de0902ca634749b48002c52adc7f046053c2b921")
self.assertEqual(
outs1[7].stealth_address,
"1e93e243a865b71e14fe4df6de0902ca634749b48002c52adc7f046053c2b921",
)
self.assertEqual(outs1[7].amount, Decimal("0.000200000000"))
self.assertEqual(outs1[8].stealth_address, "513822bad9697e8494ff82cb4b58a5a693aa433c16f0aafdaaf4a27b026a32e4")
self.assertEqual(
outs1[8].stealth_address,
"513822bad9697e8494ff82cb4b58a5a693aa433c16f0aafdaaf4a27b026a32e4",
)
self.assertEqual(outs1[8].amount, Decimal("0.000000000009"))
self.assertEqual(outs1[9].stealth_address, "6e1ace4cfdf3f5363d72c241382e3b9927af1093b549a62f2902f56137d153bc")
self.assertEqual(
outs1[9].stealth_address,
"6e1ace4cfdf3f5363d72c241382e3b9927af1093b549a62f2902f56137d153bc",
)
self.assertEqual(outs1[9].amount, Decimal("0.000000000070"))
self.assertEqual(outs1[10].stealth_address, "1df18bd04f42c9da8f6b49afe418aabc8ab973448a941d365534b5d0862a3d46")
self.assertEqual(
outs1[10].stealth_address,
"1df18bd04f42c9da8f6b49afe418aabc8ab973448a941d365534b5d0862a3d46",
)
self.assertEqual(outs1[10].amount, Decimal("0.000000002000"))
self.assertEqual(outs1[11].stealth_address, "caf3e6c07f8172fc31a56ba7f541ba8d6cc601f2c7da1a135126f8f3455e3ffc")
self.assertEqual(
outs1[11].stealth_address,
"caf3e6c07f8172fc31a56ba7f541ba8d6cc601f2c7da1a135126f8f3455e3ffc",
)
self.assertEqual(outs1[11].amount, Decimal("20.000000000000"))
self.assertEqual(outs1[12].stealth_address, "1ce506bc1ee041dfe36df3e085156023be26e133fb14f5e529b60a2d769a7c7c")
self.assertEqual(
outs1[12].stealth_address,
"1ce506bc1ee041dfe36df3e085156023be26e133fb14f5e529b60a2d769a7c7c",
)
self.assertEqual(outs1[12].amount, Decimal("0.000030000000"))
self.assertEqual(outs1[13].stealth_address, "ee1a22b1f49db4df0df56161801974326cda4ceacbbf2a17c795ebe945790281")
self.assertEqual(
outs1[13].stealth_address,
"ee1a22b1f49db4df0df56161801974326cda4ceacbbf2a17c795ebe945790281",
)
self.assertEqual(outs1[13].amount, Decimal("0.030000000000"))
outs2 = tx2.outputs()
self.assertEqual(len(outs2), 10)
self.assertEqual(outs2[0].stealth_address, "ddd1d47e5d419cf5e2298e4d9e828364b929976912dfc1bbed25fb20cc681f9f")
self.assertEqual(
outs2[0].stealth_address,
"ddd1d47e5d419cf5e2298e4d9e828364b929976912dfc1bbed25fb20cc681f9f",
)
self.assertEqual(outs2[0].amount, Decimal("3.000000000000"))
self.assertEqual(outs2[1].stealth_address, "a0c0edc478a3448a0d371755bd614854505d2f158499d9881bfffa8b05c5b3e8")
self.assertEqual(
outs2[1].stealth_address,
"a0c0edc478a3448a0d371755bd614854505d2f158499d9881bfffa8b05c5b3e8",
)
self.assertEqual(outs2[1].amount, Decimal("0.600000000000"))
self.assertEqual(outs2[2].stealth_address, "f9aeb5f16117f363adcd22f6b73d6e35eda64c25fee2f59208bd68d411b6d0c6")
self.assertEqual(
outs2[2].stealth_address,
"f9aeb5f16117f363adcd22f6b73d6e35eda64c25fee2f59208bd68d411b6d0c6",
)
self.assertEqual(outs2[2].amount, Decimal("0.000000000700"))
self.assertEqual(outs2[3].stealth_address, "17e36384cf11a4d85be1320c0e221505818edbb2d6634dd54db24e25570d0f75")
self.assertEqual(
outs2[3].stealth_address,
"17e36384cf11a4d85be1320c0e221505818edbb2d6634dd54db24e25570d0f75",
)
self.assertEqual(outs2[3].amount, Decimal("0.000000500000"))
self.assertEqual(outs2[4].stealth_address, "8b7e5dac3e0e45f9e7213ec3d4a465c5301b20f8ef30a5b2b5baba80867952b3")
self.assertEqual(
outs2[4].stealth_address,
"8b7e5dac3e0e45f9e7213ec3d4a465c5301b20f8ef30a5b2b5baba80867952b3",
)
self.assertEqual(outs2[4].amount, Decimal("0.000000000070"))
self.assertEqual(outs2[5].stealth_address, "d1e24eeaa62232cb0e4be536fc785e03075416457dd2b704437bced16da52500")
self.assertEqual(
outs2[5].stealth_address,
"d1e24eeaa62232cb0e4be536fc785e03075416457dd2b704437bced16da52500",
)
self.assertEqual(outs2[5].amount, Decimal("0.000000001000"))
self.assertEqual(outs2[6].stealth_address, "52c26fcce9d0a41f91ec57074e2cbfe301ca96b556e861deba51cd54e3e5b3e3")
self.assertEqual(
outs2[6].stealth_address,
"52c26fcce9d0a41f91ec57074e2cbfe301ca96b556e861deba51cd54e3e5b3e3",
)
self.assertEqual(outs2[6].amount, Decimal("0.000010000000"))
self.assertEqual(outs2[7].stealth_address, "c5859574278889dede61d5aa341e14d2fb2acf45941486276f61dd286e7f8895")
self.assertEqual(
outs2[7].stealth_address,
"c5859574278889dede61d5aa341e14d2fb2acf45941486276f61dd286e7f8895",
)
self.assertEqual(outs2[7].amount, Decimal("0.000000010000"))
self.assertEqual(outs2[8].stealth_address, "a3556072b7c8f77abdd16fe762fe1099c10c5ab071e16075ce0c667a3eacf1cc")
self.assertEqual(
outs2[8].stealth_address,
"a3556072b7c8f77abdd16fe762fe1099c10c5ab071e16075ce0c667a3eacf1cc",
)
self.assertEqual(outs2[8].amount, Decimal("0.090000000000"))
self.assertEqual(outs2[9].stealth_address, "d72affedd142c6a459c42318169447f22042dba0d93c0f7ade42ddb222de8914")
self.assertEqual(
outs2[9].stealth_address,
"d72affedd142c6a459c42318169447f22042dba0d93c0f7ade42ddb222de8914",
)
self.assertEqual(outs2[9].amount, Decimal("0.009000000000"))
def test_extra_unknown_tag(self):
# try initializing as string
ep = ExtraParser(
"0169858d0d6de79c2dfd94b3f97745a12c9a7a61ffbae16b7a34bbf5b36b75084302086003c919"
"1772d1f90300786a796a227d07e9ae41ff9248a6b2e55adb3f6a42eb4c7ccc1c1b3d0f42c524")
"1772d1f90300786a796a227d07e9ae41ff9248a6b2e55adb3f6a42eb4c7ccc1c1b3d0f42c524"
)
with self.assertRaises(ValueError):
pdata = ep.parse()
@ -199,6 +313,7 @@ class OutputTestCase(JSONTestCase):
ep = ExtraParser(
b"015894c0e5a8d376e931df4b4ae45b753d9442de52ec8d94036253fba5aeff9782020901cb0e5e"
b"5a80a4e135ff301ea13334dfbe1508dafcaa32762a86cf12cd4fd193ee9807edcb91bc87f6ccb6"
b"02384b54dff4664b232a058b8d28ad7d")
b"02384b54dff4664b232a058b8d28ad7d"
)
with self.assertRaises(ValueError):
pdata = ep.parse()

@ -6,33 +6,51 @@ from monero.address import Address
from monero.seed import Seed
from monero.wordlists import list_wordlists
class SeedTestCase(unittest.TestCase):
class SeedTestCase(unittest.TestCase):
def test_mnemonic_seed(self):
# Known good 25 word seed phrases should construct a class and register valid hex
seed = Seed("wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square")
self.assertEqual(seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06")
seed = Seed(
"wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square"
)
self.assertEqual(
seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06"
)
# Known good 24 word seed phrases should construct a class, store phrase, and register valid hex
seed = Seed("wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding")
self.assertEqual(seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06")
seed = Seed(
"wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding"
)
self.assertEqual(
seed.hex, "8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06"
)
# Known good 25 word hexadecimal strings should construct a class, store phrase, and register valid hex
seed = Seed("8ffa9f586b86d294d93731765d192765311bddc76a4fa60311f8af36bbf6fb06")
self.assertEqual(seed.phrase, "wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square")
self.assertEqual(
seed.phrase,
"wedge going quick racetrack auburn physics lectures light waist axes whipped habitat square awkward together injury niece nugget guarded hive obnoxious waxing faked folding square",
)
self.assertTrue(len(seed.hex) % 8 == 0)
# Known good 13 word seed phrases should construct a class and register valid hex
seed = Seed("ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls")
seed = Seed(
"ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls"
)
self.assertEqual(seed.hex, "932d70711acc2d536ca11fcb79e05516")
# Known good 12 word seed phrases should construct a class, store phrase, and register valid hex
seed = Seed("ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin")
seed = Seed(
"ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin"
)
self.assertEqual(seed.hex, "932d70711acc2d536ca11fcb79e05516")
# Known good 13 word hexadecimal strings should construct a class, store phrase, and register valid hex
seed = Seed("932d70711acc2d536ca11fcb79e05516")
self.assertEqual(seed.phrase, "ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls")
self.assertEqual(
seed.phrase,
"ought knowledge upright innocent eldest nerves gopher fowls below exquisite aces basin fowls",
)
self.assertTrue(len(seed.hex) % 8 == 0)
# Generated seed phrases should be 25 words, register valid hex
@ -55,47 +73,59 @@ class SeedTestCase(unittest.TestCase):
Seed("\\x008")
self.assertEqual(ts.expected, ValueError)
def test_keys(self):
seed = Seed("adjust mugged vaults atlas nasty mews damp toenail suddenly toxic possible "\
"framed succeed fuzzy return demonstrate nucleus album noises peculiar virtual "\
"rowboat inorganic jester fuzzy")
seed = Seed(
"adjust mugged vaults atlas nasty mews damp toenail suddenly toxic possible "
"framed succeed fuzzy return demonstrate nucleus album noises peculiar virtual "
"rowboat inorganic jester fuzzy"
)
self.assertFalse(seed.is_mymonero())
self.assertEqual(
seed.secret_spend_key(),
'482700617ba810f94035d7f4d7ccc1a29878e165b4867872b705204c85406906')
"482700617ba810f94035d7f4d7ccc1a29878e165b4867872b705204c85406906",
)
self.assertEqual(
seed.secret_view_key(),
'09ed72c713d3e9e19bef2f5204cf85f6cb25de7842aa0722abeb12697f171903')
"09ed72c713d3e9e19bef2f5204cf85f6cb25de7842aa0722abeb12697f171903",
)
self.assertEqual(
seed.public_spend_key(),
'4ee576f52b9c6a824a3d5c2832d117177d2bb9992507c2c78788bb8dbaf4b640')
"4ee576f52b9c6a824a3d5c2832d117177d2bb9992507c2c78788bb8dbaf4b640",
)
self.assertEqual(
seed.public_view_key(),
'e1ef99d66312ec0b16b17c66c591ab59594e21621588b63b62fa69fe615a768e')
"e1ef99d66312ec0b16b17c66c591ab59594e21621588b63b62fa69fe615a768e",
)
self.assertEqual(
seed.public_address(),
'44cWztNFdAqNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH4YtKdH')
"44cWztNFdAqNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH4YtKdH",
)
self.assertIsInstance(seed.public_address(), Address)
self.assertEqual(
seed.public_address(net='stage'),
'54pZ5jHDGmwNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH6cuARW')
self.assertIsInstance(seed.public_address(net='stage'), Address)
seed.public_address(net="stage"),
"54pZ5jHDGmwNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH6cuARW",
)
self.assertIsInstance(seed.public_address(net="stage"), Address)
seed = Seed("dwelt idols lopped blender haggled rabbits piloted value swagger taunts toolbox upgrade swagger")
seed = Seed(
"dwelt idols lopped blender haggled rabbits piloted value swagger taunts toolbox upgrade swagger"
)
self.assertTrue(seed.is_mymonero())
# check if the same seed without checksum matches the hex
self.assertEqual(seed.hex, Seed(" ".join(seed.phrase.split(" ")[:12])).hex)
# the following fails, #21 addresses that
self.assertEqual(
seed.secret_spend_key(),
'a67505f92004dd6242b64acd16e34ecf788a2d28b6072091e054238d84591403')
"a67505f92004dd6242b64acd16e34ecf788a2d28b6072091e054238d84591403",
)
self.assertEqual(
seed.secret_view_key(),
'83f652cb370948c8cbcf06839df043aa8c0d0ed36e38b3c827c4c00370af1a0f')
"83f652cb370948c8cbcf06839df043aa8c0d0ed36e38b3c827c4c00370af1a0f",
)
self.assertEqual(
seed.public_address(),
'47dwi1w9it69yZyTBBRD52ctQqw3B2FZx79bCEgVUKGHH2m7MjmaXrjeQfchMMkarG6AF9a36JvBWCyRaqEcUixpKLQRxdj')
"47dwi1w9it69yZyTBBRD52ctQqw3B2FZx79bCEgVUKGHH2m7MjmaXrjeQfchMMkarG6AF9a36JvBWCyRaqEcUixpKLQRxdj",
)
self.assertIsInstance(seed.public_address(), Address)
def test_languages(self):
@ -114,187 +144,266 @@ class SeedTestCase(unittest.TestCase):
self.assertEqual(seed.phrase, seed_from_hex.phrase)
def test_chinese_simplified(self):
seed = Seed(u"遭 牲 本 点 司 司 仲 吉 虎 只 绝 生 指 纯 伟 破 夫 惊 群 楚 祥 旋 暗 骨 伟", "Chinese (simplified)")
seed = Seed(
u"遭 牲 本 点 司 司 仲 吉 虎 只 绝 生 指 纯 伟 破 夫 惊 群 楚 祥 旋 暗 骨 伟", "Chinese (simplified)"
)
self.assertEqual(
seed.secret_spend_key(),
'2ec46011b23b0c00468946f1d9a64995bf0a89f9ee0bbf4f64058a3acd81a70e')
"2ec46011b23b0c00468946f1d9a64995bf0a89f9ee0bbf4f64058a3acd81a70e",
)
self.assertEqual(
seed.secret_view_key(),
'aa141796baa24539583306300b44a72495bb7823a0cc6ad856de6d372288d10f')
"aa141796baa24539583306300b44a72495bb7823a0cc6ad856de6d372288d10f",
)
self.assertEqual(
seed.public_spend_key(),
'76cc3b927e70fee85a43a6141d019b53c77f46bbcd6c4dc6d814dfc271af361c')
"76cc3b927e70fee85a43a6141d019b53c77f46bbcd6c4dc6d814dfc271af361c",
)
self.assertEqual(
seed.public_view_key(),
'91ef3783492e173ca366a818ae7ee37f062daea909fd9ed9ca40d41e7d572dd4')
"91ef3783492e173ca366a818ae7ee37f062daea909fd9ed9ca40d41e7d572dd4",
)
self.assertEqual(
seed.public_address(),
'468Dewci4TPfs7TATZ2nf4F1mKAEMp6RraG37wiSU4uT5nAbBwGz5LaB9GWHG23o6ANFJ1Q9cBYk5dRqWNNkmFN4Qx3RqBD')
"468Dewci4TPfs7TATZ2nf4F1mKAEMp6RraG37wiSU4uT5nAbBwGz5LaB9GWHG23o6ANFJ1Q9cBYk5dRqWNNkmFN4Qx3RqBD",
)
def test_dutch(self):
seed = Seed(u"ralf tolvrij copier roon ossuarium wedstrijd splijt debbie bomtapijt occlusie oester noren hiaat scenario geshockt veeteler rotten symboliek jarig bock yoghurt plegen weert zeeblauw wedstrijd", "Dutch")
seed = Seed(
u"ralf tolvrij copier roon ossuarium wedstrijd splijt debbie bomtapijt occlusie oester noren hiaat scenario geshockt veeteler rotten symboliek jarig bock yoghurt plegen weert zeeblauw wedstrijd",
"Dutch",
)
self.assertEqual(
seed.secret_spend_key(),
'600d3c5022e1844dd2df02f178a074fc2e566793e99d9e1465926adcbfa9b508')
"600d3c5022e1844dd2df02f178a074fc2e566793e99d9e1465926adcbfa9b508",
)
self.assertEqual(
seed.secret_view_key(),
'bb8984647124dafcb8682f1c257b5232bb12b96d682bfc320b4f8ce935e2d303')
"bb8984647124dafcb8682f1c257b5232bb12b96d682bfc320b4f8ce935e2d303",
)
self.assertEqual(
seed.public_spend_key(),
'df4be25f7ccaf632f1525b06fd9b0d7e9f64b21ebfb609353d643a24de16221b')
"df4be25f7ccaf632f1525b06fd9b0d7e9f64b21ebfb609353d643a24de16221b",
)
self.assertEqual(
seed.public_view_key(),
'2fcd275e4337152ea77ac68ec02f166a243f4917ebd53b2a381ab27b84d24065')
"2fcd275e4337152ea77ac68ec02f166a243f4917ebd53b2a381ab27b84d24065",
)
self.assertEqual(
seed.public_address(),
'4A5uCL4cXoB9XD3WjTrEwvNBQ6JRPTHaY9uVaxfWmcLy5YkE81tW7B28oc42XGzAeRJkhyHjKAxSE84aZnihjVBVCQf15mw')
"4A5uCL4cXoB9XD3WjTrEwvNBQ6JRPTHaY9uVaxfWmcLy5YkE81tW7B28oc42XGzAeRJkhyHjKAxSE84aZnihjVBVCQf15mw",
)
def test_esperanto(self):
seed = Seed(u"knedi aspekti boli asbesto pterido aparta muro sandalo hufumo porcelana degeli utopia ebono lifto dutaga hundo vejno ebono higieno nikotino orkestro arlekeno insekto jaguaro hundo", "Esperanto")
seed = Seed(
u"knedi aspekti boli asbesto pterido aparta muro sandalo hufumo porcelana degeli utopia ebono lifto dutaga hundo vejno ebono higieno nikotino orkestro arlekeno insekto jaguaro hundo",
"Esperanto",
)
self.assertEqual(
seed.secret_spend_key(),
'a8e8a30d3638cc4d09d1fa9f4de12ac0096c69a77896774793627c0cc6a28703')
"a8e8a30d3638cc4d09d1fa9f4de12ac0096c69a77896774793627c0cc6a28703",
)
self.assertEqual(
seed.secret_view_key(),
'8b4dcbcbafaf3d195af5bd54aa386d767a8de3b45236c9842cb876212427f103')
"8b4dcbcbafaf3d195af5bd54aa386d767a8de3b45236c9842cb876212427f103",
)
self.assertEqual(
seed.public_spend_key(),
'32c8a782c05db039018caa150bef1f66621831b3cb591401381e1dfc3c3d423e')
"32c8a782c05db039018caa150bef1f66621831b3cb591401381e1dfc3c3d423e",
)
self.assertEqual(
seed.public_view_key(),
'047963206a0267649657936d268824e35e59e3426c63b9f3b04788b14af1d85f')
"047963206a0267649657936d268824e35e59e3426c63b9f3b04788b14af1d85f",
)
self.assertEqual(
seed.public_address(),
'43YjCQcHm8TAY2kKbSMHz6J8FDZQwjPxw1Cq1vQ7SsQVBNeYEUMwGTQHppi5ffwg3df2m56DYexj2hm5uaQDtqpTBnUVzmD')
"43YjCQcHm8TAY2kKbSMHz6J8FDZQwjPxw1Cq1vQ7SsQVBNeYEUMwGTQHppi5ffwg3df2m56DYexj2hm5uaQDtqpTBnUVzmD",
)
def test_french(self):
seed = Seed(u"sauce exprimer chasse asile larve tacler digestion muguet rondeur sept clore narrer fluor arme torse dans glace tant salon sanguin globe quiche ficher flaque clore", "French")
seed = Seed(
u"sauce exprimer chasse asile larve tacler digestion muguet rondeur sept clore narrer fluor arme torse dans glace tant salon sanguin globe quiche ficher flaque clore",
"French",
)
self.assertEqual(
seed.secret_spend_key(),
'597703dd73d0da6b3996b83c3e1e2f602be4f0de453e15846171aa9076901603')
"597703dd73d0da6b3996b83c3e1e2f602be4f0de453e15846171aa9076901603",
)
self.assertEqual(
seed.secret_view_key(),
'f6e448dbbeaa7682a541b3b5b7e2e8ebb614fac032f1c3dff659ca26ab430f09')
"f6e448dbbeaa7682a541b3b5b7e2e8ebb614fac032f1c3dff659ca26ab430f09",
)
self.assertEqual(
seed.public_spend_key(),
'10b42e100196ef2a68eeec191a46d8dc5c83d73c0861c185e5244202cd432087')
"10b42e100196ef2a68eeec191a46d8dc5c83d73c0861c185e5244202cd432087",
)
self.assertEqual(
seed.public_view_key(),
'34c4c479d53b10d3e9c0a3d11432fd13611b12dc5b721c8ff3802329b7bac328')
"34c4c479d53b10d3e9c0a3d11432fd13611b12dc5b721c8ff3802329b7bac328",
)
self.assertEqual(
seed.public_address(),
'42FpfU7DfLi86RtY3ajKUKdrnKvXTx41WPPx6wsyp9XVPcfnrLDXxhucSphpzt3mDv4F1DMiCrfHmR5WPZq1erzn5bs4eA7')
"42FpfU7DfLi86RtY3ajKUKdrnKvXTx41WPPx6wsyp9XVPcfnrLDXxhucSphpzt3mDv4F1DMiCrfHmR5WPZq1erzn5bs4eA7",
)
def test_german(self):
seed = Seed(u"Erdgas Gesuch beeilen Chiffon Abendrot Alter Helium Salz Almweide Ampel Dichter Rotglut Dialekt Akkord Rampe Gesöff Ziege Boykott keuchen Krach Anbau Labor Esel Ferien Ampel", "German")
seed = Seed(
u"Erdgas Gesuch beeilen Chiffon Abendrot Alter Helium Salz Almweide Ampel Dichter Rotglut Dialekt Akkord Rampe Gesöff Ziege Boykott keuchen Krach Anbau Labor Esel Ferien Ampel",
"German",
)
self.assertEqual(
seed.secret_spend_key(),
'193152abe15c5e0a0ff56e3020229398769cd7c6ca5a4e30e439d6702c4f320a')
"193152abe15c5e0a0ff56e3020229398769cd7c6ca5a4e30e439d6702c4f320a",
)
self.assertEqual(
seed.secret_view_key(),
'cdb967c501195827d78a791e1173d4b8826a5ae73b0885984898c84b6c9dd80c')
"cdb967c501195827d78a791e1173d4b8826a5ae73b0885984898c84b6c9dd80c",
)
self.assertEqual(
seed.public_spend_key(),
'32eac115ca4b072c18198966c7ac9cb63b9f701a691eb52bfa18345d0fbcd90f')
"32eac115ca4b072c18198966c7ac9cb63b9f701a691eb52bfa18345d0fbcd90f",
)
self.assertEqual(
seed.public_view_key(),
'06a2119dfa7c48bdc03ad251026fc509bd01f3a4f7521802ca31b93cf06539ac')
"06a2119dfa7c48bdc03ad251026fc509bd01f3a4f7521802ca31b93cf06539ac",
)
self.assertEqual(
seed.public_address(),
'43Z2BHsCkU68NmZrxzfZuuXUtUHCXWttt8MdcnNyDMkC3WmfoFb9byqYjpeBaC4Xtx2dUUv8YPv1d1U4krZCLzyWLUFif2E')
"43Z2BHsCkU68NmZrxzfZuuXUtUHCXWttt8MdcnNyDMkC3WmfoFb9byqYjpeBaC4Xtx2dUUv8YPv1d1U4krZCLzyWLUFif2E",
)
def test_italian(self):
seed = Seed(u"tramonto spuntare ruota afrodite binocolo riferire moneta assalire tuta firmare malattia flagello paradiso tacere sindrome spuntare sogliola volare follia versare insulto diagnosi lapide meteo malattia", "Italian")
seed = Seed(
u"tramonto spuntare ruota afrodite binocolo riferire moneta assalire tuta firmare malattia flagello paradiso tacere sindrome spuntare sogliola volare follia versare insulto diagnosi lapide meteo malattia",
"Italian",
)
self.assertEqual(
seed.secret_spend_key(),
'29c8d9e91c1cb59e059bddd901e011db85f8d4f00f967226ffb5e185bd10e70d')
"29c8d9e91c1cb59e059bddd901e011db85f8d4f00f967226ffb5e185bd10e70d",
)
self.assertEqual(
seed.secret_view_key(),
'1f224a0330ee358428fe91fa48b6986941030c34f2d1efecc4eb26ea9f838b02')
"1f224a0330ee358428fe91fa48b6986941030c34f2d1efecc4eb26ea9f838b02",
)
self.assertEqual(
seed.public_spend_key(),
'149bdad48fd1ca40e1eb3e323b676132e2cae1eedbd715ac131b97c2c749c6b4')
"149bdad48fd1ca40e1eb3e323b676132e2cae1eedbd715ac131b97c2c749c6b4",
)
self.assertEqual(
seed.public_view_key(),
'efc1a3382c33ac58ecfdd3a71497b0d0aeef061d0af94e5c49278d653167d643')
"efc1a3382c33ac58ecfdd3a71497b0d0aeef061d0af94e5c49278d653167d643",
)
self.assertEqual(
seed.public_address(),
'42QQUPDR9PoBrSc9rB5VvG9Wf7KmtjXhEVnLhGKif9rDXGK3n1e6rsVFsh62YDqDf5buVQXuL6oLHGSHg4ANgQUu8beDd9R')
"42QQUPDR9PoBrSc9rB5VvG9Wf7KmtjXhEVnLhGKif9rDXGK3n1e6rsVFsh62YDqDf5buVQXuL6oLHGSHg4ANgQUu8beDd9R",
)
def test_japanese(self):
seed = Seed(u"いもり すあな いきる しちょう うったえる ちひょう けなみ たいちょう うぶごえ しかい しなぎれ いっせい つかれる しなん ばあさん たいまつばな しひょう おいかける あんがい ていへん せんもん きこく せんく そそぐ つかれる", "Japanese")
seed = Seed(
u"いもり すあな いきる しちょう うったえる ちひょう けなみ たいちょう うぶごえ しかい しなぎれ いっせい つかれる しなん ばあさん たいまつばな しひょう おいかける あんがい ていへん せんもん きこく せんく そそぐ つかれる",
"Japanese",
)
self.assertFalse(seed.is_mymonero())
self.assertEqual(
seed.secret_spend_key(),
'a047598095d2ada065af73758f7082900b9b0d721b5f99a541a78bd461ffc607')
"a047598095d2ada065af73758f7082900b9b0d721b5f99a541a78bd461ffc607",
)
self.assertEqual(
seed.secret_view_key(),
'080c6135edf93233176d41c8535caef0f13d596dc5093b5a5afa4279339dbc00')
"080c6135edf93233176d41c8535caef0f13d596dc5093b5a5afa4279339dbc00",
)
self.assertEqual(
seed.public_spend_key(),
'85d849793fce4d0238d991d3aab7ac790cee73e5732d378c216f11bd3b873e43')
"85d849793fce4d0238d991d3aab7ac790cee73e5732d378c216f11bd3b873e43",
)
self.assertEqual(
seed.public_view_key(),
'19dc462a6074a26fa7788b45e542a71ffdbd48502e41ae8790c46fd6de556de3')
"19dc462a6074a26fa7788b45e542a71ffdbd48502e41ae8790c46fd6de556de3",
)
self.assertEqual(
seed.public_address(),
'46hHs9s3boi1NZJHGSwMgfMFLpCBaKwdQQSSf7fqVjWdCDxudsDmqqbKgBkpYDX6JA6MMZG8o5yrMPg9ztrXHdEkSfUA131')
"46hHs9s3boi1NZJHGSwMgfMFLpCBaKwdQQSSf7fqVjWdCDxudsDmqqbKgBkpYDX6JA6MMZG8o5yrMPg9ztrXHdEkSfUA131",
)
def test_portuguese(self):
seed = Seed(u"rebuscar mefistofelico luto isca vulva ontologico autuar epiteto jarro invulneravel inquisitorial vietnamita voile potro mamute giroscopio scherzo cheroqui gueto loquaz fissurar fazer violoncelo viquingue vulva", "Portuguese")
seed = Seed(
u"rebuscar mefistofelico luto isca vulva ontologico autuar epiteto jarro invulneravel inquisitorial vietnamita voile potro mamute giroscopio scherzo cheroqui gueto loquaz fissurar fazer violoncelo viquingue vulva",
"Portuguese",
)
self.assertFalse(seed.is_mymonero())
self.assertEqual(
seed.secret_spend_key(),
'60916cfcb10fa0b2b0648e36ecd7037f5c1972d36b2e6d56c2f4feca613a4200')
"60916cfcb10fa0b2b0648e36ecd7037f5c1972d36b2e6d56c2f4feca613a4200",
)
self.assertEqual(
seed.secret_view_key(),
'b23941e3f4da76e0fab171d94a36fe70031fb501f1f80e0cb3b4b4638b5f7106')
"b23941e3f4da76e0fab171d94a36fe70031fb501f1f80e0cb3b4b4638b5f7106",
)
self.assertEqual(
seed.public_spend_key(),
'340c89026a03637e8b0abda566ac99b98a7c85b30a81281be19af869c3631dfb')
"340c89026a03637e8b0abda566ac99b98a7c85b30a81281be19af869c3631dfb",
)
self.assertEqual(
seed.public_view_key(),
'23bb38c5e34867c49a65f0e7192138483361d419febbd429f256088e5e62a55e')
"23bb38c5e34867c49a65f0e7192138483361d419febbd429f256088e5e62a55e",
)
self.assertEqual(
seed.public_address(),
'43bWUqKAoYWNAdMtuaSF2pY2yptw7zfCB5fV2fXLkYTvj1NNYUKM4aaZtJCVYJunHuD5SNE2CPTCo81wDhZc8bReBidbX1w')
"43bWUqKAoYWNAdMtuaSF2pY2yptw7zfCB5fV2fXLkYTvj1NNYUKM4aaZtJCVYJunHuD5SNE2CPTCo81wDhZc8bReBidbX1w",
)
def test_russian(self):
seed = Seed(u"дощатый ателье мыло паек азот ружье домашний уныние уплата торговля шкаф кекс газета тревога улица армия лазерный иголка друг хищник пашня дневник кричать лыжный иголка", "Russian")
seed = Seed(
u"дощатый ателье мыло паек азот ружье домашний уныние уплата торговля шкаф кекс газета тревога улица армия лазерный иголка друг хищник пашня дневник кричать лыжный иголка",
"Russian",
)
self.assertEqual(
seed.secret_spend_key(),
'6dc31f6ebcf834ab375a69006cb19c66fcccfa0732dfb3ea1b0662b455226b0d')
"6dc31f6ebcf834ab375a69006cb19c66fcccfa0732dfb3ea1b0662b455226b0d",
)
self.assertEqual(
seed.secret_view_key(),
'5467825ef0148a11582115f80b01c9af90fe31216a9cf6fb2d6b3c78698ce80a')
"5467825ef0148a11582115f80b01c9af90fe31216a9cf6fb2d6b3c78698ce80a",
)
self.assertEqual(
seed.public_spend_key(),
'200657c6d14ab19cd3fccd8634e8f23e81290a559b8eb5e58dda3696553ddffc')
"200657c6d14ab19cd3fccd8634e8f23e81290a559b8eb5e58dda3696553ddffc",
)
self.assertEqual(
seed.public_view_key(),
'f7563d9efb1c03a299b9c91a604caf7fd0c5a6998fdeedf18b58a63930958a24')
"f7563d9efb1c03a299b9c91a604caf7fd0c5a6998fdeedf18b58a63930958a24",
)
self.assertEqual(
seed.public_address(),
'42qVnaWnHSGTERsT6diSvdBTNbHfQZauSfPxpc5EuHc2jK699E28uwpUCRrHr9aaZ4NNyJ9ABdxX6hQHPHv2YcW55A26UbQ')
"42qVnaWnHSGTERsT6diSvdBTNbHfQZauSfPxpc5EuHc2jK699E28uwpUCRrHr9aaZ4NNyJ9ABdxX6hQHPHv2YcW55A26UbQ",
)
def test_spanish(self):
seed = Seed(u"riesgo lápiz martes fuerza dinero pupila pago mensaje guion libro órgano juntar imperio puñal historia pasión nación posible paso límite don afirmar receta reposo fuerza", "Spanish")
seed = Seed(
u"riesgo lápiz martes fuerza dinero pupila pago mensaje guion libro órgano juntar imperio puñal historia pasión nación posible paso límite don afirmar receta reposo fuerza",
"Spanish",
)
self.assertFalse(seed.is_mymonero())
self.assertEqual(
seed.secret_spend_key(),
'5973d91299466a9a51ddfcd20d1710c776aa1399279b292b264ab6b7ab608105')
"5973d91299466a9a51ddfcd20d1710c776aa1399279b292b264ab6b7ab608105",
)
self.assertEqual(
seed.secret_view_key(),
'5f7a66cf32120515870f89e3a156ec2024154334a3b43af1da05244ec4cf250d')
"5f7a66cf32120515870f89e3a156ec2024154334a3b43af1da05244ec4cf250d",
)
self.assertEqual(
seed.public_spend_key(),
'42161417635c6bd31a8dce8c2bd3b5f4879369fb732073d9f6fa82b18329c7f7')
"42161417635c6bd31a8dce8c2bd3b5f4879369fb732073d9f6fa82b18329c7f7",
)
self.assertEqual(
seed.public_view_key(),
'6acc984fecb5894b5661d446954ffcfe302cd1d2cf0e5177c2553aafb1dc3d2a')
"6acc984fecb5894b5661d446954ffcfe302cd1d2cf0e5177c2553aafb1dc3d2a",
)
self.assertEqual(
seed.public_address(),
'448MxehQwbgcJyJ3fKnTYYhuF7g7cs7AJdTXoybMu8UEiPFtFpEVNTaDbsK5vatPHVjWwjvJfyWKiM2pBKXJrg4U5qeGXjZ')
"448MxehQwbgcJyJ3fKnTYYhuF7g7cs7AJdTXoybMu8UEiPFtFpEVNTaDbsK5vatPHVjWwjvJfyWKiM2pBKXJrg4U5qeGXjZ",
)
if __name__ == "__main__":

@ -9,52 +9,153 @@ from monero.numbers import PaymentID
from monero.transaction import IncomingPayment, Transaction, Output, _ByHeight
from monero import exceptions
class FiltersTestCase(unittest.TestCase):
def setUp(self):
self.tx1 = Transaction(
timestamp=datetime(2018, 1, 29, 15, 0, 25),
height=1087606,
hash='a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14',
fee=Decimal('0.00352891'))
hash="a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
fee=Decimal("0.00352891"),
)
self.pm1 = IncomingPayment(
amount=Decimal('1'),
local_address=address('Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'),
payment_id=PaymentID('0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d'),
transaction=self.tx1)
amount=Decimal("1"),
local_address=address(
"Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV"
),
payment_id=PaymentID(
"0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d"
),
transaction=self.tx1,
)
# setup for one-time output tests
self.json1 = { # Actual as_json response from TX ee5bcb6430c39757ff27f8d607287572f3956a0ee16bb1d2378891f93746c8f9
'version': 2, 'unlock_time': 0, 'vin': [{'key': {'amount': 0, 'key_offsets':
[25471133, 261981, 36602, 18967, 13096, 16260, 54279, 3105, 5403, 786, 555],
'k_image': '4b48346e954a74be9a334b03cadf8aa020542d201fb6ae7416246d19fd04fdb7'}}],
'vout': [{'amount': 0, 'target': {'key': 'c55e793b4d673dcf73587e5141b777ef24e255d48826c75ce110ffc23ff762b9'}},
{'amount': 0, 'target': {'key': '93b263454cd3cc349245ad60c9c248332b885a1f2d7b5792cfc24fd87434d62a'}}],
'extra': [1, 209, 170, 43, 245, 190, 68, 82, 131, 116, 79, 134, 175, 104, 216, 127, 99, 49, 127, 141, 255, 65, 204, 101,
81, 244, 111, 253, 155, 75, 111, 14, 159, 2, 9, 1, 24, 56, 108, 94, 20, 88, 150, 94], 'rct_signatures': {'type': 5,
'txnFee': 58560000, 'ecdhInfo': [{'amount': '6c13cf459cb9ed96'}, {'amount': '373bc40c7f600bf4'}], 'outPk':
['80521a77ebe954a5daa6f14b13cc74337f999bc68177a58e76f768c18f2fa421',
'5997e64b90d59f7f810ddbc801f747c4fa43e2de593e4ea48531e16d776c00fd']}}
self.json1 = { # Actual as_json response from TX ee5bcb6430c39757ff27f8d607287572f3956a0ee16bb1d2378891f93746c8f9
"version": 2,
"unlock_time": 0,
"vin": [
{
"key": {
"amount": 0,
"key_offsets": [
25471133,
261981,
36602,
18967,
13096,
16260,
54279,
3105,
5403,
786,
555,
],
"k_image": "4b48346e954a74be9a334b03cadf8aa020542d201fb6ae7416246d19fd04fdb7",
}
}
],
"vout": [
{
"amount": 0,
"target": {
"key": "c55e793b4d673dcf73587e5141b777ef24e255d48826c75ce110ffc23ff762b9"
},
},
{
"amount": 0,
"target": {
"key": "93b263454cd3cc349245ad60c9c248332b885a1f2d7b5792cfc24fd87434d62a"
},
},
],
"extra": [
1,
209,
170,
43,
245,
190,
68,
82,
131,
116,
79,
134,
175,
104,
216,
127,
99,
49,
127,
141,
255,
65,
204,
101,
81,
244,
111,
253,
155,
75,
111,
14,
159,
2,
9,
1,
24,
56,
108,
94,
20,
88,
150,
94,
],
"rct_signatures": {
"type": 5,
"txnFee": 58560000,
"ecdhInfo": [
{"amount": "6c13cf459cb9ed96"},
{"amount": "373bc40c7f600bf4"},
],
"outPk": [
"80521a77ebe954a5daa6f14b13cc74337f999bc68177a58e76f768c18f2fa421",
"5997e64b90d59f7f810ddbc801f747c4fa43e2de593e4ea48531e16d776c00fd",
],
},
}
self.outind1 = [25884175, 25884176]
self.tx2 = Transaction(json=self.json1, output_indices=self.outind1)
self.oto1 = Output(index=25973289, amount=Decimal('0.000000000000'))
self.oto2 = Output(pubkey='0faff18f7149a0db5aa0dc3c9116887740ccbb5dc4d1eeff87895288e55e5052')
self.oto1 = Output(index=25973289, amount=Decimal("0.000000000000"))
self.oto2 = Output(
pubkey="0faff18f7149a0db5aa0dc3c9116887740ccbb5dc4d1eeff87895288e55e5052"
)
def test_hash(self):
self.assertIn(
'a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14',
repr(self.tx1))
"a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
repr(self.tx1),
)
self.assertIn(
'a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14',
repr(self.pm1))
"a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
repr(self.pm1),
)
def test_outputs(self):
out1, out2 = self.tx2.outputs()
self.assertEqual(out1.transaction, self.tx2)
self.assertEqual(out2.transaction, self.tx2)
self.assertIn(self.json1['vout'][0]['target']['key'], repr(out1))
self.assertFalse(out2 != Output(stealth_address=self.json1['vout'][1]['target']['key']))
self.assertIn('(index=25973289,amount=0E-12)', repr(self.oto1))
self.assertEqual(self.oto1, Output(index=25973289, amount=Decimal('0.000000000000')))
self.assertIn(self.json1["vout"][0]["target"]["key"], repr(out1))
self.assertFalse(
out2 != Output(stealth_address=self.json1["vout"][1]["target"]["key"])
)
self.assertIn("(index=25973289,amount=0E-12)", repr(self.oto1))
self.assertEqual(
self.oto1, Output(index=25973289, amount=Decimal("0.000000000000"))
)
with self.assertRaises(exceptions.TransactionWithoutJSON):
self.tx1.outputs()
@ -72,12 +173,17 @@ class SortingTestCase(unittest.TestCase):
IncomingPayment(transaction=Transaction(height=None)),
IncomingPayment(transaction=Transaction(height=100)),
IncomingPayment(transaction=Transaction(height=None)),
IncomingPayment(transaction=Transaction(height=1))
IncomingPayment(transaction=Transaction(height=1)),
]
for i in range(1680): # 1/3 of possible permutations
for i in range(1680): # 1/3 of possible permutations
sorted_pmts = sorted(pmts, key=_ByHeight)
self.assertEqual(
list(map(attrgetter('height'), map(attrgetter('transaction'), sorted_pmts))),
[None, None, 100, 13, 12, 10, 1])
list(
map(
attrgetter("height"),
map(attrgetter("transaction"), sorted_pmts),
)
),
[None, None, 100, 13, 12, 10, 1],
)
random.shuffle(pmts)

@ -9,6 +9,7 @@ from monero.address import address
from monero.numbers import PaymentID
from monero.transaction import IncomingPayment, Transaction
class FiltersTestCase(unittest.TestCase):
def setUp(self):
class MockBackend(object):
@ -17,90 +18,124 @@ class FiltersTestCase(unittest.TestCase):
tx = Transaction(
timestamp=datetime(2018, 1, 29, 15, 0, 25),
height=1087606,
hash='a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14',
fee=Decimal('0.00352891'))
hash="a0b876ebcf7c1d499712d84cedec836f9d50b608bb22d6cb49fd2feae3ffed14",
fee=Decimal("0.00352891"),
)
pm = IncomingPayment(
amount=Decimal('1'),
local_address=address('Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'),
payment_id=PaymentID('0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d'),
transaction=tx)
amount=Decimal("1"),
local_address=address(
"Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV"
),
payment_id=PaymentID(
"0166d8da6c0045c51273dd65d6f63734beb8a84e0545a185b2cfd053fced9f5d"
),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 14, 57, 47),
height=1087601,
hash='f34b495cec77822a70f829ec8a5a7f1e727128d62e6b1438e9cb7799654d610e',
fee=Decimal('0.008661870000'))
hash="f34b495cec77822a70f829ec8a5a7f1e727128d62e6b1438e9cb7799654d610e",
fee=Decimal("0.008661870000"),
)
pm = IncomingPayment(
amount=Decimal('3.000000000000'),
local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'),
payment_id=PaymentID('f75ad90e25d71a12'),
transaction=tx)
amount=Decimal("3.000000000000"),
local_address=address(
"BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En"
),
payment_id=PaymentID("f75ad90e25d71a12"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 13, 17, 18),
height=1087530,
hash='5c3ab739346e9d98d38dc7b8d36a4b7b1e4b6a16276946485a69797dbf887cd8',
fee=Decimal('0.000962550000'))
hash="5c3ab739346e9d98d38dc7b8d36a4b7b1e4b6a16276946485a69797dbf887cd8",
fee=Decimal("0.000962550000"),
)
pm = IncomingPayment(
amount=Decimal('10.000000000000'),
local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'),
payment_id=PaymentID('f75ad90e25d71a12'),
transaction=tx)
amount=Decimal("10.000000000000"),
local_address=address(
"9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
),
payment_id=PaymentID("f75ad90e25d71a12"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 13, 17, 18),
height=1087530,
hash='4ea70add5d0c7db33557551b15cd174972fcfc73bf0f6a6b47b7837564b708d3',
fee=Decimal('0.000962550000'))
hash="4ea70add5d0c7db33557551b15cd174972fcfc73bf0f6a6b47b7837564b708d3",
fee=Decimal("0.000962550000"),
)
pm = IncomingPayment(
amount=Decimal('4.000000000000'),
local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'),
payment_id=PaymentID('f75ad90e25d71a12'),
transaction=tx)
amount=Decimal("4.000000000000"),
local_address=address(
"9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
),
payment_id=PaymentID("f75ad90e25d71a12"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 13, 17, 18),
height=1087530,
hash='e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc',
fee=Decimal('0.000962550000'))
hash="e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc",
fee=Decimal("0.000962550000"),
)
pm = IncomingPayment(
amount=Decimal('2.120000000000'),
local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'),
payment_id=PaymentID('cb248105ea6a9189'),
transaction=tx)
amount=Decimal("2.120000000000"),
local_address=address(
"9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
),
payment_id=PaymentID("cb248105ea6a9189"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 14, 57, 47),
height=1087601,
hash='5ef7ead6a041101ed326568fbb59c128403cba46076c3f353cd110d969dac808',
fee=Decimal('0.000962430000'))
hash="5ef7ead6a041101ed326568fbb59c128403cba46076c3f353cd110d969dac808",
fee=Decimal("0.000962430000"),
)
pm = IncomingPayment(
amount=Decimal('7.000000000000'),
local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En'),
payment_id=PaymentID('0000000000000000'),
transaction=tx)
amount=Decimal("7.000000000000"),
local_address=address(
"BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En"
),
payment_id=PaymentID("0000000000000000"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 13, 17, 18),
height=1087530,
hash='cc44568337a186c2e1ccc080b43b4ae9db26a07b7afd7edeed60ce2fc4a6477f',
fee=Decimal('0.000962550000'))
hash="cc44568337a186c2e1ccc080b43b4ae9db26a07b7afd7edeed60ce2fc4a6477f",
fee=Decimal("0.000962550000"),
)
pm = IncomingPayment(
amount=Decimal('10.000000000000'),
local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'),
payment_id=PaymentID('0000000000000000'),
transaction=tx)
amount=Decimal("10.000000000000"),
local_address=address(
"9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
),
payment_id=PaymentID("0000000000000000"),
transaction=tx,
)
self.transfers.append(pm)
tx = Transaction(
timestamp=datetime(2018, 1, 29, 21, 13, 28),
height=None,
hash='d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c',
fee=Decimal('0.000961950000'))
hash="d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
fee=Decimal("0.000961950000"),
)
pm = IncomingPayment(
amount=Decimal('3.140000000000'),
local_address=address('9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC'),
payment_id=PaymentID('03f6649304ea4cb2'),
transaction=tx)
amount=Decimal("3.140000000000"),
local_address=address(
"9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
),
payment_id=PaymentID("03f6649304ea4cb2"),
transaction=tx,
)
self.transfers.append(pm)
def height(self):
@ -119,36 +154,47 @@ class FiltersTestCase(unittest.TestCase):
self.assertEqual(len(pmts), 7)
def test_filter_payment_id(self):
pmts = self.wallet.incoming(payment_id='cb248105ea6a9189')
pmts = self.wallet.incoming(payment_id="cb248105ea6a9189")
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc')
pmts = self.wallet.incoming(payment_id='f75ad90e25d71a12')
"e9a71c01875bec20812f71d155bfabf42024fde3ec82475562b817dcc8cbf8dc",
)
pmts = self.wallet.incoming(payment_id="f75ad90e25d71a12")
self.assertEqual(len(pmts), 3)
pmts = self.wallet.incoming(payment_id=('cb248105ea6a9189', 'f75ad90e25d71a12'))
pmts = self.wallet.incoming(payment_id=("cb248105ea6a9189", "f75ad90e25d71a12"))
self.assertEqual(len(pmts), 4)
self.assertEqual(
pmts,
self.wallet.incoming(payment_id=(PaymentID('cb248105ea6a9189'), 'f75ad90e25d71a12')))
self.wallet.incoming(
payment_id=(PaymentID("cb248105ea6a9189"), "f75ad90e25d71a12")
),
)
def test_filter_address(self):
pmts = self.wallet.incoming(
local_address='BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En')
local_address="BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En"
)
self.assertEqual(len(pmts), 2)
self.assertEqual(
pmts,
self.wallet.incoming(
local_address=address('BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En')))
local_address=address(
"BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En"
)
),
)
pmts = self.wallet.incoming(
local_address=(
'BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En',
'Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV'))
"BhE3cQvB7VF2uuXcpXp28Wbadez6GgjypdRS1F1Mzqn8Advd6q8VfaX8ZoEDobjejrMfpHeNXoX8MjY8q8prW1PEALgr1En",
"Bf6ngv7q2TBWup13nEm9AjZ36gLE6i4QCaZ7XScZUKDUeGbYEHmPRdegKGwLT8tBBK7P6L32RELNzCR6QzNFkmogDjvypyV",
)
)
self.assertEqual(len(pmts), 3)
def test_filter_mempool(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
pmts = self.wallet.incoming()
self.assertEqual(len(pmts), 7)
for p in pmts:
@ -159,7 +205,8 @@ class FiltersTestCase(unittest.TestCase):
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
"d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(self.wallet.confirmations(pmts[0]), 0)
self.assertEqual(self.wallet.confirmations(pmts[0].transaction), 0)
self.assertEqual(len(w), 0)
@ -167,35 +214,41 @@ class FiltersTestCase(unittest.TestCase):
self.assertEqual(len(pmts), 0)
self.assertEqual(len(w), 1)
self.assertIs(w[0].category, RuntimeWarning)
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, max_height=99999999999999)
pmts = self.wallet.incoming(
unconfirmed=True, confirmed=False, max_height=99999999999999
)
self.assertEqual(len(pmts), 0)
self.assertEqual(len(w), 2)
self.assertIs(w[1].category, RuntimeWarning)
pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2')
pmts = self.wallet.incoming(payment_id="03f6649304ea4cb2")
self.assertEqual(len(pmts), 0)
pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2')
pmts = self.wallet.incoming(unconfirmed=True, payment_id="03f6649304ea4cb2")
self.assertEqual(len(pmts), 1)
pmts = self.wallet.incoming(
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
)
self.assertEqual(len(pmts), 4)
pmts = self.wallet.incoming(
unconfirmed=True,
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
)
self.assertEqual(len(pmts), 5)
pmts = self.wallet.incoming(
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC',
payment_id='03f6649304ea4cb2')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
payment_id="03f6649304ea4cb2",
)
self.assertEqual(len(pmts), 0)
pmts = self.wallet.incoming(
unconfirmed=True,
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC',
payment_id='03f6649304ea4cb2')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
payment_id="03f6649304ea4cb2",
)
self.assertEqual(len(pmts), 1)
self.assertEqual(len(w), 2)
def test_filter_mempool_absent(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
pmts = self.wallet.incoming()
self.assertEqual(len(pmts), 7)
for p in pmts:
@ -210,94 +263,106 @@ class FiltersTestCase(unittest.TestCase):
def test_filter_mempool_present(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
pmts = self.wallet.incoming(unconfirmed=True)
self.assertEqual(len(pmts), 8)
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False)
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
"d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(self.wallet.confirmations(pmts[0]), 0)
self.assertEqual(self.wallet.confirmations(pmts[0].transaction), 0)
self.assertEqual(len(w), 0)
def test_filter_mempool_filter_height(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
# mempool is always excluded and warnings are generated
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, min_height=1)
self.assertEqual(len(pmts), 0)
self.assertEqual(len(w), 1)
self.assertIs(w[0].category, RuntimeWarning)
pmts = self.wallet.incoming(unconfirmed=True, confirmed=False, max_height=99999999999999)
pmts = self.wallet.incoming(
unconfirmed=True, confirmed=False, max_height=99999999999999
)
self.assertEqual(len(pmts), 0)
self.assertEqual(len(w), 2)
self.assertIs(w[1].category, RuntimeWarning)
def test_filter_mempool_filter_payment_id(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
# mempool excluded
pmts = self.wallet.incoming(payment_id='03f6649304ea4cb2')
pmts = self.wallet.incoming(payment_id="03f6649304ea4cb2")
self.assertEqual(len(pmts), 0)
# mempool included
pmts = self.wallet.incoming(unconfirmed=True, payment_id='03f6649304ea4cb2')
pmts = self.wallet.incoming(unconfirmed=True, payment_id="03f6649304ea4cb2")
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
"d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(len(w), 0)
def test_filter_mempool_filter_address(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
# mempool excluded
pmts = self.wallet.incoming(
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC"
)
self.assertEqual(len(pmts), 4)
# mempool included
pmts = self.wallet.incoming(
unconfirmed=True,
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
)
self.assertEqual(len(pmts), 5)
self.assertEqual(len(w), 0)
def test_filter_mempool_filter_address_and_payment_id(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
# mempool excluded
pmts = self.wallet.incoming(
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC',
payment_id='03f6649304ea4cb2')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
payment_id="03f6649304ea4cb2",
)
self.assertEqual(len(pmts), 0)
# mempool included
pmts = self.wallet.incoming(
unconfirmed=True,
local_address='9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC',
payment_id='03f6649304ea4cb2')
local_address="9tQoHWyZ4yXUgbz9nvMcFZUfDy5hxcdZabQCxmNCUukKYicXegsDL7nQpcUa3A1pF6K3fhq3scsyY88tdB1MqucULcKzWZC",
payment_id="03f6649304ea4cb2",
)
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
"d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(len(w), 0)
def test_filter_mempool_filter_txid(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter("always")
# mempool excluded
pmts = self.wallet.incoming(
tx_id='d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
tx_id="d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c"
)
self.assertEqual(len(pmts), 0)
# mempool included
pmts = self.wallet.incoming(
unconfirmed=True,
tx_id='d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
tx_id="d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(len(pmts), 1)
self.assertEqual(
pmts[0].transaction.hash,
'd29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c')
"d29264ad317e8fdb55ea04484c00420430c35be7b3fe6dd663f99aebf41a786c",
)
self.assertEqual(len(w), 0)
def test_filter_excessive(self):
self.assertRaises(ValueError, self.wallet.incoming, excessive_argument='foo')
self.assertRaises(ValueError, self.wallet.incoming, excessive_argument="foo")

Loading…
Cancel
Save