From 38704ba8ea9d93b202b4d363123139ba27abc0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Mon, 22 Jan 2018 03:55:08 +0100 Subject: [PATCH] Handle mempool transactions --- monero/account.py | 10 +++++---- monero/backends/jsonrpc.py | 45 ++++++++++++++++++++++++++++++-------- monero/daemon.py | 6 +++++ monero/transaction.py | 27 ++++++++++++++--------- monero/wallet.py | 24 ++++++++++++++++---- 5 files changed, 85 insertions(+), 27 deletions(-) diff --git a/monero/account.py b/monero/account.py index d8f6d16..292b91b 100644 --- a/monero/account.py +++ b/monero/account.py @@ -30,11 +30,13 @@ class Account(object): def get_payments(self, payment_id=None): return self._backend.get_payments(account=self.index, payment_id=payment_id) - def get_transactions_in(self): - return self._backend.get_transactions_in(account=self.index) + def get_transactions_in(self, confirmed=True, unconfirmed=False): + return self._backend.get_transactions_in( + account=self.index, confirmed=confirmed, unconfirmed=unconfirmed) - def get_transactions_out(self): - return self._backend.get_transactions_out(account=self.index) + def get_transactions_out(self, confirmed=True, unconfirmed=True): + return self._backend.get_transactions_out( + account=self.index, confirmed=confirmed, unconfirmed=unconfirmed) def transfer(self, address, amount, priority=prio.NORMAL, ringsize=5, payment_id=None, unlock_time=0, diff --git a/monero/backends/jsonrpc.py b/monero/backends/jsonrpc.py index 304b6e7..b7e2caf 100644 --- a/monero/backends/jsonrpc.py +++ b/monero/backends/jsonrpc.py @@ -37,6 +37,16 @@ class JSONRPCDaemon(object): "{status}: {reason}".format(**res), details=res) + def get_mempool(self): + res = self.raw_request('/get_transaction_pool', {}) + txs = [] + for tx in res['transactions']: + txs.append(Transaction( + hash=tx['id_hash'], + fee=from_atomic(tx['fee']), + timestamp=datetime.fromtimestamp(tx['receive_time']))) + return txs + def raw_request(self, path, data): hdr = {'Content-Type': 'application/json'} _log.debug(u"Request: {path}\nData: {data}".format( @@ -103,6 +113,9 @@ class JSONRPCWallet(object): _log.debug("JSONRPC wallet backend auth: '{user}'/'{stars}'".format( user=user, stars=('*' * len(password)) if password else '')) + def get_height(self): + return self.raw_request('getheight')['height'] + def get_view_key(self): return self.raw_request('query_key', {'key_type': 'view_key'})['key'] @@ -167,17 +180,31 @@ class JSONRPCWallet(object): pmts.append(Payment(**data)) return pmts - def get_transactions_in(self, account=0): - _transfers = self.raw_request('get_transfers', - {'account_index': account, 'in': True, 'out': False, 'pool': False}) + def get_transactions_in(self, account=0, confirmed=True, unconfirmed=False): + _txns = self.raw_request('get_transfers', + {'account_index': account, 'in': confirmed, 'out': False, 'pool': unconfirmed}) + txns = _txns.get('in', []) + if unconfirmed: + txns.extend(_txns.get('pool', [])) return [Payment(**self._tx2dict(tx)) for tx in - sorted(_transfers.get('in', []), key=operator.itemgetter('timestamp'))] + sorted(txns, key=operator.itemgetter('timestamp'))] - def get_transactions_out(self, account=0): - _transfers = self.raw_request('get_transfers', - {'account_index': account, 'in': False, 'out': True, 'pool': False}) + def get_transactions_out(self, account=0, confirmed=True, unconfirmed=True): + _txns = self.raw_request('get_transfers', + {'account_index': account, 'in': False, 'out': confirmed, 'pool': unconfirmed}) + txns = _txns.get('out', []) + if unconfirmed: + txns.extend(_txns.get('pool', [])) return [Transfer(**self._tx2dict(tx)) for tx in - sorted(_transfers.get('out', []), key=operator.itemgetter('timestamp'))] + sorted(txns, key=operator.itemgetter('timestamp'))] + + def get_transaction(self, txhash): + _tx = self.raw_request('get_transfer_by_txid', {'txid': str(txhash)})['transfer'] + try: + _class = {'in': Payment, 'out': Transfer}[_tx['type']] + except KeyError: + _class = Transaction + return _class(**self._tx2dict(_tx)) def _tx2dict(self, tx): pid = tx.get('payment_id', None) @@ -186,7 +213,7 @@ class JSONRPCWallet(object): 'timestamp': datetime.fromtimestamp(tx['timestamp']) if 'timestamp' in tx else None, 'amount': from_atomic(tx['amount']), 'fee': from_atomic(tx['fee']) if 'fee' in tx else None, - 'height': tx.get('height', tx.get('block_height')), + 'height': tx.get('height', tx.get('block_height')) or None, 'payment_id': None if pid is None else PaymentID(pid), 'note': tx.get('note'), # NOTE: address will be resolved only after PR#3010 has been merged to Monero diff --git a/monero/daemon.py b/monero/daemon.py index 81a9216..a383f7b 100644 --- a/monero/daemon.py +++ b/monero/daemon.py @@ -5,5 +5,11 @@ class Daemon(object): def get_info(self): return self._backend.get_info() + def get_height(self): + return self._backend.get_info()['height'] + def send_transaction(self, blob): return self._backend.send_transaction(blob) + + def get_mempool(self): + return self._backend.get_mempool() diff --git a/monero/transaction.py b/monero/transaction.py index 6616f30..339434d 100644 --- a/monero/transaction.py +++ b/monero/transaction.py @@ -2,37 +2,44 @@ class Transaction(object): hash = None height = None timestamp = None - payment_id = '0000000000000000' - amount = None fee = None - local_address = None + blob = None def __init__(self, hash=None, **kwargs): self.hash = hash self.height = kwargs.get('height', self.height) self.timestamp = kwargs.get('timestamp', self.timestamp) - self.payment_id = kwargs.get('payment_id', self.payment_id) - self.amount = kwargs.get('amount', self.amount) self.fee = kwargs.get('fee', self.fee) - self.local_address = kwargs.get('local_address', self.local_address) + self.blob = kwargs.get('blob', self.blob) def __repr__(self): return self.hash -class Payment(Transaction): +class LocalTransaction(Transaction): + """A transaction that concerns local wallet, either incoming or outgoing.""" + payment_id = None + amount = None + local_address = None + + def __init__(self, **kwargs): + super(LocalTransaction, self).__init__(**kwargs) + self.payment_id = kwargs.get('payment_id', self.payment_id) + self.amount = kwargs.get('amount', self.amount) + self.local_address = kwargs.get('local_address', self.local_address) + + +class Payment(LocalTransaction): """Incoming Transaction""" pass -class Transfer(Transaction): +class Transfer(LocalTransaction): """Outgoing Transaction""" key = None - blob = None note = '' def __init__(self, **kwargs): super(Transfer, self).__init__(**kwargs) self.key = kwargs.get('key', self.key) self.note = kwargs.get('note', self.note) - self.blob = kwargs.get('blob', self.blob) diff --git a/monero/wallet.py b/monero/wallet.py index 4eecf0c..74c2369 100644 --- a/monero/wallet.py +++ b/monero/wallet.py @@ -1,6 +1,7 @@ from . import address from . import prio from . import account +from . import transaction class Wallet(object): accounts = None @@ -21,6 +22,12 @@ class Wallet(object): self.accounts.append(_acc) idx += 1 + def get_height(self): + """ + Returns the height of the wallet. + """ + return self._backend.get_height() + def get_view_key(self): """ Returns private view key. @@ -39,6 +46,15 @@ class Wallet(object): self.accounts.append(acc) return acc + def get_transaction(self, hash): + return self._backend.get_transaction(hash) + + def confirmations(self, txn): + txn = self._backend.get_transaction(txn) + if txn.height is None: + return 0 + return max(0, self.get_height() - txn.height) + # Following methods operate on default account (index=0) def get_balances(self): return self.accounts[0].get_balances() @@ -58,11 +74,11 @@ class Wallet(object): def get_payments(self, payment_id=None): return self.accounts[0].get_payments(payment_id=payment_id) - def get_transactions_in(self): - return self.accounts[0].get_transactions_in() + def get_transactions_in(self, confirmed=True, unconfirmed=False): + return self.accounts[0].get_transactions_in(cofirmed=confirmed, unconfirmed=unconfirmed) - def get_transactions_out(self): - return self.accounts[0].get_transactions_out() + def get_transactions_out(self, confirmed=True, unconfirmed=True): + return self.accounts[0].get_transactions_out(confirmed=confirmed, unconfirmed=unconfirmed) def transfer(self, address, amount, priority=prio.NORMAL, ringsize=5, payment_id=None, unlock_time=0,