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

pull/66/head
Michał Sałaban 4 years ago
parent e7897f72bd
commit 6e410d9624

@ -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'

@ -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)

@ -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)))

@ -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

@ -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

@ -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))

@ -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)

@ -1,7 +1,7 @@
import json
import os
import pytest
import unittest
from monero import const
from monero.address import Address, SubAddress, IntegratedAddress, address
from tests.utils import classproperty
@ -50,6 +50,7 @@ class Tests(object):
self.assertEqual(ia.payment_id(), self.pid)
self.assertEqual(str(ia), self.iaddr)
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_recognition_and_comparisons(self):
a = Address(self.addr)
a2 = address(self.addr)
@ -58,12 +59,20 @@ class Tests(object):
self.assertEqual(a, self.addr)
self.assertEqual(self.addr, a)
self.assertEqual(hash(a), hash(self.addr))
self.assertEqual(a.is_mainnet(), self.mainnet)
self.assertEqual(a.is_testnet(), self.testnet)
self.assertEqual(a.is_stagenet(), self.stagenet)
self.assertEqual(a2.is_mainnet(), self.mainnet)
self.assertEqual(a2.is_testnet(), self.testnet)
self.assertEqual(a2.is_stagenet(), self.stagenet)
self.assertEqual(a.net, self.net)
with pytest.deprecated_call():
self.assertEqual(a.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(a.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(a.is_stagenet(), self.net == const.NET_STAGE)
self.assertEqual(a2.net, self.net)
with pytest.deprecated_call():
self.assertEqual(a2.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(a2.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(a2.is_stagenet(), self.net == const.NET_STAGE)
ia = IntegratedAddress(self.iaddr)
ia2 = address(self.iaddr)
@ -72,12 +81,20 @@ class Tests(object):
self.assertEqual(ia, self.iaddr)
self.assertEqual(self.iaddr, ia)
self.assertEqual(hash(ia), hash(self.iaddr))
self.assertEqual(ia.is_mainnet(), self.mainnet)
self.assertEqual(ia.is_testnet(), self.testnet)
self.assertEqual(ia.is_stagenet(), self.stagenet)
self.assertEqual(ia2.is_mainnet(), self.mainnet)
self.assertEqual(ia2.is_testnet(), self.testnet)
self.assertEqual(ia2.is_stagenet(), self.stagenet)
self.assertEqual(ia.net, self.net)
with pytest.deprecated_call():
self.assertEqual(ia.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(ia.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(ia.is_stagenet(), self.net == const.NET_STAGE)
self.assertEqual(ia2.net, self.net)
with pytest.deprecated_call():
self.assertEqual(ia2.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(ia2.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(ia2.is_stagenet(), self.net == const.NET_STAGE)
self.assertEqual(ia2.base_address(), a)
self.assertEqual(ia.view_key(), a.view_key())
@ -90,12 +107,20 @@ class Tests(object):
self.assertEqual(sa, self.subaddr)
self.assertEqual(self.subaddr, sa)
self.assertEqual(hash(sa), hash(self.subaddr))
self.assertEqual(sa.is_mainnet(), self.mainnet)
self.assertEqual(sa.is_testnet(), self.testnet)
self.assertEqual(sa.is_stagenet(), self.stagenet)
self.assertEqual(sa2.is_mainnet(), self.mainnet)
self.assertEqual(sa2.is_testnet(), self.testnet)
self.assertEqual(sa2.is_stagenet(), self.stagenet)
self.assertEqual(sa.net, self.net)
with pytest.deprecated_call():
self.assertEqual(sa.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(sa.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(sa.is_stagenet(), self.net == const.NET_STAGE)
self.assertEqual(sa2.net, self.net)
with pytest.deprecated_call():
self.assertEqual(sa2.is_mainnet(), self.net == const.NET_MAIN)
with pytest.deprecated_call():
self.assertEqual(sa2.is_testnet(), self.net == const.NET_TEST)
with pytest.deprecated_call():
self.assertEqual(sa2.is_stagenet(), self.net == const.NET_STAGE)
self.assertNotEqual(a, 0)
@ -177,9 +202,7 @@ class AddressTestCase(Tests, unittest.TestCase):
pid = '4a6f686e47616c74'
iaddr = '4HMcpBpe4ddJEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
subaddr = '84LooD7i35SFppgf4tQ453Vi3q5WexSUXaVgut69ro8MFnmHwuezAArEZTZyLr9fS6QotjqkSAxSF6d1aDgsPoX849izJ7m'
mainnet = True
testnet = False
stagenet = False
net = const.NET_MAIN
addr_invalid = '47ewoP19TN7JCEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
iaddr_invalid = '4HMcpBpe4ddJEEnFKUJHAYhGxkyTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
@ -193,9 +216,7 @@ class TestnetAddressTestCase(Tests, unittest.TestCase):
pid = '4a6f686e47616c74'
subaddr = 'BaU3yLuDqdcETYzeF7vFSVEKNR4sSGxBV1Evrw5yNBf2VMiuAwfDmiF3RHqLHkaA5A6RGiNNRUqvtaqhMtdjA1SQ1tnQV8D'
iaddr = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
mainnet = False
testnet = True
stagenet = False
net = const.NET_TEST
addr_invalid = '9wuKTHsxGiwEsMp3fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
iaddr_invalid = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi2oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
@ -209,9 +230,7 @@ class StagenetAddressTestCase(Tests, unittest.TestCase):
pid = '4a6f686e47616c74'
subaddr = '7AeQwvrLtPeYoXVPRkEu8oEL7N9wnqHjYKwSvTf6YKbHgYmw6AJMsjggzVLo21egMK9qcoV1mxCTfP4FbaGb7JEMDfpLetk'
iaddr = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'
mainnet = False
testnet = False
stagenet = True
net = const.NET_STAGE
addr_invalid = '52jzuBBUMty3xPL3JsQxGP74LDuV6H1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
iaddr_invalid = '5CSfuyzxyAV3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKppEgC5VPhfoAiVj8Vz8ySmoqYgTE8dR1yS'

@ -1,6 +1,8 @@
import pytest
import unittest
from monero.backends.offline import OfflineWallet, WalletIsOffline
from monero.wallet import Wallet
import unittest
from .base import JSONTestCase

@ -78,9 +78,9 @@ class SeedTestCase(unittest.TestCase):
'44cWztNFdAqNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH4YtKdH')
self.assertIsInstance(seed.public_address(), Address)
self.assertEqual(
seed.public_address(net='stagenet'),
seed.public_address(net='stage'),
'54pZ5jHDGmwNnycvZbUoj44vsbAEmKnx9aNgkjHdjtMsBrSeKiY8J4s2raH7EMawA2Fwo9utaRTV7Aw8EcTMNMxhH6cuARW')
self.assertIsInstance(seed.public_address(net='stagenet'), Address)
self.assertIsInstance(seed.public_address(net='stage'), Address)
seed = Seed("dwelt idols lopped blender haggled rabbits piloted value swagger taunts toolbox upgrade swagger")
self.assertTrue(seed.is_mymonero())
@ -100,7 +100,6 @@ class SeedTestCase(unittest.TestCase):
def test_languages(self):
for wordlist in list_wordlists():
print("Language: {}".format(wordlist))
# Generate random seed
seed = Seed(wordlist=wordlist)

@ -42,7 +42,7 @@ argsparser.add_argument('--save', dest='outdir', nargs='?', default=None, const=
argsparser.add_argument('destinations', metavar='address:amount', nargs='+', type=destpair,
help="Destination address and amount (one or more pairs)")
args = argsparser.parse_args()
prio = getattr(monero.prio, args.prio.upper())
prio = getattr(monero.const, "PRIO_{:s}".format(args.prio.upper()))
level = logging.WARNING
if args.verbosity == 1:

Loading…
Cancel
Save