Introduce monero.const, simplify net check methods and deprecate old API

This commit is contained in:
Michał Sałaban 2020-01-13 11:37:53 +01:00
parent e7897f72bd
commit 6e410d9624
11 changed files with 130 additions and 72 deletions

View file

@ -1,3 +1,3 @@
from . import address, account, daemon, wallet, numbers, prio, wordlists, seed
from . import address, account, const, daemon, wallet, numbers, wordlists, seed
__version__ = '0.7'

View file

@ -1,4 +1,4 @@
from . import prio
from . import const
from .transaction import PaymentManager
@ -70,7 +70,7 @@ class Account(object):
return self._backend.new_address(account=self.index, label=label)
def transfer(self, address, amount,
priority=prio.NORMAL, payment_id=None, unlock_time=0,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
"""
Sends a transfer. Returns a list of resulting transactions.
@ -79,7 +79,7 @@ class Account(object):
:param amount: amount to send
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as the destination)
@ -98,7 +98,7 @@ class Account(object):
relay=relay)
def transfer_multiple(self, destinations,
priority=prio.NORMAL, payment_id=None, unlock_time=0,
priority=const.PRIO_NORMAL, payment_id=None, unlock_time=0,
relay=True):
"""
Sends a batch of transfers. Returns a list of resulting transactions.
@ -107,7 +107,7 @@ class Account(object):
[(:class:`Address <monero.address.Address>`, `Decimal`), ...]
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as the destination)
@ -126,7 +126,7 @@ class Account(object):
account=self.index,
relay=relay)
def sweep_all(self, address, priority=prio.NORMAL, payment_id=None,
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.
@ -134,7 +134,7 @@ class Account(object):
:param address: destination :class:`Address <monero.address.Address>` or subtype
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as the destination)

View file

@ -3,8 +3,10 @@ import re
from sha3 import keccak_256
import six
import struct
import warnings
from . import base58
from . import const
from . import ed25519
from . import numbers
@ -36,26 +38,42 @@ class BaseAddress(object):
"""
return hexlify(self._decoded[1:33]).decode()
@property
def net(self):
return const.NETS[self._valid_netbytes.index(self._decoded[0])]
def is_mainnet(self):
"""Returns `True` if the address belongs to mainnet.
:rtype: bool
"""
return self._decoded[0] == self._valid_netbytes[0]
warnings.warn(".is_mainnet(), .is_testnet() and .is_stagenet() methods are deprecated "
"and will be gone in 0.8; use Address.net property and constants form monero.const "
"instead",
DeprecationWarning)
return self.net == const.NET_MAIN
def is_testnet(self):
"""Returns `True` if the address belongs to testnet.
:rtype: bool
"""
return self._decoded[0] == self._valid_netbytes[1]
warnings.warn(".is_mainnet(), .is_testnet() and .is_stagenet() methods are deprecated "
"and will be gone in 0.8; use Address.net property and constants form monero.const "
"instead",
DeprecationWarning)
return self.net == const.NET_TEST
def is_stagenet(self):
"""Returns `True` if the address belongs to stagenet.
:rtype: bool
"""
return self._decoded[0] == self._valid_netbytes[2]
warnings.warn(".is_mainnet(), .is_testnet() and .is_stagenet() methods are deprecated "
"and will be gone in 0.8; use Address.net property and constants form monero.const "
"instead",
DeprecationWarning)
return self.net == const.NET_STAGE
def _decode(self, address):
self._decoded = bytearray(unhexlify(base58.decode(address)))
@ -92,8 +110,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 = (18, 53, 24)
# NOTE: _valid_netbytes order is (mainnet, testnet, stagenet)
_valid_netbytes = const.MASTERADDR_NETBYTES
def check_private_view_key(self, key):
"""Checks if private view key matches this address.
@ -121,7 +138,7 @@ 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))
prefix = 54 if self.is_testnet() else 25 if self.is_stagenet() else 19
prefix = const.INTADDRR_NETBYTES[const.NETS.index(self.net)]
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)))
@ -133,8 +150,7 @@ class SubAddress(BaseAddress):
Any type of address which is not the master one for a wallet.
"""
_valid_netbytes = (42, 63, 36)
# NOTE: _valid_netbytes order is (mainnet, testnet, stagenet)
_valid_netbytes = const.SUBADDR_NETBYTES
def with_payment_id(self, _):
raise TypeError("SubAddress cannot be integrated with payment ID")
@ -146,8 +162,7 @@ class IntegratedAddress(Address):
A master address integrated with payment id (short one, max 64 bit).
"""
_valid_netbytes = (19, 54, 25)
# NOTE: _valid_netbytes order is (mainnet, testnet, stagenet)
_valid_netbytes = const.INTADDRR_NETBYTES
def __init__(self, address):
address = address.decode() if isinstance(address, bytes) else str(address)
@ -167,7 +182,7 @@ class IntegratedAddress(Address):
"""Returns the base address without payment id.
:rtype: :class:`Address`
"""
prefix = 53 if self.is_testnet() else 24 if self.is_stagenet() else 18
prefix = const.MASTERADDR_NETBYTES[const.NETS.index(self.net)]
data = bytearray([prefix]) + self._decoded[1:65]
checksum = keccak_256(data).digest()[:4]
return Address(base58.encode(hexlify(data + checksum)))

13
monero/const.py Normal file
View file

@ -0,0 +1,13 @@
NET_MAIN = "main"
NET_STAGE = "stage"
NET_TEST = "test"
NETS = (NET_MAIN, NET_TEST, NET_STAGE)
MASTERADDR_NETBYTES = (18, 53, 24)
SUBADDR_NETBYTES = (42, 63, 36)
INTADDRR_NETBYTES = (19, 54, 25)
PRIO_UNIMPORTANT = 1
PRIO_NORMAL = 2
PRIO_ELEVATED = 3
PRIO_PRIORITY = 4

View file

@ -1,3 +1,8 @@
import warnings
warnings.warn(
"monero.prio is deprecated and will be gone in 0.8; use monero.const.PRIO_* consts instead",
DeprecationWarning)
UNIMPORTANT=1
NORMAL=2
ELEVATED=3

View file

@ -35,13 +35,12 @@
# + simplified interface, changed exceptions (assertions -> explicit raise)
# + optimization
from monero import wordlists
from monero import ed25519
from monero import base58
from monero.address import address
from binascii import hexlify, unhexlify
from os import urandom
from sha3 import keccak_256
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.
@ -151,17 +150,25 @@ class Seed(object):
self._ed_pub_view_key = ed25519.public_from_secret_hex(self.secret_view_key())
return self._ed_pub_view_key
def public_address(self, net='mainnet'):
def public_address(self, net=const.NET_MAIN):
"""Returns the master :class:`Address <monero.address.Address>` represented by the seed.
:param net: the network, one of 'mainnet', 'testnet', 'stagenet'. Default is 'mainnet'.
:param net: the network, one of `const.NET_*`; default is `const.NET_MAIN`
:rtype: :class:`Address <monero.address.Address>`
"""
if net not in ('mainnet', 'testnet', 'stagenet'):
# backward compatibility
_net = net[:-3] if net.endswith('net') else net
if _net != net:
warnings.warn(
"Argument '{:s}' is deprecated and will not be accepted in 0.8, "
"use one of monero.const.NET_*".format(net),
DeprecationWarning)
net = _net
if net not in const.NETS:
raise ValueError(
"Invalid net argument. Must be one of ('mainnet', 'testnet', 'stagenet').")
netbyte = 18 if net == 'mainnet' else 53 if net == 'testnet' else 24
"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())
h = keccak_256()
h.update(unhexlify(data))

View file

@ -4,9 +4,9 @@ import struct
from . import address
from . import base58
from . import const
from . import ed25519
from . import numbers
from . import prio
from .transaction import Payment, PaymentManager
@ -224,15 +224,13 @@ class Wallet(object):
ed25519.scalarmult_B(ed25519.decodeint(m)))
# C = master_svk * D
C = ed25519.scalarmult(D, ed25519.decodeint(master_svk))
netbyte = bytearray([
42 if master_address.is_mainnet() else \
63 if master_address.is_testnet() else 36])
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=prio.NORMAL, payment_id=None, unlock_time=0,
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.
@ -241,7 +239,7 @@ class Wallet(object):
:param amount: amount to send
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as the destination)
@ -260,7 +258,7 @@ class Wallet(object):
relay=relay)
def transfer_multiple(self, destinations,
priority=prio.NORMAL, payment_id=None, unlock_time=0,
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
@ -269,7 +267,7 @@ class Wallet(object):
:param destinations: a list of destination and amount pairs: [(address, amount), ...]
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as a destination)
@ -287,7 +285,7 @@ class Wallet(object):
unlock_time=unlock_time,
relay=relay)
def sweep_all(self, address, priority=prio.NORMAL, payment_id=None,
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.
@ -296,7 +294,7 @@ class Wallet(object):
:param address: destination :class:`Address <monero.address.Address>` or subtype
:param priority: transaction priority, implies fee. The priority can be a number
from 1 to 4 (unimportant, normal, elevated, priority) or a constant
from `monero.prio`.
from `monero.const.PRIO_*`.
:param payment_id: ID for the payment (must be None if
:class:`IntegratedAddress <monero.address.IntegratedAddress>`
is used as the destination)