mmgen-wallet/test/unit_tests_d/ut_tx_deserialize.py
The MMGen Project f9a483f34f
asyncio/aiohttp support
Asynchronous HTTP significantly speeds up operations involving multiple
JSON-RPC calls to the server, such as tracking wallet views for wallets
with a large number of outputs.

This patch adds base-level asyncio infrastructure plus aiohttp support to all
applicable MMGen commands.

The aiohttp package is not currently supported by MSYS2, so Windows users will
have to choose one of the other backends ('curl' is the default).

Tested on: Linux, Armbian, Windows; Python 3.6, 3.7, 3.8

New user features:

    - configurable RPC backends via the 'rpc_backend' option.  Supported
      options are 'aiohttp' (Linux-only), 'httplib', 'requests' and 'curl'

    - configurable RPC queue size via the 'aiohttp_rpc_queue_len' option

The patch also includes a rewrite/redesign of large parts of the MMGen code
base, most importantly:

    - rpc.py - full rewrite of RPC library, new RPCBackends class
    - main_addrimport.py - full rewrite
    - main_autosign.py - LED code now handled by new LEDControl class
    - eth/tw.py, eth/tx.py - reworked logic for resolving token symbols and
      addresses
    - eth/tx.py - separate classes for signed and unsigned transactions

Testing:

    # Set a backend (choose one):
    $ export MMGEN_RPC_BACKEND='aiohttp' # Linux-only
    $ export MMGEN_RPC_BACKEND='curl'    # Windows
    $ export MMGEN_RPC_BACKEND='httplib' # compare performance with 'aiohttp'

    # Bitcoin:
    $ test/unit_tests.py rpc btc
    $ test/test.py main regtest autosign

    # Ethereum:
    $ test/unit_tests.py rpc eth
    $ test/tooltest2.py --coin=eth --testnet=1 txview
    $ test/test.py --coin=eth ethdev

    # Monero wallet:
    $ test/unit_tests.py rpc xmr_wallet
    $ test/test-release.sh -F xmr
2020-05-10 14:07:54 +00:00

132 lines
4 KiB
Python
Executable file

#!/usr/bin/env python3
"""
test/unit_tests_d/ut_tx_deserialize: TX deserialization unit test for the MMGen suite
"""
import os,json
from mmgen.common import *
from ..include.common import *
from mmgen.protocol import init_coin
from mmgen.tx import MMGenTX,DeserializedTX
from mmgen.rpc import rpc_init
from mmgen.daemon import CoinDaemon
class unit_test(object):
def _get_core_repo_root(self):
self.core_repo_root = os.getenv('CORE_REPO_ROOT')
if not self.core_repo_root:
die(1,'The environmental variable CORE_REPO_ROOT must be set before running this test')
def run_test(self,name,ut):
async def test_tx(txhex,desc,n):
def has_nonstandard_outputs(outputs):
for o in outputs:
t = o['scriptPubKey']['type']
if t in ('nonstandard','pubkey','nulldata'):
return True
return False
d = await g.rpc.call('decoderawtransaction',txhex)
if has_nonstandard_outputs(d['vout']): return False
dt = DeserializedTX(txhex)
if opt.verbose:
Msg('\n====================================================')
Msg_r('.' if opt.quiet else '{:>3}) {}\n'.format(n,desc))
if opt.verbose:
Pmsg(d)
Msg('----------------------------------------------------')
Pmsg(dt)
# metadata
assert dt['txid'] == d['txid'],'TXID does not match'
assert dt['lock_time'] == d['locktime'],'Locktime does not match'
assert dt['version'] == d['version'],'Version does not match'
# inputs
a,b = d['vin'],dt['txins']
for i in range(len(a)):
assert a[i]['txid'] == b[i]['txid'],'TxID of input {} does not match'.format(i)
assert a[i]['vout'] == b[i]['vout'],'vout of input {} does not match'.format(i)
assert a[i]['sequence'] == int(b[i]['nSeq'],16),(
'nSeq of input {} does not match'.format(i))
if 'txinwitness' in a[i]:
assert a[i]['txinwitness'] == b[i]['witness'],(
'witness of input {} does not match'.format(i))
# outputs
a,b = d['vout'],dt['txouts']
for i in range(len(a)):
A = a[i]['scriptPubKey']['addresses'][0]
B = b[i]['address']
fs = 'address of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
A = a[i]['value']
B = b[i]['amount']
fs = 'value of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
A = a[i]['scriptPubKey']['hex']
B = b[i]['scriptPubKey']
fs = 'scriptPubKey of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
return True
def print_info(fn,extra_desc):
if opt.names:
Msg_r('{} {} ({}){}'.format(
purple('Testing'),
cyan(name),
extra_desc,
'' if opt.quiet else '\n'))
else:
Msg_r('Testing transactions from {!r}'.format(fn))
if not opt.quiet: Msg('')
async def test_core_vectors():
self._get_core_repo_root()
fn_b = 'src/test/data/tx_valid.json'
fn = os.path.join(self.core_repo_root,fn_b)
data = json.loads(open(fn).read())
print_info(fn_b,'Core test vectors')
n = 1
for e in data:
if type(e[0]) == list:
await rpc_init()
await test_tx(e[1],desc,n)
n += 1
else:
desc = e[0]
Msg('OK')
async def test_mmgen_txs():
fns = ( ('btc',False,'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'),
('btc',True,'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx'),
# ('bch',False,'test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx')
)
print_info('test/ref/*rawtx','MMGen reference transactions')
for n,(coin,tn,fn) in enumerate(fns):
init_coin(coin,tn)
g.proto.daemon_data_dir = 'test/daemons/' + g.coin.lower()
g.rpc_port = CoinDaemon(coin + ('','_tn')[tn],test_suite=True).rpc_port
await rpc_init()
await test_tx(MMGenTX(fn).hex,fn,n+1)
init_coin('btc',False)
g.rpc_port = CoinDaemon('btc',test_suite=True).rpc_port
await rpc_init()
Msg('OK')
start_test_daemons('btc','btc_tn') # ,'bch')
run_session(test_mmgen_txs(),do_rpc_init=False)
run_session(test_core_vectors(),do_rpc_init=False)
stop_test_daemons('btc','btc_tn') # ,'bch')
return True