-
Notifications
You must be signed in to change notification settings - Fork 1
/
validate_block.go
85 lines (76 loc) · 3.19 KB
/
validate_block.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package blockprocessor
import (
"github.com/Nirvana-Chain/nirvanad/domain/consensus/model"
"github.com/Nirvana-Chain/nirvanad/domain/consensus/model/externalapi"
"github.com/Nirvana-Chain/nirvanad/domain/consensus/ruleerrors"
"github.com/Nirvana-Chain/nirvanad/domain/consensus/utils/consensushashing"
"github.com/Nirvana-Chain/nirvanad/util/staging"
"github.com/pkg/errors"
)
func (bp *blockProcessor) validateBlock(stagingArea *model.StagingArea, block *externalapi.DomainBlock, isBlockWithTrustedData bool) error {
blockHash := consensushashing.HeaderHash(block.Header)
log.Debugf("Validating block %s", blockHash)
// Since genesis has a lot of special cases validation rules, we make sure it's not added unintentionally
// on uninitialized node.
if blockHash.Equal(bp.genesisHash) && bp.blockStore.Count(stagingArea) != 0 {
return errors.Wrapf(ruleerrors.ErrGenesisOnInitializedConsensus, "Cannot add genesis to an initialized consensus")
}
err := bp.checkBlockStatus(stagingArea, block)
if err != nil {
return err
}
hasValidatedHeader, err := bp.hasValidatedHeader(stagingArea, blockHash)
if err != nil {
return err
}
if !hasValidatedHeader {
log.Debugf("Staging block %s header", blockHash)
bp.blockHeaderStore.Stage(stagingArea, blockHash, block.Header)
} else {
log.Debugf("Block %s header is already known, so no need to stage it", blockHash)
}
// If any validation until (included) proof-of-work fails, simply
// return an error without writing anything in the database.
// This is to prevent spamming attacks.
err = bp.validatePreProofOfWork(stagingArea, block)
if err != nil {
return err
}
if !hasValidatedHeader {
err = bp.blockValidator.ValidatePruningPointViolationAndProofOfWorkAndDifficulty(stagingArea, blockHash, isBlockWithTrustedData)
if err != nil {
return err
}
}
// If in-context validations fail, discard all changes and store the
// block with StatusInvalid.
err = bp.validatePostProofOfWork(stagingArea, block, isBlockWithTrustedData)
if err != nil {
if errors.As(err, &ruleerrors.RuleError{}) {
// We mark invalid blocks with status externalapi.StatusInvalid except in the
// case of the following errors:
// ErrMissingParents - If we got ErrMissingParents the block shouldn't be
// considered as invalid because it could be added later on when its
// parents are present.
// ErrBadMerkleRoot - if we get ErrBadMerkleRoot we shouldn't mark the
// block as invalid because later on we can get the block with
// transactions that fits the merkle root.
// ErrPrunedBlock - ErrPrunedBlock is an error that rejects a block body and
// not the block as a whole, so we shouldn't mark it as invalid.
if !errors.As(err, &ruleerrors.ErrMissingParents{}) &&
!errors.Is(err, ruleerrors.ErrBadMerkleRoot) &&
!errors.Is(err, ruleerrors.ErrPrunedBlock) {
// Use a new stagingArea so we save only the block status
stagingArea := model.NewStagingArea()
hash := consensushashing.BlockHash(block)
bp.blockStatusStore.Stage(stagingArea, hash, externalapi.StatusInvalid)
commitErr := staging.CommitAllChanges(bp.databaseContext, stagingArea)
if commitErr != nil {
return commitErr
}
}
}
return err
}
return nil
}