Browse Source

[research/ouroboros] basic non-forkable blockchain

mohab 4 năm trước cách đây
mục cha
commit
223951cfe5

+ 7 - 4
script/research/dpos/ouroboros/block.py

@@ -27,9 +27,9 @@ class Block(object):
 
 
     def __repr__(self):
     def __repr__(self):
         if self.is_genesis:
         if self.is_genesis:
-            return "GensisBlock at {slot:"+self.sl+",data:"+self.tx+",state:"+self.state+"}\n"+\
-                decode_gensis_data(self.data)
-        return "Block at {slot:"+self.sl+",data:"+self.tx+",state:"+self.state+"}"
+            #return "GensisBlock at {slot:"+str(self.sl)+",data:"+self.tx+",state:"+str(self.state)+"}\n"+decode_gensis_data(self.tx)
+            return "GensisBlock at {slot:"+str(self.sl)+",data:"+str(self.tx)+",state:"+str(self.state)+"}\n"+str(self.tx)
+        return "Block at {slot:"+str(self.sl)+",data:"+str(self.tx)+",state:"+str(self.state)+"}"
     
     
     def __hash__(self):
     def __hash__(self):
         if type(self.tx)==str:
         if type(self.tx)==str:
@@ -63,7 +63,10 @@ class Block(object):
 
 
     @property
     @property
     def empty(self):
     def empty(self):
-        return (self.data=='' or self.slot<0) and self.state==''
+        return (self.tx=='' or self.slot<0) and self.state==''
+
+    def encode(self):
+        return str(self).encode()
 
 
 class GensisBlock(Block):
 class GensisBlock(Block):
     '''
     '''

+ 9 - 4
script/research/dpos/ouroboros/environment.py

@@ -50,6 +50,11 @@ class Z(object):
     def current_leader_vrf_g(self):
     def current_leader_vrf_g(self):
         return self.stakeholders[self.current_leader_id].vrf_base
         return self.stakeholders[self.current_leader_id].vrf_base
 
 
+    @property
+    def current_leader_sig_pk(self):
+        return self.stakeholders[self.current_leader_id].sig_pk
+    
+
     #TODO complete
     #TODO complete
     def obfuscate_idx(self, i):
     def obfuscate_idx(self, i):
         return i
         return i
@@ -105,7 +110,7 @@ class Z(object):
         assert current_leader!=None, "current leader cant be None"
         assert current_leader!=None, "current leader cant be None"
         if current_leader.is_leader:
         if current_leader.is_leader:
             #pass leadership to the current slot leader from the epoch leader
             #pass leadership to the current slot leader from the epoch leader
-            self.stakeholders[self.current_epoch_leaders[slot%self.epoch_length]].set_leader()
+            self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
     
     
     def new_epoch(self, slot, sigmas, proofs):
     def new_epoch(self, slot, sigmas, proofs):
         self.current_slot=slot
         self.current_slot=slot
@@ -115,10 +120,10 @@ class Z(object):
         #assert(current_leader.is_leader)
         #assert(current_leader.is_leader)
         self.select_epoch_leaders(sigmas, proofs)
         self.select_epoch_leaders(sigmas, proofs)
 
 
-    def broadcast_block(self, signed_block):
+    def broadcast_block(self, signed_block, slot_uid):
         for stakeholder in self.stakeholders:
         for stakeholder in self.stakeholders:
             if not stakeholder.is_leader:
             if not stakeholder.is_leader:
-                self.stakeholders.receive_block(signed_block)
+                stakeholder.receive_block(signed_block, slot_uid)
 
 
     def start(self):
     def start(self):
         for sh in self.stakeholders:
         for sh in self.stakeholders:
@@ -128,4 +133,4 @@ class Z(object):
 
 
     def print_blockchain(self):
     def print_blockchain(self):
         bc = self.stakeholders[0].blockchain
         bc = self.stakeholders[0].blockchain
-        self.log.info(f"<blockchain>  {len(bc)} blocks: "+str(bc))
+        self.log.highlight(f"<blockchain>  {len(bc)} blocks: "+str(bc))

+ 1 - 1
script/research/dpos/ouroboros/epoch.py

@@ -43,7 +43,7 @@ class Epoch(list):
             # the caller of the add_block should execute (corrupt,U)
             # the caller of the add_block should execute (corrupt,U)
             pass
             pass
 
 
-        if self.length==self.R:
+        if self.length>=self.R:
             self.log.error(f"epoch length: {self.length} can't exceed Epoch's length: {self.R}")
             self.log.error(f"epoch length: {self.length} can't exceed Epoch's length: {self.R}")
         self.blocks.append(block)
         self.blocks.append(block)
     
     

+ 12 - 0
script/research/dpos/ouroboros/sig_test.py

@@ -0,0 +1,12 @@
+from ouroboros.utils import *
+from ouroboros.block import Block, EmptyBlock
+
+passwd = 'passwd'
+empty = EmptyBlock()
+block = Block(empty, 'data', 1)
+
+sk, pk = generate_sig_keys(passwd)
+message = 'msg'
+
+signature = sign_message(passwd, sk, message)
+assert verify_signature(pk, message, signature)

+ 27 - 16
script/research/dpos/ouroboros/stakeholder.py

@@ -7,10 +7,12 @@ from ouroboros.vrf import verify, VRF
 from ouroboros.utils import *
 from ouroboros.utils import *
 from ouroboros.logger import Logger
 from ouroboros.logger import Logger
 from ouroboros.consts import *
 from ouroboros.consts import *
+from copy import deepcopy
 import time
 import time
 '''
 '''
 \class Stakeholder
 \class Stakeholder
 '''
 '''
+
 class Stakeholder(object):
 class Stakeholder(object):
     def __init__(self, epoch_length=2, passwd='password'):
     def __init__(self, epoch_length=2, passwd='password'):
         #TODO (fix) remove redundant variables reley on environment
         #TODO (fix) remove redundant variables reley on environment
@@ -35,12 +37,12 @@ class Stakeholder(object):
         self.am_current_endorder=False
         self.am_current_endorder=False
         self.am_corrupt=False
         self.am_corrupt=False
         #
         #
+        self.blockchain=None
 
 
     @property
     @property
     def is_leader(self):
     def is_leader(self):
         return self.am_current_leader
         return self.am_current_leader
 
 
-    
     @property
     @property
     def vrf_pk(self):
     def vrf_pk(self):
         return self.__vrf_pk
         return self.__vrf_pk
@@ -93,7 +95,7 @@ class Stakeholder(object):
         #kickoff gensis block
         #kickoff gensis block
         # add old epoch to the ledger
         # add old epoch to the ledger
         if self.current_slot_uid > 1 and self.current_epoch!=None and len(self.current_epoch)>0:
         if self.current_slot_uid > 1 and self.current_epoch!=None and len(self.current_epoch)>0:
-            self.blockchain.add_epoch(self.current_epoch) 
+            self.blockchain.add_epoch(self.current_epoch)  
         #if leader, you need to broadcast the block
         #if leader, you need to broadcast the block
         self.__gen_genesis_epoch() 
         self.__gen_genesis_epoch() 
         if self.am_current_leader:
         if self.am_current_leader:
@@ -112,22 +114,21 @@ class Stakeholder(object):
         self.env.new_slot(slot, sigma, proof)
         self.env.new_slot(slot, sigma, proof)
         vrf_pk = self.env.current_leader_vrf_pk
         vrf_pk = self.env.current_leader_vrf_pk
         vrf_g = self.env.current_leader_vrf_g
         vrf_g = self.env.current_leader_vrf_g
-        assert(vrf_pk!=None)
-        assert(vrf_g!=None)
         if not verify(slot, sigma, proof, vrf_pk,vrf_g) :
         if not verify(slot, sigma, proof, vrf_pk,vrf_g) :
             #TODO the leader is corrupted, action to be taken against the corrupt stakeholder
             #TODO the leader is corrupted, action to be taken against the corrupt stakeholder
             #in this case this slot is empty
             #in this case this slot is empty
             self.current_block=EmptyBlock() 
             self.current_block=EmptyBlock() 
-            if self.current_epoch!=None:
-                self.current_epoch.add_block(self.current_block)
-            else:
-                self.log.warn(f"<new_slot> current_epoch is None!")
-                return
+            if self.current_epoch==None:
+                self.log.warn(f"<new_slot> leader verification fails, current_epoch is None!")
+                self.__gen_genesis_epoch()
+            self.current_epoch.add_block(self.current_block)
+            return
         if self.current_epoch==None:
         if self.current_epoch==None:
             self.log.warn(f"<new_slot> current_epoch is None!")
             self.log.warn(f"<new_slot> current_epoch is None!")
             self.__gen_genesis_epoch()
             self.__gen_genesis_epoch()
         self.current_slot_uid = slot
         self.current_slot_uid = slot
-        self.current_block=Block(self.current_block, self.tx, self.current_slot_uid)
+        prev_blk = self.blockchain[-1] if len(self.blockchain)>0 else EmptyBlock()
+        self.current_block=Block(prev_blk, self.tx, self.current_slot_uid)
         self.current_epoch.add_block(self.current_block)
         self.current_epoch.add_block(self.current_block)
         if self.am_current_leader:
         if self.am_current_leader:
             self.broadcast_block()
             self.broadcast_block()
@@ -143,16 +144,26 @@ class Stakeholder(object):
 
 
     def broadcast_block(self):
     def broadcast_block(self):
         self.log.highlight("broadcasting block")
         self.log.highlight("broadcasting block")
-        assert(self.am_current_leader)
+        assert(self.am_current_leader and self.current_block is not None)
         signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
         signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
-        self.env.broadcast_block(signed_block)
+        self.env.broadcast_block(signed_block, self.current_slot_uid)
         self.env.print_blockchain()
         self.env.print_blockchain()
 
 
-
-    def receive_block(self, received_block):
+    def receive_block(self, signed_block, blk_uid):
         self.log.highlight("receiving block")
         self.log.highlight("receiving block")
-        if verify_signature(self.env.current_leader_sig_pk, self.current_block, received_block):
-            pass
+        cur_blk = None
+        assert(blk_uid>0)
+        stashed=True
+        if blk_uid>= len(self.blockchain):
+            cur_blk = self.current_block
+        else:
+            #TODO this assumes synced blockchain
+            cur_blk = self.blockchain[blk_uid]
+            stashed=False
+        #TODO to consider deley should retrive leader_pk of corresponding blk_uid
+        if verify_signature(self.env.current_leader_sig_pk, cur_blk, signed_block):
+            if stashed:
+                self.current_epoch.add_block(cur_blk)
         else:
         else:
             self.env.corrupt(self.env.current_leader_id)
             self.env.corrupt(self.env.current_leader_id)
         self.env.print_blockchain()
         self.env.print_blockchain()

+ 13 - 0
script/research/dpos/sig_test.py

@@ -0,0 +1,13 @@
+from ouroboros.utils import *
+from ouroboros.block import Block, EmptyBlock
+
+passwd = 'passwd'
+empty = EmptyBlock()
+block = Block(empty, 'data', 1)
+
+sk, pk = generate_sig_keys(passwd)
+message = 'msg'
+
+signature = sign_message(passwd, sk, message)
+assert verify_signature(pk, message, signature)
+

+ 1 - 0
script/research/dpos/simulation.py

@@ -8,6 +8,7 @@ stakeholders = []
 for i in range(2):
 for i in range(2):
     stakeholders.append(Stakeholder(EPOCH_LENGTH))
     stakeholders.append(Stakeholder(EPOCH_LENGTH))
 
 
+stakeholders[0].set_leader()
 environment = Z(stakeholders, EPOCH_LENGTH, genesis_time=time.time())
 environment = Z(stakeholders, EPOCH_LENGTH, genesis_time=time.time())
 environment.start()
 environment.start()