mirror of
https://github.com/logos-co/nomos-specs.git
synced 2025-01-09 23:26:01 +00:00
34617dc911
* Random Beacon revision This is a proposed revision of the random beacon specification. First of all it fixes a few little mistakes in the signing process: * Use BasicSchemeMPL instead of PoPSchemeMPL since we don't use Proof of Possession. * Hashing the values prior to the call to BasicSchemeMPL.sing() is not necessary. This step has been removed. In addition, all data inside the random beacon state that anyone willing to verify must know anyway has been removed. In the current version this includes the 'context' and the public key of the signer. The verifier has to independently check that those values have been correctly obtained anyway, so there's no need to include them in the state that is passed around. Lastly, the beacon context view has been changed from using a string encoding to a little endian variable-length encoding and is now tied to qc.view instead of current_view of the processing node. * actually use the version const
92 lines
2.7 KiB
Python
92 lines
2.7 KiB
Python
# typing imports
|
|
from dataclasses import dataclass
|
|
from random import randint
|
|
from typing import TypeAlias
|
|
|
|
# carnot imports
|
|
# lib imports
|
|
from blspy import PrivateKey, Util, BasicSchemeMPL, G2Element, G1Element
|
|
|
|
# stdlib imports
|
|
from hashlib import sha256
|
|
|
|
View: TypeAlias = int
|
|
Sig: TypeAlias = bytes
|
|
Entropy: TypeAlias = bytes
|
|
PublicKey: TypeAlias = G1Element
|
|
VERSION = 0
|
|
|
|
def generate_random_sk() -> PrivateKey:
|
|
seed = bytes([randint(0, 255) for _ in range(32)])
|
|
return BasicSchemeMPL.key_gen(seed)
|
|
|
|
|
|
def view_to_bytes(view: View) -> bytes:
|
|
return view.to_bytes((view.bit_length() + 7) // 8, byteorder='little', signed=True)
|
|
|
|
@dataclass
|
|
class RandomBeacon:
|
|
version: int
|
|
sig: Sig
|
|
|
|
def entropy(self) -> Entropy:
|
|
return self.sig
|
|
|
|
|
|
class NormalMode:
|
|
|
|
@staticmethod
|
|
def verify(beacon: RandomBeacon, pk: PublicKey, view: View) -> bool:
|
|
"""
|
|
:param beacon: the provided beacon
|
|
:param view: view to verify beacon upon
|
|
:param pk: public key of the issuer of the beacon
|
|
:return:
|
|
"""
|
|
sig = G2Element.from_bytes(beacon.sig)
|
|
return BasicSchemeMPL.verify(pk, view_to_bytes(view), sig)
|
|
|
|
@staticmethod
|
|
def generate_beacon(private_key: PrivateKey, view: View) -> RandomBeacon:
|
|
return RandomBeacon(VERSION, bytes(BasicSchemeMPL.sign(private_key, view_to_bytes(view))))
|
|
|
|
|
|
class RecoveryMode:
|
|
|
|
@staticmethod
|
|
def verify(last_beacon: RandomBeacon, beacon: RandomBeacon, view: View) -> bool:
|
|
"""
|
|
:param last_beacon: beacon for view - 1
|
|
:param beacon: beacon for view
|
|
:param view: the view to verify beacon upon
|
|
:return:
|
|
"""
|
|
b = sha256(last_beacon.entropy() + view_to_bytes(view)).digest()
|
|
return b == beacon.entropy()
|
|
|
|
@staticmethod
|
|
def generate_beacon(last_beacon_entropy: Entropy, view: View) -> RandomBeacon:
|
|
return RandomBeacon(VERSION, sha256(last_beacon_entropy + view_to_bytes(view)).digest())
|
|
|
|
|
|
class RandomBeaconHandler:
|
|
def __init__(self, beacon: RandomBeacon):
|
|
"""
|
|
:param beacon: Beacon should be initialized with either the last known working beacon from recovery.
|
|
Or the hash of the genesis block in case of first consensus round.
|
|
:return: Self
|
|
"""
|
|
self.last_beacon: RandomBeacon = beacon
|
|
|
|
def verify_happy(self, new_beacon: RandomBeacon, pk: PublicKey, view: View) -> bool:
|
|
if NormalMode.verify(new_beacon, pk, view):
|
|
self.last_beacon = new_beacon
|
|
return True
|
|
return False
|
|
|
|
def verify_unhappy(self, new_beacon: RandomBeacon, view: View) -> bool:
|
|
if RecoveryMode.verify(self.last_beacon, new_beacon, view):
|
|
self.last_beacon = new_beacon
|
|
return True
|
|
return False
|