mirror of
https://github.com/logos-storage/nim-mysticeti.git
synced 2026-01-04 06:33:11 +00:00
count votes from committee members only once
This commit is contained in:
parent
da66c27de7
commit
7ce6f639f3
@ -1,39 +1,7 @@
|
||||
import ./basics
|
||||
import ./signing
|
||||
import ./committee/members
|
||||
import ./committee/committee
|
||||
import ./committee/voting
|
||||
|
||||
export members.CommitteeMember
|
||||
export members.`==`
|
||||
export members.`$`
|
||||
|
||||
type
|
||||
Committee*[Signing] = ref object
|
||||
members: seq[Identifier[Signing]]
|
||||
stakes: seq[Stake]
|
||||
Stake* = float64
|
||||
|
||||
func new*(_: type Committee, stakes: openArray[(Identifier, Stake)]): auto =
|
||||
var committee = Committee[Identifier.Signing]()
|
||||
for (member, stake) in stakes:
|
||||
committee.members.add(member)
|
||||
committee.stakes.add(stake)
|
||||
committee
|
||||
|
||||
func size*(committee: Committee): int =
|
||||
committee.members.len
|
||||
|
||||
func membership*(committee: Committee, identifier: Identifier): ?CommitteeMember =
|
||||
let index = committee.members.find(identifier)
|
||||
if index < 0:
|
||||
none CommitteeMember
|
||||
else:
|
||||
some CommitteeMember(index)
|
||||
|
||||
func stake*(committee: Committee, member: CommitteeMember): Stake =
|
||||
committee.stakes[int(member)]
|
||||
|
||||
func stake*(committee: Committee, identifier: Identifier): Stake =
|
||||
if member =? committee.membership(identifier):
|
||||
committee.stake(member)
|
||||
else:
|
||||
0
|
||||
export members
|
||||
export committee
|
||||
export voting
|
||||
|
||||
35
mysticeti/committee/committee.nim
Normal file
35
mysticeti/committee/committee.nim
Normal file
@ -0,0 +1,35 @@
|
||||
import ../basics
|
||||
import ../signing
|
||||
import ./members
|
||||
|
||||
type
|
||||
Committee*[Signing] = ref object
|
||||
members: seq[Identifier[Signing]]
|
||||
stakes: seq[Stake]
|
||||
Stake* = float64
|
||||
|
||||
func new*(_: type Committee, stakes: openArray[(Identifier, Stake)]): auto =
|
||||
var committee = Committee[Identifier.Signing]()
|
||||
for (member, stake) in stakes:
|
||||
committee.members.add(member)
|
||||
committee.stakes.add(stake)
|
||||
committee
|
||||
|
||||
func size*(committee: Committee): int =
|
||||
committee.members.len
|
||||
|
||||
func membership*(committee: Committee, identifier: Identifier): ?CommitteeMember =
|
||||
let index = committee.members.find(identifier)
|
||||
if index < 0:
|
||||
none CommitteeMember
|
||||
else:
|
||||
some CommitteeMember(index)
|
||||
|
||||
func stake*(committee: Committee, member: CommitteeMember): Stake =
|
||||
committee.stakes[int(member)]
|
||||
|
||||
func stake*(committee: Committee, identifier: Identifier): Stake =
|
||||
if member =? committee.membership(identifier):
|
||||
committee.stake(member)
|
||||
else:
|
||||
0
|
||||
14
mysticeti/committee/voting.nim
Normal file
14
mysticeti/committee/voting.nim
Normal file
@ -0,0 +1,14 @@
|
||||
import ./members
|
||||
import ./committee
|
||||
|
||||
type Voting* = object
|
||||
voted: seq[CommitteeMember]
|
||||
stake: Stake
|
||||
|
||||
func add*(voting: var Voting, member: CommitteeMember, stake: Stake) =
|
||||
if member notin voting.voted:
|
||||
voting.voted.add(member)
|
||||
voting.stake += stake
|
||||
|
||||
func stake*(voting: Voting): Stake =
|
||||
voting.stake
|
||||
@ -49,8 +49,9 @@ func updateSkipped(validator: Validator, supporter: Block) =
|
||||
for member in previous.members:
|
||||
let slot = previous[member]
|
||||
if supporter.skips(previous.number, member):
|
||||
let stake = validator.committee.stake(supporter.author)
|
||||
slot.skipBy(stake)
|
||||
let author = supporter.author
|
||||
let stake = validator.committee.stake(author)
|
||||
slot.skipBy(author, stake)
|
||||
|
||||
func updateCertified(validator: Validator, certificate: Block) =
|
||||
without certifying =? validator.rounds.latest.find(certificate.round) and
|
||||
@ -58,12 +59,14 @@ func updateCertified(validator: Validator, certificate: Block) =
|
||||
proposing =? voting.previous:
|
||||
return
|
||||
for proposal in proposing.proposals:
|
||||
var support: Stake
|
||||
var support: Voting
|
||||
for vote in voting.proposals:
|
||||
if proposal.blck.id in vote.blck.parents:
|
||||
if vote.blck.id in certificate.parents:
|
||||
support += validator.committee.stake(vote.blck.author)
|
||||
if support > 2/3:
|
||||
let author = vote.blck.author
|
||||
let stake = validator.committee.stake(author)
|
||||
support.add(author, stake)
|
||||
if support.stake > 2/3:
|
||||
let stake = validator.committee.stake(certificate.author)
|
||||
proposal.certifyBy(certificate.id, stake)
|
||||
|
||||
|
||||
@ -5,12 +5,12 @@ import ../committee
|
||||
type
|
||||
ProposerSlot*[Signing, Hashing] = ref object
|
||||
proposals: seq[Proposal[Signing, Hashing]]
|
||||
skippedBy: Stake
|
||||
skippedBy: Voting
|
||||
status: SlotStatus
|
||||
Proposal*[Signing, Hashing] = ref object
|
||||
slot: ProposerSlot[Signing, Hashing]
|
||||
signedBlock: SignedBlock[Signing, Hashing]
|
||||
certifiedBy: Stake
|
||||
certifiedBy: Voting
|
||||
certificates: seq[BlockId[Hashing]]
|
||||
SlotStatus* {.pure.} = enum
|
||||
undecided
|
||||
@ -24,7 +24,7 @@ func proposals*(slot: ProposerSlot): auto =
|
||||
func proposal*(slot: ProposerSlot): auto =
|
||||
if slot.status in [SlotStatus.commit, SlotStatus.committed]:
|
||||
for proposal in slot.proposals:
|
||||
if proposal.certifiedBy > 2/3:
|
||||
if proposal.certifiedBy.stake > 2/3:
|
||||
return some proposal
|
||||
|
||||
func status*(slot: ProposerSlot): auto =
|
||||
@ -41,13 +41,14 @@ func certificates*(proposal: Proposal): auto =
|
||||
|
||||
func addProposal*(slot: ProposerSlot, signedBlock: SignedBlock) =
|
||||
let proposal = Proposal[SignedBlock.Signing, SignedBlock.Hashing](
|
||||
slot: slot, signedBlock: signedBlock
|
||||
slot: slot,
|
||||
signedBlock: signedBlock
|
||||
)
|
||||
slot.proposals.add(proposal)
|
||||
|
||||
func skipBy*(slot: ProposerSlot, stake: Stake) =
|
||||
slot.skippedBy += stake
|
||||
if slot.skippedBy > 2/3:
|
||||
func skipBy*(slot: ProposerSlot, member: CommitteeMember, stake: Stake) =
|
||||
slot.skippedBy.add(member, stake)
|
||||
if slot.skippedBy.stake > 2/3:
|
||||
slot.status = SlotStatus.skip
|
||||
|
||||
func skip*(slot: ProposerSlot) =
|
||||
@ -57,20 +58,20 @@ func skip*(slot: ProposerSlot) =
|
||||
func commit*(slot: ProposerSlot): auto =
|
||||
assert slot.status == SlotStatus.commit
|
||||
let proposal = !slot.proposal
|
||||
assert proposal.certifiedBy > 2/3
|
||||
assert proposal.certifiedBy.stake > 2/3
|
||||
slot.status = SlotStatus.committed
|
||||
return proposal.blck
|
||||
|
||||
func certifyBy*(proposal: Proposal, certificate: BlockId, stake: Stake) =
|
||||
proposal.certificates.add(certificate)
|
||||
proposal.certifiedBy += stake
|
||||
if proposal.certifiedBy > 2/3:
|
||||
proposal.certifiedBy.add(certificate.author, stake)
|
||||
if proposal.certifiedBy.stake > 2/3:
|
||||
proposal.slot.status = SlotStatus.commit
|
||||
|
||||
func certify*(proposal, anchor: Proposal) =
|
||||
assert proposal.slot.status == SlotStatus.undecided
|
||||
assert anchor.slot.status == SlotStatus.commit
|
||||
assert anchor.certifiedBy > 2/3
|
||||
assert anchor.certifiedBy.stake > 2/3
|
||||
proposal.certificates = @[anchor.blck.id]
|
||||
proposal.certifiedBy = anchor.certifiedBy
|
||||
proposal.slot.status = SlotStatus.commit
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import ./basics
|
||||
import ../basics
|
||||
import mysticeti/committee
|
||||
import mysticeti/signing
|
||||
|
||||
25
tests/mysticeti/committee/testVoting.nim
Normal file
25
tests/mysticeti/committee/testVoting.nim
Normal file
@ -0,0 +1,25 @@
|
||||
import ../basics
|
||||
import mysticeti
|
||||
import mysticeti/committee
|
||||
|
||||
suite "Committee Voting":
|
||||
|
||||
test "by default 0 stake has voted":
|
||||
var voting: Voting
|
||||
check voting.stake == 0
|
||||
|
||||
test "when a member adds a vote, it add its stake to the total":
|
||||
var voting: Voting
|
||||
voting.add(CommitteeMember(0), 1/8)
|
||||
check voting.stake == 1/8
|
||||
voting.add(CommitteeMember(1), 1/2)
|
||||
check voting.stake == 5/8
|
||||
voting.add(CommitteeMember(3), 1/8)
|
||||
check voting.stake == 3/4
|
||||
|
||||
test "votes are only counted once":
|
||||
var voting: Voting
|
||||
voting.add(CommitteeMember(0), 1/8)
|
||||
check voting.stake == 1/8
|
||||
voting.add(CommitteeMember(0), 1/8)
|
||||
check voting.stake == 1/8
|
||||
@ -85,11 +85,11 @@ suite "Proposer Slots":
|
||||
check slot.status == SlotStatus.committed
|
||||
|
||||
test "slots can be skipped when >2/3 stake skip it":
|
||||
slot.skipBy(1/3)
|
||||
slot.skipBy(CommitteeMember(0), 1/3)
|
||||
check slot.status == SlotStatus.undecided
|
||||
slot.skipBy(1/3)
|
||||
slot.skipBy(CommitteeMember(1), 1/3)
|
||||
check slot.status == SlotStatus.undecided
|
||||
slot.skipBy(1/1000)
|
||||
slot.skipBy(CommitteeMember(2), 1/1000)
|
||||
check slot.status == SlotStatus.skip
|
||||
|
||||
test "slots can be skipped immediately":
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import ./mysticeti/testCommittee
|
||||
import ./mysticeti/testBlocks
|
||||
import ./mysticeti/committee/testCommittee
|
||||
import ./mysticeti/committee/testVoting
|
||||
import ./mysticeti/validator/testSlots
|
||||
import ./mysticeti/validator/testRound
|
||||
import ./mysticeti/validator/testRounds
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user