implement support for leader-proofs generated from evolved coins
This commit is contained in:
parent
eebf439a30
commit
5c3de9ab84
|
@ -215,20 +215,31 @@ class LedgerState:
|
||||||
block: Id = None
|
block: Id = None
|
||||||
nonce: Id = None
|
nonce: Id = None
|
||||||
total_stake: int = None
|
total_stake: int = None
|
||||||
commitments: set[Id] = field(default_factory=set) # set of commitments
|
|
||||||
nullifiers: set[Id] = field(default_factory=set) # set of nullified
|
# set of commitments
|
||||||
|
commitments_spend: set[Id] = field(default_factory=set)
|
||||||
|
|
||||||
|
# set of commitments elligible to lead
|
||||||
|
commitments_lead: set[Id] = field(default_factory=set)
|
||||||
|
|
||||||
|
# set of nullified coins
|
||||||
|
nullifiers: set[Id] = field(default_factory=set)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return LedgerState(
|
return LedgerState(
|
||||||
block=self.block,
|
block=self.block,
|
||||||
nonce=self.nonce,
|
nonce=self.nonce,
|
||||||
total_stake=self.total_stake,
|
total_stake=self.total_stake,
|
||||||
commitments=deepcopy(self.commitments),
|
commitments_spend=deepcopy(self.commitments_spend),
|
||||||
|
commitments_lead=deepcopy(self.commitments_lead),
|
||||||
nullifiers=deepcopy(self.nullifiers),
|
nullifiers=deepcopy(self.nullifiers),
|
||||||
)
|
)
|
||||||
|
|
||||||
def verify_committed(self, commitment: Id) -> bool:
|
def verify_elligible_to_spend(self, commitment: Id) -> bool:
|
||||||
return commitment in self.commitments
|
return commitment in self.commitments_spend
|
||||||
|
|
||||||
|
def verify_elligible_to_lead(self, commitment: Id) -> bool:
|
||||||
|
return commitment in self.commitments_lead
|
||||||
|
|
||||||
def verify_unspent(self, nullifier: Id) -> bool:
|
def verify_unspent(self, nullifier: Id) -> bool:
|
||||||
return nullifier not in self.nullifiers
|
return nullifier not in self.nullifiers
|
||||||
|
@ -237,6 +248,8 @@ class LedgerState:
|
||||||
assert block.parent == self.block
|
assert block.parent == self.block
|
||||||
self.block = block.id()
|
self.block = block.id()
|
||||||
self.nullifiers.add(block.leader_proof.nullifier)
|
self.nullifiers.add(block.leader_proof.nullifier)
|
||||||
|
self.commitments_spend.add(block.leader_proof.evolved_commitment)
|
||||||
|
self.commitments_lead.add(block.leader_proof.evolved_commitment)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -250,8 +263,15 @@ class EpochState:
|
||||||
# The nonce snapshot is taken 7k/f slots into the previous epoch
|
# The nonce snapshot is taken 7k/f slots into the previous epoch
|
||||||
nonce_snapshot: LedgerState
|
nonce_snapshot: LedgerState
|
||||||
|
|
||||||
def verify_commitment_is_old_enough_to_lead(self, commitment: Id) -> bool:
|
def verify_elligible_to_lead_due_to_age(self, commitment: Id) -> bool:
|
||||||
return self.stake_distribution_snapshot.verify_committed(commitment)
|
# A coin is elligible to lead if it was committed to before the the stake
|
||||||
|
# distribution snapshot was taken or it was produced by a leader proof since the snapshot was taken.
|
||||||
|
#
|
||||||
|
# This verification is checking that first condition.
|
||||||
|
#
|
||||||
|
# NOTE: `ledger_state.commitments_spend` is a super-set of `ledger_state.commitments_lead`
|
||||||
|
|
||||||
|
return self.stake_distribution_snapshot.verify_elligible_to_spend(commitment)
|
||||||
|
|
||||||
def total_stake(self) -> int:
|
def total_stake(self) -> int:
|
||||||
"""Returns the total stake that will be used to reletivize leadership proofs during this epoch"""
|
"""Returns the total stake that will be used to reletivize leadership proofs during this epoch"""
|
||||||
|
@ -287,7 +307,10 @@ class Follower:
|
||||||
) -> bool:
|
) -> bool:
|
||||||
return (
|
return (
|
||||||
proof.verify(slot) # verify slot leader proof
|
proof.verify(slot) # verify slot leader proof
|
||||||
and epoch_state.verify_commitment_is_old_enough_to_lead(proof.commitment)
|
and (
|
||||||
|
ledger_state.verify_elligible_to_lead(proof.commitment)
|
||||||
|
or epoch_state.verify_elligible_to_lead_due_to_age(proof.commitment)
|
||||||
|
)
|
||||||
and ledger_state.verify_unspent(proof.nullifier)
|
and ledger_state.verify_unspent(proof.nullifier)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@ def mk_genesis_state(initial_stake_distribution: list[Coin]) -> LedgerState:
|
||||||
block=bytes(32),
|
block=bytes(32),
|
||||||
nonce=bytes(32),
|
nonce=bytes(32),
|
||||||
total_stake=sum(c.value for c in initial_stake_distribution),
|
total_stake=sum(c.value for c in initial_stake_distribution),
|
||||||
commitments={c.commitment() for c in initial_stake_distribution},
|
commitments_spend={c.commitment() for c in initial_stake_distribution},
|
||||||
|
commitments_lead={c.commitment() for c in initial_stake_distribution},
|
||||||
nullifiers=set(),
|
nullifiers=set(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -148,10 +149,44 @@ class TestLedgerStateUpdate(TestCase):
|
||||||
block_4 = mk_block(slot=20, parent=block_3.id(), coin=Coin(sk=4, value=100))
|
block_4 = mk_block(slot=20, parent=block_3.id(), coin=Coin(sk=4, value=100))
|
||||||
follower.on_block(block_4)
|
follower.on_block(block_4)
|
||||||
assert follower.tip() == block_3
|
assert follower.tip() == block_3
|
||||||
# then we add the coin to the state associated with slot 9
|
# then we add the coin to "spendable commitments" associated with slot 9
|
||||||
follower.ledger_state[block_2.id()].commitments.add(
|
follower.ledger_state[block_2.id()].commitments_spend.add(
|
||||||
Coin(sk=4, value=100).commitment()
|
Coin(sk=4, value=100).commitment()
|
||||||
)
|
)
|
||||||
follower.on_block(block_4)
|
follower.on_block(block_4)
|
||||||
assert follower.tip() == block_4
|
assert follower.tip() == block_4
|
||||||
assert follower.tip().slot.epoch(follower.config).epoch == 2
|
assert follower.tip().slot.epoch(follower.config).epoch == 2
|
||||||
|
|
||||||
|
def test_evolved_coin_is_elligble_for_leadership(self):
|
||||||
|
coin = Coin(sk=0, value=100)
|
||||||
|
|
||||||
|
genesis = mk_genesis_state([coin])
|
||||||
|
|
||||||
|
config = Config(
|
||||||
|
k=1,
|
||||||
|
active_slot_coeff=1,
|
||||||
|
epoch_stake_distribution_stabilization=4,
|
||||||
|
epoch_period_nonce_buffer=3,
|
||||||
|
epoch_period_nonce_stabilization=3,
|
||||||
|
time=TimeConfig(slot_duration=1, chain_start_time=0),
|
||||||
|
)
|
||||||
|
|
||||||
|
follower = Follower(genesis, config)
|
||||||
|
|
||||||
|
# coin wins the first slot
|
||||||
|
block_1 = mk_block(slot=0, parent=genesis.block, coin=coin)
|
||||||
|
follower.on_block(block_1)
|
||||||
|
|
||||||
|
assert follower.tip_id() == block_1.id()
|
||||||
|
|
||||||
|
# coin can't be reused to win following slots:
|
||||||
|
block_2_reuse = mk_block(slot=1, parent=block_1.id(), coin=coin)
|
||||||
|
follower.on_block(block_2_reuse)
|
||||||
|
|
||||||
|
assert follower.tip_id() == block_1.id()
|
||||||
|
|
||||||
|
# but the evolved coin is elligible
|
||||||
|
block_2_evolve = mk_block(slot=1, parent=block_1.id(), coin=coin.evolve())
|
||||||
|
follower.on_block(block_2_evolve)
|
||||||
|
|
||||||
|
assert follower.tip_id() == block_2_evolve.id()
|
||||||
|
|
Loading…
Reference in New Issue