Forráskód Böngészése

separate ouroboros from streamlet

mohab 4 éve
szülő
commit
78f2f3be13

+ 0 - 0
script/research/PoS-blockchain/__init__.py


+ 8 - 11
script/research/streamlet/node.py → script/research/PoS-blockchain/node.py

@@ -1,9 +1,6 @@
-import copy, utils
-from block import Block
-from blockchain import Blockchain
-from vote import Vote
-from vrf import VRF
-from logger import Logger
+import copy
+from streamlet import Block, Blockchain, Vote, Logger, generate_keys, sign_message, verify_signature
+from ouroboros import VRF
 
 class Node:
 	''' This class represents a protocol node.
@@ -15,7 +12,7 @@ class Node:
 		self.id = id
 		self.clock = clock # Clock syncronization to be implemented.
 		self.password = password
-		self.private_key, self.public_key = utils.generate_keys(self.password)
+		self.private_key, self.public_key = generate_keys(self.password)
 		self.blockchain = Blockchain(init_block)
 		self.unconfirmed_transactions = []
 		self.log = Logger(self)
@@ -37,12 +34,12 @@ class Node:
 			
 	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)
+		signed_proposed_block = sign_message(self.password, self.private_key, proposed_block)
 		for node in nodes:
 			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, y, pi, vrf_pk, g, round_block, signed_round_block):
-		if not utils.verify_signature(leader_pubkey, round_block, signed_round_block):
+		if not verify_signature(leader_pubkey, round_block, signed_round_block):
 			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
@@ -61,7 +58,7 @@ class Node:
 		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)
+		signed_block = sign_message(self.password, self.private_key, self.round_block)
 		vote = Vote(signed_block, self.round_block, self.id)
 		for node in nodes:
 			node.receive_vote(self.public_key, vote, nodes)
@@ -70,7 +67,7 @@ class Node:
 		# We verify we haven't received a vote from that node again.
 		assert(vote not in self.round_block.votes)
 		# When nodes receive votes, they verify them against nodes public key.
-		assert(utils.verify_signature(node_public_key, vote.block, vote.vote))
+		assert(verify_signature(node_public_key, vote.block, vote.vote))
 		assert(self.round_block == vote.block)
 		# Additional rules must be defined by the protocol for its voting system.
 		self.round_block.votes.append(vote)

+ 1 - 0
script/research/PoS-blockchain/ouroboros/__init__.py

@@ -0,0 +1 @@
+from ouroboros.vrf import VRF

+ 40 - 0
script/research/PoS-blockchain/ouroboros/utils.py

@@ -0,0 +1,40 @@
+def extended_euclidean_algorithm(a, b):
+    """
+    Returns a three-tuple (gcd, x, y) such that
+    a * x + b * y == gcd, where gcd is the greatest
+    common divisor of a and b.
+
+    This function implements the extended Euclidean
+    algorithm and runs in O(log b) in the worst case.
+    """
+    s, old_s = 0, 1
+    t, old_t = 1, 0
+    r, old_r = b, a
+
+    while r != 0:
+        quotient = old_r // r
+        old_r, r = r, old_r - quotient * r
+        old_s, s = s, old_s - quotient * s
+        old_t, t = t, old_t - quotient * t
+
+    return old_r, old_s, old_t
+
+
+def inverse_of(n, p):
+    """
+    Returns the multiplicative inverse of
+    n modulo p.
+
+    This function returns an integer m such that
+    (n * m) % p == 1.
+    """
+    gcd, x, y = extended_euclidean_algorithm(n, p)
+    assert (n * x + p * y) % p == gcd
+
+    if gcd != 1:
+        # Either n is 0, or p is not a prime number.
+        raise ValueError(
+            '{} has no multiplicative inverse '
+            'modulo {}'.format(n, p))
+    else:
+        return x % p

+ 59 - 0
script/research/PoS-blockchain/ouroboros/vrf.py

@@ -0,0 +1,59 @@
+from streamlet.logger import Logger
+import random as rnd
+from  tate_bilinear_pairing import eta, ecc
+from ouroboros.utils import inverse_of
+eta.init(369)
+
+class VRF(object):
+    '''
+    verifiable random function implementation
+    '''
+    def __init__(self):
+        self.pk = None
+        self.sk = None
+        self.log = Logger(self)
+        #TODO (res) adhoc temporary
+        self.g = ecc.gen()
+        self.__gen()
+        self.order = ecc.order()
+
+    def __gen(self):
+        '''
+        generate pk/sk
+        '''
+        # TODO implement that is simple sk choosing mechanism for poc; 
+        self.sk = rnd.randint(0,1000)
+        self.pk = ecc.scalar_mult(self.sk, self.g)
+
+    '''
+    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, self.g)
+    
+    '''
+    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(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, 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

+ 48 - 0
script/research/PoS-blockchain/protocol.py

@@ -0,0 +1,48 @@
+from streamlet import Block
+from ouroboros import VRF
+from node import Node
+import math
+import numpy as np
+
+# Genesis block is generated.
+genesis_block = Block("⊥", 0, '⊥')
+
+# We create some nodes to participate in the Protocol.
+# There are in total n nodes numbered.
+node0 = Node(0, "clock", "node_password0", genesis_block)
+node1 = Node(1, "clock", "node_password1", genesis_block)
+node4 = Node(4, "clock", "node_password4", genesis_block)
+
+nodes = [node0, node1, node4]
+# We simulate some rounds to test consistency.
+epoch = 1
+
+# Nodes receive transactions and broacasts them between them.
+# node0 receives input and broadcasts it to rest nodes.
+node0.receive_transaction("tx0")
+node0.broadcast_transaction([node1, node4], "tx0")
+# node1 receives input and broadcasts it to rest nodes.
+node1.receive_transaction("tx2")
+node1.broadcast_transaction([node0, node4], "tx2")
+# node4 receives input and broadcasts it to rest nodes.
+node4.receive_transaction("tx3")
+node4.broadcast_transaction([node0, node1], "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[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(1, y, pi, vrf.pk, g, nodes)
+
+# Nodes vote on the block and broadcast their vote to rest nodes.
+for node in nodes:
+	node.vote_on_round_block(nodes)
+
+# We verify that all nodes have the same blockchain on round end.
+assert(node0.output() == node1.output() == node4.output())

+ 0 - 0
script/research/streamlet/2-execution-model-and-definitions.py → script/research/PoS-blockchain/streamlet/2-execution-model-and-definitions.py


+ 0 - 0
script/research/streamlet/3.2-blocks-and-blockchain.py → script/research/PoS-blockchain/streamlet/3.2-blocks-and-blockchain.py


+ 0 - 0
script/research/streamlet/3.3-votes-and-notarization.py → script/research/PoS-blockchain/streamlet/3.3-votes-and-notarization.py


+ 5 - 19
script/research/streamlet/3.4-protocol.py → script/research/PoS-blockchain/streamlet/3.4-protocol.py

@@ -2,9 +2,6 @@
 
 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, '⊥')
@@ -34,17 +31,11 @@ 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[math.ceil(y_hypotenuse2)%len(nodes)]
+leader = nodes[hash(str(epoch))%len(nodes)]
 
-print(f"proposed {x}, {y}, {pi}, {vrf.pk}, {g}")
 # Leader forms a block and broadcasts it.
-leader.propose_block(1, y, pi, vrf.pk, g, nodes)
+leader.propose_block(1, nodes)
 
 # Nodes vote on the block and broadcast their vote to rest nodes.
 for node in nodes:
@@ -70,17 +61,13 @@ 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)]
+leader = nodes[hash(str(epoch))%len(nodes)]
 # A random leader is selected.
 
-print(f"epoch number in protocol: {epoch}")
 # Leader forms a block and broadcasts it.
-leader.propose_block(epoch, y, pi, vrf.pk, g, nodes)
+leader.propose_block(epoch, nodes)
 
 # Nodes vote on the block and broadcast their vote to rest nodes.
 for node in nodes:
@@ -92,4 +79,3 @@ assert(node0.output() == node1.output() == node2.output() == node3.output() == n
 # Since node6 joined later, node0 output is a prefix or equal to node6 output.
 # Based on that, node6 output is a suffix of node0 output.
 assert(node0.output().blocks[-len(node6.output()):] == node6.output().blocks)
-print('finished...')

+ 6 - 0
script/research/PoS-blockchain/streamlet/__init__.py

@@ -0,0 +1,6 @@
+from streamlet.block import Block
+from streamlet.blockchain import Blockchain
+from streamlet.clock import Clock
+from streamlet.vote import Vote
+from streamlet.utils import *
+from streamlet.logger import Logger

+ 1 - 1
script/research/streamlet/block.py → script/research/PoS-blockchain/streamlet/block.py

@@ -1,4 +1,4 @@
-class Block:
+class Block(object):
 	''' This class represents a tuple of the form (h, e, txs).
 		Each blocks parent hash h may be computed simply as a hash of the parent block. '''
 	def __init__(self, h, e, txs):

+ 0 - 0
script/research/streamlet/blockchain.py → script/research/PoS-blockchain/streamlet/blockchain.py


+ 0 - 0
script/research/streamlet/clock.py → script/research/PoS-blockchain/streamlet/clock.py


+ 0 - 0
script/research/streamlet/logger.py → script/research/PoS-blockchain/streamlet/logger.py


+ 78 - 0
script/research/PoS-blockchain/streamlet/node.py

@@ -0,0 +1,78 @@
+import copy
+from block import Block
+from utils import *
+from blockchain import Blockchain
+from vote import Vote
+from logger import Logger
+
+class Node:
+	''' This class represents a protocol node.
+		Each node is numbered and has a secret-public keys pair, to sign messages.
+		Nodes hold a set of Blockchains(some of which are not notarized) 
+		and a set of unconfirmed pending transactions. 
+		All nodes have syncronized clocks, using GST approach.'''
+	def __init__(self, id, clock, password, init_block):
+		self.id = id
+		self.clock = clock # Clock syncronization to be implemented.
+		self.password = password
+		self.private_key, self.public_key = generate_keys(self.password)
+		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)
+	
+	def output(self):
+		return self.blockchain
+	
+	def receive_transaction(self, transaction):
+		# Additional validity rules must be defined by the protocol for its blockchain data structure.
+		self.unconfirmed_transactions.append(transaction)
+	
+	def broadcast_transaction(self, nodes, transaction):
+		for node in nodes:
+			node.receive_transaction(transaction)
+			
+	def propose_block(self, epoch, nodes):
+		proposed_block = Block(hash(self.blockchain.blocks[-1]), epoch, self.unconfirmed_transactions)
+		signed_proposed_block = 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))
+	
+	def receive_proposed_block(self, leader_pubkey, round_block, signed_round_block):
+		if not verify_signature(leader_pubkey, round_block, signed_round_block):
+			self.log.warn("the signature of the proposed block dosn't match")
+			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]:
+			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 = sign_message(self.password, self.private_key, self.round_block)
+		vote = Vote(signed_block, self.round_block, self.id)
+		for node in nodes:
+			node.receive_vote(self.public_key, vote, nodes)
+
+	def receive_vote(self, node_public_key, vote, nodes):
+		# We verify we haven't received a vote from that node again.
+		assert(vote not in self.round_block.votes)
+		# When nodes receive votes, they verify them against nodes public key.
+		assert(verify_signature(node_public_key, vote.block, vote.vote))
+		assert(self.round_block == vote.block)
+		# Additional rules must be defined by the protocol for its voting system.
+		self.round_block.votes.append(vote)
+		# When a node sees 2n/3 votes for a block it notarizes it
+		if (self.round_block != self.blockchain.blocks[-1] and len(self.round_block.votes) > (2 * len(nodes) / 3)):
+			notarized_block = copy.deepcopy(self.round_block)
+			notarized_block.notarized = True
+			self.blockchain.add_block(notarized_block)
+			# Node removes block transactions from unconfirmed_transactions array
+			#for transaction in notarized_block.txs:
+			#	self.unconfirmed_transactions.remove(transaction)
+			
+			

+ 0 - 0
script/research/streamlet/utils.py → script/research/PoS-blockchain/streamlet/utils.py


+ 0 - 0
script/research/streamlet/vote.py → script/research/PoS-blockchain/streamlet/vote.py


+ 0 - 0
script/research/streamlet/vrf.py → script/research/PoS-blockchain/streamlet/vrf.py