Example #1
0
    def interrupt_restore(self):
        '''
            User wants the restoration interrupted.
            This is not recommended because it almost
            always leaves the blockchain in unusable shape.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> restore_dir = os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1')
            >>> restore_task = RestoreTask(restore_dir)
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> test_utils.start_fake_restore()
            >>> restore_task.interrupt_restore()
            True
        '''

        max_secs = 3
        seconds = 0

        try:
            bin_dir = os.path.join(virtualenv_dir(), 'bin')
            args = [
                os.path.join(bin_dir, 'killmatch'), constants.RESTORE_PROGRAM
            ]
            args = [
                os.path.join('/usr/local/bin', 'killmatch'),
                constants.RESTORE_PROGRAM
            ]
            self.log(f'args: {args}')

            attempts = 0
            while bitcoin_utils.is_restore_running() and attempts < 5:
                command.run(*args)
                if bitcoin_utils.is_restore_running():
                    sleep(3)
                    attempts += 1
        except CalledProcessError as cpe:
            self.log(cpe)
            self.log(format_exc())

        # a new page was displayed so give socketio time to connect
        while seconds < max_secs:
            self.manager.update_header(self.STOPPED_RESTORE)
            self.manager.update_subnotice(self.STOP_RESTORE_NOT_COMPLETE)
            self.manager.notify_done()

            sleep(1)
            seconds += 1

        # return value is for testing purposes only
        return not bitcoin_utils.is_restore_running()
Example #2
0
    def wait_for_restore(self, restore_process):
        '''
            Wait for the restore to finish and display data while waiting.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> restore_task = RestoreTask(os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1'))
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> restore_process = restore_task.start_restore()
            >>> restore_process is not None
            True
            >>> restore_task.wait_for_restore(restore_process)
            >>> test_utils.stop_restore()
            >>> restore_task.wait_for_restore(None)
            >>> test_utils.stop_restore()
        '''
        def show_line(line):
            if line is not None and line.startswith('Copying:'):
                index = line.rfind(os.sep)
                if index > 0:
                    line = f'<strong>Copying: </strong>{line[index + 1:]}'
                self.manager.update_progress(line)

        self.log('starting to wait for restore')

        if restore_process is None:
            log_path = os.path.join(BASE_LOG_DIR, whoami(), 'bcb-restore.log')

            # wait until the log appears
            while bitcoin_utils.is_restore_running(
            ) and not self.is_interrupted():

                if not os.path.exists(log_path):
                    sleep(1)

            # then display the restore details
            while bitcoin_utils.is_restore_running(
            ) and not self.is_interrupted():

                with open(log_path, 'rt') as restore_log:
                    show_line(restore_log.readline())
        else:
            while (restore_process.poll() is None
                   and not self.is_interrupted()):

                show_line(restore_process.stdout.readline())

        self.log('finished waiting for restore')
Example #3
0
    def check_for_conflicts(self, request):
        ''' Check if there are other apps/tasks running. '''

        response = None

        # check that no other bitcoin-core app is running
        if bitcoin_utils.is_bitcoin_core_running():
            log(f'bitcoind running: {bitcoin_utils.is_bitcoind_running()}')
            log(f'bitcoin_qt running: {bitcoin_utils.is_bitcoin_qt_running()}')
            log(f'bitcoin_tx running: {bitcoin_utils.is_bitcoin_tx_running()}')

            response = warn_core_running(request)

        # tell user if backup is running
        elif bitcoin_utils.is_backup_running():
            log('backup running')
            response = warn_bcb_app_running(request,
                                            app=constants.BACKUP_PROGRAM)

        # tell user if restore is already running
        elif bitcoin_utils.is_restore_running():
            log('restore running')
            response = self.restore()

        # tell user if another task is running
        elif accessing_wallet() or updating() or backing_up():
            log('task running')
            response = warn_bcb_task_running(request)

        return response
Example #4
0
    def test_restore_with_missing_blocks(self):
        '''
            Test restore with missing blocks in blockchain.
        '''
        MINUTE = 60
        MAX_SECONDS = 2 * MINUTE

        # remove a block from the blockchain
        data_dir = preferences.get_data_dir()
        block_filename = os.path.join(data_dir, 'blocks', 'blk00000.dat')
        if os.path.exists(block_filename): os.remove(block_filename)

        __, preselected_date_dir = state.get_backup_dates_and_dirs()

        request = self.factory.get('/bitcoin/restore/')
        response = views.Restore.as_view()(request)
        self.assertEqual(response.status_code, 200)
        self.assertTrue(b"<title>\nAre you sure you want to restore the Bitcoin blockchain? | Blockchain Backup\n</title>" in response.content)
        self.assertTrue(b'Select backup to restore:' in response.content)
        self.assertTrue(b'<input type="submit" value="Yes, start restoring" name="yes-start-restoring-button" id="yes-start-restoring-id" alt="Yes, start restoring" class="btn btn-primary font-weight-bold " role="button"  title="Restore the blockchain now"/>' in response.content)
        self.assertTrue(b'<input type="submit" value="No, cancel restore" name="no-cancel-restore-button" id="no-cancel-restore-id" alt="No, cancel restore" class="btn btn-secondary font-weight-bold " role="button"  title="Do not restore the blockchain."/>' in response.content)

        client = Client()
        response = client.post('/bitcoin/restore/',
                               {'backup_dates_with_dirs': preselected_date_dir[0] })
        self.assertEqual(response.status_code, 200)
        self.assertTrue(b"<title>\nRestoring Bitcoin Blockchain | Blockchain Backup\n</title>" in response.content)
        self.assertTrue(b'WARNING: Do not shut down your computer until the restore finishes.' in response.content)
        self.assertTrue(b'Starting to restore the blockchain' in response.content)
        self.assertFalse(b'<input type="submit" value="Yes, start restoring" name="yes-start-restoring-button" id="yes-start-restoring-id" alt="Yes, start restoring" class="btn btn-primary font-weight-bold " role="button"  title="Restore the blockchain now"/>' in response.content)

        # wait until the restore finishes
        seconds = 0
        while not is_restore_running() and seconds < MAX_SECONDS:
            sleep(MINUTE)
            seconds += MINUTE

        # wait until bitcoind starts
        test_utils.start_bitcoind()
        seconds = 0
        while not is_bitcoind_running() and seconds < MAX_SECONDS:
            sleep(MINUTE)
            seconds += MINUTE

        self.assertTrue(is_bitcoind_running())

        # allow bitcoind to run for a while
        seconds = 0
        while is_bitcoind_running() and seconds < MAX_SECONDS:
            sleep(MINUTE)
            seconds += MINUTE

        if is_bitcoind_running():
            test_utils.stop_bitcoind()

        shutdown, error_message = test_utils.check_bitcoin_log(is_bitcoind_running)
        self.assertTrue(shutdown)
        self.assertTrue(error_message is None)
Example #5
0
    def restore_files_and_dirs(self):
        '''
            Restore files and directories to
            a previous state of the blockchain.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> restore_dir = os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1')
            >>> restore_task = RestoreTask(restore_dir)
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> restore_task.restore_files_and_dirs()
            True
            >>> test_utils.stop_restore()
            >>> test_utils.start_fake_restore()
            >>> restore_task = RestoreTask(restore_dir)
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> restore_task.restore_files_and_dirs()
            True
            >>> test_utils.stop_restore()
        '''

        ok = True
        try:
            if bitcoin_utils.is_restore_running():
                restore_pid = get_pid(constants.RESTORE_PROGRAM)
                restore_process = None
                self.log('{} is already running using pid: {}'.format(
                    constants.RESTORE_PROGRAM, restore_pid))
            else:
                self.log('starting restore')
                restore_process = self.start_restore()
                restore_pid = None

            if restore_process is not None or restore_pid is not None:
                self.wait_for_restore(restore_process)

                self.stop_restore(restore_process, restore_pid)
            else:
                self.manager.update_progress(self.RESTORE_UNABLE_TO_START)
                ok = False
        except:  # 'bare except' because it catches more than "except Exception"
            ok = False
            self.log(format_exc())

        if not ok:
            self.manager.update_progress(self.RESTORE_ERROR)

        return ok
Example #6
0
def stop_restore():
    '''
        Stop the restore.

        >>> init_database()
        >>> stop_restore()
    '''
    while bitcoin_utils.is_restore_running():
        sleep(5)

        bin_dir = os.path.join(virtualenv_dir(), 'bin')
        excluded_files = bitcoin_utils.get_excluded_files()
        args = [os.path.join(bin_dir, 'killmatch'),
                f'"{constants.RESTORE_PROGRAM} --exclude {excluded_files}"']
        result = command.run(*args).stdout
        log(f'killing restore result: {result}')
Example #7
0
    def tearDown(self):

        # make sure nothing is left running
        test_utils.stop_bitcoin_core_apps()
        if is_restore_running():
            views.InterruptRestore.as_view()(self.factory.get('/bitcoin/interrupt_restore/'))
            sleep(20)
        if is_backup_running():
            views.InterruptBackup.as_view()(self.factory.get('/bitcoin/interrupt_backup/'))
            sleep(20)

        test_utils.stop_bitcoind()
        sleep(20)

        # some tests might create a .bitcon in the home dir
        test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
Example #8
0
    def stop_restore(self, restore_process, restore_pid):
        '''
            Stop restore.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> restore_task = RestoreTask(os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1'))
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> restore_process = restore_task.start_restore()
            >>> restore_process is not None
            True
            >>> restore_task.stop_restore(restore_process, None)
            >>> test_utils.start_fake_restore()
            >>> restore_pid = get_pid(constants.RESTORE_PROGRAM)
            >>> restore_task.stop_restore(None, restore_pid)
        '''
        try:
            if restore_process is None:
                if bitcoin_utils.is_restore_running():
                    bin_dir = os.path.join(virtualenv_dir(), 'bin')
                    args = [
                        os.path.join(bin_dir, 'killmatch'),
                        '"{} --exclude {}"'.format(
                            constants.RESTORE_PROGRAM,
                            bitcoin_utils.get_excluded_files())
                    ]
                    result = command.run(*args).stdout
                    self.log(f'killing restore result: {result}')

                try:
                    pid, returncode = os.waitpid(restore_pid, os.P_WAIT)
                    self.log(f'waitpid {pid} return code: {returncode}')
                except ChildProcessError:
                    self.log('restore_pid already dead')

            else:
                # if bcb-restore hasn't stopped yet, then kill it
                if restore_process.poll() is None:
                    self.log('killing restore')
                    restore_process.terminate()

                # wait until restore terminates
                restore_process.wait()
                self.log(f'restore return code: {restore_process.returncode}')
        except:  # 'bare except' because it catches more than "except Exception"
            self.log(f'error while stopping restore\n{format_exc()}')
            self.log(f'error while stopping restore\n{format_exc()}')
Example #9
0
    def test_restore_with_bad_backup(self):
        '''
            Test restore from a bad back up.
        '''
        MINUTE = 60
        MAX_SECONDS = 2 * MINUTE

        # change the data and backup dirs
        prefs = test_utils.get_preferences()
        prefs.data_dir = '/tmp/bitcoin/data-with-missing-file/testnet3/'
        prefs.backup_dir = '/tmp/bitcoin/data-with-missing-file/testnet3/backups/'
        preferences.save_preferences(prefs)

        __, preselected_date_dir = state.get_backup_dates_and_dirs()

        request = self.factory.get('/bitcoin/restore/')
        response = views.Restore.as_view()(request)
        self.assertEqual(response.status_code, 200)
        self.assertTrue(b"<title>\nAre you sure you want to restore the Bitcoin blockchain? | Blockchain Backup\n</title>" in response.content)
        self.assertTrue(b'Select backup to restore:' in response.content)
        self.assertTrue(b'<input type="submit" value="Yes, start restoring" name="yes-start-restoring-button" id="yes-start-restoring-id" alt="Yes, start restoring" class="btn btn-primary font-weight-bold " role="button"  title="Restore the blockchain now"/>' in response.content)
        self.assertTrue(b'<input type="submit" value="No, cancel restore" name="no-cancel-restore-button" id="no-cancel-restore-id" alt="No, cancel restore" class="btn btn-secondary font-weight-bold " role="button"  title="Do not restore the blockchain."/>' in response.content)

        client = Client()
        response = client.post('/bitcoin/restore/',
                               {'backup_dates_with_dirs': preselected_date_dir[0] })
        self.assertEqual(response.status_code, 200)
        self.assertTrue(b"<title>\nRestoring Bitcoin Blockchain | Blockchain Backup\n</title>" in response.content)
        self.assertTrue(b'WARNING: Do not shut down your computer until the restore finishes.' in response.content)
        self.assertTrue(b'Starting to restore the blockchain' in response.content)
        self.assertFalse(b'<input type="submit" value="Yes, start restoring" name="yes-start-restoring-button" id="yes-start-restoring-id" alt="Yes, start restoring" class="btn btn-primary font-weight-bold " role="button"  title="Restore the blockchain now"/>' in response.content)

        # wait until the restore finishes
        seconds = 0
        while not is_restore_running() and seconds < MAX_SECONDS:
            sleep(MINUTE)
            seconds += MINUTE

        self.assertFalse(is_bitcoind_running())
        self.assertFalse(state.get_backups_enabled())

        name = 'last-updated-2020-01-17 14:41'
        last_updated_file = os.path.join(prefs.backup_dir, 'level1', name)
        self.assertFalse(os.path.exists(last_updated_file))
Example #10
0
    def get_page(self, request):

        global backup_task

        log('backing up blockchain')

        clear_action_updates()

        # check that no other bitcoin-core app is running
        if bitcoin_utils.is_bitcoin_core_running():
            message = NO_BACKUP_IF_CORE_RUNNING
            response = warn_core_running(request, message=message)

        # tell user if another app is running
        elif bitcoin_utils.is_restore_running():
            response = warn_bcb_app_running(request,
                                            app=constants.RESTORE_PROGRAM)

        # tell user if another task is running
        elif accessing_wallet() or updating() or restoring():
            response = warn_bcb_task_running(request)

        # tell user if backups have been disabled
        elif not state.get_backups_enabled():
            response = HttpResponseRedirect('/bitcoin/change_backup_status/')
            log('tried to backup when backups disabled')
            log(f'response: {response}')

        else:
            SUBNOTICE1 = 'If you need to stop the backup, then <a class="btn btn-secondary " href="/bitcoin/interrupt_backup/"'
            SUBNOTICE2 = 'role="button" id="stop-button" title="Click to stop backing up the blockchain">click here</a>.'
            SUBNOTICE = f'{SUBNOTICE1} {SUBNOTICE2}'

            context = bitcoin_utils.get_blockchain_context()

            data_dir_ok, error = preferences.data_dir_ok()
            if not preferences.bin_dir_ok():
                context['notice'] = BAD_BIN_DIR
                context['subnotice'] = ''
            elif not data_dir_ok:
                context['notice'] = BAD_DATA_DIR
                context['subnotice'] = error
            else:
                context['header'] = "Backing up bitcoin's blockchain"
                context[
                    'notice'] = 'WARNING: Stopping the backup could damage the ability to restore the blockchain.'
                context['subnotice'] = SUBNOTICE
                context['progress'] = 'Starting to back up the blockchain'
                context['update_interval'] = '1000'

                if backing_up():
                    log('already backing_up blockchain')

                else:
                    from blockchain_backup.bitcoin.backup import BackupTask

                    backup_task = BackupTask()
                    backup_task.start()
                    log('backup started')

            # make sure the button doesn't appear any more
            bitcoin_utils.send_socketio_message('button', ' ')

            response = render(request, 'bitcoin/backup.html', context=context)

        return response
Example #11
0
    def get_page(self, request):

        global update_task

        log('trying to update blockchain')

        clear_action_updates()

        # check that no other bitcoin-core app is running
        if (bitcoin_utils.is_bitcoin_qt_running()
                or bitcoin_utils.is_bitcoin_tx_running()
                or (bitcoin_utils.is_bitcoind_running() and not updating())):
            response = warn_core_running(request)

        # tell user if another blockchain_backup app is running
        elif bitcoin_utils.is_backup_running(
        ) or bitcoin_utils.is_restore_running():
            response = warn_bcb_app_running(request)

        # tell user if another task is running
        elif accessing_wallet() or backing_up() or restoring():
            response = warn_bcb_task_running(request)

        else:
            NOTICE1 = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button"'
            NOTICE2 = 'id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>'
            NOTICE = f'{NOTICE1} {NOTICE2}'

            context = bitcoin_utils.get_blockchain_context()

            data_dir_ok, error = preferences.data_dir_ok()
            if not preferences.bin_dir_ok():
                context['notice'] = BAD_BIN_DIR
                log(BAD_BIN_DIR)
            elif not data_dir_ok:
                context['notice'] = BAD_DATA_DIR
                log(error)
            else:
                context['header'] = "Updating Bitcoin's blockchain"
                context[
                    'notice'] = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button" id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>'
                context[
                    'subnotice'] = 'Shutting down before this window says it is safe <em>could damage the blockchain</em>.'
                context['progress'] = 'Starting to update the blockchain'
                context['update_interval'] = '5000'

                if updating():
                    log('already updating blockchain')

                else:
                    from blockchain_backup.bitcoin.update import UpdateTask

                    update_task = UpdateTask()
                    update_task.start()
                    log('UpdateTask started')

            # make sure the button doesn't appear any more
            bitcoin_utils.send_socketio_message('button', ' ')

            log(f'context: {context}')
            response = render(request, 'bitcoin/update.html', context=context)

        return response
Example #12
0
    def get_page(self, request):

        global accessing_wallet_task

        log('accessing bitcoin interactively')

        clear_action_updates()

        # it's ok if bitcoin-qt is running in our task
        if (bitcoin_utils.is_bitcoind_running()
                or bitcoin_utils.is_bitcoin_tx_running()
                or (bitcoin_utils.is_bitcoin_qt_running()
                    and not accessing_wallet())):
            response = warn_core_running(request)

        # tell user if another app is running
        elif bitcoin_utils.is_backup_running(
        ) or bitcoin_utils.is_restore_running():
            response = warn_bcb_app_running(request)

        # tell user if another task is running
        elif updating() or backing_up() or restoring():
            response = warn_bcb_task_running(request)

        else:
            SUBNOTICE1 = "Waiting until you exit Bitcoin-QT.<p>Bitcoin-QT will start in another window."
            SUBNOTICE2 = "You can send and receive transactions from that window."
            SUBNOTICE3 = "After you exit Bitcoin-QT, Blockchain Backup will continue updating the blockchain until it's time to back it up."
            SUBNOTICE4 = "Don't forget to back up your wallet routinely."
            SUBNOTICE = f'{SUBNOTICE1} {SUBNOTICE2} {SUBNOTICE3} {SUBNOTICE4}'

            context = bitcoin_utils.get_blockchain_context()

            data_dir_ok, error = preferences.data_dir_ok()
            if not preferences.bin_dir_ok():
                context['notice'] = BAD_BIN_DIR
            elif not data_dir_ok:
                context['notice'] = BAD_DATA_DIR
                context['subnotice'] = error
            else:
                context[
                    'header'] = "Accessing Bitcoin Core Wallet Interactively"
                context[
                    'notice'] = 'WARNING: Do not shut down your computer until Bitcoin-QT stops completely.'
                context['subnotice'] = SUBNOTICE

                if accessing_wallet():
                    log('already accessing wallet')

                else:
                    from blockchain_backup.bitcoin.access_wallet import AccessWalletTask

                    accessing_wallet_task = AccessWalletTask()
                    accessing_wallet_task.start()
                    log('access wallet started')

                # make sure the button doesn't appear any more
                bitcoin_utils.send_socketio_message('button', ' ')

            response = render(request,
                              'bitcoin/access_wallet.html',
                              context=context)

        return response