2024-08-20 10:45:49 +02:00
|
|
|
import ./basics
|
2024-09-19 10:32:39 +02:00
|
|
|
import ./committee
|
2024-08-20 14:40:02 +02:00
|
|
|
import ./blocks
|
2024-10-14 12:37:32 +02:00
|
|
|
import ./validator/slots
|
2024-10-17 14:35:50 +02:00
|
|
|
import ./validator/rounds
|
2024-10-28 12:54:36 +01:00
|
|
|
import ./validator/checks
|
2024-10-14 12:37:32 +02:00
|
|
|
|
|
|
|
|
export slots
|
2024-10-28 12:54:36 +01:00
|
|
|
export checks
|
2024-08-20 10:45:49 +02:00
|
|
|
|
2024-11-06 13:45:37 +01:00
|
|
|
type Validator*[Dependencies] = ref object
|
2024-12-09 16:02:04 +01:00
|
|
|
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
|
2024-11-06 13:45:37 +01:00
|
|
|
rounds: Rounds[Dependencies]
|
2024-12-11 14:22:39 +01:00
|
|
|
clockThreshold: Voting
|
2024-09-30 16:22:17 +02:00
|
|
|
|
2024-11-27 11:48:50 +01:00
|
|
|
func new*[Dependencies](
|
|
|
|
|
_: type Validator[Dependencies],
|
2024-12-09 16:02:04 +01:00
|
|
|
identifier: Dependencies.Identifier,
|
2024-11-27 11:48:50 +01:00
|
|
|
committee: Committee[Dependencies.Identifier]
|
2024-11-28 14:24:35 +01:00
|
|
|
): Validator[Dependencies] =
|
2024-12-09 16:02:04 +01:00
|
|
|
without membership =? committee.membership(identifier):
|
2024-11-28 14:24:35 +01:00
|
|
|
raiseAssert "identity is not a member of the committee"
|
|
|
|
|
Validator[Dependencies](
|
2024-12-09 16:02:04 +01:00
|
|
|
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 =
|
2024-12-09 16:02:04 +01:00
|
|
|
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 =
|
2024-10-21 10:29:41 +02:00
|
|
|
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:
|
2024-11-26 10:44:13 +01:00
|
|
|
for proposer in previous.proposers:
|
|
|
|
|
let slot = previous[proposer]
|
|
|
|
|
if supporter.skips(previous.number, proposer):
|
2024-10-30 14:50:00 +01:00
|
|
|
let author = supporter.author
|
|
|
|
|
let stake = validator.committee.stake(author)
|
|
|
|
|
slot.skipBy(author, stake)
|
2024-09-18 15:41:53 +02:00
|
|
|
|
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:
|
2024-09-19 10:08:42 +02:00
|
|
|
return
|
2024-10-07 16:31:16 +02:00
|
|
|
for proposal in proposing.proposals:
|
2024-10-30 14:50:00 +01:00
|
|
|
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:
|
2024-10-30 14:50:00 +01:00
|
|
|
let author = vote.blck.author
|
|
|
|
|
let stake = validator.committee.stake(author)
|
|
|
|
|
support.add(author, stake)
|
|
|
|
|
if support.stake > 2/3:
|
2024-10-14 12:37:32 +02:00
|
|
|
let stake = validator.committee.stake(certificate.author)
|
|
|
|
|
proposal.certifyBy(certificate.id, stake)
|
2024-09-19 10:08:42 +02:00
|
|
|
|
2024-12-11 14:22:39 +01:00
|
|
|
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()
|
|
|
|
|
|
2024-11-05 14:38:43 +01:00
|
|
|
func addBlock(validator: Validator, signedBlock: SignedBlock) =
|
2024-12-11 14:22:39 +01:00
|
|
|
let blck = signedBlock.blck
|
|
|
|
|
if round =? validator.rounds.latest.find(blck.round):
|
2024-11-05 14:38:43 +01:00
|
|
|
round.addProposal(signedBlock)
|
2024-12-11 14:22:39 +01:00
|
|
|
validator.updateSkipped(blck)
|
|
|
|
|
validator.updateCertified(blck)
|
|
|
|
|
validator.updateRound(blck)
|
2024-11-05 14:38:43 +01:00
|
|
|
|
2024-12-09 15:58:51 +01:00
|
|
|
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]
|
2024-12-09 15:58:51 +01:00
|
|
|
if previous =? validator.rounds.latest.previous:
|
2024-10-17 16:00:22 +02:00
|
|
|
for slot in previous.slots:
|
2024-12-11 15:12:55 +01:00
|
|
|
if slot.proposals.len > 0:
|
2024-12-09 15:58:51 +01:00
|
|
|
parents.add(slot.proposals[0].blck.id)
|
|
|
|
|
parents
|
2024-09-19 10:15:41 +02:00
|
|
|
|
2024-10-28 12:54:36 +01:00
|
|
|
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)
|
2024-12-11 10:53:36 +01:00
|
|
|
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):
|
2024-10-28 12:54:36 +01:00
|
|
|
return BlockCheck.invalid("block is not signed by a committee member")
|
2024-10-22 16:26:12 +02:00
|
|
|
if member != signed.blck.author:
|
2024-10-28 12:54:36 +01:00
|
|
|
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:
|
2024-10-28 12:54:36 +01:00
|
|
|
return BlockCheck.invalid("block has a parent from an invalid round")
|
2024-10-24 15:00:13 +02:00
|
|
|
for i in 0..<signed.blck.parents.len:
|
|
|
|
|
for j in 0..<i:
|
|
|
|
|
if signed.blck.parents[i] == signed.blck.parents[j]:
|
2024-10-28 12:54:36 +01:00
|
|
|
return BlockCheck.invalid("block includes a parent more than once")
|
2024-10-24 16:18:00 +02:00
|
|
|
if signed.blck.round > 0:
|
|
|
|
|
var stake: Stake
|
|
|
|
|
for parent in signed.blck.parents:
|
2024-10-29 12:19:20 +01:00
|
|
|
if parent.round == signed.blck.round - 1:
|
2024-10-24 16:18:00 +02:00
|
|
|
stake += validator.committee.stake(parent.author)
|
|
|
|
|
if stake <= 2/3:
|
2024-10-28 12:54:36 +01:00
|
|
|
return BlockCheck.invalid(
|
|
|
|
|
"block does not include parents representing >2/3 stake from previous round"
|
|
|
|
|
)
|
2024-10-29 12:19:20 +01:00
|
|
|
var missing: seq[BlockId]
|
|
|
|
|
for parent in signed.blck.parents:
|
2024-10-29 12:58:22 +01:00
|
|
|
if parent.round >= validator.rounds.oldest.number:
|
|
|
|
|
if validator.rounds.latest.find(parent).isNone:
|
|
|
|
|
missing.add(parent)
|
2024-10-29 12:19:20 +01:00
|
|
|
if missing.len > 0:
|
|
|
|
|
return BlockCheck.incomplete(missing)
|
2024-10-29 13:05:38 +01:00
|
|
|
if validator.rounds.latest.find(signed.blck.id).isSome:
|
|
|
|
|
return BlockCheck.invalid("block already received")
|
2024-10-29 12:19:20 +01:00
|
|
|
BlockCheck.correct(signed)
|
2024-10-28 12:54:36 +01:00
|
|
|
|
2024-12-09 16:49:44 +01:00
|
|
|
func add*(validator: Validator, correct: CorrectBlock) =
|
2024-11-05 14:38:43 +01:00
|
|
|
validator.addBlock(correct.signedBlock)
|
2024-10-29 12:19:20 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2024-10-02 15:44:01 +02:00
|
|
|
func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
|
2024-10-17 16:03:20 +02:00
|
|
|
without anchor =? round.findAnchor():
|
2024-10-02 15:44:01 +02:00
|
|
|
return
|
2024-10-14 13:31:10 +02:00
|
|
|
without anchorProposal =? anchor.proposal:
|
2024-10-02 15:44:01 +02:00
|
|
|
return
|
2024-12-09 18:07:43 +01:00
|
|
|
var todo = anchorProposal.blck.parents
|
2024-10-02 15:44:01 +02:00
|
|
|
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:
|
2024-10-14 12:37:32 +02:00
|
|
|
slotProposal.certify(anchorProposal)
|
2024-10-02 15:44:01 +02:00
|
|
|
return
|
2024-10-17 16:06:28 +02:00
|
|
|
without parentBlock =? round.find(parent):
|
2024-10-29 12:19:20 +01:00
|
|
|
raiseAssert "parent block not found"
|
2024-12-09 18:07:43 +01:00
|
|
|
todo.add(parentBlock.blck.parents)
|
2024-10-14 12:37:32 +02:00
|
|
|
slot.skip()
|
2024-10-02 15:44:01 +02:00
|
|
|
|
2024-09-25 11:25:20 +02:00
|
|
|
iterator committed*(validator: Validator): auto =
|
|
|
|
|
var done = false
|
2024-10-21 10:29:41 +02:00
|
|
|
while not done:
|
|
|
|
|
let round = validator.rounds.oldest
|
2024-10-17 16:00:22 +02:00
|
|
|
for slot in round.slots:
|
2024-10-02 15:44:01 +02:00
|
|
|
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:
|
2024-10-14 12:37:32 +02:00
|
|
|
yield slot.commit()
|
2024-09-26 08:17:54 +02:00
|
|
|
if not done:
|
2024-10-21 10:29:41 +02:00
|
|
|
validator.rounds.removeOldestRound()
|