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:
parent
6f05392693
commit
cde1e92c9e
|
@ -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
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
Loading…
Reference in New Issue