From a037f121b7befd5a6766c3a129e05eea2e74e9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Mon, 21 May 2018 17:25:28 +0200 Subject: [PATCH] Add support for stagenet addresses --- monero/address.py | 28 ++++++++++++++++++++++------ tests/test_address.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/monero/address.py b/monero/address.py index 86cf038..95efee3 100644 --- a/monero/address.py +++ b/monero/address.py @@ -19,8 +19,8 @@ class Address(object): :param label: a label for the address (defaults to `None`) """ label = None - _valid_netbytes = (18, 53) - # NOTE: _valid_netbytes order is (real, testnet) + _valid_netbytes = (18, 53, 24) + # NOTE: _valid_netbytes order is (mainnet, testnet, stagenet) def __init__(self, addr, label=None): addr = str(addr) @@ -40,6 +40,13 @@ class Address(object): nb=self._decoded[0], allowed=", ".join(map(lambda b: '%02x' % b, self._valid_netbytes)))) + def is_mainnet(self): + """Returns `True` if the address belongs to mainnet. + + :rtype: bool + """ + return self._decoded[0] == self._valid_netbytes[0] + def is_testnet(self): """Returns `True` if the address belongs to testnet. @@ -47,6 +54,13 @@ class Address(object): """ return self._decoded[0] == self._valid_netbytes[1] + def is_stagenet(self): + """Returns `True` if the address belongs to stagenet. + + :rtype: bool + """ + return self._decoded[0] == self._valid_netbytes[2] + def view_key(self): """Returns public view key. @@ -73,7 +87,7 @@ class Address(object): 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 19 + prefix = 54 if self.is_testnet() else 25 if self.is_stagenet() else 19 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))) @@ -95,7 +109,8 @@ class SubAddress(Address): Any type of address which is not the master one for a wallet. """ - _valid_netbytes = (42, 63) + _valid_netbytes = (42, 63, 36) + # NOTE: _valid_netbytes order is (mainnet, testnet, stagenet) def with_payment_id(self, _): raise TypeError("SubAddress cannot be integrated with payment ID") @@ -107,7 +122,8 @@ class IntegratedAddress(Address): A master address integrated with payment id (short one, max 64 bit). """ - _valid_netbytes = (19, 54) + _valid_netbytes = (19, 54, 25) + # NOTE: _valid_netbytes order is (mainnet, testnet, stagenet) def __init__(self, address): address = str(address) @@ -127,7 +143,7 @@ class IntegratedAddress(Address): """Returns the base address without payment id. :rtype: :class:`Address` """ - prefix = 53 if self.is_testnet() else 18 + prefix = 53 if self.is_testnet() else 24 if self.is_stagenet() else 18 data = bytearray([prefix]) + self._decoded[1:65] checksum = keccak_256(data).digest()[:4] return Address(base58.encode(hexlify(data + checksum))) diff --git a/tests/test_address.py b/tests/test_address.py index 7ddcbde..cd91ad3 100644 --- a/tests/test_address.py +++ b/tests/test_address.py @@ -33,8 +33,12 @@ class Tests(object): self.assertEqual(a, a2) self.assertEqual(a, self.addr) self.assertEqual(self.addr, a) + 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) ia = IntegratedAddress(self.iaddr) ia2 = address(self.iaddr) @@ -42,8 +46,12 @@ class Tests(object): self.assertEqual(ia, ia2) self.assertEqual(ia, self.iaddr) self.assertEqual(self.iaddr, ia) + 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(ia2.base_address(), a) self.assertEqual(ia.view_key(), a.view_key()) @@ -55,8 +63,12 @@ class Tests(object): self.assertEqual(sa, sa2) self.assertEqual(sa, self.subaddr) self.assertEqual(self.subaddr, sa) + 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.assertNotEqual(a, 0) @@ -120,7 +132,9 @@ class AddressTestCase(unittest.TestCase, Tests): pid = '4a6f686e47616c74' subaddr = '83bK2pMxCQXdRyd6W1haNWYRsF6Qb3iGa8gxKEynm9U7cYoXrMHFwRrFFuxRSgnLtGe7LM8SmrPY6L3TVBa3UV3YLuVJ7Rw' iaddr = '4DHKLPmWW8aBoEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuhAR6GpL18QNwE8h3TuF' + mainnet = True testnet = False + stagenet = False addr_invalid = '43aeKax1ts4boEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuh7PFUAmd' iaddr_invalid = '4DHKLpmWW8aBoEbSyzKVbbDRmc8nsnpZLUpQBYvhUxs3KVrodnaFaBEQMDp69u4VaiEG3LSQXA6M61mXPrztCLuhAR6GpL18QNwE8h3TuF' @@ -132,6 +146,22 @@ class TestnetAddressTestCase(AddressTestCase, Tests): pid = '4a6f686e47616c74' iaddr = 'A6PA4wkzmeyWik5QSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUBbV6YbfyqvDecDn3E7cvp9b' subaddr = 'BbBjyYoYNNwFfL8RRVRTMiZUofBLpjRxdNnd5E4LyGcAK5CEsnL3gmE5QkrDRta7RPficGHcFdR6rUwWcjnwZVvCE3tLxhJ' + mainnet = False testnet = True + stagenet = False addr_invalid = '9vgV48wWAPTWik5QSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUbbV6YQN2Q9ag' iaddr_invalid = 'A6PA4wkzmeyWik5qSUSoGYicdvvsbSNHrT9Arsx1XBTz6VrWPSgfmnUKSPZDMyX4Ms8R9TkhB4uFqK9s5LUBbV6YbfyqvDecDn3E7cvp9b' + + +class StagenetAddressTestCase(AddressTestCase, Tests): + addr = '56cXYWG13YKaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkeUVogGN3' + psk = '7e33891fe6ea30c7fd79d48e250906329104dc77407cf732699f41564df8ca8e' + pvk = '77a3720428f91f0f58a196bb374f703b3ca45fa55f0764adc81ff241c4c797f3' + pid = '4a6f686e47616c74' + iaddr = '5GKCZK5VeoqaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkehhE4RH8N7QfEAC8jMy' + subaddr = '7417qYoKBoYXCugU2KvJBZExmyjav4n1MVME74AeWNwxQ39wKtbWdyP6YGuMK6C7HkAjBuVcbUYmCWbxDLwk9GAX4qyb48U' + mainnet = False + testnet = False + stagenet = True + addr_invalid ='7417qYoKBoYXCugU2KvJBZExmyjav4n1MVME74AeWNwxQ39wKtbWdyP6YGuMK6C7HkAjBuVcbUYmCWbyDLwk9GAX4qyb48U' + iaddr_invalid = '5GKCZK5VeuqaT9z1aEy2hb9TZNnxrW3zE9S4nTQVDux5Qq7UYsmjuux3Zstxkorj9HAufyWLU3FwHW4uERQF6tkehhE4RH8N7QfEAC8jMy'