[research/dpos] reverted from pvss to vrf

This commit is contained in:
mohab
2022-03-20 22:16:02 +02:00
committed by parazyd
parent 7b8e34fd9c
commit ef22eb9977
10 changed files with 1789 additions and 38 deletions

View File

@@ -6,11 +6,9 @@ from ouroboros.logger import Logger
'''
\class TrustedBeacon
the trusted beacon is decentralized, such that at the onset of the Epoch,
the leader of the first slot generated the signed seed, and release the signature,
proof, and base to the ge nesis block.
#TODO implement trustedbeacon as a node
leaky, non-resettable beacon, leaky in the sense that the slots are
predictable, and non-resettable, beacon is basically a synchronized
timestamp.
'''
class TrustedBeacon(SynchedNTPClock, threading.Thread):
@@ -42,7 +40,3 @@ class TrustedBeacon(SynchedNTPClock, threading.Thread):
y, pi = vrf.sign(slot_idx)
rands[slot_idx] = (y,pi)
return rands
'''
def verify(self, y, pi, pk_raw, g):
return VRF.verify(self.current_slot, y, pi, pk_raw, g)
'''

View File

@@ -25,17 +25,27 @@ class Block(object):
self.state=state_hash(previous_block)
self.tx = data
self.sl = slot_uid
self.signature = None # block issuer signature
self.sigma = None # proof that the block is valid.
self.is_genesis=genesis
self.endorsed=False
self.log = Logger(genesis_time)
self.leader_id=None
self.endorser_id=None
@property
def slot(self):
return self.sl
def __repr__(self):
if self.is_genesis:
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)+"}"
return "GensisBlock " + str(self.__to_dict)
return "Block " + str(self.__to_dict)
@property
def __to_dict(self):
return {SLOT: self.st, STATE: self.state, DATA: self.tx, PROOF: self.sigma, SIGN: self.signature}
def __hash__(self):
if type(self.tx)==str:
return hash((self.state, self.tx, self.sl))
@@ -50,9 +60,7 @@ class Block(object):
self.sl == block.sl
def to_json(self):
d = {'state':self.state, \
'data': self.tx, \
'sl': self.sl}
d = self.__to_dict
return json.encoder(d)
def set_endorsed(self):
@@ -65,6 +73,12 @@ class Block(object):
def set_leader(self, id):
self.leader_id=id
def set_sigma(self, sigma):
self.sigma = sigma
def set_signature(self, sign):
self.signature = sign
@property
def data(self):
return self.tx
@@ -78,7 +92,7 @@ class Block(object):
return (self.tx=='' or self.slot<0) and self.state==''
def encode(self):
return str(self).encode()
return str(self.__to_dict).encode()
class GensisBlock(Block):
'''

View File

@@ -8,7 +8,7 @@ import math
class SynchedNTPClock(object):
def __init__(self, epoch_length, slot_length=180, ntp_server='europe.pool.ntp.org'):
def __init__(self, epoch_length, slot_length=60, ntp_server='europe.pool.ntp.org'):
#TODO how long should be the slot length
self.epoch_length=epoch_length # how many slots in a a block
self.slot_length=slot_length

View File

@@ -1,4 +1,16 @@
STAKEHOLDERS_DISTRIBUTIONS='distributions'
STAKEHOLDERS='stakeholders'
SEED='seed'
TX='tx'
TX='tx'
# block keys
'''
the block in praos is consistent of the following
(sl, st,d, B_pi, sigma)
'''
SLOT = "SLOT" # sl
STATE = "STATE" #st
DATA = "DATA" #d
PROOF = "PROOF" #B_pi
SIGN = "SIGNATURE" #sigma

View File

@@ -17,7 +17,7 @@ from ouroboros.stakeholder import Stakeholder
environment is ought to interfece with the network
'''
class Z(object):
def __init__(self, stakeholdes, epoch_length, genesis_time=time.time()):
def __init__(self, stakeholdes, epoch_length, genesis_time=time.time(), f=0.1):
self.genesis_time=genesis_time
self.beacon = TrustedBeacon(epoch_length, genesis_time)
self.log = Logger(self, genesis_time)
@@ -41,8 +41,19 @@ class Z(object):
self.prev_leader_id=-1
#
self.current_block=None
self.f = f
self.init()
'''
active slot coefficient determine the relation between,
probability of leader being selected, and the relative stake,
it's defined as the probability that a party holding all the stake
will be selected to be a leader for given slot.
'''
@property
def slot_coef(self):
return self.f
def init(self):
for sh in self.stakeholders:
@@ -51,9 +62,10 @@ class Z(object):
#pick initial leader to be the first stakeholder
initial_leader = self.stakeholders[0]
#pick initial endorser to be the first endorser
initial_endorser = self.stakeholders[1]
#initial_endorser = self.stakeholders[1]
self.current_epoch = self.beacon.epoch
self.rands = self.beacon.next_epoch_seeds(initial_leader.vrf)
self.log.info(f"rands length: {len(self.rands)}")
self.current_slot = self.beacon.slot
self.select_epoch_leaders()
self.prev_leader_id=0
@@ -116,9 +128,15 @@ class Z(object):
def __repr__(self):
buff= f"envirnment of {self.length} stakholders\tcurrent leader's id: {self.current_leader_id}\tepoch_slot: {self.epoch_slot}\tendorser_id: {self.current_endorser_id}"
for sh in self.stakeholders:
buff+=str(sh)+"\n"
buff = ''
if len (self.slot_committee)>0:
buff = f"envirnment of {self.length} stakholders\tcurrent leader's id: \
{self.current_leader_id}\tepoch_slot: {self.epoch_slot}\tendorser_id: \
{self.current_endorser_id}"
for sh in self.stakeholders:
buff+=str(sh)+"\n"
else:
buff = f"envirnment of {self.length} stakholders\tepoch_slot: {self.epoch_slot}"
return buff
'''
@@ -163,7 +181,7 @@ class Z(object):
@property
def current_leader_id(self):
return self.slot_committee[self.current_slot][0]
return self.slot_committee[self.epoch_slot][0]
@property
def current_stakeholder(self):
@@ -172,7 +190,7 @@ class Z(object):
@property
def current_endorser_id(self):
return self.slot_committee[self.current_slot][1]
return self.slot_committee[self.epoch_slot][1]
@property
def current_endorser_uid(self):
@@ -197,7 +215,7 @@ class Z(object):
return self.endorser(slot).vrf_pk
def is_current_endorser(self, id):
_, edr_idx = self.slot_committee[self.beacon.slot]
_, edr_idx = self.slot_committee[self.epoch_slot]
return id == self.stakeholders[edr_idx].id
@property
@@ -209,7 +227,7 @@ class Z(object):
return self.stakeholders[self.current_leader_id].vrf_base
def is_current_leader(self, id):
ldr_idx, _ = self.slot_committee[self.beacon.current_slot]
ldr_idx, _ = self.slot_committee[self.epoch_slot]
return id == self.stakeholders[ldr_idx].id
@@ -230,8 +248,6 @@ class Z(object):
def current_leader_sig_pk(self):
return self.stakeholders[self.current_leader_id].sig_pk
#note! assumes epoch_slot lays in the current epoch
def leader(self, slot):
return self.stakeholders[self.slot_committee[slot][0]]
@@ -301,11 +317,14 @@ class Z(object):
return self.beacon.epoch
def select_epoch_leaders(self):
self.log.info("[Z/select_epoch_leaders]")
#assert len(self.sigmas)==self.epoch_length and len(self.proofs)==self.epoch_length, \
#self.log.error(f"size mismatch between sigmas: {len(self.sigmas)}, proofs: {len(self.proofs)}, and epoch_length: {self.epoch_length}")
for i in range(self.epoch_length):
#self.log.info(f"current sigma of index {i} , epoch_length: {self.epoch_length}")
self.log.info(f"current sigma of index {i} , epoch_length: {self.epoch_length}, rand : {self.rands}")
slot_idx = self.current_slot + i
assert len(self.rands)>0
slot_idx_relative = slot_idx%self.epoch_length
sigma, _ = self.rands[slot_idx]
assert sigma!=None, 'proof cant be None'
def leader_selection_hash(sigma):
@@ -324,11 +343,11 @@ class Z(object):
# under a single condition that no one is able to predict who is next
assert not leader_idx==endorser_idx
#TODO move leader/endorser to a dictionary
self.slot_committee[slot_idx] = (leader_idx, endorser_idx)
self.slot_committee[slot_idx_relative] = (leader_idx, endorser_idx)
self.log.highlight(f'slot {slot_idx} has committee leader/endorser {leader_idx}/{endorser_idx}\nleader: {self.stakeholders[leader_idx]}\nendorser: {self.stakeholders[endorser_idx]}')
self.epoch_initialized[str(self.epoch)] = True
def broadcast_block(self, cur_block, signed_block, slot_uid):
def broadcast_block(self, cur_block):
while self.current_blk_endorser_sig is None:
self.log.info('pending endorsing...')
time.sleep(1)
@@ -337,11 +356,10 @@ class Z(object):
self.current_block = cur_block
for stakeholder in self.stakeholders:
if not stakeholder.is_leader:
stakeholder.receive_block(signed_block, self.current_blk_endorser_sig, slot_uid)
stakeholder.receive_block(cur_block, self.current_blk_endorser_sig)
#stakeholder.receive_block(cur_block.signature, self.current_blk_endorser_sig, cur_block.slot)
self.print_blockchain()
@property
def block_id(self):
return self.current_slot%self.epoch_length

View File

@@ -1,5 +1,6 @@
from copy import deepcopy
import time
import numpy as np
from ouroboros.block import Block, GensisBlock, EmptyBlock
from ouroboros.blockchain import Blockchain
from ouroboros.epoch import Epoch
@@ -9,6 +10,7 @@ from ouroboros.logger import Logger
from ouroboros.consts import *
from ouroboros.data import Data, Transaction, Item
'''
\class Stakeholder
'''
@@ -53,7 +55,21 @@ class Stakeholder(object):
@property
def vrf_base(self):
return self.__vrf_base
@property
def probability_leader_election(self):
return 1 - np.pow(1 - self.env.slot_coef, self.stake)
'''
true random oracle from the blockchain
it's simulate by a hash function that is modeled as a random oracle. This hash function
is applied to the concatenation of VRF values that are inserted into each block, using values from
all blocks up to and including the middle ~ 8k slots of an epoch that lasts approximately 24k slots
in entirety
'''
def random_oracle(self):
pass
def __repr__(self):
buff=''
if self.env.is_current_leader(self.id):
@@ -154,7 +170,8 @@ class Stakeholder(object):
self.current_block = EmptyBlock(self.env.genesis_time)
'''
signed_block = sign_message(self.passwd, self.sig_sk, self.current_block)
self.env.broadcast_block(self.current_block, signed_block, self.current_slot_uid)
self.current_block.set_signature(signed_block)
self.env.broadcast_block(self.current_block)
@property
def current_slot(self):
@@ -198,14 +215,14 @@ class Stakeholder(object):
time.sleep(1)
return cur_blk, stashed
def receive_block(self, signed_block, endorser_sig, blk_uid):
def receive_block(self, blk, endorser_sig):
self.log.highlight("receiving block")
cur_blk, stashed = self.__get_blk(blk_uid)
cur_blk, stashed = self.__get_blk(blk.slot)
#TODO to consider deley should retrive leader_pk of corresponding blk_uid
self.log.highlight(f'receiving block {str(cur_blk)}')
self.log.highlight(f'receiving block has slot_uid: {self.current_slot_uid}')
self.log.highlight(f'receiving block has sig_pk: {self.env.current_endorser_sig_pk}')
blk_verified = verify_signature(self.env.current_leader_sig_pk, cur_blk, signed_block)
blk_verified = verify_signature(self.env.current_leader_sig_pk, cur_blk, blk.signature)
self.log.info("endorser sig_pk {self.env.current_endorser_sig_pk}, cur_blk: {cur_blk}, endorser_sig: {endorser_sig}")
blk_edrs_verified = verify_signature(self.env.current_endorser_sig_pk, cur_blk, endorser_sig)
if blk_verified and blk_edrs_verified:

View File

View File

@@ -0,0 +1 @@
target/*

1695
script/research/streamlet_rust/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff