Chord Socket ~~~~~~~~~~~~ .. warning:: This module is partly unstable, and should be regarded as "pre-alpha". If you're considering using this, please wait until this warning is removed. Expected beta status is by end of November 2016. Basic Usage ----------- The chord schema is used as a distributed hash table. Its primary purpose is to ensure data syncronization between peers. While it's not entirely :py:class:`dict`-like, it has a substantial subset of this API. To connect to a chord network, use the :py:class:`~py2p.chord.chord_socket` object. this is instantiated as follows: .. code-block:: python >>> from py2p import chord >>> sock = chord.chord_socket('0.0.0.0', 4444, k=2) >>> sock.join() # This indicates you want to store data There are two arguments to explain here. The keyword ``k`` specifies the maximum number of seeding nodes on the network. In other words, for a given ``k``, you can have up to ``2**k`` nodes storing data, and as few as ``k``. ``k`` is also the maximum number of requests you can expect to issue for a given piece of data. So lookup time will be ``O(k)``. And like in :py:class:`~py2p.mesh.mesh_socket`, using ``'0.0.0.0'`` will automatically grab your LAN address. Using an outbound internet connection requires a little more work. First, ensure that you have a port forward set up (NAT busting is not in the scope of this project). Then specify your outward address as follows: .. code-block:: python >>> from py2p import chord >>> sock = chord.chord_socket('0.0.0.0', 4444, k=2 out_addr=('35.24.77.21', 44565)) >>> sock.join() # This indicates you want to store data In addition, SSL encryption can be enabled if `cryptography `_ is installed. This works by specifying a custom :py:class:`~py2p.base.protocol` object, like so: .. code-block:: python >>> from py2p import chord, base >>> sock = chord.chord_socket('0.0.0.0', 4444, k=2, prot=base.protocol('chord', 'SSL')) Eventually that will be the default, but while things are being tested it will default to plaintext. If `cryptography `_ is not installed, this will generate an :py:exc:`ImportError` Specifying a different protocol object will ensure that the node *only* can connect to people who share its object structure. So if someone has ``'chord2'`` instead of ``'chord'``, it will fail to connect. You can see the current default by looking at :py:data:`py2p.chord.default_protocol`. This same check is performed for the ``k`` value provided. The full check which happens is essentially: .. code-block:: python assert your_protocol.id + to_base_58(your_k) == peer_protocol.id + to_base_58(peer_k) Unfortunately, this failure is currently silent. Because this is asynchronous in nature, raising an :py:exc:`Exception` is not possible. Because of this, it's good to perform the following check after connecting: .. code-block:: python >>> from py2p import chord >>> import time >>> sock = chord.chord_socket('0.0.0.0', 4444, k=2) >>> sock.connect('192.168.1.14', 4567) >>> time.sleep(1) >>> assert sock.routing_table or sock.awaiting_ids Using the constructed table is very easy. Several :py:class:`dict`-like methods have been implemented. :py:meth:`~py2p.chord.chord_socket.get` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A value can be retrieved by using the :py:meth:`~py2p.chord.chord_socket.get` method, or alternately with :py:meth:`~py2p.chord.chord_socket.__getitem__`. .. code-block:: python >>> foo = sock.get('test key', None) # Returns None if there is nothing at that key >>> bar = sock[b'test key'] # Raises KeyError if there is nothing at that key It is important to note that keys are all translated to :py:class:`bytes` before being used, so it is required that you use a :py:class:`bytes`-like object. It is also safer to manually convert :py:class:`unicode` keys to :py:class:`bytes`, as there are sometimes inconsistencies betwen the Javascript and Python implementation. If you notice one of these, please file a bug report. :py:meth:`~py2p.chord.chord_socket.set` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A value can be stored by using the :py:meth:`~py2p.chord.chord_socket.set` method, or alternately with :py:meth:`~py2p.chord.chord_socket.__setitem__`. .. code-block:: python >>> sock.set('test key', 'value') >>> sock[b'test key'] = b'value' Like above, keys and values are all translated to :py:class:`bytes` before being used, so it is required that you use a :py:class:`bytes`-like object. :py:meth:`~py2p.chord.chord_socket.update` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The update method is simply a wrapper which updates based on a fed :py:class:`dict`. Essentially it runs the following: .. code-block:: python >>> for key in update_dict: ... sock[key] = update_dict[key] Advanced Usage -------------- Refer to :doc:`the mesh socket tutorial <./mesh>`