コード例 #1
0
    def test_decode_p2wpkh(self):
        swap_file = os.path.join(TEST_DATADIRS, 'test') + '.json'

        swap_info = {
            'side': 'a',
            'a_coin': 'PART',
            'b_coin': 'XMR',
            'a_amount': 1,
            'b_amount': 2,
            'a_feerate': 0.00032595,
            'b_feerate': 0.0012595,
            'a_addr_f': 'bart1qy68z7jn0mz64cpcgq77uvlp8h0yxmsdpsq6a6j',
            'lock1': 10,
            'lock2': 11,
        }
        swap_info['a_connect'] = {
            'port': 1234,
            'username': '******'.format(123),
            'password': '******'.format(123)
        }
        swap_info['b_connect'] = {
            'port': 1235,
            'wallet_port': 123,
            'wallet_auth': '123',
        }
        try:
            callSwapTool(swap_file, 'init', swap_info)
            assert (False), 'Should fail'
        except Exception as e:
            assert ('Unknown bech32 hrp' in str(e))
コード例 #2
0
    def startSwap(self, ID_ALICE_SWAP, ID_BOB_SWAP, amount_a, amount_b):
        logging.info('Set initial parameters.')
        part_addr_bob = callnoderpc(
            ID_BOB_PART, 'getnewaddress',
            ['bob\'s addr', False, False, False, 'bech32'])
        # After a successful swap the coinA amount will be in an output to part_addr_bob

        swap_info = {
            'side': 'a',
            'a_coin': 'PART',
            'b_coin': 'XMR',
            'a_amount': amount_a,
            'b_amount': amount_b,
            'a_feerate': 0.00032595,
            'b_feerate': 0.0012595,
            'a_addr_f': part_addr_bob,
            'lock1': 10,
            'lock2': 11,
        }
        swap_info['a_connect'] = {
            'port': BASE_RPC_PORT + ID_ALICE_PART,
            'username': '******'.format(ID_ALICE_PART),
            'password': '******'.format(ID_ALICE_PART)
        }
        swap_info['b_connect'] = {
            'port': XMR_BASE_RPC_PORT + ID_ALICE_XMR,
            'wallet_port': XMR_BASE_WALLET_RPC_PORT + ID_ALICE_XMR,
            'wallet_auth': self.xmr_wallet_auth[ID_ALICE_XMR],
        }
        callSwapTool(ID_ALICE_SWAP, 'init', swap_info)

        swap_info['a_connect'] = {
            'port': BASE_RPC_PORT + ID_BOB_PART,
            'username': '******'.format(ID_BOB_PART),
            'password': '******'.format(ID_BOB_PART)
        }
        swap_info['b_connect'] = {
            'port': XMR_BASE_RPC_PORT + ID_BOB_XMR,
            'wallet_port': XMR_BASE_WALLET_RPC_PORT + ID_BOB_XMR,
            'wallet_auth': self.xmr_wallet_auth[ID_BOB_XMR],
        }
        swap_info['side'] = 'b'
        callSwapTool(ID_BOB_SWAP, 'init', swap_info)

        logging.info('Alice and Bob exchange keys.')
        msg1f = callSwapTool(ID_ALICE_SWAP, 'msg1f')
        msg1l = callSwapTool(ID_BOB_SWAP, 'msg1l')

        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg1l)
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg1f)
コード例 #3
0
    def startSwap(self, ID_ALICE_SWAP, ID_BOB_SWAP, amount_a, amount_b):
        logging.info('Set initial parameters.')
        btc_addr_bob1 = callnoderpc(ID_BOB_BTC, 'getnewaddress',
                                    ['bob\'s addr', 'bech32'])
        ignr, a_pkhash_f = segwit_addr.decode('bcrt', btc_addr_bob1)
        # After a successful swap the coinA amount will be in an output to a_pkhash_f

        swap_info = {
            'side': 'a',
            'a_coin': 'BTC',
            'b_coin': 'XMR',
            'a_amount': amount_a,
            'b_amount': amount_b,
            'a_feerate': 0.00032595,
            'b_feerate': 0.0012595,
            'a_pkhash_f': bytes(a_pkhash_f).hex(),
        }
        swap_info['a_connect'] = {
            'port': BASE_RPC_PORT + ID_ALICE_BTC,
            'username': '******'.format(ID_ALICE_BTC),
            'password': '******'.format(ID_ALICE_BTC)
        }
        swap_info['b_connect'] = {
            'port': XMR_BASE_RPC_PORT + ID_ALICE_XMR,
            'wallet_port': XMR_BASE_WALLET_RPC_PORT + ID_ALICE_XMR,
            'wallet_auth': self.xmr_wallet_auth[ID_ALICE_XMR],
        }
        callSwapTool(ID_ALICE_SWAP, 'init', swap_info)

        swap_info['a_connect'] = {
            'port': BASE_RPC_PORT + ID_BOB_BTC,
            'username': '******'.format(ID_BOB_BTC),
            'password': '******'.format(ID_BOB_BTC)
        }
        swap_info['b_connect'] = {
            'port': XMR_BASE_RPC_PORT + ID_BOB_XMR,
            'wallet_port': XMR_BASE_WALLET_RPC_PORT + ID_BOB_XMR,
            'wallet_auth': self.xmr_wallet_auth[ID_BOB_XMR],
        }
        swap_info['side'] = 'b'
        callSwapTool(ID_BOB_SWAP, 'init', swap_info)

        logging.info('Alice and Bob exchange keys.')
        msg1f = callSwapTool(ID_ALICE_SWAP, 'msg1f')
        msg1l = callSwapTool(ID_BOB_SWAP, 'msg1l')

        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg1l)
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg1f)
コード例 #4
0
    def publishALockRefundTx(self, user_id_part, user_id_swap):
        alockrefundtxid = None
        for i in range(20):
            '''
            self.part_stakelimit += 10
            callnoderpc(0, 'walletsettings', ['stakelimit', {'height': self.part_stakelimit}])
            print('self.part_stakelimit', self.part_stakelimit)
            '''
            time.sleep(1)

            logging.info(
                'PART blocks: %d',
                callnoderpc(user_id_part, 'getblockchaininfo')['blocks'])
            try:
                alockrefundtxid = callSwapTool(user_id_swap,
                                               'publishalockrefundtx').strip()
                break
            except Exception as e:
                print(str(e))
                if 'Transaction already in block chain' in str(e):
                    break
                assert ('non-BIP68-final' in str(e))

        assert (alockrefundtxid is not None)
        logging.info('alockrefundtxid %s', alockrefundtxid)
        return alockrefundtxid
コード例 #5
0
    def publishALockRefundFollowerSpendTx(self, user_id_part, user_id_swap,
                                          a_pkhash_f):
        alockrefundspendtxid = None
        for i in range(20):
            time.sleep(1)

            logging.info(
                'PART blocks: %d',
                callnoderpc(user_id_part, 'getblockchaininfo')['blocks'])
            try:
                alockrefundspendtxid = callSwapTool(
                    user_id_swap,
                    'publishalockrefundspendftx',
                    str_param=bytes(a_pkhash_f).hex()).strip()
                break
            except Exception as e:
                print(str(e))
                if 'Transaction already in block chain' in str(e):
                    break
                assert ('non-BIP68-final' in str(e))

        assert (alockrefundspendtxid is not None)
        logging.info('alockrefundspendtxid %s', alockrefundspendtxid)
        return alockrefundspendtxid
コード例 #6
0
    def test_04_follower_recover_b_lock_tx(self):
        ID_ALICE_SWAP = os.path.join(TEST_DIR,
                                     'test_04_alice_swap_state') + '.json'
        ID_BOB_SWAP = os.path.join(TEST_DIR,
                                   'test_04_bob_swap_state') + '.json'

        alice_btc_start = make_int(
            callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted'])
        bob_btc_start = make_int(
            callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted'])
        alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR,
                                                 'get_balance')['balance']
        bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR,
                                               'get_balance')['balance']

        logging.info(
            'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld',
            alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start)

        # Same steps as in test_01_swap_successful
        self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 3, 4)
        msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f')
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f)
        msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l')
        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l)
        msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f')
        a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip()

        logging.info(
            'Bob verifies the lock spend tx and encrypted signature from Alice.'
        )
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg4f)

        logging.info('Bob waits for the script-chain lock tx to confirm.')

        num_tries = 30
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_BOB_SWAP, 'confirmalocktx')
            print('confirmalocktx', rv)
            if rv.strip() == 'True':
                break

            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for script-chain lock tx to confirm.')

        logging.info('Then publishes the second-chain lock tx.')
        b_lock_txid = callSwapTool(ID_BOB_SWAP, 'publishblocktx')

        logging.info(
            'Alice waits for the scriptless-chain lock tx to confirm.')

        num_tries = 120
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_ALICE_SWAP, 'confirmblocktx')
            print('confirmblocktx', rv)
            if rv.strip() == 'True':
                break

            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for scriptless-chain lock tx to confirm.'
                )
            time.sleep(2)

        logging.info(
            'Alice detects a problem with the scriptless-chain lock tx and decides to cancel the swap'
        )

        a_lock_refund_txid = self.publishALockRefundTx(ID_ALICE_PART,
                                                       ID_ALICE_SWAP)

        # Import key to receive refund in wallet.  Simple method for testing.
        kal = callSwapTool(ID_ALICE_SWAP, 'getkal')
        kal_wif = bytes_to_wif(h2b(kal), prefix=0x2e)
        callnoderpc(ID_ALICE_PART, 'importprivkey', [kal_wif, 'swap refund'])

        alockrefundspendtxid = callSwapTool(ID_ALICE_SWAP,
                                            'publishalockrefundspendtx')

        rv = callnoderpc(ID_ALICE_PART, 'getbalances')
        print('getbalances', dumpj(rv))
        alice_btc = make_int(rv['mine']['trusted']) + make_int(
            rv['mine']['untrusted_pending'])
        logging.info('alice_btc %ld', alice_btc)

        logging.info('Bob waits for Alice to spend the lock refund tx.')

        num_tries = 20
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_BOB_SWAP, 'findalockrefundspendtx')
            print('findalockrefundspendtx', rv)
            if rv.strip() == 'True':
                break
            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for script-chain lock refund spend tx to confirm.'
                )
            time.sleep(1)

        logging.info('Then he can recover his scriptless-chain lock tx coin.')

        self.callxmrnodewallet(ID_BOB_XMR, 'open_wallet',
                               {'filename': 'testwallet'})
        xmr_addr_bob = self.callxmrnodewallet(ID_BOB_XMR,
                                              'get_address')['address']

        rv = callSwapTool(ID_BOB_SWAP, 'redeemblocktx', str_param=xmr_addr_bob)
        print('redeemblocktx', rv)

        self.callxmrnodewallet(ID_BOB_XMR, 'close_wallet')
        self.callxmrnodewallet(ID_BOB_XMR, 'open_wallet',
                               {'filename': 'testwallet'})
コード例 #7
0
    def test_03_follower_recover_a_lock_tx(self):
        ID_ALICE_SWAP = os.path.join(TEST_DIR,
                                     'test_03_alice_swap_state') + '.json'
        ID_BOB_SWAP = os.path.join(TEST_DIR,
                                   'test_03_bob_swap_state') + '.json'

        alice_btc_start = make_int(
            callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted'])
        bob_btc_start = make_int(
            callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted'])
        alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR,
                                                 'get_balance')['balance']
        bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR,
                                               'get_balance')['balance']

        logging.info(
            'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld',
            alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start)

        # Same steps as in test_01_swap_successful
        self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 3, 4)
        msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f')
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f)
        msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l')
        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l)
        msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f')
        a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip()

        logging.info('Alice stops responding here.')

        # Wait for the mining node to receive the tx
        for i in range(10):
            try:
                callnoderpc(0, 'getrawtransaction', [a_lock_txid])
                break
            except Exception as e:
                print('Waiting for node 0 to see tx', str(e))
            time.sleep(1)

        a_lock_refund_txid = self.publishALockRefundTx(ID_BOB_PART,
                                                       ID_BOB_SWAP)

        # Wait for the mining node to receive the tx
        for i in range(10):
            try:
                callnoderpc(0, 'getrawtransaction', [a_lock_refund_txid])
                break
            except Exception as e:
                print('Waiting for node 0 to see tx', str(e))
            time.sleep(1)

        btc_addr_bob = callnoderpc(
            ID_BOB_PART, 'getnewaddress',
            ['bob\'s addr', False, False, False, 'bech32'])
        ignr, a_pkhash_f = segwit_addr.decode('rtpw', btc_addr_bob)
        assert (a_pkhash_f is not None)

        a_lock_refund_spend_txid = self.publishALockRefundFollowerSpendTx(
            ID_BOB_PART, ID_BOB_SWAP, a_pkhash_f)

        rv = callnoderpc(ID_BOB_PART, 'getbalances')
        print('getbalances', dumpj(rv))
        bob_btc_end = make_int(rv['mine']['trusted']) + make_int(
            rv['mine']['untrusted_pending'])
        logging.info('bob_btc_end %ld', bob_btc_end)

        assert (bob_btc_end > bob_btc_start)
コード例 #8
0
    def test_02_leader_recover_a_lock_tx(self):
        ID_ALICE_SWAP = os.path.join(TEST_DIR,
                                     'test_02_alice_swap_state') + '.json'
        ID_BOB_SWAP = os.path.join(TEST_DIR,
                                   'test_02_bob_swap_state') + '.json'

        alice_btc_start = make_int(
            callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted'])
        bob_btc_start = make_int(
            callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted'])

        alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR,
                                                 'get_balance')['balance']
        bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR,
                                               'get_balance')['balance']

        logging.info(
            'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld',
            alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start)

        self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 2, 3)

        logging.info(
            'Alice creates the script-chain lock and refund txns and signs the refund tx, sends to Bob.'
        )
        msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f')

        logging.info(
            'Bob verifies the txns and signs the refund tx and creates an encrypted signature for the refund spend tx encumbered by Alice\'s coin B key share.'
        )
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f)
        msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l')

        logging.info(
            'Alice verifies the signature and encrypted signature from Bob.')
        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l)

        logging.info(
            'Creates the lock spend tx and signs an encrypted signature encumbered by Bob\'s coin B key share'
        )
        msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f')

        logging.info('Publishes the script-chain lock tx.')
        a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip()

        # Wait for the mining node to receive the tx
        for i in range(10):
            try:
                callnoderpc(0, 'getrawtransaction', [a_lock_txid])
                break
            except Exception as e:
                print('Waiting for node 0 to see tx', str(e))
            time.sleep(1)

        logging.info('Bob stops responding here.')

        alice_btc = make_int(
            callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted'])
        logging.info('alice_btc %ld', alice_btc)

        a_lock_refund_txid = self.publishALockRefundTx(ID_ALICE_PART,
                                                       ID_ALICE_SWAP)

        # Import key to receive refund in wallet.  Simple method for testing.
        kal = callSwapTool(ID_ALICE_SWAP, 'getkal')
        kal_wif = bytes_to_wif(h2b(kal), prefix=0x2e)
        callnoderpc(ID_ALICE_PART, 'importprivkey', [kal_wif, 'swap refund'])

        alockrefundspendtxid = callSwapTool(ID_ALICE_SWAP,
                                            'publishalockrefundspendtx')

        rv = callnoderpc(ID_ALICE_PART, 'getbalances')
        alice_btc_end = make_int(rv['mine']['trusted']) + make_int(
            rv['mine']['untrusted_pending'])
        logging.info('alice_btc_end %ld', alice_btc_end)

        assert (alice_btc_end > alice_btc)
コード例 #9
0
    def test_01_swap_successful(self):
        ID_ALICE_SWAP = os.path.join(TEST_DIR,
                                     'test_01_alice_swap_state') + '.json'
        ID_BOB_SWAP = os.path.join(TEST_DIR,
                                   'test_01_bob_swap_state') + '.json'

        self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 1, 2)

        logging.info(
            'Alice creates the script-chain lock and refund txns and signs the refund tx, sends to Bob.'
        )
        msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f')

        logging.info(
            'Bob verifies the txns and signs the refund tx and creates an encrypted signature for the refund spend tx encumbered by Alice\'s coin B key share.'
        )
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f)
        msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l')

        logging.info(
            'Alice verifies the signature and encrypted signature from Bob.')
        callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l)

        logging.info(
            'Creates the lock spend tx and signs an encrypted signature encumbered by Bob\'s coin B key share'
        )
        msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f')

        logging.info('Publishes the script-chain lock tx.')
        a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx')

        # Check that the script-chain lock refund tx isn't mineable yet
        try:
            rv = callSwapTool(ID_ALICE_SWAP, 'publishalockrefundtx')
            assert (False)
        except Exception as e:
            assert ('non-BIP68-final' in str(e))

        logging.info(
            'Bob verifies the lock spend tx and encrypted signature from Alice.'
        )
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg4f)

        logging.info('Bob waits for the script-chain lock tx to confirm.')

        num_tries = 30
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_BOB_SWAP, 'confirmalocktx')
            print('confirmalocktx', rv)
            if rv.strip() == 'True':
                break

            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for script-chain lock tx to confirm.')

        logging.info('Then publishes the second-chain lock tx.')
        b_lock_txid = callSwapTool(ID_BOB_SWAP, 'publishblocktx')

        logging.info(
            'Alice waits for the scriptless-chain lock tx to confirm.')

        num_tries = 120
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_ALICE_SWAP, 'confirmblocktx')
            print('confirmblocktx', rv)
            if rv.strip() == 'True':
                break

            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for scriptless-chain lock tx to confirm.'
                )
            time.sleep(2)

        logging.info(
            'Alice shares the secret value with Bob, allowing the script-chain lock tx to be spent'
        )
        msg5f = callSwapTool(ID_ALICE_SWAP, 'msg5f')
        callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg5f)

        logging.info('Bob spends from the script-chain lock tx')
        alockspendtxid = callSwapTool(ID_BOB_SWAP, 'publishalockspendtx')
        logging.info('alockspendtxid %s', alockspendtxid)

        logging.info(
            'Alice looks for Bob\'s script-chain lock spend tx and extracts the sig'
        )

        num_tries = 20
        for i in range(1 + num_tries):
            rv = callSwapTool(ID_ALICE_SWAP, 'findalockspendtx')
            print('findalockspendtx', rv)
            if rv.strip() == 'True':
                break

            if i >= num_tries:
                raise ValueError(
                    'Timed out waiting for script-chain lock spend tx to confirm.'
                )
            time.sleep(1)

        self.callxmrnodewallet(ID_ALICE_XMR, 'open_wallet',
                               {'filename': 'testwallet'})
        xmr_addr_alice1 = self.callxmrnodewallet(ID_ALICE_XMR,
                                                 'get_address')['address']

        logging.info(
            'Alice redeems the scriptless-chain lock tx to her address: %s',
            xmr_addr_alice1)
        rv = callSwapTool(ID_ALICE_SWAP,
                          'redeemblocktx',
                          str_param=xmr_addr_alice1)
        print('redeemblocktx', rv)

        self.callxmrnodewallet(ID_ALICE_XMR, 'close_wallet')
        self.callxmrnodewallet(ID_ALICE_XMR, 'open_wallet',
                               {'filename': 'testwallet'})

        logging.info('Waiting for Alice\'s XMR to confirm...')
        num_tries = 120
        for i in range(num_tries + 1):
            rv = self.callxmrnodewallet(ID_ALICE_XMR, 'get_balance')
            if rv['balance'] > 0 and rv['blocks_to_unlock'] == 0:
                break

            r = callrpc_xmr_na(XMR_BASE_RPC_PORT + ID_ALICE_XMR,
                               'get_block_count')
            print('XMR blocks', r['count'])

            if i >= num_tries:
                raise ValueError(
                    'Balance not confirming on node {}'.format(ID_ALICE_XMR))
            time.sleep(2)

        logging.info('Waiting for Bob\'s BTC to confirm...')
        for i in range(num_tries + 1):
            rv = callnoderpc(ID_BOB_PART, 'getbalances')
            if rv['mine']['trusted'] > 0:
                break
            print('btc height', i,
                  callnoderpc(ID_BOB_PART, 'getblockchaininfo')['blocks'])
            if i >= num_tries:
                raise ValueError(
                    'Balance not confirming on node {}'.format(ID_ALICE_XMR))
            time.sleep(1)