add tests for Round

This commit is contained in:
Mark Spanbroek 2024-10-22 12:29:55 +02:00
parent f3c013ced0
commit 1692bde0f4
4 changed files with 175 additions and 15 deletions

View File

@ -10,14 +10,14 @@ type
parents: seq[BlockId[Hashing]]
transactions: seq[Transaction]
func new*(
_: type Block,
func new*[Hashing](
_: type Block[Hashing];
author: CommitteeMember,
round: uint64,
parents: seq[BlockId],
parents: seq[BlockId[Hashing]],
transactions: seq[Transaction]
): auto =
Block[BlockId.Hashing](
Block[Hashing](
author: author,
round: round,
parents: parents,

View File

@ -9,6 +9,7 @@ type Round*[Hashing] = ref object
slots: seq[ProposerSlot[Hashing]]
func new*(T: type Round, number: uint64, slots: int): T =
assert slots > 0
type Slot = ProposerSlot[T.Hashing]
let slots = newSeqWith(slots, Slot.new())
T(number: number, slots: slots)
@ -42,7 +43,7 @@ iterator slots*(round: Round): auto =
yield round[member]
iterator proposals*(round: Round): auto =
for slot in round.slots:
for slot in slots(round):
for proposal in slot.proposals:
yield proposal
@ -71,11 +72,15 @@ func find*(round: Round, blockId: BlockId): auto =
func findAnchor*(round: Round): auto =
var next = round.find(round.number + 3)
while current =? next:
for slot in current.slots:
for slot in slots(current):
if slot.status in [SlotStatus.undecided, SlotStatus.commit]:
return some slot
next = current.next
func addProposal*(round: Round, blck: Block): auto =
assert blck.round == round.number
round[blck.author].addProposal(blck)
func remove*(round: Round) =
if previous =? round.previous:
previous.next = round.next
@ -83,6 +88,3 @@ func remove*(round: Round) =
next.previous = round.previous
round.next = none Round
round.previous = none Round
func addProposal*(round: Round, blck: Block): auto =
round[blck.author].addProposal(blck)

View File

@ -28,12 +28,14 @@ proc example*(T: type BlockId): T =
let hash = Hash[T.Hashing].example
BlockId.new(author, round, hash)
proc example*(T: type Block): T =
let author = CommitteeMember.example
let round = uint64.example
proc example*(
T: type Block,
author = CommitteeMember.example,
round = uint64.example
): T =
let parents = seq[BlockId[T.Hashing]].example
let transactions = seq[Transaction].example
Block.new(author, round, parents, transactions)
T.new(author, round, parents, transactions)
proc example*[T](_: type seq[T], length=0..10): seq[T] =
let size = rand(length)

View File

@ -1,10 +1,123 @@
import ../basics
import mysticeti
import mysticeti/validator/rounds
import mysticeti/blocks
import mysticeti/validator/slots
import mysticeti/validator/round
suite "Validator Round":
type Round = rounds.Round[MockHashing]
type Round = round.Round[MockHashing]
type Block = mysticeti.Block[MockHashing]
test "rounds have a number":
check Round.new(0, 1).number == 0
check Round.new(42, 1).number == 42
check Round.new(1337, 1).number == 1337
test "round has a fixed number of slots":
check toSeq(Round.new(0, 1).slots).len == 1
check toSeq(Round.new(0, 42).slots).len == 42
check toSeq(Round.new(0, 1337).slots).len == 1337
test "round requires at least one slot":
expect Defect:
discard Round.new(0, 0)
test "round has a slot for each committee member":
let round = Round.new(0, 4)
check not isNil round[CommitteeMember(0)]
check not isNil round[CommitteeMember(1)]
check not isNil round[CommitteeMember(2)]
check not isNil round[CommitteeMember(3)]
expect Defect:
discard round[CommitteeMember(4)]
test "round stores proposed blocks in the corresponding slots":
let round = Round.new(0, 4)
let block1 = Block.example(author = CommitteeMember(1), round = 0)
let block2 = Block.example(author = CommitteeMember(2), round = 0)
let block3 = Block.example(author = CommitteeMember(2), round = 0)
round.addProposal(block1)
round.addProposal(block2)
round.addProposal(block3)
let slot1 = round[CommitteeMember(1)]
check slot1.proposals.len == 1
check slot1.proposals[0].blck == block1
let slot2 = round[CommitteeMember(2)]
check slot2.proposals.len == 2
check slot2.proposals[0].blck == block2
check slot2.proposals[1].blck == block3
test "round does not accept blocks meant for different rounds":
let blck = Block.example(author = CommitteeMember(0), round = 42)
let round42 = Round.new(42, 4)
let round43 = Round.new(43, 4)
round42.addProposal(blck)
expect Defect:
round43.addProposal(blck)
test "round is part of a doubly linked list":
let first = Round.new(0, 4)
let second = Round.new(first)
let third = Round.new(second)
check first.previous == none Round
check first.next == some second
check second.previous == some first
check second.next == some third
check third.previous == some second
check third.next == none Round
test "doubly linked list has increasing round numbers":
let first = Round.new(42, 4)
let second = Round.new(first)
let third = Round.new(second)
check first.number == 42
check second.number == 43
check third.number == 44
test "doubly linked list can be used to find a round by its number":
let first = Round.new(42, 4)
let second = Round.new(first)
let third = Round.new(second)
for round in [first, second, third]:
check round.find(41'u64) == none Round
check round.find(42'u64) == some first
check round.find(43'u64) == some second
check round.find(44'u64) == some third
check round.find(45'u64) == none Round
test "doubly linked list can be used to find a block":
let first = Round.new(42, 4)
let second = Round.new(first)
let third = Round.new(second)
let block1 = Block.example(author = CommitteeMember(1), round = 42)
let block2 = Block.example(author = CommitteeMember(2), round = 43)
let block3 = Block.example(author = CommitteeMember(2), round = 43)
first.addProposal(block1)
second.addProposal(block2)
second.addProposal(block3)
for round in [first, second, third]:
check round.find(block1.id) == some block1
check round.find(block2.id) == some block2
check round.find(block3.id) == some block3
check round.find(Block.example.id) == none Block
test "round can be removed from a doubly linked list":
let first = Round.new(42, 4)
let second = Round.new(first)
let third = Round.new(second)
second.remove()
check first.previous == none Round
check first.next == some third
check second.previous == none Round
check second.next == none Round
check third.previous == some first
check third.next == none Round
third.remove()
check first.previous == none Round
check first.next == none Round
check third.previous == none Round
check third.next == none Round
test "members are ordered round-robin for each round":
var round: Round
@ -18,3 +131,46 @@ suite "Validator Round":
check toSeq(round.members) == @[3, 0, 1, 2].mapIt(CommitteeMember(it))
round = Round.new(4, 4)
check toSeq(round.members) == @[0, 1, 2, 3].mapIt(CommitteeMember(it))
test "slots are ordered round-robin too":
let round = Round.new(2, 4)
let slots = toSeq(round.slots)
check slots[0] == round[CommitteeMember(2)]
check slots[1] == round[CommitteeMember(3)]
check slots[2] == round[CommitteeMember(0)]
check slots[3] == round[CommitteeMember(1)]
test "proposals are ordered round-robin as well":
var blocks: seq[Block]
blocks.add(Block.example(author = CommitteeMember(0), round = 2))
blocks.add(Block.example(author = CommitteeMember(0), round = 2))
blocks.add(Block.example(author = CommitteeMember(1), round = 2))
blocks.add(Block.example(author = CommitteeMember(1), round = 2))
blocks.add(Block.example(author = CommitteeMember(2), round = 2))
blocks.add(Block.example(author = CommitteeMember(2), round = 2))
blocks.add(Block.example(author = CommitteeMember(3), round = 2))
blocks.add(Block.example(author = CommitteeMember(3), round = 2))
let round = Round.new(2, 4)
for blck in blocks:
round.addProposal(blck)
let proposals = toSeq(round.proposals)
check proposals[0].blck == blocks[4]
check proposals[1].blck == blocks[5]
check proposals[2].blck == blocks[6]
check proposals[3].blck == blocks[7]
check proposals[4].blck == blocks[0]
check proposals[5].blck == blocks[1]
check proposals[6].blck == blocks[2]
check proposals[7].blck == blocks[3]
test "doubly linked list can be used to find the anchor for a round":
let proposing = Round.new(2, 4)
let voting = Round.new(proposing)
let certifying = Round.new(voting)
let anchoring = Round.new(certifying)
let orderedSlots = toSeq(anchoring.slots)
check proposing.findAnchor() == some orderedSlots[0]
orderedSlots[0].skip()
check proposing.findAnchor() == some orderedSlots[1]
orderedSlots[1].skip()
check proposing.findAnchor() == some orderedSlots[2]