Add retrieval of transaction from daemon

This commit is contained in:
Michał Sałaban 2019-12-11 11:25:43 +01:00
parent df9dd7152b
commit 44d87be2e6
6 changed files with 208 additions and 0 deletions

View file

@ -71,6 +71,22 @@ class JSONRPCDaemon(object):
return res['headers']
raise exceptions.BackendException(res['status'])
def transactions(self, hashes):
res = self.raw_request('/get_transactions', {
'txs_hashes': hashes,
'decode_as_json': True})
if res['status'] != 'OK':
raise exceptions.BackendException(res['status'])
txs = []
for tx in res.get('txs', []):
txs.append(Transaction(
hash=tx['tx_hash'],
height=None if tx['in_pool'] else tx['block_height'],
timestamp=datetime.fromtimestamp(tx['block_timestamp']) if 'block_timestamp' in tx else None,
blob=tx['as_hex'],
json=json.loads(tx['as_json'])))
return txs
def raw_request(self, path, data):
hdr = {'Content-Type': 'application/json'}
_log.debug(u"Request: {path}\nData: {data}".format(

View file

@ -1,3 +1,5 @@
import six
class Daemon(object):
"""Monero daemon.
@ -51,3 +53,13 @@ class Daemon(object):
:rtype: list of dict
"""
return self._backend.headers(start_height, end_height)
def transactions(self, hashes):
"""
Returns transactions matching given hashes. Accepts single hash or a sequence.
:hashes: str or list of str
"""
if isinstance(hashes, six.string_types):
hashes = [hashes]
return self._backend.transactions(hashes)

View file

@ -72,8 +72,13 @@ class Transaction(object):
timestamp = None
key = None
blob = None
json = None
confirmations = None
@property
def size(self):
return len(self.blob)//2
def __init__(self, **kwargs):
self.hash = kwargs.get('hash', self.hash)
self.fee = kwargs.get('fee', self.fee)
@ -81,6 +86,7 @@ class Transaction(object):
self.timestamp = kwargs.get('timestamp', self.timestamp)
self.key = kwargs.get('key', self.key)
self.blob = kwargs.get('blob', self.blob)
self.json = kwargs.get('json', self.json)
self.confirmations = kwargs.get('confirmations', self.confirmations)
def __repr__(self):

File diff suppressed because one or more lines are too long

View file

@ -10,6 +10,7 @@ from .base import JSONTestCase
class JSONRPCDaemonTestCase(JSONTestCase):
jsonrpc_url = 'http://127.0.0.1:18081/json_rpc'
mempool_url = 'http://127.0.0.1:18081/get_transaction_pool'
transactions_url = 'http://127.0.0.1:18081/get_transactions'
data_subdir = 'test_jsonrpcdaemon'
def setUp(self):
@ -37,3 +38,33 @@ class JSONRPCDaemonTestCase(JSONTestCase):
self.assertEqual(txs[1].confirmations, 0)
self.assertGreater(txs[0].fee, 0)
self.assertGreater(txs[1].fee, 0)
@responses.activate
def test_transactions(self):
responses.add(responses.POST, self.transactions_url,
json=self._read('test_transactions.json'),
status=200)
txs = self.daemon.transactions([
"050679bd5717cd4c3d0ed1db7dac4aa7e8a222ffc7661b249e5a595a3af37d3c", # @471570
"e3a3b8361777c8f4f1fd423b86655b5c775de0230b44aa5b82f506135a96c53a", # @451993
"e2871c4203e29433257219bc20fa58c68dc12efed8f05a86d59921969a2b97cc", # @472279
"035a1cfadd2f80124998f5af8c7bb6703743a4f322d0a20b7f7b502956ada59d", # mempool
"feed00000000000face00000000000bad00000000000beef00000000000acab0", # doesn't exist
])
self.assertEqual(len(txs), 4)
self.assertEqual(txs[0].hash,
"050679bd5717cd4c3d0ed1db7dac4aa7e8a222ffc7661b249e5a595a3af37d3c")
self.assertEqual(txs[0].height, 471570)
self.assertEqual(txs[0].size, 2826)
self.assertEqual(txs[1].hash,
"e3a3b8361777c8f4f1fd423b86655b5c775de0230b44aa5b82f506135a96c53a")
self.assertEqual(txs[1].height, 451993)
self.assertEqual(txs[1].size, 2596)
self.assertEqual(txs[2].hash,
"e2871c4203e29433257219bc20fa58c68dc12efed8f05a86d59921969a2b97cc")
self.assertEqual(txs[2].height, 472279)
self.assertEqual(txs[2].size, 2796)
self.assertEqual(txs[3].hash,
"035a1cfadd2f80124998f5af8c7bb6703743a4f322d0a20b7f7b502956ada59d")
self.assertIsNone(txs[3].height)
self.assertEqual(txs[3].size, 2724)

61
utils/dumptx.py Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/python
import argparse
import json
import logging
import operator
import re
import sys
from monero.backends.jsonrpc import JSONRPCDaemon
from monero.daemon import Daemon
def url_data(url):
gs = re.compile(r"^(?P<host>[^:\s]+)(?::(?P<port>[0-9]+))?$").match(url).groupdict()
return dict(filter(operator.itemgetter(1), gs.items()))
argsparser = argparse.ArgumentParser(
description="Retrieve transaction(s) from daemon and print them"
)
argsparser.add_argument("tx_id", nargs="+", type=str, help="Transaction id (hash)")
argsparser.add_argument(
"-d",
dest="daemon_rpc_url",
type=url_data,
default="127.0.0.1:18081",
help="Daemon RPC URL [host[:port]]",
)
argsparser.add_argument(
"-t", dest="timeout", type=int, default=30, help="Request timeout"
)
argsparser.add_argument(
"-v",
dest="verbosity",
action="count",
default=0,
help="Verbosity (repeat to increase; -v for INFO, -vv for DEBUG",
)
args = argsparser.parse_args()
level = logging.WARNING
if args.verbosity == 1:
level = logging.INFO
elif args.verbosity > 1:
level = logging.DEBUG
logging.basicConfig(level=level, format="%(asctime)-15s %(message)s")
d = Daemon(JSONRPCDaemon(timeout=args.timeout, **args.daemon_rpc_url))
txs = list(d.transactions(args.tx_id))
print("Found {:d} transaction(s)".format(len(txs)))
if len(txs) > 0:
print("-" * 79)
for tx in txs:
print("id: {:s}".format(tx.hash))
print(
"height: {:s}".format("None" if tx.height is None else "{:d}".format(tx.height))
)
print("size: {:d}".format(tx.size))
print("JSON:")
print(json.dumps(tx.json, indent=2))
print("-" * 79)