How to find nodes with exact info_hash in Bittorrent DHT? - p2p

In the documentation of DHT protocol for bittorrent,it is given that get_peers method is used for finding nodes with given info_hash.It says that if response contains "values" key,the queried node has returned the information about nodes containing exact info_hash.If node returns "nodes" key,it has returned K nodes closest to the result.Should we recursively call get_peers on returned nodes(closest) in order to reach till exact nodes(with same info_hash)?

Should we recursively call get_peers on returned nodes(closest) in order to reach till exact nodes(with same info_hash)?
Yes and no. You could use a recursive function if you are the LISP kind. That said, a while loop will do the job. Here is some python code that implement the FIND_VALUE algorithm with some comments:
async def _get(self, key):
"""Fetch the value associated with KEY from the network"""
uid = key
queried = set()
while True:
# retrieve the k nearest peers and remove already queried peers
peers = nearest(k, self.peers)
# no more peer to query, the key is not found in the dht
if not peers:
raise KeyError(uid)
# query selected peers
responses = dict()
for address in peers:
response = self._protocol.rpc(address, "value", uid)
responses[address] = response
# check responses: look for the value or add peers more near KEY
for (address, response) in responses.items():
queried.add(address)
if isinstance(response, Exception):
continue
elif response[0] == b"VALUE":
# value is found, return it
value = response[1]
if hash(value) == unpack(uid):
# at last!
return value
else:
log.warning(
"[%r] bad value returned from %r", self._uid, address
)
await self.blacklist(address)
continue
elif response[0] == b"PEERS":
# value not found but we got peers that are more near KEY
await self._welcome_peers(response[1])
This code is based on qadom's peer.py

Related

AI50 Pset0 Degrees: What seems to be the problem in my code's shortest_path function?

This function is supposed to return a list containing tuples of (movie_id, person_id), in order to figure out the number of degrees of separation by movies between two different actors (whose person_id are denoted by source and target).
But it takes very long to compute the output, even with small datasets, and sometimes the program just crashes, even if the degree of separation is only 2 or 3. How can I make this algorithm more efficient?
See here for more background: https://cs50.harvard.edu/ai/2020/projects/0/degrees/
def shortest_path(source, target):
# create node for the start. initialise action and parent to None, and state to start id parameter
source_node = Node (source, None, None)
# create empty set of tuples called explored
explored = set()
frontier = QueueFrontier()
# add start node to frontier list
frontier.add (source_node)
# execute search for target id (while True loop):
while True:
# remove a node from frontier (name it removed_node)
removed_node = frontier.remove()
# check if removed_node.state is target id
if removed_node.state != target:
neighbours = neighbors_for_person(removed_node.state)
for neighbour in neighbours:
neighbour_node = Node (neighbour[1], removed_node.state, neighbour[0])
if neighbour != target:
frontier.add(neighbour_node)
else:
frontier.add_infront(neighbour_node)
explored.add (removed_node)
# if removed_node.state is not target id:
# get set of neighbours from get_neighbours function with its input as removed_node
# generate node for each neighbour in the set, and add all the neighbours to frontier
# add removed_node to explored set
else:
path = []
path.append ((removed_node.action, removed_node.state))
while removed_node.parent != None:
find_node = removed_node.parent
for a_node in explored:
if find_node == a_node.state:
removed_node = a_node
path.append ((removed_node.action, removed_node.state))
break
path.reverse()
path.pop(0)
print (path)
return path

Changing the values in a dict, changes the values of the original returned dict

def data_query(Chan, Mode, Format, Sampling, Wave_Data):
if Mode.get_state() == 'NORM':
if Chan.get_state() == 'CHAN1':
wave_dict = Wave_Data.get_wave_data(1)
if Format.get_state() == 'ASCII':
return wave_dict
elif Format.get_state() == 'BYTE':
for i in range(0, len(wave_dict)):
wave_dict[i] = bin(int(wave_dict[i]))
return wave_dict
So in the code above, the parameter 'Wave_Data' is an instance of another class which holds the value of a dict 'self.wave1' which is returned by the function 'get_wave_data'.
def get_wave_data(self, channel=1):
if channel == 1:
return self.wave1
elif channel == 2:
pass
My problem is that in the code above when I make changes to the values in the local dict - 'wave_dict' (i.e. convert the values to binary), it also the changes the values in self.wave1. If I understand this correctly, its acting as a pointer to the self.wave1 object (which I am streaming using udp sockets via another thread) rather than a normal local variable.
Btw, the first code block is a function in the main thread and the second code block is a function in a class that is running as a daemon thread, the instance of which is also passed in the 'data_query' function.
Any help would be appreciated. Sorry if I've used wrong terminology anywhere.
I fixed this by creating an array and appending the hex(dict values) to this array, then returning the array instead of the dict.
Then I handle this on the receiving end by try-except to accept either a dict or a list:
try:
Wdata = list(Wdata_dict.values())
except:
Wdata = Wdata_dict

Signing and Verifying of Signature using Pycryptodome always fails

Hi I'm using the Pycryptodome package to try and verify signatures of transactions in a Blockchain. My issue is that when trying to add a new transaction, I first create a signature to be passed into a verify transaction method but for some reason it always fails even though the logic seems to be right when I compare it to the documentation. If anyone could point me where I'm going wrong it would be much appreciated. I have 3 methods that handle all of this and i'm not sure where the issue is
The generate keys method
def generate_keys(self):
# generate private key pair
private_key = RSA.generate(1024, Crypto.Random.new().read)
# public key comes as part of private key generation
public_key = private_key.publickey()
# return keys as hexidecimal representation of binary data
return (binascii.hexlify(public_key.exportKey(format='DER')).decode('ascii'), binascii.hexlify(private_key.exportKey(format='DER')).decode('ascii'))
The sign transaction method
def sign_transaction(self, sender, recipient, amount, key):
# convert transaction data to SHA256 string
hash_signer = SHA256.new(
(str(sender) + str(recipient) + str(amount)).encode('utf-8'))
# sign transaction
signature = pkcs1_15.new(RSA.importKey(
binascii.unhexlify(key))).sign(hash_signer)
# return hexidecimal representation of signature
return binascii.hexlify(signature).decode('ascii')
and the verify transaction method
#staticmethod
def verify_transaction(transaction):
# convert public key back to binary representation
public_key = RSA.importKey(binascii.unhexlify(
transaction.sender))
try:
# create signature from transaction data
hash_signer = SHA256.new(
(str(transaction.sender) + str(transaction.recipient) + str(transaction.amount)).encode('utf-8'))
pkcs1_15.new(public_key).verify(
hash_signer, binascii.unhexlify(transaction.signature))
return True
except ValueError:
return False
Once i've generated my key pair and attempt to use them to sign and verify transactions it always fails. I know this because it always returns false from the verify method leading me to believe a value error is always raised. Thanks in advance hopefully someone can help me out.

Calling instance method of class from another another containing only self

class Cust(object):
def __init__(self,cust_id,cust_day,cust_amt):
self.id=cust_id
self.total=0
self.days=[cust_day]
self.amounts=[cust_amt]
def add_Purchases(self,day,amount):
self.amounts.append(amount)
self.days.append(day)
print("add Purchase Executed for ",self.id,self.amounts)
print(self.get_Total_Sales())
def get_Total_Sales(self):
total = sum(self.amounts)
return total
class DB(object):
def __init__(self,filedir):
self.dir=filedir
self.DB_cust={}
self.createDatabase(filedir)
def importFile(self,file):
file.readline()
for line in file:
val=line.split(",")
customerID=data[0]
if customerID in self.DB_cust:
self.DB_cust[customerID].add_Purchases(val[2],float(val[3]))
else:
self.DB_cust[customerID]=Cust(val[0],val[1],val[2],float(val[3]))
I am trying to get the ID for maximum value of total. I call get_total_sales() from the instance method in DB class get_Max_ID()
def get_Max_ID(self):
print("Max Sale ID here")
Cust.get_Total_Sales()
This obviously results in
TypeError: get_Total_Sales() missing 1 required positional argument: 'self'
If I pass self through get_Max_ID. It would pass the self of DB and not Cust. I am not sure how to pass the self of Cust whilst calling it from DB class.
Clarification - The get_Total_Sales() gets the total for each customer but I am trying to get maximum value of total out of all the customer instances. (I have not written that part of code which draws maximum total)
Going by what OP says they want to do in the question comments, they probably want something like:
max([cust.get_Total_Sales() for id, cust in self.DB_cust.items()])
It will iterate over the dictionary of customers, and for each pair key/value pair (id and cust) will call and return that Cust's get_Total_Sales().
Then max will take all the individual results from the generated list of values and return only the maximum value.
As asked in the comment, although I see no reason not to use a comprehension:
results = []
for id, cust in self.DB_cust.items():
results.append(cust.get_Total_Sales())
return max(results)

DHT InfoHash Lookup sequence. PeerID vs InfoHash

I know there is a previous question about this somewhere on SO, but I cannot find it again. The relationship between NodeId and InfoHash.
Is the following diagram roughly correct?
Background (no need to read)
I'm trying to implement my own DHT/bittorrent application in Java.
I know that there are already some excellent implementations that I will never better. But this is purely a hedonistic pursuit.
What was it Kennedy said? "we choose to do this not because it is easy.."
I have conquered the easy part, which is to write the low level socket handling and Remote Procedure Call syntax etc.
Now I am on to the hard part, I must behave responsibly and service incoming requests on the DHT. (maintain KBuckets etc.)
Yes that diagram is correct. Here some python code that implements FIND_VALUE algorithm you describe:
async def _get(self, key):
"""Fetch the value associated with KEY from the network"""
uid = pack(key)
queried = set()
while True:
# retrieve the k nearest peers and remove already queried peers
peers = await self.peers((None, None), uid)
peers = [address for address in peers if address not in queried]
# no more peer to query, the key is not found in the dht
if not peers:
raise KeyError(unpack(uid))
# query selected peers
queries = dict()
for address in peers:
query = self._protocol.rpc(address, "value", uid)
queries[address] = query
responses = await gather(queries, return_exceptions=True)
for (address, response) in responses.items():
queried.add(address)
if isinstance(response, Exception):
continue
elif response[0] == b"VALUE":
value = response[1]
if hash(value) == unpack(uid):
# store it
#h.transactional
def add(tr, key, value):
tr.add("QADOM:MAPPING", key, "value", value)
await self._run(add, self._hoply, key, value)
# at last!
return value
else:
log.warning(
"[%r] bad value returned from %r", self._uid, address
)
await self.blacklist(address)
continue
elif response[0] == b"PEERS":
await self._welcome_peers(response[1])
else:
await self.blacklist(address)
log.warning(
"[%r] unknown response %r from %r",
self._uid,
response[0],
address,
This is extract from qadom project peer.py.

Resources