mirror of
https://github.com/logos-storage/nim-mysticeti.git
synced 2026-01-06 23:53:09 +00:00
commit blocks using the indirect decision rule
This commit is contained in:
parent
c2ee75694e
commit
5aa7ee0fbd
@ -20,6 +20,7 @@ type
|
||||
Proposal[Signing, Hashing] = ref object
|
||||
blck: Block[Signing, Hashing]
|
||||
certifiedBy: Stake
|
||||
certificates: seq[BlockId[Signing, Hashing]]
|
||||
SlotStatus* {.pure.} = enum
|
||||
undecided
|
||||
skip
|
||||
@ -112,6 +113,7 @@ func updateCertified(validator: Validator, certificate: Block) =
|
||||
support += validator.committee.stake(vote.blck.author)
|
||||
if support > 2/3:
|
||||
proposal.certifiedBy += validator.committee.stake(certificate.author)
|
||||
proposal.certificates.add(certificate.id)
|
||||
if proposal.certifiedBy > 2/3:
|
||||
proposerSlot.status = SlotStatus.commit
|
||||
|
||||
@ -153,12 +155,60 @@ func status*(validator: Validator, blck: Block): ?SlotStatus =
|
||||
func status*(validator: Validator, proposal: SignedBlock): ?SlotStatus =
|
||||
validator.status(proposal.blck)
|
||||
|
||||
func findAnchor(validator: Validator, round: Round): auto =
|
||||
var next = round.next.?next.?next
|
||||
while current =? next:
|
||||
for member in validator.committee.ordered(current.number):
|
||||
let slot = current[member]
|
||||
if slot.status in [SlotStatus.undecided, SlotStatus.commit]:
|
||||
return some slot
|
||||
next = current.next
|
||||
|
||||
func searchBackwards(round: Round, blockId: BlockId): auto =
|
||||
var current = round
|
||||
while current.number > blockId.round and previous =? current.previous:
|
||||
current = previous
|
||||
if current.number == blockId.round:
|
||||
let slot = current[blockId.author]
|
||||
for proposal in slot.proposals:
|
||||
let blck = proposal.blck
|
||||
if blck.id == blockId:
|
||||
return some blck
|
||||
|
||||
func certifiedProposal(slot: ProposerSlot): auto =
|
||||
if slot.status in [SlotStatus.commit, SlotStatus.committed]:
|
||||
for proposal in slot.proposals:
|
||||
if proposal.certifiedBy > 2/3:
|
||||
return some proposal
|
||||
|
||||
func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
|
||||
without anchor =? validator.findAnchor(round):
|
||||
return
|
||||
without anchorProposal =? anchor.certifiedProposal:
|
||||
return
|
||||
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.certifiedBy = anchorProposal.certifiedBy
|
||||
slot.status = SlotStatus.commit
|
||||
return
|
||||
without parentBlock =? round.searchBackwards(parent):
|
||||
discard
|
||||
todo.add(parentBlock.parents)
|
||||
slot.status = SlotStatus.skip
|
||||
|
||||
iterator committed*(validator: Validator): auto =
|
||||
var done = false
|
||||
var current = some validator.first
|
||||
while not done and round =? current:
|
||||
for member in validator.committee.ordered(round.number):
|
||||
let slot = round[member]
|
||||
if slot.status == SlotStatus.undecided:
|
||||
validator.updateIndirect(slot, round)
|
||||
case slot.status
|
||||
of SlotStatus.undecided:
|
||||
done = true
|
||||
@ -167,9 +217,9 @@ iterator committed*(validator: Validator): auto =
|
||||
discard
|
||||
of SlotStatus.commit:
|
||||
slot.status = SlotStatus.committed
|
||||
for proposal in slot.proposals:
|
||||
if proposal.certifiedBy > 2/3:
|
||||
yield proposal.blck
|
||||
without proposal =? slot.certifiedProposal:
|
||||
raiseAssert "slot state is 'commit', but no proposal is certified"
|
||||
yield proposal.blck
|
||||
if not done:
|
||||
validator.remove(round)
|
||||
current = round.next
|
||||
|
||||
@ -84,3 +84,36 @@ suite "Commitee of Validators":
|
||||
discard exchangeProposals()
|
||||
check toSeq(validators[0].committed()) == second
|
||||
|
||||
test "commits blocks using the indirect decision rule":
|
||||
# first round: proposal is seen by majority
|
||||
let proposal = validators[0].propose(seq[Transaction].example)
|
||||
for index in 1..3:
|
||||
discard validators[index].propose(seq[Transaction].example)
|
||||
validators[1].receive(proposal)
|
||||
validators[2].receive(proposal)
|
||||
# second round: majority votes are only seen by first validator
|
||||
nextRound()
|
||||
discard validators[0].propose(seq[Transaction].example)
|
||||
let vote2 = validators[1].propose(seq[Transaction].example)
|
||||
let vote3 = validators[2].propose(seq[Transaction].example)
|
||||
discard validators[3].propose(seq[Transaction].example)
|
||||
validators[0].receive(vote2)
|
||||
validators[0].receive(vote3)
|
||||
# third round: only first validator creates a certificate
|
||||
nextRound()
|
||||
let certificate = validators[0].propose(seq[Transaction].example)
|
||||
for index in 1..3:
|
||||
discard validators[index].propose(seq[Transaction].example)
|
||||
validators[1].receive(certificate)
|
||||
validators[2].receive(certificate)
|
||||
validators[3].receive(certificate)
|
||||
# fourth round: anchors
|
||||
nextRound()
|
||||
discard exchangeProposals()
|
||||
# fifth round: voting on anchors
|
||||
nextRound()
|
||||
discard exchangeProposals()
|
||||
# sixth round: certifying anchors
|
||||
nextRound()
|
||||
discard exchangeProposals()
|
||||
check toSeq(validators[0].committed()).?[0] == some proposal.blck
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user