github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/identify2_with_uid.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"sync"
    10  	"time"
    11  
    12  	gregor "github.com/keybase/client/go/gregor"
    13  	libkb "github.com/keybase/client/go/libkb"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	jsonw "github.com/keybase/go-jsonw"
    16  )
    17  
    18  type Identify2TestStats struct {
    19  	untrackedFastPaths int
    20  }
    21  
    22  type Identify2WithUIDTestArgs struct {
    23  	noMe                   bool                  // don't load ME
    24  	tcl                    *libkb.TrackChainLink // the track chainlink to use
    25  	selfLoad               bool                  // on if this is a self load
    26  	noCache                bool                  // on if we shouldn't use the cache
    27  	cache                  libkb.Identify2Cacher
    28  	clock                  func() time.Time
    29  	forceRemoteCheck       bool // on if we should force remote checks (like busting caches)
    30  	allowUntrackedFastPath bool // on if we can allow untracked fast path in test
    31  	stats                  Identify2TestStats
    32  }
    33  
    34  type identify2TrackType int
    35  
    36  const (
    37  	identify2NoTrack identify2TrackType = iota
    38  	identify2TrackOK
    39  	identify2TrackBroke
    40  )
    41  
    42  type identifyUser struct {
    43  	arg       libkb.LoadUserArg
    44  	full      *libkb.User
    45  	thin      *keybase1.UserPlusKeysV2AllIncarnations
    46  	isDeleted bool
    47  }
    48  
    49  func (i *identifyUser) GetUID() keybase1.UID {
    50  	if i.thin != nil {
    51  		return i.thin.GetUID()
    52  	}
    53  	if i.full != nil {
    54  		return i.full.GetUID()
    55  	}
    56  	panic("null user")
    57  }
    58  
    59  func (i *identifyUser) GetName() string {
    60  	if i.thin != nil {
    61  		return i.thin.GetName()
    62  	}
    63  	if i.full != nil {
    64  		return i.full.GetName()
    65  	}
    66  	panic("null user")
    67  }
    68  
    69  func (i *identifyUser) GetStatus() keybase1.StatusCode {
    70  	if i.thin != nil {
    71  		return i.thin.GetStatus()
    72  	}
    73  	if i.full != nil {
    74  		return i.full.GetStatus()
    75  	}
    76  	panic("null user")
    77  }
    78  
    79  func (i *identifyUser) GetNormalizedName() libkb.NormalizedUsername {
    80  	return libkb.NewNormalizedUsername(i.GetName())
    81  }
    82  
    83  func (i *identifyUser) BaseProofSet() *libkb.ProofSet {
    84  	if i.thin != nil {
    85  		return libkb.BaseProofSet(i.thin)
    86  	}
    87  	if i.full != nil {
    88  		return i.full.BaseProofSet()
    89  	}
    90  	panic("null user")
    91  }
    92  
    93  func (i *identifyUser) User(cache libkb.Identify2Cacher) (*libkb.User, error) {
    94  	if i.full != nil {
    95  		return i.full, nil
    96  	}
    97  	if cache != nil {
    98  		cache.DidFullUserLoad(i.GetUID())
    99  	}
   100  	var err error
   101  	i.full, err = libkb.LoadUser(i.arg)
   102  	return i.full, err
   103  }
   104  
   105  func (i *identifyUser) Export() *keybase1.User {
   106  	if i.thin != nil {
   107  		tmp := i.thin.ExportToSimpleUser()
   108  		return &tmp
   109  	}
   110  	if i.full != nil {
   111  		return i.full.Export()
   112  	}
   113  	panic("null user")
   114  }
   115  
   116  func (i *identifyUser) ExportToUserPlusKeysV2AllIncarnations() (*keybase1.UserPlusKeysV2AllIncarnations, error) {
   117  	if i.thin != nil {
   118  		return i.thin, nil
   119  	}
   120  	if i.full != nil {
   121  		return i.full.ExportToUPKV2AllIncarnations()
   122  	}
   123  	return nil, errors.New("null user in identify2: ExportToUserPlusKeysV2AllIncarnations")
   124  }
   125  
   126  func (i *identifyUser) IsCachedIdentifyFresh(upk *keybase1.UserPlusKeysV2AllIncarnations) bool {
   127  	if i.thin != nil {
   128  		ret := i.thin.Uvv.Equal(upk.Uvv)
   129  		return ret
   130  	}
   131  	if i.full != nil {
   132  		return i.full.IsCachedIdentifyFresh(upk)
   133  	}
   134  	panic("null user")
   135  }
   136  
   137  func (i *identifyUser) Equal(i2 *identifyUser) bool {
   138  	return i.GetUID().Equal(i2.GetUID())
   139  }
   140  
   141  func (i *identifyUser) load(g *libkb.GlobalContext) (err error) {
   142  	i.thin, i.full, err = g.GetUPAKLoader().LoadV2(i.arg)
   143  	return err
   144  }
   145  
   146  func (i *identifyUser) forceFullLoad(m libkb.MetaContext) (err error) {
   147  	arg := i.arg.WithForceReload()
   148  	i.thin, i.full, err = m.G().GetUPAKLoader().LoadV2(arg)
   149  	return err
   150  }
   151  
   152  func (i *identifyUser) isNil() bool {
   153  	return i.thin == nil && i.full == nil
   154  }
   155  
   156  func (i *identifyUser) Full() *libkb.User {
   157  	return i.full
   158  }
   159  
   160  func loadIdentifyUser(m libkb.MetaContext, arg libkb.LoadUserArg, cache libkb.Identify2Cacher) (*identifyUser, error) {
   161  	ret := &identifyUser{arg: arg}
   162  	err := ret.load(m.G())
   163  	if ret.isNil() {
   164  		ret = nil
   165  	} else if ret.full != nil && cache != nil {
   166  		cache.DidFullUserLoad(ret.GetUID())
   167  	}
   168  	return ret, err
   169  }
   170  
   171  func (i *identifyUser) trackChainLinkFor(m libkb.MetaContext, name libkb.NormalizedUsername, uid keybase1.UID) (ret *libkb.TrackChainLink, err error) {
   172  	defer m.Trace(fmt.Sprintf("identifyUser#trackChainLinkFor(%s)", name), &err)()
   173  
   174  	if i.full != nil {
   175  		m.Debug("| Using full user object")
   176  		return i.full.TrackChainLinkFor(m, name, uid)
   177  	}
   178  
   179  	if i.thin != nil {
   180  
   181  		m.Debug("| Using thin user object")
   182  
   183  		// In the common case, we look at the thin UPAK and get the chain link
   184  		// ID of the track chain link for tracking the given user. We'll then
   185  		// go ahead and load that chain link from local level DB, and it's almost
   186  		// always going to be there, since it was written as a side effect of
   187  		// fetching the full user. There's a corner case, see just below...
   188  		ret, err = libkb.TrackChainLinkFromUPK2AI(m, i.thin, name, uid)
   189  		if _, inconsistent := err.(libkb.InconsistentCacheStateError); !inconsistent {
   190  			m.Debug("| returning in common case -> (found=%v, err=%v)", (ret != nil), err)
   191  			return ret, err
   192  		}
   193  
   194  		m.Debug("| fell through to forceFullLoad corner case")
   195  
   196  		//
   197  		// NOTE(max) 2016-12-31
   198  		//
   199  		// There's a corner case here -- the track chain link does exist, but
   200  		// it wasn't found on disk. This is probably because the db cache was nuked.
   201  		// Thus, in this case we force a full user reload, and we're sure to get
   202  		// the tracking information then.
   203  		//
   204  		// See Jira ticket CORE-4310
   205  		//
   206  		err = i.forceFullLoad(m)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  		return i.full.TrackChainLinkFor(m, name, uid)
   211  	}
   212  
   213  	// No user loaded, so no track chain link.
   214  	m.Debug("| fell through the empty default case")
   215  	return nil, nil
   216  }
   217  
   218  //
   219  // TODOs:
   220  //   - think harder about what we're caching in failure cases; right now we're only
   221  //     caching full successes.
   222  //   - Better error typing for various failures.
   223  //   - Work back in the identify card
   224  //
   225  
   226  // Identify2WithUID is the Identify engine used in KBFS, chat, and as a
   227  // subroutine of command-line crypto.
   228  type Identify2WithUID struct {
   229  	libkb.Contextified
   230  
   231  	arg           *keybase1.Identify2Arg
   232  	testArgs      *Identify2WithUIDTestArgs
   233  	trackToken    keybase1.TrackToken
   234  	confirmResult keybase1.ConfirmResult
   235  	cachedRes     *keybase1.Identify2ResUPK2
   236  
   237  	metaContext libkb.MetaContext
   238  
   239  	// If we just resolved a user, then we can plumb this through to loadUser()
   240  	ResolveBody *jsonw.Wrapper
   241  
   242  	me   *identifyUser
   243  	them *identifyUser
   244  
   245  	themAssertion   libkb.AssertionExpression
   246  	remoteAssertion libkb.AssertionAnd
   247  	localAssertion  libkb.AssertionAnd
   248  
   249  	state        libkb.IdentifyState
   250  	useTracking  bool
   251  	identifyKeys []keybase1.IdentifyKey
   252  
   253  	resultCh chan<- error
   254  
   255  	// For eagerly checking remote Assertions as they come in, these
   256  	// member variables maintain state, protected by the remotesMutex.
   257  	remotesMutex     sync.Mutex
   258  	remotesReceived  *libkb.ProofSet
   259  	remotesError     error
   260  	remotesCompleted bool
   261  
   262  	responsibleGregorItem gregor.Item
   263  
   264  	// When tracking is being performed, the identify engine is used with a tracking ui.
   265  	// These options are sent to the ui based on command line options.
   266  	// For normal identify, safe to leave these in their default zero state.
   267  	trackOptions keybase1.TrackOptions
   268  
   269  	// When called from chat, we should just collect breaking tracking failures, but
   270  	// not fail track. This is where we collect them
   271  	trackBreaks *keybase1.IdentifyTrackBreaks
   272  }
   273  
   274  var _ (Engine2) = (*Identify2WithUID)(nil)
   275  var _ (libkb.CheckCompletedListener) = (*Identify2WithUID)(nil)
   276  
   277  // Name is the unique engine name.
   278  func (e *Identify2WithUID) Name() string {
   279  	return "Identify2WithUID"
   280  }
   281  
   282  func NewIdentify2WithUID(g *libkb.GlobalContext, arg *keybase1.Identify2Arg) *Identify2WithUID {
   283  	return &Identify2WithUID{
   284  		Contextified: libkb.NewContextified(g),
   285  		arg:          arg,
   286  	}
   287  }
   288  
   289  // GetPrereqs returns the engine prereqs.
   290  func (e *Identify2WithUID) Prereqs() Prereqs {
   291  	return Prereqs{}
   292  }
   293  
   294  func (e *Identify2WithUID) WantDelegate(k libkb.UIKind) bool {
   295  	return k == libkb.IdentifyUIKind && e.arg.UseDelegateUI
   296  }
   297  
   298  func (e *Identify2WithUID) resetError(m libkb.MetaContext, inErr error) (outErr error) {
   299  
   300  	defer m.Trace(fmt.Sprintf("Identify2WithUID#resetError(%s)", libkb.ErrToOk(inErr)), &outErr)()
   301  
   302  	if inErr == nil {
   303  		return nil
   304  	}
   305  
   306  	// Check to see if this is an identify failure, and if not just return. If it is, we want
   307  	// to check what identify mode we are in here before returning an error.
   308  	if !libkb.IsIdentifyProofError(inErr) {
   309  		return inErr
   310  	}
   311  
   312  	if e.arg.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
   313  		m.Debug("| Reset err from %v -> nil since caller is '%s' %d", inErr, e.arg.IdentifyBehavior, e.arg.IdentifyBehavior)
   314  		return nil
   315  	}
   316  
   317  	return inErr
   318  }
   319  
   320  // Run then engine
   321  func (e *Identify2WithUID) Run(m libkb.MetaContext) (err error) {
   322  
   323  	m = m.WithLogTag("ID2")
   324  
   325  	n := fmt.Sprintf("Identify2WithUID#Run(UID=%v, Assertion=%s)", e.arg.Uid, e.arg.UserAssertion)
   326  	defer m.Trace(n, &err)()
   327  	m.Debug("| Full Arg: %+v", e.arg)
   328  
   329  	if e.arg.Uid.IsNil() {
   330  		return libkb.NoUIDError{}
   331  	}
   332  
   333  	// Only the first send matters, but we don't want to block the subsequent no-op
   334  	// sends. This code will break when we have more than 100 unblocking opportunities.
   335  	ch := make(chan error, 100)
   336  
   337  	e.resultCh = ch
   338  	go e.run(m)
   339  	err = <-ch
   340  
   341  	// Potentially reset the error based on the error and the calling context.
   342  	err = e.resetError(m, err)
   343  	return err
   344  }
   345  
   346  func (e *Identify2WithUID) notifyChat(m libkb.MetaContext, inErr error) error {
   347  	m.Debug("Identify2WithUID.run: notifyChat")
   348  	if !e.arg.IdentifyBehavior.ShouldRefreshChatView() {
   349  		return nil
   350  	}
   351  	if e.them == nil {
   352  		return fmt.Errorf("nil user")
   353  	}
   354  	nun := e.them.GetNormalizedName()
   355  	if nun.IsNil() {
   356  		return fmt.Errorf("no username for user")
   357  	}
   358  	// Don't notify if there's a non-identity proof error.
   359  	if inErr != nil && !libkb.IsIdentifyProofError(inErr) {
   360  		return nil
   361  	}
   362  
   363  	res, err := e.Result(m)
   364  	if err != nil {
   365  		return err
   366  	}
   367  
   368  	update := keybase1.CanonicalTLFNameAndIDWithBreaks{
   369  		CanonicalName: keybase1.CanonicalTlfName(nun),
   370  	}
   371  	if libkb.IsIdentifyProofError(inErr) {
   372  		update.Breaks = keybase1.TLFBreak{Breaks: []keybase1.TLFIdentifyFailure{
   373  			{
   374  				User:   *e.them.Export(),
   375  				Breaks: res.TrackBreaks,
   376  			},
   377  		}}
   378  	}
   379  	m.Debug("Identify2WithUID.run: running HandleChatIdentifyUpdate: %#v", update)
   380  	m.G().NotifyRouter.HandleChatIdentifyUpdate(m.Ctx(), update)
   381  
   382  	return nil
   383  }
   384  
   385  func (e *Identify2WithUID) run(m libkb.MetaContext) {
   386  	err := e.runReturnError(m)
   387  
   388  	if notifyChatErr := e.notifyChat(m, err); notifyChatErr != nil {
   389  		m.Warning("failed to notify chat of identify result: %s", notifyChatErr)
   390  	}
   391  
   392  	// always cancel IdentifyUI to allow clients to clean up.
   393  	// If no identifyUI was specified (because running the background)
   394  	// then don't do anything.
   395  	if m.UIs().IdentifyUI != nil {
   396  		err := m.UIs().IdentifyUI.Cancel(m)
   397  		if err != nil {
   398  			m.Debug("| error during cancel: %+v", err)
   399  		}
   400  	}
   401  	e.unblock(m, true /* isFinal */, err)
   402  }
   403  
   404  func (e *Identify2WithUID) hitFastCache(m libkb.MetaContext) bool {
   405  
   406  	if !e.allowCaching() {
   407  		m.Debug("| missed fast cache: no caching allowed")
   408  		return false
   409  	}
   410  	if e.useAnyAssertions() {
   411  		m.Debug("| missed fast cache: has assertions")
   412  		return false
   413  	}
   414  	if !e.allowEarlyOuts() {
   415  		m.Debug("| missed fast cache: we don't allow early outs")
   416  		return false
   417  	}
   418  	if !e.checkFastCacheHit(m) {
   419  		m.Debug("| missed fast cache: didn't hit")
   420  		return false
   421  	}
   422  	return true
   423  }
   424  
   425  func (e *Identify2WithUID) untrackedFastPath(m libkb.MetaContext) (ret bool) {
   426  
   427  	defer m.Trace("Identify2WithUID#untrackedFastPath", nil)()
   428  
   429  	if !e.arg.IdentifyBehavior.CanUseUntrackedFastPath() {
   430  		m.Debug("| Can't use untracked fast path due to identify behavior %v", e.arg.IdentifyBehavior)
   431  		return false
   432  	}
   433  
   434  	statInc := func() {
   435  		if e.testArgs != nil {
   436  			e.testArgs.stats.untrackedFastPaths++
   437  		}
   438  	}
   439  
   440  	if e.testArgs != nil && !e.testArgs.allowUntrackedFastPath {
   441  		m.Debug("| Can't use untracked fast path since disallowed in test")
   442  		return false
   443  	}
   444  
   445  	if e.them == nil {
   446  		m.Debug("| Can't use untracked fast path since failed to load them users")
   447  		return false
   448  	}
   449  
   450  	nun := e.them.GetNormalizedName()
   451  
   452  	// check if there's a tcl in the testArgs
   453  	if e.testArgs != nil && e.testArgs.tcl != nil {
   454  		trackedUsername, err := e.testArgs.tcl.GetTrackedUsername()
   455  		if err == nil && trackedUsername == nun {
   456  			m.Debug("| Test track link found for %s", nun.String())
   457  			return false
   458  		}
   459  	}
   460  
   461  	if e.me == nil {
   462  		m.Debug("| Can use untracked fastpath since there is no logged in user")
   463  		statInc()
   464  		return true
   465  	}
   466  
   467  	tcl, err := e.me.trackChainLinkFor(m, nun, e.them.GetUID())
   468  	if err != nil {
   469  		m.Debug("| Error getting track chain link: %s", err)
   470  		return false
   471  	}
   472  
   473  	if tcl != nil {
   474  		m.Debug("| Track found for %s", nun.String())
   475  		return false
   476  	}
   477  
   478  	statInc()
   479  	return true
   480  }
   481  
   482  func (e *Identify2WithUID) runReturnError(m libkb.MetaContext) (err error) {
   483  
   484  	m.Debug("+ acquire singleflight lock for %s", e.arg.Uid)
   485  	lock, err := m.G().IDLocktab.AcquireOnNameWithContext(m.Ctx(), m.G(), e.arg.Uid.String())
   486  	if err != nil {
   487  		m.Debug("| error acquiring singleflight lock for %s: %v", e.arg.Uid, err)
   488  		return err
   489  	}
   490  	m.Debug("- acquired singleflight lock")
   491  
   492  	defer func() {
   493  		m.Debug("+ Releasing singleflight lock for %s", e.arg.Uid)
   494  		lock.Release(m.Ctx())
   495  		m.Debug("- Released singleflight lock")
   496  	}()
   497  
   498  	if err = e.loadAssertion(m); err != nil {
   499  		return err
   500  	}
   501  
   502  	if e.hitFastCache(m) {
   503  		m.Debug("| hit fast cache")
   504  		e.maybeNotify(m, "hit fast cache")
   505  		return nil
   506  	}
   507  
   508  	m.Debug("| Identify2WithUID.loadUsers")
   509  	if err = e.loadUsers(m); err != nil {
   510  		return err
   511  	}
   512  
   513  	if err = e.checkLocalAssertions(); err != nil {
   514  		return err
   515  	}
   516  
   517  	if e.isSelfLoad() && !e.arg.NoSkipSelf && !e.useRemoteAssertions() {
   518  		m.Debug("| was a self load, short-circuiting")
   519  		e.maybeCacheSelf(m)
   520  		return nil
   521  	}
   522  
   523  	// If we are rekeying or reclaiming quota from KBFS, then let's
   524  	// skip the external checks.
   525  	if e.arg.IdentifyBehavior.SkipExternalChecks() {
   526  		m.Debug("| skip external checks specified, short-circuiting")
   527  		return nil
   528  	}
   529  
   530  	if !e.useRemoteAssertions() && e.allowEarlyOuts() {
   531  
   532  		if e.untrackedFastPath(m) {
   533  			m.Debug("| used untracked fast path")
   534  			e.maybeNotify(m, "untracked fast path")
   535  			return nil
   536  		}
   537  
   538  		if e.checkSlowCacheHit(m) {
   539  			m.Debug("| hit slow cache, first check")
   540  			e.maybeNotify(m, "slow cache, first check")
   541  			return nil
   542  		}
   543  	}
   544  
   545  	m.Debug("| Identify2WithUID.createIdentifyState")
   546  	if err = e.createIdentifyState(m); err != nil {
   547  		return err
   548  	}
   549  
   550  	if err = e.runIdentifyPrecomputation(); err != nil {
   551  		return err
   552  	}
   553  
   554  	// First we check that all remote assertions as present for the user,
   555  	// whether or not the remote check actually succeeds (hence the
   556  	// ProofState_NONE check).
   557  	okStates := []keybase1.ProofState{keybase1.ProofState_NONE, keybase1.ProofState_OK}
   558  	if err = e.checkRemoteAssertions(okStates); err != nil {
   559  		m.Debug("| Early fail due to missing remote assertions")
   560  		return err
   561  	}
   562  
   563  	if e.useRemoteAssertions() && e.allowEarlyOuts() && e.checkSlowCacheHit(m) {
   564  		m.Debug("| hit slow cache, second check")
   565  		e.maybeNotify(m, "slow cache, second check")
   566  		return nil
   567  	}
   568  
   569  	// If we're not using tracking and we're not using remote assertions,
   570  	// we can unblock the RPC caller here, and perform the identifyUI operations
   571  	// in the background. NOTE: we need to copy out our background context,
   572  	// since it will the foreground context will disappear after we unblock.
   573  	m = m.BackgroundWithLogTags()
   574  
   575  	if (!e.useTracking && !e.useRemoteAssertions() && e.allowEarlyOuts()) || e.arg.IdentifyBehavior.UnblockThenForceIDTable() {
   576  		e.unblock(m, false /* isFinal */, nil /* err */)
   577  	}
   578  
   579  	return e.runIdentifyUI(m)
   580  }
   581  
   582  func (e *Identify2WithUID) allowEarlyOuts() bool {
   583  	return !e.arg.NeedProofSet && !e.arg.IdentifyBehavior.UnblockThenForceIDTable()
   584  }
   585  
   586  func (e *Identify2WithUID) getNow(m libkb.MetaContext) time.Time {
   587  	if e.testArgs != nil && e.testArgs.clock != nil {
   588  		return e.testArgs.clock()
   589  	}
   590  	return m.G().Clock().Now()
   591  }
   592  
   593  func (e *Identify2WithUID) unblock(m libkb.MetaContext, isFinal bool, err error) {
   594  	m.Debug("| unblocking...")
   595  	if e.arg.AlwaysBlock && !isFinal {
   596  		m.Debug("| skipping unblock; isFinal=%v; AlwaysBlock=%v...", isFinal, e.arg.AlwaysBlock)
   597  	} else {
   598  		e.resultCh <- err
   599  		m.Debug("| unblock sent...")
   600  	}
   601  }
   602  
   603  func (e *Identify2WithUID) maybeCacheSelf(m libkb.MetaContext) {
   604  	if e.getCache() != nil {
   605  		v, err := e.exportToResult(m)
   606  		if v != nil && err == nil {
   607  			err := e.getCache().Insert(v)
   608  			if err != nil {
   609  				m.Debug("| error inserting: %+v", err)
   610  			}
   611  		}
   612  	}
   613  }
   614  
   615  // exportToResult either returns (non-nil, nil) on success, or (nil, non-nil) on error.
   616  func (e *Identify2WithUID) exportToResult(m libkb.MetaContext) (*keybase1.Identify2ResUPK2, error) {
   617  	if e.them == nil {
   618  		// this should never happen
   619  		return nil, libkb.UserNotFoundError{Msg: "failed to get a them user in Identify2WithUID#exportToResult"}
   620  	}
   621  	upk, err := e.toUserPlusKeysv2AllIncarnations()
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	if upk == nil {
   626  		// this should never happen
   627  		return nil, libkb.UserNotFoundError{Msg: "failed export a them user in Identify2WithUID#exportToResult"}
   628  	}
   629  	return &keybase1.Identify2ResUPK2{
   630  		Upk:          *upk,
   631  		TrackBreaks:  e.trackBreaks,
   632  		IdentifiedAt: keybase1.ToTime(e.getNow(m)),
   633  	}, nil
   634  }
   635  
   636  func (e *Identify2WithUID) maybeCacheResult(m libkb.MetaContext) {
   637  
   638  	isOK := e.state.Result().IsOK()
   639  	canCacheFailures := e.arg.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks()
   640  
   641  	m.Debug("+ maybeCacheResult (ok=%v; canCacheFailures=%v)", isOK, canCacheFailures)
   642  	defer m.Debug("- maybeCacheResult")
   643  
   644  	if e.getCache() == nil {
   645  		m.Debug("| cache is disabled, so nothing to do")
   646  		return
   647  	}
   648  
   649  	// If we hit an identify failure, and we're not allowed to cache failures,
   650  	// then at least bust out the cache.
   651  	if !isOK && !canCacheFailures {
   652  		m.Debug("| clearing cache due to failure")
   653  		uid := e.them.GetUID()
   654  		err := e.getCache().Delete(uid)
   655  		if err != nil {
   656  			m.Debug("| Error deleting uid: %+v", err)
   657  		}
   658  		if err := e.removeSlowCacheFromDB(m); err != nil {
   659  			m.Debug("| Error in removing slow cache from db: %s", err)
   660  		}
   661  		return
   662  	}
   663  
   664  	// Common case --- (isOK || canCacheFailures)
   665  	v, err := e.exportToResult(m)
   666  	if err != nil {
   667  		m.Debug("| not caching: error exporting: %s", err)
   668  		return
   669  	}
   670  	if v == nil {
   671  		m.Debug("| not caching; nil result")
   672  		return
   673  	}
   674  	err = e.getCache().Insert(v)
   675  	if err != nil {
   676  		m.Debug("| error inserting: %+v", err)
   677  	}
   678  
   679  	m.VLogf(libkb.VLog1, "| insert %+v", v)
   680  
   681  	// Don't write failures to the disk cache
   682  	if isOK {
   683  		if err := e.storeSlowCacheToDB(m); err != nil {
   684  			m.Debug("| Error in storing slow cache to db: %s", err)
   685  		}
   686  	}
   687  }
   688  
   689  func (e *Identify2WithUID) insertTrackToken(m libkb.MetaContext, outcome *libkb.IdentifyOutcome) (err error) {
   690  	defer m.Trace("Identify2WithUID#insertTrackToken", &err)()
   691  	e.trackToken, err = m.G().TrackCache().Insert(outcome)
   692  	if err != nil {
   693  		return err
   694  	}
   695  	return m.UIs().IdentifyUI.ReportTrackToken(m, e.trackToken)
   696  }
   697  
   698  // CCLCheckCompleted is triggered whenever a remote proof check completes.
   699  // We get these calls as a result of being a "CheckCompletedListener".
   700  // When each result comes in, we check against our pool of needed remote
   701  // assertions. If the set is complete, or if one that we need errors,
   702  // we can unblock the caller.
   703  func (e *Identify2WithUID) CCLCheckCompleted(lcr *libkb.LinkCheckResult) {
   704  	e.remotesMutex.Lock()
   705  	defer e.remotesMutex.Unlock()
   706  	m := e.metaContext
   707  
   708  	m.Debug("+ CheckCompleted for %s", lcr.GetLink().ToIDString())
   709  	defer m.Debug("- CheckCompleted")
   710  
   711  	// Always add to remotesReceived list, so that we have a full ProofSet.
   712  	pf := libkb.RemoteProofChainLinkToProof(lcr.GetLink())
   713  	e.remotesReceived.Add(pf)
   714  
   715  	if !e.useRemoteAssertions() || e.useTracking {
   716  		m.Debug("| Not using remote assertions or is tracking")
   717  		return
   718  	}
   719  
   720  	if !e.remoteAssertion.HasFactor(pf) {
   721  		m.Debug("| Proof isn't needed in our remote-assertion early-out check: %v", pf)
   722  		return
   723  	}
   724  
   725  	if err := lcr.GetError(); err != nil {
   726  		m.Debug("| got error -> %v", err)
   727  		e.remotesError = err
   728  	}
   729  
   730  	// note(maxtaco): this is a little ugly in that it's O(n^2) where n is the number
   731  	// of identities in the assertion. But I can't imagine n > 3, so this is fine
   732  	// for now.
   733  	matched := e.remoteAssertion.MatchSet(*e.remotesReceived)
   734  	m.Debug("| matched -> %v", matched)
   735  	if matched {
   736  		e.remotesCompleted = true
   737  	}
   738  
   739  	if e.remotesError != nil || e.remotesCompleted {
   740  		m.Debug("| unblocking, with err = %v", e.remotesError)
   741  		e.unblock(m, false, e.remotesError)
   742  	}
   743  }
   744  
   745  func (e *Identify2WithUID) checkLocalAssertions() error {
   746  	if !e.localAssertion.MatchSet(*e.them.BaseProofSet()) {
   747  		return libkb.UnmetAssertionError{User: e.them.GetName(), Remote: false}
   748  	}
   749  	return nil
   750  }
   751  
   752  func (e *Identify2WithUID) checkRemoteAssertions(okStates []keybase1.ProofState) error {
   753  	if e.them.isDeleted {
   754  		if e.G().Env.GetReadDeletedSigChain() {
   755  			return nil
   756  		}
   757  		return libkb.UnmetAssertionError{User: e.them.GetName(), Remote: true}
   758  	}
   759  	ps := libkb.NewProofSet(nil)
   760  	e.state.Result().AddProofsToSet(ps, okStates)
   761  	if !e.remoteAssertion.MatchSet(*ps) {
   762  		return libkb.UnmetAssertionError{User: e.them.GetName(), Remote: true}
   763  	}
   764  	return nil
   765  }
   766  
   767  func (e *Identify2WithUID) loadAssertion(mctx libkb.MetaContext) (err error) {
   768  	if len(e.arg.UserAssertion) == 0 {
   769  		return nil
   770  	}
   771  	e.themAssertion, err = libkb.AssertionParseAndOnly(e.G().MakeAssertionContext(mctx), e.arg.UserAssertion)
   772  	if err == nil {
   773  		e.remoteAssertion, e.localAssertion = libkb.CollectAssertions(e.themAssertion)
   774  	}
   775  	return err
   776  }
   777  
   778  func (e *Identify2WithUID) useAnyAssertions() bool {
   779  	return e.useLocalAssertions() || e.useRemoteAssertions()
   780  }
   781  
   782  func (e *Identify2WithUID) allowCaching() bool {
   783  	return e.arg.IdentifyBehavior.AllowCaching()
   784  }
   785  
   786  func (e *Identify2WithUID) useLocalAssertions() bool {
   787  	return e.localAssertion.Len() > 0
   788  }
   789  
   790  // If we need a ProofSet, it's as if we need remote assertions.
   791  func (e *Identify2WithUID) useRemoteAssertions() bool {
   792  	return (e.remoteAssertion.Len() > 0)
   793  }
   794  
   795  func (e *Identify2WithUID) runIdentifyPrecomputation() (err error) {
   796  
   797  	keyDiffDisplayHook := func(k keybase1.IdentifyKey) error {
   798  		e.identifyKeys = append(e.identifyKeys, k)
   799  		return nil
   800  	}
   801  	revokedKeyHook := func(id libkb.TrackIDComponent, diff libkb.TrackDiff) {
   802  		if diff == nil {
   803  			return
   804  		}
   805  		ipb := keybase1.IdentifyProofBreak{
   806  			RemoteProof: libkb.ExportTrackIDComponentToRevokedProof(id).Proof,
   807  			Lcr: keybase1.LinkCheckResult{
   808  				Diff:           libkb.ExportTrackDiff(diff),
   809  				BreaksTracking: true,
   810  			},
   811  		}
   812  		if e.trackBreaks == nil {
   813  			e.trackBreaks = &keybase1.IdentifyTrackBreaks{}
   814  		}
   815  		e.trackBreaks.Proofs = append(e.trackBreaks.Proofs, ipb)
   816  	}
   817  	e.state.Precompute(keyDiffDisplayHook, revokedKeyHook)
   818  	return nil
   819  }
   820  
   821  func (e *Identify2WithUID) displayUserCardAsync(m libkb.MetaContext) <-chan error {
   822  	// Skip showing the userCard if we are allowing deleted users since this
   823  	// will error out.
   824  	if e.arg.IdentifyBehavior.SkipUserCard() || e.G().Env.GetReadDeletedSigChain() {
   825  		return nil
   826  	}
   827  	return libkb.DisplayUserCardAsync(m, e.them.GetUID(), (e.me != nil))
   828  }
   829  
   830  func (e *Identify2WithUID) setupIdentifyUI(m libkb.MetaContext) libkb.MetaContext {
   831  	if e.arg.IdentifyBehavior.ShouldSuppressTrackerPopups() {
   832  		m.Debug("| using the loopback identify UI")
   833  		iui := NewLoopbackIdentifyUI(m.G(), &e.trackBreaks)
   834  		m = m.WithIdentifyUI(iui)
   835  	} else if e.useTracking && e.arg.CanSuppressUI && !e.arg.ForceDisplay {
   836  		iui := newBufferedIdentifyUI(m.G(), m.UIs().IdentifyUI, keybase1.ConfirmResult{
   837  			IdentityConfirmed: true,
   838  		})
   839  		m = m.WithIdentifyUI(iui)
   840  	}
   841  	return m
   842  }
   843  
   844  func (e *Identify2WithUID) runIdentifyUI(m libkb.MetaContext) (err error) {
   845  	n := fmt.Sprintf("+ runIdentifyUI(%s)", e.them.GetName())
   846  	defer m.Trace(n, &err)()
   847  
   848  	// RemoteReceived, start with the baseProofSet that has PGP
   849  	// fingerprints and the user's UID and username.
   850  	e.remotesReceived = e.them.BaseProofSet()
   851  
   852  	m = e.setupIdentifyUI(m)
   853  	iui := m.UIs().IdentifyUI
   854  
   855  	m.Debug("| IdentifyUI.Start(%s)", e.them.GetName())
   856  	if err = iui.Start(m, e.them.GetName(), e.arg.Reason, e.arg.ForceDisplay); err != nil {
   857  		return err
   858  	}
   859  	for _, k := range e.identifyKeys {
   860  		if err = iui.DisplayKey(m, k); err != nil {
   861  			return err
   862  		}
   863  	}
   864  	m.Debug("| IdentifyUI.ReportLastTrack(%s)", e.them.GetName())
   865  	if err = iui.ReportLastTrack(m, libkb.ExportTrackSummary(e.state.TrackLookup(), e.them.GetName())); err != nil {
   866  		return err
   867  	}
   868  	m.Debug("| IdentifyUI.LaunchNetworkChecks(%s)", e.them.GetName())
   869  	if err = iui.LaunchNetworkChecks(m, e.state.ExportToUncheckedIdentity(m), e.them.Export()); err != nil {
   870  		return err
   871  	}
   872  
   873  	waiter := e.displayUserCardAsync(m)
   874  
   875  	m.Debug("| IdentifyUI.Identify(%s)", e.them.GetName())
   876  	var them *libkb.User
   877  	them, err = e.them.User(e.getCache())
   878  	if err != nil {
   879  		return err
   880  	}
   881  
   882  	identifyTableMode := libkb.IdentifyTableModeActive
   883  	if e.arg.IdentifyBehavior.ShouldSuppressTrackerPopups() {
   884  		identifyTableMode = libkb.IdentifyTableModePassive
   885  	}
   886  
   887  	// When we get a callback from IDTable().Identify, we don't get to thread our metacontext
   888  	// through (for now), so stash it in the this.
   889  	e.metaContext = m
   890  	if them.IDTable() == nil {
   891  		m.Debug("| No IDTable for user")
   892  	} else if err = them.IDTable().Identify(m, e.state, e.forceRemoteCheck(), iui, e, identifyTableMode); err != nil {
   893  		m.Debug("| Failure in running IDTable")
   894  		return err
   895  	}
   896  
   897  	if waiter != nil {
   898  		m.Debug("+ Waiting for UserCard")
   899  		if err = <-waiter; err != nil {
   900  			m.Debug("| Failure in showing UserCard")
   901  			return err
   902  		}
   903  		m.Debug("- Waited for UserCard")
   904  	}
   905  
   906  	// use Confirm to display the IdentifyOutcome
   907  	outcome := e.state.Result()
   908  	outcome.TrackOptions = e.trackOptions
   909  	e.confirmResult, err = iui.Confirm(m, outcome.Export(e.G()))
   910  	if err != nil {
   911  		m.Debug("| Failure in iui.Confirm")
   912  		return err
   913  	}
   914  
   915  	err = e.insertTrackToken(m, outcome)
   916  	if err != nil {
   917  		m.Debug("| Error inserting track token: %+v", err)
   918  	}
   919  
   920  	if err = iui.Finish(m); err != nil {
   921  		m.Debug("| Failure in iui.Finish")
   922  		return err
   923  	}
   924  	m.Debug("| IdentifyUI.Finished(%s)", e.them.GetName())
   925  
   926  	err = e.checkRemoteAssertions([]keybase1.ProofState{keybase1.ProofState_OK})
   927  	e.maybeCacheResult(m)
   928  
   929  	m.Debug("| IdentifyUI: checked remote assertions (%s)", e.them.GetName())
   930  
   931  	if err == nil && !e.arg.NoErrorOnTrackFailure {
   932  		// We only care about tracking errors in this case; hence GetErrorLax
   933  		_, err = e.state.Result().GetErrorLax()
   934  	}
   935  
   936  	if outcome.IsOK() {
   937  		e.maybeNotify(m, "runIdentifyUI complete IsOk")
   938  	}
   939  
   940  	return err
   941  }
   942  
   943  func (e *Identify2WithUID) forceRemoteCheck() bool {
   944  	return e.arg.ForceRemoteCheck || (e.testArgs != nil && e.testArgs.forceRemoteCheck)
   945  }
   946  
   947  func (e *Identify2WithUID) createIdentifyState(m libkb.MetaContext) (err error) {
   948  	defer m.Trace("createIdentifyState", &err)()
   949  	var them *libkb.User
   950  	them, err = e.them.User(e.getCache())
   951  	if err != nil {
   952  		return err
   953  	}
   954  
   955  	e.state = libkb.NewIdentifyStateWithGregorItem(m.G(), e.responsibleGregorItem, them)
   956  
   957  	if e.testArgs != nil && e.testArgs.tcl != nil {
   958  		m.Debug("| using test track")
   959  		e.useTracking = true
   960  		e.state.SetTrackLookup(e.testArgs.tcl)
   961  		return nil
   962  	}
   963  
   964  	if e.me == nil {
   965  		m.Debug("| null me")
   966  		return nil
   967  	}
   968  
   969  	tcl, err := e.me.trackChainLinkFor(m, them.GetNormalizedName(), them.GetUID())
   970  	if tcl != nil {
   971  		m.Debug("| using track token %s", tcl.LinkID())
   972  		e.useTracking = true
   973  		e.state.SetTrackLookup(tcl)
   974  		if ttcl, _ := libkb.TmpTrackChainLinkFor(m, e.me.GetUID(), them.GetUID()); ttcl != nil {
   975  			m.Debug("| also have temporary track")
   976  			e.state.SetTmpTrackLookup(ttcl)
   977  		}
   978  	}
   979  
   980  	return nil
   981  }
   982  
   983  // RequiredUIs returns the required UIs.
   984  func (e *Identify2WithUID) RequiredUIs() []libkb.UIKind {
   985  	ret := []libkb.UIKind{}
   986  	if e.arg == nil || !e.arg.IdentifyBehavior.ShouldSuppressTrackerPopups() {
   987  		ret = append(ret, libkb.IdentifyUIKind)
   988  	}
   989  	return ret
   990  }
   991  
   992  // SubConsumers returns the other UI consumers for this engine.
   993  func (e *Identify2WithUID) SubConsumers() []libkb.UIConsumer {
   994  	return nil
   995  }
   996  
   997  func (e *Identify2WithUID) isSelfLoad() bool {
   998  	if e.testArgs != nil && e.testArgs.selfLoad {
   999  		return true
  1000  	}
  1001  	return e.me != nil && e.them != nil && e.me.Equal(e.them)
  1002  }
  1003  
  1004  func (e *Identify2WithUID) loadUserOpts(arg libkb.LoadUserArg) libkb.LoadUserArg {
  1005  	if !e.allowCaching() {
  1006  		arg = arg.WithForcePoll(true)
  1007  	}
  1008  	return arg
  1009  }
  1010  
  1011  func (e *Identify2WithUID) loadMe(m libkb.MetaContext, uid keybase1.UID) (err error) {
  1012  
  1013  	// Short circuit loadMe for testing
  1014  	if e.testArgs != nil && e.testArgs.noMe {
  1015  		return nil
  1016  	}
  1017  	arg := libkb.NewLoadUserArgWithMetaContext(m).WithUID(uid).WithSelf(true).WithStubMode(libkb.StubModeUnstubbed)
  1018  	e.me, err = loadIdentifyUser(m, e.loadUserOpts(arg), e.getCache())
  1019  	return err
  1020  }
  1021  
  1022  func (e *Identify2WithUID) loadThem(m libkb.MetaContext) (err error) {
  1023  	arg := e.loadUserOpts(libkb.NewLoadUserArgWithMetaContext(m).WithUID(e.arg.Uid).WithResolveBody(e.ResolveBody).WithPublicKeyOptional())
  1024  	e.them, err = loadIdentifyUser(m, arg, e.getCache())
  1025  	if err != nil {
  1026  		switch err.(type) {
  1027  		case libkb.NoKeyError:
  1028  			// convert this error to NoSigChainError
  1029  			return libkb.NoSigChainError{}
  1030  		case libkb.NotFoundError:
  1031  			return libkb.UserNotFoundError{UID: e.arg.Uid, Msg: "in Identify2WithUID"}
  1032  		default: // including libkb.UserDeletedError
  1033  			return err
  1034  		}
  1035  	}
  1036  	if e.them == nil {
  1037  		return libkb.UserNotFoundError{UID: e.arg.Uid, Msg: "in Identify2WithUID"}
  1038  	}
  1039  	err = libkb.UserErrorFromStatus(e.them.GetStatus())
  1040  	if _, ok := err.(libkb.UserDeletedError); ok && e.arg.IdentifyBehavior.AllowDeletedUsers() || e.G().Env.GetReadDeletedSigChain() {
  1041  		e.them.isDeleted = true
  1042  		return nil
  1043  	}
  1044  	return err
  1045  }
  1046  
  1047  func (e *Identify2WithUID) loadUsers(m libkb.MetaContext) (err error) {
  1048  	var loadMeErr, loadThemErr error
  1049  
  1050  	var selfLoad bool
  1051  	var wg sync.WaitGroup
  1052  
  1053  	if !e.arg.ActLoggedOut {
  1054  		loggedIn, myUID := isLoggedIn(m)
  1055  		if loggedIn {
  1056  			selfLoad = myUID.Equal(e.arg.Uid)
  1057  			wg.Add(1)
  1058  			go func() {
  1059  				loadMeErr = e.loadMe(m, myUID)
  1060  				wg.Done()
  1061  			}()
  1062  		}
  1063  	}
  1064  
  1065  	if !selfLoad {
  1066  		wg.Add(1)
  1067  		go func() {
  1068  			loadThemErr = e.loadThem(m)
  1069  			wg.Done()
  1070  		}()
  1071  	}
  1072  	wg.Wait()
  1073  
  1074  	if loadMeErr != nil {
  1075  		return loadMeErr
  1076  	}
  1077  	if loadThemErr != nil {
  1078  		return loadThemErr
  1079  	}
  1080  
  1081  	if selfLoad {
  1082  		e.them = e.me
  1083  	}
  1084  
  1085  	return nil
  1086  }
  1087  
  1088  func (e *Identify2WithUID) checkFastCacheHit(m libkb.MetaContext) (hit bool) {
  1089  	prfx := fmt.Sprintf("Identify2WithUID#checkFastCacheHit(%s)", e.arg.Uid)
  1090  	defer m.Trace(prfx, nil)()
  1091  	if e.getCache() == nil {
  1092  		return false
  1093  	}
  1094  
  1095  	fn := func(u keybase1.Identify2ResUPK2) keybase1.Time { return u.Upk.Uvv.CachedAt }
  1096  	dfn := func(u keybase1.Identify2ResUPK2) time.Duration {
  1097  		return libkb.Identify2CacheShortTimeout
  1098  	}
  1099  	u, err := e.getCache().Get(e.arg.Uid, fn, dfn, e.arg.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks())
  1100  
  1101  	if err != nil {
  1102  		m.Debug("| fast cache error for %s: %s", e.arg.Uid, err)
  1103  	}
  1104  	if u == nil {
  1105  		m.Debug("| fast cache returning false on nil output")
  1106  		return false
  1107  	}
  1108  	e.cachedRes = u
  1109  	return true
  1110  }
  1111  
  1112  func (e *Identify2WithUID) dbKey(them keybase1.UID) libkb.DbKey {
  1113  	return libkb.DbKey{
  1114  		Typ: libkb.DBIdentify,
  1115  		Key: fmt.Sprintf("%s-%s", e.me.GetUID(), them),
  1116  	}
  1117  }
  1118  
  1119  func (e *Identify2WithUID) loadSlowCacheFromDB(m libkb.MetaContext) (ret *keybase1.Identify2ResUPK2) {
  1120  	defer m.Trace("Identify2WithUID#loadSlowCacheFromDB", nil)()
  1121  
  1122  	if e.getCache() != nil && !e.getCache().UseDiskCache() {
  1123  		m.Debug("| Disk cached disabled")
  1124  		return nil
  1125  	}
  1126  
  1127  	var ktm keybase1.Time
  1128  	key := e.dbKey(e.them.GetUID())
  1129  	found, err := e.G().LocalDb.GetInto(&ktm, key)
  1130  	if err != nil {
  1131  		m.Debug("| Error loading key %+v from cache: %s", key, err)
  1132  		return nil
  1133  	}
  1134  	if !found {
  1135  		m.Debug("| Key wasn't found: %+v", key)
  1136  		return nil
  1137  	}
  1138  	tm := ktm.Time()
  1139  	now := e.getNow(m)
  1140  	diff := now.Sub(tm)
  1141  	if diff > libkb.Identify2CacheLongTimeout {
  1142  		m.Debug("| Object timed out %s ago", diff)
  1143  		return nil
  1144  	}
  1145  	var tmp keybase1.Identify2ResUPK2
  1146  	upk2ai, err := e.them.ExportToUserPlusKeysV2AllIncarnations()
  1147  	if err != nil {
  1148  		m.Warning("| Failed to export: %s", err)
  1149  		return nil
  1150  	}
  1151  	tmp.Upk = *upk2ai
  1152  	tmp.IdentifiedAt = ktm
  1153  	ret = &tmp
  1154  	return ret
  1155  }
  1156  
  1157  // Store (meUID, themUID) -> SuccessfulIDTime as we cache users to the slow cache.
  1158  // Thus, after a cold boot, we don't start up with a cold identify cache.
  1159  func (e *Identify2WithUID) storeSlowCacheToDB(m libkb.MetaContext) (err error) {
  1160  	prfx := fmt.Sprintf("Identify2WithUID#storeSlowCacheToDB(%s)", e.them.GetUID())
  1161  	defer e.G().Trace(prfx, &err)()
  1162  	if e.me == nil {
  1163  		m.Debug("not storing to persistent slow cache since no me user")
  1164  		return nil
  1165  	}
  1166  
  1167  	key := e.dbKey(e.them.GetUID())
  1168  	now := keybase1.ToTime(e.getNow(m))
  1169  	err = e.G().LocalDb.PutObj(key, nil, now)
  1170  	return err
  1171  }
  1172  
  1173  // Remove (themUID) from the identify cache, if they're there.
  1174  func (e *Identify2WithUID) removeSlowCacheFromDB(m libkb.MetaContext) (err error) {
  1175  	prfx := fmt.Sprintf("Identify2WithUID#removeSlowCacheFromDB(%s)", e.them.GetUID())
  1176  	defer e.G().Trace(prfx, &err)()
  1177  	if e.me == nil {
  1178  		m.Debug("not removing from persistent slow cache since no me user")
  1179  		return nil
  1180  	}
  1181  	key := e.dbKey(e.them.GetUID())
  1182  	err = e.G().LocalDb.Delete(key)
  1183  	return err
  1184  }
  1185  
  1186  func (e *Identify2WithUID) checkSlowCacheHit(m libkb.MetaContext) (ret bool) {
  1187  	prfx := fmt.Sprintf("Identify2WithUID#checkSlowCacheHit(%s)", e.them.GetUID())
  1188  	defer m.Trace(prfx, nil)()
  1189  
  1190  	if e.getCache() == nil {
  1191  		return false
  1192  	}
  1193  
  1194  	if !e.allowCaching() {
  1195  		m.Debug("| missed fast cache: no caching allowed")
  1196  		return false
  1197  	}
  1198  
  1199  	timeFn := func(u keybase1.Identify2ResUPK2) keybase1.Time { return u.IdentifiedAt }
  1200  	durationFn := func(u keybase1.Identify2ResUPK2) time.Duration {
  1201  		if u.TrackBreaks != nil {
  1202  			return libkb.Identify2CacheBrokenTimeout
  1203  		}
  1204  		return libkb.Identify2CacheLongTimeout
  1205  	}
  1206  	u, err := e.getCache().Get(e.them.GetUID(), timeFn, durationFn, e.arg.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks())
  1207  
  1208  	trackBrokenError := false
  1209  	if err != nil {
  1210  		m.Debug("| slow cache error for %s: %s", e.them.GetUID(), err)
  1211  		if _, ok := err.(libkb.TrackBrokenError); ok {
  1212  			trackBrokenError = true
  1213  		}
  1214  	}
  1215  
  1216  	if u == nil && e.me != nil && !trackBrokenError {
  1217  		u = e.loadSlowCacheFromDB(m)
  1218  	}
  1219  
  1220  	if u == nil {
  1221  		m.Debug("| %s: identify missed cache", prfx)
  1222  		return false
  1223  	}
  1224  
  1225  	if !e.them.IsCachedIdentifyFresh(&u.Upk) {
  1226  		m.Debug("| %s: cached identify was stale", prfx)
  1227  		return false
  1228  	}
  1229  
  1230  	e.cachedRes = u
  1231  
  1232  	// Update so that it hits the fast cache the next time
  1233  	u.Upk.Uvv.CachedAt = keybase1.ToTime(e.getNow(m))
  1234  	err = e.getCache().Insert(u)
  1235  	if err != nil {
  1236  		m.Debug("| error on insert: %+v", err)
  1237  	}
  1238  	return true
  1239  }
  1240  
  1241  // Result will return (non-nil,nil) on success, and (nil,non-nil) on failure.
  1242  func (e *Identify2WithUID) Result(m libkb.MetaContext) (*keybase1.Identify2ResUPK2, error) {
  1243  	if e.cachedRes != nil {
  1244  		return e.cachedRes, nil
  1245  	}
  1246  	res, err := e.exportToResult(m)
  1247  	if err != nil {
  1248  		return nil, err
  1249  	}
  1250  	if res == nil {
  1251  		return nil, libkb.UserNotFoundError{Msg: "identify2 unexpectly returned an empty user"}
  1252  	}
  1253  	return res, nil
  1254  }
  1255  
  1256  func (e *Identify2WithUID) GetProofSet() *libkb.ProofSet {
  1257  	return e.remotesReceived
  1258  }
  1259  
  1260  func (e *Identify2WithUID) GetIdentifyOutcome() *libkb.IdentifyOutcome {
  1261  	return e.state.Result()
  1262  }
  1263  
  1264  func (e *Identify2WithUID) toUserPlusKeysv2AllIncarnations() (*keybase1.UserPlusKeysV2AllIncarnations, error) {
  1265  	return e.them.ExportToUserPlusKeysV2AllIncarnations()
  1266  }
  1267  
  1268  func (e *Identify2WithUID) getCache() libkb.Identify2Cacher {
  1269  	if e.testArgs != nil && e.testArgs.cache != nil {
  1270  		return e.testArgs.cache
  1271  	}
  1272  	if e.testArgs != nil && e.testArgs.noCache {
  1273  		return nil
  1274  	}
  1275  	return e.G().Identify2Cache()
  1276  }
  1277  
  1278  func (e *Identify2WithUID) getTrackType() identify2TrackType {
  1279  	switch {
  1280  	case !e.useTracking || e.state.Result() == nil:
  1281  		return identify2NoTrack
  1282  	case e.state.Result().IsOK():
  1283  		return identify2TrackOK
  1284  	default:
  1285  		return identify2TrackBroke
  1286  	}
  1287  }
  1288  
  1289  func (e *Identify2WithUID) SetResponsibleGregorItem(item gregor.Item) {
  1290  	e.responsibleGregorItem = item
  1291  }
  1292  
  1293  func (e *Identify2WithUID) TrackToken() keybase1.TrackToken {
  1294  	return e.trackToken
  1295  }
  1296  
  1297  func (e *Identify2WithUID) ConfirmResult() keybase1.ConfirmResult {
  1298  	return e.confirmResult
  1299  }
  1300  
  1301  func (e *Identify2WithUID) FullMeUser() *libkb.User {
  1302  	if e.me == nil {
  1303  		return nil
  1304  	}
  1305  	return e.me.Full()
  1306  }
  1307  
  1308  func (e *Identify2WithUID) FullThemUser() *libkb.User {
  1309  	if e.them == nil {
  1310  		return nil
  1311  	}
  1312  	return e.them.Full()
  1313  }
  1314  
  1315  func (e *Identify2WithUID) maybeNotify(mctx libkb.MetaContext, explanation string) {
  1316  	target := e.arg.Uid
  1317  	if e.them != nil {
  1318  		target = e.them.GetUID()
  1319  	}
  1320  	if e.me == nil {
  1321  		// This check is needed because ActLoggedOut causes the untracked fast path
  1322  		// to succeed even when the true active user is tracking the identifyee.
  1323  		mctx.Debug("Identify2WithUID.maybeNotify(%v, %v) nope missing ME", target, explanation)
  1324  		return
  1325  	}
  1326  	if target.IsNil() {
  1327  		mctx.Debug("Identify2WithUID.maybeNotify(%v, %v) nope missing UID", target, explanation)
  1328  		return
  1329  	}
  1330  	if e.arg.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
  1331  		mctx.Debug("Identify2WithUID.maybeNotify(%v, %v) nope WarningInsteadOfErrorOnBrokenTracks", target, explanation)
  1332  		return
  1333  	}
  1334  	mctx.Debug("Identify2WithUID.maybeNotify(%v, %v) -> sending", target, explanation)
  1335  	go mctx.G().IdentifyDispatch.NotifyTrackingSuccess(mctx, target)
  1336  }