func (sc *SigChain) verifySubchain(kf KeyFamily, links []*ChainLink) (cached bool, cki *ComputedKeyInfos, err error) {
	un := sc.username
	sc.G().Log.Debug("+ verifySubchain")
	defer func() {
		sc.G().Log.Debug("- verifySubchain -> %v, %s", cached, ErrToOk(err))
	}()
	if len(links) == 0 {
		err = InternalError{"verifySubchain should never get an empty chain."}
		return
	}
	last := links[len(links)-1]
	if cki = last.GetSigCheckCache(); cki != nil {
		cached = true
		sc.G().Log.Debug("Skipped verification (cached): %s", last.id)
		return
	}
	cki = NewComputedKeyInfos(sc.G())
	ckf := ComputedKeyFamily{kf: &kf, cki: cki, Contextified: sc.Contextified}
	first := true
	for linkIndex, link := range links {
		if isBad, reason := link.IsBad(); isBad {
			sc.G().Log.Debug("Ignoring bad chain link with sig ID %s: %s", link.GetSigID(), reason)
			continue
		}
		newKID := link.GetKID()
		tcl, w := NewTypedChainLink(link)
		if w != nil {
			w.Warn(sc.G())
		}
		sc.G().VDL.Log(VLog1, "| Verify link: %s", link.id)
		if first {
			if err = ckf.InsertEldestLink(tcl, un); err != nil {
				return
			}
			first = false
		}
		isDelegating := (tcl.GetRole() != DLGNone)
		isModifyingKeys := isDelegating || tcl.Type() == PGPUpdateType
		isFinalLink := (linkIndex == len(links)-1)
		isLastLinkInSameKeyRun := (isFinalLink || newKID != links[linkIndex+1].GetKID())
		if pgpcl, ok := tcl.(*PGPUpdateChainLink); ok {
			if hash := pgpcl.GetPGPFullHash(); hash != "" {
				sc.G().Log.Debug("| Setting active PGP hash for %s: %s", pgpcl.kid, hash)
				ckf.SetActivePGPHash(pgpcl.kid, hash)
			}
		}
		if isModifyingKeys || isFinalLink || isLastLinkInSameKeyRun {
			_, err = link.VerifySigWithKeyFamily(ckf)
			if err != nil {
				sc.G().Log.Debug("| Failure in VerifySigWithKeyFamily: %s", err)
				return
			}
		}
		if isDelegating {
			err = ckf.Delegate(tcl)
			if err != nil {
				sc.G().Log.Debug("| Failure in Delegate: %s", err)
				return
			}
		}
		if err = tcl.VerifyReverseSig(ckf); err != nil {
			sc.G().Log.Debug("| Failure in VerifyReverseSig: %s", err)
			return
		}
		if err = ckf.Revoke(tcl); err != nil {
			return
		}
		if err = ckf.UpdateDevices(tcl); err != nil {
			return
		}
		if err != nil {
			return
		}
	}
	last.PutSigCheckCache(cki)
	return
}
verifySubchain verifies the given subchain and outputs a yes/no answer
on whether or not it's well-formed, and also yields ComputedKeyInfos for
all keys found in the process, including those that are now retired.
package libkb
import (
	"fmt"
	"io"
	"time"
	keybase1 "github.com/keybase/client/go/protocol"
)
type SigChain struct {
	Contextified
	uid               keybase1.UID
	username          NormalizedUsername
	chainLinks        []*ChainLink
	idVerified        bool
	allKeys           bool
	loadedFromLinkOne bool
	wasFullyCached    bool
	localCki *ComputedKeyInfos
	localChainTail *MerkleTriple
	localChainUpdateTime time.Time
}
func (sc SigChain) Len() int {
	return len(sc.chainLinks)
}
func (sc *SigChain) LocalDelegate(kf *KeyFamily, key GenericKey, sigID keybase1.SigID, signingKid keybase1.KID, isSibkey bool) (err error) {
	cki := sc.localCki
	l := sc.GetLastLink()
	if cki == nil && l != nil && l.cki != nil {
		cki = l.cki.ShallowCopy()
	}
	if cki == nil {
		sc.G().Log.Debug("LocalDelegate: creating new cki (signingKid: %s)", signingKid)
		cki = NewComputedKeyInfos(sc.G())
		cki.InsertLocalEldestKey(signingKid)
	}
	sc.localCki = cki
	if len(sigID) > 0 {
		var zeroTime time.Time
		err = cki.Delegate(key.GetKID(), NowAsKeybaseTime(0), sigID, signingKid, signingKid, ""  , isSibkey, time.Unix(0, 0), zeroTime)
	}
	return
}
func (sc SigChain) GetComputedKeyInfos() (cki *ComputedKeyInfos) {
	cki = sc.localCki
	if cki == nil {
		if l := sc.GetLastLink(); l != nil {
			if l.cki == nil {
				sc.G().Log.Debug("GetComputedKeyInfos: l.cki is nil")
			}
			cki = l.cki
		}
	}
	return
}
func (sc SigChain) GetFutureChainTail() (ret *MerkleTriple) {
	now := sc.G().Clock().Now()
	if sc.localChainTail != nil && now.Sub(sc.localChainUpdateTime) < ServerUpdateLag {
		ret = sc.localChainTail
	}
	return
}
func reverse(links []*ChainLink) {
	for i, j := 0, len(links)-1; i < j; i, j = i+1, j-1 {
		links[i], links[j] = links[j], links[i]
	}
}
func first(links []*ChainLink) (ret *ChainLink) {
	if len(links) == 0 {
		return nil
	}
	return links[0]
}
func last(links []*ChainLink) (ret *ChainLink) {
	if len(links) == 0 {
		return nil
	}
	return links[len(links)-1]
}
func (sc *SigChain) VerifiedChainLinks(fp PGPFingerprint) (ret []*ChainLink) {
	last := sc.GetLastLink()
	if last == nil || !last.sigVerified {
		return
	}
	start := -1
	for i := len(sc.chainLinks) - 1; i >= 0 && sc.chainLinks[i].MatchFingerprint(fp); i-- {
		start = i
	}
	if start >= 0 {
		ret = sc.chainLinks[start:]
	}
	return
}
func (sc *SigChain) Bump(mt MerkleTriple) {
	mt.Seqno = sc.GetLastKnownSeqno() + 1
	sc.G().Log.Debug("| Bumping SigChain LastKnownSeqno to %d", mt.Seqno)
	sc.localChainTail = &mt
	sc.localChainUpdateTime = sc.G().Clock().Now()
}
func (sc *SigChain) LoadFromServer(t *MerkleTriple, selfUID keybase1.UID) (dirtyTail *MerkleTriple, err error) {
	low := sc.GetLastLoadedSeqno()
	sc.loadedFromLinkOne = (low == Seqno(0) || low == Seqno(-1))
	sc.G().Log.Debug("+ Load SigChain from server (uid=%s, low=%d)", sc.uid, low)
	defer func() { sc.G().Log.Debug("- Loaded SigChain -> %s", ErrToOk(err)) }()
	res, err := sc.G().API.Get(APIArg{
		Endpoint:    "sig/get",
		NeedSession: false,
		Args: HTTPArgs{
			"uid": UIDArg(sc.uid),
			"low": I{int(low)},
		},
	})
	if err != nil {
		return
	}
	v := res.Body.AtKey("sigs")
	var lim int
	if lim, err = v.Len(); err != nil {
		return
	}
	foundTail := false
	sc.G().Log.Debug("| Got back %d new entries", lim)
	var links []*ChainLink
	var tail *ChainLink
	for i := 0; i < lim; i++ {
		var link *ChainLink
		if link, err = ImportLinkFromServer(sc.G(), sc, v.AtIndex(i), selfUID); err != nil {
			return
		}
		if link.GetSeqno() <= low {
			continue
		}
		if selfUID.Equal(link.GetUID()) {
			sc.G().Log.Debug("| Setting isOwnNewLinkFromServer=true for seqno %d", link.GetSeqno())
			link.isOwnNewLinkFromServer = true
		}
		links = append(links, link)
		if !foundTail && t != nil {
			if foundTail, err = link.checkAgainstMerkleTree(t); err != nil {
				return
			}
		}
		tail = link
	}
	if t != nil && !foundTail {
		err = NewServerChainError("Failed to reach (%s, %d) in server response",
			t.LinkID, int(t.Seqno))
		return
	}
	if tail != nil {
		dirtyTail = tail.ToMerkleTriple()
		if sc.localChainTail != nil && sc.localChainTail.Less(*dirtyTail) {
			sc.G().Log.Debug("| Clear cached last (%d < %d)", sc.localChainTail.Seqno, dirtyTail.Seqno)
			sc.localChainTail = nil
			sc.localCki = nil
		}
	}
	sc.chainLinks = append(sc.chainLinks, links...)
	return
}
func (sc *SigChain) getFirstSeqno() (ret Seqno) {
	if len(sc.chainLinks) > 0 {
		ret = sc.chainLinks[0].GetSeqno()
	}
	return ret
}
func (sc *SigChain) VerifyChain() (err error) {
	sc.G().Log.Debug("+ SigChain::VerifyChain()")
	defer func() {
		sc.G().Log.Debug("- SigChain::VerifyChain() -> %s", ErrToOk(err))
	}()
	for i := len(sc.chainLinks) - 1; i >= 0; i-- {
		curr := sc.chainLinks[i]
		if curr.chainVerified {
			sc.G().Log.Debug("| short-circuit at link %d", i)
			break
		}
		if err = curr.VerifyLink(); err != nil {
			return
		}
		if i > 0 {
			prev := sc.chainLinks[i-1]
			if !prev.id.Eq(curr.GetPrev()) {
				return ChainLinkPrevHashMismatchError{fmt.Sprintf("Chain mismatch at seqno=%d", curr.GetSeqno())}
			}
			if prev.GetSeqno()+1 != curr.GetSeqno() {
				return ChainLinkWrongSeqnoError{fmt.Sprintf("Chain seqno mismatch at seqno=%d (previous=%d)", curr.GetSeqno(), prev.GetSeqno())}
			}
		}
		if err = curr.CheckNameAndID(sc.username, sc.uid); err != nil {
			return
		}
		curr.chainVerified = true
	}
	return
}
func (sc SigChain) GetCurrentTailTriple() (ret *MerkleTriple) {
	if l := sc.GetLastLink(); l != nil {
		ret = l.ToMerkleTriple()
	}
	return
}
func (sc SigChain) GetLastLoadedID() (ret LinkID) {
	if l := last(sc.chainLinks); l != nil {
		ret = l.id
	}
	return
}
func (sc SigChain) GetLastKnownID() (ret LinkID) {
	if sc.localChainTail != nil {
		ret = sc.localChainTail.LinkID
	} else {
		ret = sc.GetLastLoadedID()
	}
	return
}
func (sc SigChain) GetFirstLink() *ChainLink {
	return first(sc.chainLinks)
}
func (sc SigChain) GetLastLink() *ChainLink {
	return last(sc.chainLinks)
}
func (sc SigChain) GetLastKnownSeqno() (ret Seqno) {
	sc.G().Log.Debug("+ GetLastKnownSeqno()")
	defer func() {
		sc.G().Log.Debug("- GetLastKnownSeqno() -> %d", ret)
	}()
	if sc.localChainTail != nil {
		sc.G().Log.Debug("| Cached in last summary object...")
		ret = sc.localChainTail.Seqno
	} else {
		ret = sc.GetLastLoadedSeqno()
	}
	return
}
func (sc SigChain) GetLastLoadedSeqno() (ret Seqno) {
	sc.G().Log.Debug("+ GetLastLoadedSeqno()")
	defer func() {
		sc.G().Log.Debug("- GetLastLoadedSeqno() -> %d", ret)
	}()
	if l := last(sc.chainLinks); l != nil {
		sc.G().Log.Debug("| Fetched from main chain")
		ret = l.GetSeqno()
	}
	return
}
func (sc *SigChain) Store() (err error) {
	for i := len(sc.chainLinks) - 1; i >= 0; i-- {
		link := sc.chainLinks[i]
		var didStore bool
		if didStore, err = link.Store(sc.G()); err != nil || !didStore {
			return
		}
	}
	return nil
}
var hardcodedResets = map[keybase1.SigID]bool{
	"11111487aa193b9fafc92851176803af8ed005983cad1eaf5d6a49a459b8fffe0f": true,
	"df0005f6c61bd6efd2867b320013800781f7f047e83fd44d484c2cb2616f019f0f": true,
	"32eab86aa31796db3200f42f2553d330b8a68931544bbb98452a80ad2b0003d30f": true,
	"5ed7a3356fd0f759a4498fc6fed1dca7f62611eb14f782a2a9cda1b836c58db50f": true,
	"d5fe2c5e31958fe45a7f42b325375d5bd8916ef757f736a6faaa66a6b18bec780f": true,
	"1e116e81bc08b915d9df93dc35c202a75ead36c479327cdf49a15f3768ac58f80f": true,
}
func (sc *SigChain) GetCurrentSubchain(eldest keybase1.KID) (links []*ChainLink, err error) {
	if sc.chainLinks == nil {
		return
	}
	l := len(sc.chainLinks)
	lastGood := l
	for i := l - 1; i >= 0; i-- {
		if sc.chainLinks[i].ToEldestKID().Equal(eldest) {
			lastGood = i
		} else {
			break
		}
		if sc.chainLinks[i].unpacked.typ == string(EldestType) {
			break
		}
		if hardcodedResets[sc.chainLinks[i].unpacked.sigID] {
			break
		}
	}
	if lastGood == 0 {
		links = sc.chainLinks
	} else {
		links = sc.chainLinks[lastGood:]
	}
	return
}
func (sc *SigChain) Dump(w io.Writer) {
	fmt.Fprintf(w, "sigchain dump\n")
	for i, l := range sc.chainLinks {
		fmt.Fprintf(w, "link %d: %+v\n", i, l)
	}
	fmt.Fprintf(w, "last known seqno: %d\n", sc.GetLastKnownSeqno())
	fmt.Fprintf(w, "last known id: %s\n", sc.GetLastKnownID())
}