mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-09 05:20:10 +00:00
blocksync: separate verification into different file
This commit is contained in:
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/store"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -67,55 +66,6 @@ func (e peerError) Error() string {
|
||||
return fmt.Sprintf("error with peer %v: %s", e.peerID, e.err.Error())
|
||||
}
|
||||
|
||||
func (r *Reactor) VerifyAdjacent(
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
untrustedHeader *types.SignedHeader, // height=X+1
|
||||
untrustedVals *types.ValidatorSet, // height=X+1)
|
||||
) error {
|
||||
|
||||
if len(trustedHeader.NextValidatorsHash) == 0 {
|
||||
return errors.New("next validators hash in trusted header is empty")
|
||||
}
|
||||
|
||||
if untrustedHeader.Height != trustedHeader.Height+1 {
|
||||
return errors.New("headers must be adjacent in height")
|
||||
}
|
||||
|
||||
if err := untrustedHeader.ValidateBasic(trustedHeader.ChainID); err != nil {
|
||||
return fmt.Errorf("untrustedHeader.ValidateBasic failed: %w", err)
|
||||
}
|
||||
|
||||
if untrustedHeader.Height <= trustedHeader.Height {
|
||||
return fmt.Errorf("expected new header height %d to be greater than one of old header %d",
|
||||
untrustedHeader.Height,
|
||||
trustedHeader.Height)
|
||||
}
|
||||
|
||||
if !untrustedHeader.Time.After(trustedHeader.Time) {
|
||||
return fmt.Errorf("expected new header time %v to be after old header time %v",
|
||||
untrustedHeader.Time,
|
||||
trustedHeader.Time)
|
||||
}
|
||||
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) {
|
||||
return fmt.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d",
|
||||
untrustedHeader.ValidatorsHash,
|
||||
untrustedVals.Hash(),
|
||||
untrustedHeader.Height,
|
||||
)
|
||||
}
|
||||
|
||||
// Check the validator hashes are the same
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) {
|
||||
err := fmt.Errorf("expected old header's next validators (%X) to match those from new header (%X)",
|
||||
trustedHeader.NextValidatorsHash,
|
||||
untrustedHeader.ValidatorsHash,
|
||||
)
|
||||
return light.ErrInvalidHeader{Reason: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reactor handles long-term catchup syncing.
|
||||
type Reactor struct {
|
||||
service.BaseService
|
||||
@@ -556,75 +506,46 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
|
||||
newBlockID = types.BlockID{Hash: newBlock.Hash(), PartSetHeader: newBlockPartSetHeader}
|
||||
)
|
||||
|
||||
// ToDo @jmalicevic
|
||||
// newBlock.last commit - validates r.lastTrustedBlock
|
||||
// if fail - peer dismissed
|
||||
// r.lastTrustedBlock.header. nextValidator = newBlock. validator
|
||||
// validators. validate (newBlock)
|
||||
if r.lastTrustedBlock != nil {
|
||||
err := VerifyNextBlock(newBlock, newBlockID, verifyBlock, r.lastTrustedBlock.block, r.lastTrustedBlock.commit, state.NextValidators)
|
||||
|
||||
// If the blockID in LastCommit of NewBlock does not match the trusted block
|
||||
// we can assume NewBlock is not correct
|
||||
if !(newBlock.LastCommit.BlockID.Equals(r.lastTrustedBlock.commit.BlockID)) {
|
||||
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 1)
|
||||
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
|
||||
NodeID: peerID,
|
||||
Err: errors.New("invalid block for verification"),
|
||||
}); serr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Verify verifyBlock.LastCommit validators against state.NextValidators
|
||||
// If they do not match, need a new verifyBlock
|
||||
|
||||
if err := state.NextValidators.VerifyCommitLight(state.ChainID, newBlockID, newBlock.Height, verifyBlock.LastCommit); err != nil {
|
||||
|
||||
err = fmt.Errorf("invalid verification block, validator hash does not match %w", err)
|
||||
r.logger.Error(
|
||||
err.Error(),
|
||||
"last_commit", verifyBlock.LastCommit,
|
||||
"block_id", newBlockID,
|
||||
"height", r.lastTrustedBlock.block.Height,
|
||||
)
|
||||
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 2)
|
||||
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
|
||||
NodeID: peerID,
|
||||
Err: err,
|
||||
}); serr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Verify NewBlock usign the validator set obtained after applying the last block
|
||||
// Note: VerifyAdjacent in the LightClient relies on a trusting period which is not applicable here
|
||||
// ToDo: We need witness verification here as well
|
||||
err := r.VerifyAdjacent(&types.SignedHeader{Header: &r.lastTrustedBlock.block.Header, Commit: r.lastTrustedBlock.commit}, &types.SignedHeader{Header: &newBlock.Header, Commit: verifyBlock.LastCommit}, state.NextValidators)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid last commit: %w", err)
|
||||
r.logger.Error(
|
||||
err.Error(),
|
||||
"last_commit", verifyBlock.LastCommit,
|
||||
"block_id", newBlockID,
|
||||
"height", r.lastTrustedBlock.block.Height,
|
||||
)
|
||||
|
||||
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 1)
|
||||
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
|
||||
NodeID: peerID,
|
||||
Err: err,
|
||||
}); serr != nil {
|
||||
return
|
||||
switch err.(type) {
|
||||
case ErrBlockIDDiff:
|
||||
case ErrValidationFailed:
|
||||
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 1)
|
||||
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
|
||||
NodeID: peerID,
|
||||
Err: errors.New("invalid block for verification"),
|
||||
}); serr != nil {
|
||||
return
|
||||
}
|
||||
case ErrInvalidVerifyBlock:
|
||||
r.logger.Error(
|
||||
err.Error(),
|
||||
"last_commit", verifyBlock.LastCommit,
|
||||
"block_id", newBlockID,
|
||||
"height", r.lastTrustedBlock.block.Height,
|
||||
)
|
||||
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 2)
|
||||
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
|
||||
NodeID: peerID,
|
||||
Err: err,
|
||||
}); serr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// we need to load the last block we trusted
|
||||
if r.initialState.LastBlockHeight != 0 {
|
||||
r.lastTrustedBlock = &BlockResponse{r.store.LoadBlock(r.initialState.LastBlockHeight), r.store.LoadBlockCommit(r.initialState.LastBlockHeight)}
|
||||
if r.lastTrustedBlock == nil {
|
||||
panic("Failed to load last trusted block")
|
||||
}
|
||||
}
|
||||
// chainID := r.initialState.ChainID
|
||||
oldHash := r.initialState.Validators.Hash()
|
||||
if !bytes.Equal(oldHash, newBlock.ValidatorsHash) {
|
||||
|
||||
|
||||
108
internal/blocksync/verify.go
Normal file
108
internal/blocksync/verify.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func VerifyAdjacent(
|
||||
trustedHeader *types.SignedHeader, // height=X
|
||||
untrustedHeader *types.SignedHeader, // height=X+1
|
||||
untrustedVals *types.ValidatorSet, // height=X+1)
|
||||
) error {
|
||||
|
||||
if len(trustedHeader.NextValidatorsHash) == 0 {
|
||||
return errors.New("next validators hash in trusted header is empty")
|
||||
}
|
||||
|
||||
if untrustedHeader.Height != trustedHeader.Height+1 {
|
||||
return errors.New("headers must be adjacent in height")
|
||||
}
|
||||
|
||||
if err := untrustedHeader.ValidateBasic(trustedHeader.ChainID); err != nil {
|
||||
return fmt.Errorf("untrustedHeader.ValidateBasic failed: %w", err)
|
||||
}
|
||||
|
||||
if untrustedHeader.Height <= trustedHeader.Height {
|
||||
return fmt.Errorf("expected new header height %d to be greater than one of old header %d",
|
||||
untrustedHeader.Height,
|
||||
trustedHeader.Height)
|
||||
}
|
||||
|
||||
if !untrustedHeader.Time.After(trustedHeader.Time) {
|
||||
return fmt.Errorf("expected new header time %v to be after old header time %v",
|
||||
untrustedHeader.Time,
|
||||
trustedHeader.Time)
|
||||
}
|
||||
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) {
|
||||
return fmt.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d",
|
||||
untrustedHeader.ValidatorsHash,
|
||||
untrustedVals.Hash(),
|
||||
untrustedHeader.Height,
|
||||
)
|
||||
}
|
||||
|
||||
// Check the validator hashes are the same
|
||||
if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) {
|
||||
err := fmt.Errorf("expected old header's next validators (%X) to match those from new header (%X)",
|
||||
trustedHeader.NextValidatorsHash,
|
||||
untrustedHeader.ValidatorsHash,
|
||||
)
|
||||
return light.ErrInvalidHeader{Reason: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ErrBlockIDDiff struct {
|
||||
Reason error
|
||||
}
|
||||
|
||||
func (e ErrBlockIDDiff) Error() string {
|
||||
return "block ID in lastCommit of new block is not matching trusted block ID"
|
||||
}
|
||||
|
||||
type ErrInvalidVerifyBlock struct {
|
||||
Reason error
|
||||
}
|
||||
|
||||
func (e ErrInvalidVerifyBlock) Error() string {
|
||||
return "last commit of invalid block used to verify new block"
|
||||
}
|
||||
|
||||
type ErrValidationFailed struct {
|
||||
Reason error
|
||||
}
|
||||
|
||||
func (e ErrValidationFailed) Error() string {
|
||||
return "failed to verify next block"
|
||||
}
|
||||
|
||||
func VerifyNextBlock(newBlock *types.Block, newBlockID types.BlockID, verifyBlock *types.Block, trustedBlock *types.Block, trustedCommit *types.Commit, validators *types.ValidatorSet) error {
|
||||
|
||||
// If the blockID in LastCommit of NewBlock does not match the trusted block
|
||||
// we can assume NewBlock is not correct
|
||||
if !(newBlock.LastCommit.BlockID.Equals(trustedCommit.BlockID)) {
|
||||
return ErrBlockIDDiff{}
|
||||
}
|
||||
|
||||
// Todo: Verify verifyBlock.LastCommit validators against state.NextValidators
|
||||
// If they do not match, need a new verifyBlock
|
||||
if err := validators.VerifyCommitLight(trustedBlock.ChainID, newBlockID, newBlock.Height, verifyBlock.LastCommit); err != nil {
|
||||
return ErrInvalidVerifyBlock{Reason: err}
|
||||
}
|
||||
|
||||
// Verify NewBlock usign the validator set obtained after applying the last block
|
||||
// Note: VerifyAdjacent in the LightClient relies on a trusting period which is not applicable here
|
||||
// ToDo: We need witness verification here as well and backwards verification from a state where we can trust validators
|
||||
if err := VerifyAdjacent(&types.SignedHeader{Header: &trustedBlock.Header, Commit: trustedCommit}, &types.SignedHeader{Header: &newBlock.Header, Commit: verifyBlock.LastCommit}, validators); err != nil {
|
||||
return ErrValidationFailed{Reason: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user