Leader VRF uses coin nonce as well as sk (#68)

* standardize endianness to "big"

* slot.encode()

* include coin nonce in leader election VRF

---------

Co-authored-by: David Rusu <davidrusu@Davids-MacBook-Pro.local>
This commit is contained in:
davidrusu 2024-02-07 18:28:36 +04:00 committed by GitHub
parent 6f05392693
commit cde1e92c9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 19 deletions

View File

@ -70,6 +70,9 @@ class Slot:
def epoch(self, config: Config) -> Epoch: def epoch(self, config: Config) -> Epoch:
return Epoch(self.absolute_slot // config.epoch_length) return Epoch(self.absolute_slot // config.epoch_length)
def encode(self) -> bytes:
return int.to_bytes(self.absolute_slot, length=8, byteorder="big")
def __eq__(self, other): def __eq__(self, other):
return self.absolute_slot == other.absolute_slot return self.absolute_slot == other.absolute_slot
@ -84,15 +87,19 @@ class Coin:
nonce: bytes = bytes(32) nonce: bytes = bytes(32)
@property @property
def pk(self): def pk(self) -> int:
return self.sk return self.sk
def evolve(self) -> "Coin": def encode_sk(self) -> bytes:
sk_bytes = int.to_bytes(self.sk, length=32, byteorder="little") return int.to_bytes(self.sk, length=32, byteorder="big")
def encode_pk(self) -> bytes:
return int.to_bytes(self.pk, length=32, byteorder="big")
def evolve(self) -> "Coin":
h = blake2b(digest_size=32) h = blake2b(digest_size=32)
h.update(b"coin-evolve") h.update(b"coin-evolve")
h.update(sk_bytes) h.update(self.encode_sk())
h.update(self.nonce) h.update(self.nonce)
evolved_nonce = h.digest() evolved_nonce = h.digest()
@ -100,25 +107,23 @@ class Coin:
def commitment(self) -> Id: def commitment(self) -> Id:
# TODO: mocked until CL is understood # TODO: mocked until CL is understood
pk_bytes = int.to_bytes(self.pk, length=32, byteorder="little") value_bytes = int.to_bytes(self.value, length=32, byteorder="big")
value_bytes = int.to_bytes(self.value, length=32, byteorder="little")
h = sha256() h = sha256()
h.update(b"coin-commitment") h.update(b"coin-commitment")
h.update(self.nonce) h.update(self.nonce)
h.update(pk_bytes) h.update(self.encode_pk())
h.update(value_bytes) h.update(value_bytes)
return h.digest() return h.digest()
def nullifier(self) -> Id: def nullifier(self) -> Id:
# TODO: mocked until CL is understood # TODO: mocked until CL is understood
pk_bytes = int.to_bytes(self.pk, length=32, byteorder="little") value_bytes = int.to_bytes(self.value, length=32, byteorder="big")
value_bytes = int.to_bytes(self.value, length=32, byteorder="little")
h = sha256() h = sha256()
h.update(b"coin-nullifier") h.update(b"coin-nullifier")
h.update(self.nonce) h.update(self.nonce)
h.update(pk_bytes) h.update(self.encode_pk())
h.update(value_bytes) h.update(value_bytes)
return h.digest() return h.digest()
@ -171,7 +176,7 @@ class BlockHeader:
h.update(self.content_id) h.update(self.content_id)
# slot # slot
h.update(int.to_bytes(self.slot.absolute_slot, length=8, byteorder="big")) h.update(self.slot.encode())
# parent # parent
assert len(self.parent) == 32 assert len(self.parent) == 32
@ -256,7 +261,7 @@ class LedgerState:
h.update("epoch-nonce".encode(encoding="utf-8")) h.update("epoch-nonce".encode(encoding="utf-8"))
h.update(self.nonce) h.update(self.nonce)
h.update(block.leader_proof.nullifier) h.update(block.leader_proof.nullifier)
h.update(block.slot.absolute_slot.to_bytes(8, byteorder="big")) h.update(block.slot.encode())
self.nonce = h.digest() self.nonce = h.digest()
self.block = block.id() self.block = block.id()
@ -442,11 +447,14 @@ class MOCK_LEADER_VRF:
ORDER = 2**256 ORDER = 2**256
@classmethod @classmethod
def vrf(cls, sk: int, nonce: bytes, slot: int) -> int: def vrf(cls, coin: Coin, epoch_nonce: bytes, slot: Slot) -> int:
h = sha256() h = sha256()
h.update(int.to_bytes(sk, length=32)) h.update(b"lead")
h.update(nonce) h.update(epoch_nonce)
h.update(int.to_bytes(slot, length=16)) # 64bit slots h.update(slot.encode())
h.update(coin.encode_sk())
h.update(coin.nonce)
return int.from_bytes(h.digest()) return int.from_bytes(h.digest())
@classmethod @classmethod
@ -471,7 +479,7 @@ class Leader:
def _is_slot_leader(self, epoch: EpochState, slot: Slot): def _is_slot_leader(self, epoch: EpochState, slot: Slot):
relative_stake = self.coin.value / epoch.total_stake() relative_stake = self.coin.value / epoch.total_stake()
r = MOCK_LEADER_VRF.vrf(self.coin.pk, epoch.nonce(), slot) r = MOCK_LEADER_VRF.vrf(self.coin, epoch.nonce(), slot)
return r < MOCK_LEADER_VRF.ORDER * phi( return r < MOCK_LEADER_VRF.ORDER * phi(
self.config.active_slot_coeff, relative_stake self.config.active_slot_coeff, relative_stake

View File

@ -2,7 +2,16 @@ from unittest import TestCase
import numpy as np import numpy as np
from .cryptarchia import Leader, Config, EpochState, LedgerState, Coin, phi, TimeConfig from .cryptarchia import (
Leader,
Config,
EpochState,
LedgerState,
Coin,
phi,
TimeConfig,
Slot,
)
class TestLeader(TestCase): class TestLeader(TestCase):
@ -35,7 +44,10 @@ class TestLeader(TestCase):
# After N slots, the measured leader rate should be within the interval `p +- margin_of_error` with high probabiltiy # After N slots, the measured leader rate should be within the interval `p +- margin_of_error` with high probabiltiy
leader_rate = ( leader_rate = (
sum(l.try_prove_slot_leader(epoch, slot) is not None for slot in range(N)) sum(
l.try_prove_slot_leader(epoch, Slot(slot)) is not None
for slot in range(N)
)
/ N / N
) )
assert ( assert (