mirror of https://github.com/nucypher/nucypher.git
83 lines
3.0 KiB
Python
83 lines
3.0 KiB
Python
import asyncio
|
|
import random
|
|
|
|
from kademlia.crawling import NodeSpiderCrawl
|
|
from kademlia.network import Server
|
|
from kademlia.utils import digest
|
|
from nkms.network.capabilities import SeedOnly, ServerCapability
|
|
from nkms.network.node import NuCypherNode
|
|
from nkms.network.protocols import NuCypherSeedOnlyProtocol, NuCypherHashProtocol
|
|
from nkms.network.storage import SeedOnlyStorage
|
|
|
|
|
|
class NuCypherDHTServer(Server):
|
|
protocol_class = NuCypherHashProtocol
|
|
capabilities = ()
|
|
digests_set = 0
|
|
|
|
def __init__(self, ksize=20, alpha=3, id=None, storage=None, *args, **kwargs):
|
|
super().__init__(ksize=20, alpha=3, id=None, storage=None, *args, **kwargs)
|
|
self.node = NuCypherNode(id or digest(
|
|
random.getrandbits(255))) # TODO: Assume that this can be attacked to get closer to desired kFrags.
|
|
|
|
def serialize_capabilities(self):
|
|
return [ServerCapability.stringify(capability) for capability in self.capabilities]
|
|
|
|
async def bootstrap_node(self, addr):
|
|
"""
|
|
Announce node including capabilities
|
|
"""
|
|
result = await self.protocol.ping(addr, self.node.id, self.serialize_capabilities())
|
|
return NuCypherNode(result[1], addr[0], addr[1]) if result[0] else None
|
|
|
|
async def set_digest(self, dkey, value):
|
|
"""
|
|
Set the given SHA1 digest key (bytes) to the given value in the network.
|
|
|
|
Returns True if a digest was in fact set.
|
|
"""
|
|
node = self.node_class(dkey)
|
|
|
|
nearest = self.protocol.router.findNeighbors(node)
|
|
if len(nearest) == 0:
|
|
self.log.warning("There are no known neighbors to set key %s" % dkey.hex())
|
|
return False
|
|
|
|
spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha)
|
|
nodes = await spider.find()
|
|
|
|
self.log.info("setting '%s' on %s" % (dkey.hex(), list(map(str, nodes))))
|
|
|
|
# if this node is close too, then store here as well
|
|
if self.node.distanceTo(node) < max([n.distanceTo(node) for n in nodes]):
|
|
self.storage[dkey] = value
|
|
ds = []
|
|
for n in nodes:
|
|
_disposition, value_was_set = await self.protocol.callStore(n, dkey, value)
|
|
if value_was_set:
|
|
self.digests_set += 1
|
|
ds.append(value_was_set)
|
|
# return true only if at least one store call succeeded
|
|
return any(ds)
|
|
|
|
def get_now(self, key):
|
|
loop = asyncio.get_event_loop()
|
|
return loop.run_until_complete(self.get(key))
|
|
|
|
async def set(self, key, value):
|
|
"""
|
|
Set the given string key to the given value in the network.
|
|
"""
|
|
self.log.debug("setting '%s' = '%s' on network" % (key, value))
|
|
key = digest(key)
|
|
return await self.set_digest(key, value)
|
|
|
|
|
|
class NuCypherSeedOnlyDHTServer(NuCypherDHTServer):
|
|
protocol_class = NuCypherSeedOnlyProtocol
|
|
capabilities = (SeedOnly(),)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.storage = SeedOnlyStorage()
|