Merge branch 'master' of github.com:onenameio/opennamed

This commit is contained in:
Ryan Shea
2014-12-11 18:33:07 -05:00
13 changed files with 514 additions and 57 deletions

View File

@@ -10,8 +10,8 @@
import sys
import os
#hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
# Hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
parent_dir = os.path.abspath(current_dir + "/../")
sys.path.insert(0, parent_dir)

View File

@@ -10,8 +10,8 @@
import sys
import os
#hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
# Hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
parent_dir = os.path.abspath(current_dir + "/../")
sys.path.insert(0, parent_dir)

View File

@@ -1,32 +1,41 @@
"""
Opennamed
~~~~~
:copyright: (c) 2014 by Openname.org
:license: MIT, see LICENSE for more details.
Opennamed
~~~~~
:copyright: (c) 2014 by Openname.org
:license: MIT, see LICENSE for more details.
"""
import os
import os
DEBUG = True
DEFAULT_PORT = '8344'
LISTEN_IP = '0.0.0.0'
VERSION = 'v0.1-beta'
RPC_TIMEOUT = 5 # seconds
try:
OPENNAMED_SERVER = os.environ['OPENNAMED_SERVER']
OPENNAMED_PORT = os.environ['OPENNAMED_PORT']
OPENNAMED_SERVER = os.environ['OPENNAMED_SERVER']
OPENNAMED_PORT = os.environ['OPENNAMED_PORT']
except:
OPENNAMED_SERVER = 'localhost'
OPENNAMED_PORT = DEFAULT_PORT
OPENNAMED_SERVER = 'localhost'
OPENNAMED_PORT = DEFAULT_PORT
try:
BITCOIND_SERVER = os.environ['BITCOIND_SERVER']
BITCOIND_PORT = os.environ['BITCOIND_PORT']
BITCOIND_USER = os.environ['BITCOIND_USER']
BITCOIND_PASSWD = os.environ['BITCOIND_PASSWD']
BITCOIND_SERVER = os.environ['BITCOIND_SERVER']
BITCOIND_PORT = os.environ['BITCOIND_PORT']
BITCOIND_USER = os.environ['BITCOIND_USER']
BITCOIND_PASSWD = os.environ['BITCOIND_PASSWD']
except:
BITCOIND_SERVER = 'btcd.onename.com'
BITCOIND_PORT = '8332'
BITCOIND_USER = 'openname'
BITCOIND_PASSWD = 'opennamesystem'
BITCOIND_SERVER = 'btcd.onename.com'
BITCOIND_PORT = '8332'
BITCOIND_USER = 'openname'
BITCOIND_PASSWD = 'opennamesystem'
# ---------------------------
# config for DHT
DHT_PORT = 8468
DEFAULT_DHT_SERVERS = [('54.173.110.67', DHT_PORT)]
YEAR = 29030400 # seconds
STORAGE_TTL = 3 * YEAR

1
dht/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.pickle

8
dht/README.md Normal file
View File

@@ -0,0 +1,8 @@
openname-dht
============
Kademlia DHT for Openname data
To run a stand-alone server:
> twistd -noy dht/server.tac

0
dht/__init__.py Normal file
View File

42
dht/image/Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
############################################################
# Dockerfile to build kademlia container image
# Based on Ubuntu
############################################################
# Set the base image to Ubuntu
FROM ubuntu
# File Author / Maintainer
MAINTAINER Muneeb Ali (@muneeb)
# Update the repository sources list
RUN apt-get dist-upgrade
################## BEGIN INSTALLATION ######################
# Add the package verification key
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
# Update the repository sources list once more
RUN apt-get update
RUN apt-get -y upgrade
# Install packages
RUN apt-get install -y git
RUN apt-get install -y python-pip
RUN apt-get install -y python-dev
# Create the default directory
RUN mkdir -p /srv/openname-dht
# Clone the app from github and install packages
RUN git clone https://github.com/openname/opennamed.git /srv/opennamed
RUN pip install kademlia
##################### INSTALLATION END #####################
# Expose the default port
EXPOSE 8468
# Set default entry point
ENTRYPOINT /usr/local/bin/twistd -noy /srv/opennamed/dht/server.tac

3
dht/image/README.md Normal file
View File

@@ -0,0 +1,3 @@
For quick deployment:
> sudo docker run -d -p 8468:8468/udp openname/dht

32
dht/server.tac Normal file
View File

@@ -0,0 +1,32 @@
from twisted.application import service, internet
from twisted.python.log import ILogObserver
from twisted.internet import reactor, task
import sys
import os
sys.path.append(os.path.dirname(__file__))
# Hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
parent_dir = os.path.abspath(current_dir + "/../")
sys.path.insert(0, parent_dir)
from kademlia.network import Server
from kademlia import log
from storage import OpennameStorage
from config import DHT_PORT
application = service.Application("kademlia")
application.setComponent(ILogObserver, log.FileLogObserver(sys.stdout, log.INFO).emit)
if os.path.isfile('cache.pickle'):
kserver = Server.loadState('cache.pickle')
else:
kserver = Server(storage=OpennameStorage())
kserver.bootstrap([("1.2.3.4", DHT_PORT)])
kserver.saveStateRegularly('cache.pickle', 10)
server = internet.UDPServer(DHT_PORT, kserver.protocol)
server.setServiceParent(application)

120
dht/storage.py Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Opennamed
~~~~~
:copyright: (c) 2014 by Openname.org
:license: MIT, see LICENSE for more details.
"""
import time
from itertools import izip
from itertools import imap
from itertools import takewhile
import operator
from collections import OrderedDict
from zope.interface import implements
from zope.interface import Interface
from kademlia.storage import IStorage
from kademlia.log import Logger
import sys
import json
import coinkit
import os
# Hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
parent_dir = os.path.abspath(current_dir + "/../")
sys.path.insert(0, parent_dir)
from config import STORAGE_TTL
class OpennameStorage(object):
implements(IStorage)
""" OpennameStorage has following properties:
a) is content-addressable (all keys must be hash(value))
b) high TTL (effectively the keys don't expire)
c) stores only valid JSON values
"""
def __init__(self, ttl=STORAGE_TTL):
"""
By default, max age is three years.
"""
self.data = OrderedDict()
self.ttl = ttl
self.log = Logger(system=self)
def __setitem__(self, key, value):
try:
value = json.loads(value)
except:
self.log.info("value not JSON, not storing")
return
hash = coinkit.hex_hash160(json.dumps(value))
if key != hash:
self.log.info("hash(value) doesn't match, not storing")
return
if key in self.data:
del self.data[key]
self.data[key] = (time.time(), value)
self.cull()
def cull(self):
for k, v in self.iteritemsOlderThan(self.ttl):
self.data.popitem(first=True)
def get(self, key, default=None):
self.cull()
if key in self.data:
value = self[key]
hash = coinkit.hex_hash160(value)
if key != hash:
self.log.info("hash(value) doesn't match, ignoring value")
return default
return self[key]
return default
def __getitem__(self, key):
self.cull()
return self.data[key][1]
def __iter__(self):
self.cull()
return iter(self.data)
def __repr__(self):
self.cull()
return repr(self.data)
def iteritemsOlderThan(self, secondsOld):
minBirthday = time.time() - secondsOld
zipped = self._tripleIterable()
matches = takewhile(lambda r: minBirthday >= r[1], zipped)
return imap(operator.itemgetter(0, 2), matches)
def _tripleIterable(self):
ikeys = self.data.iterkeys()
ibirthday = imap(operator.itemgetter(0), self.data.itervalues())
ivalues = imap(operator.itemgetter(1), self.data.itervalues())
return izip(ikeys, ibirthday, ivalues)
def iteritems(self):
self.cull()
ikeys = self.data.iterkeys()
ivalues = imap(operator.itemgetter(1), self.data.itervalues())
return izip(ikeys, ivalues)

58
dht/test.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Opennamed
~~~~~
:copyright: (c) 2014 by Openname.org
:license: MIT, see LICENSE for more details.
"""
from twisted.internet import reactor
from twisted.python import log
from kademlia.network import Server
import sys
import coinkit
import json
log.startLogging(sys.stdout)
#key = 'u/muneeb'
#value = "temp"
value = '{"name": "Muneeb Ali"}'
key = coinkit.hex_hash160(value)
print key
print value
hold_display = None
from storage import OpennameStorage
def done(result):
print "Key result: ", result
print "Found: ", hold_display
reactor.stop()
def get_key(result, server):
global key, value
server.get(key).addCallback(done)
def set_key(found, server):
global key, value, hold_display
hold_display = found
server.set(key, value).addCallback(get_key, server)
server = Server(storage=OpennameStorage())
server.listen(8467)
server.bootstrap([("127.0.0.1", 8468)]).addCallback(set_key, server)
reactor.run()

View File

@@ -14,10 +14,10 @@ import json
import zerorpc
import config
c = zerorpc.Client(timeout=5)
c.connect('tcp://' + config.OPENNAMED_SERVER + ':' + config.OPENNAMED_PORT)
client = zerorpc.Client(timeout=config.RPC_TIMEOUT)
client.connect('tcp://' + config.OPENNAMED_SERVER + ':' + config.OPENNAMED_PORT)
import logging
import logging
log = logging.getLogger()
log.setLevel(logging.DEBUG if config.DEBUG else logging.INFO)
@@ -27,11 +27,13 @@ formatter = logging.Formatter('%(message)s')
console.setFormatter(formatter)
log.addHandler(console)
def pretty_dump(input):
""" pretty dump
"""
return json.dumps(input, sort_keys=False, indent=4, separators=(',', ': '))
def run_cli():
""" run cli
"""
@@ -46,17 +48,97 @@ def run_cli():
'--opennamed-port', type=int,
help="""the opennamed RPC port to connect to
(default: {})""".format(config.OPENNAMED_PORT))
subparsers = parser.add_subparsers(
dest='action', help='the action to be taken')
dest='action',
help='the action to be taken')
parser_cli = subparsers.add_parser(
'getinfo', help='get basic info from the opennamed server')
parser_cli = subparsers.add_parser(
'name_show', help='<name> display value of a registered name')
subparser = subparsers.add_parser(
'getinfo',
help='get basic info from the opennamed server')
#print default help message, if no argument is given
if len(sys.argv)==1:
# ------------------------------------
subparser = subparsers.add_parser(
'preorder',
help='<name> <privatekey> | preorder a name')
subparser.add_argument(
'name', type=str,
help='the name that you want to preorder')
subparser.add_argument(
'privatekey', type=str,
help='the privatekey of the Bitcoin address that will own the name')
# ------------------------------------
subparser = subparsers.add_parser(
'register',
help='<name> <salt> <privatekey> | register/claim a name')
subparser.add_argument(
'name', type=str,
help='the name that you want to register/claim')
subparser.add_argument(
'salt', type=str,
help='the salt')
subparser.add_argument(
'privatekey', type=str,
help='the privatekey of the Bitcoin address that will own the name')
# ------------------------------------
subparser = subparsers.add_parser(
'update',
help='<name> <data or datahash> <privatekey> | update data')
subparser.add_argument(
'name', type=str,
help='the name that you want to update')
subparser.add_argument(
'data', type=str,
help='data associated with name (value part of key-value) or datahash')
subparser.add_argument(
'privatekey', type=str,
help='the privatekey of the owner Bitcoin address')
# ------------------------------------
subparser = subparsers.add_parser(
'transfer',
help='<name> <address> <privatekey> | transfer a name')
subparser.add_argument(
'name', type=str,
help='the name that you want to register/claim')
subparser.add_argument(
'address', type=str,
help='the new owner Bitcoin address')
subparser.add_argument(
'privatekey', type=str,
help='the privatekey of the owner Bitcoin address')
# ------------------------------------
subparser = subparsers.add_parser(
'renew',
help='<name> <privatekey> | renew a name')
subparser.add_argument(
'name', type=str,
help='the name that you want to renew')
subparser.add_argument(
'privatekey', type=str,
help='the privatekey of the owner Bitcoin address')
# ------------------------------------
subparser = subparsers.add_parser(
'storedata',
help='<data> | data value to store in DHT')
subparser.add_argument(
'data', type=str,
help='the data to store in DHT')
# ------------------------------------
subparser = subparsers.add_parser(
'getdata',
help='<hash> | get the data from DHT for given hash')
subparser.add_argument(
'hash', type=str,
help='the hash of the data, used as lookup key for DHT')
# Print default help message, if no argument is given
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
@@ -64,13 +146,45 @@ def run_cli():
if args.action == 'getinfo':
try:
log.info(pretty_dump(c.getinfo()))
log.info(pretty_dump(client.getinfo()))
except Exception as e:
log.info("Couldn't connect to opennamed server")
exit(0)
elif args.action == 'name_show':
log.info('in name_show')
#name_show code here
elif args.action == 'preorder':
log.debug('Preordering %s', args.name)
log.info(pretty_dump(
client.preorder(args.name, args.privatekey)))
elif args.action == 'register':
log.debug('Registering %s', args.name)
log.info(pretty_dump(
client.register(args.name, args.salt, args.privatekey)))
elif args.action == 'update':
log.debug('Updating %s', args.name)
log.info(pretty_dump(
client.update(args.name, args.data, args.privatekey)))
elif args.action == 'transfer':
log.debug('Transfering %s', args.name)
log.info(pretty_dump(
client.transfer(args.name, args.address, args.privatekey)))
elif args.action == 'renew':
log.debug('Renewing %s', args.name)
log.info(pretty_dump(
client.renew(args.name, args.privatekey)))
elif args.action == 'storedata':
log.debug('Storing %s', args.data)
log.info(pretty_dump(
client.storedata(args.data)))
elif args.action == 'getdata':
log.debug('Get %s', args.hash)
log.info(pretty_dump(
client.getdata(args.hash)))
if __name__ == '__main__':
run_cli()

View File

@@ -31,6 +31,7 @@ config_options = 'https://' + config.BITCOIND_USER + ':' + \
bitcoind = AuthServiceProxy(config_options)
class OpennamedRPC(object):
""" opennamed rpc
"""
@@ -40,8 +41,65 @@ class OpennamedRPC(object):
reply['blocks'] = info['blocks']
return reply
def preorder(self, name, privatekey):
""" Preorder a name
"""
log.debug('preorder <%s, %s>' % (name, privatekey))
return
def register(self, name, salt, privatekey):
""" Register a name
"""
log.debug('register <%s, %s, %s>' % (name, salt, privatekey))
return
def update(self, name, data, privatekey):
""" Update a name
"""
log.debug('update <%s, %s, %s>' % (name, data, privatekey))
return
def transfer(self, name, address, privatekey):
""" Transfer a name
"""
log.debug('transfer <%s, %s, %s>' % (name, address, privatekey))
return
def renew(self, name, privatekey):
""" Renew a name
"""
log.debug('renew <%s, %s>' % (name, privatekey))
return
def storedata(self, data):
""" Store data in DHT
"""
log.debug('storedata <%s>' % data)
return
def getdata(self, hash):
""" Get data from DHT
"""
log.debug('getdata <%s>' % hash)
return
def run_server():
""" run the server
""" run the opennamed server
"""
try:
server = zerorpc.Server(OpennamedRPC())
@@ -51,6 +109,25 @@ def run_server():
log.debug(e)
log.info('Exiting opennamed server')
def stop_server():
""" Stop the opennamed server
"""
# Quick hack to kill a background daemon
import subprocess
import signal
import os
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
out, err = p.communicate()
for line in out.splitlines():
if 'opennamed start' in line:
log.info('Stopping opennamed server')
pid = int(line.split(None, 1)[0])
os.kill(pid, signal.SIGTERM)
def run_opennamed():
""" run opennamed
"""
@@ -61,25 +138,28 @@ def run_opennamed():
'--bitcoind-server',
help='the hostname or IP address of the bitcoind RPC server')
parser.add_argument(
'--bitcoind-port', type=int, help='the bitcoind RPC port to connect to')
'--bitcoind-port', type=int,
help='the bitcoind RPC port to connect to')
parser.add_argument(
'--bitcoind-user', help='the username for bitcoind RPC server')
'--bitcoind-user',
help='the username for bitcoind RPC server')
parser.add_argument(
'--bitcoind-passwd', help='the password for bitcoind RPC server')
'--bitcoind-passwd',
help='the password for bitcoind RPC server')
subparsers = parser.add_subparsers(
dest='action', help='the action to be taken')
parser_server = subparsers.add_parser(
'start', help='start the opennamed server')
'start',
help='start the opennamed server')
parser_server.add_argument(
'--foreground', action='store_true',
help='start the opennamed server in foreground')
parser_server = subparsers.add_parser(
'stop', help='stop the opennamed server')
'stop',
help='stop the opennamed server')
#print default help message, if no argument is given
if len(sys.argv)==1:
# Print default help message, if no argument is given
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
@@ -94,17 +174,7 @@ def run_opennamed():
with daemon.DaemonContext():
run_server()
elif args.action == 'stop':
log.info('Stopping opennamed server')
#quick hack to kill a background daemon
import subprocess, signal
import os
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
out, err = p.communicate()
for line in out.splitlines():
if 'opennamed' in line:
pid = int(line.split(None, 1)[0])
os.kill(pid, signal.SIGTERM)
stop_server()
if __name__ == '__main__':
run_opennamed()