Skip to content

Commit

Permalink
fix: recently signed check when slashing unavailable validator
Browse files Browse the repository at this point in the history
The first validator in snap.Recents is shifted out and allowed to seal the block
already. However, when determining if we should slash the validator in Finalize
and FinalizeAndAssemble, we mark that first validator as unable to seal block
and don't slash this validator. This commit fixes that bug and creates a
separate helper function to check if the validator recently signed a block for
consistency in all places the check happens.
  • Loading branch information
minh-bq committed Apr 28, 2023
1 parent bc72d5e commit fca21ee
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 36 deletions.
58 changes: 22 additions & 36 deletions consensus/parlia/parlia.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,13 +754,8 @@ func (p *Parlia) verifySeal(chain consensus.ChainHeaderReader, header *types.Hea
return errUnauthorizedValidator
}

for seen, recent := range snap.Recents {
if recent == signer {
// Signer is among recents, only fail if the current block doesn't shift it out
if limit := uint64(len(snap.Validators)/2 + 1); seen > number-limit {
return errRecentlySigned
}
}
if snap.SignRecently(signer) {
return errRecentlySigned
}

// Ensure that the difficulty corresponds to the turn-ness of the signer
Expand Down Expand Up @@ -1065,12 +1060,17 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
if header.Difficulty.Cmp(diffInTurn) != 0 {
spoiledVal := snap.supposeValidator()
signedRecently := false
for _, recent := range snap.Recents {
if recent == spoiledVal {
signedRecently = true
break
if p.chainConfig.IsPlato(header.Number) {
signedRecently = snap.SignRecently(spoiledVal)
} else {
for _, recent := range snap.Recents {
if recent == spoiledVal {
signedRecently = true
break
}
}
}

if !signedRecently {
log.Trace("slash validator", "block hash", header.Hash(), "address", spoiledVal)
err = p.slash(spoiledVal, state, header, cx, txs, receipts, systemTxs, usedGas, false)
Expand Down Expand Up @@ -1123,10 +1123,14 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
}
spoiledVal := snap.supposeValidator()
signedRecently := false
for _, recent := range snap.Recents {
if recent == spoiledVal {
signedRecently = true
break
if p.chainConfig.IsPlato(header.Number) {
signedRecently = snap.SignRecently(spoiledVal)
} else {
for _, recent := range snap.Recents {
if recent == spoiledVal {
signedRecently = true
break
}
}
}
if !signedRecently {
Expand Down Expand Up @@ -1294,14 +1298,8 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res
}

// If we're amongst the recent signers, wait for the next block
for seen, recent := range snap.Recents {
if recent == val {
// Signer is among recents, only wait if the current block doesn't shift it out
if limit := uint64(len(snap.Validators)/2 + 1); number < limit || seen > number-limit {
log.Info("Signed recently, must wait for others")
return nil
}
}
if snap.SignRecently(val) {
return nil
}

// Sweet, the protocol permits us to sign the block, wait for our time
Expand Down Expand Up @@ -1408,19 +1406,7 @@ func (p *Parlia) SignRecently(chain consensus.ChainReader, parent *types.Block)
return true, errUnauthorizedValidator
}

// If we're amongst the recent signers, wait for the next block
number := parent.NumberU64() + 1
for seen, recent := range snap.Recents {
if recent != p.val {
continue
}

// Signer is among recents, only wait if the current block doesn't shift it out
if limit := uint64(len(snap.Validators)/2 + 1); number < limit || seen > number-limit {
return true, nil
}
}
return false, nil
return snap.SignRecently(p.val), nil
}

// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
Expand Down
11 changes: 11 additions & 0 deletions consensus/parlia/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,17 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C
}
}

func (s *Snapshot) SignRecently(validator common.Address) bool {
for seen, recent := range s.Recents {
if recent == validator {
if limit := uint64(len(s.Validators)/2 + 1); s.Number+1 < limit || seen > s.Number+1-limit {
return true
}
}
}
return false
}

func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderReader, parents []*types.Header, chainConfig *params.ChainConfig) (*Snapshot, error) {
// Allow passing in no headers for cleaner code
if len(headers) == 0 {
Expand Down

0 comments on commit fca21ee

Please sign in to comment.