Add offline subaddress generation

This commit is contained in:
Michał Sałaban 2019-01-03 01:32:56 +00:00
parent 6c1f667840
commit d0a2d35176
11 changed files with 409 additions and 32 deletions

View File

@ -1,4 +1,3 @@
import warnings
from . import prio
from .transaction import PaymentManager
@ -16,6 +15,7 @@ class Account(object):
:param index: the account's index within the wallet
"""
index = None
wallet = None
def __init__(self, backend, index):
self.index = index

View File

@ -0,0 +1,72 @@
from .. import exceptions
from ..account import Account
from ..address import Address
from ..seed import Seed
class WalletIsOffline(exceptions.BackendException):
pass
class OfflineWallet(object):
"""
Offline backend for Monero wallet. Provides support for address generation.
"""
_address = None
_svk = None
_ssk = None
def __init__(self, address, view_key=None, spend_key=None):
self._address = Address(address)
self._svk = view_key or self._svk
self._ssk = spend_key or self._ssk
def height(self):
raise WalletIsOffline()
def spend_key(self):
return self._ssk
def view_key(self):
return self._svk
def seed(self):
return Seed(self._ssk)
def accounts(self):
return [Account(self, 0)]
def new_account(self, label=None):
raise WalletIsOffline()
def addresses(self, account=0):
if account == 0:
return [self._address]
raise WalletIsOffline()
def new_address(self, account=0, label=None):
raise WalletIsOffline()
def balances(self, account=0):
raise WalletIsOffline()
def transfers_in(self, account, pmtfilter):
raise WalletIsOffline()
def transfers_out(self, account, pmtfilter):
raise WalletIsOffline()
def export_outputs(self):
raise WalletIsOffline()
def import_outputs(self, outputs_hex):
raise WalletIsOffline()
def export_key_images(self):
raise WalletIsOffline()
def import_key_images(self, key_images):
raise WalletIsOffline()
def transfer(self, *args, **kwargs):
raise WalletIsOffline()

View File

@ -49,6 +49,13 @@ def xrecover(y):
if x % 2 != 0: 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)
By = 4 * inv(5)
Bx = xrecover(By)
B = [Bx%q, By%q]
@ -62,6 +69,20 @@ def edwards(P, Q):
y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
return [x3%q, y3%q]
def add(P, Q):
A = (P[1]-P[0])*(Q[1]-Q[0]) % q
B = (P[1]+P[0])*(Q[1]+Q[0]) % q
C = 2 * P[3] * Q[3] * d % q
D = 2 * P[2] * Q[2] % q
E = B-A
F = D-C
G = D+C
H = B+A
return (E*F, G*H, F*G, E*H)
def add_compressed(P, Q):
return compress(add(decompress(P), decompress(Q)))
def scalarmult(P, e):
if e == 0: return [0, 1]
Q = scalarmult(P, e//2)
@ -92,14 +113,6 @@ def Hint(m):
h = H(m)
return sum(2**i * bit(h, i) for i in range(2*b))
def signature(m, sk, pk):
h = H(sk)
a = 2**(b-2) + sum(2**i * bit(h, i) for i in range(3, b-2))
r = Hint(intlist2bytes([indexbytes(h, j) for j in range(b//8, b//4)]) + m)
R = scalarmult(B, r)
S = (r + Hint(encodepoint(R)+pk+m) * a) % l
return encodepoint(R) + encodeint(S)
def isoncurve(P):
x = P[0]
y = P[1]
@ -116,28 +129,29 @@ def decodepoint(s):
if not isoncurve(P): raise Exception("decoding point that is not on curve")
return P
def checkvalid(s, m, pk):
if len(s) != b//4: raise Exception("signature length is wrong")
if len(pk) != b//8: raise Exception("public-key length is wrong")
R = decodepoint(s[0:b//8])
A = decodepoint(pk)
S = decodeint(s[b//8:b//4])
h = Hint(encodepoint(R) + pk + m)
if scalarmult(B, S) != edwards(R, scalarmult(A, h)):
raise Exception("signature does not pass verification")
# This is from https://github.com/monero-project/mininero by Shen Noether with The Monero Project
def scalarmultbase(e):
if e == 0: return [0, 1]
Q = scalarmult(B, e//2)
Q = edwards(Q, Q)
if e & 1: Q = edwards(Q, B)
return Q
# These are unused but let's keep them
#def signature(m, sk, pk):
# h = H(sk)
# a = 2**(b-2) + sum(2**i * bit(h, i) for i in range(3, b-2))
# r = Hint(intlist2bytes([indexbytes(h, j) for j in range(b//8, b//4)]) + m)
# R = scalarmult(B, r)
# S = (r + Hint(encodepoint(R)+pk+m) * a) % l
# return encodepoint(R) + encodeint(S)
#
#def checkvalid(s, m, pk):
# if len(s) != b//4: raise Exception("signature length is wrong")
# if len(pk) != b//8: raise Exception("public-key length is wrong")
# R = decodepoint(s[0:b//8])
# A = decodepoint(pk)
# S = decodeint(s[b//8:b//4])
# h = Hint(encodepoint(R) + pk + m)
# if scalarmult(B, S) != edwards(R, scalarmult(A, h)):
# raise Exception("signature does not pass verification")
def public_from_secret(k):
keyInt = decodeint(k)
aG = scalarmultbase(keyInt)
return encodepoint(aG)
aB = scalarmult(B, keyInt)
return encodepoint(aB)
def public_from_secret_hex(hk):
return hexlify(public_from_secret(unhexlify(hk))).decode()

View File

@ -1,3 +1,10 @@
from binascii import hexlify, unhexlify
from sha3 import keccak_256
import struct
from . import address
from . import base58
from . import ed25519
from . import prio
from .transaction import Payment, PaymentManager
@ -36,6 +43,7 @@ class Wallet(object):
self.accounts = self.accounts or []
idx = 0
for _acc in self._backend.accounts():
_acc.wallet = self
try:
if self.accounts[idx]:
continue
@ -184,6 +192,38 @@ class Wallet(object):
"""
return self.accounts[0].new_address(label=label)
def get_address(self, major, minor):
"""
Calculates sub-address for account index (`major`) and address index within
the account (`minor`).
:rtype: :class:`BaseAddress <monero.address.BaseAddress>`
"""
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)])
m = keccak_256(hsdata).digest()
# TODO: OK, the hash is calculated correctly. What's missing here is ed25519 math
# to do the following:
# D = master_psk + m * B
D = ed25519.add_compressed(
ed25519.decodepoint(master_psk),
ed25519.scalarmult(ed25519.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])
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,
relay=True):

View File

@ -1,6 +1,8 @@
from . import test_ed25519
from . import test_address
from . import test_numbers
from . import test_seed
from . import test_transaction
from . import test_wallet
from . import test_offline
from . import test_jsonrpcwallet
from . import test_seed

View File

@ -0,0 +1,62 @@
[
[
"47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef",
"84LooD7i35SFppgf4tQ453Vi3q5WexSUXaVgut69ro8MFnmHwuezAArEZTZyLr9fS6QotjqkSAxSF6d1aDgsPoX849izJ7m",
"89U1fxtSjwuF1UQmC1bYspT8dMgQLc9433VUrjcaLQ9w4dK6Jb1Wc3YdwejuCmdY451Z3eRiXg3zcSE2aQLGhsYECo2bsiQ",
"88pqQ8Vwjwpiw3HA8Xvr575gakn4KH3MvCmt2HgAn2ywc8YxhpKFjbJQsX7eR7hMBSUKRJ6VGzZBsQMoHKcVf6fDVSvKb5y",
"89h4ii3UQoKEF2ytqEaG1cSPinEiwc5iRiRL6JomXGijiag4bo93jjuFmyfBj9kiE1ZU2dE8Yb5SLaRyCMc2mBTHJHduV9N",
"88LWznBFZh5XGNpqZXx1GJZ9hYgd8N8qvUdEgiy1fXfEYuTyenhdUe24itzekyydSxKY65GaT8taWCEzHK87Ky6ELxZPzKU",
"83b4QihSFfDbs65VBh36VWFJHPLrmwdN6UfX4wHX8UC2NX2wBkoCX7MVGNbUgmWVUq9zqj8PM2SQmRoSsFRHFy3K9f8J1NU",
"86ycKZVPr7EdncyoeM2SzUZCQ24Nzz8WW4WapfYxWomgDJj4wUQ6i581m6naAEW8gvRVrHucze9FvEVBGVcnYYzo6TupW6S",
"8AuUoksJzoF19LJvsmX8YR6vBJWYkzteMcJGRy76iwjWZcSg41uyfzC9rhWfJeT9NNWjpUmhDHKw9aoDBgLjnSe1PQwjvWZ",
"8AXCb69SM49PNQcQSeVB6JEc3qR7cXExThCw8ox4Mpo3TX5FvD8WRGNM2BgpApPybRG1cdvNqmswN2rGx46TBm7b4staEEV"
],
[
"83MdKpu2ANWNrbT8Heg7UPbJ4sUM3TEUX2oTRFrgFrC1Pdq4GLhf6FG8p2o2E2zguw5EkQHApqGyCifncLMyi2Jr3UUiGyE",
"84kuGxcFKzdRoN5X6HmWbX6gwHXoDKfqBJoUSCu9ifejLYeEQSzu8ViLt4SJBss1ijT9Duk8Awn5tKFy7sQxWvLcDAR2bNz",
"8B1ktYJrKtBJzE5dds8Fg3EjMPo9dNKtJNePgLFDqqR6EjCZnhjZCWF88wVwcHdLJK4BrzYWV81PZHUztnsh9zisAsTSppv",
"83VX1Q16Zbv1ewS3sxbSt9Cjm4Z38m4sYNh7SVPVVtak1j4WzDqq5XzNa1NuEeWTBZjJRYwmjkHorHHACY3BBrTr6Ftbr8h",
"82Y7EaHa3Td77uCMFtL5MhWS6svQPE1Gvh89dnRLXSjD4tTPbaSsXk9gqGYEkrwXsqH7zXpoHZR8MZVDC8isM3L3JWtPkFM",
"85oDKs2QpUWHZ99hR4rFThSBujnMDAREvEUb2ooLgZGd6sY1vkMUGETSK7gYWBmwWEM61XxpXuj8cSnh4dCsGVVTB4LUY4q",
"86q2xdB6MUP8YeFPuS4aqVNkKMxwmKczrSN2mg3FN7JNVbMYjcw3FyNSbL5GCqrBdPHWpk9efKyw9LkNuXfRm3rY915j85s",
"83jHFZ9RBJk33dMinJ7HJFCbVF49v5cdZev873iQvABTSme6pEBF3AR2MmrkjcUuv6NZi1UV79UYf1QFhy21iF9EUua9nVW",
"8BWsU8ezPMf6adzVvxFurLekoARoFJLieAm6aycTwuAo6AGu1sMfdtW1diHhYgogq9Soon7YwAnPT2pk9cEsZgq19qmHS8M",
"87EVWV1MCF3C3j9tYBP7YTcU8ML8nXHSWXkDWBuXPXjkVXpGMEUqdYfGshSdHUUgYca2upy6zTo6v9oX8Qt4Kje5CpACB65"
],
[
"82vEjyhAtn58qSkoMKcdG4XFMxHbRwatbBvLcHEL774f2RoDR9yduUhPCPR864RGrgXM2awr6SjeJChCW3jv3BtEMrQpkbp",
"85mB3RjnZEoUC1mi4TxvRi8HEb3FqJf974sEzmsGPrQVMF8GDiEqY3PMgQuqjZsDY9MzCpQPQT5SmGkWYyWpzrRG9yP57ip",
"865XMywqZgufky63ZWPU5DALsUd33nsn2NhsznUoBsNDUgxz9upvUQsUkwmJ2RZVZef31VdV82qKRe7BkfNsmWvRVogrAN9",
"87tJKsNVuACb6tbz2mz6VdPBAKZzCDrrCC2ywdqh1FCRCHpjMNRAP4c2uLWX6nboyjVP6sTU1YR5UYCBkqLgpVWQSBeumvd",
"8Br4ZBkj6AVEWnvyqMuoE5AJMYtNncqy5PcKew88mTGPJSCVZyoHvSvDK9cTSQ5cZ1Un9tjScY718bHqEUauPhQC7fA4Abv",
"8677kud5PZ3fTMwQryC1Tw7HMLKLYpPkQE9zLbyuZTGB3vumdfD6Uh72uqjn7MopfTAB73vbMS8YdhrRRC5FJYb9Jy4N789",
"88BAAY8e45H6mAiUk3TafLL3HXCu9FdNQXEe59MY7ogFNYJAgF2NzPVTqT8ppnCD9h4ks3QRx3aMeMHFPEZb9VXiUx7aSzf",
"859jYzfKGCAfjaFzR3xAeSWtxvJA6PRppjVYGFMXThoN5jym82j6zVjRbJZ7EDcRcfg6dJwAwLeefFEuXnB8PeHHGt325FM",
"8AT57c4dPboJU85RBLeS3mAQAuHxUqZvKBqsi7pEFA8X4jhxqESKLt9cfd5477iqcZNgCgCGfc6RBAjicfAiqQ8QQLVjECj",
"87m4XYZn3x3Hf3vHsNyt9R9CFR8QrAVCZKeYPmEtftwgLU1ggQ3h17g8yUmSxKdgLGKtyEwpoMkFe2HQ1gSA2vd29ZpAJgd"
],
[
"85pksp3TvcreViEHc7KkB9UEQDie7Zduc6abeKxMsY6uUE1WtjwCkMdGLY5pWmifqCEh7Me8Xa59tCjmhR4oDtPCQ4nwkwj",
"88rzK1dpNHY8EMYgwydsRiHNr3UfMKsehYKCG1B3dV2rNmn7fPykXq5MoixjCSU1qmDtcLghuvg9EXrYHA4Ej9wk9dVJe59",
"87kqU8apGto6rZ5ZwuuW1pLDmnircUB27XYSGFQF4D8vcZr87cM9MkNJJGShREYSLR2MiK9WXJAvGWBDT47C93Td2rCZfCu",
"8BcNaua6uG6RSUs4vhJ4uUC5fG3yaiSp2SYP4DdEaDTkdtJgXrTH3ZcYdgCPtUYELc76znQADhDhaNmi9w37Hc2e6tLd13k",
"8AUZCjSAwp31YKmsgd4MZEWKTcDVFL4Xg5FFvVoDV2oWBdLgeozA8JLNT2GQTLZKrzcBPNfPFadDBFjwnjR5TPTZ6UhLJGk",
"8ALkYCkxwnE6ZnbJyEqnmXTWFh7owB4MWjZsZbkjbMtodBf9E8etmobfTn2dUYd6vr98SgZAnSudZXnX3GubKzU5F8X7bgU",
"83HnemUBptw1MAzUpCuU1PaMDzpj17K4sFdcCPDMmvXAKcC4BdHwHib3xmARdCeMr1ZVVZUgWTzusjBsKoEoSKnoFrrfEgf",
"85xU6QncBsjiBeiUVs7df5dRBH6dLwN7m1oLC1ZnQAuQ2vKbTmczL4wegnaFZLKtWhCtDbWQYgStwj2raHUcfDtaDZEfKx9",
"87DRqkCNPW11XWeyozsToXae5nSKxPDEY3my3gFQ1Yb7RXqsEWZ5BbWaDgcfMCnjd5RQUDoDqe8MkMeHNvsCMGdtKyLsfJL",
"8BBboWcfWXNh2fNTNiMUZeaYABYQe9DNm9MPHKywogFnHk3VxTk2yNAHYisjeZ5LoFZerCpGwuburgknDjUXa2P13DRaegi"
],
[
"87Schz4up6jEpfbHjia2q7VzruvGdhLnJPkyLDrWJnuzBg7KuPPBQk32utS6xA8crgW8PPVqoLT3tLyRPfemT4j4GosT7w2",
"8BWmh7TkZeNewjSHauaWLFYyNuaSzFTNRNrcwUoFixMB2j1B8MdtcnpRf5sRtv6AgXhrkJvW75TdA1gHTC1iZq9vLXirMNS",
"89mcRTWF1XCeu7dWaQr5C1S3fZ6pyWki2jRVhaybgwf34d8rgkUsaDvj6DwLeziLar787tsMBxfQgUFfhsSLek1PNCkE8JA",
"89bfr4NXY4D8jczTGpVxn5YhUbDohyJ6E3RQ3vzie4NMAwdU7EmCj9n82bn7k8z7krZsih2yhtUHhiwRBm9QofSU166F453",
"87bGcZzkTwLUJRTjuG7yhdUe195Er5aqY3yaUWZwrgRMUn6MTDG3euP9TwuM1TqqRMPAquhF7abayidBrr64mTJj5LELx9T",
"84kGzr5mv65UrNzqYnwjkxiiGEciCYeiE3fMZMfRLZt1LN9hc5idZT3cXyF6VKhcseYND4hA7Db3mabxvoUTPGHh4FGDUX7",
"89XMhHqZDsCBES3uSznQT1gbqi6GnfFLfAaeftGoqtDd5ei6ovBQk128ic1NWFkXGLCPK6BHRxBJ64tpa8uasR1h2XX1Viw",
"85ENJqzHuycgLL64xqxvDhbRhekk9pXCt86AEVBqUFo4Rqxfe7ffyHYZrhryXpgZtV7ceFDY8N53bh7beRecb2NxKvtM7CP",
"84qSwP2XSTK7ijaDwtFUd6eF3NqQfW8ET31oL13GGv4XRpA35aFqYUUcXspsSJYeNXQxGJGCzEcLsCRoYM9ok2ta9MBhgUv",
"84nTtTGVwaCcCVe7uUSkshC9Mbbt4EEXkYnJ1uQJrH4xE7YHLAeyJUTiFRJ19dAQwU8qehtrUEWc5AkcTAbqUuowMEhBLmP"
]
]

View File

@ -0,0 +1,62 @@
[
[
"52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6",
"7AeQwvrLtPeYoXVPRkEu8oEL7N9wnqHjYKwSvTf6YKbHgYmw6AJMsjggzVLo21egMK9qcoV1mxCTfP4FbaGb7JEMDfpLetk",
"73YMtNiQmazP56aYSAR2SjT6uCfCNEBFnfYc4PHUUJaqC8urLbipC1Z3NUUqoraQV4PJmLRHV81cnadnoUAcLiqD8jQB4mL",
"7Ab6cPPwU97LD8HwX1X8QSJGLtkLq4vYHG5RFYiLXAvvX74yHoMxPGYXPchEmeQaVGjT72mXBYouNA8HLSvnxdP9LD931f3",
"73g1pYwHUuPYLW7AbvpGAJGhyJBUEZyiGhMbh3WPMHp8gLAhk8vN4Z7dKEorEiredWZSfC6zbKy8qMSzA51pHL6M8TT2ECH",
"77P8NdNM9gZVYEy8ieogWY8oDr5tpkBb3AvHSy4pPPktJMGjBeQsKzjb4K2k4g8hkM6vZrgSuH6UC1Diq27upfCSJBUkKkd",
"782sDPUzzbgVbc5iDR7hziFVXTpCdwL4SDzn62MjVDZaHPhp2QDjmcwjb4N5nEapVKHzXkd5PkuB42iKRjkF6HEf5xcfeQL",
"7BAZYSZKLjii4MhRivcNqSV2TLsKdhtr2TSBVUdgjnkDSctE75vBsdoGuJ1QL7NV3y7wsrBq8nTcCZdRRfHbmuVxE3iAd3x",
"7AZJHyGefLwcYVnyaeGaNgPRLVP6oDaNRRmknyLckVCANoZp6MmsB4YX8BZcg3AjyXAM3JwM1SYRvhwH5stCVL2cA23JPzx",
"73KFRi9wGafL6osnpjAn4UiyU2WEDVgfKSfSnou8oV4jdEg2CQHdb99YDWS3nLN7DwQaERM7pZaq1AHVz42fJ2dm3eamP1s"
],
[
"73WqhTXVZM6Rq9TLi2EQHGK5jwopSRNHheT6JXCfmg9DVPps4um6En5Yn1RMcKezSM3iyUZYDEnEdamWPABSpRS1Eq7gnsr",
"7BeLmCeBQ1LHXYxUyApGJf3nj6TjGDTEX3NtbdFfNg2SKewZFcDFZnFUgJy5kFNAig2CEXAo3wwGodDvknhZ3R6VE1w1i5h",
"77ebau9MYFkN8YNNhkC9msLEKJh42XdrP21ScYtayPRkEkNfwdMqr3eNV8UEkxrcVtM7psu1XzUYWZB7HPApuVyw91dXjcp",
"733Yfh5QvoFiHwE6unFnHD6ZZcegJQPdGZL5sxtBYVRv6UmCnbb8uTGBZeeW6Ua3sCVaz8TUtCux1UXLEbaLPSQVNLRgwwV",
"75VewnRuMctjjJNs6kHPRcAwXYDC4xPq1QDGnWBKtDBRFDJ9EHRJ4uvbaYKBXXvsXR8Hja28LAmftQ1jhFq6GAXb749pftb",
"75vUzzztkyRFvRmxU1Zc66dRt1SsgK5waAfFMHN4Y5r2bxUU4vLn8dPHMPbv1ZmcsANmd2p9ntkAzTxhXmsZEakb9ZqJiGP",
"7AW25a5qHwDfJipEtw3C2j5mJYPxr1HiJYgb4gxeg4ac88hxPQtnzGqLnCtsPrpdn3PxvpNnBqHo2JR5DwJirgTvL6Qohrf",
"77YaotboUyiYftkqN52mZbPV41eUbPvwj6EzKd4SUaBHSrYXVimYivzW9paJ1pz3zsMYkDPAMbSaUWo9ysHMdLo8EU15Lno",
"73AExKobDPQTuYoAZ2CDBE71HnP33YQw3FDir36EV3JcTDgX4gVCCZqHZ17zMqX57uG3XpVXmBUkZGG7nJczfQaLHA2vznq",
"73ttD7a5qDtTCeFx97BsiEHjEqZmrQ8ob5JrBGvPV8Gu3T349eaZ3ZnEvqUbTjV5qnNiCymZZTZwRcj1fTczkaBk7Dezqey"
],
[
"78kWgiukLJxNFoydUFRFWJXspLE1SKMZN6A7NutrsQbXQZkCY9WwCnyRCgBCWG8nNYjAyLGDTe1gLdJgeGfaYomJGrPvMPT",
"79TdTdLrhAVKDwbte5XHpd9Q1oJx7y5nUcDh5jYEHN8M7i9gHpLMDugYnesqKUZJ5BLuAkxnsSo56b5Y65jjhvnu3QrLMuf",
"79aQMJ1zsCyHrHfZFv3KtCDRZM3RGuNF3BqJ79Q9BCzDGJ53bm2yej9AcqCj7TrqKHYjYvuLSxNV2h1b3zKDfRFJEB74bt3",
"78Axv2Zd9HTaT8dxMcxF3U23nWGtsjUL2AUD4jA1u8c9AjhEs4qsBZCQDNavkNh6zy2nHw4md1GcwDXnJYEvNi8572uVn1e",
"76oCbbYGsRwDw96uJCbkHPXigtXfk15azDmgqinBZ9pe8H7b9um4KYvPboMmhtPeE1Qt4eKnLmKqs13JhHBC5r6NAk6BxGP",
"73gFMMdk59McfEhLhJEREZ5sLT2WtRrEGLvz4wVXkEe7infmwTUxQphFwXn4jscRvt4tVAnaxX7qoB253WVsV8hDRZ2uZoh",
"78j5teCaMFh8U2SyyHPeoQ2ZuXzFYuwW3gTZmAa35deYbYKii1EfZrQHTgBbXztPrsXPVmP8tM6iQhkrVjCuT46BLk4x5d4",
"78ACS1bi6mVUq8QfBY7hJwcL4LRgNBPPcR3v8h7hEhj81wUDLHHjPKjAWBhSJ97XRENP5CguNec528CLvfajfiCEJwUoTTC",
"76CCd77MheahSt1jX6WfBN9xPvNrnX5MXUTzNepRa41cMfpHbccoFnN93h6vLjXTRY7sKp2ZFEqBBD15uqhcN8HXMusHKjn",
"75RroztLkkeVGxzFt3wbZ2Dh77wmXaUiUMQSwMFfKKFPEqriHVzYEpkK9FMResvRmiTFyZMmCmfrgLqCcQ3DN7S2NTAaSwR"
],
[
"79Tci63JsCjTVByYeACvo8fYVuV1eUAYvUiqUcq4ahMWbJ32KAKRt6u33YPiEmjBHSRLu3ycedGsPNS5ZPaz9iai7z3sRxk",
"7B7JZefpcm24CmrrSJMNzkfJvfXMbzPXUeyQrSEJr4wW5kBWG3wqhSEgMfmFz4AuP8UZUB6m7reFnQRN6HkcEksPDaMNnJ4",
"73wtz5eGxiV1pLGA7J8bSnbjDpGXZfEkQWugtEwP6bhuf7eW6g9UXgXKTsZqe78qDnNbzcdM3LVxg6Dh1nbXAReVTDsUVZy",
"75fAk67wWr1GpTH11vvEVLSYw1MPxvTgPZSi2sSVZaA5CTxS2YmhW8k4rsgzf1kRj4AeAKrZxs2WBBxQdeS8kyJC7j8u199",
"77NqmJoiak1VQZhep1bvce5i9HCriv9jAWmNPqGQ4Gcw3U1ycUo6eAj5f6CsmCRQmpNutG7yypidoSKmyb7FEaxXHPcGJ2D",
"7BkT9XCvK8YXnTZp2jVQ4aPG5t8ibTP6rLPKgqncqmiDLfm2T4mnoLpPwBFh1C1Fn88L9cFzFzvSHdHacCZBwiv8D7dEYKP",
"76ntFovqGQEVL4k1oKZMPNY3rWfvKaVhvbXg4KNQee7iZNZKZi5exF5bobqAJ4a3xmaicgBHmput4icW1q9FBJQJR66rALL",
"738J577wiYTMQoSQhE5JaEey5dhDSXrH6aPLzwUAkxjR4DpxaMon6ED8Ynp9G2EFTTXRHDx7bhnFENpdAVBbufvi7o9k2rr",
"75yKvJDzVKj7W3CdMVJRgpcGK8Rdesno2E1QJgitwb8NLRN9boGp8XeV5ZZtUbKgmm1PwHi1rtPR7NxCB5siS7eBCCmFJsm",
"79uA9y4SiXBAJujB2MPzzTFPcZ2PfBdPFNzMERszuN1ZGtZzoTk9BB4MU1YErF9BGFQhqHUwbxyNYMvbkVtLuqiCNRYEiv3"
],
[
"744ZzJeiqBMKtPdfLtTFR61H2mwZSRnZr8ovCNAFL2dF2vfyQ4AC2mifxDEm95HipZND6M5hhsiR66dTJuWAiQ7hJgHpZ5y",
"752fmPXtgVcjV4nWBQFJWoZHB6vY3zfbgHgfLzLg8Zx87dJziiqSW7WcWzkpHqv4bCKfwaAjTpWKbjLDRboDYLwMPJVhQ2y",
"73Jw9BDUxH9M75MamR9kskEKBDPfvjYMvTP1WK1o7KvJTEoWUMNewifEkS5SwjqKbLE7K9ELwac6PfVBP7pb4B7xD1nzpWS",
"75aAVEyrKAhYo3SY3WDm15bXybQY3EP3D2SE4WfFU3doPEsatx8CSeV9Q3E76re9ZNKwSA9SAs9WFNrmAcpL79SnRnBiBVf",
"763yuVT9DNYPK6b9agG15x6gVwRCPS5KjETpQ9snyJsPLKv1HoPgagMY8GauPTgfowfEiyKBudVWhCaKqfLgqnv2MUNuEUB",
"75h6qSWVVthF5hNhE8wNDkjRSiw9R4wfX4sFxNgGw54K3QZQTQYs41HfbHJbx2G8gh5UPsXnJ7YkYgMou5eAH5fc1R2t99y",
"75a5At6zLyW27m3yR2rjuNDLWzgMa3r9yE5yE49V2bDXUQxnwMggneff4tTofUM2JDZt7d1VQXMXm8zgJoGXgeMY5iTLT17",
"79xa3UNp6dpAqyGpzZyiSFWocK4A3pDSSEEu8nLiDDzdXZfmR2MDUP5Wu544WjRS2nce2nrZhPZ3saEmHKtWXxxp9tfbPhJ",
"759k7wLcsG8WGxc8QucsumgRTD8dApgUnh2WMyYYzg5UGGojtGUq4W6ZMZer7hn5vx6Nj8toj9eSUfJ8mXfWXYcSBr3szU1",
"76gfDy2DkWZdkCQUbaxkqWDKvo1GE7AY6CLgv9D98ddkHiU2rszQWDi5jamMwhfPx4b16KSxogQJRZNeTaU2cR2i7PMKsiW"
]
]

View File

@ -0,0 +1,62 @@
[
[
"9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N",
"BaU3yLuDqdcETYzeF7vFSVEKNR4sSGxBV1Evrw5yNBf2VMiuAwfDmiF3RHqLHkaA5A6RGiNNRUqvtaqhMtdjA1SQ1tnQV8D",
"BZSWhWTMA8TEJRZL4MopGLBrN2P9jAjnfTwbN2mRhFHmfUkZwpzJ4anKb1Wd2BjrUDSzzsbSt2bxAeXRvXvNsPT58TEDMh7",
"BYe3G2jNpAdb2m5eD1y76Q7pd4WibxqvbfBhyo5sNgNUBPfac2Mu5M3FhdKmHVsMECQFR9bbWpf3TRXwEqeqK25JEnKskbc",
"Bfs6mpyksF93RX1eDPhY3TWWKQKUvJUcygjhv8pVyNtifcTJMQw54gVGZYkk7P4nAyYeHb2Fjus7B1opGkeqdJ3t5SXkCAL",
"Be1Vp5Y1TGs6eKF4UiqenUfQKkAMuytWzTd5oGQBVxKrRYJr99r1jZ6TeXgBXqpZ3AEuYDnLD639JYSdUK5GfkHxFA31x9m",
"BejKhUoDTZiVfs1jqNtUSDAmGPi9xSXhVVHNStkzj4cv5GWsG8DcXUZNccYMCkpYUYSsLCTPnFE1x7zkmzGYT442SkCuPQd",
"BcUQoLEptAiRwS2nWnz5f8KtDt28i3eq41ff1wXcLymX79QWQKHQKGXSeC8xh3JV59KBijGXfNDJWT752HQEU6T8REDetPZ",
"BgAYtGuSVCA7riFyWwmQdz2zCvh1VocVt6an4uQSNwgSXJXrhCGqyc285gxAdF2xLJQ4sSV2drWgaY6pHKDtY8KkCGXinFZ",
"Bcm6LrZtZBoiGJpVRKPTHNeZr4FLVaufScKaJ5Tp28WZSaWdRFE1bgFhZhZpY11nKr8SgDKRa33pRKb5tPMGsLk42xyx4YN"
],
[
"Bhk752y7cTjjfZ3sARAkkyZTBxsJAWbeidNPodDih3dKdRAs8kYgidsfNNhULLiHJwfYVqZJtGDDuNK4tG6tfg8V7EmaURg",
"BeQeMRXthNQXFgcBRUsGxZKKR2jWG7gn9SP6zzNz35kJ1VuC7fxGkKYhDWcwfpUU7gAS63KcdZiE748yS7booUndHFeR2vA",
"Bc9Bvg6wtRM5pRjTocTCRXeyePhAggk4LifsqopRCEykipwzwyE94qPFbMd4n8TsyH19cjGB7TFC9Eda4wH3kboY1JZGxqz",
"BewjdZNpycoXTNiAtg5NdEAyRJnnnp3ytGNbypjyb4Yd8GsrWLuSwGzgahNwDe4S4vCaD21cGfb9g3KtnwhSKnR9MJN6E51",
"Bdw7pMMV64ZhHAqvtRFaXa5wqm8y8g4tRKFQCUPPGNcdYAUKScprqAFgmCXGR4kVxdcKQyVQAzGeHdqxqt3mqHgSUbTJSnL",
"BePRGXi2z9dMY5dZJVQYCn75tzJ7JrjQjd3yS3EEiSd7KikTCp7gg7LdQSxvhKfmsQZZF7XJeG7JTUTVXvgeRra7EStiHaL",
"Bgu7XFUTf3NhzvMKCNdW9vh8pRdGZ68t8FQjwYUu1EXL8TVqwj3PM16EtKBagwS2HLhs6Z65Ki4NYTwHAmUgW8vdU2Hs92o",
"BZ4zbcn8y1g2K9Swm9vzfa3oHa9YmPu82ZzwnCb75QZWNcaR1xM5ZYTUcUjUDwpKXq7vpUyGWfdNP7MWcAkmbSF9Sf7gmTu",
"Bf5KMYsScBcDM87pvgcbZnbezjPPmBKhpRU1t99wBzWwcuFcT8oJZj9VNps8Ywvd8y82xMNpbZVmiPKAKuyChsRTNnQHEGt",
"BcuZ7DxxYDNdQUrWnrwQE384M94sSgCc8U4a3WivB3ZqNk8eBgormwABKrUaJ4tQBgJTfpdiAJYzcWXCbDAYS9jf4Y2zbew"
],
[
"BfaTkVhk5YoeQ3tvqNhpakfjiyXnBYJZNNMdr5uvqRDWhFezWWknmn7C54omZbsPhURZCpnq9sA5AZLAMtXSiwz5MtVW67e",
"BeC8Ce8uhsSdfeJqMfBCfNTGNBu2nVv65RUjtq9hNCrpMBB28hmm4AwMsW1LrAwcnobzUZHBK5Yn88bCrUserJDx977qVm7",
"BgNzdhRgayTPw48xvCMNmF4bWB8BfUfQ1UyU6ZURQHLT3n35giaXK3y2N5Qf27tM5r7Ysivpi1yf3AzjixDj58rMQ2XcS1S",
"Bbs9cHWzoM6ZvnU4nmQFA2aFfJRWqL4vtPBaZQ61QenQ7UqxuY6HoX3gQ1DBcwruu5EDnnJXH9hQ6ERv75KKuieu5AwPdHa",
"BZy4Pg73hi4DwdsKbTQz9oAHPHywUxDFJK4LyRZbHVpZG8HWt9mCgEbHcvkUSimjuHTLxYvSfq36K4XjrAAaYCh661LdksL",
"Bhsot4LAizKg7Y8wZ2WuJX8zTW1AK6zinJWgR6Sx2SKG9nnUJkzfBu4b2s6R3bjymiP5xbd7hbHXP8QBbi711KxWA16j7cP",
"Bay7nanyktS5h23g4SeuujEdsWXxut7LDGDzv7e3PUfUTMmCgi6ZAWg5bDPHF8icNYPQKtgi2TejVXawasaEe5PE5UZWHSZ",
"BeTUFbQPUrPKGRC9Fbjk6Ha14PaPtU5ugjBELtKhULAREH4mw4B4XbYBCtcycWqYUg8CU5Ye92xTDcc8z5XRoBA65MdVkKF",
"BdvCUZocX8xPf7zMkY5c2kQ2uQm5ABeaD4oQCDTaXHLf626eA38XFkCHuocXxx3v18PbqWHkzT17cNVuWaecD6L4Tgx2pNs",
"BYPumPuxceS2FAZj5bDxvqR55RneFNbT7bBiLdQLtpH1ESuFtTWgWSi8wD5En2evXdNy2VyfnmqF8XCcPmfwdrKK4Tha1M1"
],
[
"BgyMm6Mxb2YARcWDvVG12h6RpTzaaADrQJyzn4P1hZ5ra6zYPy3CaE9NLmMuZdjzMYPT6bbH6vA4zZzYpK7uGQyQBUWwfir",
"BgnK7oh39thSQtWXAHyj5uCeWvtEN5VW5L1QQAZJr85SMaSyUGreHmgVXmRkN4FCmuM2DvrKkrqPDAq5RMJzBuEGBpRDdwA",
"BgZrRi8UGxHiLvHnBibYTDZJWs3oo4YUycoT5M9khn6xTmr4zZ7CshSe8MxYD4fA5jecR8hPWxPWEca86S1nwbsf6FEvi8Q",
"Bd6xZJfxmCDCNydf8MEEs39XSvyJUoSs44at78hgSiCRLhnmsrtgfVTgTPaqaweVTH4hnV1gKnM2fXcxTx899wdk7pDKkEZ",
"BajjPFr9BvxavdUhZoViMU1W6j2EVbkAC3sGyeb4w7mQLF5K3CBt7XFjaQzuzk85YxQjXn5PEk3aEFnRg3bUeoWRUMqJL1b",
"BfFgXgbNm4JakCubziSwfBaRCSEUVPz3qd7TCmdPvYmt3jE8RfnrFA3YvcbQys3DncQrJREaJNw1p3ESFbQMFF8T9Wj7RYG",
"Bf8cHLmgXjQMHbf17csXrDhc1Ltjd6wmUBQ6NXCVmJvCaFTM8F1WzBCftZ9D6ckekFFU4f7cRaev7C8EsuBP2dHiLFH6FxJ",
"BcHrnGsjXJVCi7PpN2vcMNPq4pgExX3tcYsXvLqfm4PR5FCHxpYvuoBbAUXXxA9D9BDWSCJRAFvJMKLm3Rje7gmRKXuzCKk",
"BZ56YcnPXBfNGSbEkB7QacF5SXofABVhYR5EGJFM9GeVCT5RdmX4p9shhPmTfHNmtqKcaGyPtJGg9j17qF6hsnVmVCXjSPq",
"BhbkhSYb9xxS8GxiynF71X1q2KyX548ksXzJNvvrx7au8WLexBBsaMg8W79yzdqPGs6FpHAb6xTfU6PUvJxbgEHBKyASAbe"
],
[
"BbpBVwUXnajCYKyTk7VYyfN8K6Roz9WHZ7zzVZXCcikd6U4iTavBLSqTkaa2aCyKHfQ2QhLRzhwX9aUynaqRUF2LEHpQ56U",
"BbqwEhcSqyDPEG7mkknmasCwSepn1Fx5A2HbSWRFds5YfCgHa1eYZizMmwNrKPu5MDHN7fHnaZbyJd5qbb8yeSSTFx45U39",
"BaLSP572vzmBj6XuYW8Dgghj66ktDWkejTHQQwLEFWvZJYxeENihKAi6CGJLFbuztC8dcRFjC5r3rAw9MtUebebQ4JVjNvR",
"BbUhnH1aLeTR3pUGm8Ba2B4whVcBTarPkUFRNRxzPFTSRLAHKAnhjLpDHoyNgBxmmMKJDLfZsdtA6STdUXTypeZX3pJqpwX",
"Ba8i7eKK8wSgZFWqSZYUGV3RTa2N5aWpJf8rQ2AFxUC77hX1tLek4315SSEovNG3KQaxzUzWWgp65gKPijDjpDk3UKMhMod",
"Bgz9rAfVFdR3VkmSjbKkjdPGCgbnQwYUqQbq9ffTKFvhPN2UwtBPegvGg72ghLsgpZX5dHCCMGpUweJNBUguEo49LPJQE3b",
"BgZXkTeydXDQSHePYVYKu3W1aifuX7qscTpr7Qx1t6Bj6iKmS3puFPB56r22BmYyUgVX6dprSYQMbbeNTAJfD4FT5oo1oVw",
"BbTZrrWUSVECHuqmCfskFyZJ8wi1GoVDNHy8xmbqVu8aKxc3uRtiHZwUCJVfnFnpTpekoskkxkCAxfLw9dbcNawLFJxD6jr",
"Bbi4mDqnUyBTKCMLWs3V8hKjbhGKGNxHzUCmAm89xDC5DwLJNE3SZ6BcatJ4cdBFNYApw7nuoU2hZi9UXngM2GXTLJpMVWd",
"BfRbRVP2aZDMM9Bm9375djX1VxfTNZKDRDtoSJt4XcdYDWxLnEXxer32DDvQHCidCBYPu7qVUdTUjQ4Nc6kqJcQ4NMV6D9a"
]
]

View File

@ -1,3 +1,5 @@
import json
import os
import unittest
from monero.address import Address, SubAddress, IntegratedAddress, address
@ -147,7 +149,7 @@ class Tests(object):
self.assertRaises(TypeError, sa.with_payment_id, self.pid)
class AddressTestCase(unittest.TestCase, Tests):
class AddressTestCase(Tests, unittest.TestCase):
addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
ssk = 'e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a'
svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
@ -163,7 +165,7 @@ class AddressTestCase(unittest.TestCase, Tests):
iaddr_invalid = '4HMcpBpe4ddJEEnFKUJHAYhGxkyTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8M7yKhzQhKW3ECCLWQw'
class TestnetAddressTestCase(AddressTestCase, Tests):
class TestnetAddressTestCase(Tests, unittest.TestCase):
addr = '9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
ssk = '4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201'
svk = '60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103'
@ -179,7 +181,7 @@ class TestnetAddressTestCase(AddressTestCase, Tests):
iaddr_invalid = 'A7bzU6hSszTEsMp2fYzJiVahyhU2aZi2oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwZqGSmLeBXqMEBnZVkh'
class StagenetAddressTestCase(AddressTestCase, Tests):
class StagenetAddressTestCase(Tests, unittest.TestCase):
addr = '52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
ssk = 'a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503'
svk = 'fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f'

8
tests/test_ed25519.py Normal file
View File

@ -0,0 +1,8 @@
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)]
for p in pts:
self.assertEqual(p, ed25519.compress(ed25519.decompress(p)))

53
tests/test_offline.py Normal file
View File

@ -0,0 +1,53 @@
import json
import os
import unittest
from monero.backends.offline import OfflineWallet
from monero.wallet import Wallet
from tests.utils import classproperty
class Tests(object):
@classproperty
def __test__(cls):
return issubclass(cls, unittest.TestCase)
def setUp(self):
self.subaddresses = json.load(open(os.path.join(
os.path.dirname(__file__),
'data',
'{}-subaddrs.json'.format(self.net))))
self.wallet = Wallet(OfflineWallet(self.addr, view_key=self.svk))
def test_subaddresses(self):
major = 0
for acc in self.subaddresses:
minor = 0
for subaddr in acc:
self.assertEqual(
self.wallet.get_address(major, minor),
subaddr,
msg='major={}, minor={}'.format(major,minor))
minor += 1
major += 1
class AddressTestCase(Tests, unittest.TestCase):
addr = '47ewoP19TN7JEEnFKUJHAYhGxkeTRH82sf36giEp9AcNfDBfkAtRLX7A6rZz18bbNHPNV7ex6WYbMN3aKisFRJZ8Ebsmgef'
ssk = 'e0fe01d5794e240a26609250c0d7e01673219eececa3f499d5cfa20a75739b0a'
svk = '6d9056aa2c096bfcd2f272759555e5764ba204dd362604a983fa3e0aafd35901'
net = 'mainnet'
class TestnetAddressTestCase(Tests, unittest.TestCase):
addr = '9wuKTHsxGiwEsMp2fYzJiVahyhU2aZi1oZ6R6fK5U64uRa1Pxi8diZh2S1GJFqYXRRhcbfzfWiPD819zKEZkXTMwP7hMs5N'
ssk = '4f5b7af2c1942067ba33d34318b9735cb46ab5d50b75294844c82a9dd872c201'
svk = '60cf228f2bf7f6a70643afe9468fde254145dbd3aab4072ede14bf8bae914103'
net = 'testnet'
class StagenetAddressTestCase(Tests, unittest.TestCase):
addr = '52jzuBBUMty3xPL3JsQxGP74LDuV6E1LS8Zda1PbdqQjGzFmH6N9ep9McbFKMALujVT9S5mKpbEgC5VPhfoAiVj8LdAqbp6'
ssk = 'a8733c61797115db4ec8a5ce39fb811f81dd4ec163b880526683e059c7e62503'
svk = 'fd5c0d25f8f994268079a4f7844274dc870a7c2b88fbfc24ba318375e1d9430f'
net = 'stagenet'