evidence: modularise evidence by moving verification function into evidence package (#5234)

This commit is contained in:
Callum Waters
2020-08-20 18:11:21 +02:00
committed by GitHub
parent 8ca24e2f5a
commit b7f6e47a42
19 changed files with 547 additions and 718 deletions
+7 -104
View File
@@ -144,50 +144,23 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
block.Height, state.InitialHeight)
}
// Limit the amount of evidence
numEvidence := len(block.Evidence.Evidence)
// MaxNumEvidence is capped at uint16, so conversion is always safe.
if maxEvidence := int(state.ConsensusParams.Evidence.MaxNum); numEvidence > maxEvidence {
return types.NewErrEvidenceOverflow(maxEvidence, numEvidence)
// Check evidence doesn't exceed the limit. MaxNumEvidence is capped at uint16, so conversion is always safe.
if max, got := int(state.ConsensusParams.Evidence.MaxNum), len(block.Evidence.Evidence); got > max {
return types.NewErrEvidenceOverflow(max, got)
}
// Validate all evidence.
for idx, ev := range block.Evidence.Evidence {
// check that no evidence has been submitted more than once
// Check that no evidence has been submitted more than once
for i := idx + 1; i < len(block.Evidence.Evidence); i++ {
if ev.Equal(block.Evidence.Evidence[i]) {
return types.NewErrEvidenceInvalid(ev, errors.New("evidence was submitted twice"))
}
}
if evidencePool != nil {
if evidencePool.IsCommitted(ev) {
return types.NewErrEvidenceInvalid(ev, errors.New("evidence was already committed"))
}
if evidencePool.IsPending(ev) {
continue
}
}
// if we don't already have amnesia evidence we need to add it to start our own trial period unless
// a) a valid polc has already been attached
// b) the accused node voted back on an earlier round
if ae, ok := ev.(*types.AmnesiaEvidence); ok && ae.Polc.IsAbsent() && ae.PotentialAmnesiaEvidence.VoteA.Round <
ae.PotentialAmnesiaEvidence.VoteB.Round {
if err := evidencePool.AddEvidence(ae.PotentialAmnesiaEvidence); err != nil {
return types.NewErrEvidenceInvalid(ev,
fmt.Errorf("unknown amnesia evidence, trying to add to evidence pool, err: %w", err))
}
return types.NewErrEvidenceInvalid(ev, errors.New("amnesia evidence is new and hasn't undergone trial period yet"))
}
// A header needs to be fetched. For lunatic evidence this is so we can verify
// that some of the fields are different to the ones we have. For all evidence it
// it so we can verify that the time of the evidence is correct
header := evidencePool.Header(ev.Height())
if header == nil {
return fmt.Errorf("don't have block meta at height #%d", ev.Height())
}
if err := VerifyEvidence(stateDB, state, ev, header); err != nil {
// Verify evidence using the evidence pool
err := evidencePool.Verify(ev)
if err != nil {
return types.NewErrEvidenceInvalid(ev, err)
}
@@ -195,73 +168,3 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block
return nil
}
// VerifyEvidence verifies the evidence fully by checking:
// - it is sufficiently recent (MaxAge)
// - it is from a key who was a validator at the given height
// - it is internally consistent
// - it was properly signed by the alleged equivocator
func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence, committedHeader *types.Header) error {
var (
height = state.LastBlockHeight
evidenceParams = state.ConsensusParams.Evidence
ageDuration = state.LastBlockTime.Sub(evidence.Time())
ageNumBlocks = height - evidence.Height()
)
if committedHeader.Time != evidence.Time() {
return fmt.Errorf("evidence time (%v) is different to the time of the header we have for the same height (%v)",
evidence.Time(),
committedHeader.Time,
)
}
if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks {
return fmt.Errorf(
"evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v",
evidence.Height(),
evidence.Time(),
height-evidenceParams.MaxAgeNumBlocks,
state.LastBlockTime.Add(evidenceParams.MaxAgeDuration),
)
}
if ev, ok := evidence.(*types.LunaticValidatorEvidence); ok {
if err := ev.VerifyHeader(committedHeader); err != nil {
return err
}
}
valset, err := LoadValidators(stateDB, evidence.Height())
if err != nil {
// TODO: if err is just that we cant find it cuz we pruned, ignore.
// TODO: if its actually bad evidence, punish peer
return err
}
addr := evidence.Address()
var val *types.Validator
if ae, ok := evidence.(*types.AmnesiaEvidence); ok {
// check the validator set against the polc to make sure that a majority of valid votes was reached
if !ae.Polc.IsAbsent() {
err = ae.Polc.ValidateVotes(valset, state.ChainID)
if err != nil {
return fmt.Errorf("amnesia evidence contains invalid polc, err: %w", err)
}
}
}
// For all other types, expect evidence.Address to be a validator at height
// evidence.Height.
_, val = valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("address %X was not a validator at height %d", addr, evidence.Height())
}
if err := evidence.Verify(state.ChainID, val.PubKey); err != nil {
return err
}
return nil
}