nim-mysticeti/mysticeti/validator.nim

189 lines
6.6 KiB
Nim
Raw Permalink Normal View History

2024-08-20 10:45:49 +02:00
import ./basics
2024-09-19 10:32:39 +02:00
import ./committee
import ./blocks
import ./validator/slots
2024-10-17 14:35:50 +02:00
import ./validator/rounds
import ./validator/checks
export slots
export checks
2024-08-20 10:45:49 +02:00
type Validator*[Dependencies] = ref object
identifier: Dependencies.Identifier
2024-11-27 11:48:50 +01:00
committee: Committee[Dependencies.Identifier]
2024-10-17 16:06:50 +02:00
membership: CommitteeMember
rounds: Rounds[Dependencies]
clockThreshold: Voting
2024-09-30 16:22:17 +02:00
2024-11-27 11:48:50 +01:00
func new*[Dependencies](
_: type Validator[Dependencies],
identifier: Dependencies.Identifier,
2024-11-27 11:48:50 +01:00
committee: Committee[Dependencies.Identifier]
): Validator[Dependencies] =
without membership =? committee.membership(identifier):
raiseAssert "identity is not a member of the committee"
Validator[Dependencies](
identifier: identifier,
2024-09-25 11:41:48 +02:00
committee: committee,
membership: membership,
2024-11-27 11:48:50 +01:00
rounds: Rounds[Dependencies].init(committee.size)
2024-09-25 11:41:48 +02:00
)
2024-09-18 11:42:59 +02:00
2024-08-21 11:44:23 +02:00
func identifier*(validator: Validator): auto =
validator.identifier
2024-08-20 10:45:49 +02:00
2024-09-25 11:41:48 +02:00
func membership*(validator: Validator): CommitteeMember =
validator.membership
2024-08-21 11:44:23 +02:00
func round*(validator: Validator): uint64 =
validator.rounds.latest.number
2024-08-20 14:43:46 +02:00
2024-11-26 10:59:04 +01:00
func primaryProposer*(validator: Validator): CommitteeMember =
validator.rounds.latest.primaryProposer
2024-12-09 18:07:43 +01:00
func updateSkipped(validator: Validator, supporter: Validator.Dependencies.Block) =
func skips(blck: Validator.Dependencies.Block, round: uint64, author: CommitteeMember): bool =
for parent in blck.parents:
if parent.round == round and parent.author == author:
return false
true
2024-10-30 10:45:54 +01:00
if round =? validator.rounds.latest.find(supporter.round) and
previous =? round.previous:
for proposer in previous.proposers:
let slot = previous[proposer]
if supporter.skips(previous.number, proposer):
let author = supporter.author
let stake = validator.committee.stake(author)
slot.skipBy(author, stake)
2024-12-09 18:07:43 +01:00
func updateCertified(validator: Validator, certificate: Validator.Dependencies.Block) =
mixin id
2024-10-30 11:25:24 +01:00
without certifying =? validator.rounds.latest.find(certificate.round) and
voting =? certifying.previous and
proposing =? voting.previous:
return
2024-10-07 16:31:16 +02:00
for proposal in proposing.proposals:
var support: Voting
2024-10-07 16:31:16 +02:00
for vote in voting.proposals:
if proposal.blck.id in vote.blck.parents:
if vote.blck.id in certificate.parents:
let author = vote.blck.author
let stake = validator.committee.stake(author)
support.add(author, stake)
if support.stake > 2/3:
let stake = validator.committee.stake(certificate.author)
proposal.certifyBy(certificate.id, stake)
func updateRound(validator: Validator, blck: Validator.Dependencies.Block) =
if blck.round == validator.round:
let author = blck.author
let stake = validator.committee.stake(author)
validator.clockThreshold.add(author, stake)
if validator.clockThreshold.stake > 2/3:
validator.rounds.addNewRound()
validator.clockThreshold.reset()
func addBlock(validator: Validator, signedBlock: SignedBlock) =
let blck = signedBlock.blck
if round =? validator.rounds.latest.find(blck.round):
round.addProposal(signedBlock)
validator.updateSkipped(blck)
validator.updateCertified(blck)
validator.updateRound(blck)
func parentBlocks*(validator: Validator): auto =
2024-12-09 18:07:43 +01:00
mixin id
2024-12-10 12:28:16 +01:00
type Block = Validator.Dependencies.Block
type BlockId = typeof(Block.default.id)
var parents: seq[BlockId]
if previous =? validator.rounds.latest.previous:
for slot in previous.slots:
if slot.proposals.len > 0:
parents.add(slot.proposals[0].blck.id)
parents
func check*(validator: Validator, signed: SignedBlock): auto =
2024-12-09 18:07:43 +01:00
mixin id
2024-12-10 12:28:16 +01:00
type BlockCheck = checks.BlockCheck[Validator.Dependencies]
type Block = Validator.Dependencies.Block
type BlockId = typeof(Block.default.id)
if not signed.verifySignature():
return BlockCheck.invalid("block signature is incorrect")
2024-10-22 16:26:12 +02:00
without member =? validator.committee.membership(signed.signer):
return BlockCheck.invalid("block is not signed by a committee member")
2024-10-22 16:26:12 +02:00
if member != signed.blck.author:
return BlockCheck.invalid("block is not signed by its author")
2024-10-22 16:26:12 +02:00
for parent in signed.blck.parents:
if parent.round >= signed.blck.round:
return BlockCheck.invalid("block has a parent from an invalid round")
for i in 0..<signed.blck.parents.len:
for j in 0..<i:
if signed.blck.parents[i] == signed.blck.parents[j]:
return BlockCheck.invalid("block includes a parent more than once")
if signed.blck.round > 0:
var stake: Stake
for parent in signed.blck.parents:
if parent.round == signed.blck.round - 1:
stake += validator.committee.stake(parent.author)
if stake <= 2/3:
return BlockCheck.invalid(
"block does not include parents representing >2/3 stake from previous round"
)
var missing: seq[BlockId]
for parent in signed.blck.parents:
if parent.round >= validator.rounds.oldest.number:
if validator.rounds.latest.find(parent).isNone:
missing.add(parent)
if missing.len > 0:
return BlockCheck.incomplete(missing)
if validator.rounds.latest.find(signed.blck.id).isSome:
return BlockCheck.invalid("block already received")
BlockCheck.correct(signed)
func add*(validator: Validator, correct: CorrectBlock) =
validator.addBlock(correct.signedBlock)
func getBlock*(validator: Validator, id: BlockId): auto =
validator.rounds.latest.find(id)
2024-08-20 10:45:49 +02:00
2024-10-21 11:05:20 +02:00
func status*(validator: Validator, round: uint64, author: CommitteeMember): auto =
if round =? validator.rounds.oldest.find(round):
return some round[author].status
2024-09-25 11:25:20 +02:00
func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
2024-10-17 16:03:20 +02:00
without anchor =? round.findAnchor():
return
without anchorProposal =? anchor.proposal:
return
2024-12-09 18:07:43 +01:00
var todo = anchorProposal.blck.parents
while todo.len > 0:
let parent = todo.pop()
if parent.round < round.number + 2:
continue
for slotProposal in slot.proposals:
if parent in slotProposal.certificates:
slotProposal.certify(anchorProposal)
return
2024-10-17 16:06:28 +02:00
without parentBlock =? round.find(parent):
raiseAssert "parent block not found"
2024-12-09 18:07:43 +01:00
todo.add(parentBlock.blck.parents)
slot.skip()
2024-09-25 11:25:20 +02:00
iterator committed*(validator: Validator): auto =
var done = false
while not done:
let round = validator.rounds.oldest
for slot in round.slots:
if slot.status == SlotStatus.undecided:
validator.updateIndirect(slot, round)
2024-09-25 11:25:20 +02:00
case slot.status
2024-09-30 15:25:09 +02:00
of SlotStatus.undecided:
2024-09-25 11:25:20 +02:00
done = true
break
2024-09-30 15:25:09 +02:00
of SlotStatus.skip, SlotStatus.committed:
2024-09-25 11:25:20 +02:00
discard
2024-09-30 15:25:09 +02:00
of SlotStatus.commit:
yield slot.commit()
if not done:
validator.rounds.removeOldestRound()