#!/usr/bin/env python3 # # Execute the standard smoke tests for Zcash releases. # # Usage: # # ZCASHD=./src/zcashd ZCASHCLI=./src/zcash-cli ./qa/zcash/smoke_tests.py --wallet=wallet.smoketest.dat "$HOME/.zcash" # # ZCASHD=./src/zcashd ZCASHCLI=./src/zcash-cli ./qa/zcash/smoke_tests.py --wallet=wallet.smoketest.dat "$HOME/.zcash" --automate --use-faucet # import argparse import datetime import os import requests import subprocess import sys import time import traceback from decimal import Decimal from slickrpc import Proxy from slickrpc.exc import RpcException DEFAULT_FEE = Decimal('0.00001') URL_FAUCET_DONATION = 'https://faucet.testnet.z.cash/donations' URL_FAUCET_TAP = 'https://faucet.testnet.z.cash/' # # Smoke test definitions # # (case, expected_mainnet, expected_testnet) SMOKE_TESTS = [ # zcashd start/stop/restart flows ('1a', True, True), # zcashd start ('1b', True, True), # Graceful zcashd stop ('1c', True, True), # Ungraceful zcashd stop ('1d', True, True), # zcashd start; graceful zcashd stop; zcashd start ('1e', True, True), # zcashd start; ungraceful zcashd stop; zcashd start # Control ('2a', True, True), # Run getinfo ('2b', True, True), # Run help # Address generation #('3a', True, True), # Generate a Sprout z-addr #('3b', True, True), # Generate multiple Sprout z-addrs ('3c', True, True), # Generate a t-addr ('3d', True, True), # Generate multiple t-addrs ('3e', True, True), # Generate a Sapling z-addr ('3f', True, True), # Generate multiple Sapling z-addrs # Transactions #('4a', True, True ), # Send funds from Sprout z-addr to same Sprout z-addr #('4b', True, True ), # Send funds from Sprout z-addr to a different Sprout z-addr #('4c', True, True ), # Send funds from Sprout z-addr to a t-addr #('4d', False, False ), # Send funds from t-addr to Sprout z-addr(expected fail as of Canopy) ('4e', True, True ), # Send funds from t-addr to t-addr ('4f', True, True ), # Send funds from t-addr to Sapling z-addr ('4g', True, True ), # Send funds from Sapling z-addr to same Sapling z-addr ('4h', True, True ), # Send funds from Sapling z-addr to a different Sapling z-addr ('4i', True, True ), # Send funds from Sapling z-addr to a t-addr #('4j', False, False), # Send funds from Sprout z-addr to Sapling z-addr #('4k', True, True ), # Send funds from Sprout z-addr to multiple Sprout z-addrs #('4l', True, True ), # Send funds from Sprout z-addr to multiple t-addrs #('4m', True, True ), # Send funds from Sprout z-addr to t-addr and Sprout z-addrs #('4n', False, False), # Send funds from Sprout z-addr to t-addr and Sapling z-addr #('4o', False, False), # Send funds from Sprout z-addr to multiple Sapling z-addrs ('4p', True, True ), # Send funds from t-addr to multiple t-addrs #('4q', False, False ), # Send funds from t-addr to multiple Sprout z-addrs(expected fail as of Canopy) ('4r', True, True ), # Send funds from t-addr to multiple Sapling z-addrs #('4s', False, False), # Send funds from t-addr to Sprout z-addr and Sapling z-addr ('4t', True, True ), # Send funds from Sapling z-addr to multiple Sapling z-addrs #('4u', False, False), # Send funds from Sapling z-addr to multiple Sprout z-addrs ('4v', True, True ), # Send funds from Sapling z-addr to multiple t-addrs ('4w', True, True ), # Send funds from Sapling z-addr to t-addr and Sapling z-addr #('4x', False, False), # Send funds from Sapling z-addr to Sapling z-addr and Sprout z-addr #('4y', False, False ), # Send funds from t-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) #('4z', False, False ), # Send funds from 2 different t-addrs to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) #('4aa', False, False), # Send funds from the same 2 t-addrs to Sprout z-addr using z_mergetoaddress ('4bb', True, True ), # Send funds from 2 different t-addrs to Sapling z-addr using z_mergetoaddress ('4cc', True, True ), # Send funds from t-addr to Sapling z-addr using z_mergetoaddress #('4dd', False, False ), # Send funds from t-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress(expected fail as of Canopy) ('4ee', True, True ), # Send funds from t-addr and Sapling z-addr to Sapling z-addr using z_mergetoaddress #('4ff', True, True ), # Send funds from Sprout z-addr and Sprout z-addr to Sprout z-addr using z_mergetoaddress ('4gg', True, True ), # Send funds from Sapling z-addr and Sapling z-addr to Sapling z-addr using z_mergetoaddress # Wallet ('5a', True, True), # After generating multiple z-addrs, run z_listaddresses #('5b', True, True), # Run z_validateaddress with a Sprout z-addr ('5c', True, True), # Run z_validateaddress with a Sapling z-addr ('5d', True, True), # After a transaction, run z_listunspent ('5e', True, True), # After a transaction, run z_listreceivedbyaddress ('5f', True, True), # After a transaction, run z_getbalance ('5g', True, True), # After a transaction, run z_gettotalbalance ('5h', True, True), # Run z_exportkey using a Sprout z-addr ('5i', True, True), # Run z_importkey using the zkey from a Sprout z-addr ('5j', True, True), # Run z_exportkey using a Sapling z-addr ('5k', True, True), # Run z_importkey using the zkey from a Sapling z-addr ('5l', True, True), # Run z_exportwallet ('5m', True, True), # Run z_importwallet ('5n', True, True), # Run z_shieldcoinbase ('5o', True, True), # Run getwalletinfo # Network ('6a', True, True), # Run getpeerinfo ('6b', True, True), # Run getnetworkinfo ('6c', True, False), # Run getdeprecationinfo ('6d', True, True), # Run getconnectioncount ('6e', True, True), # Run getaddednodeinfo # Mining ('7a', True, True), # Run getblocksubsidy ('7b', True, True), # Run getblocktemplate ('7c', True, True), # Run getmininginfo ('7d', True, True), # Run getnetworkhashps ('7e', True, True), # Run getnetworksolps ] TIME_STARTED = datetime.datetime.now() # # Test helpers # def run_cmd(results, case, zcash, name, args=[]): print('----- %s -----' % (datetime.datetime.now() - TIME_STARTED)) print('%s $ zcash-cli %s %s' % ( case.ljust(3), name, ' '.join([str(arg) for arg in args], ))) try: res = zcash.__getattr__(name)(*args) print(res) print() if results is not None and len(case) > 0: results[case] = True return res except RpcException as e: print('ERROR: %s' % str(e)) if results is not None and len(case) > 0: results[case] = False return None def wait_for_balance(zcash, zaddr, expected=None, minconf=1): if (minconf == 1): print('Waiting for funds to %s...' % zaddr) else: print('Waiting for funds to %s to receive %d confirmations...' % (zaddr, minconf)) unconfirmed_balance = Decimal(zcash.z_getbalance(zaddr, 0)).quantize(Decimal('1.00000000')) print('Expecting: %s; Unconfirmed Balance: %s' % (expected, unconfirmed_balance)) if expected is not None and unconfirmed_balance != expected: print('WARNING: Unconfirmed balance does not match expected balance') # Default timeout is 15 minutes ttl = 900 while True: balance = Decimal(zcash.z_getbalance(zaddr, minconf)).quantize(Decimal('1.00000000')) if (expected is not None and balance == unconfirmed_balance) or (expected is None and balance > 0): print('Received %s' % balance) return balance time.sleep(1) ttl -= 1 if ttl == 0: if zcash.automated: # Reset timeout ttl = 300 else: # Ask user if they want to keep waiting print() print('Balance: %s Expected: %s' % (balance, expected)) ret = input('Do you wish to continue waiting? (Y/n) ') if ret.lower() == 'n': print('Address contained %s at timeout' % balance) return balance else: # Wait another 5 minutes before asking again ttl = 300 def wait_and_check_balance(results, case, zcash, addr, expected): #Wait for async call to finish and persist completely to caller time.sleep(5) balance = wait_for_balance(zcash, addr, expected) if balance != expected and results is not None and len(case) > 0: results[case] = False return balance def wait_for_txid_operation(zcash, opid, timeout=300): print('Waiting for async operation %s' % opid) result = None for _ in iter(range(timeout)): results = zcash.z_getoperationresult([opid]) if len(results) > 0: result = results[0] break time.sleep(1) status = result['status'] if status == 'failed': print('Operation failed') print(result['error']['message']) return None elif status == 'success': txid = result['result']['txid'] print('txid: %s' % txid) return txid def async_txid_cmd(results, case, zcash, name, args=[]): opid = run_cmd(results, case, zcash, name, args) # Some async commands return a dictionary containing the opid if isinstance(opid, dict): opid = opid['opid'] if opid is None: if results is not None and len(case) > 0: results[case] = False return None txid = wait_for_txid_operation(zcash, opid) if txid is None: if results is not None and len(case) > 0: results[case] = False return txid def z_sendmany(results, case, zcash, from_addr, recipients): return async_txid_cmd(results, case, zcash, 'z_sendmany', [ from_addr, [{ 'address': to_addr, 'amount': amount, } for (to_addr, amount) in recipients], 1 # minconf ]) def check_z_sendmany(results, case, zcash, from_addr, recipients): txid = z_sendmany(results, case, zcash, from_addr, recipients) if txid is None: return [Decimal('0')] return [wait_and_check_balance(results, case, zcash, to_addr, amount) for (to_addr, amount) in recipients] def check_z_sendmany_parallel(results, zcash, runs): # First attempt to create all the transactions txids = [(run, z_sendmany(results, run[0], zcash, run[1], run[2])) for run in runs] # Then wait for balance updates caused by successful transactions return [ wait_and_check_balance(results, run[0], zcash, to_addr, amount) if txid is not None else Decimal('0') for (run, txid) in txids for (to_addr, amount) in run[2]] def z_mergetoaddress(results, case, zcash, from_addrs, to_addr): for addr in from_addrs: balance = Decimal(zcash.z_getbalance(addr, 0)).quantize(Decimal('1.00000000')) if balance > 0: wait_for_balance(zcash, addr, balance, minconf=4) return async_txid_cmd(results, case, zcash, 'z_mergetoaddress', [from_addrs, to_addr]) def check_z_mergetoaddress(results, case, zcash, from_addrs, to_addr, amount): txid = z_mergetoaddress(results, case, zcash, from_addrs, to_addr) if txid is None: return Decimal('0') return wait_and_check_balance(results, case, zcash, to_addr, amount) def check_z_mergetoaddress_parallel(results, zcash, runs): # First attempt to create all the transactions txids = [(run, z_mergetoaddress(results, run[0], zcash, run[1], run[2])) for run in runs] # Then wait for balance updates caused by successful transactions return [ wait_and_check_balance(results, run[0], zcash, run[2], run[3]) if txid is not None else Decimal('0') for (run, txid) in txids] def tap_zfaucet(addr): with requests.Session() as session: # Get token to request TAZ from faucet with a given zcash address response = session.get(URL_FAUCET_TAP) if response.status_code != 200: print("Error establishing session at:", URL_FAUCET_TAP) os.sys.exit(1) csrftoken = response.cookies['csrftoken'] # Request TAZ from the faucet data_params = dict(csrfmiddlewaretoken=csrftoken, address=addr) response2 = session.post(URL_FAUCET_TAP, data=data_params, headers=dict(Referer=URL_FAUCET_TAP)) if response2.status_code != 200: print("Error tapping faucet at:", URL_FAUCET_TAP) os.sys.exit(1) def get_zfaucet_addrs(): with requests.Session() as session: response = session.get(URL_FAUCET_DONATION) if response.status_code != 200: print("Error establishing session at:", URL_FAUCET_DONATION) os.sys.exit(1) data = response.json() return data def get_zfaucet_taddr(): return get_zfaucet_addrs()["t_address"] def get_zfaucet_zsapaddr(): # At the time of writing this, it appears these(keys) are backwards return get_zfaucet_addrs()["z_address_legacy"] def get_zfaucet_zsproutaddr(): # At the time of writing this, it appears these(keys) are backwards return get_zfaucet_addrs()["z_address_sapling"] # # Test runners # def simple_commands(zcash): results = {} run_cmd(results, '2a', zcash, 'getinfo'), run_cmd(results, '2b', zcash, 'help'), run_cmd(results, '5o', zcash, 'getwalletinfo'), run_cmd(results, '6a', zcash, 'getpeerinfo'), run_cmd(results, '6b', zcash, 'getnetworkinfo'), run_cmd(results, '6c', zcash, 'getdeprecationinfo'), run_cmd(results, '6d', zcash, 'getconnectioncount'), run_cmd(results, '6e', zcash, 'getaddednodeinfo', [False]), run_cmd(results, '7a', zcash, 'getblocksubsidy'), run_cmd(results, '7c', zcash, 'getmininginfo'), run_cmd(results, '7d', zcash, 'getnetworkhashps'), run_cmd(results, '7e', zcash, 'getnetworksolps'), return results def transaction_chain(zcash): results = {} # Generate the various addresses we will use taddr_1 = run_cmd(results, '3c', zcash, 'getnewaddress') taddr_2 = run_cmd(results, '3d', zcash, 'getnewaddress') taddr_3 = run_cmd(results, '3d', zcash, 'getnewaddress') taddr_4 = run_cmd(results, '3d', zcash, 'getnewaddress') taddr_5 = run_cmd(results, '3d', zcash, 'getnewaddress') sapling_zaddr_1 = run_cmd(results, '3e', zcash, 'z_getnewaddress', ['sapling']) sapling_zaddr_2 = run_cmd(results, '3f', zcash, 'z_getnewaddress', ['sapling']) sapling_zaddr_3 = run_cmd(results, '3f', zcash, 'z_getnewaddress', ['sapling']) # Check that the zaddrs are all listed zaddrs = run_cmd(results, '5a', zcash, 'z_listaddresses') if ( sapling_zaddr_1 not in zaddrs or sapling_zaddr_2 not in zaddrs): results['5a'] = False # Validate the addresses ret = run_cmd(results, '5c', zcash, 'z_validateaddress', [sapling_zaddr_1]) if not ret['isvalid'] or ret['type'] != 'sapling': results['5c'] = False # Set up beginning and end of the chain print('#') print('# Initialising transaction chain') print('#') print() if zcash.use_faucet: print('Tapping the testnet faucet for testing funds...') chain_end = get_zfaucet_taddr() tap_zfaucet(taddr_1) print('Done! Leftover funds will be sent back to the faucet.') else: chain_end = input('Type or paste transparent address where leftover funds should be sent: ') if not zcash.validateaddress(chain_end)['isvalid']: print('Invalid transparent address') return results print() print('Please send at least 0.01 ZEC/TAZ to the following address:') print(taddr_1) print() input('Press Enter once the funds have been sent.') print() # Wait to receive starting balance taddr_balance = wait_for_balance(zcash, taddr_1) starting_balance = taddr_balance # # Start the transaction chain! # print() print('#') print('# Starting transaction chain') print('#') print() try: # taddr -> Sapling # Send it all here because z_sendmany pick a new t-addr for change sapling_balance = check_z_sendmany( results, '4f', zcash, taddr_1, [(sapling_zaddr_1, taddr_balance - DEFAULT_FEE)])[0] taddr_balance = Decimal('0') # Sapling -> taddr taddr_balance = check_z_sendmany( results, '4i', zcash, sapling_zaddr_1, [(taddr_1, (starting_balance / Decimal('10')) * Decimal('3'))])[0] sapling_balance -= taddr_balance + DEFAULT_FEE # # Intra-pool tests # # Sapling -> same Sapling sapling_balance = check_z_sendmany( results, '4g',zcash, sapling_zaddr_1, [(sapling_zaddr_1, sapling_balance - DEFAULT_FEE)])[0] # taddr -> different taddr # Sapling -> different Sapling (taddr_balance, sapling_balance) = check_z_sendmany_parallel(results, zcash, [ ('4e', taddr_1, [(taddr_2, taddr_balance - DEFAULT_FEE)]), ('4h', sapling_zaddr_1, [(sapling_zaddr_2, sapling_balance - DEFAULT_FEE)]), ]) # taddr -> multiple taddr # Sapling -> multiple Sapling check_z_sendmany_parallel(results, zcash, [ ('4p', taddr_2, [ (taddr_1, starting_balance / Decimal('10')), (taddr_3, taddr_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE), ]), ('4t', sapling_zaddr_2, [ (sapling_zaddr_1, starting_balance / Decimal('10')), (sapling_zaddr_3, starting_balance / Decimal('10')), ]), ]) taddr_balance -= DEFAULT_FEE sapling_balance -= DEFAULT_FEE # multiple Sapling -> Sapling # multiple taddr -> taddr check_z_mergetoaddress_parallel(results, zcash, [ ('4gg', [sapling_zaddr_1, sapling_zaddr_3], sapling_zaddr_2, sapling_balance - DEFAULT_FEE), ('', [taddr_1, taddr_3], taddr_2, taddr_balance - DEFAULT_FEE), ]) sapling_balance -= DEFAULT_FEE taddr_balance -= DEFAULT_FEE taddr_2_balance = taddr_balance # # Inter-pool tests # # Sapling -> taddr and Sapling check_z_sendmany( results, '4w', zcash, sapling_zaddr_2,[ (taddr_3, starting_balance / Decimal('10')), (sapling_zaddr_1, starting_balance / Decimal('10'))])[0] sapling_balance -= (starting_balance / Decimal('10')) + DEFAULT_FEE taddr_balance += starting_balance / Decimal('10') # taddr and Sapling -> Sapling check_z_mergetoaddress(results, '4ee', zcash, [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE) sapling_balance += (starting_balance / Decimal('10')) - DEFAULT_FEE taddr_balance -= starting_balance / Decimal('10') # Sapling -> multiple taddr check_z_sendmany(results, '4v', zcash, sapling_zaddr_2, [ (taddr_4, (starting_balance / Decimal('10'))), (taddr_5, (starting_balance / Decimal('10')))])[0] sapling_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + DEFAULT_FEE taddr_balance += (starting_balance / Decimal('10')) * Decimal('2') # multiple taddr -> Sapling check_z_mergetoaddress(results, '4bb',zcash, [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE) sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE taddr_balance -= (starting_balance / Decimal('10')) * Decimal('2') # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, taddr_2_balance + sapling_balance - DEFAULT_FEE) taddr_balance += sapling_balance - DEFAULT_FEE sapling_balance = Decimal('0') # taddr -> multiple Sapling taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) check_z_sendmany(results, '4r', zcash, taddr_2, [ (sapling_zaddr_1, (starting_balance / Decimal('10'))), (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE)])[0] sapling_balance = taddr_2_balance - DEFAULT_FEE taddr_balance -= taddr_2_balance # multiple Sapling -> taddr check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE) taddr_balance += sapling_balance - DEFAULT_FEE sapling_balance = Decimal('0') # z_mergetoaddress taddr -> Sapling taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) check_z_mergetoaddress(results, '4cc', zcash, [taddr_2], sapling_zaddr_1, taddr_2_balance - DEFAULT_FEE) sapling_balance = taddr_2_balance - DEFAULT_FEE taddr_balance -= taddr_2_balance except Exception as e: print('Error: %s' % e) traceback.print_exc() finally: # # End the chain by returning the remaining funds # print() print('#') print('# Finishing transaction chain') print('#') all_addrs = [ taddr_1, taddr_2, taddr_3, taddr_4, taddr_5, sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3, ] print() print('Waiting for all transactions to be mined') for addr in all_addrs: balance = Decimal(zcash.z_getbalance(addr, 0)).quantize(Decimal('1.00000000')) if balance > 0: wait_for_balance(zcash, addr, balance) print() print('Returning remaining balance minus fees') for addr in all_addrs: balance = Decimal(zcash.z_getbalance(addr)).quantize(Decimal('1.00000000')) if balance > 0: z_sendmany(None, '', zcash, addr, [(chain_end, balance - DEFAULT_FEE)]) return results # # Test stages # STAGES = [ 'simple-commands', 'transaction-chain' ] STAGE_COMMANDS = { 'simple-commands': simple_commands, 'transaction-chain': transaction_chain, } def run_stage(stage, zcash): print('Running stage %s' % stage) print('=' * (len(stage) + 14)) print() cmd = STAGE_COMMANDS[stage] if cmd is not None: ret = cmd(zcash) else: print('WARNING: stage not yet implemented, skipping') ret = {} print() print('-' * (len(stage) + 15)) print('Finished stage %s' % stage) print() return ret # # Zcash wrapper # class ZcashNode(object): def __init__(self, args, zcashd=None, zcash_cli=None): if zcashd is None: zcashd = os.getenv('ZCASHD', 'zcashd') if zcash_cli is None: zcash_cli = os.getenv('ZCASHCLI', 'zcash-cli') self.__datadir = args.datadir self.__wallet = args.wallet self.__testnet = not args.mainnet self.__zcashd = zcashd self.__zcash_cli = zcash_cli self.__process = None self.__proxy = None self.automated = args.automate self.use_faucet = args.faucet def start(self, extra_args=None, timewait=None): if self.__proxy is not None: raise RuntimeError('Already started') rpcuser = 'st' rpcpassword = 'st' args = [ self.__zcashd, '-datadir=%s' % self.__datadir, '-wallet=%s' % self.__wallet, '-rpcuser=%s' % rpcuser, '-rpcpassword=%s' % rpcpassword, '-showmetrics=0', '-experimentalfeatures', '-zmergetoaddress', '-walletrequirebackup=false' ] if self.__testnet: args.append('-testnet=1') if extra_args is not None: args.extend(extra_args) self.__process = subprocess.Popen(args) cli_args = [ self.__zcash_cli, '-datadir=%s' % self.__datadir, '-rpcuser=%s' % rpcuser, '-rpcpassword=%s' % rpcpassword, '-rpcwait', ] if self.__testnet: cli_args.append('-testnet=1') cli_args.append('getblockcount') devnull = open('/dev/null', 'w+', encoding='utf8') if os.getenv('PYTHON_DEBUG', ''): print('start_node: zcashd started, calling zcash-cli -rpcwait getblockcount') subprocess.check_call(cli_args, stdout=devnull) if os.getenv('PYTHON_DEBUG', ''): print('start_node: calling zcash-cli -rpcwait getblockcount returned') devnull.close() rpcuserpass = '%s:%s' % (rpcuser, rpcpassword) rpchost = '127.0.0.1' rpcport = 18232 if self.__testnet else 8232 url = 'http://%s@%s:%d' % (rpcuserpass, rpchost, rpcport) if timewait is not None: self.__proxy = Proxy(url, timeout=timewait) else: self.__proxy = Proxy(url) def stop(self): if self.__proxy is None: raise RuntimeError('Not running') self.__proxy.stop() self.__process.wait() self.__proxy = None self.__process = None def __getattr__(self, name): if self.__proxy is None: raise RuntimeError('Not running') return self.__proxy.__getattr__(name) # # Test driver # def main(): parser = argparse.ArgumentParser() parser.add_argument('--automate', action='store_true', help='Run the smoke tests without a user present') parser.add_argument('--list-stages', dest='list', action='store_true') parser.add_argument('--mainnet', action='store_true', help='Use mainnet instead of testnet') parser.add_argument('--use-faucet', dest='faucet', action='store_true', help='Use testnet faucet as source of funds') parser.add_argument('--wallet', default='wallet.dat', help='Wallet file to use (within data directory)') parser.add_argument('datadir', help='Data directory to use for smoke testing', default=None) parser.add_argument('stage', nargs='*', default=STAGES, help='One of %s' % STAGES) args = parser.parse_args() # Check for list if args.list: for s in STAGES: print(s) sys.exit(0) # Check validity of stages for s in args.stage: if s not in STAGES: print("Invalid stage '%s' (choose from %s)" % (s, STAGES)) sys.exit(1) # Don't allow using the default wallet.dat in mainnet mode if args.mainnet and args.wallet == 'wallet.dat': print('Cannot use wallet.dat as wallet file when running mainnet tests. Keep your funds safe!') sys.exit(1) # Testnet faucet cannot be used in mainnet mode if args.mainnet and args.faucet: print('Cannot use testnet faucet when running mainnet tests.') sys.exit(1) # Enforce correctly-configured automatic mode if args.automate: if args.mainnet: print('Cannot yet automate mainnet tests.') sys.exit(1) if not args.faucet: print('--automate requires --use-faucet') sys.exit(1) # Start zcashd zcash = ZcashNode(args) print('Start time: %s' % TIME_STARTED) print('Starting zcashd...') zcash.start() print() # Run the stages results = {} for s in args.stage: results.update(run_stage(s, zcash)) # Stop zcashd print('Stopping zcashd...') zcash.stop() passed = True print() print('========================') print(' Results') print('========================') print('Case | Expected | Actual') print('========================') for test_case in SMOKE_TESTS: case = test_case[0] expected = test_case[1 if args.mainnet else 2] if case in results: actual = results[case] actual_str = '%s%s' % ( 'Passed' if actual else 'Failed', '' if expected == actual else '!!!' ) passed &= (expected == actual) else: actual_str = ' N/A' print('%s | %s | %s' % ( case.ljust(4), ' Passed ' if expected else ' Failed ', actual_str )) if not passed: print() print("!!! One or more smoke test stages failed !!!") sys.exit(1) if __name__ == '__main__': main()