mirror of
https://github.com/logos-storage/nim-mysticeti.git
synced 2026-01-06 23:53:09 +00:00
add tests for Round
This commit is contained in:
parent
f3c013ced0
commit
1692bde0f4
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user