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

     1  package libkb
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sync"
     7  
     8  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     9  )
    10  
    11  // Pegboard keeps track of automatic private follows.
    12  // When the logged-in user interacts with another user, that other user
    13  // gets pegged to their incarnation. After the target resets,
    14  // the logged-in user will be alerted even if there's no explicit following.
    15  // CORE-10522 For now, pegboard is disabled by default and when enabled
    16  //
    17  //	only has in-memory storage.
    18  type Pegboard struct {
    19  	mu      sync.Mutex
    20  	enabled bool
    21  	store   map[keybase1.UID]Peg
    22  	// CORE-10522 The eventual plan is to store pegs in user's private sigchain
    23  	//            or just leveldb. With an lru in front.
    24  }
    25  
    26  // CORE-10522 When a peg breaks, the chat UI of "Let them in" doesn't make sense.
    27  
    28  // CORE-10522 In chat only impteam convs are handled. Have to figure out story for KBFS convs.
    29  
    30  // CORE-10522 In addition to chat, KBFS might should also use pegs.
    31  
    32  // CORE-10522 Looking at a profile should call ObserveUV.
    33  
    34  func NewPegboard() *Pegboard {
    35  	return &Pegboard{
    36  		enabled: os.Getenv("KEYBASE_EXPERIMENT_PEGBOARD") == "1",
    37  		store:   make(map[keybase1.UID]Peg),
    38  	}
    39  }
    40  
    41  type Peg struct {
    42  	UID         keybase1.UID
    43  	EldestSeqno keybase1.Seqno
    44  	// CORE-10522 Seqno may not be enough. Consider pegging LinkID.
    45  }
    46  
    47  // Update a peg.
    48  // The logged-in user has purposefully interacted with this version of the user.
    49  func (p *Pegboard) TrackUPAK(mctx MetaContext, upak keybase1.UserPlusKeysV2) (err error) {
    50  	if !p.enabled {
    51  		return nil
    52  	}
    53  	defer mctx.Trace("Pegboard.TrackUPAK", &err)()
    54  	p.mu.Lock()
    55  	defer p.mu.Unlock()
    56  	if peg, ok := p.store[upak.Uid]; ok && peg.EldestSeqno != 0 && upak.EldestSeqno != 0 && upak.EldestSeqno < peg.EldestSeqno {
    57  		// User is already tracked. But at a newer version than the argument.
    58  		// CORE-10522 check with explicit local and remote follows?
    59  		return fmt.Errorf("Cannot update to older version of user: %v %v < %v", upak.Uid, upak.EldestSeqno, peg.EldestSeqno)
    60  	}
    61  	p.store[upak.Uid] = Peg{
    62  		UID:         upak.Uid,
    63  		EldestSeqno: upak.EldestSeqno,
    64  	}
    65  	return nil
    66  }
    67  
    68  // Returns an error if this user has reset since their peg was last udpated.
    69  // Pegs users that haven't been seen before.
    70  func (p *Pegboard) CheckUV(mctx MetaContext, uv keybase1.UserVersion) error {
    71  	return p.checkUV(mctx, uv)
    72  }
    73  
    74  // Pegs users that haven't been seen before.
    75  func (p *Pegboard) ObserveUV(mctx MetaContext, uv keybase1.UserVersion) {
    76  	_ = p.checkUV(mctx, uv)
    77  }
    78  
    79  func (p *Pegboard) checkUV(mctx MetaContext, uv keybase1.UserVersion) error {
    80  	if !p.enabled {
    81  		return nil
    82  	}
    83  	p.mu.Lock()
    84  	defer p.mu.Unlock()
    85  	if peg, ok := p.store[uv.Uid]; ok {
    86  		// This uid has been seen before.
    87  		if peg.EldestSeqno == 0 {
    88  			// Track the new version.
    89  			p.store[uv.Uid] = Peg{
    90  				UID:         uv.Uid,
    91  				EldestSeqno: uv.EldestSeqno,
    92  			}
    93  		} else {
    94  			if uv.EldestSeqno != peg.EldestSeqno {
    95  				return fmt.Errorf("User version does not match peg %v != %v", uv.EldestSeqno, peg.EldestSeqno)
    96  			}
    97  		}
    98  	} else {
    99  		// First time seeing this uid.
   100  		p.store[uv.Uid] = Peg{
   101  			UID:         uv.Uid,
   102  			EldestSeqno: uv.EldestSeqno,
   103  		}
   104  	}
   105  	return nil
   106  }
   107  
   108  func (p *Pegboard) OnLogout(mctx MetaContext) {
   109  	if !p.enabled {
   110  		return
   111  	}
   112  	p.mu.Lock()
   113  	defer p.mu.Unlock()
   114  	p.store = make(map[keybase1.UID]Peg)
   115  }