Skip to content

Commit ceb86af

Browse files
committed
blockchain -> clique: reworked voting mechanism including touched account (beneficiary) analysis and signer state updates
1 parent e41ec4d commit ceb86af

File tree

1 file changed

+73
-59
lines changed

1 file changed

+73
-59
lines changed

packages/blockchain/src/index.ts

Lines changed: 73 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,10 @@ export default class Blockchain implements BlockchainInterface {
488488

489489
await this.dbManager.batch(dbOps)
490490
// Output active signers for debugging purposes
491-
for (const [i, signer] of this.cliqueActiveSigners().entries()) {
491+
let i = 0
492+
for (const signer of this.cliqueActiveSigners()) {
492493
debug(`Clique signer [${i}]: ${signer}`)
494+
i++
493495
}
494496
}
495497

@@ -506,63 +508,34 @@ export default class Blockchain implements BlockchainInterface {
506508
const nonce = header.nonce
507509
const latestVote: CliqueVote = [header.number, [signer, beneficiary, nonce]]
508510

509-
const alreadyVoted = this._cliqueLatestVotes.find((vote) => {
510-
return (
511-
vote[1][0].equals(signer) && vote[1][1].equals(beneficiary) && vote[1][2].equals(nonce)
512-
)
513-
})
514-
? true
515-
: false
516-
517-
// Always add the latest vote to the history no matter if already voted
518-
// the same vote or not
519-
this._cliqueLatestVotes.push(latestVote)
520-
debug(
521-
`[Block ${header.number.toNumber()}] New clique vote: ${signer} -> ${beneficiary} ${
522-
nonce.equals(CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP'
523-
}`
524-
)
525-
526-
// remove any opposite votes for [signer, beneficiary]
527-
const oppositeNonce = nonce.equals(CLIQUE_NONCE_AUTH) ? CLIQUE_NONCE_DROP : CLIQUE_NONCE_AUTH
528-
this._cliqueLatestVotes = this._cliqueLatestVotes.filter(
529-
(vote) =>
530-
!(
531-
vote[1][0].equals(signer) &&
532-
vote[1][1].equals(beneficiary) &&
533-
vote[1][2].equals(oppositeNonce)
534-
)
535-
)
511+
// Do two rounds here, one to execute on a potential previously reached consensus
512+
// on the newly touched beneficiary, one with the added new vote
513+
for (let round = 1; round <= 2; round++) {
536514

537-
// If same vote not already in history see if there is a new majority consensus
538-
// to update the signer list
539-
if (!alreadyVoted) {
515+
// See if there is a new majority consensus to update the signer list
540516
const lastEpochBlockNumber = header.number.sub(
541517
header.number.mod(new BN(this._common.consensusConfig().epoch))
542518
)
543-
const beneficiaryVotesAuth = this._cliqueLatestVotes.filter(
544-
(vote) =>
545-
vote[0].gte(lastEpochBlockNumber) &&
546-
vote[1][1].equals(beneficiary) &&
547-
vote[1][2].equals(CLIQUE_NONCE_AUTH)
548-
)
549-
const beneficiaryVotesDrop = this._cliqueLatestVotes.filter(
550-
(vote) =>
551-
vote[0].gte(lastEpochBlockNumber) &&
552-
vote[1][1].equals(beneficiary) &&
553-
vote[1][2].equals(CLIQUE_NONCE_DROP)
554-
)
555519
const limit = this.cliqueSignerLimit()
556-
const consensus =
557-
beneficiaryVotesAuth.length >= limit || beneficiaryVotesDrop.length >= limit
558-
const auth = beneficiaryVotesAuth.length >= limit
559-
// Majority consensus
560-
if (consensus) {
561-
debug(
562-
`[Block ${header.number.toNumber()}] Clique majority consensus -> update signer states`
563-
)
564-
let activeSigners = this.cliqueActiveSigners()
565-
if (auth) {
520+
let activeSigners = this.cliqueActiveSigners()
521+
let consensus = false
522+
523+
// AUTH vote analysis
524+
if (round === 1 || round === 2 && nonce.equals(CLIQUE_NONCE_AUTH)) {
525+
let beneficiaryVotesAUTH = this._cliqueLatestVotes.filter((vote) => {
526+
return (
527+
vote[0].gte(lastEpochBlockNumber) &&
528+
!vote[1][0].equals(signer) &&
529+
vote[1][1].equals(beneficiary) &&
530+
vote[1][2].equals(CLIQUE_NONCE_AUTH)
531+
)
532+
}).length
533+
if (round === 2 && nonce.equals(CLIQUE_NONCE_AUTH)) {
534+
beneficiaryVotesAUTH += 1
535+
}
536+
// Majority consensus
537+
if (beneficiaryVotesAUTH >= limit) {
538+
consensus = true
566539
// Authorize new signer
567540
activeSigners.push(beneficiary)
568541
activeSigners.sort((a, b) => {
@@ -573,16 +546,61 @@ export default class Blockchain implements BlockchainInterface {
573546
this._cliqueLatestVotes = this._cliqueLatestVotes.filter(
574547
(vote) => !vote[1][1].equals(beneficiary)
575548
)
576-
} else {
549+
debug(
550+
`[Block ${header.number.toNumber()}] Clique majority consensus (AUTH ${beneficiary})`
551+
)
552+
}
553+
}
554+
// DROP vote
555+
if (round === 1 || round === 2 && nonce.equals(CLIQUE_NONCE_DROP)) {
556+
let beneficiaryVotesDROP = this._cliqueLatestVotes.filter((vote) => {
557+
return (
558+
vote[0].gte(lastEpochBlockNumber) &&
559+
!vote[1][0].equals(signer) &&
560+
vote[1][1].equals(beneficiary) &&
561+
vote[1][2].equals(CLIQUE_NONCE_DROP)
562+
)
563+
}).length
564+
if (round === 2 && nonce.equals(CLIQUE_NONCE_DROP)) {
565+
beneficiaryVotesDROP += 1
566+
}
567+
// Majority consensus
568+
if (beneficiaryVotesDROP >= limit) {
569+
consensus = true
577570
// Drop signer
578571
activeSigners = activeSigners.filter((signer) => !signer.equals(beneficiary))
579572
// Discard votes from removed signer
580573
this._cliqueLatestVotes = this._cliqueLatestVotes.filter(
581574
(vote) => !vote[1][0].equals(beneficiary)
582575
)
576+
debug(
577+
`[Block ${header.number.toNumber()}] Clique majority consensus (DROP ${beneficiary})`
578+
)
579+
}
580+
}
581+
if (round === 1) {
582+
// Always add the latest vote to the history no matter if already voted
583+
// the same vote or not
584+
this._cliqueLatestVotes.push(latestVote)
585+
debug(
586+
`[Block ${header.number.toNumber()}] New clique vote: ${signer} -> ${beneficiary} ${
587+
nonce.equals(CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP'
588+
}`
589+
)
590+
}
591+
if (consensus) {
592+
if (round === 1) {
593+
debug(
594+
`[Block ${header.number.toNumber()}] Clique majority consensus on existing votes -> update signer states`
595+
)
596+
} else {
597+
debug(
598+
`[Block ${header.number.toNumber()}] Clique majority consensus on new vote -> update signer states`
599+
)
583600
}
584601
const newSignerState: CliqueSignerState = [header.number, activeSigners]
585602
await this.cliqueUpdateSignerStates(newSignerState)
603+
return
586604
}
587605
}
588606
}
@@ -843,11 +861,7 @@ export default class Blockchain implements BlockchainInterface {
843861

844862
if (this._validateBlocks && !isGenesis) {
845863
// this calls into `getBlock`, which is why we cannot lock yet
846-
if (item instanceof BlockHeader) {
847-
await block.header.validate(this)
848-
} else {
849-
await block.validate(this)
850-
}
864+
await block.validate(this)
851865
}
852866

853867
if (this._validateConsensus) {

0 commit comments

Comments
 (0)