Sfoglia il codice sorgente

update protocol with vrf

mohab 4 anni fa
parent
commit
0b24171d26

+ 20 - 4
script/research/streamlet/3.4-protocol.py

@@ -2,6 +2,9 @@
 
 from block import Block
 from node import Node
+from vrf import VRF
+import math
+import numpy as np
 
 # Genesis block is generated.
 genesis_block = Block("⊥", 0, '⊥')
@@ -31,11 +34,17 @@ node1.broadcast_transaction([node0, node2, node3, node4, node5], "tx2")
 node4.receive_transaction("tx3")
 node4.broadcast_transaction([node0, node1, node2, node3, node5], "tx3")
 
+vrf = VRF()
+x = epoch
+y, pi, g = vrf.sign(x)
+Y = np.array(y)
+y_hypotenuse2 = np.sum(Y[1]**2+Y[2]**2)
 # A random leader is selected.
-leader = nodes[hash(str(epoch))%len(nodes)]
+leader = nodes[math.ceil(y_hypotenuse2)%len(nodes)]
 
+print(f"proposed {x}, {y}, {pi}, {vrf.pk}, {g}")
 # Leader forms a block and broadcasts it.
-leader.propose_block(epoch, nodes)
+leader.propose_block(1, y, pi, vrf.pk, g, nodes)
 
 # Nodes vote on the block and broadcast their vote to rest nodes.
 for node in nodes:
@@ -60,11 +69,18 @@ node5.broadcast_transaction([node0, node1, node2, node3, node4, node6], "tx5")
 node6.receive_transaction("tx6")
 node6.broadcast_transaction([node0, node1, node2, node3, node4, node5], "tx6")
 
+x = epoch
+vrf = VRF()
+y, pi, g = vrf.sign(x)
+Y = np.array(y)
+y_hypotenuse2 = np.sum(Y[1]**2+Y[2]**2)
+# A random leader is selected.
+leader = nodes[math.ceil(y_hypotenuse2)%len(nodes)]
 # A random leader is selected.
-leader = nodes[hash(str(epoch))%len(nodes)]
 
+print(f"epoch number in protocol: {epoch}")
 # Leader forms a block and broadcasts it.
-leader.propose_block(epoch, nodes)
+leader.propose_block(epoch, y, pi, vrf.pk, g, nodes)
 
 # Nodes vote on the block and broadcast their vote to rest nodes.
 for node in nodes:

+ 54 - 0
script/research/streamlet/clock.py

@@ -0,0 +1,54 @@
+'''
+synchronized clock
+'''
+
+import ntplib
+from time import ctime
+import math
+
+class Clock(object):
+    def __init__(self, epoch_length=180, ntp_server='europe.pool.ntp.org'):
+        self.epoch_length=epoch_length #2 minutes
+        self.ntp_server = ntp_server
+        self.ntp_client = ntplib.NTPClient()
+        #TODO validate the server
+        # when was darkfi birthday? as seconds since the epoch 
+        self.darkfi_epoch=0
+        self.observers = []
+    def __repr__(self):
+        return 'darkfi time: '+ ctime(self.darkfi_time) + ', current synched time: ' + ctime(self.synched_time)
+
+    def __get_time_stat(self):
+        response=None
+        success=True
+        while not success:
+            try:
+                response = self.ntp_client.request(self.ntp_server, version=3)
+                success=True
+            except ntplib.NTPException as e:
+                 print("connection failed: {}".format(e.what()))
+        return response
+    @property
+    def synched_time(self):
+        state = self.__get_time_stat()
+        synched_time = state.tx_time
+        return synched_time
+
+    @property
+    def darkfi_time(self):
+        return self.synched_time - self.darkfi_epoch
+
+    @property
+    def epoch(self):   
+        return math.floor(self.darkfi_time/self.epoch_length)
+
+    def bind(self, callback):
+        self.observers.append((callback))
+
+    def background(self):
+        current_epoch = self.epoch
+        while True:
+            if self.epoch !=current_epoch:
+                current_epoch = self.epoch
+                for obs in self.observers:
+                    obs(current_epoch)

+ 15 - 5
script/research/streamlet/node.py

@@ -2,6 +2,7 @@ import copy, utils
 from block import Block
 from blockchain import Blockchain
 from vote import Vote
+from vrf import VRF
 from logger import Logger
 
 class Node:
@@ -18,6 +19,7 @@ class Node:
 		self.blockchain = Blockchain(init_block)
 		self.unconfirmed_transactions = []
 		self.log = Logger(self)
+		self.current_epoch=None #this need to be set by the clock tics
 	
 	def __repr__(self):
 		return "Node=[id={0}, clock={1}, password={2}, private_key={3}, public_key={4}, blockchain={5}, unconfirmed_transactions={6}".format(self.id, self.clock, self.password, self.private_key, self.public_key, self.blockchain, self.unconfirmed_transactions)
@@ -33,22 +35,30 @@ class Node:
 		for node in nodes:
 			node.receive_transaction(transaction)
 			
-	def propose_block(self, epoch, nodes):
+	def propose_block(self, epoch, y, pi, vrf_pk, g, nodes):
 		proposed_block = Block(hash(self.blockchain.blocks[-1]), epoch, self.unconfirmed_transactions)
 		signed_proposed_block = utils.sign_message(self.password, self.private_key, proposed_block)
 		for node in nodes:
-			node.receive_proposed_block(self.public_key, copy.deepcopy(proposed_block), copy.deepcopy(signed_proposed_block))
+			node.receive_proposed_block(self.public_key, y, pi, vrf_pk, g, copy.deepcopy(proposed_block), copy.deepcopy(signed_proposed_block))
 	
-	def receive_proposed_block(self, leader_pubkey,  round_block, signed_round_block):
+	def receive_proposed_block(self, leader_pubkey, y, pi, vrf_pk, g, round_block, signed_round_block):
 		if not utils.verify_signature(leader_pubkey, round_block, signed_round_block):
-			self.warn("the signature of the proposed block dosn't match")
+			self.log.warn("the signature of the proposed block dosn't match")
+			return
+		#TODO alert that is insecure, e should be set by the ticing clock
+		x = round_block.e
+		#TODO pass and verify the proposed leader id
+		print(f"epoch number in verification {round_block.e}")
+		print(f"verifying {x}, {y}, {pi}, {vrf_pk}, {g}")
+		if not VRF.verify(x, y, pi, vrf_pk, g):
+			self.log.warn("failed verifying choosing leader")
 			return
 		self.round_block = round_block
 		
 	def vote_on_round_block(self, nodes):
 		# Node verifies proposed block extends from one of the longest notarized chains that node has seen at the time.
 		# Already notarized check.
-		if (self.round_block != self.blockchain.blocks[-1]):
+		if self.round_block != self.blockchain.blocks[-1]:
 			self.blockchain.check_block_validity(self.round_block, self.blockchain.blocks[-1])
 		#TODO implement: at this point we need to verify the unconfirmed transactions
 		signed_block = utils.sign_message(self.password, self.private_key, self.round_block)

+ 26 - 21
script/research/streamlet/vrf.py

@@ -1,8 +1,7 @@
-from msilib import type_string
 from logger import Logger
 import random as rnd
 from  tate_bilinear_pairing import eta, ecc
-
+eta.init(369)
 
 def extended_euclidean_algorithm(a, b):
     """
@@ -51,9 +50,8 @@ class VRF(object):
         self.pk = None
         self.sk = None
         self.log = Logger(self)
-        eta.init(rnd.randint(0,369))
+        #TODO (res) adhoc temporary
         self.g = ecc.gen()
-        self.password='somepasskey'
         self.__gen()
         self.order = ecc.order()
 
@@ -65,28 +63,35 @@ class VRF(object):
         self.sk = rnd.randint(0,1000)
         self.pk = ecc.scalar_mult(self.sk, self.g)
 
-    def prove(self, x):
+    '''
+    short signature without random oracle
+    @param x: message to be signed
+    '''
+    def sign(self, x):
         pi = ecc.scalar_mult(inverse_of(x+self.sk, self.order), self.g)
         y = eta.pairing(*self.g[1:], *pi[1:])
-        return (y, pi)
+        return (y, pi, self.g)
     
     '''
-    @param y: signed output
+    verify signature
+    @param x: signed messaged
+    @param y: signature
     @param pi: [inf, x, y] proof components
     @param pk: [inf, x, y] public key components of the prover 
+    @param g: group base
     '''
-    def verify(self, x, y, pi, pk):
-        gx = ecc.scalar_mult(x, self.g)
-        pk = ecc.scalar_mult(1, pk)
-        rhs = eta.pairing(*ecc.scalar_mult(1,self.g)[1:], *pi[1:])
-        assert(y == rhs)
-        gxs = ecc.add(gx, pk)
+    def verify(x, y, pi, pk_raw, g):
+        gx = ecc.scalar_mult(x, g)
+        #pk = ecc.scalar_mult(1, pk_raw)
+        rhs = eta.pairing(*ecc.scalar_mult(1,g)[1:], *pi[1:])
+        if not y == rhs:
+            print(f"y: {y}, rhs: {rhs}")
+            return False
+        gxs = ecc.add(gx, pk_raw)
         lhs = eta.pairing(*gxs[1:], *pi[1:])
-        rhs = eta.pairing(*ecc.scalar_mult(1,self.g)[1:], *ecc.scalar_mult(1,self.g)[1:])
-        assert(lhs==rhs)
-    
-
-vrf = VRF()
-x = 2
-y, pi = vrf.prove(x)
-vrf.verify(x, y, pi, vrf.pk)
+        rhs = eta.pairing(*ecc.scalar_mult(1, g)[1:], *ecc.scalar_mult(1, g)[1:])
+        if not lhs==rhs:
+            print(f"proposed {x}, {y}, {pi}, {pk_raw}, {g}")
+            print(f"lhs: {lhs},\nrhs: {rhs}")
+            return False
+        return True