Documentation
¶
Overview ¶
Package anchorverify hardens Lantern's boot-time trusted-root selection (security issue #54).
Background. Every block Lantern fetches downstream of the anchor is content-address-verified (hamt.VerifyBlockCID). That protects the *integrity* of state under a given root: a malicious source cannot forge state for a root we already trust. It does NOT protect the *choice* of root — i.e. "is this the canonical/heaviest chain, or an attacker's valid-but-non-canonical fork (or a stale, superseded head)?".
The default daemon boot historically accepted the head returned by a single source (the gateway, or the Glif fallback) on faith. This package closes that gap with three independent, cheap checks that need no full header-sync pipeline:
Multi-source agreement. Fetch the head from >=2 independent sources and require them to agree on (StateRoot, TipSetKey). Agreement across independent operators is a strong signal the head is canonical.
F3 finality cross-check. When an F3 latest finality certificate is available, require the selected anchor to be consistent with the F3-finalized tipset (equal to it, or a descendant of it by epoch). F3 certs carry BLS-aggregate signatures from the power table, so this is a cryptographic anchor, not a trust-the-endpoint anchor.
Heaviest-weight tiebreak. On disagreement with no F3 resolution, pick the candidate with the greatest ParentWeight (Filecoin's fork-choice rule) and emit a loud warning rather than silently trusting source #1.
An operator can bypass all of this with InsecureAllowSingleSource (the --insecure-anchor flag) for localhost/dev against a single endpoint.
Index ¶
Constants ¶
const GatherTimeout = 12 * time.Second
GatherTimeout bounds how long Gather waits for all sources combined.
Variables ¶
var ( // ErrNoCandidates means every source failed to return a head. ErrNoCandidates = errors.New("anchorverify: no usable head candidates from any source") // ErrNoAgreement means sources disagreed and nothing (F3 or weight) // could safely resolve the canonical head. ErrNoAgreement = errors.New("anchorverify: sources disagree on head and no F3 finality could resolve it") // ErrF3Conflict means the agreed/selected head conflicts with the // F3-finalized chain (selected epoch is at/below F3 finality but the // tipset differs — a fork below finality, which must never be accepted). ErrF3Conflict = errors.New("anchorverify: selected head conflicts with F3 finalized tipset") )
Functions ¶
This section is empty.
Types ¶
type Candidate ¶
type Candidate struct {
Source string // human label, e.g. "gateway", "glif"
Epoch abi.ChainEpoch
StateRoot cid.Cid
TipSetKey ltypes.TipSetKey
ParentWeight ltypes.BigInt
}
Candidate is one source's view of the current head, normalized to the fields the verifier compares. Sources that cannot supply a field leave it at its zero value; only StateRoot + TipSetKey + Epoch are load-bearing.
func Gather ¶
func Gather(ctx context.Context, pol Policy, fetchers ...HeadFetcher) []Candidate
Gather queries every fetcher (best-effort, in parallel-ish sequence) and returns all candidates that succeeded. Failures are reported via warnf but do not abort — the verifier decides whether the survivors are enough.
type F3Finalized ¶
type F3Finalized struct {
Available bool
Instance uint64
Epoch abi.ChainEpoch
TipSetKey ltypes.TipSetKey
}
F3Finalized is the (epoch, tipset) the latest F3 finality certificate finalized. Used for the cryptographic cross-check. Zero value means "no F3 finality available", which makes the cross-check a no-op (multi-source agreement still applies).
func FinalizedFromCert ¶
func FinalizedFromCert(cert *certs.FinalityCertificate) (F3Finalized, error)
FinalizedFromCert extracts the finalized (epoch, tipset key) from an F3 finality certificate. Returns Available=false on any nil/empty cert so callers can treat "no F3" uniformly.
type HeadFetcher ¶
HeadFetcher fetches one source's current head as a Candidate. Implemented by thin adapters over the gateway/Glif clients in cmd/lantern.
type Policy ¶
type Policy struct {
// MinAgreeingSources is the number of independent sources that must
// agree on the same (StateRoot, TipSetKey) for an anchor to be
// accepted without an F3 cross-check or weight tiebreak. Default 2.
MinAgreeingSources int
// InsecureAllowSingleSource bypasses agreement + F3 + tiebreak and
// accepts a single source on faith. Maps to --insecure-anchor. Intended
// for localhost/dev only.
InsecureAllowSingleSource bool
// Warnf, if set, receives human-readable warnings (e.g. source
// disagreement). Defaults to a no-op.
Warnf func(format string, args ...any)
}
Policy controls how strict the verifier is.
type Result ¶
type Result struct {
// Chosen is the accepted candidate.
Chosen Candidate
// Method explains how Chosen was selected, for logging/audit.
Method string
// AgreeingSources is how many sources backed Chosen's identity.
AgreeingSources int
// F3Checked is true when an F3 finality cross-check was applied.
F3Checked bool
}
Result is the outcome of a verification.
func Verify ¶
func Verify(cands []Candidate, f3 F3Finalized, pol Policy) (Result, error)
Verify applies the policy to the gathered candidates + optional F3 finality and returns the accepted candidate or an error.
Decision order:
insecure? -> accept first valid candidate (loud no-op of checks) 0 valid candidates -> ErrNoCandidates F3 available: reject any candidate that conflicts below finality (ErrF3Conflict) prefer the largest agreeing group consistent with F3 agreement >= Min -> accept agreed identity no agreement: if F3 available, accept the F3-consistent heaviest candidate else ErrNoAgreement