Commit d95a6658 authored by THAKARE AKSHAY HARIBHAU's avatar THAKARE AKSHAY HARIBHAU

Merge remote-tracking branch 'origin/netSim'

This merge contains:
sortition implementation
verify sortion implementation
VRF implementation
verify VRF and
crypto library

Unsolved question : Does commitee formed according to stakes of users , this is not tested fully but implementation is according to paper
parents d10cebea d5f53728
from SystemEntity import SystemEntity
from Node import Node
import logging
class Link(SystemEntity):
def __init__(self,link,network,linkDelay):
'''
:param link: (1,2)
:param network:
:param linkDelay: delay of a link
'''
SystemEntity.__init__(self,network)
self.logger = logging.getLogger(__name__ + "-" + str(link))
self.upNode = link[0]
self.downNode = link[1]
self.link = link
self.messageQueue = []
self.linkDelay = linkDelay
'''
contains string command and its corresponding method to be executed
useful to execute task from todolist
'''
self.commands = {"delieverMessage": self.delieverMessage,
}
pass
def delieverMessage(self,time,params):
'''
:param time:
:param params: [ whomtodeliever , payload ]
:return:
'''
node = self.network.nodes.get(params[0])
node.enqueMessage((time, params[1]))
pass
def enqueMessage(self,message):
'''
:param message: (timestamp , payload) --- timestamp is the tick value at which node has inserted this message in queue
payload #1 : ('NOde1', 'Node2', 'Content of message')
payload #2 :
:return:
'''
self.messageQueue.append(message)
# print("message enqued")
def dequeMessage(self):
return self.messageQueue.pop(0)
def scheduleMessageDelievery(self,time,payload):
task = ("delieverMessage",payload[1],payload) #(command,whomtodeliever,what to deliever)
scheduledTime = str(int(time) + self.linkDelay)
self.todoList[scheduledTime].append(task)
def simulate(self,time):
# self.logger.info("simulating .. ")
'''
Process message first then process task in todo-list
because messages in the link are placed in last tick
and the processing of those messages may create some tasks for current tick
all messages will be proceessed i.e., at the end of each tick simulation links will not have
any unprocessed message the receive buffer or message-queue
'''
#todo process messages in queue
# generate tasks for todolist based on messges
for message in self.messageQueue:
self.scheduleMessageDelievery(message[0],message[1])
# TODO : perform task from todolist which are relevant for current tick
try:
tasks = self.todoList.pop(time)
for task in tasks:
command = self.commands.get(task[0])
command(time,task[1:])
except KeyError as ke:
# not task pending at this time
pass
if __name__ == '__main__':
x = {(1,2):"1*2",
(2,1):"2*1",
(3,2):"3*2"}
print(x.get((2,1,3)))
1 2
2 3
3 4
4 1
from SystemEntity import SystemEntity
from Link import Link
import csv
from Node import Node
from OverlayNode import OverlayNode
import logging
logger = logging.getLogger(__name__)
class Network(SystemEntity):
class Network():
def __init__(self):
#TODO need better datastructure store these
self.nodes = []
self.links = []
self.nodes = {}#{1 : Node(1),2:Node(2)}
self.links = {} #{(1,2):Link(1-2),(2,1):Link(2,1),(3,2):Link(3*2)} linkID = tuple(1,2)
self.networkClockTime = 0
def setupNetwork(self):
# TODO add nodes
'''
Assumption : nodes are unique
links are unique and bidirectional
no errorneous nodes present in the file
no erroneous link present in the file
self loops are not there
Adds links and nodes to the network .It is a one time activity.
:return:
'''
with open('Nodes.dat', 'r') as f:
reader = csv.reader(f)
for row in reader:
node = row[0] # tuple of a link
self.nodes.update({node:OverlayNode(node,self)})
# print(self.nodes.get('1')) #keys are strings
with open('Links.dat', 'r') as f:
reader = csv.reader(f)
for row in reader:
link = tuple(row[0].split()) # tuple of a link
linkDelay = 1
self.links.update({link:Link(link,self,linkDelay)})
# adding adjacent nodes list to each node
self.nodes.get(link[0]).adjacentNodes.append(link[1])
self.nodes.get(link[1]).adjacentNodes.append(link[0])
# print(self.links.get(('1', '2'))) #keys are strings
# TODO add links
pass
def simulate(self,time):
logger.info("simulating .. ")
# logger.info("simulating .. ")
self.networkClockTime = time
# Note links should be simulated before nodes
# in order to receive the messge which is supposed to be received in this tick
for link in self.links:
for link in self.links.values():
link.simulate(time)
for node in self.nodes:
for node in self.nodes.values():
node.simulate(time)
pass
if __name__ == '__main__':
network = Network()
network.setupNetwork()
pass
import SystemEntity
from SystemEntity import SystemEntity
import logging
logger = logging.getLogger(__name__)
class Node(SystemEntity.SystemEntity):
def __init__(self,id):
class Node(SystemEntity):
def __init__(self,id,network):
SystemEntity.__init__(self,network)
self.logger = logging.getLogger(__name__ + "-" + str(id))
self.id = id
self.adjacentNodes = []
self.messageQueue = []
'''make sure methods included here are not overriddenn'''
self.commands={"sendMessage":self.sendMessage,
}
def enqueMessage(self,message):
'''
:param message: (timestamp , payload) --- timestamp is the tick value at which node has inserted this message in queue
payload #1 : ('NOde1', 'Node2', 'Content of message')
payload #2 :
:return:
'''
self.messageQueue.append(message)
# print("message enqued")
def dequeMessage(self):
return self.messageQueue.pop(0)
def sendMessage(self,time,params):
'''
:param params: ('1','2','hello2')
:return:
'''
link = self.network.links.get((params[0],params[1]))
link.enqueMessage((time,params))
self.logger.info("Sending : " + str((time,params)))
pass
def broadcast(self,time,gossipPayload):
for node in self.adjacentNodes:
link = self.network.links.get((self.id, node.id))
link.enqueMessage((time, (self.id, node.id,gossipPayload)))
self.logger.info("Sending : " + str((time, (self.id, node.id,gossipPayload))))
def processMessage(self,time,payload):
print("fianlly reached : " + str(payload))
'''remove any extra headers like source and destination here '''
payload.pop(0) # removed source header
payload.pop(0) #removed destination header
def simulate(self,time):
logger.info("simulating .. ")
logger.info(" hello "+self.id)
pass
\ No newline at end of file
# self.logger.info("simulating .. ")
'''
Process message first then process task in todo list
Here it is just intuition , i don`t what should be processing sequence
all messages will be proceessed i.e., at the end of each tick simulation links will not have
any unprocessed message the receive buffer or message-queue
'''
# todo process messages in queue
# generate tasks for todolist based on messges
for message in list(self.messageQueue): #this creates adupliacte list and iterate over it while making changes to original queue
self.processMessage(message[0],list(message[1])) # message : (time,(payload))
self.logger.info("received : "+str(message))
self.dequeMessage()
# TODO : perform task from todolist which are relevant for current tick
try:
tasks = self.todoList.pop(time)
for task in tasks:
command = self.commands.get(task[0])
command(time,task[1:])
except KeyError as ke:
# not task pending at this time
pass
pass
if __name__ == '__main__':
# node = Node('1')
# cmd = node.commands.get("sendMessage")
# cmd("1 ","2 "," : Hello world")
x = [1,2,3,4,5,6]
for i in list(x):
y= x.pop(0)
print(y)
print(x)
from Node import Node
import logging
class OverlayNode(Node):
def __init__(self,id,network):
Node.__init__(self,id,network)
self.commands.update({})
def processMessage(self,time,payload):
super(OverlayNode, self).processMessage(time,payload)
print("from overlay node : "+str(payload))
class A(object):
def __init__(self,id):
self.id = id
self.commands = {"sendMessage": self.sendMessage,
}
def sendMessage(self):
pass
def process(self,param):
print("from A " +str(param) )
param.pop(0)
param.pop(0)
class B(A):
def __init__(self,id):
A.__init__(self, id)
self.commands.update({"hello":self.working})
def working(self):
pass
def process(self,param):
super(B, self).process(param)
print("from B" + str(param))
if __name__ == '__main__':
l = (1,2,tuple(str('3|4|5').split('|')))
x = B(2)
print(x.commands)
x.process(list(l))
from TimeSimulator import TimeSimulator
from Network import Network
from TestCases import populateTodolist
import logging
......@@ -7,11 +8,10 @@ logging.basicConfig(filename='ASim.log', level=logging.INFO)
logger = logging.getLogger(__name__)
def simulateThis(time):
logger.info("ticking time"+str(time))
if __name__ == '__main__':
metronome = TimeSimulator()
network = Network()
network.setupNetwork()
populateTodolist(network)
metronome.startTicking(network.simulate)
pass
\ No newline at end of file
from collections import defaultdict
import logging
logger = logging.getLogger(__name__)
class SystemEntity(object):
def __init__(self):
self.todoList = []
def __init__(self,network):
self.todoList = defaultdict(list)
self.network = network
#{'key': [('sendMessage','1','2','hello2'), ('sendMessage','1','3','hello3')],'key2':["hello"]}
# key is timestamp at which to perfrom action
def simulate(self,time):
pass
'''
If there is a messge in queue process it
if there is a task with key of current_time_stamp perform it
:param time: current tick in system time
:return:
'''
pass
from Network import Network
import csv
import logging
logger = logging.getLogger(__name__)
def populateTodolist(net):
if not net:
network = Network()
network.setupNetwork()
else:
network = net
with open('todolist.dat', 'r') as f:
reader = csv.reader(f)
for row in reader:
instruction = row[0].split() # tuple of a link
node = network.nodes.get(instruction[0])
node.todoList[instruction[1]].append(tuple(instruction[2:]))
for id,node in network.nodes.items():
logger.info(id +" : "+str(node.todoList))
def simulateNetwork(net):
if not net:
network = Network()
network.setupNetwork()
else:
network = net
populateTodolist(network)
for time in range(7):
logger.info("TIme : "+str(time))
network.simulate(str(time))
if __name__ == '__main__':
logging.basicConfig(filename='TestCases.log', level=logging.INFO)
# populateTodolist()
simulateNetwork(None)
pass
......@@ -15,7 +15,7 @@ class TimeSimulator(object):
for i in range(10):
self._systemTime = self._systemTime + 1
logger.info(self)
callback(self._systemTime)
callback(str(self._systemTime))
instance = None
def __new__(cls): # __new__ always a classmethod
......
from ecdsa import SigningKey,SECP256k1
from ecdsa.keys import BadSignatureError
import binascii
import random
import scipy.stats as stats
def bytes_to_int(bytes):
result = 0
for b in bytes:
result = result * 256 + int(b)
return result
def pseudoRandomGenerator(seed) :
random.seed(seed)
return random.getrandbits(256)
def genratePublicPrivateKey():
sk = SigningKey.generate(curve=SECP256k1) # uses NIST192p
vk = sk.get_verifying_key()
signature = sk.sign(b"message")
assert vk.verify(signature, b"message")
return (sk,vk)
pass
def VRF(sk,seed,pk):
'''
:param sk: secrete key
:param seed:
:param pk: public key of user
:return: (bytes array of signature may not b of fixed length | public key as a proof)
'''
prgoutput = pseudoRandomGenerator(seed)
temp = bytes(str(prgoutput).encode('UTF8'))
hash = sk.sign(temp)
proof = pk
return (hash,proof)
def verifyVRF(pk,hash,proof,seed):
prgoutput = pseudoRandomGenerator(seed)
temp = bytes(str(prgoutput).encode('UTF8'))
try:
proof.verify(hash, temp)
except BadSignatureError as badSign:
return False
return True
def sortition(sk,seed,rolecount,role,w,badaW,pk):
'''
:param sk: secrete key
:param seed:
:param rolecount: tou
:param role:
:param w: users stake or weight
:param badaW: total stake in game i.e. sum of wi
:param pk: public key of user
:return: (bytes array of signature may not b of fixed length | public key as a proof)
'''
newseed = (seed,role)
hash,proof = VRF(sk, newseed, pk)
p = rolecount/badaW
j=0
#simplifying the computation : hash/(2**hashlen)
tempHash = bytes_to_int(hash[:32]) # converting first 32 bytes to integer i.e 32*8 bits ie 256 bits
x = tempHash / (2 ** 256)
# print(x)
lastValue = 0;
# print("probability : ",p)
flag = True
while(flag):
lastValue = lastValue + stats.binom.pmf(j, w, p)
nextvalue = lastValue + stats.binom.pmf(j + 1, w, p)
# print(lastValue,nextvalue)
if (((lastValue<=x) and (nextvalue>x))):
break
if j == w+1:
j=0
break
j = j+1
return (hash,proof,j)
def verifySort(pk,hash,proof,seed,rolecount,role,w,badaW):
'''
:param pk: public key of user
:param hash:
:param proof:
:param seed:
:param rolecount: tou
:param role:
:param w: users stake or weight
:param badaW: total stake in game i.e. sum of wi
:return: number of selected users in commity for the user with pk
'''
newseed = (seed,role)
if not verifyVRF(pk,hash,proof,newseed):
return 0;
p = rolecount/badaW
j=0
#simplifying the computation : hash/(2**hashlen)
tempHash = bytes_to_int(hash[:32]) # converting first 32 bytes to integer i.e 32*8 bits ie 256 bits
x = tempHash / (2 ** 256)
# print(x)
lastValue = 0;
# print("probability : ",p)
flag = True
while(flag):
lastValue = lastValue + stats.binom.pmf(j, w, p)
nextvalue = lastValue + stats.binom.pmf(j + 1, w, p)
# print(lastValue,nextvalue)
if (((lastValue<=x) and (nextvalue>x))):
break
if j == w+1:
j=0
break
j = j+1
return j
def computeRange():
badaW=10
lastValue = 0;
p = 0.5
x = 0.0023
j = 0
if (x<=stats.binom.pmf(0, badaW, p)):
j = 0
else :
for k in range(0,badaW+1):
lastValue = lastValue + stats.binom.pmf(k, badaW, p)
nextvalue =lastValue+ stats.binom.pmf(k+1, badaW, p)
print(lastValue)
print(nextvalue)
print("------------")
j =j+1
def testingBinom():
w = 1
p=0.2
m = 0
for k in range(0,2):
ev = stats.binom.pmf(k, w, p)
m = m+ ev
print(ev)
print(m)
def testHashlen():
keypair = genratePublicPrivateKey()
seed = ("a", 1, 2)
x = VRF(keypair[0], seed, keypair[1])
print(len(binascii.hexlify(x[0])))
print(len(x[0]))
temp = bytes_to_int(x[0][:32]) # converting first 32 bytes to integer i.e 32*8 bits ie 256 bits
print(temp / (2 ** 256)) #computes something simalar to hash/(2**haslen)
def testSortition():
sk,pk = genratePublicPrivateKey()
seed = ("a",1,2)
roleCount = 26
role = "LEAD"
w = 20
badaW = 100
hash,proof,j = sortition(sk,seed,roleCount,role,w,badaW,pk)
# print(hash) #this is a real content of hash it should be used as final thing and not hexlify
print(binascii.hexlify(hash))
print(proof)
print(j)
def testVerifySort():
sk, pk = genratePublicPrivateKey()
seed = ("a", 1, 2)
roleCount = 26
role = "LEAD"
w = 20
badaW = 100
hash, proof, j = sortition(sk, seed, roleCount, role, w, badaW, pk)
# print(hash) #this is a real content of hash it should be used as final thing and not hexlify
print("-----sortition output -----------")
print(binascii.hexlify(hash))
print(proof)
print(j)
print("----------------------------------")
seed2 = ("a", 1, 2)
roleCount2 = 26
role2 = "LEAD"
w2 = 20
badaW = 100
y = verifySort(pk,hash,proof, seed2, roleCount2, role2, w2, badaW)
print(y)
assert (j==y),"Test VerifySort failed : "
seed3 = ("a", 1, 2)
roleCount3 = 26
role3 = "Commitee"
w3 = 20
badaW = 100
y = verifySort(pk,hash,proof, seed3, roleCount3, role3, w3, badaW)
print(y)
assert (y==0),"Test Verify sort failed : change of seed not detected"
def testVerifyVRF():
sk,pk = genratePublicPrivateKey()
seed = ("a",1,2)
hash,proof= VRF(sk,seed,pk)
seed2 = ("a", 1, 2)
status = verifyVRF(pk,hash,proof,seed2)
print(status)
if __name__ == '__main__':
# testSortition()
# testVerifyVRF()
testVerifySort()
# Verifiable-Random-Functions