diff --git a/namecoin.py b/namecoin.py new file mode 100644 index 0000000..795b59a --- /dev/null +++ b/namecoin.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python2 + +import json +from bitcoin.rpc import Proxy + +import httplib +import os.path + +import platform + +def get_proxy(user, password, port=8336): + return Proxy(service_url="http://{0}:{1}@localhost:{2}".format(user, password, port)) + +class NameTakenError(ValueError): + pass + +class Namecoin(object): + def __init__(self, proxy): + self.proxy = proxy + + def get_secret(self, name): + dns_record = self.proxy._call('name_show', 'd/' + name) # throws if the name doesn't exist, iirc + name_info = json.loads(dns_record['value']) + return name_info['syncnet']['secret'] # of course this could throw a KeyError + + def validate_address(self, name): + try: + self.clean_address(name) + except ValueError: + return False + else: + return True + + def clean_address(self, address): + name, suffix = address.rsplit('.', 1) + if suffix != 'bit': + raise ValueError("Address {0} ends with suffix .{1} instead of .bit".format(address, suffix)) + else: + return name + + def name_exists(self, name): + dns_record = self.proxy._call('name_show', 'd/' + name) # throws if the name doesn't exist, iirc + try: + name_info = json.loads(dns_record['value']) + except ValueError: + return False + else: + return bool(name_info.get('syncnet', {}).get('secret')) # of course this could throw a KeyError + + def register_name(self, name): + if self.name_exists(name): + raise NameTakenError("Name {0}.bit taken!".format(name)) # XXX: we might own this name though + + txid, rnd = self.proxy._call("name_new", 'd/' + name) + + # XXX: Store txid and/or rnd for name_firstupdate, poll at/around expected time + + def test_connection(self): + try: + self.proxy._call('help') + return True + except: + return False + + def _build_json_value(self, secret, existing=None): + existing = dict(existing) or {} + existing['syncnet'] = existing.get('syncnet', {}) + existing['syncnet']['secret'] = secret + return existing diff --git a/setup.py b/setup.py index 78a4607..cadb27c 100644 --- a/setup.py +++ b/setup.py @@ -8,12 +8,14 @@ README = f.read() requires = [ + 'python-bitcoinlib', 'atom', 'enaml', 'BTSync', ] dependency_links=[ + "https://github.com/petertodd/python-bitcoinlib/archive/pythonize.zip#egg=python-bitcoinlib", "https://github.com/ademan/python-btsync/archive/packaging.zip#egg=BTSync", ] @@ -25,9 +27,7 @@ "Programming Language :: Python", ], packages=find_packages(), - package_data={ - 'syncnet': ['*.enaml'], - }, + include_package_data=True, install_requires=requires, dependency_links=dependency_links, - ) + ) \ No newline at end of file diff --git a/syncnet/main.py b/syncnet/main.py index 5d21b45..6ef2938 100644 --- a/syncnet/main.py +++ b/syncnet/main.py @@ -8,7 +8,7 @@ import enaml from enaml.qt.qt_application import QtApplication from PyQt4.QtCore import QFileSystemWatcher -from atom.api import Atom, Unicode, observe, Typed, Property, Int +from atom.api import Atom, Unicode, observe, Typed, Property, Int, Value from btsync import BTSync @@ -36,6 +36,8 @@ class SyncNet(Atom): # Instance of the BTSync API wrapper. btsync = Typed(BTSync, ()) + dns = Value() + # The QUrl object referencing the currently displayed resource. It must be # replaced wholesale for the UI to react. url = Unicode() @@ -139,9 +141,20 @@ def _address_changed(self, change): a valid secret. If so, attempt to load that secret. """ - address = self.address.upper() - if self.is_valid_secret(address): - self.load_secret(address) + address = self.address.lower() + + try: + name = self.dns.clean_address(address) + except ValueError: + secret = self.address.upper() + else: + try: + secret = self.dns.get_secret(name) + except (IndexError, KeyError, IOError): + secret = self.address.upper() + + if self.is_valid_secret(secret): + self.load_secret(secret) def on_directory_changed(self, dirname): """ Slot connected to the `QFileSystemWatcher.directoryChanged` Signal. @@ -232,12 +245,24 @@ def _update_address_bar(self, url): with enaml.imports(): from syncnet_view import SyncNetView syncnet = SyncNet() + if getattr(sys, 'frozen', False): HERE = os.path.dirname(sys.executable) btsync_path = os.path.join( HERE, 'BitTorrent\ Sync.app/Contents/MacOS/BitTorrent\ Sync') syncnet.btsync.btsync_path = btsync_path syncnet.btsync.start() + + + from namecoin import get_proxy, Namecoin + namecoin = Namecoin(get_proxy('asdf', 'asdf')) + + if namecoin.test_connection(): + syncnet.dns = namecoin + else: + logger.warn('Failed to initialize connection to local namecoin instance.') + + app = QtApplication() view = SyncNetView(model=syncnet) view.show()