diff --git a/mysticeti/validator.nim b/mysticeti/validator.nim index e421a75..401a160 100644 --- a/mysticeti/validator.nim +++ b/mysticeti/validator.nim @@ -70,6 +70,12 @@ func updateCertified(validator: Validator, certificate: Block) = let stake = validator.committee.stake(certificate.author) proposal.certifyBy(certificate.id, stake) +func addBlock(validator: Validator, signedBlock: SignedBlock) = + if round =? validator.rounds.latest.find(signedBlock.blck.round): + round.addProposal(signedBlock) + validator.updateSkipped(signedBlock.blck) + validator.updateCertified(signedBlock.blck) + proc propose*(validator: Validator, transactions: seq[Transaction]): auto = type SignedBlock = blocks.SignedBlock[Validator.Signing, Validator.Hashing] let round = validator.rounds.latest @@ -93,8 +99,7 @@ proc propose*(validator: Validator, transactions: seq[Transaction]): auto = transactions = transactions ) let signedBlock = validator.identity.sign(blck) - round.addProposal(signedBlock) - validator.updateCertified(blck) + validator.addBlock(signedBlock) success signedBlock func check*(validator: Validator, signed: SignedBlock): auto = @@ -134,10 +139,7 @@ func check*(validator: Validator, signed: SignedBlock): auto = BlockCheck.correct(signed) func receive*(validator: Validator, correct: CorrectBlock) = - if round =? validator.rounds.latest.find(correct.blck.round): - round.addProposal(correct.signedBlock) - validator.updateSkipped(correct.blck) - validator.updateCertified(correct.blck) + validator.addBlock(correct.signedBlock) func getBlock*(validator: Validator, id: BlockId): auto = validator.rounds.latest.find(id) diff --git a/tests/mysticeti/scenarios.nim b/tests/mysticeti/scenarios.nim new file mode 100644 index 0000000..35aafaf --- /dev/null +++ b/tests/mysticeti/scenarios.nim @@ -0,0 +1,52 @@ +import ./basics +import ./simulator + +proc scenarioFigure4*(simulator: NetworkSimulator): ?!seq[seq[SignedBlock]] = + # replays scenario from Figure 4 in the Mysticeti paper + # https://arxiv.org/pdf/2310.14821v4 + # note: round robin is not applied correctly in the figure from + # the Mysticeti paper, so this simulation uses different proposer + # labels from the fourth round + var proposals: seq[seq[SignedBlock]] + proposals.add(? simulator.exchangeProposals { + 0: @[0, 1, 2, 3], + 1: @[0, 1], + 2: @[0, 2, 3], + 3: @[1, 2, 3] + }) + simulator.nextRound() + proposals.add(? simulator.exchangeProposals { + 0: @[0, 1, 3], + 1: @[0, 1, 3], + 2: @[0, 3], + 3: @[1, 3] + }) + simulator.nextRound() + proposals.add(? simulator.exchangeProposals { + 0: @[2, 3, 0, 1], + 1: @[2, 3, 0, 1], + + 3: @[2, 3, 0, 1] + }) + simulator.nextRound() + proposals.add(? simulator.exchangeProposals { + 2: @[2, 3, 0, 1], + 3: @[3], + 0: @[2, 3, 0, 1], + 1: @[2, 3, 0, 1] + }) + simulator.nextRound() + proposals.add(? simulator.exchangeProposals { + 2: @[], + 3: @[2, 3, 0], + 0: @[2, 3, 0], + 1: @[2, 3, 0] + }) + simulator.nextRound() + proposals.add(? simulator.exchangeProposals { + 2: @[2, 3, 0], + 3: @[2, 3, 0], + 0: @[2, 3, 0] + + }) + success proposals diff --git a/tests/mysticeti/simulator.nim b/tests/mysticeti/simulator.nim index 6ca643f..a66df6c 100644 --- a/tests/mysticeti/simulator.nim +++ b/tests/mysticeti/simulator.nim @@ -2,9 +2,9 @@ import ./basics import mysticeti import mysticeti/blocks -type Validator = mysticeti.Validator[MockSigning, MockHashing] -type Identity = mysticeti.Identity[MockSigning] -type SignedBlock = blocks.SignedBlock[MockSigning, MockHashing] +type Validator* = mysticeti.Validator[MockSigning, MockHashing] +type Identity* = mysticeti.Identity[MockSigning] +type SignedBlock* = blocks.SignedBlock[MockSigning, MockHashing] type NetworkSimulator* = object identities: seq[Identity] diff --git a/tests/mysticeti/validator/testMultiple.nim b/tests/mysticeti/validator/testMultiple.nim index 1b98f79..a8fadf0 100644 --- a/tests/mysticeti/validator/testMultiple.nim +++ b/tests/mysticeti/validator/testMultiple.nim @@ -1,5 +1,6 @@ import ../basics import ../simulator +import ../scenarios import mysticeti import mysticeti/blocks import mysticeti/hashing @@ -269,70 +270,11 @@ suite "Multiple Validators": check toSeq(simulator.validators[0].committed()) == second test "commits blocks using the indirect decision rule": - # first round: proposals - let proposals = !simulator.exchangeProposals { - 0: @[0, 1, 2, 3], - 1: @[0, 1], - 2: @[0, 2, 3], - 3: @[1, 2, 3] - } - # second round: voting - simulator.nextRound() - discard !simulator.exchangeProposals { - 0: @[0, 1, 3], - 1: @[0, 1, 3], - 2: @[0, 3], - 3: @[1, 3] - } - # third round: certifying - simulator.nextRound() - discard !simulator.exchangeProposals { - 0: @[0, 1, 2, 3], - 1: @[0, 1, 2, 3], - 3: @[0, 1, 2, 3] - } - # fourth round: anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - # fifth round: voting on anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - # sixth round: certifying anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - check toSeq(simulator.validators[0].committed()).contains(proposals[3].blck) + let proposals = !scenarioFigure4(simulator) + let committed = toSeq(simulator.validators[0].committed()) + check committed.contains(proposals[0][3].blck) test "skips blocks using the indirect decision rule": - # Modelled after Figure 3f from the Mysticeti paper - # first round: proposals - let proposals = !simulator.exchangeProposals { - 0: @[0, 1, 2, 3], - 1: @[0, 1], - 2: @[0, 2, 3], - 3: @[1, 2, 3] - } - # second round: voting - simulator.nextRound() - discard !simulator.exchangeProposals { - 0: @[0, 1, 3], - 1: @[0, 1, 3], - 2: @[0, 3], - 3: @[1, 3] - } - # third round: certifying - simulator.nextRound() - discard !simulator.exchangeProposals { - 0: @[0, 1, 2, 3], - 1: @[0, 1, 2, 3], - 3: @[0, 1, 2, 3] - } - # fourth round: anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - # fifth round: voting on anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - # sixth round: certifying anchor - simulator.nextRound() - discard !simulator.exchangeProposals() - check not toSeq(simulator.validators[0].committed()).contains(proposals[1].blck) + let proposals = !scenarioFigure4(simulator) + let committed = toSeq(simulator.validators[0].committed()) + check not committed.contains(proposals[0][1].blck)