github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/sig_hints.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 libkb
     5  
     6  import (
     7  	"fmt"
     8  
     9  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    10  	jsonw "github.com/keybase/go-jsonw"
    11  )
    12  
    13  type SigHint struct {
    14  	sigID     keybase1.SigID
    15  	remoteID  string
    16  	apiURL    string
    17  	humanURL  string
    18  	checkText string
    19  	// `isVerified` indicates if the client generated the values or they were
    20  	// received from the server and are trusted but not verified.
    21  	isVerified bool
    22  }
    23  
    24  func (sh SigHint) GetHumanURL() string  { return sh.humanURL }
    25  func (sh SigHint) GetAPIURL() string    { return sh.apiURL }
    26  func (sh SigHint) GetCheckText() string { return sh.checkText }
    27  
    28  func NewSigHint(jw *jsonw.Wrapper) (sh *SigHint, err error) {
    29  	if jw == nil || !jw.IsOk() {
    30  		return nil, nil
    31  	}
    32  	sh = &SigHint{}
    33  	sh.sigID, err = GetSigID(jw.AtKey("sig_id"))
    34  	sh.remoteID, _ = jw.AtKey("remote_id").GetString()
    35  	sh.apiURL, _ = jw.AtKey("api_url").GetString()
    36  	sh.humanURL, _ = jw.AtKey("human_url").GetString()
    37  	sh.checkText, _ = jw.AtKey("proof_text_check").GetString()
    38  	sh.isVerified, _ = jw.AtKey("isVerified").GetBool()
    39  	return sh, err
    40  }
    41  
    42  func NewVerifiedSigHint(sigID keybase1.SigID, remoteID, apiURL, humanURL, checkText string) *SigHint {
    43  	return &SigHint{
    44  		sigID:      sigID,
    45  		remoteID:   remoteID,
    46  		apiURL:     apiURL,
    47  		humanURL:   humanURL,
    48  		checkText:  checkText,
    49  		isVerified: true,
    50  	}
    51  }
    52  
    53  func (sh SigHint) MarshalToJSON() *jsonw.Wrapper {
    54  	ret := jsonw.NewDictionary()
    55  	_ = ret.SetKey("sig_id", jsonw.NewString(sh.sigID.String()))
    56  	_ = ret.SetKey("remote_id", jsonw.NewString(sh.remoteID))
    57  	_ = ret.SetKey("api_url", jsonw.NewString(sh.apiURL))
    58  	_ = ret.SetKey("human_url", jsonw.NewString(sh.humanURL))
    59  	_ = ret.SetKey("proof_text_check", jsonw.NewString(sh.checkText))
    60  	_ = ret.SetKey("is_verified", jsonw.NewBool(sh.isVerified))
    61  	return ret
    62  }
    63  
    64  type SigHints struct {
    65  	Contextified
    66  	uid     keybase1.UID
    67  	version int
    68  	hints   map[keybase1.SigIDMapKey]*SigHint
    69  	dirty   bool
    70  }
    71  
    72  func NewSigHints(jw *jsonw.Wrapper, uid keybase1.UID, dirty bool, g *GlobalContext) (sh *SigHints, err error) {
    73  	sh = &SigHints{
    74  		uid:          uid,
    75  		dirty:        dirty,
    76  		version:      0,
    77  		Contextified: NewContextified(g),
    78  	}
    79  	err = sh.PopulateWith(jw)
    80  	if err != nil {
    81  		sh = nil
    82  	}
    83  	return
    84  }
    85  
    86  func (sh SigHints) Lookup(i keybase1.SigID) *SigHint {
    87  	obj := sh.hints[i.ToMapKey()]
    88  	return obj
    89  }
    90  
    91  func (sh *SigHints) PopulateWith(jw *jsonw.Wrapper) (err error) {
    92  
    93  	if jw == nil || jw.IsNil() {
    94  		return
    95  	}
    96  
    97  	jw.AtKey("version").GetIntVoid(&sh.version, &err)
    98  	if err != nil {
    99  		return
   100  	}
   101  
   102  	sh.hints = make(map[keybase1.SigIDMapKey]*SigHint)
   103  	var n int
   104  	n, err = jw.AtKey("hints").Len()
   105  	if err != nil {
   106  		return
   107  	}
   108  
   109  	for i := 0; i < n; i++ {
   110  		hint, tmpe := NewSigHint(jw.AtKey("hints").AtIndex(i))
   111  		if tmpe != nil {
   112  			sh.G().Log.Warning("Bad SigHint Loaded: %s", tmpe)
   113  		} else {
   114  			sh.hints[hint.sigID.ToMapKey()] = hint
   115  		}
   116  	}
   117  	return
   118  }
   119  
   120  func (sh SigHints) MarshalToJSON() *jsonw.Wrapper {
   121  	ret := jsonw.NewDictionary()
   122  	_ = ret.SetKey("version", jsonw.NewInt(sh.version))
   123  	_ = ret.SetKey("hints", jsonw.NewArray(len(sh.hints)))
   124  	i := 0
   125  	for _, v := range sh.hints {
   126  		_ = ret.AtKey("hints").SetIndex(i, v.MarshalToJSON())
   127  		i++
   128  	}
   129  	return ret
   130  }
   131  
   132  func (sh *SigHints) Store(m MetaContext) (err error) {
   133  	m.Debug("+ SigHints.Store() for uid=%s", sh.uid)
   134  	if sh.dirty {
   135  		err = sh.G().LocalDb.Put(DbKeyUID(DBSigHints, sh.uid), []DbKey{}, sh.MarshalToJSON())
   136  		sh.dirty = false
   137  	} else {
   138  		m.Debug("| SigHints.Store() skipped; wasn't dirty")
   139  	}
   140  	m.Debug("- SigHints.Store() for uid=%s -> %v", sh.uid, ErrToOk(err))
   141  	return err
   142  }
   143  
   144  func LoadSigHints(m MetaContext, uid keybase1.UID) (sh *SigHints, err error) {
   145  	defer m.Trace(fmt.Sprintf("+ LoadSigHints(%s)", uid), &err)()
   146  	var jw *jsonw.Wrapper
   147  	jw, err = m.G().LocalDb.Get(DbKeyUID(DBSigHints, uid))
   148  	if err != nil {
   149  		jw = nil
   150  		m.Debug("| SigHints failed to access local storage: %s", err)
   151  	}
   152  	// jw might be nil here, but that's allowed.
   153  	sh, err = NewSigHints(jw, uid, false, m.G())
   154  	if err == nil {
   155  		m.Debug("| SigHints loaded @v%d", sh.version)
   156  	}
   157  	m.Debug("- LoadSigHints(%s)", uid)
   158  	return
   159  }
   160  
   161  func (sh *SigHints) Refresh(m MetaContext) (err error) {
   162  	defer m.Trace(fmt.Sprintf("Refresh SigHints for uid=%s", sh.uid), &err)()
   163  	res, err := m.G().API.Get(m, APIArg{
   164  		Endpoint:    "sig/hints",
   165  		SessionType: APISessionTypeNONE,
   166  		Args: HTTPArgs{
   167  			"uid": UIDArg(sh.uid),
   168  			"low": I{sh.version},
   169  		},
   170  	})
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	return sh.RefreshWith(m, res.Body)
   176  }
   177  
   178  func (sh *SigHints) RefreshWith(m MetaContext, jw *jsonw.Wrapper) (err error) {
   179  	defer m.Trace("RefreshWith", &err)()
   180  
   181  	n, err := jw.AtKey("hints").Len()
   182  	if err != nil {
   183  		return err
   184  	}
   185  	if n == 0 {
   186  		m.Debug("| No changes; version %d was up-to-date", sh.version)
   187  	} else if err = sh.PopulateWith(jw); err != nil {
   188  		return err
   189  	} else {
   190  		sh.dirty = true
   191  	}
   192  	return nil
   193  }
   194  
   195  func LoadAndRefreshSigHints(m MetaContext, uid keybase1.UID) (*SigHints, error) {
   196  	sh, err := LoadSigHints(m, uid)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	if err = sh.Refresh(m); err != nil {
   201  		return nil, err
   202  	}
   203  	return sh, nil
   204  }