/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/engine/scanproofs.go
501 строка
12 KB
zoom-ua
modernize error handling with errors.As/Is (#29350)
25 июн 2026, 17:41
Не верифицирован
25 июн 2026, 17:41
e09e254
Код
Авторство
О чём код?
// Copyright 2016 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. package engine import ( "bufio" "encoding/csv" "encoding/gob" "errors" "fmt" "io" "os" "strconv" "strings" "time" "github.com/keybase/client/go/libkb" keybase1 "github.com/keybase/client/go/protocol/keybase1" ) type ScanProofsCacheData struct { // Map from sigid to whether the proof is ok. Proofs map[string]bool } type ScanProofsCache struct { data ScanProofsCacheData dirty bool } func NewScanProofsCache() *ScanProofsCache { return &ScanProofsCache{ data: ScanProofsCacheData{ Proofs: make(map[string]bool), }, dirty: false, } } func LoadScanProofsCache(filepath string) (*ScanProofsCache, error) { f, err := os.Open(filepath) if err != nil { return nil, err } dec := gob.NewDecoder(f) var c ScanProofsCache err = dec.Decode(&c.data) if err != nil { return nil, err } return &c, nil } func (c *ScanProofsCache) Get(sigID string) bool { return c.data.Proofs[sigID] } func (c *ScanProofsCache) Set(sigID string) { if !c.data.Proofs[sigID] { c.dirty = true } c.data.Proofs[sigID] = true } func (c *ScanProofsCache) Save(filepath string) error { if !c.dirty { // Don't save if nothing has changed return nil } temppath, f, err := libkb.OpenTempFile(filepath, "", 0o644) if err != nil { return err } enc := gob.NewEncoder(f) err = enc.Encode(c.data) if err != nil { f.Close() return err } f.Close() err = os.Rename(temppath, filepath) if err != nil { return err } c.dirty = false return nil } type ScanProofsTickers map[keybase1.ProofType]*time.Ticker type ScanProofsEngine struct { libkb.Contextified infile string indices string sigid string ratelimit int cachefile string ignorefile string } var _ Engine2 = (*ScanProofsEngine)(nil) func NewScanProofsEngine(infile string, indices string, sigid string, ratelimit int, cachefile string, ignorefile string, g *libkb.GlobalContext) *ScanProofsEngine { return &ScanProofsEngine{ infile: infile, indices: indices, sigid: sigid, ratelimit: ratelimit, cachefile: cachefile, ignorefile: ignorefile, Contextified: libkb.NewContextified(g), } } func (e *ScanProofsEngine) Name() string { return "ScanProofs" } func (e *ScanProofsEngine) Prereqs() Prereqs { return Prereqs{} } func (e *ScanProofsEngine) RequiredUIs() []libkb.UIKind { return []libkb.UIKind{ libkb.LogUIKind, } } func (e *ScanProofsEngine) SubConsumers() []libkb.UIConsumer { return []libkb.UIConsumer{} } func (e *ScanProofsEngine) Run(m libkb.MetaContext) (err error) { defer m.Trace("ScanProofsEngine#Run", &err)() var cache *ScanProofsCache saveevery := 10 var ignored []string if len(e.cachefile) > 0 { lcache, err := LoadScanProofsCache(e.cachefile) if err == nil { m.Info("Using cache: %v (%v entries)", e.cachefile, len(lcache.data.Proofs)) cache = lcache } else { m.Warning("Could not load cache: %v", err) cache = NewScanProofsCache() } } if len(e.ignorefile) > 0 { ignored, err = LoadScanProofsIgnore(e.ignorefile) if err != nil { return fmt.Errorf("Could not open ignore file: %v", err) } m.Info("Using ignore file: %v (%v entries)", e.ignorefile, len(ignored)) } if len(e.sigid) > 0 && len(e.indices) > 0 { return fmt.Errorf("Only one of sigid and indices allowed") } // One ticker for each proof type. tickers := make(map[keybase1.ProofType]*time.Ticker) m.Info("Running with ratelimit: %v ms", e.ratelimit) if e.ratelimit < 0 { return fmt.Errorf("Ratelimit value can not be negative: %v", e.ratelimit) } if e.ratelimit > 0 { for _, ptype := range keybase1.ProofTypeMap { switch ptype { case keybase1.ProofType_GENERIC_WEB_SITE, keybase1.ProofType_DNS: // Web sites and DNS do not need a rate limit. default: tickers[ptype] = time.NewTicker(time.Millisecond * time.Duration(e.ratelimit)) } } } defer func(tickers *map[keybase1.ProofType]*time.Ticker) { for _, ticker := range *tickers { if ticker != nil { ticker.Stop() } } }(&tickers) f, err := os.Open(e.infile) if err != nil { return err } r := csv.NewReader(f) var records []map[string]string header, err := r.Read() if err != nil { return fmt.Errorf("Could not read header: %v", err) } m.Debug("Reading csv... ") for { rec, err := r.Read() if errors.Is(err, io.EOF) { break } if err != nil { return err } record := make(map[string]string) for i, val := range rec { record[header[i]] = val } records = append(records, record) } m.Debug("done") startindex := 0 endindex := len(records) if len(e.indices) > 0 { startindex, endindex, err = e.ParseIndices(e.indices) if err != nil { return err } } if startindex < 0 { return fmt.Errorf("Invalid start index: %v", startindex) } if endindex > len(records) { return fmt.Errorf("Invalid end index: %v (%v records)", endindex, len(records)) } nrun := 0 nok := 0 for i := startindex; i < endindex; i++ { rec := records[i] if len(e.sigid) > 0 && e.sigid != rec["sig_id"] { continue } m.Info("i:%v user:%v type:%v sigid:%v", i, rec["username"], rec["proof_type"], rec["sig_id"]) err := e.ProcessOne(m, i, rec, cache, ignored, tickers) nrun++ if err == nil { m.Info("Ok\n") nok++ if cache != nil { cache.Set(rec["sig_id"]) if i%saveevery == 0 { saveerr := cache.Save(e.cachefile) if saveerr != nil { m.Warning("Could not save cache: %v", saveerr) } } } } else { m.Error("%v FAILED: %v\n", i, err) } } m.Info("---") m.Info("proofs checked : %v", nrun) m.Info("oks : %v", nok) m.Info("fails : %v", nrun-nok) if cache != nil { saveerr := cache.Save(e.cachefile) if saveerr != nil { m.Warning("Could not save cache: %v", saveerr) } } return nil } func (e *ScanProofsEngine) ProcessOne(m libkb.MetaContext, i int, rec map[string]string, cache *ScanProofsCache, ignored []string, tickers ScanProofsTickers) error { serverstate, err := strconv.Atoi(rec["state"]) if err != nil { return fmt.Errorf("Could not read server state: %v", err) } shouldsucceed := true skip := false var skipreason string badstate := false switch keybase1.ProofState(serverstate) { case keybase1.ProofState_NONE: badstate = true case keybase1.ProofState_OK: case keybase1.ProofState_TEMP_FAILURE: shouldsucceed = false case keybase1.ProofState_PERM_FAILURE: shouldsucceed = false case keybase1.ProofState_LOOKING: skip = true skipreason = "server LOOKING" case keybase1.ProofState_SUPERSEDED: skip = true skipreason = "server SUPERSEDED" case keybase1.ProofState_POSTED: badstate = true case keybase1.ProofState_REVOKED: skip = true skipreason = "server REVOKED" case keybase1.ProofState_DELETED: skip = true skipreason = "server DELETED" default: badstate = true } if cache != nil && cache.Get(rec["sig_id"]) { skip = true skipreason = "cached success" } for _, x := range ignored { if x == rec["sig_id"] { skip = true skipreason = "in ignored list" } } if badstate { return fmt.Errorf("Unsupported serverstate: %v", serverstate) } if skip { m.Info("skipping: %v", skipreason) return nil } deluserstr := "Error loading user: Deleted" perr1, foundhint1, err := e.CheckOne(m, rec, tickers) if err != nil { if err.Error() == deluserstr { m.Info("deleted user") return nil } return err } // Skip the rate limit on the second check. perr2, foundhint2, err := e.CheckOne(m, rec, nil) if err != nil { return err } if foundhint1 != foundhint2 { return fmt.Errorf("Local verifiers disagree: foundhint1:%v foundhint:%v (likely timing)", foundhint1, foundhint2) } if (perr1 == nil) != (perr2 == nil) { return fmt.Errorf("Local verifiers disagree:\n %v\n %v", perr1, perr2) } succeeded := foundhint1 && (perr1 == nil) if succeeded != shouldsucceed { return fmt.Errorf("Local verifiers disagree with server: server:%v client:%v", serverstate, perr1) } return nil } // CheckOne checks one proof using two checkers (default, pvl). // NOTE: This doesn't make sense anymore because pvl is the default. // Returns nil or an error, whether a hint was found, and any more serious error func (e *ScanProofsEngine) CheckOne(m libkb.MetaContext, rec map[string]string, tickers ScanProofsTickers) (libkb.ProofError, bool, error) { uid := keybase1.UID(rec["uid"]) sigid := keybase1.SigID(rec["sig_id"]) foundhint := false hint, err := e.GetSigHint(m, uid, sigid) if err != nil { return nil, foundhint, err } if hint == nil { return nil, foundhint, nil } foundhint = true link, err := e.GetRemoteProofChainLink(m, uid, sigid) if err != nil { return nil, foundhint, err } pc, err := libkb.MakeProofChecker(m, m.G().GetProofServices(), link) if err != nil { return nil, foundhint, err } // Beyond this point, external requests will occur, and rate limiting is used ptype := link.GetProofType() if tickers[ptype] != nil { m.Info("Waiting for ticker: %v (%v)", keybase1.ProofTypeRevMap[ptype], ptype) <-tickers[ptype].C } pvlSource := m.G().GetPvlSource() if pvlSource == nil { return nil, foundhint, fmt.Errorf("no pvl source for proof verification") } pvlU, err := pvlSource.GetLatestEntry(m) if err != nil { return nil, foundhint, fmt.Errorf("error getting pvl: %s", err) } if _, perr := pc.CheckStatus(m, *hint, libkb.ProofCheckerModeActive, pvlU); perr != nil { return perr, foundhint, nil } return nil, foundhint, nil } // GetSigHint gets the SigHint. This can return (nil, nil) if nothing goes wrong but there is no hint. func (e *ScanProofsEngine) GetSigHint(m libkb.MetaContext, uid keybase1.UID, sigid keybase1.SigID) (*libkb.SigHint, error) { sighints, err := libkb.LoadAndRefreshSigHints(m, uid) if err != nil { return nil, err } sighint := sighints.Lookup(sigid) if sighint == nil { return nil, nil } return sighint, nil } func (e *ScanProofsEngine) GetRemoteProofChainLink(m libkb.MetaContext, uid keybase1.UID, sigid keybase1.SigID) (libkb.RemoteProofChainLink, error) { user, err := libkb.LoadUser(libkb.NewLoadUserArgWithMetaContext(m).WithUID(uid)) if err != nil { return nil, fmt.Errorf("Error loading user: %v", err) } link := user.LinkFromSigID(sigid) if link == nil { return nil, fmt.Errorf("Could not find link from sigid") } tlink, w := libkb.NewTypedChainLink(link) if w != nil { return nil, fmt.Errorf("Could not get typed chain link: %v", w.Warning()) } switch vlink := tlink.(type) { case libkb.RemoteProofChainLink: return vlink, nil default: return nil, fmt.Errorf("Link is not a RemoteProofChainLink: %v", tlink) } } func (e *ScanProofsEngine) ParseIndices(indices string) (start int, end int, reterr error) { wrap := func(format string, arg ...any) error { f2 := fmt.Sprintf("Invalid indices: %s", format) return fmt.Errorf(f2, arg...) } ss := strings.Split(strings.TrimSpace(indices), ":") if len(ss) != 2 { return start, end, wrap("must be like start:end") } var err error start, err = strconv.Atoi(ss[0]) if err != nil { return start, end, wrap("could not convert start: %v", err) } end, err = strconv.Atoi(ss[1]) if err != nil { return start, end, wrap("could not convert end: %v", err) } if end <= start { return start, end, wrap("%v <= %v", end, start) } reterr = nil return } // LoadScanProofsIgnore loads an ignore file and returns the list of proofids to ignore. func LoadScanProofsIgnore(filepath string) ([]string, error) { f, err := os.Open(filepath) if err != nil { return nil, err } defer f.Close() scanner := bufio.NewScanner(f) scanner.Split(bufio.ScanLines) var ignored []string for scanner.Scan() { x := strings.TrimSpace(scanner.Text()) if strings.HasPrefix(x, "//") { continue } ignored = append(ignored, x) } return ignored, nil }