mirror of
https://github.com/logos-storage/nim-mysticeti.git
synced 2026-01-08 00:23:13 +00:00
add tests for Round
This commit is contained in:
parent
f3c013ced0
commit
1692bde0f4
@ -10,14 +10,14 @@ type
|
|||||||
parents: seq[BlockId[Hashing]]
|
parents: seq[BlockId[Hashing]]
|
||||||
transactions: seq[Transaction]
|
transactions: seq[Transaction]
|
||||||
|
|
||||||
func new*(
|
func new*[Hashing](
|
||||||
_: type Block,
|
_: type Block[Hashing];
|
||||||
author: CommitteeMember,
|
author: CommitteeMember,
|
||||||
round: uint64,
|
round: uint64,
|
||||||
parents: seq[BlockId],
|
parents: seq[BlockId[Hashing]],
|
||||||
transactions: seq[Transaction]
|
transactions: seq[Transaction]
|
||||||
): auto =
|
): auto =
|
||||||
Block[BlockId.Hashing](
|
Block[Hashing](
|
||||||
author: author,
|
author: author,
|
||||||
round: round,
|
round: round,
|
||||||
parents: parents,
|
parents: parents,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ type Round*[Hashing] = ref object
|
|||||||
slots: seq[ProposerSlot[Hashing]]
|
slots: seq[ProposerSlot[Hashing]]
|
||||||
|
|
||||||
func new*(T: type Round, number: uint64, slots: int): T =
|
func new*(T: type Round, number: uint64, slots: int): T =
|
||||||
|
assert slots > 0
|
||||||
type Slot = ProposerSlot[T.Hashing]
|
type Slot = ProposerSlot[T.Hashing]
|
||||||
let slots = newSeqWith(slots, Slot.new())
|
let slots = newSeqWith(slots, Slot.new())
|
||||||
T(number: number, slots: slots)
|
T(number: number, slots: slots)
|
||||||
@ -42,7 +43,7 @@ iterator slots*(round: Round): auto =
|
|||||||
yield round[member]
|
yield round[member]
|
||||||
|
|
||||||
iterator proposals*(round: Round): auto =
|
iterator proposals*(round: Round): auto =
|
||||||
for slot in round.slots:
|
for slot in slots(round):
|
||||||
for proposal in slot.proposals:
|
for proposal in slot.proposals:
|
||||||
yield proposal
|
yield proposal
|
||||||
|
|
||||||
@ -71,11 +72,15 @@ func find*(round: Round, blockId: BlockId): auto =
|
|||||||
func findAnchor*(round: Round): auto =
|
func findAnchor*(round: Round): auto =
|
||||||
var next = round.find(round.number + 3)
|
var next = round.find(round.number + 3)
|
||||||
while current =? next:
|
while current =? next:
|
||||||
for slot in current.slots:
|
for slot in slots(current):
|
||||||
if slot.status in [SlotStatus.undecided, SlotStatus.commit]:
|
if slot.status in [SlotStatus.undecided, SlotStatus.commit]:
|
||||||
return some slot
|
return some slot
|
||||||
next = current.next
|
next = current.next
|
||||||
|
|
||||||
|
func addProposal*(round: Round, blck: Block): auto =
|
||||||
|
assert blck.round == round.number
|
||||||
|
round[blck.author].addProposal(blck)
|
||||||
|
|
||||||
func remove*(round: Round) =
|
func remove*(round: Round) =
|
||||||
if previous =? round.previous:
|
if previous =? round.previous:
|
||||||
previous.next = round.next
|
previous.next = round.next
|
||||||
@ -83,6 +88,3 @@ func remove*(round: Round) =
|
|||||||
next.previous = round.previous
|
next.previous = round.previous
|
||||||
round.next = none Round
|
round.next = none Round
|
||||||
round.previous = 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
|
let hash = Hash[T.Hashing].example
|
||||||
BlockId.new(author, round, hash)
|
BlockId.new(author, round, hash)
|
||||||
|
|
||||||
proc example*(T: type Block): T =
|
proc example*(
|
||||||
let author = CommitteeMember.example
|
T: type Block,
|
||||||
let round = uint64.example
|
author = CommitteeMember.example,
|
||||||
|
round = uint64.example
|
||||||
|
): T =
|
||||||
let parents = seq[BlockId[T.Hashing]].example
|
let parents = seq[BlockId[T.Hashing]].example
|
||||||
let transactions = seq[Transaction].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] =
|
proc example*[T](_: type seq[T], length=0..10): seq[T] =
|
||||||
let size = rand(length)
|
let size = rand(length)
|
||||||
|
|||||||
@ -1,10 +1,123 @@
|
|||||||
import ../basics
|
import ../basics
|
||||||
import mysticeti
|
import mysticeti
|
||||||
import mysticeti/validator/rounds
|
import mysticeti/blocks
|
||||||
|
import mysticeti/validator/slots
|
||||||
|
import mysticeti/validator/round
|
||||||
|
|
||||||
suite "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":
|
test "members are ordered round-robin for each round":
|
||||||
var round: Round
|
var round: Round
|
||||||
@ -18,3 +131,46 @@ suite "Validator Round":
|
|||||||
check toSeq(round.members) == @[3, 0, 1, 2].mapIt(CommitteeMember(it))
|
check toSeq(round.members) == @[3, 0, 1, 2].mapIt(CommitteeMember(it))
|
||||||
round = Round.new(4, 4)
|
round = Round.new(4, 4)
|
||||||
check toSeq(round.members) == @[0, 1, 2, 3].mapIt(CommitteeMember(it))
|
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