mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
136 lines
3.6 KiB
Go
136 lines
3.6 KiB
Go
package global
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"errors"
|
|
|
|
"go.uber.org/zap"
|
|
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
|
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/store"
|
|
)
|
|
|
|
// FrameChainReader captures the minimal subset of clock store functionality
|
|
// required for sequential frame verification.
|
|
type FrameChainReader interface {
|
|
GetGlobalClockFrame(uint64) (*protobufs.GlobalFrame, error)
|
|
GetGlobalClockFrameCandidate(uint64, []byte) (*protobufs.GlobalFrame, error)
|
|
}
|
|
|
|
// FrameChainChecker verifies whether a proposal's parent chain can be linked
|
|
// through stored frame candidates or sealed frames.
|
|
type FrameChainChecker struct {
|
|
store FrameChainReader
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewFrameChainChecker creates a new FrameChainChecker.
|
|
func NewFrameChainChecker(
|
|
store FrameChainReader,
|
|
logger *zap.Logger,
|
|
) *FrameChainChecker {
|
|
if store == nil {
|
|
return nil
|
|
}
|
|
if logger == nil {
|
|
logger = zap.NewNop()
|
|
}
|
|
return &FrameChainChecker{store: store, logger: logger}
|
|
}
|
|
|
|
// CanProcessSequentialChain returns true if the proposal's ancestors can be
|
|
// chained back to an existing sealed frame or the finalized state.
|
|
func (c *FrameChainChecker) CanProcessSequentialChain(
|
|
finalized *models.State[*protobufs.GlobalFrame],
|
|
proposal *protobufs.GlobalProposal,
|
|
) bool {
|
|
if c == nil || c.store == nil || proposal == nil ||
|
|
proposal.State == nil || proposal.State.Header == nil {
|
|
return false
|
|
}
|
|
|
|
parentSelector := proposal.State.Header.ParentSelector
|
|
if len(parentSelector) == 0 {
|
|
return false
|
|
}
|
|
|
|
frameNumber := proposal.State.Header.FrameNumber
|
|
if frameNumber == 0 {
|
|
return false
|
|
}
|
|
|
|
for frameNumber > 0 && len(parentSelector) > 0 {
|
|
frameNumber--
|
|
|
|
if sealed, err := c.store.GetGlobalClockFrame(frameNumber); err == nil &&
|
|
sealed != nil &&
|
|
bytes.Equal([]byte(sealed.Identity()), parentSelector) {
|
|
c.logger.Debug(
|
|
"frame chain linked to sealed frame",
|
|
zap.Uint64("sealed_frame_number", frameNumber),
|
|
)
|
|
return true
|
|
} else if err != nil && !errors.Is(err, store.ErrNotFound) {
|
|
c.logger.Warn(
|
|
"failed to read sealed frame during chain validation",
|
|
zap.Uint64("frame_number", frameNumber),
|
|
zap.Error(err),
|
|
)
|
|
return false
|
|
}
|
|
|
|
candidate, err := c.store.GetGlobalClockFrameCandidate(
|
|
frameNumber,
|
|
parentSelector,
|
|
)
|
|
if err == nil && candidate != nil {
|
|
if candidate.Header == nil ||
|
|
candidate.Header.FrameNumber != frameNumber {
|
|
c.logger.Debug(
|
|
"candidate frame had mismatched header",
|
|
zap.Uint64("frame_number", frameNumber),
|
|
)
|
|
return false
|
|
}
|
|
c.logger.Debug(
|
|
"frame chain matched candidate",
|
|
zap.Uint64("candidate_frame_number", frameNumber),
|
|
)
|
|
parentSelector = candidate.Header.ParentSelector
|
|
continue
|
|
}
|
|
if err != nil && !errors.Is(err, store.ErrNotFound) {
|
|
c.logger.Warn(
|
|
"failed to read candidate frame during chain validation",
|
|
zap.Uint64("frame_number", frameNumber),
|
|
zap.Error(err),
|
|
)
|
|
return false
|
|
}
|
|
|
|
if finalized != nil && finalized.State != nil &&
|
|
(*finalized.State).Header != nil &&
|
|
frameNumber == (*finalized.State).Header.FrameNumber &&
|
|
bytes.Equal([]byte(finalized.Identifier), parentSelector) {
|
|
c.logger.Debug(
|
|
"frame chain linked to finalized frame",
|
|
zap.Uint64("finalized_frame_number", frameNumber),
|
|
)
|
|
return true
|
|
}
|
|
|
|
c.logger.Debug(
|
|
"missing ancestor frame while validating chain",
|
|
zap.Uint64("missing_frame_number", frameNumber),
|
|
zap.String(
|
|
"expected_parent_selector",
|
|
hex.EncodeToString(parentSelector),
|
|
),
|
|
)
|
|
return false
|
|
}
|
|
|
|
return false
|
|
}
|