URI: 
       tmove openalias from plugins to core - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 616becd9a811fae31f0b50f602a60e318538f56b
   DIR parent 90d32038faf618031241e2ca4ea206ee7504051c
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Thu,  2 Jul 2015 12:44:53 +0200
       
       move openalias from plugins to core
       
       Diffstat:
         M gui/qt/main_window.py               |      31 ++++++++++++++++++-------------
         M gui/qt/paytoedit.py                 |      48 +++++++++++++++++++++++++++++--
         M lib/commands.py                     |       3 ++-
         A lib/contacts.py                     |     172 ++++++++++++++++++++++++++++++
         M lib/util.py                         |      19 -------------------
         M plugins/__init__.py                 |       7 -------
         D plugins/openalias.py                |     297 -------------------------------
       
       7 files changed, 238 insertions(+), 339 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -19,6 +19,10 @@
        import sys, time, threading
        import os.path, json, traceback
        import shutil
       +import socket
       +import webbrowser
       +import csv
       +from decimal import Decimal
        
        
        import PyQt4
       t@@ -26,11 +30,10 @@ from PyQt4.QtGui import *
        from PyQt4.QtCore import *
        import PyQt4.QtCore as QtCore
        
       -from electrum.bitcoin import MIN_RELAY_TX_FEE, COIN, is_valid
       -from electrum.plugins import run_hook
       -
        import icons_rc
        
       +from electrum.bitcoin import MIN_RELAY_TX_FEE, COIN, is_valid
       +from electrum.plugins import run_hook
        from electrum.i18n import _
        from electrum.util import block_explorer, block_explorer_info, block_explorer_URL
        from electrum.util import print_error, print_msg
       t@@ -41,6 +44,7 @@ from electrum import util, bitcoin, commands, Wallet
        from electrum import SimpleConfig, Wallet, WalletStorage
        from electrum import Imported_Wallet
        from electrum import paymentrequest
       +from electrum.contacts import Contacts
        
        from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit
        from network_dialog import NetworkDialog
       t@@ -48,12 +52,6 @@ from qrcodewidget import QRCodeWidget, QRDialog
        from qrtextedit import ScanQRTextEdit, ShowQRTextEdit
        from transaction_dialog import show_transaction
        
       -from decimal import Decimal
       -
       -import socket
       -import webbrowser
       -import csv
       -
        
        
        
       t@@ -120,7 +118,7 @@ class ElectrumWindow(QMainWindow):
                self.app = gui_object.app
        
                self.invoices = InvoiceStore(self.config)
       -        self.contacts = util.Contacts(self.config)
       +        self.contacts = Contacts(self.config)
        
                self.create_status_bar()
                self.need_update = threading.Event()
       t@@ -482,17 +480,16 @@ class ElectrumWindow(QMainWindow):
                if self.need_update.is_set():
                    self.update_wallet()
                    self.need_update.clear()
       -
       +        # resolve aliases
       +        self.payto_e.resolve()
                run_hook('timer_actions')
        
            def format_amount(self, x, is_diff=False, whitespaces=False):
                return format_satoshis(x, is_diff, self.num_zeros, self.decimal_point, whitespaces)
        
       -
            def get_decimal_point(self):
                return self.decimal_point
        
       -
            def base_unit(self):
                assert self.decimal_point in [2, 5, 8]
                if self.decimal_point == 2:
       t@@ -1051,6 +1048,13 @@ class ElectrumWindow(QMainWindow):
                        return
                    outputs = self.payto_e.get_outputs()
        
       +            if self.payto_e.is_alias and self.payto_e.validated is False:
       +                alias = self.payto_e.toPlainText()
       +                msg = _('WARNING: the alias "%s" could not be validated via an additional security check, DNSSEC, and thus may not be correct.'%alias) + '\n'
       +                msg += _('Do you wish to continue?')
       +                if not self.question(msg):
       +                    return
       +
                if not outputs:
                    QMessageBox.warning(self, _('Error'), _('No outputs'), _('OK'))
                    return
       t@@ -1208,6 +1212,7 @@ class ElectrumWindow(QMainWindow):
                    self.payment_request = None
                    return
        
       +        self.payto_e.is_pr = True
                if not pr.has_expired():
                    self.payto_e.setGreen()
                else:
   DIR diff --git a/gui/qt/paytoedit.py b/gui/qt/paytoedit.py
       t@@ -34,6 +34,7 @@ class PayToEdit(ScanQRTextEdit):
        
            def __init__(self, win):
                ScanQRTextEdit.__init__(self)
       +        self.win = win
                self.amount_edit = win.amount_e
                self.document().contentsChanged.connect(self.update_size)
                self.heightMin = 0
       t@@ -43,10 +44,13 @@ class PayToEdit(ScanQRTextEdit):
                self.outputs = []
                self.errors = []
                self.is_pr = False
       +        self.is_alias = False
                self.scan_f = win.pay_from_URI
                self.update_size()
                self.payto_address = None
        
       +        self.previous_payto = ''
       +
            def lock_amount(self):
                self.amount_edit.setFrozen(True)
        
       t@@ -60,11 +64,9 @@ class PayToEdit(ScanQRTextEdit):
                    button.setHidden(b)
        
            def setGreen(self):
       -        self.is_pr = True
                self.setStyleSheet("QWidget { background-color:#80ff80;}")
        
            def setExpired(self):
       -        self.is_pr = True
                self.setStyleSheet("QWidget { background-color:#ffcccc;}")
        
            def parse_address_and_amount(self, line):
       t@@ -252,3 +254,45 @@ class PayToEdit(ScanQRTextEdit):
                if data.startswith("bitcoin:"):
                    self.scan_f(data)
                    # TODO: update fee
       +
       +    def resolve(self):
       +        self.is_alias = False
       +        if self.hasFocus():
       +            return
       +        if self.is_multiline():  # only supports single line entries atm
       +            return
       +        if self.is_pr:
       +            return
       +        key = str(self.toPlainText())
       +        if key == self.previous_payto:
       +            return
       +        self.previous_payto = key
       +        if not (('.' in key) and (not '<' in key) and (not ' ' in key)):
       +            return
       +        try:
       +            data = self.win.contacts.resolve(key)
       +        except:
       +            return
       +        if not data:
       +            return
       +        self.is_alias = True
       +
       +        address = data.get('address')
       +        name = data.get('name')
       +        new_url = key + ' <' + address + '>'
       +        self.setText(new_url)
       +        self.previous_payto = new_url
       +
       +        #if self.win.config.get('openalias_autoadd') == 'checked':
       +        self.win.contacts[key] = ('openalias', name)
       +        self.win.update_contacts_tab()
       +
       +        self.setFrozen(True)
       +        if data.get('type') == 'openalias':
       +            self.validated = data.get('validated')
       +            if self.validated:
       +                self.setGreen()
       +            else:
       +                self.setExpired()
       +        else:
       +            self.validated = None
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -34,6 +34,7 @@ from bitcoin import is_address, hash_160_to_bc_address, hash_160, COIN
        from transaction import Transaction
        import paymentrequest
        from paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED
       +import contacts
        
        known_commands = {}
        
       t@@ -78,7 +79,7 @@ class Commands:
                self.network = network
                self._callback = callback
                self.password = None
       -        self.contacts = util.Contacts(self.config)
       +        self.contacts = contacts.Contacts(self.config)
        
            def _run(self, method, args, password_getter):
                cmd = known_commands[method]
   DIR diff --git a/lib/contacts.py b/lib/contacts.py
       t@@ -0,0 +1,172 @@
       +import sys
       +import re
       +import dns
       +import traceback
       +
       +import bitcoin
       +from util import StoreDict, print_error
       +from i18n import _
       +
       +# Import all of the rdtypes, as py2app and similar get confused with the dnspython
       +# autoloader and won't include all the rdatatypes
       +try:
       +    import dns.name
       +    import dns.query
       +    import dns.dnssec
       +    import dns.message
       +    import dns.resolver
       +    import dns.rdatatype
       +    import dns.rdtypes.ANY.NS
       +    import dns.rdtypes.ANY.CNAME
       +    import dns.rdtypes.ANY.DLV
       +    import dns.rdtypes.ANY.DNSKEY
       +    import dns.rdtypes.ANY.DS
       +    import dns.rdtypes.ANY.NSEC
       +    import dns.rdtypes.ANY.NSEC3
       +    import dns.rdtypes.ANY.NSEC3PARAM
       +    import dns.rdtypes.ANY.RRSIG
       +    import dns.rdtypes.ANY.SOA
       +    import dns.rdtypes.ANY.TXT
       +    import dns.rdtypes.IN.A
       +    import dns.rdtypes.IN.AAAA
       +    from dns.exception import DNSException
       +    OA_READY = True
       +except ImportError:
       +    OA_READY = False
       +
       +
       +class Contacts(StoreDict):
       +
       +    def __init__(self, config):
       +        StoreDict.__init__(self, config, 'contacts')
       +
       +    def resolve(self, k):
       +        if bitcoin.is_address(k):
       +            return {
       +                'address': k,
       +                'type': 'address'
       +            }
       +
       +        if k in self.keys():
       +            _type, addr = self[k]
       +            if _type == 'address':
       +                return {
       +                    'address': addr,
       +                    'type': 'contact'
       +                }
       +
       +        out = self.resolve_openalias(k)
       +        if out:
       +            address, name = out
       +            try:
       +                validated = self.validate_dnssec(k)
       +            except:
       +                validated = False
       +                traceback.print_exc(file=sys.stderr)
       +            return {
       +                'address': address,
       +                'name': name,
       +                'type': 'openalias',
       +                'validated': validated
       +            }
       +
       +        raise Exception("Invalid Bitcoin address or alias", k)
       +
       +    def resolve_openalias(self, url):
       +        '''Resolve OpenAlias address using url.'''
       +        print_error('[OA] Attempting to resolve OpenAlias data for ' + url)
       +
       +        url = url.replace('@', '.')  # support email-style addresses, per the OA standard
       +        prefix = 'btc'
       +        retries = 3
       +        err = None
       +        for i in range(0, retries):
       +            try:
       +                resolver = dns.resolver.Resolver()
       +                resolver.timeout = 2.0
       +                resolver.lifetime = 4.0
       +                records = resolver.query(url, dns.rdatatype.TXT)
       +                for record in records:
       +                    string = record.strings[0]
       +                    if string.startswith('oa1:' + prefix):
       +                        address = self.find_regex(string, r'recipient_address=([A-Za-z0-9]+)')
       +                        name = self.find_regex(string, r'recipient_name=([^;]+)')
       +                        if not name:
       +                            name = address
       +                        if not address:
       +                            continue
       +                        return (address, name)
       +                err = _('No OpenAlias record found.')
       +                break
       +            except dns.resolver.NXDOMAIN:
       +                err = _('No such domain.')
       +                continue
       +            except dns.resolver.Timeout:
       +                err = _('Timed out while resolving.')
       +                continue
       +            except DNSException:
       +                err = _('Unhandled exception.')
       +                continue
       +            except Exception, e:
       +                err = _('Unexpected error: ' + str(e))
       +                continue
       +            break
       +        if err:
       +            print_error(err)
       +        return 0
       +
       +    def find_regex(self, haystack, needle):
       +        regex = re.compile(needle)
       +        try:
       +            return regex.search(haystack).groups()[0]
       +        except AttributeError:
       +            return None
       +
       +    def validate_dnssec(self, url):
       +        print_error('Checking DNSSEC trust chain for ' + url)
       +        default = dns.resolver.get_default_resolver()
       +        ns = default.nameservers[0]
       +        parts = url.split('.')
       +
       +        for i in xrange(len(parts), 0, -1):
       +            sub = '.'.join(parts[i - 1:])
       +            query = dns.message.make_query(sub, dns.rdatatype.NS)
       +            response = dns.query.udp(query, ns, 3)
       +            if response.rcode() != dns.rcode.NOERROR:
       +                print_error("query error")
       +                return False
       +
       +            if len(response.authority) > 0:
       +                rrset = response.authority[0]
       +            else:
       +                rrset = response.answer[0]
       +
       +            rr = rrset[0]
       +            if rr.rdtype == dns.rdatatype.SOA:
       +                #Same server is authoritative, don't check again
       +                continue
       +
       +            query = dns.message.make_query(sub,
       +                                           dns.rdatatype.DNSKEY,
       +                                           want_dnssec=True)
       +            response = dns.query.udp(query, ns, 3)
       +            if response.rcode() != 0:
       +                self.print_error("query error")
       +                return False
       +                # HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
       +
       +            # answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
       +            answer = response.answer
       +            if len(answer) != 2:
       +                print_error("answer error", answer)
       +                return False
       +
       +            # the DNSKEY should be self signed, validate it
       +            name = dns.name.from_text(sub)
       +            try:
       +                dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
       +            except dns.dnssec.ValidationFailure:
       +                print_error("validation error")
       +                return False
       +
       +        return True
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -469,22 +469,3 @@ class StoreDict(dict):
                    self.save()
        
        
       -import bitcoin
       -from plugins import run_hook
       -
       -class Contacts(StoreDict):
       -
       -    def __init__(self, config):
       -        StoreDict.__init__(self, config, 'contacts')
       -
       -    def resolve(self, k):
       -        if bitcoin.is_address(k):
       -            return {'address':k, 'type':'address'}
       -        if k in self.keys():
       -            _type, addr = self[k]
       -            if _type == 'address':
       -                return {'address':addr, 'type':'contact'}
       -        out = run_hook('resolve_address', k)
       -        if out:
       -            return out
       -        raise Exception("Invalid Bitcoin address or alias", k)
   DIR diff --git a/plugins/__init__.py b/plugins/__init__.py
       t@@ -69,13 +69,6 @@ descriptions = [
                'available_for': ['qt']
            },
            {
       -        'name': 'openalias',
       -        'fullname': 'OpenAlias',
       -        'description': _('Allow for payments to OpenAlias addresses.'),
       -        'requires': [('dns', 'dnspython')],
       -        'available_for': ['qt', 'cmdline']
       -    },
       -    {
                'name': 'plot',
                'fullname': 'Plot History',
                'description': _("Ability to plot transaction history in graphical mode."),
   DIR diff --git a/plugins/openalias.py b/plugins/openalias.py
       t@@ -1,297 +0,0 @@
       -# Copyright (c) 2014-2015, The Monero Project
       -# 
       -# All rights reserved.
       -
       -# This plugin is licensed under the GPL v3 license (see the LICENSE file in the base of
       -# the project source code). The Monero Project reserves the right to change this license
       -# in future to match or be compliant with any relicense of the Electrum project.
       -
       -# This plugin implements the OpenAlias standard. For information on the standard please
       -# see: https://openalias.org
       -
       -# Donations for ongoing development of the standard and hosting resolvers can be sent to
       -# openalias.org or donate.monero.cc
       -
       -# Version: 0.1
       -# Todo: optionally use OA resolvers; add DNSCrypt support
       -
       -import re
       -import traceback
       -
       -from PyQt4.QtGui import *
       -from PyQt4.QtCore import *
       -
       -from electrum_gui.qt.util import *
       -from electrum.plugins import BasePlugin, hook
       -from electrum.util import print_error
       -from electrum.i18n import _
       -
       -
       -# Import all of the rdtypes, as py2app and similar get confused with the dnspython
       -# autoloader and won't include all the rdatatypes
       -try:
       -    import dns.name
       -    import dns.query
       -    import dns.dnssec
       -    import dns.message
       -    import dns.resolver
       -    import dns.rdatatype
       -    import dns.rdtypes.ANY.NS
       -    import dns.rdtypes.ANY.CNAME
       -    import dns.rdtypes.ANY.DLV
       -    import dns.rdtypes.ANY.DNSKEY
       -    import dns.rdtypes.ANY.DS
       -    import dns.rdtypes.ANY.NSEC
       -    import dns.rdtypes.ANY.NSEC3
       -    import dns.rdtypes.ANY.NSEC3PARAM
       -    import dns.rdtypes.ANY.RRSIG
       -    import dns.rdtypes.ANY.SOA
       -    import dns.rdtypes.ANY.TXT
       -    import dns.rdtypes.IN.A
       -    import dns.rdtypes.IN.AAAA
       -    from dns.exception import DNSException
       -    OA_READY = True
       -except ImportError:
       -    OA_READY = False
       -
       -
       -class Plugin(BasePlugin):
       -    def is_available(self):
       -        return OA_READY
       -
       -    def __init__(self, gui, name):
       -        BasePlugin.__init__(self, gui, name)
       -        self._is_available = OA_READY
       -        self.print_error('OA_READY is ' + str(OA_READY))
       -        self.previous_payto = ''
       -
       -    @hook
       -    def init_qt(self, gui):
       -        self.gui = gui
       -        self.win = gui.main_window
       -
       -    def requires_settings(self):
       -        return True
       -
       -    def settings_widget(self, window):
       -        return EnterButton(_('Settings'), self.settings_dialog)
       -
       -    @hook
       -    def timer_actions(self):
       -        if self.win.payto_e.hasFocus():
       -            return
       -        if self.win.payto_e.is_multiline():  # only supports single line entries atm
       -            return
       -        if self.win.payto_e.is_pr:
       -            return
       -
       -        url = str(self.win.payto_e.toPlainText())
       -
       -        if url == self.previous_payto:
       -            return
       -        self.previous_payto = url
       -
       -        if not (('.' in url) and (not '<' in url) and (not ' ' in url)):
       -            return
       -
       -        data = self.resolve(url)
       -
       -        if not data:
       -            self.previous_payto = url
       -            return True
       -
       -        address, name = data
       -        new_url = url + ' <' + address + '>'
       -        self.win.payto_e.setText(new_url)
       -        self.previous_payto = new_url
       -
       -        if self.config.get('openalias_autoadd') == 'checked':
       -            self.win.contacts[url] = ('openalias', name)
       -            self.win.update_contacts_tab()
       -
       -        self.win.payto_e.setFrozen(True)
       -        try:
       -            self.validated = self.validate_dnssec(url)
       -        except:
       -            self.validated = False
       -            traceback.print_exc(file=sys.stderr)
       -
       -        if self.validated:
       -            self.win.payto_e.setGreen()
       -        else:
       -            self.win.payto_e.setExpired()
       -
       -    @hook
       -    def before_send(self):
       -        '''
       -        Change URL to address before making a send.
       -        IMPORTANT:
       -            return False to continue execution of the send
       -            return True to stop execution of the send
       -        '''
       -
       -        if self.win.payto_e.is_multiline():  # only supports single line entries atm
       -            return False
       -        if self.win.payto_e.is_pr:
       -            return
       -        payto_e = str(self.win.payto_e.toPlainText())
       -        regex = re.compile(r'^([^\s]+) <([A-Za-z0-9]+)>')  # only do that for converted addresses
       -        try:
       -            (url, address) = regex.search(payto_e).groups()
       -        except AttributeError:
       -            return False
       -
       -        if not self.validated:
       -            msgBox = QMessageBox()
       -            msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
       -            msgBox.setInformativeText(_('Do you wish to continue?'))
       -            msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
       -            msgBox.setDefaultButton(QMessageBox.Cancel)
       -            reply = msgBox.exec_()
       -            if reply != QMessageBox.Ok:
       -                return True
       -
       -        return False
       -
       -    def settings_dialog(self):
       -        '''Settings dialog.'''
       -        d = QDialog()
       -        d.setWindowTitle("Settings")
       -        layout = QGridLayout(d)
       -        layout.addWidget(QLabel(_('Automatically add to contacts')), 0, 0)
       -        autoadd_checkbox = QCheckBox()
       -        autoadd_checkbox.setEnabled(True)
       -        autoadd_checkbox.setChecked(self.config.get('openalias_autoadd', 'unchecked') != 'unchecked')
       -        layout.addWidget(autoadd_checkbox, 0, 1)
       -        ok_button = QPushButton(_("OK"))
       -        ok_button.clicked.connect(d.accept)
       -        layout.addWidget(ok_button, 1, 1)
       -
       -        def on_change_autoadd(checked):
       -            if checked:
       -                self.config.set_key('openalias_autoadd', 'checked')
       -            else:
       -                self.config.set_key('openalias_autoadd', 'unchecked')
       -
       -        autoadd_checkbox.stateChanged.connect(on_change_autoadd)
       -
       -        return bool(d.exec_())
       -
       -
       -    @hook
       -    def resolve_address(self, url):
       -        data = self.resolve(url)
       -        if not data:
       -            return
       -        address, name = data
       -        try:
       -            validated = self.validate_dnssec(url)
       -        except:
       -            validated = False
       -            traceback.print_exc(file=sys.stderr)
       -        return {
       -            'address': address,
       -            'name': name,
       -            'type': 'openalias',
       -            'validated': validated
       -        }
       -
       -
       -    def resolve(self, url):
       -        '''Resolve OpenAlias address using url.'''
       -        self.print_error('[OA] Attempting to resolve OpenAlias data for ' + url)
       -
       -        url = url.replace('@', '.')  # support email-style addresses, per the OA standard
       -        prefix = 'btc'
       -        retries = 3
       -        err = None
       -        for i in range(0, retries):
       -            try:
       -                resolver = dns.resolver.Resolver()
       -                resolver.timeout = 2.0
       -                resolver.lifetime = 4.0
       -                records = resolver.query(url, dns.rdatatype.TXT)
       -                for record in records:
       -                    string = record.strings[0]
       -                    if string.startswith('oa1:' + prefix):
       -                        address = self.find_regex(string, r'recipient_address=([A-Za-z0-9]+)')
       -                        name = self.find_regex(string, r'recipient_name=([^;]+)')
       -                        if not name:
       -                            name = address
       -                        if not address:
       -                            continue
       -                        return (address, name)
       -                QMessageBox.warning(self.win, _('Error'), _('No OpenAlias record found.'), _('OK'))
       -                return 0
       -            except dns.resolver.NXDOMAIN:
       -                err = _('No such domain.')
       -                continue
       -            except dns.resolver.Timeout:
       -                err = _('Timed out while resolving.')
       -                continue
       -            except DNSException:
       -                err = _('Unhandled exception.')
       -                continue
       -            except Exception, e:
       -                err = _('Unexpected error: ' + str(e))
       -                continue
       -            break
       -        if err:
       -            QMessageBox.warning(self.win, _('Error'), err, _('OK'))
       -        return 0
       -
       -    def find_regex(self, haystack, needle):
       -        regex = re.compile(needle)
       -        try:
       -            return regex.search(haystack).groups()[0]
       -        except AttributeError:
       -            return None
       -
       -    def validate_dnssec(self, url):
       -        self.print_error('Checking DNSSEC trust chain for ' + url)
       -        default = dns.resolver.get_default_resolver()
       -        ns = default.nameservers[0]
       -        parts = url.split('.')
       -
       -        for i in xrange(len(parts), 0, -1):
       -            sub = '.'.join(parts[i - 1:])
       -            query = dns.message.make_query(sub, dns.rdatatype.NS)
       -            response = dns.query.udp(query, ns, 3)
       -            if response.rcode() != dns.rcode.NOERROR:
       -                self.print_error("query error")
       -                return False
       -
       -            if len(response.authority) > 0:
       -                rrset = response.authority[0]
       -            else:
       -                rrset = response.answer[0]
       -
       -            rr = rrset[0]
       -            if rr.rdtype == dns.rdatatype.SOA:
       -                #Same server is authoritative, don't check again
       -                continue
       -
       -            query = dns.message.make_query(sub,
       -                                           dns.rdatatype.DNSKEY,
       -                                           want_dnssec=True)
       -            response = dns.query.udp(query, ns, 3)
       -            if response.rcode() != 0:
       -                self.print_error("query error")
       -                return False
       -                # HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
       -
       -            # answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
       -            answer = response.answer
       -            if len(answer) != 2:
       -                self.print_error("answer error", answer)
       -                return False
       -
       -            # the DNSKEY should be self signed, validate it
       -            name = dns.name.from_text(sub)
       -            try:
       -                dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
       -            except dns.dnssec.ValidationFailure:
       -                self.print_error("validation error")
       -                return False
       -
       -        return True