github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/loader.go (about)

     1  package teams
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"sync"
     9  	"time"
    10  
    11  	"golang.org/x/net/context"
    12  
    13  	"github.com/keybase/client/go/gregor"
    14  	"github.com/keybase/client/go/libkb"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	hidden "github.com/keybase/client/go/teams/hidden"
    17  	storage "github.com/keybase/client/go/teams/storage"
    18  	pkgErrors "github.com/pkg/errors"
    19  )
    20  
    21  // Show detailed team profiling
    22  var teamEnv struct {
    23  	Profile             bool
    24  	UserPreloadEnable   bool
    25  	UserPreloadParallel bool
    26  	UserPreloadWait     bool
    27  	ProofSetParallel    bool
    28  }
    29  
    30  func init() {
    31  	teamEnv.Profile = os.Getenv("KEYBASE_TEAM_PROF") == "1"
    32  	teamEnv.UserPreloadEnable = os.Getenv("KEYBASE_TEAM_PE") == "1"
    33  	teamEnv.UserPreloadParallel = os.Getenv("KEYBASE_TEAM_PP") == "1"
    34  	teamEnv.UserPreloadWait = os.Getenv("KEYBASE_TEAM_PW") == "1"
    35  	teamEnv.ProofSetParallel = os.Getenv("KEYBASE_TEAM_SP") == "0"
    36  }
    37  
    38  // How long until the tail of a team sigchain is considered non-fresh
    39  const freshnessLimit = time.Duration(1) * time.Hour
    40  
    41  // Load a Team from the TeamLoader.
    42  // Can be called from inside the teams package.
    43  func Load(ctx context.Context, g *libkb.GlobalContext, lArg keybase1.LoadTeamArg) (*Team, error) {
    44  	teamData, hidden, err := g.GetTeamLoader().Load(ctx, lArg)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	team := NewTeam(ctx, g, teamData, hidden)
    49  
    50  	if lArg.RefreshUIDMapper {
    51  		// If we just loaded the group, then inform the UIDMapper of any UID->EldestSeqno
    52  		// mappings, so that we're guaranteed they aren't stale.
    53  		team.refreshUIDMapper(ctx, g)
    54  	}
    55  
    56  	// OK if it errors; just move on and return the team.
    57  	_, _ = team.calculateAndCacheMemberCount(ctx)
    58  
    59  	return team, nil
    60  }
    61  
    62  // Loader of keybase1.TeamData objects. Handles caching.
    63  // Because there is one of this global object and it is attached to G,
    64  // its Load interface must return a keybase1.TeamData not a teams.Team.
    65  // To load a teams.Team use the package-level function Load.
    66  // Threadsafe.
    67  type TeamLoader struct {
    68  	libkb.Contextified
    69  	world         LoaderContext
    70  	storage       *storage.Storage
    71  	merkleStorage *storage.Merkle
    72  	// Single-flight locks per team ID.
    73  	// (Private and public loads of the same ID will block each other, should be fine)
    74  	locktab *libkb.LockTable
    75  
    76  	// Cache lookups of team name -> ID for a few seconds, to absorb bursts of lookups
    77  	// from the frontend
    78  	nameLookupBurstCache *libkb.BurstCache
    79  
    80  	// We can get pushed by the server into "force repoll" mode, in which we're
    81  	// not getting cache invalidations. An example: when Coyne or Nojima revokes
    82  	// a device. We want to cut down on notification spam. So instead, all attempts
    83  	// to load a team result in a preliminary poll for freshness, which this state is enabled.
    84  	forceRepollMutex sync.RWMutex
    85  	forceRepollUntil gregor.TimeOrOffset
    86  }
    87  
    88  var _ libkb.TeamLoader = (*TeamLoader)(nil)
    89  
    90  func NewTeamLoader(g *libkb.GlobalContext, world LoaderContext, storage *storage.Storage, merkleStorage *storage.Merkle) *TeamLoader {
    91  	return &TeamLoader{
    92  		Contextified:         libkb.NewContextified(g),
    93  		world:                world,
    94  		storage:              storage,
    95  		merkleStorage:        merkleStorage,
    96  		nameLookupBurstCache: libkb.NewBurstCache(g, 100, 10*time.Second, "SubteamNameToID"),
    97  		locktab:              libkb.NewLockTable(),
    98  	}
    99  }
   100  
   101  // NewTeamLoaderAndInstall creates a new loader and installs it into G.
   102  func NewTeamLoaderAndInstall(g *libkb.GlobalContext) *TeamLoader {
   103  	world := NewLoaderContextFromG(g)
   104  	st := storage.NewStorage(g)
   105  	mst := storage.NewMerkle()
   106  	l := NewTeamLoader(g, world, st, mst)
   107  	g.SetTeamLoader(l)
   108  	g.AddLogoutHook(l, "teamLoader")
   109  	g.AddDbNukeHook(l, "teamLoader")
   110  	return l
   111  }
   112  
   113  func (l *TeamLoader) Load(ctx context.Context, lArg keybase1.LoadTeamArg) (res *keybase1.TeamData, hidden *keybase1.HiddenTeamChain, err error) {
   114  	me, err := l.world.getMe(ctx)
   115  	if err != nil {
   116  		return nil, nil, err
   117  	}
   118  	if me.IsNil() && !lArg.Public {
   119  		return nil, nil, libkb.NewLoginRequiredError("login required to load a private team")
   120  	}
   121  	return l.load1(ctx, me, lArg)
   122  }
   123  
   124  func newFrozenChain(chain *keybase1.TeamSigChainState) keybase1.TeamSigChainState {
   125  	return keybase1.TeamSigChainState{
   126  		Id:         chain.Id,
   127  		Public:     chain.Public,
   128  		LastSeqno:  chain.LastSeqno,
   129  		LastLinkID: chain.LastLinkID,
   130  	}
   131  }
   132  
   133  func (l *TeamLoader) Freeze(ctx context.Context, teamID keybase1.TeamID) (err error) {
   134  	defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#Freeze(%s)", teamID), &err)()
   135  	lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String())
   136  	defer lock.Release(ctx)
   137  	mctx := libkb.NewMetaContext(ctx, l.G())
   138  	td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic())
   139  	if frozen || td == nil {
   140  		return nil
   141  	}
   142  	newTD := &keybase1.TeamData{
   143  		Frozen:     true,
   144  		Tombstoned: tombstoned,
   145  		Chain:      newFrozenChain(&td.Chain),
   146  	}
   147  	l.storage.Put(mctx, newTD)
   148  	return nil
   149  }
   150  
   151  func (l *TeamLoader) Tombstone(ctx context.Context, teamID keybase1.TeamID) (err error) {
   152  	defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#Tombstone(%s)", teamID), &err)()
   153  	lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String())
   154  	defer lock.Release(ctx)
   155  	mctx := libkb.NewMetaContext(ctx, l.G())
   156  	td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic())
   157  	if tombstoned || td == nil {
   158  		return nil
   159  	}
   160  	newTD := &keybase1.TeamData{
   161  		Frozen:     frozen,
   162  		Tombstoned: true,
   163  		Chain:      newFrozenChain(&td.Chain),
   164  	}
   165  	l.storage.Put(mctx, newTD)
   166  	return nil
   167  }
   168  
   169  func (l *TeamLoader) HintLatestSeqno(ctx context.Context, teamID keybase1.TeamID, seqno keybase1.Seqno) error {
   170  	// Single-flight lock by team ID.
   171  	lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String())
   172  	defer lock.Release(ctx)
   173  	mctx := libkb.NewMetaContext(ctx, l.G())
   174  
   175  	// Load from the cache
   176  	td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic())
   177  	if frozen || tombstoned || td == nil {
   178  		// Nothing to store the hint on.
   179  		return nil
   180  	}
   181  
   182  	if seqno < td.LatestSeqnoHint {
   183  		// The hint is behind the times, ignore.
   184  		return nil
   185  	}
   186  
   187  	td.LatestSeqnoHint = seqno
   188  	l.storage.Put(mctx, td)
   189  	return nil
   190  }
   191  
   192  // Get whether a team is open. Returns an arbitrarily stale answer.
   193  func (l *TeamLoader) IsOpenCached(ctx context.Context, teamID keybase1.TeamID) (bool, error) {
   194  	// Single-flight lock by team ID.
   195  	lock := l.locktab.AcquireOnName(ctx, l.G(), teamID.String())
   196  	defer lock.Release(ctx)
   197  	mctx := libkb.NewMetaContext(ctx, l.G())
   198  
   199  	// Load from the cache
   200  	td, frozen, tombstoned := l.storage.Get(mctx, teamID, teamID.IsPublic())
   201  	if frozen || tombstoned || td == nil {
   202  		return false, fmt.Errorf("team not available data:%v frozen:%v tombstoned:%v", td != nil, frozen, tombstoned)
   203  	}
   204  	return td.Chain.Open, nil
   205  }
   206  
   207  type nameLookupBurstCacheKey struct {
   208  	teamName keybase1.TeamName
   209  	public   bool
   210  }
   211  
   212  func (n nameLookupBurstCacheKey) String() string {
   213  	return fmt.Sprintf("%s:%v", n.teamName.String(), n.public)
   214  }
   215  
   216  // Resolve a team name to a team ID.
   217  // Will always hit the server for subteams. The server can lie in this return value.
   218  func (l *TeamLoader) ResolveNameToIDUntrusted(ctx context.Context, teamName keybase1.TeamName, public bool, allowCache bool) (id keybase1.TeamID, err error) {
   219  
   220  	defer l.G().CVTrace(ctx, libkb.VLog0, fmt.Sprintf("resolveNameToUIDUntrusted(%s,%v,%v)", teamName.String(), public, allowCache), &err)()
   221  
   222  	// For root team names, just hash.
   223  	if teamName.IsRootTeam() {
   224  		return teamName.ToTeamID(public), nil
   225  	}
   226  
   227  	if !allowCache {
   228  		return resolveNameToIDUntrustedAPICall(ctx, l.G(), teamName, public)
   229  	}
   230  
   231  	var idVoidPointer interface{}
   232  	key := nameLookupBurstCacheKey{teamName, public}
   233  	idVoidPointer, err = l.nameLookupBurstCache.Load(ctx, key, l.makeNameLookupBurstCacheLoader(ctx, l.G(), key))
   234  	if err != nil {
   235  		return keybase1.TeamID(""), err
   236  	}
   237  	if idPointer, ok := idVoidPointer.(*keybase1.TeamID); ok && idPointer != nil {
   238  		id = *idPointer
   239  	} else {
   240  		return keybase1.TeamID(""), errors.New("bad cast out of nameLookupBurstCache")
   241  	}
   242  	return id, nil
   243  }
   244  
   245  func resolveNameToIDUntrustedAPICall(ctx context.Context, g *libkb.GlobalContext, teamName keybase1.TeamName, public bool) (id keybase1.TeamID, err error) {
   246  	mctx := libkb.NewMetaContext(ctx, g)
   247  	arg := libkb.NewAPIArg("team/get")
   248  	arg.SessionType = libkb.APISessionTypeREQUIRED
   249  	arg.Args = libkb.HTTPArgs{
   250  		"name":        libkb.S{Val: teamName.String()},
   251  		"lookup_only": libkb.B{Val: true},
   252  		"public":      libkb.B{Val: public},
   253  	}
   254  
   255  	var rt rawTeam
   256  	if err := mctx.G().API.GetDecode(mctx, arg, &rt); err != nil {
   257  		return id, err
   258  	}
   259  	id = rt.ID
   260  	if !id.Exists() {
   261  		return id, fmt.Errorf("could not resolve team name: %v", teamName.String())
   262  	}
   263  	return id, nil
   264  }
   265  
   266  func (l *TeamLoader) makeNameLookupBurstCacheLoader(ctx context.Context, g *libkb.GlobalContext, key nameLookupBurstCacheKey) libkb.BurstCacheLoader {
   267  	return func() (obj interface{}, err error) {
   268  		id, err := resolveNameToIDUntrustedAPICall(ctx, g, key.teamName, key.public)
   269  		if err != nil {
   270  			return nil, err
   271  		}
   272  		return &id, nil
   273  	}
   274  }
   275  
   276  // Load1 unpacks the loadArg, calls load2, and does some final checks.
   277  // The key difference between load1 and load2 is that load2 is recursive (for subteams).
   278  func (l *TeamLoader) load1(ctx context.Context, me keybase1.UserVersion, lArg keybase1.LoadTeamArg) (data *keybase1.TeamData, hiddenChain *keybase1.HiddenTeamChain, err error) {
   279  	mctx := libkb.NewMetaContext(ctx, l.G())
   280  	err = l.checkArg(ctx, lArg)
   281  	if err != nil {
   282  		return nil, nil, err
   283  	}
   284  
   285  	var teamName *keybase1.TeamName
   286  	if len(lArg.Name) > 0 {
   287  		teamNameParsed, err := keybase1.TeamNameFromString(lArg.Name)
   288  		if err != nil {
   289  			return nil, nil, fmt.Errorf("invalid team name: %v", err)
   290  		}
   291  		teamName = &teamNameParsed
   292  	}
   293  
   294  	teamID := lArg.ID
   295  	// Resolve the name to team ID. Will always hit the server for subteams.
   296  	// It is safe for the answer to be wrong because the name is checked on the way out,
   297  	// and the merkle tree check guarantees one sigchain per team id.
   298  	if !teamID.Exists() {
   299  		teamID, err = l.ResolveNameToIDUntrusted(ctx, *teamName, lArg.Public, lArg.AllowNameLookupBurstCache)
   300  		if err != nil {
   301  			mctx.Debug("TeamLoader looking up team by name failed: %v -> %v", *teamName, err)
   302  			if code, ok := libkb.GetAppStatusCode(err); ok && code == keybase1.StatusCode_SCTeamNotFound {
   303  				mctx.Debug("replacing error: %v", err)
   304  				return nil, nil, NewTeamDoesNotExistError(lArg.Public, teamName.String())
   305  			}
   306  			return nil, nil, err
   307  		}
   308  	}
   309  
   310  	defer func() {
   311  		if hidden.ShouldClearSupportFlagOnError(err) {
   312  			mctx.Debug("Clearing support hidden chain flag for team %s because of error %v in team loader (load1)", teamID, err)
   313  			mctx.G().GetHiddenTeamChainManager().ClearSupportFlagIfFalse(mctx, teamID)
   314  		}
   315  	}()
   316  
   317  	mungedForceRepoll := lArg.ForceRepoll
   318  	mungedWantMembers, err := l.mungeWantMembers(ctx, lArg.Refreshers.WantMembers)
   319  	if err != nil {
   320  		mctx.Debug("TeamLoader munge failed: %v", err)
   321  		// drop the error and just force a repoll.
   322  		mungedForceRepoll = true
   323  		mungedWantMembers = nil
   324  	}
   325  
   326  	ret, err := l.load2(ctx, load2ArgT{
   327  		teamID: teamID,
   328  
   329  		needAdmin:                             lArg.NeedAdmin,
   330  		needKeyGeneration:                     lArg.Refreshers.NeedKeyGeneration,
   331  		needApplicationsAtGenerations:         lArg.Refreshers.NeedApplicationsAtGenerations,
   332  		needApplicationsAtGenerationsWithKBFS: lArg.Refreshers.NeedApplicationsAtGenerationsWithKBFS,
   333  		needKBFSKeyGeneration:                 lArg.Refreshers.NeedKBFSKeyGeneration,
   334  		wantMembers:                           mungedWantMembers,
   335  		wantMembersRole:                       lArg.Refreshers.WantMembersRole,
   336  		forceFullReload:                       lArg.ForceFullReload,
   337  		forceRepoll:                           mungedForceRepoll,
   338  		staleOK:                               lArg.StaleOK,
   339  		public:                                lArg.Public,
   340  		auditMode:                             lArg.AuditMode,
   341  		skipNeedHiddenRotateCheck:             lArg.SkipNeedHiddenRotateCheck,
   342  
   343  		needSeqnos:    nil,
   344  		readSubteamID: nil,
   345  
   346  		me: me,
   347  	})
   348  	switch err := err.(type) {
   349  	case TeamDoesNotExistError:
   350  		if teamName == nil {
   351  			return nil, nil, err
   352  		}
   353  		// Replace the not found error so that it has a name instead of team ID.
   354  		// If subteams are involved the name might not correspond to the ID
   355  		// but it's better to have this understandable error message that's accurate
   356  		// most of the time than one with an ID that's always accurate.
   357  		mctx.Debug("replacing error: %v", err)
   358  		return nil, nil, NewTeamDoesNotExistError(lArg.Public, teamName.String())
   359  	case nil:
   360  	default:
   361  		return nil, nil, err
   362  	}
   363  	if ret == nil {
   364  		return nil, nil, fmt.Errorf("team loader fault: got nil from load2")
   365  	}
   366  
   367  	// Public teams are allowed to be behind on secrets since you can load a
   368  	// public team you're not in. Restricted bot members don't have any secrets
   369  	// and are also exempt.
   370  	if !l.hasSyncedSecrets(mctx, ret.teamShim()) &&
   371  		!(ret.team.Chain.Public || ret.team.Chain.UserRole(me).IsRestrictedBot()) {
   372  		// this should not happen
   373  		return nil, nil, fmt.Errorf("missing secrets for team")
   374  	}
   375  
   376  	// Check team name on the way out
   377  	// The snapshot may have already been written to cache, but that should be ok,
   378  	// because the cache is keyed by ID.
   379  	if teamName != nil {
   380  		// (TODO: this won't work for renamed level 3 teams or above. There's work on this in miles/teamloader-names)
   381  		if !teamName.Eq(ret.team.Name) {
   382  			return nil, nil, fmt.Errorf("team name mismatch: %v != %v", ret.team.Name, teamName.String())
   383  		}
   384  	}
   385  
   386  	if ShouldRunBoxAudit(mctx) {
   387  		newMctx, shouldReload := VerifyBoxAudit(mctx, teamID)
   388  		if shouldReload {
   389  			return l.load1(newMctx.Ctx(), me, lArg)
   390  		}
   391  	} else {
   392  		mctx.Debug("Box auditor feature flagged off; not checking jail during team load...")
   393  	}
   394  
   395  	return &ret.team, ret.hidden, nil
   396  }
   397  
   398  func (l *TeamLoader) checkArg(ctx context.Context, lArg keybase1.LoadTeamArg) error {
   399  	hasID := lArg.ID.Exists()
   400  	hasName := len(lArg.Name) > 0
   401  	if hasID {
   402  		id, err := keybase1.TeamIDFromString(lArg.ID.String())
   403  		if err != nil {
   404  			return fmt.Errorf("team load arg has invalid ID: %q", lArg.ID)
   405  		}
   406  		if id.IsPublic() != lArg.Public {
   407  			return libkb.NewTeamVisibilityError(lArg.Public, id.IsPublic())
   408  		}
   409  	}
   410  	if !hasID && !hasName {
   411  		return fmt.Errorf("team load arg must have either ID or Name")
   412  	}
   413  	return nil
   414  }
   415  
   416  // Mostly the same as the public keybase.LoadTeamArg
   417  // but only supports loading by ID, and has neededSeqnos.
   418  type load2ArgT struct {
   419  	teamID keybase1.TeamID
   420  
   421  	reason string // optional tag for debugging why this load is happening
   422  
   423  	needAdmin                             bool
   424  	needKeyGeneration                     keybase1.PerTeamKeyGeneration
   425  	needApplicationsAtGenerations         map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication
   426  	needApplicationsAtGenerationsWithKBFS map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication
   427  	needKBFSKeyGeneration                 keybase1.TeamKBFSKeyRefresher
   428  	// wantMembers here is different from wantMembers on LoadTeamArg:
   429  	// The EldestSeqno's should not be 0.
   430  	wantMembers               []keybase1.UserVersion
   431  	wantMembersRole           keybase1.TeamRole
   432  	forceFullReload           bool
   433  	forceRepoll               bool
   434  	staleOK                   bool
   435  	public                    bool
   436  	skipNeedHiddenRotateCheck bool
   437  	skipSeedCheck             bool
   438  	foundRKMHole              bool // if the previous load found an RKM hole, this is set
   439  
   440  	auditMode keybase1.AuditMode
   441  
   442  	needSeqnos []keybase1.Seqno
   443  	// Non-nil if we are loading an ancestor for the greater purpose of
   444  	// loading a subteam. This parameter helps the server figure out whether
   445  	// to give us a subteam-reader version of the team.
   446  	// If and only if this is set, load2 is allowed to return a secret-less TeamData.
   447  	// Load1 can return secret-less TeamData if the team is public or the
   448  	// current user is a restricted bot member.
   449  	readSubteamID *keybase1.TeamID
   450  
   451  	// If the user is logged out, this will be a nil UserVersion, meaning
   452  	/// me.IsNil() will be true.
   453  	me keybase1.UserVersion
   454  }
   455  
   456  type load2ResT struct {
   457  	team      keybase1.TeamData
   458  	hidden    *keybase1.HiddenTeamChain
   459  	didRepoll bool
   460  }
   461  
   462  func (l load2ResT) teamShim() *TeamShim {
   463  	return &TeamShim{Data: &l.team, Hidden: l.hidden}
   464  }
   465  
   466  // Load2 does the rest of the work loading a team.
   467  // It is `playchain` described in the pseudocode in teamplayer.txt
   468  func (l *TeamLoader) load2(ctx context.Context, arg load2ArgT) (ret *load2ResT, err error) {
   469  	ctx = libkb.WithLogTag(ctx, "LT") // Load team
   470  	if arg.reason != "" {
   471  		ctx = libkb.WithLogTag(ctx, "LT2") // Load team recursive
   472  	}
   473  	mctx := libkb.NewMetaContext(ctx, l.G())
   474  
   475  	traceLabel := fmt.Sprintf("TeamLoader#load2(%v, public:%v)", arg.teamID, arg.public)
   476  	if len(arg.reason) > 0 {
   477  		traceLabel = traceLabel + " '" + arg.reason + "'"
   478  	}
   479  
   480  	defer l.G().CTrace(ctx, traceLabel, &err)()
   481  	ret, err = l.load2Inner(ctx, arg)
   482  	if hidden.ShouldClearSupportFlagOnError(err) {
   483  		mctx.Debug("Clearing support hidden chain flag for team %s because of error %v in team loader (load2)", arg.teamID, err)
   484  		mctx.G().GetHiddenTeamChainManager().ClearSupportFlagIfFalse(mctx, arg.teamID)
   485  	}
   486  	return ret, err
   487  }
   488  
   489  func (l *TeamLoader) load2Inner(ctx context.Context, arg load2ArgT) (*load2ResT, error) {
   490  
   491  	// Single-flight lock by team ID.
   492  	lock := l.locktab.AcquireOnName(ctx, l.G(), arg.teamID.String())
   493  	defer lock.Release(ctx)
   494  
   495  	return l.load2InnerLocked(ctx, arg)
   496  }
   497  
   498  func (l *TeamLoader) load2InnerLocked(ctx context.Context, arg load2ArgT) (res *load2ResT, err error) {
   499  	const nRetries = 3
   500  	for i := 0; i < nRetries; i++ {
   501  		res, err = l.load2InnerLockedRetry(ctx, arg)
   502  		switch pkgErrors.Cause(err).(type) {
   503  		case nil:
   504  			return res, nil
   505  		case MissingReaderKeyMaskError:
   506  			l.G().Log.CDebugf(ctx, "Got MissingReaderKeyMaskError (%s); retrying with forceFullReload=true", err.Error())
   507  			arg.foundRKMHole = true
   508  			origErr := err
   509  			res, err = l.load2InnerLockedRetry(ctx, arg)
   510  			if err == nil {
   511  				l.G().Log.CDebugf(ctx, "Found an holes in RKMS in which busting the cache saved the day (original error was: %s)", origErr.Error())
   512  			}
   513  			return res, err
   514  		case ProofError:
   515  			if arg.forceRepoll {
   516  				return res, err
   517  			}
   518  			// Something went wrong, throw out the cache and try again.
   519  			l.G().Log.CDebugf(ctx, "Got proof error (%s); trying again with forceRepoll=true", err.Error())
   520  			arg.forceRepoll = true
   521  			arg.forceFullReload = true
   522  			origErr := err
   523  			res, err = l.load2InnerLockedRetry(ctx, arg)
   524  			if err == nil {
   525  				l.G().Log.CDebugf(ctx, "Found an unexpected TeamLoader case in which busting the cache saved the day (original error was: %s)", origErr.Error())
   526  			}
   527  			return res, err
   528  		case GreenLinkError:
   529  			// Try again
   530  			l.G().Log.CDebugf(ctx, "TeamLoader retrying after green link")
   531  			arg.forceRepoll = true
   532  			continue
   533  		}
   534  		return res, err
   535  	}
   536  	if err == nil {
   537  		// Should never happen
   538  		return res, fmt.Errorf("failed retryable team load")
   539  	}
   540  	// Return the last error
   541  	return res, err
   542  }
   543  
   544  func (l *TeamLoader) checkHiddenResponse(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) {
   545  	mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v", hiddenResp, hiddenResp.UncommittedSeqno)
   546  
   547  	switch hiddenResp.RespType {
   548  	case libkb.MerkleHiddenResponseTypeNONE:
   549  		mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as no hidden data was received. If the server had to show us the hidden chain and didn't, we will error out later (once we can establish our role in the team).")
   550  		return true, nil
   551  	case libkb.MerkleHiddenResponseTypeFLAGOFF:
   552  		mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as the hidden flag is off.")
   553  		return true, nil
   554  	default:
   555  		return hiddenPackage.CheckHiddenMerklePathResponseAndAddRatchets(mctx, hiddenResp)
   556  	}
   557  }
   558  
   559  func (l *TeamLoader) load2InnerLockedRetry(ctx context.Context, arg load2ArgT) (*load2ResT, error) {
   560  	ctx, tbs := l.G().CTimeBuckets(ctx)
   561  	mctx := libkb.NewMetaContext(ctx, l.G())
   562  	tracer := l.G().CTimeTracer(ctx, "TeamLoader.load2ILR", teamEnv.Profile)
   563  	defer tracer.Finish()
   564  
   565  	defer tbs.LogIfNonZero(ctx, "API.request")
   566  
   567  	var err error
   568  	var didRepoll bool
   569  	lkc := newLoadKeyCache()
   570  
   571  	// Fetch from cache
   572  	tracer.Stage("cache load")
   573  	tailCheckRet, frozen, tombstoned := l.storage.Get(mctx, arg.teamID, arg.public)
   574  	if tombstoned {
   575  		return nil, NewTeamTombstonedError()
   576  	}
   577  
   578  	// Fetch last polled time from merkle cache
   579  	merklePolledAt := l.merkleStorage.Get(mctx, arg.teamID, arg.public)
   580  
   581  	var ret *keybase1.TeamData
   582  	if !frozen && !arg.forceFullReload {
   583  		// Load from cache
   584  		ret = tailCheckRet
   585  	}
   586  
   587  	if ret != nil && !ret.Chain.Reader.Eq(arg.me) {
   588  		// Check that we are the same person as when this team was last loaded as a courtesy.
   589  		// This should never happen. We shouldn't be able to decrypt someone else's snapshot.
   590  		mctx.Warning("TeamLoader discarding snapshot for wrong user: (%v, %v) != (%v, %v)",
   591  			arg.me.Uid, arg.me.EldestSeqno, ret.Chain.Reader.Uid, ret.Chain.Reader.EldestSeqno)
   592  		ret = nil
   593  	}
   594  
   595  	var cachedName *keybase1.TeamName
   596  	if ret != nil && !ret.Name.IsNil() {
   597  		cachedName = &ret.Name
   598  	}
   599  
   600  	hiddenPackage, err := l.hiddenPackage(mctx, arg.teamID, ret, arg.me)
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  
   605  	teamShim := func() *TeamShim {
   606  		return &TeamShim{Data: ret, Hidden: hiddenPackage.ChainData()}
   607  	}
   608  
   609  	// Determine whether to repoll merkle.
   610  	discardCache, repoll := l.load2DecideRepoll(mctx, arg, teamShim(), merklePolledAt)
   611  	if discardCache {
   612  		ret = nil
   613  		repoll = true
   614  	}
   615  
   616  	tracer.Stage("deepcopy")
   617  	if ret != nil {
   618  		// If we're pulling from a previous snapshot (that, let's say, we got from a shared cache),
   619  		// then make sure to DeepCopy() data out of it before we start mutating it below. We used
   620  		// to do this every step through the new links, but that was very expensive in terms of CPU
   621  		// for big teams, since it was hidden quadratic behavior.
   622  		tmp := ret.DeepCopy()
   623  		ret = &tmp
   624  	} else {
   625  		mctx.Debug("TeamLoader not using snapshot")
   626  	}
   627  
   628  	tracer.Stage("merkle")
   629  	var lastSeqno keybase1.Seqno
   630  	var lastLinkID keybase1.LinkID
   631  	var hiddenIsFresh bool
   632  	var lastMerkleRoot *libkb.MerkleRoot
   633  
   634  	// hiddenResp will be nill iff we do not make the merkleLookupWithHidden
   635  	// call. If the server does not return any hidden data, we will encode that
   636  	// as a non nil response whose RespType is MerkleHiddenResponseTypeNONE.
   637  	var hiddenResp *libkb.MerkleHiddenResponse
   638  
   639  	if (ret == nil) || repoll {
   640  		mctx.Debug("TeamLoader looking up merkle leaf (force:%v)", arg.forceRepoll)
   641  
   642  		// Reference the merkle tree to fetch the sigchain tail leaf for the team.
   643  		lastSeqno, lastLinkID, hiddenResp, lastMerkleRoot, err = l.world.merkleLookupWithHidden(ctx, arg.teamID, arg.public)
   644  		if err != nil {
   645  			return nil, err
   646  		}
   647  		mctx.Debug("received lastSeqno %v, lastLinkID %v", lastSeqno, lastLinkID)
   648  
   649  		hiddenIsFresh, err = l.checkHiddenResponse(mctx, hiddenPackage, hiddenResp)
   650  		if err != nil {
   651  			return nil, err
   652  		}
   653  
   654  		didRepoll = true
   655  	} else {
   656  		lastSeqno = ret.Chain.LastSeqno
   657  		lastLinkID = ret.Chain.LastLinkID
   658  		hiddenIsFresh = true
   659  	}
   660  
   661  	// For child calls to load2, the subteam reader ID is carried up
   662  	// or if it doesn't exist, start at this team.
   663  	readSubteamID := arg.teamID
   664  	if arg.readSubteamID != nil {
   665  		readSubteamID = *arg.readSubteamID
   666  	}
   667  
   668  	proofSet := newProofSet(l.G())
   669  	var parentChildOperations []*parentChildOperation
   670  
   671  	// Backfill stubbed links that need to be filled now.
   672  	tracer.Stage("backfill")
   673  	var filledInStubbedLinks bool
   674  	if ret != nil && len(arg.needSeqnos) > 0 {
   675  		ret, proofSet, parentChildOperations, err = l.fillInStubbedLinks(
   676  			mctx, arg.me, arg.teamID, ret, arg.needSeqnos, readSubteamID, proofSet, parentChildOperations, lkc)
   677  		if err != nil {
   678  			return nil, err
   679  		}
   680  		filledInStubbedLinks = true
   681  	}
   682  
   683  	tracer.Stage("pre-fetch")
   684  	var fetchLinksAndOrSecrets bool
   685  	if ret == nil {
   686  		mctx.Debug("TeamLoader fetching: no cache")
   687  		// We have no cache
   688  		fetchLinksAndOrSecrets = true
   689  	} else if ret.Chain.LastSeqno < lastSeqno {
   690  		mctx.Debug("TeamLoader fetching: chain update")
   691  		// The cache is definitely behind
   692  		fetchLinksAndOrSecrets = true
   693  	} else if !hiddenIsFresh {
   694  		mctx.Debug("TeamLoader fetching: hidden chain wasn't fresh")
   695  		fetchLinksAndOrSecrets = true
   696  	} else if !l.hasSyncedSecrets(mctx, teamShim()) {
   697  		// The cached secrets are behind the cached chain.
   698  		// We may need to hit the server for secrets, even though there are no new links.
   699  		if arg.needAdmin {
   700  			mctx.Debug("TeamLoader fetching: NeedAdmin")
   701  			// Admins should always have up-to-date secrets. But not necessarily RKMs.
   702  			fetchLinksAndOrSecrets = true
   703  		}
   704  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, teamShim()); err != nil {
   705  			mctx.Debug("TeamLoader fetching: NeedKeyGeneration: %v", err)
   706  			fetchLinksAndOrSecrets = true
   707  		}
   708  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, teamShim()); err != nil {
   709  			mctx.Debug("TeamLoader fetching: KBFSNeedKeyGeneration: %v", err)
   710  			fetchLinksAndOrSecrets = true
   711  		}
   712  		if arg.readSubteamID == nil {
   713  			// This is not a recursive load. We should have the keys.
   714  			// This may be an extra round trip for public teams you're not in.
   715  			mctx.Debug("TeamLoader fetching: primary load")
   716  			fetchLinksAndOrSecrets = true
   717  		}
   718  	}
   719  	// hasSyncedSecrets does not account for RKMs. So check RKM refreshers separeately.
   720  
   721  	if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, teamShim()); err != nil {
   722  		mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerations: %v", err)
   723  		fetchLinksAndOrSecrets = true
   724  	}
   725  	if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx,
   726  		arg.needApplicationsAtGenerationsWithKBFS, teamShim()); err != nil {
   727  		mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerationsWithKBFS: %v", err)
   728  		fetchLinksAndOrSecrets = true
   729  	}
   730  
   731  	// Pull new links from the server
   732  	tracer.Stage("fetch")
   733  	var teamUpdate *rawTeam
   734  	if fetchLinksAndOrSecrets {
   735  		lows := l.lows(mctx, ret, hiddenPackage, arg)
   736  		mctx.Debug("TeamLoader getting links from server (%+v)", lows)
   737  		teamUpdate, err = l.world.getNewLinksFromServer(ctx, arg.teamID, lows, arg.readSubteamID)
   738  		if err != nil {
   739  			return nil, err
   740  		}
   741  		mctx.Debug("TeamLoader got %v links", len(teamUpdate.Chain))
   742  		hiddenPackage.SetRatchetBlindingKeySet(teamUpdate.RatchetBlindingKeySet)
   743  	}
   744  
   745  	tracer.Stage("unpack")
   746  	links, err := teamUpdate.unpackLinks(mctx)
   747  	if err != nil {
   748  		return nil, err
   749  	}
   750  	var prev libkb.LinkID
   751  	if ret != nil {
   752  		prev, err = TeamSigChainState{inner: ret.Chain}.GetLatestLibkbLinkID()
   753  		if err != nil {
   754  			return nil, err
   755  		}
   756  	}
   757  
   758  	// A link which was signed by an admin. Sloppily the latest such link.
   759  	// Sloppy because this calculation misses out on e.g. a rotate_key signed by an admin.
   760  	// This value is used for skipping fullVerify on team.leave links, see `verifyLink`.
   761  	var fullVerifyCutoff keybase1.Seqno
   762  	for i := len(links) - 1; i >= 0; i-- {
   763  		if links[i].LinkType().RequiresAtLeastRole().IsAdminOrAbove() {
   764  			fullVerifyCutoff = links[i].Seqno()
   765  			break
   766  		}
   767  	}
   768  	if fullVerifyCutoff > 0 {
   769  		mctx.Debug("fullVerifyCutoff: %v", fullVerifyCutoff)
   770  	}
   771  
   772  	tracer.Stage("userPreload enable:%v parallel:%v wait:%v",
   773  		teamEnv.UserPreloadEnable, teamEnv.UserPreloadParallel, teamEnv.UserPreloadWait)
   774  	preloadCancel := l.userPreload(ctx, links, fullVerifyCutoff)
   775  	defer preloadCancel()
   776  
   777  	tracer.Stage("linkloop (%v)", len(links))
   778  	parentsCache := make(parentChainCache)
   779  
   780  	// Don't log in the middle links if there are a great many links.
   781  	suppressLoggingStart := 5
   782  	suppressLoggingUpto := len(links) - 5
   783  	for i, link := range links {
   784  		var err error
   785  		ret, prev, err = l.doOneLink(mctx, arg, ret, hiddenPackage, link, i, suppressLoggingStart, suppressLoggingUpto, lastSeqno, &parentChildOperations, prev, fullVerifyCutoff, readSubteamID, proofSet, lkc, &parentsCache)
   786  		if err != nil {
   787  			return nil, err
   788  		}
   789  	}
   790  
   791  	if ret == nil {
   792  		return nil, fmt.Errorf("team loader fault: got nil from load2")
   793  	}
   794  
   795  	encKID, gen, role, err := l.hiddenPackageGetter(mctx, arg.teamID, ret, arg.me)()
   796  	if err != nil {
   797  		return nil, err
   798  	}
   799  
   800  	// If we did get an update from the server (hiddenResp != nil) are not a
   801  	// restricted bot AND this is not a recursive load (arg.readSubteamID == nil),
   802  	// then the server should have given us hidden chain data.
   803  	if hiddenResp != nil && hiddenResp.RespType == libkb.MerkleHiddenResponseTypeNONE && !role.IsRestrictedBot() && arg.readSubteamID == nil {
   804  		return nil, libkb.NewHiddenChainDataMissingError("Not a restricted bot or recursive load, but the server did not return merkle hidden chain data")
   805  	}
   806  
   807  	// Update the hidden package with team metadata once we process all of the
   808  	// links. This is necessary since we need the role to be up to date to know
   809  	// if we should skip seed checks on the hidden chain if we are loading as a
   810  	// RESTRICTEDBOT.
   811  	hiddenPackage.UpdateTeamMetadata(encKID, gen, role)
   812  
   813  	// Be sure to update the hidden chain after the main chain, since the latter can "ratchet" the former
   814  	err = hiddenPackage.Update(mctx, teamUpdate.GetHiddenChain(), hiddenResp.GetUncommittedSeqno())
   815  
   816  	if err != nil {
   817  		return nil, err
   818  	}
   819  	err = hiddenPackage.CheckPTKsForDuplicates(mctx, func(g keybase1.PerTeamKeyGeneration) bool {
   820  		_, ok := ret.Chain.PerTeamKeys[g]
   821  		return ok
   822  	})
   823  	if err != nil {
   824  		return nil, err
   825  	}
   826  
   827  	// The hidden team has pointers from the hidden chain up to the visible chain; check that they
   828  	// match the loaded team. We should have a full load of the team, so all parent pointers
   829  	// better hit their mark.
   830  	err = hiddenPackage.CheckParentPointersOnFullLoad(mctx, ret)
   831  	if err != nil {
   832  		return nil, err
   833  	}
   834  
   835  	preloadCancel()
   836  	if len(links) > 0 {
   837  		tbs.Log(ctx, "TeamLoader.verifyLink")
   838  		tbs.Log(ctx, "TeamLoader.applyNewLink")
   839  		tbs.Log(ctx, "SigChain.LoadFromServer.ReadAll")
   840  		tbs.Log(ctx, "loadKeyCache.loadKeyV2")
   841  		if teamEnv.Profile {
   842  			tbs.Log(ctx, "LoaderContextG.loadKeyV2")
   843  			tbs.Log(ctx, "CachedUPAKLoader.LoadKeyV2") // note LoadKeyV2 calls Load2
   844  			tbs.Log(ctx, "CachedUPAKLoader.LoadV2")
   845  			tbs.Log(ctx, "CachedUPAKLoader.DeepCopy")
   846  			mctx.Debug("TeamLoader lkc cache hits: %v", lkc.cacheHits)
   847  		}
   848  	}
   849  
   850  	if !ret.Chain.LastLinkID.Eq(lastLinkID) {
   851  		return nil, fmt.Errorf("wrong sigchain link ID: %v != %v",
   852  			ret.Chain.LastLinkID, lastLinkID)
   853  	}
   854  
   855  	if tailCheckRet != nil {
   856  		// If we previously discarded cache due to forceFullReload, or left the
   857  		// team, froze it, and are rejoining, make sure the previous tail is
   858  		// still in the chain.
   859  		// The chain loader ensures it is part of a well-formed chain with correct prevs.
   860  		linkID := ret.Chain.LinkIDs[tailCheckRet.Chain.LastSeqno]
   861  		if !linkID.Eq(tailCheckRet.Chain.LastLinkID) {
   862  			return nil, fmt.Errorf("got wrong sigchain link ID for seqno %d: expected %v from previous cache entry (frozen=%t); got %v in new chain", tailCheckRet.Chain.LastSeqno,
   863  				tailCheckRet.Chain.LastLinkID, ret.Frozen, linkID)
   864  		}
   865  	}
   866  
   867  	tracer.Stage("pco")
   868  	err = l.checkParentChildOperations(ctx,
   869  		arg.me, arg.teamID, ret.Chain.ParentID, readSubteamID, parentChildOperations, proofSet)
   870  	if err != nil {
   871  		return nil, err
   872  	}
   873  
   874  	tracer.Stage("checkproofs")
   875  	err = l.checkProofs(ctx, ret, proofSet)
   876  	if err != nil {
   877  		return nil, err
   878  	}
   879  
   880  	tracer.Stage("secrets")
   881  	if teamUpdate != nil {
   882  		if teamUpdate.SubteamReader {
   883  			// Only allow subteam-reader results if we are in a recursive load.
   884  			if arg.readSubteamID == nil {
   885  				return nil, fmt.Errorf("unexpected subteam reader result")
   886  			}
   887  		} else {
   888  			stateWrapper := newTeamSigChainState(teamShim())
   889  			role, err := stateWrapper.GetUserRole(arg.me)
   890  			if err != nil {
   891  				role = keybase1.TeamRole_NONE
   892  			}
   893  			// Add the secrets.
   894  			// If it's a public team, there might not be secrets. (If we're not in the team)
   895  			// Restricted bots don't have any team secrets, so we also short circuit.
   896  			if !role.IsRestrictedBot() && (!ret.Chain.Public || (teamUpdate.Box != nil)) {
   897  				err = l.addSecrets(mctx, teamShim(), arg.me, teamUpdate.Box, teamUpdate.Prevs, teamUpdate.ReaderKeyMasks)
   898  				if err != nil {
   899  					return nil, pkgErrors.Wrap(err, "loading team secrets")
   900  				}
   901  
   902  				err = l.computeSeedChecks(ctx, ret)
   903  				if err != nil {
   904  					return nil, err
   905  				}
   906  
   907  				if teamUpdate.LegacyTLFUpgrade != nil {
   908  					err = l.addKBFSCryptKeys(mctx, teamShim(), teamUpdate.LegacyTLFUpgrade)
   909  					if err != nil {
   910  						return nil, fmt.Errorf("loading KBFS crypt keys: %v", err)
   911  					}
   912  				}
   913  			}
   914  			if role.IsRestrictedBot() {
   915  				// Clear out any secrets we may have had in memory if we were a
   916  				// previous role that had PTK access.
   917  				state := teamShim().MainChain()
   918  				state.PerTeamKeySeedsUnverified = make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem)
   919  				state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64)
   920  				state.TlfCryptKeys = make(map[keybase1.TeamApplication][]keybase1.CryptKey)
   921  			}
   922  		}
   923  	}
   924  
   925  	// Note that we might have done so just above after adding secrets, but before adding
   926  	// KBFS crypt keys. But it's cheap to run this method twice in a row.
   927  	tracer.Stage("computeSeedChecks")
   928  	err = l.computeSeedChecks(ctx, ret)
   929  	if err != nil {
   930  		return nil, err
   931  	}
   932  
   933  	if !arg.skipSeedCheck && arg.readSubteamID == nil {
   934  		err = hiddenPackage.CheckUpdatesAgainstSeedsWithMap(mctx, ret.PerTeamKeySeedsUnverified)
   935  		if err != nil {
   936  			return nil, err
   937  		}
   938  	}
   939  
   940  	// Make sure public works out
   941  	if ret.Chain.Public != arg.public {
   942  		return nil, fmt.Errorf("team public mismatch: chain:%v != arg:%v", ret.Chain.Public, arg.public)
   943  	}
   944  	if ret.Chain.Id.IsPublic() != ret.Chain.Public {
   945  		return nil, fmt.Errorf("team public mismatch: id:%v != chain:%v", ret.Chain.Id.IsPublic(), ret.Chain.Public)
   946  	}
   947  
   948  	// Sanity check the id
   949  	if !ret.Chain.Id.Eq(arg.teamID) {
   950  		return nil, fmt.Errorf("team id mismatch: %v != %v", ret.Chain.Id.String(), arg.teamID.String())
   951  	}
   952  
   953  	// Recalculate the team name.
   954  	// This must always run to pick up changes on chain and off-chain with ancestor renames.
   955  	// Also because without this a subteam could claim any parent in its name.
   956  	tracer.Stage("namecalc")
   957  	newName, err := l.calculateName(ctx, ret, arg.me, readSubteamID, arg.staleOK)
   958  	if err != nil {
   959  		return nil, fmt.Errorf("error recalculating name for %v: %v", ret.Name, err)
   960  	}
   961  	if !ret.Name.Eq(newName) {
   962  		// This deep copy is an absurd price to pay, but these mid-team renames should be quite rare.
   963  		copy := ret.DeepCopy()
   964  		ret = &copy
   965  		ret.Name = newName
   966  	}
   967  
   968  	var needHiddenRotate bool
   969  	if !arg.skipNeedHiddenRotateCheck {
   970  		needHiddenRotate, err = l.checkNeedRotate(mctx, ret, arg.me, hiddenPackage)
   971  		if err != nil {
   972  			return nil, err
   973  		}
   974  	}
   975  
   976  	err = hiddenPackage.Commit(mctx)
   977  	if err != nil {
   978  		return nil, err
   979  	}
   980  
   981  	l.logIfUnsyncedSecrets(ctx, ret)
   982  
   983  	// Mutating this field is safe because only TeamLoader
   984  	// while holding the single-flight lock reads or writes this field.
   985  	ret.CachedAt = keybase1.ToTime(l.G().Clock().Now())
   986  
   987  	// Clear the untrusted seqno hint.
   988  	// Mutating this field is safe because only TeamLoader
   989  	// while holding the single-flight lock reads or writes this field.
   990  	ret.LatestSeqnoHint = 0
   991  
   992  	if didRepoll {
   993  		tracer.Stage("audit")
   994  		auditMode := arg.auditMode
   995  		// in case of restricted bots or recursive loads, do not audit the
   996  		// hidden chain (as we might not have permission to see it).
   997  		if (role.IsRestrictedBot() || arg.readSubteamID != nil) && auditMode == keybase1.AuditMode_STANDARD {
   998  			auditMode = keybase1.AuditMode_STANDARD_NO_HIDDEN
   999  		}
  1000  
  1001  		err = l.audit(ctx, readSubteamID, &ret.Chain, hiddenPackage.ChainData(), lastMerkleRoot, auditMode)
  1002  		if err != nil {
  1003  			return nil, err
  1004  		}
  1005  	} else {
  1006  		mctx.Debug("Skipping audit in the TeamLoader as we did not repoll merkle")
  1007  	}
  1008  
  1009  	// Cache the validated result if it was actually updated via the team/get endpoint. In many cases, we're not
  1010  	// actually mutating the teams. Also, if we wound up filling in stubbed links, let's also restore the cache.
  1011  	if teamUpdate != nil || filledInStubbedLinks {
  1012  		tracer.Stage("put")
  1013  		l.storage.Put(mctx, ret)
  1014  	}
  1015  
  1016  	// If we wound up repolling the merkle tree for this team, say that we did.
  1017  	if didRepoll {
  1018  		l.merkleStorage.Put(mctx, arg.teamID, arg.public, keybase1.ToTime(mctx.G().Clock().Now()))
  1019  	}
  1020  
  1021  	tracer.Stage("notify")
  1022  	if cachedName != nil && !cachedName.Eq(newName) {
  1023  		chain := TeamSigChainState{inner: ret.Chain, hidden: hiddenPackage.ChainData()}
  1024  		// Send a notification if we used to have the name cached and it has changed at all.
  1025  		changeSet := keybase1.TeamChangeSet{Renamed: true}
  1026  		go l.G().NotifyRouter.HandleTeamChangedByID(context.Background(), chain.GetID(), chain.GetLatestSeqno(),
  1027  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1028  		go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), cachedName.String(), chain.GetLatestSeqno(),
  1029  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1030  		go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), newName.String(), chain.GetLatestSeqno(),
  1031  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1032  	}
  1033  
  1034  	// Check request constraints
  1035  	tracer.Stage("postcheck")
  1036  	err = l.load2CheckReturn(mctx, arg, teamShim())
  1037  	if err != nil {
  1038  		return nil, err
  1039  	}
  1040  
  1041  	load2res := load2ResT{
  1042  		team:      *ret,
  1043  		didRepoll: didRepoll,
  1044  	}
  1045  
  1046  	if hd := hiddenPackage.ChainData(); hd != nil {
  1047  		hd.NeedRotate = needHiddenRotate
  1048  		load2res.hidden = hd
  1049  	}
  1050  
  1051  	if needHiddenRotate {
  1052  		l.G().GetTeamBoxAuditor().MaybeScheduleDelayedBoxAuditTeam(mctx, arg.teamID)
  1053  	}
  1054  
  1055  	return &load2res, nil
  1056  }
  1057  
  1058  func (l *TeamLoader) hiddenPackageGetter(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration, role keybase1.TeamRole, err error) {
  1059  	return func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration,
  1060  		role keybase1.TeamRole, err error) {
  1061  		if team == nil {
  1062  			return encKID, gen, keybase1.TeamRole_NONE, nil
  1063  		}
  1064  		state := TeamSigChainState{inner: team.Chain}
  1065  
  1066  		ptk, err := state.GetLatestPerTeamKey(mctx)
  1067  		if err != nil {
  1068  			return encKID, gen, keybase1.TeamRole_NONE, err
  1069  		}
  1070  		role, err = state.GetUserRole(me)
  1071  		if err != nil {
  1072  			return encKID, gen, keybase1.TeamRole_NONE, err
  1073  		}
  1074  		return ptk.EncKID, ptk.Gen, role, nil
  1075  	}
  1076  }
  1077  
  1078  func (l *TeamLoader) hiddenPackage(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) (ret *hidden.LoaderPackage, err error) {
  1079  	getter := l.hiddenPackageGetter(mctx, id, team, me)
  1080  	return hidden.NewLoaderPackage(mctx, id, getter)
  1081  }
  1082  
  1083  func (l *TeamLoader) isAllowedKeyerOf(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, them keybase1.UserVersion) (ret bool, err error) {
  1084  	state := TeamSigChainState{inner: chain.Chain}
  1085  	mctx = mctx.WithLogTag("IAKO")
  1086  	defer mctx.Trace(fmt.Sprintf("TeamLoader#isAllowedKeyerOf(%s, %s)", state.GetID(), them), &err)()
  1087  
  1088  	role, err := state.GetUserRole(them)
  1089  	if err != nil {
  1090  		return false, err
  1091  	}
  1092  	switch role {
  1093  	case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER:
  1094  		mctx.Debug("user fits explicit role (%s)", role)
  1095  		return true, nil
  1096  	}
  1097  
  1098  	if state.GetParentID() == nil {
  1099  		mctx.Debug("user is not an allowed keyer of the team")
  1100  		return false, nil
  1101  	}
  1102  
  1103  	// now check implict adminship
  1104  	yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, them)
  1105  	if err != nil {
  1106  		return false, err
  1107  	}
  1108  
  1109  	if yes {
  1110  		mctx.Debug("user is an implicit admin of the team")
  1111  		return true, err
  1112  	}
  1113  
  1114  	mctx.Debug("user is not an allowed keyer of the team")
  1115  
  1116  	return false, nil
  1117  
  1118  }
  1119  
  1120  func (l *TeamLoader) checkNeedRotate(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, hiddenPackage *hidden.LoaderPackage) (ret bool, err error) {
  1121  	signer := hiddenPackage.LastReaderKeyRotator(mctx)
  1122  	if signer == nil {
  1123  		mctx.Debug("not checking need rotate, since last signer of hidden chain was nil")
  1124  		return false, nil
  1125  	}
  1126  	return l.checkNeedRotateWithSigner(mctx, chain, me, *signer)
  1127  }
  1128  
  1129  func (l *TeamLoader) checkNeedRotateWithSigner(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, signer keybase1.Signer) (ret bool, err error) {
  1130  
  1131  	defer mctx.Trace(fmt.Sprintf("TeamLoader::checkNeedRotateWithSigner(%+v)", signer), &err)()
  1132  
  1133  	uv := signer.UserVersion()
  1134  
  1135  	var isKeyer, amIKeyer bool
  1136  
  1137  	amIKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, me)
  1138  	if err != nil {
  1139  		return false, err
  1140  	}
  1141  	if !amIKeyer {
  1142  		mctx.Debug("I am not a keyer for this team, so I can't rotate it even if required")
  1143  		return false, nil
  1144  	}
  1145  
  1146  	isKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, uv)
  1147  	if err != nil {
  1148  		return false, err
  1149  	}
  1150  
  1151  	if !isKeyer {
  1152  		mctx.Debug("need rotate since %+v isn't an allowed keyer of the team", uv)
  1153  		return true, nil
  1154  	}
  1155  
  1156  	var found bool
  1157  	var revokedAt *keybase1.KeybaseTime
  1158  
  1159  	found, revokedAt, _, err = mctx.G().GetUPAKLoader().CheckKIDForUID(mctx.Ctx(), uv.Uid, signer.K)
  1160  	if err != nil {
  1161  		return false, err
  1162  	}
  1163  
  1164  	if !found || revokedAt != nil {
  1165  		var s string
  1166  		if revokedAt != nil {
  1167  			tm := revokedAt.Unix.Time()
  1168  			s = fmt.Sprintf(" (revoked at %s [%s ago])", tm, mctx.G().Clock().Now().Sub(tm))
  1169  		}
  1170  		mctx.Debug("KID %s wasn't found for %+v%s", signer, s)
  1171  		return true, nil
  1172  	}
  1173  
  1174  	return false, nil
  1175  }
  1176  
  1177  func (l *TeamLoader) doOneLink(mctx libkb.MetaContext, arg load2ArgT, ret *keybase1.TeamData, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked, i int, suppressLoggingStart int, suppressLoggingUpto int, lastSeqno keybase1.Seqno, parentChildOperations *[](*parentChildOperation), prev libkb.LinkID, fullVerifyCutoff keybase1.Seqno, readSubteamID keybase1.TeamID, proofSet *proofSetT, lkc *loadKeyCache, parentsCache *parentChainCache) (*keybase1.TeamData, libkb.LinkID, error) {
  1178  
  1179  	var nilPrev libkb.LinkID
  1180  
  1181  	ctx := mctx.Ctx()
  1182  	if suppressLoggingStart <= i && i < suppressLoggingUpto {
  1183  		if i == suppressLoggingStart {
  1184  			mctx.Debug("TeamLoader suppressing logs until %v", suppressLoggingUpto)
  1185  		}
  1186  		ctx = WithSuppressLogging(ctx, true)
  1187  		mctx = mctx.WithContext(ctx)
  1188  	}
  1189  
  1190  	if !ShouldSuppressLogging(ctx) {
  1191  		mctx.Debug("TeamLoader processing link seqno:%v", link.Seqno())
  1192  	}
  1193  
  1194  	if link.Seqno() > lastSeqno {
  1195  		// This link came from a point in the chain after when we checked the merkle leaf.
  1196  		// Processing it would require re-checking merkle.
  1197  		// It would be tricky to ignore it because off-chain data is asserted to be in sync with the chain.
  1198  		// So, return an error that the caller will retry.
  1199  		mctx.Debug("TeamLoader found green link seqno:%v", link.Seqno())
  1200  		return nil, nilPrev, NewGreenLinkError(link.Seqno())
  1201  	}
  1202  
  1203  	if err := l.checkStubbed(ctx, arg, link); err != nil {
  1204  		return nil, nilPrev, err
  1205  	}
  1206  
  1207  	if !link.Prev().Eq(prev) {
  1208  		return nil, nilPrev, NewPrevError("team replay failed: prev chain broken at link %d (%v != %v)",
  1209  			i, link.Prev(), prev)
  1210  	}
  1211  
  1212  	if err := consumeRatchets(mctx, hiddenPackage, link); err != nil {
  1213  		return nil, nilPrev, err
  1214  	}
  1215  
  1216  	if err := checkPTKGenerationNotOnHiddenChain(mctx, hiddenPackage, link); err != nil {
  1217  		return nil, nilPrev, err
  1218  	}
  1219  
  1220  	var signer *SignerX
  1221  	var err error
  1222  	signer, err = l.verifyLink(ctx, arg.teamID, ret, arg.me, link, fullVerifyCutoff,
  1223  		readSubteamID, proofSet, lkc, *parentsCache)
  1224  	if err != nil {
  1225  		return nil, nilPrev, err
  1226  	}
  1227  
  1228  	if l.isParentChildOperation(ctx, link) {
  1229  		pco, err := l.toParentChildOperation(ctx, link)
  1230  		if err != nil {
  1231  			return nil, nilPrev, err
  1232  		}
  1233  		*parentChildOperations = append(*parentChildOperations, pco)
  1234  	}
  1235  
  1236  	ret, err = l.applyNewLink(ctx, ret, hiddenPackage.ChainData(), link, signer, arg.me)
  1237  	if err != nil {
  1238  		return nil, nilPrev, err
  1239  	}
  1240  
  1241  	return ret, link.LinkID(), nil
  1242  }
  1243  
  1244  // userPreload warms the upak cache with users who will probably need to be loaded to verify the chain.
  1245  // Uses teamEnv and may be disabled.
  1246  func (l *TeamLoader) userPreload(ctx context.Context, links []*ChainLinkUnpacked, fullVerifyCutoff keybase1.Seqno) (cancel func()) {
  1247  	ctx, cancel = context.WithCancel(ctx)
  1248  	if teamEnv.UserPreloadEnable {
  1249  		uidSet := make(map[keybase1.UID]struct{})
  1250  		for _, link := range links {
  1251  			// fullVerify definition copied from verifyLink
  1252  			fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) ||
  1253  				(link.Seqno() >= fullVerifyCutoff) ||
  1254  				(link.source.EldestSeqno == 0)
  1255  			if !link.isStubbed() && fullVerify {
  1256  				uidSet[link.inner.Body.Key.UID] = struct{}{}
  1257  			}
  1258  		}
  1259  		l.G().Log.CDebugf(ctx, "TeamLoader userPreload uids: %v", len(uidSet))
  1260  		if teamEnv.UserPreloadParallel {
  1261  			// Note this is full-parallel. Probably want pipelining if this is to be turned on by default.
  1262  			var wg sync.WaitGroup
  1263  			for uid := range uidSet {
  1264  				wg.Add(1)
  1265  				go func(uid keybase1.UID) {
  1266  					_, _, err := l.G().GetUPAKLoader().LoadV2(
  1267  						libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx))
  1268  					if err != nil {
  1269  						l.G().Log.CDebugf(ctx, "error preloading uid %v", uid)
  1270  					}
  1271  					wg.Done()
  1272  				}(uid)
  1273  			}
  1274  			if teamEnv.UserPreloadWait {
  1275  				wg.Wait()
  1276  			}
  1277  		} else {
  1278  			for uid := range uidSet {
  1279  				_, _, err := l.G().GetUPAKLoader().LoadV2(
  1280  					libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx))
  1281  				if err != nil {
  1282  					l.G().Log.CDebugf(ctx, "error preloading uid %v", uid)
  1283  				}
  1284  			}
  1285  		}
  1286  	}
  1287  	return cancel
  1288  }
  1289  
  1290  // Decide whether to repoll merkle based on load arg.
  1291  // Returns (discardCache, repoll)
  1292  // discardCache - the caller should throw out their cached copy and repoll.
  1293  // repoll - hit up merkle for the latest tail
  1294  // Considers:
  1295  //   - NeedAdmin
  1296  //   - NeedKeyGeneration
  1297  //   - NeedApplicationsAtGenerations
  1298  //   - WantMembers
  1299  //   - ForceRepoll
  1300  //   - Cache freshness / StaleOK
  1301  //   - NeedSeqnos
  1302  //   - JustUpdated
  1303  //   - If this user is in global "force repoll" mode, where it would be too spammy to
  1304  //     push out individual team changed notifications, so all team loads need a repoll.
  1305  func (l *TeamLoader) load2DecideRepoll(mctx libkb.MetaContext, arg load2ArgT, fromCache Teamer, cachedPolledAt *keybase1.Time) (discardCache bool, repoll bool) {
  1306  	var reason string
  1307  	defer func() {
  1308  		if discardCache || repoll || reason != "" {
  1309  			mctx.Debug("load2DecideRepoll -> (discardCache:%v, repoll:%v) %v", discardCache, repoll, reason)
  1310  		}
  1311  	}()
  1312  	// NeedAdmin is a special constraint where we start from scratch.
  1313  	// Because of admin-only invite links.
  1314  	if arg.needAdmin {
  1315  		if !l.satisfiesNeedAdmin(mctx, arg.me, fromCache) {
  1316  			// Start from scratch if we are newly admin
  1317  			reason = "!satisfiesNeedAdmin"
  1318  			return true, true
  1319  		}
  1320  	}
  1321  
  1322  	if arg.forceRepoll {
  1323  		reason = "forceRepoll"
  1324  		return false, true
  1325  	}
  1326  
  1327  	// Repoll if the server has previously hinted that the team has new links.
  1328  	if fromCache != nil && fromCache.MainChain() != nil && fromCache.MainChain().Chain.LastSeqno < fromCache.MainChain().LatestSeqnoHint {
  1329  		reason = "behind seqno hint"
  1330  		return false, true
  1331  	}
  1332  
  1333  	if fromCache != nil && fromCache.HiddenChain() != nil && fromCache.HiddenChain().IsStale() {
  1334  		reason = "behind hidden seqno hint"
  1335  		return false, true
  1336  	}
  1337  
  1338  	// Repoll to get a new key generation
  1339  	if arg.needKeyGeneration > 0 {
  1340  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, fromCache); err != nil {
  1341  			reason = fmt.Sprintf("satisfiesNeedKeyGeneration -> %v", err)
  1342  			return false, true
  1343  		}
  1344  	}
  1345  	// Repoll to get new applications at generations
  1346  	if len(arg.needApplicationsAtGenerations) > 0 {
  1347  		if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, fromCache); err != nil {
  1348  			reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerations -> %v", err)
  1349  			return false, true
  1350  		}
  1351  	}
  1352  	if arg.needKBFSKeyGeneration.Generation > 0 {
  1353  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, fromCache); err != nil {
  1354  			reason = fmt.Sprintf("satisfiesNeedsKBFSKeyGeneration -> %v", err)
  1355  			return false, true
  1356  		}
  1357  	}
  1358  
  1359  	if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 {
  1360  		if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx,
  1361  			arg.needApplicationsAtGenerationsWithKBFS, fromCache); err != nil {
  1362  			reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerationsWithKBFS -> %v", err)
  1363  			return false, true
  1364  		}
  1365  	}
  1366  
  1367  	// Repoll because it might help get the wanted members
  1368  	if len(arg.wantMembers) > 0 {
  1369  		if err := l.satisfiesWantMembers(mctx, arg.wantMembers, arg.wantMembersRole, fromCache); err != nil {
  1370  			reason = fmt.Sprintf("satisfiesWantMembers -> %v", err)
  1371  			return false, true
  1372  		}
  1373  	}
  1374  
  1375  	// Repoll if we need a seqno not in the cache.
  1376  	// Does not force a repoll if we just need to fill in previous links
  1377  	if len(arg.needSeqnos) > 0 {
  1378  		if fromCache == nil || fromCache.MainChain() == nil {
  1379  			reason = "need seqnos and no cache"
  1380  			return false, true
  1381  		}
  1382  		if fromCache.MainChain().Chain.LastSeqno < l.seqnosMax(arg.needSeqnos) {
  1383  			reason = "need seqnos"
  1384  			return false, true
  1385  		}
  1386  	}
  1387  
  1388  	if fromCache == nil || fromCache.MainChain() == nil {
  1389  		reason = "no cache"
  1390  		// We need a merkle leaf when starting from scratch.
  1391  		return false, true
  1392  	}
  1393  
  1394  	cachedAt := fromCache.MainChain().CachedAt
  1395  	if cachedPolledAt != nil && *cachedPolledAt > cachedAt {
  1396  		cachedAt = *cachedPolledAt
  1397  	}
  1398  
  1399  	cacheIsOld := !l.isFresh(mctx, cachedAt)
  1400  	if cacheIsOld && !arg.staleOK {
  1401  		// We need a merkle leaf
  1402  		reason = "cacheIsOld"
  1403  		return false, true
  1404  	}
  1405  
  1406  	// InForceRepoll needs to a acquire a lock, so avoid it by checking it last.
  1407  	if l.InForceRepollMode(mctx) {
  1408  		reason = "InForceRepollMode"
  1409  		return false, true
  1410  	}
  1411  
  1412  	return false, false
  1413  }
  1414  
  1415  // Check whether the load produced a snapshot that can be returned to the caller.
  1416  // This should not check anything that is critical to the validity of the snapshot
  1417  // because the snapshot is put into the cache before this check.
  1418  // Considers:
  1419  // - NeedAdmin
  1420  // - NeedKeyGeneration
  1421  // - NeedSeqnos
  1422  func (l *TeamLoader) load2CheckReturn(mctx libkb.MetaContext, arg load2ArgT, shim Teamer) error {
  1423  	if arg.needAdmin {
  1424  		if !l.satisfiesNeedAdmin(mctx, arg.me, shim) {
  1425  			mctx.Debug("user %v is not an admin of team %v at seqno:%v", arg.me, arg.teamID, shim.MainChain().Chain.LastSeqno)
  1426  			return fmt.Errorf("user %v is not an admin of the team", arg.me)
  1427  		}
  1428  	}
  1429  
  1430  	// Repoll to get a new key generation
  1431  	if arg.needKeyGeneration > 0 {
  1432  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, shim); err != nil {
  1433  			return err
  1434  		}
  1435  	}
  1436  	if len(arg.needApplicationsAtGenerations) > 0 {
  1437  		if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, shim); err != nil {
  1438  			return err
  1439  		}
  1440  	}
  1441  	if arg.needKBFSKeyGeneration.Generation > 0 {
  1442  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, shim); err != nil {
  1443  			return err
  1444  		}
  1445  	}
  1446  	if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 {
  1447  		if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, arg.needApplicationsAtGenerationsWithKBFS, shim); err != nil {
  1448  			return err
  1449  		}
  1450  	}
  1451  
  1452  	if len(arg.needSeqnos) > 0 {
  1453  		if err := l.checkNeededSeqnos(mctx.Ctx(), shim.MainChain(), arg.needSeqnos); err != nil {
  1454  			return err
  1455  		}
  1456  	}
  1457  
  1458  	return nil
  1459  }
  1460  
  1461  // Whether the user is an admin at the snapshot, and there are no stubbed links, and keys are up to date.
  1462  func (l *TeamLoader) satisfiesNeedAdmin(mctx libkb.MetaContext, me keybase1.UserVersion, team Teamer) bool {
  1463  	if team == nil || team.MainChain() == nil {
  1464  		return false
  1465  	}
  1466  	state := newTeamSigChainState(team)
  1467  	if state.HasAnyStubbedLinks() {
  1468  		return false
  1469  	}
  1470  	if !l.hasSyncedSecrets(mctx, team) {
  1471  		return false
  1472  	}
  1473  	role, err := state.GetUserRole(me)
  1474  	if err != nil {
  1475  		mctx.Debug("TeamLoader error getting my role: %v", err)
  1476  		return false
  1477  	}
  1478  	if !role.IsAdminOrAbove() {
  1479  		if !state.IsSubteam() {
  1480  			return false
  1481  		}
  1482  		yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, me)
  1483  		if err != nil {
  1484  			mctx.Debug("TeamLoader error getting checking implicit admin: %s", err)
  1485  			return false
  1486  		}
  1487  		if !yes {
  1488  			return false
  1489  		}
  1490  	}
  1491  	return true
  1492  }
  1493  
  1494  // Check whether a user is an implicit admin of a team.
  1495  func (l *TeamLoader) isImplicitAdminOf(ctx context.Context, teamID keybase1.TeamID, ancestorID *keybase1.TeamID,
  1496  	me keybase1.UserVersion, uv keybase1.UserVersion) (bool, error) {
  1497  
  1498  	// IDs of ancestors that were not freshly polled.
  1499  	// Check them again with forceRepoll if the affirmative is not found cached.
  1500  	checkAgain := make(map[keybase1.TeamID]bool)
  1501  
  1502  	check1 := func(chain *TeamSigChainState) bool {
  1503  		role, err := chain.GetUserRole(uv)
  1504  		if err != nil {
  1505  			return false
  1506  		}
  1507  		return role.IsAdminOrAbove()
  1508  	}
  1509  
  1510  	i := 0
  1511  	for {
  1512  		i++
  1513  		if i >= 100 {
  1514  			// Break in case there's a bug in this loop.
  1515  			return false, fmt.Errorf("stuck in a loop while checking for implicit admin: %v", ancestorID)
  1516  		}
  1517  
  1518  		// Use load2 so that we can use subteam-reader and get secretless teams.
  1519  		ancestor, err := l.load2(ctx, load2ArgT{
  1520  			teamID:        *ancestorID,
  1521  			reason:        "isImplicitAdminOf-1",
  1522  			me:            me,
  1523  			readSubteamID: &teamID,
  1524  		})
  1525  		if err != nil {
  1526  			return false, err
  1527  		}
  1528  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1529  		// Do not let it out of sight.
  1530  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1531  
  1532  		if !ancestor.didRepoll {
  1533  			checkAgain[ancestorChain.GetID()] = true
  1534  		}
  1535  
  1536  		if check1(&ancestorChain) {
  1537  			return true, nil
  1538  		}
  1539  
  1540  		if !ancestorChain.IsSubteam() {
  1541  			break
  1542  		}
  1543  		// Get the next level up.
  1544  		ancestorID = ancestorChain.GetParentID()
  1545  	}
  1546  
  1547  	// The answer was not found to be yes in the cache.
  1548  	// Try again with the teams that were not polled as they might have unseen updates.
  1549  	for ancestorID := range checkAgain {
  1550  		ancestor, err := l.load2(ctx, load2ArgT{
  1551  			teamID:        ancestorID,
  1552  			reason:        "isImplicitAdminOf-again",
  1553  			me:            me,
  1554  			forceRepoll:   true, // Get the latest info.
  1555  			readSubteamID: &teamID,
  1556  		})
  1557  		if err != nil {
  1558  			return false, err
  1559  		}
  1560  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1561  		// Do not let it out of sight.
  1562  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1563  		if check1(&ancestorChain) {
  1564  			return true, nil
  1565  		}
  1566  	}
  1567  
  1568  	return false, nil
  1569  }
  1570  
  1571  func (l *TeamLoader) satisfiesNeedsKBFSKeyGeneration(mctx libkb.MetaContext,
  1572  	kbfs keybase1.TeamKBFSKeyRefresher, state Teamer) error {
  1573  	if kbfs.Generation == 0 {
  1574  		return nil
  1575  	}
  1576  	if state == nil {
  1577  		return fmt.Errorf("nil team does not contain KBFS key generation: %#v", kbfs)
  1578  	}
  1579  
  1580  	gen, err := newTeamSigChainState(state).GetLatestKBFSGeneration(kbfs.AppType)
  1581  	if err != nil {
  1582  		return err
  1583  	}
  1584  	if kbfs.Generation > gen {
  1585  		return NewKBFSKeyGenerationError(kbfs.Generation, gen)
  1586  	}
  1587  	return nil
  1588  }
  1589  
  1590  // Whether the snapshot has loaded at least up to the key generation and has the secret.
  1591  func (l *TeamLoader) satisfiesNeedKeyGeneration(mctx libkb.MetaContext, needKeyGeneration keybase1.PerTeamKeyGeneration, state Teamer) error {
  1592  	if needKeyGeneration == 0 {
  1593  		return nil
  1594  	}
  1595  	if state == nil {
  1596  		return fmt.Errorf("nil team does not contain key generation: %v", needKeyGeneration)
  1597  	}
  1598  	key, err := newTeamSigChainState(state).GetLatestPerTeamKey(mctx)
  1599  	if err != nil {
  1600  		return err
  1601  	}
  1602  	if needKeyGeneration > key.Gen {
  1603  		return fmt.Errorf("team key generation too low: %v < %v", key.Gen, needKeyGeneration)
  1604  	}
  1605  	_, ok := state.MainChain().PerTeamKeySeedsUnverified[needKeyGeneration]
  1606  	if !ok {
  1607  		return fmt.Errorf("team key secret missing for generation: %v", needKeyGeneration)
  1608  	}
  1609  	return nil
  1610  }
  1611  
  1612  // Whether the snapshot has loaded the reader key masks and key generations we
  1613  // need.
  1614  func (l *TeamLoader) satisfiesNeedApplicationsAtGenerations(mctx libkb.MetaContext,
  1615  	needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, team Teamer) error {
  1616  	if len(needApplicationsAtGenerations) == 0 {
  1617  		return nil
  1618  	}
  1619  	if team == nil || team.MainChain() == nil {
  1620  		return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations)
  1621  	}
  1622  	for ptkGen, apps := range needApplicationsAtGenerations {
  1623  		for _, app := range apps {
  1624  			if _, err := ApplicationKeyAtGeneration(mctx, team, app, ptkGen); err != nil {
  1625  				return err
  1626  			}
  1627  		}
  1628  	}
  1629  	return nil
  1630  }
  1631  
  1632  func (l *TeamLoader) satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx libkb.MetaContext,
  1633  	needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication,
  1634  	state Teamer) error {
  1635  	if len(needApplicationsAtGenerations) == 0 {
  1636  		return nil
  1637  	}
  1638  	if state == nil || state.MainChain() == nil {
  1639  		return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations)
  1640  	}
  1641  	for ptkGen, apps := range needApplicationsAtGenerations {
  1642  		for _, app := range apps {
  1643  			if _, err := ApplicationKeyAtGenerationWithKBFS(mctx, state, app, ptkGen); err != nil {
  1644  				return err
  1645  			}
  1646  		}
  1647  	}
  1648  	return nil
  1649  }
  1650  
  1651  // Whether the snapshot has each of `wantMembers` as a member.
  1652  func (l *TeamLoader) satisfiesWantMembers(mctx libkb.MetaContext,
  1653  	wantMembers []keybase1.UserVersion, wantMembersRole keybase1.TeamRole, state Teamer) error {
  1654  
  1655  	if wantMembersRole == keybase1.TeamRole_NONE {
  1656  		// Default to writer.
  1657  		wantMembersRole = keybase1.TeamRole_WRITER
  1658  	}
  1659  	if len(wantMembers) == 0 {
  1660  		return nil
  1661  	}
  1662  	if state == nil {
  1663  		return fmt.Errorf("nil team does not have wanted members")
  1664  	}
  1665  	for _, uv := range wantMembers {
  1666  		role, err := newTeamSigChainState(state).GetUserRole(uv)
  1667  		if err != nil {
  1668  			return fmt.Errorf("could not get wanted user role: %v", err)
  1669  		}
  1670  		if !role.IsOrAbove(wantMembersRole) {
  1671  			return fmt.Errorf("wanted user %v is a %v which is not at least %v", uv, role, wantMembersRole)
  1672  		}
  1673  	}
  1674  	return nil
  1675  }
  1676  
  1677  func (l *TeamLoader) mungeWantMembers(ctx context.Context, wantMembers []keybase1.UserVersion) (res []keybase1.UserVersion, err error) {
  1678  	for _, uv1 := range wantMembers {
  1679  		uv2 := uv1
  1680  		if uv2.EldestSeqno == 0 {
  1681  			// Lookup the latest eldest seqno for that uid.
  1682  			// This value may come from a cache.
  1683  			uv2.EldestSeqno, err = l.world.lookupEldestSeqno(ctx, uv2.Uid)
  1684  			if err != nil {
  1685  				return res, err
  1686  			}
  1687  			l.G().Log.CDebugf(ctx, "TeamLoader resolved wantMember %v -> %v", uv2.Uid, uv2.EldestSeqno)
  1688  		}
  1689  		res = append(res, uv2)
  1690  	}
  1691  	return res, err
  1692  }
  1693  
  1694  // Whether y is in xs.
  1695  func (l *TeamLoader) seqnosContains(xs []keybase1.Seqno, y keybase1.Seqno) bool {
  1696  	for _, x := range xs {
  1697  		if x.Eq(y) {
  1698  			return true
  1699  		}
  1700  	}
  1701  	return false
  1702  }
  1703  
  1704  // Return the max in a list of positive seqnos. Returns 0 if the list is empty
  1705  func (l *TeamLoader) seqnosMax(seqnos []keybase1.Seqno) (ret keybase1.Seqno) {
  1706  	for _, x := range seqnos {
  1707  		if x > ret {
  1708  			ret = x
  1709  		}
  1710  	}
  1711  	return ret
  1712  }
  1713  
  1714  // Whether a TeamData from the cache is fresh.
  1715  func (l *TeamLoader) isFresh(mctx libkb.MetaContext, cachedAt keybase1.Time) bool {
  1716  	if cachedAt.IsZero() {
  1717  		// This should never happen.
  1718  		mctx.Warning("TeamLoader encountered zero cached time")
  1719  		return false
  1720  	}
  1721  	diff := mctx.G().Clock().Now().Sub(cachedAt.Time())
  1722  	fresh := (diff <= freshnessLimit)
  1723  	if !fresh {
  1724  		mctx.Debug("TeamLoader cached snapshot is old: %v", diff)
  1725  	}
  1726  	return fresh
  1727  }
  1728  
  1729  // Whether the teams secrets are synced to the same point as its sigchain
  1730  // Does not check RKMs.
  1731  func (l *TeamLoader) hasSyncedSecrets(mctx libkb.MetaContext, team Teamer) bool {
  1732  	state := team.MainChain()
  1733  	n := len(team.MainChain().Chain.PerTeamKeys)
  1734  	offChainGen := len(state.PerTeamKeySeedsUnverified)
  1735  	mctx.Debug("TeamLoader#hasSyncedSecrets: found %d PTKs on the main chain (versus %d seeds)", n, offChainGen)
  1736  	if team.HiddenChain() != nil {
  1737  		m := len(team.HiddenChain().ReaderPerTeamKeys)
  1738  		mctx.Debug("TeamLoader#hasSyncedSecrets: found another %d PTKs on the hidden chain", m)
  1739  		n += m
  1740  	}
  1741  	return (n == offChainGen)
  1742  }
  1743  
  1744  func (l *TeamLoader) logIfUnsyncedSecrets(ctx context.Context, state *keybase1.TeamData) {
  1745  	onChainGen := keybase1.PerTeamKeyGeneration(len(state.Chain.PerTeamKeys))
  1746  	offChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified))
  1747  	if onChainGen != offChainGen {
  1748  		l.G().Log.CDebugf(ctx, "TeamLoader unsynced secrets local:%v != chain:%v ", offChainGen, onChainGen)
  1749  	}
  1750  }
  1751  
  1752  func (l *TeamLoader) lows(mctx libkb.MetaContext, state *keybase1.TeamData, hp *hidden.LoaderPackage, arg load2ArgT) getLinksLows {
  1753  	var lows getLinksLows
  1754  	if state != nil {
  1755  		chain := TeamSigChainState{inner: state.Chain}
  1756  		lows.Seqno = chain.GetLatestSeqno()
  1757  		if !arg.foundRKMHole {
  1758  			lows.PerTeamKey = keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified))
  1759  		}
  1760  	}
  1761  	if hp != nil {
  1762  		lows.HiddenChainSeqno = hp.LastFullSeqno()
  1763  	}
  1764  	return lows
  1765  }
  1766  
  1767  func (l *TeamLoader) OnLogout(mctx libkb.MetaContext) error {
  1768  	l.storage.ClearMem()
  1769  	return nil
  1770  }
  1771  
  1772  func (l *TeamLoader) OnDbNuke(mctx libkb.MetaContext) error {
  1773  	l.storage.ClearMem()
  1774  	return nil
  1775  }
  1776  
  1777  // Clear the in-memory cache.
  1778  func (l *TeamLoader) ClearMem() {
  1779  	l.storage.ClearMem()
  1780  }
  1781  
  1782  func (l *TeamLoader) VerifyTeamName(ctx context.Context, id keybase1.TeamID, name keybase1.TeamName) error {
  1783  	if name.IsRootTeam() {
  1784  		if !name.ToTeamID(id.IsPublic()).Eq(id) {
  1785  			return NewResolveError(name, id)
  1786  		}
  1787  		return nil
  1788  	}
  1789  	teamData, _, err := l.Load(ctx, keybase1.LoadTeamArg{
  1790  		ID:     id,
  1791  		Public: id.IsPublic(),
  1792  	})
  1793  	if err != nil {
  1794  		return err
  1795  	}
  1796  	gotName := teamData.Name
  1797  	if !gotName.Eq(name) {
  1798  		return NewResolveError(name, id)
  1799  	}
  1800  	return nil
  1801  }
  1802  
  1803  // List all the admins of ancestor teams.
  1804  // Includes admins of the specified team only if they are also admins of ancestor teams.
  1805  // The specified team must be a subteam, or an error is returned.
  1806  // Always sends a flurry of RPCs to get the most up to date info.
  1807  func (l *TeamLoader) ImplicitAdmins(ctx context.Context, teamID keybase1.TeamID) (impAdmins []keybase1.UserVersion, err error) {
  1808  	impAdminsMap := make(map[string]keybase1.UserVersion) // map to remove dups
  1809  	err = l.MapTeamAncestors(ctx, func(t keybase1.TeamSigChainState, _ keybase1.TeamName) error {
  1810  		ancestorChain := TeamSigChainState{inner: t}
  1811  		// Gather the admins.
  1812  		adminRoles := []keybase1.TeamRole{keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN}
  1813  		for _, role := range adminRoles {
  1814  			uvs, err := ancestorChain.GetUsersWithRole(role)
  1815  			if err != nil {
  1816  				return err
  1817  			}
  1818  			for _, uv := range uvs {
  1819  				impAdminsMap[uv.String()] = uv
  1820  			}
  1821  		}
  1822  		return nil
  1823  	}, teamID, "implicitAdminsAncestor", func(keybase1.TeamSigChainState) bool { return true })
  1824  	if err != nil {
  1825  		return nil, err
  1826  	}
  1827  	for _, uv := range impAdminsMap {
  1828  		impAdmins = append(impAdmins, uv)
  1829  	}
  1830  	return impAdmins, nil
  1831  }
  1832  
  1833  // MapTeamAncestors does NOT map over the team itself.
  1834  func (l *TeamLoader) MapTeamAncestors(
  1835  	ctx context.Context,
  1836  	f func(keybase1.TeamSigChainState, keybase1.TeamName) error,
  1837  	teamID keybase1.TeamID,
  1838  	reason string,
  1839  	forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool,
  1840  ) (err error) {
  1841  	initialTeamIdx := 0
  1842  
  1843  	me, err := l.world.getMe(ctx)
  1844  	if err != nil {
  1845  		return NewMapAncestorsError(err, initialTeamIdx)
  1846  	}
  1847  
  1848  	// Load the argument team
  1849  	team, _, err := l.load1(ctx, me, keybase1.LoadTeamArg{
  1850  		ID:      teamID,
  1851  		Public:  teamID.IsPublic(),
  1852  		StaleOK: true, // We only use immutable fields.
  1853  	})
  1854  	if err != nil {
  1855  		return NewMapAncestorsError(err, initialTeamIdx)
  1856  	}
  1857  	teamChain := TeamSigChainState{inner: team.Chain}
  1858  	if !teamChain.IsSubteam() {
  1859  		return NewMapAncestorsError(
  1860  			fmt.Errorf("cannot map over parents of a root team: %v", teamID),
  1861  			initialTeamIdx,
  1862  		)
  1863  	}
  1864  	return l.mapTeamAncestorsHelper(ctx, f, teamID, teamChain.GetParentID(), reason, forceFullReloadOnceToAssert)
  1865  }
  1866  
  1867  func (l *TeamLoader) mapTeamAncestorsHelper(
  1868  	ctx context.Context,
  1869  	f func(keybase1.TeamSigChainState, keybase1.TeamName) error,
  1870  	teamID keybase1.TeamID,
  1871  	ancestorID *keybase1.TeamID,
  1872  	reason string,
  1873  	forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool,
  1874  ) (err error) {
  1875  	i := 0
  1876  
  1877  	defer func() {
  1878  		if err != nil {
  1879  			err = NewMapAncestorsError(err, i)
  1880  		}
  1881  	}()
  1882  
  1883  	me, err := l.world.getMe(ctx)
  1884  	if err != nil {
  1885  		return err
  1886  	}
  1887  
  1888  	for {
  1889  		i++
  1890  		if i >= 100 {
  1891  			// Break in case there's a bug in this loop.
  1892  			return fmt.Errorf("stuck in a loop while mapping over team parents: %v", ancestorID)
  1893  		}
  1894  
  1895  		load2Arg := load2ArgT{
  1896  			teamID:        *ancestorID,
  1897  			reason:        reason,
  1898  			me:            me,
  1899  			forceRepoll:   true, // Get the latest info.
  1900  			readSubteamID: &teamID,
  1901  		}
  1902  
  1903  		var ancestor *load2ResT
  1904  		for {
  1905  			var err error
  1906  			// Use load2 so that we can use subteam-reader and get secretless teams.
  1907  			ancestor, err = l.load2(ctx, load2Arg)
  1908  			if err != nil {
  1909  				return err
  1910  			}
  1911  
  1912  			if forceFullReloadOnceToAssert == nil ||
  1913  				forceFullReloadOnceToAssert(ancestor.team.Chain) {
  1914  				break
  1915  			}
  1916  			if load2Arg.forceFullReload {
  1917  				return fmt.Errorf("failed to assert predicate in ancestor %v after full force reload", ancestor.team.ID())
  1918  			}
  1919  			load2Arg.forceFullReload = true
  1920  		}
  1921  
  1922  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1923  		// Do not let it out of sight.
  1924  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1925  
  1926  		err = f(ancestor.team.Chain, ancestor.team.Name)
  1927  		if err != nil {
  1928  			return err
  1929  		}
  1930  
  1931  		if !ancestorChain.IsSubteam() {
  1932  			break
  1933  		}
  1934  		// Get the next level up.
  1935  		ancestorID = ancestorChain.GetParentID()
  1936  	}
  1937  
  1938  	return nil
  1939  }
  1940  
  1941  func (l *TeamLoader) NotifyTeamRename(ctx context.Context, id keybase1.TeamID, newName string) error {
  1942  	// ignore newName from the server
  1943  
  1944  	// Load up the ancestor chain with ForceRepoll.
  1945  	// Then load down the ancestor chain without it (expect cache hits).
  1946  	// Not the most elegant way, but it will get the job done.
  1947  	// Each load on the way down will recalculate that team's name.
  1948  
  1949  	var ancestorIDs []keybase1.TeamID
  1950  
  1951  	me, err := l.world.getMe(ctx)
  1952  	if err != nil {
  1953  		return err
  1954  	}
  1955  
  1956  	loopID := &id
  1957  	for loopID != nil {
  1958  		load2Res, err := l.load2(ctx, load2ArgT{
  1959  			teamID:        *loopID,
  1960  			reason:        "NotifyTeamRename-force",
  1961  			forceRepoll:   true,
  1962  			readSubteamID: &id,
  1963  			me:            me,
  1964  		})
  1965  		if err != nil {
  1966  			return err
  1967  		}
  1968  		ancestorIDs = append(ancestorIDs, *loopID)
  1969  		chain := TeamSigChainState{inner: load2Res.team.Chain}
  1970  		if chain.IsSubteam() {
  1971  			loopID = chain.GetParentID()
  1972  		} else {
  1973  			loopID = nil
  1974  		}
  1975  	}
  1976  
  1977  	// reverse ancestorIDs so the root team appears first
  1978  	sort.SliceStable(ancestorIDs, func(i, j int) bool { return i > j })
  1979  
  1980  	for _, loopID := range ancestorIDs {
  1981  		_, err := l.load2(ctx, load2ArgT{
  1982  			teamID:        loopID,
  1983  			reason:        "NotifyTeamRename-quick",
  1984  			readSubteamID: &id,
  1985  			me:            me,
  1986  		})
  1987  		if err != nil {
  1988  			return err
  1989  		}
  1990  	}
  1991  
  1992  	return nil
  1993  }
  1994  
  1995  func (l *TeamLoader) getHeadMerkleSeqno(mctx libkb.MetaContext, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState) (ret keybase1.Seqno, err error) {
  1996  	defer mctx.Trace("TeamLoader#getHeadMerkleSeqno", &err)()
  1997  
  1998  	if state.HeadMerkle != nil {
  1999  		return state.HeadMerkle.Seqno, nil
  2000  	}
  2001  	headSeqno := keybase1.Seqno(1)
  2002  	expectedLinkRaw, ok := state.LinkIDs[headSeqno]
  2003  	if !ok {
  2004  		return ret, fmt.Errorf("couldn't find head link in team state during audit")
  2005  	}
  2006  	expectedLink, err := libkb.ImportLinkID(expectedLinkRaw)
  2007  	if err != nil {
  2008  		return ret, err
  2009  	}
  2010  	teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Id, []keybase1.Seqno{headSeqno}, &readSubteamID)
  2011  	if err != nil {
  2012  		return ret, err
  2013  	}
  2014  	newLinks, err := teamUpdate.unpackLinks(mctx)
  2015  	if err != nil {
  2016  		return ret, err
  2017  	}
  2018  	if len(newLinks) != 1 {
  2019  		return ret, fmt.Errorf("expected only one chainlink back; got %d", len(newLinks))
  2020  	}
  2021  	headLink := newLinks[0]
  2022  	err = headLink.AssertInnerOuterMatch()
  2023  	if err != nil {
  2024  		return ret, err
  2025  	}
  2026  	if headLink.Seqno() != headSeqno {
  2027  		return ret, NewInvalidLink(headLink, "wrong head seqno; wanted 1 but got something else")
  2028  	}
  2029  	if !headLink.LinkID().Eq(expectedLink) {
  2030  		return ret, NewInvalidLink(headLink, "wrong head link hash: %s != %s", headLink.LinkID(), expectedLink)
  2031  	}
  2032  	if headLink.isStubbed() {
  2033  		return ret, NewInvalidLink(headLink, "got a stubbed head link, but wasn't expecting that")
  2034  	}
  2035  	headMerkle := headLink.inner.Body.MerkleRoot.ToMerkleRootV2()
  2036  	state.HeadMerkle = &headMerkle
  2037  	return headMerkle.Seqno, nil
  2038  }
  2039  
  2040  func (l *TeamLoader) audit(ctx context.Context, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState, hiddenChain *keybase1.HiddenTeamChain, lastMerkleRoot *libkb.MerkleRoot, auditMode keybase1.AuditMode) (err error) {
  2041  	mctx := libkb.NewMetaContext(ctx, l.G())
  2042  
  2043  	if l.G().Env.Test.TeamSkipAudit {
  2044  		mctx.Debug("skipping audit in test due to flag")
  2045  		return nil
  2046  	}
  2047  
  2048  	headMerklSeqno, err := l.getHeadMerkleSeqno(mctx, readSubteamID, state)
  2049  	if err != nil {
  2050  		return err
  2051  	}
  2052  
  2053  	err = mctx.G().GetTeamAuditor().AuditTeam(mctx, state.Id, state.Public, headMerklSeqno, state.LinkIDs, hiddenChain.GetOuter(), state.LastSeqno, hiddenChain.GetLastCommittedSeqno(), lastMerkleRoot, auditMode)
  2054  	return err
  2055  }
  2056  
  2057  func (l *TeamLoader) ForceRepollUntil(ctx context.Context, dtime gregor.TimeOrOffset) error {
  2058  	l.G().Log.CDebugf(ctx, "TeamLoader#ForceRepollUntil(%+v)", dtime)
  2059  	l.forceRepollMutex.Lock()
  2060  	defer l.forceRepollMutex.Unlock()
  2061  	l.forceRepollUntil = dtime
  2062  	return nil
  2063  }
  2064  
  2065  func (l *TeamLoader) InForceRepollMode(mctx libkb.MetaContext) bool {
  2066  	l.forceRepollMutex.Lock()
  2067  	defer l.forceRepollMutex.Unlock()
  2068  	if l.forceRepollUntil == nil {
  2069  		return false
  2070  	}
  2071  	if !l.forceRepollUntil.Before(mctx.G().Clock().Now()) {
  2072  		mctx.Debug("TeamLoader#InForceRepollMode: returning true")
  2073  		return true
  2074  	}
  2075  	l.forceRepollUntil = nil
  2076  	return false
  2077  }