mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
[research/ouroboros] added Input endorser
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import threading
|
||||
from ouroboros.clock import SynchedNTPClock
|
||||
from ouroboros.vrf import VRF
|
||||
from ouroboros.logger import Logger
|
||||
import threading
|
||||
import time
|
||||
|
||||
'''
|
||||
\class TrustedBeacon
|
||||
@@ -51,6 +50,8 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
|
||||
else:
|
||||
sigmas = []
|
||||
proofs = []
|
||||
#TODO since it's expensive, but to generate single (y,pi) pair as seed
|
||||
# and use random hash function to generate the rest randomly.
|
||||
for i in range(self.epoch_length):
|
||||
self.log.info(f"callback: new slot of idx: {self.current_slot}, epoch slot {i}")
|
||||
y, pi = self.vrf.sign(self.current_slot)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
from ouroboros.utils import encode_genesis_data, decode_gensis_data, state_hash
|
||||
from ouroboros.consts import *
|
||||
from ouroboros.logger import Logger
|
||||
|
||||
'''
|
||||
single block B_i for slot i in the system live time L,
|
||||
@@ -16,7 +17,7 @@ class Block(object):
|
||||
it's one-based
|
||||
@param gensis: boolean, True for gensis block
|
||||
'''
|
||||
def __init__(self, previous_block, data, slot_uid, genesis=False):
|
||||
def __init__(self, previous_block, data, slot_uid, genesis_time, genesis=False):
|
||||
# state is hte hash of the previous block in the blockchain
|
||||
self.state=''
|
||||
if slot_uid>1:
|
||||
@@ -24,6 +25,8 @@ class Block(object):
|
||||
self.tx = data
|
||||
self.sl = slot_uid
|
||||
self.is_genesis=genesis
|
||||
self.endorsed=False
|
||||
self.log = Logger(genesis_time)
|
||||
|
||||
def __repr__(self):
|
||||
if self.is_genesis:
|
||||
@@ -52,6 +55,9 @@ class Block(object):
|
||||
'sl': self.sl}
|
||||
return json.encoder(d)
|
||||
|
||||
def set_endorsed(self):
|
||||
self.endorsed=True
|
||||
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
@@ -103,5 +109,5 @@ lead by offline leader
|
||||
is an empty Block
|
||||
'''
|
||||
class EmptyBlock(Block):
|
||||
def __init__(self):
|
||||
Block.__init__(self, '', -1, False)
|
||||
def __init__(self, genesis_time):
|
||||
Block.__init__(self, '', -1, genesis_time, False)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from ouroboros.logger import Logger
|
||||
import time
|
||||
|
||||
'''
|
||||
Non-forkable Blockchain for simplicity
|
||||
#TODO consider forkable property
|
||||
#TODO implement forkable chain
|
||||
'''
|
||||
class Blockchain(object):
|
||||
def __init__(self, R, genesis_time):
|
||||
@@ -37,6 +37,3 @@ class Blockchain(object):
|
||||
self.__add_block(block)
|
||||
else:
|
||||
self.log.warn(f"an empty block at index of index: {block.index},\nrelative slot:{idx}\nabsolute slot: {self.length*idx+block.slot}")
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from debugpy import configure
|
||||
import numpy as np
|
||||
import math
|
||||
import random
|
||||
import time
|
||||
from ouroboros.logger import Logger
|
||||
from ouroboros.consts import *
|
||||
import time
|
||||
|
||||
'''
|
||||
\class Z is the environment
|
||||
'''
|
||||
@@ -15,8 +17,10 @@ class Z(object):
|
||||
self.stakeholders = np.array(stakeholdes)
|
||||
self.adversary_mask=np.array([True]*len(stakeholdes))
|
||||
self.current_epoch_leaders=[-1]*self.epoch_length
|
||||
self.current_epoch_endorsers=[-1]*self.epoch_length
|
||||
self.current_slot=0
|
||||
self.log.info("Z initialized")
|
||||
self.current_blk_endorser_sig=None
|
||||
|
||||
def __repr__(self):
|
||||
buff= f"envirnment of {self.length} stakholders"
|
||||
@@ -35,13 +39,27 @@ class Z(object):
|
||||
return genesis_data
|
||||
|
||||
@property
|
||||
def current_leader_id(self):
|
||||
def epoch_slot(self):
|
||||
return self.current_slot%self.epoch_length
|
||||
|
||||
@property
|
||||
def current_leader_id(self):
|
||||
return self.current_epoch_leaders[self.epoch_slot]
|
||||
|
||||
@property
|
||||
def current_stakeholder(self):
|
||||
self.log.info(f"getting leader of id{self.current_leader_id} of size {len(self.stakeholders)}")
|
||||
self.log.info(f"getting leader of id: {self.current_leader_id}")
|
||||
return self.stakeholders[self.current_leader_id]
|
||||
|
||||
@property
|
||||
def current_endorser_id(self):
|
||||
return self.current_endorser_id[self.epoch_slot]
|
||||
|
||||
@property
|
||||
def current_endorser(self):
|
||||
self.log.info(f"getting endorser of id: {self.current_leader_id}")
|
||||
return self.stakeholders[self.current_endorser_id]
|
||||
|
||||
@property
|
||||
def current_leader_vrf_pk(self):
|
||||
return self.stakeholders[self.current_leader_id].vrf_pk
|
||||
@@ -54,6 +72,9 @@ class Z(object):
|
||||
def current_leader_sig_pk(self):
|
||||
return self.stakeholders[self.current_leader_id].sig_pk
|
||||
|
||||
@property
|
||||
def current_endorser_sig_pk(self):
|
||||
return self.stakeholders[self.current_endorser_id].sig_pk
|
||||
|
||||
#TODO complete
|
||||
def obfuscate_idx(self, i):
|
||||
@@ -78,6 +99,7 @@ class Z(object):
|
||||
@property
|
||||
def length(self):
|
||||
return len(self.stakeholders)
|
||||
|
||||
@property
|
||||
def honest(self):
|
||||
return len(self.stakeholders[self.adversary_mask])
|
||||
@@ -95,13 +117,17 @@ class Z(object):
|
||||
seed = leader_selection_hash(sigma)
|
||||
random.seed(seed)
|
||||
leader_idx=seed%self.length
|
||||
endorser_idx=random.randint(0,self.length)
|
||||
# only select an honest leaders
|
||||
while not self.adversary_mask[leader_idx]:
|
||||
while leader_idx==endorser_idx or not self.adversary_mask[leader_idx] or not self.adversary_mask[endorser_idx]:
|
||||
leader_idx=random.randint(0,self.length)
|
||||
enderser_idx=random.randint(0,self.length)
|
||||
|
||||
#TODO select the following leader for this epoch, note,
|
||||
# under a single condition that no one is able to predict who is next
|
||||
self.current_epoch_leaders[i]=leader_idx
|
||||
return self.current_epoch_leaders
|
||||
self.current_epoch_endorsers[i]=endorser_idx
|
||||
return self.current_epoch_leaders, self.current_epoch_endorsers
|
||||
|
||||
def new_slot(self, slot, sigma, proof):
|
||||
self.current_slot=slot
|
||||
@@ -111,21 +137,27 @@ class Z(object):
|
||||
if current_leader.is_leader:
|
||||
#pass leadership to the current slot leader from the epoch leader
|
||||
self.stakeholders[self.current_epoch_leaders[self.current_leader_id]].set_leader()
|
||||
self.stakeholders[self.current_epoch_endorsers[self.current_endorser_id]].set_endorser()
|
||||
|
||||
def new_epoch(self, slot, sigmas, proofs):
|
||||
self.current_slot=slot
|
||||
#self.log.info(f"stakeholders: {self.stakeholders}")
|
||||
#current_leader = self.stakeholders[self.current_leader_id]
|
||||
#assert current_leader!=None, 'current leader cant be none'
|
||||
#assert(current_leader.is_leader)
|
||||
self.select_epoch_leaders(sigmas, proofs)
|
||||
leaders, endorsers = self.select_epoch_leaders(sigmas, proofs)
|
||||
return leaders, endorsers
|
||||
|
||||
def broadcast_block(self, signed_block, slot_uid):
|
||||
for stakeholder in self.stakeholders:
|
||||
if not stakeholder.is_leader:
|
||||
stakeholder.receive_block(signed_block, slot_uid)
|
||||
stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
|
||||
self.print_blockchain()
|
||||
|
||||
def encorse_block(self, sig, slot_uid):
|
||||
#TODO commit this step to handshake phases
|
||||
self.current_blk_endorser_sig=sig
|
||||
confirmed = self.stakeholders[self.current_leader_id].confirm_endorsing(sig, slot_uid)
|
||||
if confirmed:
|
||||
self.current_blk_endorser_sig=sig
|
||||
|
||||
|
||||
def start(self):
|
||||
for sh in self.stakeholders:
|
||||
sh(self)
|
||||
@@ -134,4 +166,19 @@ class Z(object):
|
||||
def print_blockchain(self):
|
||||
for sh in self.stakeholders:
|
||||
bc = sh.blockchain
|
||||
self.log.highlight(f"<blockchain> {len(bc)} blocks: "+str(bc))
|
||||
self.log.highlight(f"<blockchain> {len(bc)} blocks: "+str(bc))
|
||||
|
||||
def confirm_endorsing(self, sig, blk_uid):
|
||||
if blk_uid==self.current_slot:
|
||||
self.current_blk_endorser_sig = sig
|
||||
|
||||
def corrupt_leader(self):
|
||||
self.corrupt(self.current_leader_id)
|
||||
|
||||
def corrupt_endorse(self):
|
||||
self.corrupt(self.current_endorser_id)
|
||||
|
||||
def corrupt_blk(self):
|
||||
self.log.warn(f"<corrupt_blk> at slot: {self.current_slot}")
|
||||
self.corrupt_leader()
|
||||
self.corrupt_endorse()
|
||||
@@ -1,4 +1,3 @@
|
||||
#from asyncio.log import logger
|
||||
from ouroboros.block import Block, GensisBlock, EmptyBlock
|
||||
from ouroboros.blockchain import Blockchain
|
||||
from ouroboros.epoch import Epoch
|
||||
@@ -7,13 +6,12 @@ from ouroboros.vrf import verify, VRF
|
||||
from ouroboros.utils import *
|
||||
from ouroboros.logger import Logger
|
||||
from ouroboros.consts import *
|
||||
from copy import deepcopy
|
||||
import time
|
||||
|
||||
'''
|
||||
\class Stakeholder
|
||||
'''
|
||||
|
||||
class Stakeholder(object):
|
||||
|
||||
def __init__(self, epoch_length=2, passwd='password'):
|
||||
#TODO (fix) remove redundant variables reley on environment
|
||||
self.passwd=passwd
|
||||
@@ -38,6 +36,7 @@ class Stakeholder(object):
|
||||
self.am_corrupt=False
|
||||
#
|
||||
self.blockchain=None
|
||||
self.current_blk_endorser_sig = None
|
||||
|
||||
@property
|
||||
def is_leader(self):
|
||||
@@ -78,8 +77,9 @@ class Stakeholder(object):
|
||||
self.tx = self.env.get_genesis_data()
|
||||
self.tx[TX]=self.uncommited_tx
|
||||
self.uncommited_tx=''
|
||||
self.current_block=GensisBlock(self.current_block, self.tx, self.current_slot_uid)
|
||||
self.current_block=GensisBlock(self.current_block, self.tx, self.current_slot_uid, self.env.genesis_time)
|
||||
self.current_epoch=Epoch(self.current_block, self.epoch_length, self.epoch_index, self.env.genesis_time)
|
||||
|
||||
'''
|
||||
it's a callback function, and called by the diffuser
|
||||
'''
|
||||
@@ -100,6 +100,7 @@ class Stakeholder(object):
|
||||
self.__gen_genesis_epoch()
|
||||
if self.am_current_leader:
|
||||
self.broadcast_block()
|
||||
self.am_current_leader=False
|
||||
|
||||
'''
|
||||
it's a callback function, and called by the diffuser
|
||||
@@ -117,7 +118,7 @@ class Stakeholder(object):
|
||||
if not verify(slot, sigma, proof, vrf_pk,vrf_g) :
|
||||
#TODO the leader is corrupted, action to be taken against the corrupt stakeholder
|
||||
#in this case this slot is empty
|
||||
self.current_block=EmptyBlock()
|
||||
self.current_block=EmptyBlock(self.env.genesis_time)
|
||||
if self.current_epoch==None:
|
||||
self.log.warn(f"<new_slot> leader verification fails, current_epoch is None!")
|
||||
self.__gen_genesis_epoch()
|
||||
@@ -127,11 +128,24 @@ class Stakeholder(object):
|
||||
self.log.warn(f"<new_slot> current_epoch is None!")
|
||||
self.__gen_genesis_epoch()
|
||||
self.current_slot_uid = slot
|
||||
prev_blk = self.blockchain[-1] if len(self.blockchain)>0 else EmptyBlock()
|
||||
self.current_block=Block(prev_blk, self.tx, self.current_slot_uid)
|
||||
prev_blk = self.blockchain[-1] if len(self.blockchain)>0 else EmptyBlock(self.env.genesis_time)
|
||||
self.current_block=Block(prev_blk, self.tx, self.current_slot_uid, self.env.genesis_time)
|
||||
self.current_epoch.add_block(self.current_block)
|
||||
if self.am_current_leader:
|
||||
self.broadcast_block()
|
||||
self.end_leadership()
|
||||
elif self.am_current_endorder:
|
||||
self.endorse_block()
|
||||
self.end_endorsing()
|
||||
|
||||
|
||||
def end_leadership(self):
|
||||
self.log(f"stakeholder:{str(self)} ending leadership for slot{self.current_slot_uid}")
|
||||
self.am_current_leader=False
|
||||
|
||||
def end_endorsing(self):
|
||||
self.log(f"stakeholder:{str(self)} ending endorsing for slot{self.current_slot_uid}")
|
||||
self.am_current_endorder=False
|
||||
|
||||
def set_leader(self):
|
||||
self.am_current_leader=True
|
||||
@@ -145,11 +159,22 @@ class Stakeholder(object):
|
||||
def broadcast_block(self):
|
||||
self.log.highlight("broadcasting block")
|
||||
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=None
|
||||
#TODO should wait for l slot until block is endorsed
|
||||
if not self.current_block.endorsed:
|
||||
self.current_block = EmptyBlock(self.env.genesis_time)
|
||||
else:
|
||||
signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
|
||||
self.env.broadcast_block(signed_block, self.current_slot_uid)
|
||||
self.current_block=None
|
||||
|
||||
def endorse_block(self):
|
||||
if not self.am_current_endorder:
|
||||
return
|
||||
sig = sign_message(self.passwd, self.sig_sk, self.current_block)
|
||||
self.env.endorse_block(sig, self.current_slot_uid)
|
||||
|
||||
def receive_block(self, signed_block, blk_uid):
|
||||
self.log.highlight("receiving block")
|
||||
def __get_blk(self, blk_uid):
|
||||
cur_blk = None
|
||||
assert(blk_uid>0)
|
||||
stashed=True
|
||||
@@ -159,9 +184,34 @@ class Stakeholder(object):
|
||||
#TODO this assumes synced blockchain
|
||||
cur_blk = self.blockchain[blk_uid]
|
||||
stashed=False
|
||||
return cur_blk, stashed
|
||||
|
||||
def receive_block(self, signed_block, endorser_sig, blk_uid):
|
||||
self.log.highlight("receiving block")
|
||||
cur_blk, stashed = self.__get_blk(blk_uid)
|
||||
|
||||
#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 verify_signature(self.env.current_leader_sig_pk, cur_blk, signed_block) \
|
||||
and verify_signature(self.env.current_endorser_sig_pk, cur_blk, endorser_sig):
|
||||
if stashed:
|
||||
self.current_epoch.add_block(cur_blk)
|
||||
else:
|
||||
self.env.corrupt(self.env.current_leader_id)
|
||||
self.env.corrupt_blk()
|
||||
|
||||
def confirm_endorsing(self, signed_endorsed_block, blk_uid):
|
||||
confirmed=False
|
||||
if self.am_current_leader:
|
||||
cur_blk, _ = self.__get_blk(blk_uid)
|
||||
if verify_signature(self.env.current_endorser_sig_pk, cur_blk, signed_endorsed_block):
|
||||
if blk_uid==self.current_slot_uid:
|
||||
self.current_block.set_endorsed()
|
||||
else:
|
||||
self.blockchain[blk_uid].set_endorsed()
|
||||
confirmed=True
|
||||
#self.env.confirm_endorsing(signed_endorsed_block, blk_uid)
|
||||
else:
|
||||
confirmed=False
|
||||
#self.env.confirm_endorsing(None, blk_uid)
|
||||
self.log.info(f"<confirm_endorsing enderser signature: {self.current_blk_endorser_sig}")
|
||||
return confirmed
|
||||
|
||||
|
||||
@@ -5,7 +5,29 @@ from ouroboros.utils import inverse_of
|
||||
from ouroboros.utils import vrf_hash
|
||||
eta.init(369)
|
||||
|
||||
|
||||
'''
|
||||
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)
|
||||
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
|
||||
|
||||
class VRF(object):
|
||||
'''
|
||||
verifiable random function implementation
|
||||
@@ -21,7 +43,6 @@ class VRF(object):
|
||||
self.pk = pk
|
||||
self.sk = sk
|
||||
self.g=g
|
||||
|
||||
|
||||
'''
|
||||
short signature without random oracle
|
||||
@@ -58,26 +79,3 @@ class VRF(object):
|
||||
print(f"lhs: {lhs},\nrhs: {rhs}")
|
||||
return False
|
||||
return True
|
||||
|
||||
'''
|
||||
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)
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
from ouroboros import Stakeholder
|
||||
from ouroboros import Z
|
||||
import time
|
||||
|
||||
EPOCH_LENGTH = 2
|
||||
stakeholders = []
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
from dpos.ouroboros import Stakeholder
|
||||
from dpos.ouroboros import Z
|
||||
import random
|
||||
|
||||
EPOCH_LENGTH = 7
|
||||
stakeholders = []
|
||||
for i in range(3):
|
||||
stakeholders.append(Stakeholder)(EPOCH_LENGTH)
|
||||
|
||||
environment = Z(stakeholders, EPOCH_LENGTH)
|
||||
|
||||
environment.start()
|
||||
Reference in New Issue
Block a user