github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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  	if hiddenResp.CommittedHiddenTail != nil {
   546  		mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v CommittedSeqno %v", hiddenResp, hiddenResp.UncommittedSeqno, hiddenResp.CommittedHiddenTail.Seqno)
   547  	} else {
   548  		mctx.Debug("hiddenResp: %+v UncommittedSeqno %+v", hiddenResp, hiddenResp.UncommittedSeqno)
   549  	}
   550  
   551  	switch hiddenResp.RespType {
   552  	case libkb.MerkleHiddenResponseTypeNONE:
   553  		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).")
   554  		return true, nil
   555  	case libkb.MerkleHiddenResponseTypeFLAGOFF:
   556  		mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as the hidden flag is off.")
   557  		return true, nil
   558  	default:
   559  		return hiddenPackage.CheckHiddenMerklePathResponseAndAddRatchets(mctx, hiddenResp)
   560  	}
   561  }
   562  
   563  func (l *TeamLoader) load2InnerLockedRetry(ctx context.Context, arg load2ArgT) (*load2ResT, error) {
   564  	ctx, tbs := l.G().CTimeBuckets(ctx)
   565  	mctx := libkb.NewMetaContext(ctx, l.G())
   566  	tracer := l.G().CTimeTracer(ctx, "TeamLoader.load2ILR", teamEnv.Profile)
   567  	defer tracer.Finish()
   568  
   569  	defer tbs.LogIfNonZero(ctx, "API.request")
   570  
   571  	var err error
   572  	var didRepoll bool
   573  	lkc := newLoadKeyCache()
   574  
   575  	// Fetch from cache
   576  	tracer.Stage("cache load")
   577  	tailCheckRet, frozen, tombstoned := l.storage.Get(mctx, arg.teamID, arg.public)
   578  	if tombstoned {
   579  		return nil, NewTeamTombstonedError()
   580  	}
   581  
   582  	// Fetch last polled time from merkle cache
   583  	merklePolledAt := l.merkleStorage.Get(mctx, arg.teamID, arg.public)
   584  
   585  	var ret *keybase1.TeamData
   586  	if !frozen && !arg.forceFullReload {
   587  		// Load from cache
   588  		ret = tailCheckRet
   589  	}
   590  
   591  	if ret != nil && !ret.Chain.Reader.Eq(arg.me) {
   592  		// Check that we are the same person as when this team was last loaded as a courtesy.
   593  		// This should never happen. We shouldn't be able to decrypt someone else's snapshot.
   594  		mctx.Warning("TeamLoader discarding snapshot for wrong user: (%v, %v) != (%v, %v)",
   595  			arg.me.Uid, arg.me.EldestSeqno, ret.Chain.Reader.Uid, ret.Chain.Reader.EldestSeqno)
   596  		ret = nil
   597  	}
   598  
   599  	var cachedName *keybase1.TeamName
   600  	if ret != nil && !ret.Name.IsNil() {
   601  		cachedName = &ret.Name
   602  	}
   603  
   604  	hiddenPackage, err := l.hiddenPackage(mctx, arg.teamID, ret, arg.me)
   605  	if err != nil {
   606  		return nil, err
   607  	}
   608  
   609  	teamShim := func() *TeamShim {
   610  		return &TeamShim{Data: ret, Hidden: hiddenPackage.ChainData()}
   611  	}
   612  
   613  	// Determine whether to repoll merkle.
   614  	discardCache, repoll := l.load2DecideRepoll(mctx, arg, teamShim(), merklePolledAt)
   615  	if discardCache {
   616  		ret = nil
   617  		repoll = true
   618  	}
   619  
   620  	tracer.Stage("deepcopy")
   621  	if ret != nil {
   622  		// If we're pulling from a previous snapshot (that, let's say, we got from a shared cache),
   623  		// then make sure to DeepCopy() data out of it before we start mutating it below. We used
   624  		// to do this every step through the new links, but that was very expensive in terms of CPU
   625  		// for big teams, since it was hidden quadratic behavior.
   626  		tmp := ret.DeepCopy()
   627  		ret = &tmp
   628  	} else {
   629  		mctx.Debug("TeamLoader not using snapshot")
   630  	}
   631  
   632  	tracer.Stage("merkle")
   633  	var lastSeqno keybase1.Seqno
   634  	var lastLinkID keybase1.LinkID
   635  	var hiddenIsFresh bool
   636  	var lastMerkleRoot *libkb.MerkleRoot
   637  
   638  	// hiddenResp will be nill iff we do not make the merkleLookupWithHidden
   639  	// call. If the server does not return any hidden data, we will encode that
   640  	// as a non nil response whose RespType is MerkleHiddenResponseTypeNONE.
   641  	var hiddenResp *libkb.MerkleHiddenResponse
   642  
   643  	if (ret == nil) || repoll {
   644  		mctx.Debug("TeamLoader looking up merkle leaf (force:%v)", arg.forceRepoll)
   645  
   646  		// Reference the merkle tree to fetch the sigchain tail leaf for the team.
   647  		lastSeqno, lastLinkID, hiddenResp, lastMerkleRoot, err = l.world.merkleLookupWithHidden(ctx, arg.teamID, arg.public)
   648  		if err != nil {
   649  			return nil, err
   650  		}
   651  		mctx.Debug("received lastSeqno %v, lastLinkID %v", lastSeqno, lastLinkID)
   652  
   653  		hiddenIsFresh, err = l.checkHiddenResponse(mctx, hiddenPackage, hiddenResp)
   654  		if err != nil {
   655  			return nil, err
   656  		}
   657  
   658  		didRepoll = true
   659  	} else {
   660  		lastSeqno = ret.Chain.LastSeqno
   661  		lastLinkID = ret.Chain.LastLinkID
   662  		hiddenIsFresh = true
   663  	}
   664  
   665  	// For child calls to load2, the subteam reader ID is carried up
   666  	// or if it doesn't exist, start at this team.
   667  	readSubteamID := arg.teamID
   668  	if arg.readSubteamID != nil {
   669  		readSubteamID = *arg.readSubteamID
   670  	}
   671  
   672  	proofSet := newProofSet(l.G())
   673  	var parentChildOperations []*parentChildOperation
   674  
   675  	// Backfill stubbed links that need to be filled now.
   676  	tracer.Stage("backfill")
   677  	var filledInStubbedLinks bool
   678  	if ret != nil && len(arg.needSeqnos) > 0 {
   679  		ret, proofSet, parentChildOperations, err = l.fillInStubbedLinks(
   680  			mctx, arg.me, arg.teamID, ret, arg.needSeqnos, readSubteamID, proofSet, parentChildOperations, lkc)
   681  		if err != nil {
   682  			return nil, err
   683  		}
   684  		filledInStubbedLinks = true
   685  	}
   686  
   687  	tracer.Stage("pre-fetch")
   688  	var fetchLinksAndOrSecrets bool
   689  	if ret == nil {
   690  		mctx.Debug("TeamLoader fetching: no cache")
   691  		// We have no cache
   692  		fetchLinksAndOrSecrets = true
   693  	} else if ret.Chain.LastSeqno < lastSeqno {
   694  		mctx.Debug("TeamLoader fetching: chain update")
   695  		// The cache is definitely behind
   696  		fetchLinksAndOrSecrets = true
   697  	} else if !hiddenIsFresh {
   698  		mctx.Debug("TeamLoader fetching: hidden chain wasn't fresh")
   699  		fetchLinksAndOrSecrets = true
   700  	} else if !l.hasSyncedSecrets(mctx, teamShim()) {
   701  		// The cached secrets are behind the cached chain.
   702  		// We may need to hit the server for secrets, even though there are no new links.
   703  		if arg.needAdmin {
   704  			mctx.Debug("TeamLoader fetching: NeedAdmin")
   705  			// Admins should always have up-to-date secrets. But not necessarily RKMs.
   706  			fetchLinksAndOrSecrets = true
   707  		}
   708  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, teamShim()); err != nil {
   709  			mctx.Debug("TeamLoader fetching: NeedKeyGeneration: %v", err)
   710  			fetchLinksAndOrSecrets = true
   711  		}
   712  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, teamShim()); err != nil {
   713  			mctx.Debug("TeamLoader fetching: KBFSNeedKeyGeneration: %v", err)
   714  			fetchLinksAndOrSecrets = true
   715  		}
   716  		if arg.readSubteamID == nil {
   717  			// This is not a recursive load. We should have the keys.
   718  			// This may be an extra round trip for public teams you're not in.
   719  			mctx.Debug("TeamLoader fetching: primary load")
   720  			fetchLinksAndOrSecrets = true
   721  		}
   722  	}
   723  	// hasSyncedSecrets does not account for RKMs. So check RKM refreshers separeately.
   724  
   725  	if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, teamShim()); err != nil {
   726  		mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerations: %v", err)
   727  		fetchLinksAndOrSecrets = true
   728  	}
   729  	if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx,
   730  		arg.needApplicationsAtGenerationsWithKBFS, teamShim()); err != nil {
   731  		mctx.Debug("TeamLoader fetching: NeedApplicationsAtGenerationsWithKBFS: %v", err)
   732  		fetchLinksAndOrSecrets = true
   733  	}
   734  
   735  	// Pull new links from the server
   736  	tracer.Stage("fetch")
   737  	var teamUpdate *rawTeam
   738  	if fetchLinksAndOrSecrets {
   739  		lows := l.lows(mctx, ret, hiddenPackage, arg)
   740  		mctx.Debug("TeamLoader getting links from server (%+v)", lows)
   741  		teamUpdate, err = l.world.getNewLinksFromServer(ctx, arg.teamID, lows, arg.readSubteamID)
   742  		if err != nil {
   743  			return nil, err
   744  		}
   745  		mctx.Debug("TeamLoader got %v links", len(teamUpdate.Chain))
   746  		hiddenPackage.SetRatchetBlindingKeySet(teamUpdate.RatchetBlindingKeySet)
   747  	}
   748  
   749  	tracer.Stage("unpack")
   750  	links, err := teamUpdate.unpackLinks(mctx)
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  	var prev libkb.LinkID
   755  	if ret != nil {
   756  		prev, err = TeamSigChainState{inner: ret.Chain}.GetLatestLibkbLinkID()
   757  		if err != nil {
   758  			return nil, err
   759  		}
   760  	}
   761  
   762  	// A link which was signed by an admin. Sloppily the latest such link.
   763  	// Sloppy because this calculation misses out on e.g. a rotate_key signed by an admin.
   764  	// This value is used for skipping fullVerify on team.leave links, see `verifyLink`.
   765  	var fullVerifyCutoff keybase1.Seqno
   766  	for i := len(links) - 1; i >= 0; i-- {
   767  		if links[i].LinkType().RequiresAtLeastRole().IsAdminOrAbove() {
   768  			fullVerifyCutoff = links[i].Seqno()
   769  			break
   770  		}
   771  	}
   772  	if fullVerifyCutoff > 0 {
   773  		mctx.Debug("fullVerifyCutoff: %v", fullVerifyCutoff)
   774  	}
   775  
   776  	tracer.Stage("userPreload enable:%v parallel:%v wait:%v",
   777  		teamEnv.UserPreloadEnable, teamEnv.UserPreloadParallel, teamEnv.UserPreloadWait)
   778  	preloadCancel := l.userPreload(ctx, links, fullVerifyCutoff)
   779  	defer preloadCancel()
   780  
   781  	tracer.Stage("linkloop (%v)", len(links))
   782  	parentsCache := make(parentChainCache)
   783  
   784  	// Don't log in the middle links if there are a great many links.
   785  	suppressLoggingStart := 5
   786  	suppressLoggingUpto := len(links) - 5
   787  	for i, link := range links {
   788  		var err error
   789  		ret, prev, err = l.doOneLink(mctx, arg, ret, hiddenPackage, link, i, suppressLoggingStart, suppressLoggingUpto, lastSeqno, &parentChildOperations, prev, fullVerifyCutoff, readSubteamID, proofSet, lkc, &parentsCache)
   790  		if err != nil {
   791  			return nil, err
   792  		}
   793  	}
   794  
   795  	if ret == nil {
   796  		return nil, fmt.Errorf("team loader fault: got nil from load2")
   797  	}
   798  
   799  	encKID, gen, role, err := l.hiddenPackageGetter(mctx, arg.teamID, ret, arg.me)()
   800  	if err != nil {
   801  		return nil, err
   802  	}
   803  
   804  	// If we did get an update from the server (hiddenResp != nil) are not a
   805  	// restricted bot AND this is not a recursive load (arg.readSubteamID == nil),
   806  	// then the server should have given us hidden chain data.
   807  	if hiddenResp != nil && hiddenResp.RespType == libkb.MerkleHiddenResponseTypeNONE && !role.IsRestrictedBot() && arg.readSubteamID == nil {
   808  		return nil, libkb.NewHiddenChainDataMissingError("Not a restricted bot or recursive load, but the server did not return merkle hidden chain data")
   809  	}
   810  
   811  	// Update the hidden package with team metadata once we process all of the
   812  	// links. This is necessary since we need the role to be up to date to know
   813  	// if we should skip seed checks on the hidden chain if we are loading as a
   814  	// RESTRICTEDBOT.
   815  	hiddenPackage.UpdateTeamMetadata(encKID, gen, role)
   816  
   817  	// Be sure to update the hidden chain after the main chain, since the latter can "ratchet" the former
   818  	err = hiddenPackage.Update(mctx, teamUpdate.GetHiddenChain(), hiddenResp.GetUncommittedSeqno())
   819  
   820  	if err != nil {
   821  		return nil, err
   822  	}
   823  	err = hiddenPackage.CheckPTKsForDuplicates(mctx, func(g keybase1.PerTeamKeyGeneration) bool {
   824  		_, ok := ret.Chain.PerTeamKeys[g]
   825  		return ok
   826  	})
   827  	if err != nil {
   828  		return nil, err
   829  	}
   830  
   831  	// The hidden team has pointers from the hidden chain up to the visible chain; check that they
   832  	// match the loaded team. We should have a full load of the team, so all parent pointers
   833  	// better hit their mark.
   834  	err = hiddenPackage.CheckParentPointersOnFullLoad(mctx, ret)
   835  	if err != nil {
   836  		return nil, err
   837  	}
   838  
   839  	preloadCancel()
   840  	if len(links) > 0 {
   841  		tbs.Log(ctx, "TeamLoader.verifyLink")
   842  		tbs.Log(ctx, "TeamLoader.applyNewLink")
   843  		tbs.Log(ctx, "SigChain.LoadFromServer.ReadAll")
   844  		tbs.Log(ctx, "loadKeyCache.loadKeyV2")
   845  		if teamEnv.Profile {
   846  			tbs.Log(ctx, "LoaderContextG.loadKeyV2")
   847  			tbs.Log(ctx, "CachedUPAKLoader.LoadKeyV2") // note LoadKeyV2 calls Load2
   848  			tbs.Log(ctx, "CachedUPAKLoader.LoadV2")
   849  			tbs.Log(ctx, "CachedUPAKLoader.DeepCopy")
   850  			mctx.Debug("TeamLoader lkc cache hits: %v", lkc.cacheHits)
   851  		}
   852  	}
   853  
   854  	if !ret.Chain.LastLinkID.Eq(lastLinkID) {
   855  		return nil, fmt.Errorf("wrong sigchain link ID: %v != %v",
   856  			ret.Chain.LastLinkID, lastLinkID)
   857  	}
   858  
   859  	if tailCheckRet != nil {
   860  		// If we previously discarded cache due to forceFullReload, or left the
   861  		// team, froze it, and are rejoining, make sure the previous tail is
   862  		// still in the chain.
   863  		// The chain loader ensures it is part of a well-formed chain with correct prevs.
   864  		linkID := ret.Chain.LinkIDs[tailCheckRet.Chain.LastSeqno]
   865  		if !linkID.Eq(tailCheckRet.Chain.LastLinkID) {
   866  			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,
   867  				tailCheckRet.Chain.LastLinkID, ret.Frozen, linkID)
   868  		}
   869  	}
   870  
   871  	tracer.Stage("pco")
   872  	err = l.checkParentChildOperations(ctx,
   873  		arg.me, arg.teamID, ret.Chain.ParentID, readSubteamID, parentChildOperations, proofSet)
   874  	if err != nil {
   875  		return nil, err
   876  	}
   877  
   878  	tracer.Stage("checkproofs")
   879  	err = l.checkProofs(ctx, ret, proofSet)
   880  	if err != nil {
   881  		return nil, err
   882  	}
   883  
   884  	tracer.Stage("secrets")
   885  	if teamUpdate != nil {
   886  		if teamUpdate.SubteamReader {
   887  			// Only allow subteam-reader results if we are in a recursive load.
   888  			if arg.readSubteamID == nil {
   889  				return nil, fmt.Errorf("unexpected subteam reader result")
   890  			}
   891  		} else {
   892  			stateWrapper := newTeamSigChainState(teamShim())
   893  			role, err := stateWrapper.GetUserRole(arg.me)
   894  			if err != nil {
   895  				role = keybase1.TeamRole_NONE
   896  			}
   897  			// Add the secrets.
   898  			// If it's a public team, there might not be secrets. (If we're not in the team)
   899  			// Restricted bots don't have any team secrets, so we also short circuit.
   900  			if !role.IsRestrictedBot() && (!ret.Chain.Public || (teamUpdate.Box != nil)) {
   901  				err = l.addSecrets(mctx, teamShim(), arg.me, teamUpdate.Box, teamUpdate.Prevs, teamUpdate.ReaderKeyMasks)
   902  				if err != nil {
   903  					return nil, pkgErrors.Wrap(err, "loading team secrets")
   904  				}
   905  
   906  				err = l.computeSeedChecks(ctx, ret)
   907  				if err != nil {
   908  					return nil, err
   909  				}
   910  
   911  				if teamUpdate.LegacyTLFUpgrade != nil {
   912  					err = l.addKBFSCryptKeys(mctx, teamShim(), teamUpdate.LegacyTLFUpgrade)
   913  					if err != nil {
   914  						return nil, fmt.Errorf("loading KBFS crypt keys: %v", err)
   915  					}
   916  				}
   917  			}
   918  			if role.IsRestrictedBot() {
   919  				// Clear out any secrets we may have had in memory if we were a
   920  				// previous role that had PTK access.
   921  				state := teamShim().MainChain()
   922  				state.PerTeamKeySeedsUnverified = make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem)
   923  				state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64)
   924  				state.TlfCryptKeys = make(map[keybase1.TeamApplication][]keybase1.CryptKey)
   925  			}
   926  		}
   927  	}
   928  
   929  	// Note that we might have done so just above after adding secrets, but before adding
   930  	// KBFS crypt keys. But it's cheap to run this method twice in a row.
   931  	tracer.Stage("computeSeedChecks")
   932  	err = l.computeSeedChecks(ctx, ret)
   933  	if err != nil {
   934  		return nil, err
   935  	}
   936  
   937  	if !arg.skipSeedCheck && arg.readSubteamID == nil {
   938  		err = hiddenPackage.CheckUpdatesAgainstSeedsWithMap(mctx, ret.PerTeamKeySeedsUnverified)
   939  		if err != nil {
   940  			return nil, err
   941  		}
   942  	}
   943  
   944  	// Make sure public works out
   945  	if ret.Chain.Public != arg.public {
   946  		return nil, fmt.Errorf("team public mismatch: chain:%v != arg:%v", ret.Chain.Public, arg.public)
   947  	}
   948  	if ret.Chain.Id.IsPublic() != ret.Chain.Public {
   949  		return nil, fmt.Errorf("team public mismatch: id:%v != chain:%v", ret.Chain.Id.IsPublic(), ret.Chain.Public)
   950  	}
   951  
   952  	// Sanity check the id
   953  	if !ret.Chain.Id.Eq(arg.teamID) {
   954  		return nil, fmt.Errorf("team id mismatch: %v != %v", ret.Chain.Id.String(), arg.teamID.String())
   955  	}
   956  
   957  	// Recalculate the team name.
   958  	// This must always run to pick up changes on chain and off-chain with ancestor renames.
   959  	// Also because without this a subteam could claim any parent in its name.
   960  	tracer.Stage("namecalc")
   961  	newName, err := l.calculateName(ctx, ret, arg.me, readSubteamID, arg.staleOK)
   962  	if err != nil {
   963  		return nil, fmt.Errorf("error recalculating name for %v: %v", ret.Name, err)
   964  	}
   965  	if !ret.Name.Eq(newName) {
   966  		// This deep copy is an absurd price to pay, but these mid-team renames should be quite rare.
   967  		copy := ret.DeepCopy()
   968  		ret = &copy
   969  		ret.Name = newName
   970  	}
   971  
   972  	var needHiddenRotate bool
   973  	if !arg.skipNeedHiddenRotateCheck {
   974  		needHiddenRotate, err = l.checkNeedRotate(mctx, ret, arg.me, hiddenPackage)
   975  		if err != nil {
   976  			return nil, err
   977  		}
   978  	}
   979  
   980  	err = hiddenPackage.Commit(mctx)
   981  	if err != nil {
   982  		return nil, err
   983  	}
   984  
   985  	l.logIfUnsyncedSecrets(ctx, ret)
   986  
   987  	// Mutating this field is safe because only TeamLoader
   988  	// while holding the single-flight lock reads or writes this field.
   989  	ret.CachedAt = keybase1.ToTime(l.G().Clock().Now())
   990  
   991  	// Clear the untrusted seqno hint.
   992  	// Mutating this field is safe because only TeamLoader
   993  	// while holding the single-flight lock reads or writes this field.
   994  	ret.LatestSeqnoHint = 0
   995  
   996  	if didRepoll {
   997  		tracer.Stage("audit")
   998  		auditMode := arg.auditMode
   999  		// in case of restricted bots or recursive loads, do not audit the
  1000  		// hidden chain (as we might not have permission to see it).
  1001  		if (role.IsRestrictedBot() || arg.readSubteamID != nil) && auditMode == keybase1.AuditMode_STANDARD {
  1002  			auditMode = keybase1.AuditMode_STANDARD_NO_HIDDEN
  1003  		}
  1004  
  1005  		err = l.audit(ctx, readSubteamID, &ret.Chain, hiddenPackage.ChainData(), lastMerkleRoot, auditMode)
  1006  		if err != nil {
  1007  			return nil, err
  1008  		}
  1009  	} else {
  1010  		mctx.Debug("Skipping audit in the TeamLoader as we did not repoll merkle")
  1011  	}
  1012  
  1013  	// Cache the validated result if it was actually updated via the team/get endpoint. In many cases, we're not
  1014  	// actually mutating the teams. Also, if we wound up filling in stubbed links, let's also restore the cache.
  1015  	if teamUpdate != nil || filledInStubbedLinks {
  1016  		tracer.Stage("put")
  1017  		l.storage.Put(mctx, ret)
  1018  	}
  1019  
  1020  	// If we wound up repolling the merkle tree for this team, say that we did.
  1021  	if didRepoll {
  1022  		l.merkleStorage.Put(mctx, arg.teamID, arg.public, keybase1.ToTime(mctx.G().Clock().Now()))
  1023  	}
  1024  
  1025  	tracer.Stage("notify")
  1026  	if cachedName != nil && !cachedName.Eq(newName) {
  1027  		chain := TeamSigChainState{inner: ret.Chain, hidden: hiddenPackage.ChainData()}
  1028  		// Send a notification if we used to have the name cached and it has changed at all.
  1029  		changeSet := keybase1.TeamChangeSet{Renamed: true}
  1030  		go l.G().NotifyRouter.HandleTeamChangedByID(context.Background(), chain.GetID(), chain.GetLatestSeqno(),
  1031  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1032  		go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), cachedName.String(), chain.GetLatestSeqno(),
  1033  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1034  		go l.G().NotifyRouter.HandleTeamChangedByName(context.Background(), newName.String(), chain.GetLatestSeqno(),
  1035  			chain.IsImplicit(), changeSet, chain.GetLatestHiddenSeqno(), keybase1.Seqno(0), keybase1.TeamChangedSource_LOCAL_RENAME)
  1036  	}
  1037  
  1038  	// Check request constraints
  1039  	tracer.Stage("postcheck")
  1040  	err = l.load2CheckReturn(mctx, arg, teamShim())
  1041  	if err != nil {
  1042  		return nil, err
  1043  	}
  1044  
  1045  	load2res := load2ResT{
  1046  		team:      *ret,
  1047  		didRepoll: didRepoll,
  1048  	}
  1049  
  1050  	if hd := hiddenPackage.ChainData(); hd != nil {
  1051  		hd.NeedRotate = needHiddenRotate
  1052  		load2res.hidden = hd
  1053  	}
  1054  
  1055  	if needHiddenRotate {
  1056  		l.G().GetTeamBoxAuditor().MaybeScheduleDelayedBoxAuditTeam(mctx, arg.teamID)
  1057  	}
  1058  
  1059  	return &load2res, nil
  1060  }
  1061  
  1062  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) {
  1063  	return func() (encKID keybase1.KID, gen keybase1.PerTeamKeyGeneration,
  1064  		role keybase1.TeamRole, err error) {
  1065  		if team == nil {
  1066  			return encKID, gen, keybase1.TeamRole_NONE, nil
  1067  		}
  1068  		state := TeamSigChainState{inner: team.Chain}
  1069  
  1070  		ptk, err := state.GetLatestPerTeamKey(mctx)
  1071  		if err != nil {
  1072  			return encKID, gen, keybase1.TeamRole_NONE, err
  1073  		}
  1074  		role, err = state.GetUserRole(me)
  1075  		if err != nil {
  1076  			return encKID, gen, keybase1.TeamRole_NONE, err
  1077  		}
  1078  		return ptk.EncKID, ptk.Gen, role, nil
  1079  	}
  1080  }
  1081  
  1082  func (l *TeamLoader) hiddenPackage(mctx libkb.MetaContext, id keybase1.TeamID, team *keybase1.TeamData, me keybase1.UserVersion) (ret *hidden.LoaderPackage, err error) {
  1083  	getter := l.hiddenPackageGetter(mctx, id, team, me)
  1084  	return hidden.NewLoaderPackage(mctx, id, getter)
  1085  }
  1086  
  1087  func (l *TeamLoader) isAllowedKeyerOf(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, them keybase1.UserVersion) (ret bool, err error) {
  1088  	state := TeamSigChainState{inner: chain.Chain}
  1089  	mctx = mctx.WithLogTag("IAKO")
  1090  	defer mctx.Trace(fmt.Sprintf("TeamLoader#isAllowedKeyerOf(%s, %s)", state.GetID(), them), &err)()
  1091  
  1092  	role, err := state.GetUserRole(them)
  1093  	if err != nil {
  1094  		return false, err
  1095  	}
  1096  	switch role {
  1097  	case keybase1.TeamRole_WRITER, keybase1.TeamRole_ADMIN, keybase1.TeamRole_OWNER:
  1098  		mctx.Debug("user fits explicit role (%s)", role)
  1099  		return true, nil
  1100  	}
  1101  
  1102  	if state.GetParentID() == nil {
  1103  		mctx.Debug("user is not an allowed keyer of the team")
  1104  		return false, nil
  1105  	}
  1106  
  1107  	// now check implict adminship
  1108  	yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, them)
  1109  	if err != nil {
  1110  		return false, err
  1111  	}
  1112  
  1113  	if yes {
  1114  		mctx.Debug("user is an implicit admin of the team")
  1115  		return true, err
  1116  	}
  1117  
  1118  	mctx.Debug("user is not an allowed keyer of the team")
  1119  
  1120  	return false, nil
  1121  
  1122  }
  1123  
  1124  func (l *TeamLoader) checkNeedRotate(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, hiddenPackage *hidden.LoaderPackage) (ret bool, err error) {
  1125  	signer := hiddenPackage.LastReaderKeyRotator(mctx)
  1126  	if signer == nil {
  1127  		mctx.Debug("not checking need rotate, since last signer of hidden chain was nil")
  1128  		return false, nil
  1129  	}
  1130  	return l.checkNeedRotateWithSigner(mctx, chain, me, *signer)
  1131  }
  1132  
  1133  func (l *TeamLoader) checkNeedRotateWithSigner(mctx libkb.MetaContext, chain *keybase1.TeamData, me keybase1.UserVersion, signer keybase1.Signer) (ret bool, err error) {
  1134  
  1135  	defer mctx.Trace(fmt.Sprintf("TeamLoader::checkNeedRotateWithSigner(%+v)", signer), &err)()
  1136  
  1137  	uv := signer.UserVersion()
  1138  
  1139  	var isKeyer, amIKeyer bool
  1140  
  1141  	amIKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, me)
  1142  	if err != nil {
  1143  		return false, err
  1144  	}
  1145  	if !amIKeyer {
  1146  		mctx.Debug("I am not a keyer for this team, so I can't rotate it even if required")
  1147  		return false, nil
  1148  	}
  1149  
  1150  	isKeyer, err = l.isAllowedKeyerOf(mctx, chain, me, uv)
  1151  	if err != nil {
  1152  		return false, err
  1153  	}
  1154  
  1155  	if !isKeyer {
  1156  		mctx.Debug("need rotate since %+v isn't an allowed keyer of the team", uv)
  1157  		return true, nil
  1158  	}
  1159  
  1160  	var found bool
  1161  	var revokedAt *keybase1.KeybaseTime
  1162  
  1163  	found, revokedAt, _, err = mctx.G().GetUPAKLoader().CheckKIDForUID(mctx.Ctx(), uv.Uid, signer.K)
  1164  	if err != nil {
  1165  		return false, err
  1166  	}
  1167  
  1168  	if !found || revokedAt != nil {
  1169  		var s string
  1170  		if revokedAt != nil {
  1171  			tm := revokedAt.Unix.Time()
  1172  			s = fmt.Sprintf(" (revoked at %s [%s ago])", tm, mctx.G().Clock().Now().Sub(tm))
  1173  		}
  1174  		mctx.Debug("KID %s wasn't found for %+v%s", signer, s)
  1175  		return true, nil
  1176  	}
  1177  
  1178  	return false, nil
  1179  }
  1180  
  1181  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) {
  1182  
  1183  	var nilPrev libkb.LinkID
  1184  
  1185  	ctx := mctx.Ctx()
  1186  	if suppressLoggingStart <= i && i < suppressLoggingUpto {
  1187  		if i == suppressLoggingStart {
  1188  			mctx.Debug("TeamLoader suppressing logs until %v", suppressLoggingUpto)
  1189  		}
  1190  		ctx = WithSuppressLogging(ctx, true)
  1191  		mctx = mctx.WithContext(ctx)
  1192  	}
  1193  
  1194  	if !ShouldSuppressLogging(ctx) {
  1195  		mctx.Debug("TeamLoader processing link seqno:%v", link.Seqno())
  1196  	}
  1197  
  1198  	if link.Seqno() > lastSeqno {
  1199  		// This link came from a point in the chain after when we checked the merkle leaf.
  1200  		// Processing it would require re-checking merkle.
  1201  		// It would be tricky to ignore it because off-chain data is asserted to be in sync with the chain.
  1202  		// So, return an error that the caller will retry.
  1203  		mctx.Debug("TeamLoader found green link seqno:%v", link.Seqno())
  1204  		return nil, nilPrev, NewGreenLinkError(link.Seqno())
  1205  	}
  1206  
  1207  	if err := l.checkStubbed(ctx, arg, link); err != nil {
  1208  		return nil, nilPrev, err
  1209  	}
  1210  
  1211  	if !link.Prev().Eq(prev) {
  1212  		return nil, nilPrev, NewPrevError("team replay failed: prev chain broken at link %d (%v != %v)",
  1213  			i, link.Prev(), prev)
  1214  	}
  1215  
  1216  	if err := consumeRatchets(mctx, hiddenPackage, link); err != nil {
  1217  		return nil, nilPrev, err
  1218  	}
  1219  
  1220  	if err := checkPTKGenerationNotOnHiddenChain(mctx, hiddenPackage, link); err != nil {
  1221  		return nil, nilPrev, err
  1222  	}
  1223  
  1224  	var signer *SignerX
  1225  	var err error
  1226  	signer, err = l.verifyLink(ctx, arg.teamID, ret, arg.me, link, fullVerifyCutoff,
  1227  		readSubteamID, proofSet, lkc, *parentsCache)
  1228  	if err != nil {
  1229  		return nil, nilPrev, err
  1230  	}
  1231  
  1232  	if l.isParentChildOperation(ctx, link) {
  1233  		pco, err := l.toParentChildOperation(ctx, link)
  1234  		if err != nil {
  1235  			return nil, nilPrev, err
  1236  		}
  1237  		*parentChildOperations = append(*parentChildOperations, pco)
  1238  	}
  1239  
  1240  	ret, err = l.applyNewLink(ctx, ret, hiddenPackage.ChainData(), link, signer, arg.me)
  1241  	if err != nil {
  1242  		return nil, nilPrev, err
  1243  	}
  1244  
  1245  	return ret, link.LinkID(), nil
  1246  }
  1247  
  1248  // userPreload warms the upak cache with users who will probably need to be loaded to verify the chain.
  1249  // Uses teamEnv and may be disabled.
  1250  func (l *TeamLoader) userPreload(ctx context.Context, links []*ChainLinkUnpacked, fullVerifyCutoff keybase1.Seqno) (cancel func()) {
  1251  	ctx, cancel = context.WithCancel(ctx)
  1252  	if teamEnv.UserPreloadEnable {
  1253  		uidSet := make(map[keybase1.UID]struct{})
  1254  		for _, link := range links {
  1255  			// fullVerify definition copied from verifyLink
  1256  			fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) ||
  1257  				(link.Seqno() >= fullVerifyCutoff) ||
  1258  				(link.source.EldestSeqno == 0)
  1259  			if !link.isStubbed() && fullVerify {
  1260  				uidSet[link.inner.Body.Key.UID] = struct{}{}
  1261  			}
  1262  		}
  1263  		l.G().Log.CDebugf(ctx, "TeamLoader userPreload uids: %v", len(uidSet))
  1264  		if teamEnv.UserPreloadParallel {
  1265  			// Note this is full-parallel. Probably want pipelining if this is to be turned on by default.
  1266  			var wg sync.WaitGroup
  1267  			for uid := range uidSet {
  1268  				wg.Add(1)
  1269  				go func(uid keybase1.UID) {
  1270  					_, _, err := l.G().GetUPAKLoader().LoadV2(
  1271  						libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx))
  1272  					if err != nil {
  1273  						l.G().Log.CDebugf(ctx, "error preloading uid %v", uid)
  1274  					}
  1275  					wg.Done()
  1276  				}(uid)
  1277  			}
  1278  			if teamEnv.UserPreloadWait {
  1279  				wg.Wait()
  1280  			}
  1281  		} else {
  1282  			for uid := range uidSet {
  1283  				_, _, err := l.G().GetUPAKLoader().LoadV2(
  1284  					libkb.NewLoadUserArg(l.G()).WithUID(uid).WithPublicKeyOptional().WithNetContext(ctx))
  1285  				if err != nil {
  1286  					l.G().Log.CDebugf(ctx, "error preloading uid %v", uid)
  1287  				}
  1288  			}
  1289  		}
  1290  	}
  1291  	return cancel
  1292  }
  1293  
  1294  // Decide whether to repoll merkle based on load arg.
  1295  // Returns (discardCache, repoll)
  1296  // discardCache - the caller should throw out their cached copy and repoll.
  1297  // repoll - hit up merkle for the latest tail
  1298  // Considers:
  1299  //   - NeedAdmin
  1300  //   - NeedKeyGeneration
  1301  //   - NeedApplicationsAtGenerations
  1302  //   - WantMembers
  1303  //   - ForceRepoll
  1304  //   - Cache freshness / StaleOK
  1305  //   - NeedSeqnos
  1306  //   - JustUpdated
  1307  //   - If this user is in global "force repoll" mode, where it would be too spammy to
  1308  //     push out individual team changed notifications, so all team loads need a repoll.
  1309  func (l *TeamLoader) load2DecideRepoll(mctx libkb.MetaContext, arg load2ArgT, fromCache Teamer, cachedPolledAt *keybase1.Time) (discardCache bool, repoll bool) {
  1310  	var reason string
  1311  	defer func() {
  1312  		if discardCache || repoll || reason != "" {
  1313  			mctx.Debug("load2DecideRepoll -> (discardCache:%v, repoll:%v) %v", discardCache, repoll, reason)
  1314  		}
  1315  	}()
  1316  	// NeedAdmin is a special constraint where we start from scratch.
  1317  	// Because of admin-only invite links.
  1318  	if arg.needAdmin {
  1319  		if !l.satisfiesNeedAdmin(mctx, arg.me, fromCache) {
  1320  			// Start from scratch if we are newly admin
  1321  			reason = "!satisfiesNeedAdmin"
  1322  			return true, true
  1323  		}
  1324  	}
  1325  
  1326  	if arg.forceRepoll {
  1327  		reason = "forceRepoll"
  1328  		return false, true
  1329  	}
  1330  
  1331  	// Repoll if the server has previously hinted that the team has new links.
  1332  	if fromCache != nil && fromCache.MainChain() != nil && fromCache.MainChain().Chain.LastSeqno < fromCache.MainChain().LatestSeqnoHint {
  1333  		reason = "behind seqno hint"
  1334  		return false, true
  1335  	}
  1336  
  1337  	if fromCache != nil && fromCache.HiddenChain() != nil && fromCache.HiddenChain().IsStale() {
  1338  		reason = "behind hidden seqno hint"
  1339  		return false, true
  1340  	}
  1341  
  1342  	// Repoll to get a new key generation
  1343  	if arg.needKeyGeneration > 0 {
  1344  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, fromCache); err != nil {
  1345  			reason = fmt.Sprintf("satisfiesNeedKeyGeneration -> %v", err)
  1346  			return false, true
  1347  		}
  1348  	}
  1349  	// Repoll to get new applications at generations
  1350  	if len(arg.needApplicationsAtGenerations) > 0 {
  1351  		if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, fromCache); err != nil {
  1352  			reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerations -> %v", err)
  1353  			return false, true
  1354  		}
  1355  	}
  1356  	if arg.needKBFSKeyGeneration.Generation > 0 {
  1357  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, fromCache); err != nil {
  1358  			reason = fmt.Sprintf("satisfiesNeedsKBFSKeyGeneration -> %v", err)
  1359  			return false, true
  1360  		}
  1361  	}
  1362  
  1363  	if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 {
  1364  		if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx,
  1365  			arg.needApplicationsAtGenerationsWithKBFS, fromCache); err != nil {
  1366  			reason = fmt.Sprintf("satisfiesNeedApplicationsAtGenerationsWithKBFS -> %v", err)
  1367  			return false, true
  1368  		}
  1369  	}
  1370  
  1371  	// Repoll because it might help get the wanted members
  1372  	if len(arg.wantMembers) > 0 {
  1373  		if err := l.satisfiesWantMembers(mctx, arg.wantMembers, arg.wantMembersRole, fromCache); err != nil {
  1374  			reason = fmt.Sprintf("satisfiesWantMembers -> %v", err)
  1375  			return false, true
  1376  		}
  1377  	}
  1378  
  1379  	// Repoll if we need a seqno not in the cache.
  1380  	// Does not force a repoll if we just need to fill in previous links
  1381  	if len(arg.needSeqnos) > 0 {
  1382  		if fromCache == nil || fromCache.MainChain() == nil {
  1383  			reason = "need seqnos and no cache"
  1384  			return false, true
  1385  		}
  1386  		if fromCache.MainChain().Chain.LastSeqno < l.seqnosMax(arg.needSeqnos) {
  1387  			reason = "need seqnos"
  1388  			return false, true
  1389  		}
  1390  	}
  1391  
  1392  	if fromCache == nil || fromCache.MainChain() == nil {
  1393  		reason = "no cache"
  1394  		// We need a merkle leaf when starting from scratch.
  1395  		return false, true
  1396  	}
  1397  
  1398  	cachedAt := fromCache.MainChain().CachedAt
  1399  	if cachedPolledAt != nil && *cachedPolledAt > cachedAt {
  1400  		cachedAt = *cachedPolledAt
  1401  	}
  1402  
  1403  	cacheIsOld := !l.isFresh(mctx, cachedAt)
  1404  	if cacheIsOld && !arg.staleOK {
  1405  		// We need a merkle leaf
  1406  		reason = "cacheIsOld"
  1407  		return false, true
  1408  	}
  1409  
  1410  	// InForceRepoll needs to a acquire a lock, so avoid it by checking it last.
  1411  	if l.InForceRepollMode(mctx) {
  1412  		reason = "InForceRepollMode"
  1413  		return false, true
  1414  	}
  1415  
  1416  	return false, false
  1417  }
  1418  
  1419  // Check whether the load produced a snapshot that can be returned to the caller.
  1420  // This should not check anything that is critical to the validity of the snapshot
  1421  // because the snapshot is put into the cache before this check.
  1422  // Considers:
  1423  // - NeedAdmin
  1424  // - NeedKeyGeneration
  1425  // - NeedSeqnos
  1426  func (l *TeamLoader) load2CheckReturn(mctx libkb.MetaContext, arg load2ArgT, shim Teamer) error {
  1427  	if arg.needAdmin {
  1428  		if !l.satisfiesNeedAdmin(mctx, arg.me, shim) {
  1429  			mctx.Debug("user %v is not an admin of team %v at seqno:%v", arg.me, arg.teamID, shim.MainChain().Chain.LastSeqno)
  1430  			return fmt.Errorf("user %v is not an admin of the team", arg.me)
  1431  		}
  1432  	}
  1433  
  1434  	// Repoll to get a new key generation
  1435  	if arg.needKeyGeneration > 0 {
  1436  		if err := l.satisfiesNeedKeyGeneration(mctx, arg.needKeyGeneration, shim); err != nil {
  1437  			return err
  1438  		}
  1439  	}
  1440  	if len(arg.needApplicationsAtGenerations) > 0 {
  1441  		if err := l.satisfiesNeedApplicationsAtGenerations(mctx, arg.needApplicationsAtGenerations, shim); err != nil {
  1442  			return err
  1443  		}
  1444  	}
  1445  	if arg.needKBFSKeyGeneration.Generation > 0 {
  1446  		if err := l.satisfiesNeedsKBFSKeyGeneration(mctx, arg.needKBFSKeyGeneration, shim); err != nil {
  1447  			return err
  1448  		}
  1449  	}
  1450  	if len(arg.needApplicationsAtGenerationsWithKBFS) > 0 {
  1451  		if err := l.satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx, arg.needApplicationsAtGenerationsWithKBFS, shim); err != nil {
  1452  			return err
  1453  		}
  1454  	}
  1455  
  1456  	if len(arg.needSeqnos) > 0 {
  1457  		if err := l.checkNeededSeqnos(mctx.Ctx(), shim.MainChain(), arg.needSeqnos); err != nil {
  1458  			return err
  1459  		}
  1460  	}
  1461  
  1462  	return nil
  1463  }
  1464  
  1465  // Whether the user is an admin at the snapshot, and there are no stubbed links, and keys are up to date.
  1466  func (l *TeamLoader) satisfiesNeedAdmin(mctx libkb.MetaContext, me keybase1.UserVersion, team Teamer) bool {
  1467  	if team == nil || team.MainChain() == nil {
  1468  		return false
  1469  	}
  1470  	state := newTeamSigChainState(team)
  1471  	if state.HasAnyStubbedLinks() {
  1472  		return false
  1473  	}
  1474  	if !l.hasSyncedSecrets(mctx, team) {
  1475  		return false
  1476  	}
  1477  	role, err := state.GetUserRole(me)
  1478  	if err != nil {
  1479  		mctx.Debug("TeamLoader error getting my role: %v", err)
  1480  		return false
  1481  	}
  1482  	if !role.IsAdminOrAbove() {
  1483  		if !state.IsSubteam() {
  1484  			return false
  1485  		}
  1486  		yes, err := l.isImplicitAdminOf(mctx.Ctx(), state.GetID(), state.GetParentID(), me, me)
  1487  		if err != nil {
  1488  			mctx.Debug("TeamLoader error getting checking implicit admin: %s", err)
  1489  			return false
  1490  		}
  1491  		if !yes {
  1492  			return false
  1493  		}
  1494  	}
  1495  	return true
  1496  }
  1497  
  1498  // Check whether a user is an implicit admin of a team.
  1499  func (l *TeamLoader) isImplicitAdminOf(ctx context.Context, teamID keybase1.TeamID, ancestorID *keybase1.TeamID,
  1500  	me keybase1.UserVersion, uv keybase1.UserVersion) (bool, error) {
  1501  
  1502  	// IDs of ancestors that were not freshly polled.
  1503  	// Check them again with forceRepoll if the affirmative is not found cached.
  1504  	checkAgain := make(map[keybase1.TeamID]bool)
  1505  
  1506  	check1 := func(chain *TeamSigChainState) bool {
  1507  		role, err := chain.GetUserRole(uv)
  1508  		if err != nil {
  1509  			return false
  1510  		}
  1511  		return role.IsAdminOrAbove()
  1512  	}
  1513  
  1514  	i := 0
  1515  	for {
  1516  		i++
  1517  		if i >= 100 {
  1518  			// Break in case there's a bug in this loop.
  1519  			return false, fmt.Errorf("stuck in a loop while checking for implicit admin: %v", ancestorID)
  1520  		}
  1521  
  1522  		// Use load2 so that we can use subteam-reader and get secretless teams.
  1523  		ancestor, err := l.load2(ctx, load2ArgT{
  1524  			teamID:        *ancestorID,
  1525  			reason:        "isImplicitAdminOf-1",
  1526  			me:            me,
  1527  			readSubteamID: &teamID,
  1528  		})
  1529  		if err != nil {
  1530  			return false, err
  1531  		}
  1532  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1533  		// Do not let it out of sight.
  1534  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1535  
  1536  		if !ancestor.didRepoll {
  1537  			checkAgain[ancestorChain.GetID()] = true
  1538  		}
  1539  
  1540  		if check1(&ancestorChain) {
  1541  			return true, nil
  1542  		}
  1543  
  1544  		if !ancestorChain.IsSubteam() {
  1545  			break
  1546  		}
  1547  		// Get the next level up.
  1548  		ancestorID = ancestorChain.GetParentID()
  1549  	}
  1550  
  1551  	// The answer was not found to be yes in the cache.
  1552  	// Try again with the teams that were not polled as they might have unseen updates.
  1553  	for ancestorID := range checkAgain {
  1554  		ancestor, err := l.load2(ctx, load2ArgT{
  1555  			teamID:        ancestorID,
  1556  			reason:        "isImplicitAdminOf-again",
  1557  			me:            me,
  1558  			forceRepoll:   true, // Get the latest info.
  1559  			readSubteamID: &teamID,
  1560  		})
  1561  		if err != nil {
  1562  			return false, err
  1563  		}
  1564  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1565  		// Do not let it out of sight.
  1566  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1567  		if check1(&ancestorChain) {
  1568  			return true, nil
  1569  		}
  1570  	}
  1571  
  1572  	return false, nil
  1573  }
  1574  
  1575  func (l *TeamLoader) satisfiesNeedsKBFSKeyGeneration(mctx libkb.MetaContext,
  1576  	kbfs keybase1.TeamKBFSKeyRefresher, state Teamer) error {
  1577  	if kbfs.Generation == 0 {
  1578  		return nil
  1579  	}
  1580  	if state == nil {
  1581  		return fmt.Errorf("nil team does not contain KBFS key generation: %#v", kbfs)
  1582  	}
  1583  
  1584  	gen, err := newTeamSigChainState(state).GetLatestKBFSGeneration(kbfs.AppType)
  1585  	if err != nil {
  1586  		return err
  1587  	}
  1588  	if kbfs.Generation > gen {
  1589  		return NewKBFSKeyGenerationError(kbfs.Generation, gen)
  1590  	}
  1591  	return nil
  1592  }
  1593  
  1594  // Whether the snapshot has loaded at least up to the key generation and has the secret.
  1595  func (l *TeamLoader) satisfiesNeedKeyGeneration(mctx libkb.MetaContext, needKeyGeneration keybase1.PerTeamKeyGeneration, state Teamer) error {
  1596  	if needKeyGeneration == 0 {
  1597  		return nil
  1598  	}
  1599  	if state == nil {
  1600  		return fmt.Errorf("nil team does not contain key generation: %v", needKeyGeneration)
  1601  	}
  1602  	key, err := newTeamSigChainState(state).GetLatestPerTeamKey(mctx)
  1603  	if err != nil {
  1604  		return err
  1605  	}
  1606  	if needKeyGeneration > key.Gen {
  1607  		return fmt.Errorf("team key generation too low: %v < %v", key.Gen, needKeyGeneration)
  1608  	}
  1609  	_, ok := state.MainChain().PerTeamKeySeedsUnverified[needKeyGeneration]
  1610  	if !ok {
  1611  		return fmt.Errorf("team key secret missing for generation: %v", needKeyGeneration)
  1612  	}
  1613  	return nil
  1614  }
  1615  
  1616  // Whether the snapshot has loaded the reader key masks and key generations we
  1617  // need.
  1618  func (l *TeamLoader) satisfiesNeedApplicationsAtGenerations(mctx libkb.MetaContext,
  1619  	needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication, team Teamer) error {
  1620  	if len(needApplicationsAtGenerations) == 0 {
  1621  		return nil
  1622  	}
  1623  	if team == nil || team.MainChain() == nil {
  1624  		return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations)
  1625  	}
  1626  	for ptkGen, apps := range needApplicationsAtGenerations {
  1627  		for _, app := range apps {
  1628  			if _, err := ApplicationKeyAtGeneration(mctx, team, app, ptkGen); err != nil {
  1629  				return err
  1630  			}
  1631  		}
  1632  	}
  1633  	return nil
  1634  }
  1635  
  1636  func (l *TeamLoader) satisfiesNeedApplicationsAtGenerationsWithKBFS(mctx libkb.MetaContext,
  1637  	needApplicationsAtGenerations map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication,
  1638  	state Teamer) error {
  1639  	if len(needApplicationsAtGenerations) == 0 {
  1640  		return nil
  1641  	}
  1642  	if state == nil || state.MainChain() == nil {
  1643  		return fmt.Errorf("nil team does not contain applications: %v", needApplicationsAtGenerations)
  1644  	}
  1645  	for ptkGen, apps := range needApplicationsAtGenerations {
  1646  		for _, app := range apps {
  1647  			if _, err := ApplicationKeyAtGenerationWithKBFS(mctx, state, app, ptkGen); err != nil {
  1648  				return err
  1649  			}
  1650  		}
  1651  	}
  1652  	return nil
  1653  }
  1654  
  1655  // Whether the snapshot has each of `wantMembers` as a member.
  1656  func (l *TeamLoader) satisfiesWantMembers(mctx libkb.MetaContext,
  1657  	wantMembers []keybase1.UserVersion, wantMembersRole keybase1.TeamRole, state Teamer) error {
  1658  
  1659  	if wantMembersRole == keybase1.TeamRole_NONE {
  1660  		// Default to writer.
  1661  		wantMembersRole = keybase1.TeamRole_WRITER
  1662  	}
  1663  	if len(wantMembers) == 0 {
  1664  		return nil
  1665  	}
  1666  	if state == nil {
  1667  		return fmt.Errorf("nil team does not have wanted members")
  1668  	}
  1669  	for _, uv := range wantMembers {
  1670  		role, err := newTeamSigChainState(state).GetUserRole(uv)
  1671  		if err != nil {
  1672  			return fmt.Errorf("could not get wanted user role: %v", err)
  1673  		}
  1674  		if !role.IsOrAbove(wantMembersRole) {
  1675  			return fmt.Errorf("wanted user %v is a %v which is not at least %v", uv, role, wantMembersRole)
  1676  		}
  1677  	}
  1678  	return nil
  1679  }
  1680  
  1681  func (l *TeamLoader) mungeWantMembers(ctx context.Context, wantMembers []keybase1.UserVersion) (res []keybase1.UserVersion, err error) {
  1682  	for _, uv1 := range wantMembers {
  1683  		uv2 := uv1
  1684  		if uv2.EldestSeqno == 0 {
  1685  			// Lookup the latest eldest seqno for that uid.
  1686  			// This value may come from a cache.
  1687  			uv2.EldestSeqno, err = l.world.lookupEldestSeqno(ctx, uv2.Uid)
  1688  			if err != nil {
  1689  				return res, err
  1690  			}
  1691  			l.G().Log.CDebugf(ctx, "TeamLoader resolved wantMember %v -> %v", uv2.Uid, uv2.EldestSeqno)
  1692  		}
  1693  		res = append(res, uv2)
  1694  	}
  1695  	return res, err
  1696  }
  1697  
  1698  // Whether y is in xs.
  1699  func (l *TeamLoader) seqnosContains(xs []keybase1.Seqno, y keybase1.Seqno) bool {
  1700  	for _, x := range xs {
  1701  		if x.Eq(y) {
  1702  			return true
  1703  		}
  1704  	}
  1705  	return false
  1706  }
  1707  
  1708  // Return the max in a list of positive seqnos. Returns 0 if the list is empty
  1709  func (l *TeamLoader) seqnosMax(seqnos []keybase1.Seqno) (ret keybase1.Seqno) {
  1710  	for _, x := range seqnos {
  1711  		if x > ret {
  1712  			ret = x
  1713  		}
  1714  	}
  1715  	return ret
  1716  }
  1717  
  1718  // Whether a TeamData from the cache is fresh.
  1719  func (l *TeamLoader) isFresh(mctx libkb.MetaContext, cachedAt keybase1.Time) bool {
  1720  	if cachedAt.IsZero() {
  1721  		// This should never happen.
  1722  		mctx.Warning("TeamLoader encountered zero cached time")
  1723  		return false
  1724  	}
  1725  	diff := mctx.G().Clock().Now().Sub(cachedAt.Time())
  1726  	fresh := (diff <= freshnessLimit)
  1727  	if !fresh {
  1728  		mctx.Debug("TeamLoader cached snapshot is old: %v", diff)
  1729  	}
  1730  	return fresh
  1731  }
  1732  
  1733  // Whether the teams secrets are synced to the same point as its sigchain
  1734  // Does not check RKMs.
  1735  func (l *TeamLoader) hasSyncedSecrets(mctx libkb.MetaContext, team Teamer) bool {
  1736  	state := team.MainChain()
  1737  	n := len(team.MainChain().Chain.PerTeamKeys)
  1738  	offChainGen := len(state.PerTeamKeySeedsUnverified)
  1739  	mctx.Debug("TeamLoader#hasSyncedSecrets: found %d PTKs on the main chain (versus %d seeds)", n, offChainGen)
  1740  	if team.HiddenChain() != nil {
  1741  		m := len(team.HiddenChain().ReaderPerTeamKeys)
  1742  		mctx.Debug("TeamLoader#hasSyncedSecrets: found another %d PTKs on the hidden chain", m)
  1743  		n += m
  1744  	}
  1745  	return (n == offChainGen)
  1746  }
  1747  
  1748  func (l *TeamLoader) logIfUnsyncedSecrets(ctx context.Context, state *keybase1.TeamData) {
  1749  	onChainGen := keybase1.PerTeamKeyGeneration(len(state.Chain.PerTeamKeys))
  1750  	offChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified))
  1751  	if onChainGen != offChainGen {
  1752  		l.G().Log.CDebugf(ctx, "TeamLoader unsynced secrets local:%v != chain:%v ", offChainGen, onChainGen)
  1753  	}
  1754  }
  1755  
  1756  func (l *TeamLoader) lows(mctx libkb.MetaContext, state *keybase1.TeamData, hp *hidden.LoaderPackage, arg load2ArgT) getLinksLows {
  1757  	var lows getLinksLows
  1758  	if state != nil {
  1759  		chain := TeamSigChainState{inner: state.Chain}
  1760  		lows.Seqno = chain.GetLatestSeqno()
  1761  		if !arg.foundRKMHole {
  1762  			lows.PerTeamKey = keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified))
  1763  		}
  1764  	}
  1765  	if hp != nil {
  1766  		lows.HiddenChainSeqno = hp.LastFullSeqno()
  1767  	}
  1768  	return lows
  1769  }
  1770  
  1771  func (l *TeamLoader) OnLogout(mctx libkb.MetaContext) error {
  1772  	l.storage.ClearMem()
  1773  	return nil
  1774  }
  1775  
  1776  func (l *TeamLoader) OnDbNuke(mctx libkb.MetaContext) error {
  1777  	l.storage.ClearMem()
  1778  	return nil
  1779  }
  1780  
  1781  // Clear the in-memory cache.
  1782  func (l *TeamLoader) ClearMem() {
  1783  	l.storage.ClearMem()
  1784  }
  1785  
  1786  func (l *TeamLoader) VerifyTeamName(ctx context.Context, id keybase1.TeamID, name keybase1.TeamName) error {
  1787  	if name.IsRootTeam() {
  1788  		if !name.ToTeamID(id.IsPublic()).Eq(id) {
  1789  			return NewResolveError(name, id)
  1790  		}
  1791  		return nil
  1792  	}
  1793  	teamData, _, err := l.Load(ctx, keybase1.LoadTeamArg{
  1794  		ID:     id,
  1795  		Public: id.IsPublic(),
  1796  	})
  1797  	if err != nil {
  1798  		return err
  1799  	}
  1800  	gotName := teamData.Name
  1801  	if !gotName.Eq(name) {
  1802  		return NewResolveError(name, id)
  1803  	}
  1804  	return nil
  1805  }
  1806  
  1807  // List all the admins of ancestor teams.
  1808  // Includes admins of the specified team only if they are also admins of ancestor teams.
  1809  // The specified team must be a subteam, or an error is returned.
  1810  // Always sends a flurry of RPCs to get the most up to date info.
  1811  func (l *TeamLoader) ImplicitAdmins(ctx context.Context, teamID keybase1.TeamID) (impAdmins []keybase1.UserVersion, err error) {
  1812  	impAdminsMap := make(map[string]keybase1.UserVersion) // map to remove dups
  1813  	err = l.MapTeamAncestors(ctx, func(t keybase1.TeamSigChainState, _ keybase1.TeamName) error {
  1814  		ancestorChain := TeamSigChainState{inner: t}
  1815  		// Gather the admins.
  1816  		adminRoles := []keybase1.TeamRole{keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN}
  1817  		for _, role := range adminRoles {
  1818  			uvs, err := ancestorChain.GetUsersWithRole(role)
  1819  			if err != nil {
  1820  				return err
  1821  			}
  1822  			for _, uv := range uvs {
  1823  				impAdminsMap[uv.String()] = uv
  1824  			}
  1825  		}
  1826  		return nil
  1827  	}, teamID, "implicitAdminsAncestor", func(keybase1.TeamSigChainState) bool { return true })
  1828  	if err != nil {
  1829  		return nil, err
  1830  	}
  1831  	for _, uv := range impAdminsMap {
  1832  		impAdmins = append(impAdmins, uv)
  1833  	}
  1834  	return impAdmins, nil
  1835  }
  1836  
  1837  // MapTeamAncestors does NOT map over the team itself.
  1838  func (l *TeamLoader) MapTeamAncestors(
  1839  	ctx context.Context,
  1840  	f func(keybase1.TeamSigChainState, keybase1.TeamName) error,
  1841  	teamID keybase1.TeamID,
  1842  	reason string,
  1843  	forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool,
  1844  ) (err error) {
  1845  	initialTeamIdx := 0
  1846  
  1847  	me, err := l.world.getMe(ctx)
  1848  	if err != nil {
  1849  		return NewMapAncestorsError(err, initialTeamIdx)
  1850  	}
  1851  
  1852  	// Load the argument team
  1853  	team, _, err := l.load1(ctx, me, keybase1.LoadTeamArg{
  1854  		ID:      teamID,
  1855  		Public:  teamID.IsPublic(),
  1856  		StaleOK: true, // We only use immutable fields.
  1857  	})
  1858  	if err != nil {
  1859  		return NewMapAncestorsError(err, initialTeamIdx)
  1860  	}
  1861  	teamChain := TeamSigChainState{inner: team.Chain}
  1862  	if !teamChain.IsSubteam() {
  1863  		return NewMapAncestorsError(
  1864  			fmt.Errorf("cannot map over parents of a root team: %v", teamID),
  1865  			initialTeamIdx,
  1866  		)
  1867  	}
  1868  	return l.mapTeamAncestorsHelper(ctx, f, teamID, teamChain.GetParentID(), reason, forceFullReloadOnceToAssert)
  1869  }
  1870  
  1871  func (l *TeamLoader) mapTeamAncestorsHelper(
  1872  	ctx context.Context,
  1873  	f func(keybase1.TeamSigChainState, keybase1.TeamName) error,
  1874  	teamID keybase1.TeamID,
  1875  	ancestorID *keybase1.TeamID,
  1876  	reason string,
  1877  	forceFullReloadOnceToAssert func(t keybase1.TeamSigChainState) bool,
  1878  ) (err error) {
  1879  	i := 0
  1880  
  1881  	defer func() {
  1882  		if err != nil {
  1883  			err = NewMapAncestorsError(err, i)
  1884  		}
  1885  	}()
  1886  
  1887  	me, err := l.world.getMe(ctx)
  1888  	if err != nil {
  1889  		return err
  1890  	}
  1891  
  1892  	for {
  1893  		i++
  1894  		if i >= 100 {
  1895  			// Break in case there's a bug in this loop.
  1896  			return fmt.Errorf("stuck in a loop while mapping over team parents: %v", ancestorID)
  1897  		}
  1898  
  1899  		load2Arg := load2ArgT{
  1900  			teamID:        *ancestorID,
  1901  			reason:        reason,
  1902  			me:            me,
  1903  			forceRepoll:   true, // Get the latest info.
  1904  			readSubteamID: &teamID,
  1905  		}
  1906  
  1907  		var ancestor *load2ResT
  1908  		for {
  1909  			var err error
  1910  			// Use load2 so that we can use subteam-reader and get secretless teams.
  1911  			ancestor, err = l.load2(ctx, load2Arg)
  1912  			if err != nil {
  1913  				return err
  1914  			}
  1915  
  1916  			if forceFullReloadOnceToAssert == nil ||
  1917  				forceFullReloadOnceToAssert(ancestor.team.Chain) {
  1918  				break
  1919  			}
  1920  			if load2Arg.forceFullReload {
  1921  				return fmt.Errorf("failed to assert predicate in ancestor %v after full force reload", ancestor.team.ID())
  1922  			}
  1923  			load2Arg.forceFullReload = true
  1924  		}
  1925  
  1926  		// Be wary, `ancestor` could be, and is likely, a secretless team.
  1927  		// Do not let it out of sight.
  1928  		ancestorChain := TeamSigChainState{inner: ancestor.team.Chain}
  1929  
  1930  		err = f(ancestor.team.Chain, ancestor.team.Name)
  1931  		if err != nil {
  1932  			return err
  1933  		}
  1934  
  1935  		if !ancestorChain.IsSubteam() {
  1936  			break
  1937  		}
  1938  		// Get the next level up.
  1939  		ancestorID = ancestorChain.GetParentID()
  1940  	}
  1941  
  1942  	return nil
  1943  }
  1944  
  1945  func (l *TeamLoader) NotifyTeamRename(ctx context.Context, id keybase1.TeamID, newName string) error {
  1946  	// ignore newName from the server
  1947  
  1948  	// Load up the ancestor chain with ForceRepoll.
  1949  	// Then load down the ancestor chain without it (expect cache hits).
  1950  	// Not the most elegant way, but it will get the job done.
  1951  	// Each load on the way down will recalculate that team's name.
  1952  
  1953  	var ancestorIDs []keybase1.TeamID
  1954  
  1955  	me, err := l.world.getMe(ctx)
  1956  	if err != nil {
  1957  		return err
  1958  	}
  1959  
  1960  	loopID := &id
  1961  	for loopID != nil {
  1962  		load2Res, err := l.load2(ctx, load2ArgT{
  1963  			teamID:        *loopID,
  1964  			reason:        "NotifyTeamRename-force",
  1965  			forceRepoll:   true,
  1966  			readSubteamID: &id,
  1967  			me:            me,
  1968  		})
  1969  		if err != nil {
  1970  			return err
  1971  		}
  1972  		ancestorIDs = append(ancestorIDs, *loopID)
  1973  		chain := TeamSigChainState{inner: load2Res.team.Chain}
  1974  		if chain.IsSubteam() {
  1975  			loopID = chain.GetParentID()
  1976  		} else {
  1977  			loopID = nil
  1978  		}
  1979  	}
  1980  
  1981  	// reverse ancestorIDs so the root team appears first
  1982  	sort.SliceStable(ancestorIDs, func(i, j int) bool { return i > j })
  1983  
  1984  	for _, loopID := range ancestorIDs {
  1985  		_, err := l.load2(ctx, load2ArgT{
  1986  			teamID:        loopID,
  1987  			reason:        "NotifyTeamRename-quick",
  1988  			readSubteamID: &id,
  1989  			me:            me,
  1990  		})
  1991  		if err != nil {
  1992  			return err
  1993  		}
  1994  	}
  1995  
  1996  	return nil
  1997  }
  1998  
  1999  func (l *TeamLoader) getHeadMerkleSeqno(mctx libkb.MetaContext, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState) (ret keybase1.Seqno, err error) {
  2000  	defer mctx.Trace("TeamLoader#getHeadMerkleSeqno", &err)()
  2001  
  2002  	if state.HeadMerkle != nil {
  2003  		return state.HeadMerkle.Seqno, nil
  2004  	}
  2005  	headSeqno := keybase1.Seqno(1)
  2006  	expectedLinkRaw, ok := state.LinkIDs[headSeqno]
  2007  	if !ok {
  2008  		return ret, fmt.Errorf("couldn't find head link in team state during audit")
  2009  	}
  2010  	expectedLink, err := libkb.ImportLinkID(expectedLinkRaw)
  2011  	if err != nil {
  2012  		return ret, err
  2013  	}
  2014  	teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Id, []keybase1.Seqno{headSeqno}, &readSubteamID)
  2015  	if err != nil {
  2016  		return ret, err
  2017  	}
  2018  	newLinks, err := teamUpdate.unpackLinks(mctx)
  2019  	if err != nil {
  2020  		return ret, err
  2021  	}
  2022  	if len(newLinks) != 1 {
  2023  		return ret, fmt.Errorf("expected only one chainlink back; got %d", len(newLinks))
  2024  	}
  2025  	headLink := newLinks[0]
  2026  	err = headLink.AssertInnerOuterMatch()
  2027  	if err != nil {
  2028  		return ret, err
  2029  	}
  2030  	if headLink.Seqno() != headSeqno {
  2031  		return ret, NewInvalidLink(headLink, "wrong head seqno; wanted 1 but got something else")
  2032  	}
  2033  	if !headLink.LinkID().Eq(expectedLink) {
  2034  		return ret, NewInvalidLink(headLink, "wrong head link hash: %s != %s", headLink.LinkID(), expectedLink)
  2035  	}
  2036  	if headLink.isStubbed() {
  2037  		return ret, NewInvalidLink(headLink, "got a stubbed head link, but wasn't expecting that")
  2038  	}
  2039  	headMerkle := headLink.inner.Body.MerkleRoot.ToMerkleRootV2()
  2040  	state.HeadMerkle = &headMerkle
  2041  	return headMerkle.Seqno, nil
  2042  }
  2043  
  2044  func (l *TeamLoader) audit(ctx context.Context, readSubteamID keybase1.TeamID, state *keybase1.TeamSigChainState, hiddenChain *keybase1.HiddenTeamChain, lastMerkleRoot *libkb.MerkleRoot, auditMode keybase1.AuditMode) (err error) {
  2045  	mctx := libkb.NewMetaContext(ctx, l.G())
  2046  
  2047  	if l.G().Env.Test.TeamSkipAudit {
  2048  		mctx.Debug("skipping audit in test due to flag")
  2049  		return nil
  2050  	}
  2051  
  2052  	headMerklSeqno, err := l.getHeadMerkleSeqno(mctx, readSubteamID, state)
  2053  	if err != nil {
  2054  		return err
  2055  	}
  2056  
  2057  	err = mctx.G().GetTeamAuditor().AuditTeam(mctx, state.Id, state.Public, headMerklSeqno, state.LinkIDs, hiddenChain.GetOuter(), state.LastSeqno, hiddenChain.GetLastCommittedSeqno(), lastMerkleRoot, auditMode)
  2058  	return err
  2059  }
  2060  
  2061  func (l *TeamLoader) ForceRepollUntil(ctx context.Context, dtime gregor.TimeOrOffset) error {
  2062  	l.G().Log.CDebugf(ctx, "TeamLoader#ForceRepollUntil(%+v)", dtime)
  2063  	l.forceRepollMutex.Lock()
  2064  	defer l.forceRepollMutex.Unlock()
  2065  	l.forceRepollUntil = dtime
  2066  	return nil
  2067  }
  2068  
  2069  func (l *TeamLoader) InForceRepollMode(mctx libkb.MetaContext) bool {
  2070  	l.forceRepollMutex.Lock()
  2071  	defer l.forceRepollMutex.Unlock()
  2072  	if l.forceRepollUntil == nil {
  2073  		return false
  2074  	}
  2075  	if !l.forceRepollUntil.Before(mctx.G().Clock().Now()) {
  2076  		mctx.Debug("TeamLoader#InForceRepollMode: returning true")
  2077  		return true
  2078  	}
  2079  	l.forceRepollUntil = nil
  2080  	return false
  2081  }