2015-08-20 17:27:15 +02:00
#!/usr/bin/env python
#
# This is a basic single-node example of how to use the Entangled DHT. It creates a Node and (optionally) joins an existing DHT.
# It then does a Kademlia store and find, and then it deletes the stored value (non-Kademlia method).
#
# No tuple space functionality is demonstrated by this script.
#
# To test it properly, start a multi-node Kademlia DHT with the "create_network.py"
# script and point this node to that, e.g.:
# $python create_network.py 10 127.0.0.1
#
# $python basic_example.py 5000 127.0.0.1 4000
#
# This library is free software, distributed under the terms of
# the GNU Lesser General Public License Version 3, or any later version.
# See the COPYING file included in this archive
#
# Thanks to Paul Cannon for IP-address resolution functions (taken from aspn.activestate.com)
2016-08-08 22:42:35 +02:00
import sys , hashlib , random
2015-08-20 17:27:15 +02:00
import twisted . internet . reactor
from lbrynet . dht . node import Node
#from entangled.kademlia.datastore import SQLiteDataStore
# The Entangled DHT node; instantiated in the main() method
node = None
# The key to use for this example when storing/retrieving data
hash = hashlib . sha384 ( )
hash . update ( " key " )
KEY = hash . digest ( )
# The value to store
VALUE = random . randint ( 10000 , 20000 )
import binascii
lbryid = KEY
def storeValue ( key , value ) :
""" Stores the specified value in the DHT using the specified key """
global node
print ' \n Storing value; Key: %s , Value: %s ' % ( key , value )
# Store the value in the DHT. This method returns a Twisted Deferred result, which we then add callbacks to
deferredResult = node . announceHaveHash ( key , value )
# Add our callback; this method is called when the operation completes...
deferredResult . addCallback ( storeValueCallback )
# ...and for error handling, add an "error callback" as well.
# For this example script, I use a generic error handler; usually you would need something more specific
deferredResult . addErrback ( genericErrorCallback )
def storeValueCallback ( * args , * * kwargs ) :
""" Callback function that is invoked when the storeValue() operation succeeds """
print ' Value has been stored in the DHT '
# Now that the value has been stored, schedule that the value is read again after 2.5 seconds
print ' Scheduling retrieval in 2.5 seconds... '
twisted . internet . reactor . callLater ( 2.5 , getValue )
def genericErrorCallback ( failure ) :
""" Callback function that is invoked if an error occurs during any of the DHT operations """
print ' An error has occurred: ' , failure . getErrorMessage ( )
twisted . internet . reactor . callLater ( 0 , stop )
def getValue ( ) :
""" Retrieves the value of the specified key (KEY) from the DHT """
global node , KEY
# Get the value for the specified key (immediately returns a Twisted deferred result)
print ' \n Retrieving value from DHT for key " %s " ... ' % binascii . unhexlify ( " f7d9dc4de674eaa2c5a022eb95bc0d33ec2e75c6 " )
deferredResult = node . iterativeFindValue ( binascii . unhexlify ( " f7d9dc4de674eaa2c5a022eb95bc0d33ec2e75c6 " ) )
#deferredResult = node.iterativeFindValue(KEY)
# Add a callback to this result; this will be called as soon as the operation has completed
deferredResult . addCallback ( getValueCallback )
# As before, add the generic error callback
deferredResult . addErrback ( genericErrorCallback )
def getValueCallback ( result ) :
""" Callback function that is invoked when the getValue() operation succeeds """
# Check if the key was found (result is a dict of format {key: value}) or not (in which case a list of "closest" Kademlia contacts would be returned instead")
print " Got the value "
print result
#if type(result) == dict:
# for v in result[binascii.unhexlify("5292fa9c426621f02419f5050900392bdff5036c")]:
# print "v:", v
# print "v[6:", v[6:]
# print "lbryid:",lbryid
# print "lbryid == v[6:]:", lbryid == v[6:]
# print 'Value successfully retrieved: %s' % result[KEY]
#else:
# print 'Value not found'
# Either way, schedule a "delete" operation for the key
#print 'Scheduling removal in 2.5 seconds...'
#twisted.internet.reactor.callLater(2.5, deleteValue)
print ' Scheduling shutdown in 2.5 seconds... '
twisted . internet . reactor . callLater ( 2.5 , stop )
def stop ( ) :
""" Stops the Twisted reactor, and thus the script """
print ' \n Stopping Kademlia node and terminating script... '
twisted . internet . reactor . stop ( )
if __name__ == ' __main__ ' :
2016-08-08 22:42:35 +02:00
import sys
2015-08-20 17:27:15 +02:00
if len ( sys . argv ) < 2 :
print ' Usage: \n %s UDP_PORT [KNOWN_NODE_IP KNOWN_NODE_PORT] ' % sys . argv [ 0 ]
print ' or: \n %s UDP_PORT [FILE_WITH_KNOWN_NODES] ' % sys . argv [ 0 ]
print ' \n If a file is specified, it should containg one IP address and UDP port \n per line, seperated by a space. '
sys . exit ( 1 )
try :
int ( sys . argv [ 1 ] )
except ValueError :
print ' \n UDP_PORT must be an integer value. \n '
print ' Usage: \n %s UDP_PORT [KNOWN_NODE_IP KNOWN_NODE_PORT] ' % sys . argv [ 0 ]
print ' or: \n %s UDP_PORT [FILE_WITH_KNOWN_NODES] ' % sys . argv [ 0 ]
print ' \n If a file is specified, it should contain one IP address and UDP port \n per line, seperated by a space. '
sys . exit ( 1 )
if len ( sys . argv ) == 4 :
knownNodes = [ ( sys . argv [ 2 ] , int ( sys . argv [ 3 ] ) ) ]
elif len ( sys . argv ) == 3 :
knownNodes = [ ]
f = open ( sys . argv [ 2 ] , ' r ' )
lines = f . readlines ( )
f . close ( )
for line in lines :
ipAddress , udpPort = line . split ( )
knownNodes . append ( ( ipAddress , int ( udpPort ) ) )
else :
knownNodes = None
print ' \n NOTE: You have not specified any remote DHT node(s) to connect to '
print ' It will thus not be aware of any existing DHT, but will still function as a self-contained DHT (until another node contacts it). '
print ' Run this script without any arguments for info. \n '
# Set up SQLite-based data store (you could use an in-memory store instead, for example)
#if os.path.isfile('/tmp/dbFile%s.db' % sys.argv[1]):
# os.remove('/tmp/dbFile%s.db' % sys.argv[1])
#dataStore = SQLiteDataStore(dbFile = '/tmp/dbFile%s.db' % sys.argv[1])
# Create the Entangled node. It extends the functionality of a basic Kademlia node (but is fully backwards-compatible with a Kademlia-only network)
# If you wish to have a pure Kademlia network, use the entangled.kademlia.node.Node class instead
print ' Creating Node... '
#node = EntangledNode( udpPort=int(sys.argv[1]), dataStore=dataStore )
2016-11-04 21:09:40 +01:00
node = Node ( udpPort = int ( sys . argv [ 1 ] ) , lbryid = lbryid )
2015-08-20 17:27:15 +02:00
2016-11-04 21:09:40 +01:00
# Schedule the node to join the Kademlia/Entangled DHT
2015-08-20 17:27:15 +02:00
node . joinNetwork ( knownNodes )
# Schedule the "storeValue() call to be invoked after 2.5 seconds, using KEY and VALUE as arguments
#twisted.internet.reactor.callLater(2.5, storeValue, KEY, VALUE)
twisted . internet . reactor . callLater ( 2.5 , getValue )
# Start the Twisted reactor - this fires up all networking, and allows the scheduled join operation to take place
print ' Twisted reactor started (script will commence in 2.5 seconds) '
twisted . internet . reactor . run ( )