2020-09-26 08:04:07 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
2020-09-26 08:04:07 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
2021-09-21 19:59:25 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/config/params"
|
2021-09-23 15:23:37 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
2021-11-03 15:07:46 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
2021-10-01 20:17:57 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/time/slots"
|
2020-09-26 08:04:07 +00:00
|
|
|
)
|
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
var errWSBlockNotFound = errors.New("weak subjectivity root not found in db")
|
|
|
|
var errWSBlockNotFoundInEpoch = errors.New("weak subjectivity root not found in db within epoch")
|
2020-09-26 08:04:07 +00:00
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
type weakSubjectivityDB interface {
|
|
|
|
HasBlock(ctx context.Context, blockRoot [32]byte) bool
|
|
|
|
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type WeakSubjectivityVerifier struct {
|
|
|
|
enabled bool
|
|
|
|
verified bool
|
|
|
|
root [32]byte
|
|
|
|
epoch types.Epoch
|
|
|
|
slot types.Slot
|
|
|
|
db weakSubjectivityDB
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewWeakSubjectivityVerifier validates a checkpoint, and if valid, uses it to initialize a weak subjectivity verifier
|
|
|
|
func NewWeakSubjectivityVerifier(wsc *ethpb.Checkpoint, db weakSubjectivityDB) (*WeakSubjectivityVerifier, error) {
|
|
|
|
// TODO(7342): Weak subjectivity checks are currently optional. When we require the flag to be specified
|
|
|
|
// per 7342, a nil checkpoint, zero-root or zero-epoch should all fail validation
|
|
|
|
// and return an error instead of creating a WeakSubjectivityVerifier that permits any chain history.
|
|
|
|
if wsc == nil || len(wsc.Root) == 0 || wsc.Epoch == 0 {
|
|
|
|
return &WeakSubjectivityVerifier{
|
|
|
|
enabled: false,
|
|
|
|
}, nil
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
2021-11-03 15:07:46 +00:00
|
|
|
startSlot, err := slots.EpochStart(wsc.Epoch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
2021-11-03 15:07:46 +00:00
|
|
|
return &WeakSubjectivityVerifier{
|
|
|
|
enabled: true,
|
|
|
|
verified: false,
|
|
|
|
root: bytesutil.ToBytes32(wsc.Root),
|
|
|
|
epoch: wsc.Epoch,
|
|
|
|
db: db,
|
|
|
|
slot: startSlot,
|
|
|
|
}, nil
|
|
|
|
}
|
2020-09-26 08:04:07 +00:00
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
// VerifyWeakSubjectivity verifies the weak subjectivity root in the service struct.
|
|
|
|
// Reference design: https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/weak-subjectivity.md#weak-subjectivity-sync-procedure
|
|
|
|
func (v *WeakSubjectivityVerifier) VerifyWeakSubjectivity(ctx context.Context, finalizedEpoch types.Epoch) error {
|
|
|
|
if v.verified || !v.enabled {
|
|
|
|
return nil
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
2021-11-03 15:07:46 +00:00
|
|
|
|
|
|
|
// Two conditions are described in the specs:
|
|
|
|
// IF epoch_number > store.finalized_checkpoint.epoch,
|
|
|
|
// then ASSERT during block sync that block with root block_root
|
|
|
|
// is in the sync path at epoch epoch_number. Emit descriptive critical error if this assert fails,
|
|
|
|
// then exit client process.
|
|
|
|
// we do not handle this case ^, because we can only blocks that have been processed / are currently
|
|
|
|
// in line for finalization, we don't have the ability to look ahead. so we only satisfy the following:
|
|
|
|
// IF epoch_number <= store.finalized_checkpoint.epoch,
|
|
|
|
// then ASSERT that the block in the canonical chain at epoch epoch_number has root block_root.
|
|
|
|
// Emit descriptive critical error if this assert fails, then exit client process.
|
|
|
|
if v.epoch > finalizedEpoch {
|
|
|
|
return nil
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
2021-11-03 15:07:46 +00:00
|
|
|
log.Infof("Performing weak subjectivity check for root %#x in epoch %d", v.root, v.epoch)
|
2020-09-26 08:04:07 +00:00
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
if !v.db.HasBlock(ctx, v.root) {
|
|
|
|
return errors.Wrap(errWSBlockNotFound, fmt.Sprintf("missing root %#x", v.root))
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
2021-11-03 15:07:46 +00:00
|
|
|
filter := filters.NewFilter().SetStartSlot(v.slot).SetEndSlot(v.slot + params.BeaconConfig().SlotsPerEpoch)
|
2020-09-26 08:04:07 +00:00
|
|
|
// A node should have the weak subjectivity block corresponds to the correct epoch in the DB.
|
2021-11-03 15:07:46 +00:00
|
|
|
roots, err := v.db.BlockRoots(ctx, filter)
|
2020-09-26 08:04:07 +00:00
|
|
|
if err != nil {
|
2021-11-03 15:07:46 +00:00
|
|
|
return errors.Wrap(err, "error while retrieving block roots to verify weak subjectivity")
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|
|
|
|
for _, root := range roots {
|
2021-11-03 15:07:46 +00:00
|
|
|
if v.root == root {
|
2020-09-26 08:04:07 +00:00
|
|
|
log.Info("Weak subjectivity check has passed")
|
2021-11-03 15:07:46 +00:00
|
|
|
v.verified = true
|
2020-09-26 08:04:07 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 15:07:46 +00:00
|
|
|
return errors.Wrap(errWSBlockNotFoundInEpoch, fmt.Sprintf("root=%#x, epoch=%d", v.root, v.epoch))
|
2020-09-26 08:04:07 +00:00
|
|
|
}
|