github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/db.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  	"encoding/json"
     8  	"fmt"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/keybase/client/go/msgpack"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	jsonw "github.com/keybase/go-jsonw"
    16  )
    17  
    18  const (
    19  	DBUser              = 0x00
    20  	DBSig               = 0x0f
    21  	DBTeamChain         = 0x10
    22  	DBUserPlusAllKeysV1 = 0x19
    23  
    24  	DBChatArchiveRegistry            = 0xa3
    25  	DBIncomingSharePreference        = 0xa4
    26  	DBChatUserEmojis                 = 0xa5
    27  	DBChatInboxIndex                 = 0xa6
    28  	DBChatInboxConvs                 = 0xa7
    29  	DBChatParticipants               = 0xa8
    30  	DBOpenTeams                      = 0xa9
    31  	DBNetworkInstrumentation         = 0xaa
    32  	DBFeaturedBots                   = 0xab
    33  	DBChatEphemeralTracker           = 0xac
    34  	DBLoginTimes                     = 0xad
    35  	DBChatJourney                    = 0xae
    36  	DBTeamRoleMap                    = 0xaf
    37  	DBMisc                           = 0xb0
    38  	DBTeamMerkleCheck                = 0xb1
    39  	DBUidToServiceMap                = 0xb2
    40  	DBChatPinIgnore                  = 0xb3
    41  	DBTeambotKey                     = 0xb4
    42  	DBTeambotKeyWrongKID             = 0xb5
    43  	DBChatBotCommands                = 0xb6
    44  	DBSavedContacts                  = 0xb7
    45  	DBChatLocation                   = 0xb8
    46  	DBHiddenChainStorage             = 0xb9
    47  	DBContactResolution              = 0xba
    48  	DBBoxAuditorPermanent            = 0xbb
    49  	DBBoxAuditor                     = 0xbc
    50  	DBUserPlusKeysVersionedUnstubbed = 0xbd
    51  	DBOfflineRPC                     = 0xbe
    52  	DBChatCollapses                  = 0xbf
    53  	DBSupportsHiddenFlagStorage      = 0xc0
    54  	DBMerkleAudit                    = 0xca
    55  	DBUnfurler                       = 0xcb
    56  	DBStellarDisclaimer              = 0xcc
    57  	DBFTLStorage                     = 0xcd
    58  	DBTeamAuditor                    = 0xce
    59  	DBAttachmentUploader             = 0xcf
    60  	DBLegacyHasRandomPW              = 0xd0
    61  	DBDiskLRUEntries                 = 0xda
    62  	DBDiskLRUIndex                   = 0xdb
    63  	DBImplicitTeamConflictInfo       = 0xdc
    64  	DBUidToFullName                  = 0xdd
    65  	DBUidToUsername                  = 0xde
    66  	DBUserPlusKeysVersioned          = 0xdf
    67  	DBLink                           = 0xe0
    68  	DBLocalTrack                     = 0xe1
    69  	DBPGPKey                         = 0xe3
    70  	DBSigHints                       = 0xe4
    71  	DBProofCheck                     = 0xe5
    72  	DBUserSecretKeys                 = 0xe6
    73  	DBSigChainTailPublic             = 0xe7
    74  	DBSigChainTailSemiprivate        = 0xe8
    75  	DBSigChainTailEncrypted          = 0xe9
    76  	DBChatActive                     = 0xea
    77  	DBUserEKBox                      = 0xeb
    78  	DBTeamEKBox                      = 0xec
    79  	DBChatIndex                      = 0xed
    80  	DBChatGiphy                      = 0xee
    81  	DBChatReacji                     = 0xef
    82  	DBMerkleRoot                     = 0xf0
    83  	DBTrackers                       = 0xf1
    84  	DBGregor                         = 0xf2
    85  	DBUnverifiedTrackersFollowers    = 0xf3
    86  	DBUnverifiedTrackersFollowing    = 0xf4
    87  	DBNotificationDismiss            = 0xf5
    88  	DBChatBlockIndex                 = 0xf6
    89  	DBChatBlocks                     = 0xf7
    90  	DBChatOutbox                     = 0xf8
    91  	DBChatInbox                      = 0xf9
    92  	DBIdentify                       = 0xfa
    93  	DBResolveUsernameToUID           = 0xfb
    94  	DBChatBodyHashIndex              = 0xfc
    95  	DBMerkleStore                    = 0xfd
    96  	DBChatConvFailures               = 0xfe
    97  	DBTeamList                       = 0xff
    98  )
    99  
   100  // Note(maxtaco) 2018.10.08 --- Note a bug here, that we used the `libkb.DBChatInbox` type here.
   101  // That's a copy-paste bug, but we get away with it since we have a `tid:` prefix that
   102  // disambiguates these entries from true Chat entries. We're not going to fix it now
   103  // since it would kill the team cache, but sometime in the future we should fix it.
   104  const (
   105  	DBSlowTeamsAlias = DBChatInbox
   106  )
   107  
   108  const (
   109  	DBLookupUsername = 0x00
   110  	// was once used to store latest merkle root with Key:"HEAD"
   111  	DBLookupMerkleRoot = 0x01
   112  )
   113  
   114  func DbKeyUID(t ObjType, uid keybase1.UID) DbKey {
   115  	return DbKey{Typ: t, Key: uid.String()}
   116  }
   117  
   118  func DbKeyNotificationDismiss(prefix string, username NormalizedUsername) DbKey {
   119  	return DbKey{
   120  		Typ: DBNotificationDismiss,
   121  		Key: fmt.Sprintf("%s:%s", prefix, username),
   122  	}
   123  }
   124  
   125  // IsPermDbKey returns true for keys ignored by the leveldb cleaner and always
   126  // persisted to disk. Ideally these keys handling some cleanup/size bounding
   127  // themselves.
   128  func IsPermDbKey(typ ObjType) bool {
   129  	switch typ {
   130  	case DBDiskLRUEntries,
   131  		DBDiskLRUIndex,
   132  		DBOfflineRPC,
   133  		DBChatCollapses,
   134  		DBLegacyHasRandomPW,
   135  		DBChatReacji,
   136  		DBStellarDisclaimer,
   137  		DBChatIndex,
   138  		DBBoxAuditorPermanent,
   139  		DBSavedContacts,
   140  		DBContactResolution,
   141  		DBTeambotKeyWrongKID,
   142  		DBMisc,
   143  		DBIncomingSharePreference:
   144  		return true
   145  	default:
   146  		return false
   147  	}
   148  }
   149  
   150  type ObjType byte
   151  
   152  type DbKey struct {
   153  	Typ ObjType
   154  	Key string
   155  }
   156  
   157  // tablePrefix builds a key prefix for the given table for use in `util.Range`
   158  // or `util.BytesPrefix`
   159  func tablePrefix(table string) []byte {
   160  	return []byte(fmt.Sprintf("%s:", table))
   161  }
   162  
   163  func prefixStringWithTable(table string, typ ObjType) string {
   164  	return fmt.Sprintf("%s:%02x", table, typ)
   165  }
   166  
   167  func PrefixString(typ ObjType) string {
   168  	return prefixStringWithTable(typ.table(), typ)
   169  }
   170  
   171  func (t ObjType) table() string {
   172  	if IsPermDbKey(t) {
   173  		return levelDbTablePerm
   174  	}
   175  	return levelDbTableKv
   176  }
   177  
   178  func (k DbKey) toBytes(prefixString string) []byte {
   179  	return []byte(fmt.Sprintf("%s:%s", prefixString, k.Key))
   180  }
   181  
   182  func (k DbKey) ToBytes() []byte {
   183  	return k.toBytes(PrefixString(k.Typ))
   184  }
   185  
   186  func (k DbKey) ToBytesLookup() []byte {
   187  	return k.toBytes(prefixStringWithTable(levelDbTableLo, k.Typ))
   188  }
   189  
   190  var fieldExp = regexp.MustCompile(`[a-f0-9]{2}`)
   191  
   192  func DbKeyParse(s string) (string, DbKey, error) {
   193  	v := strings.Split(s, ":")
   194  	if len(v) < 3 {
   195  		return "", DbKey{}, fmt.Errorf("expected 3 colon-separated fields, found %d", len(v))
   196  	}
   197  
   198  	if !fieldExp.MatchString(v[1]) {
   199  		return "", DbKey{}, fmt.Errorf("2nd field should be a 1-byte hex string")
   200  	}
   201  
   202  	b, err := strconv.ParseUint(v[1], 16, 8)
   203  	if err != nil {
   204  		return "", DbKey{}, err
   205  	}
   206  	dbKey := DbKey{
   207  		Typ: ObjType(b),
   208  		Key: strings.Join(v[2:], ":"),
   209  	}
   210  	return v[0], dbKey, nil
   211  }
   212  
   213  func jsonLocalDbPut(ops LocalDbOps, id DbKey, aliases []DbKey, val *jsonw.Wrapper) error {
   214  	bytes, err := val.Marshal()
   215  	if err == nil {
   216  		err = ops.Put(id, aliases, bytes)
   217  	}
   218  	return err
   219  }
   220  func jsonLocalDbGet(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) {
   221  	bytes, found, err := ops.Get(id)
   222  	var ret *jsonw.Wrapper
   223  	if found {
   224  		ret, err = jsonw.Unmarshal(bytes)
   225  	}
   226  	return ret, err
   227  }
   228  
   229  func jsonLocalDbGetInto(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) {
   230  	var buf []byte
   231  	buf, found, err = ops.Get(id)
   232  	if err == nil && found {
   233  		err = jsonw.EnsureMaxDepthBytesDefault(buf)
   234  		if err != nil {
   235  			return found, err
   236  		}
   237  		err = json.Unmarshal(buf, &obj)
   238  	}
   239  	return found, err
   240  }
   241  
   242  func jsonLocalDbPutObj(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) (err error) {
   243  	var bytes []byte
   244  	bytes, err = json.Marshal(obj)
   245  	if err == nil {
   246  		err = ops.Put(id, aliases, bytes)
   247  	}
   248  	return err
   249  }
   250  
   251  func jsonLocalDbLookup(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) {
   252  	bytes, found, err := ops.Lookup(id)
   253  	var ret *jsonw.Wrapper
   254  	if found {
   255  		ret, err = jsonw.Unmarshal(bytes)
   256  	}
   257  	return ret, err
   258  }
   259  
   260  func jsonLocalDbLookupIntoMsgpack(ops LocalDbOps, obj interface{}, alias DbKey) (found bool, err error) {
   261  	var buf []byte
   262  	buf, found, err = ops.Lookup(alias)
   263  	if err != nil || !found {
   264  		return found, err
   265  	}
   266  	err = msgpack.Decode(obj, buf)
   267  	return true, err
   268  }
   269  
   270  func jsonLocalDbGetIntoMsgpack(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) {
   271  	var buf []byte
   272  	buf, found, err = ops.Get(id)
   273  	if err != nil || !found {
   274  		return found, err
   275  	}
   276  	err = msgpack.Decode(obj, buf)
   277  	return true, err
   278  }
   279  
   280  func jsonLocalDbPutObjMsgpack(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) error {
   281  	bytes, err := msgpack.Encode(obj)
   282  	if err != nil {
   283  		return err
   284  	}
   285  	return ops.Put(id, aliases, bytes)
   286  }
   287  
   288  type JSONLocalDb struct {
   289  	engine LocalDb
   290  }
   291  
   292  func NewJSONLocalDb(e LocalDb) *JSONLocalDb   { return &JSONLocalDb{e} }
   293  func (j *JSONLocalDb) Open() error            { return j.engine.Open() }
   294  func (j *JSONLocalDb) ForceOpen() error       { return j.engine.ForceOpen() }
   295  func (j *JSONLocalDb) Close() error           { return j.engine.Close() }
   296  func (j *JSONLocalDb) Nuke() (string, error)  { return j.engine.Nuke() }
   297  func (j *JSONLocalDb) Clean(force bool) error { return j.engine.Clean(force) }
   298  func (j *JSONLocalDb) Stats() string          { return j.engine.Stats() }
   299  func (j *JSONLocalDb) CompactionStats() (bool, bool, error) {
   300  	return j.engine.CompactionStats()
   301  }
   302  func (j *JSONLocalDb) KeysWithPrefixes(prefixes ...[]byte) (DBKeySet, error) {
   303  	return j.engine.KeysWithPrefixes(prefixes...)
   304  }
   305  
   306  func (j *JSONLocalDb) PutRaw(id DbKey, b []byte) error       { return j.engine.Put(id, nil, b) }
   307  func (j *JSONLocalDb) GetRaw(id DbKey) ([]byte, bool, error) { return j.engine.Get(id) }
   308  func (j *JSONLocalDb) Delete(id DbKey) error                 { return j.engine.Delete(id) }
   309  
   310  func (j *JSONLocalDb) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error {
   311  	return jsonLocalDbPut(j.engine, id, aliases, val)
   312  }
   313  
   314  func (j *JSONLocalDb) Get(id DbKey) (*jsonw.Wrapper, error) {
   315  	return jsonLocalDbGet(j.engine, id)
   316  }
   317  
   318  func (j *JSONLocalDb) GetInto(obj interface{}, id DbKey) (found bool, err error) {
   319  	return jsonLocalDbGetInto(j.engine, obj, id)
   320  }
   321  
   322  func (j *JSONLocalDb) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) {
   323  	return jsonLocalDbPutObj(j.engine, id, aliases, obj)
   324  }
   325  
   326  func (j *JSONLocalDb) Lookup(id DbKey) (*jsonw.Wrapper, error) {
   327  	return jsonLocalDbLookup(j.engine, id)
   328  }
   329  
   330  func (j *JSONLocalDb) LookupIntoMsgpack(obj interface{}, alias DbKey) (found bool, err error) {
   331  	return jsonLocalDbLookupIntoMsgpack(j.engine, obj, alias)
   332  }
   333  
   334  func (j *JSONLocalDb) GetIntoMsgpack(obj interface{}, id DbKey) (found bool, err error) {
   335  	return jsonLocalDbGetIntoMsgpack(j.engine, obj, id)
   336  }
   337  
   338  func (j *JSONLocalDb) PutObjMsgpack(id DbKey, aliases []DbKey, obj interface{}) (err error) {
   339  	return jsonLocalDbPutObjMsgpack(j.engine, id, aliases, obj)
   340  }
   341  
   342  func (j *JSONLocalDb) OpenTransaction() (JSONLocalDbTransaction, error) {
   343  	var (
   344  		jtr JSONLocalDbTransaction
   345  		err error
   346  	)
   347  	if jtr.tr, err = j.engine.OpenTransaction(); err != nil {
   348  		return JSONLocalDbTransaction{}, err
   349  	}
   350  	return jtr, nil
   351  }
   352  
   353  func (j *JSONLocalDb) GetEngine() LocalDb {
   354  	return j.engine
   355  }
   356  
   357  type JSONLocalDbTransaction struct {
   358  	tr LocalDbTransaction
   359  }
   360  
   361  func (j JSONLocalDbTransaction) PutRaw(id DbKey, b []byte) error       { return j.tr.Put(id, nil, b) }
   362  func (j JSONLocalDbTransaction) GetRaw(id DbKey) ([]byte, bool, error) { return j.tr.Get(id) }
   363  func (j JSONLocalDbTransaction) Delete(id DbKey) error                 { return j.tr.Delete(id) }
   364  
   365  func (j JSONLocalDbTransaction) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error {
   366  	return jsonLocalDbPut(j.tr, id, aliases, val)
   367  }
   368  
   369  func (j JSONLocalDbTransaction) Get(id DbKey) (*jsonw.Wrapper, error) {
   370  	return jsonLocalDbGet(j.tr, id)
   371  }
   372  
   373  func (j JSONLocalDbTransaction) GetInto(obj interface{}, id DbKey) (found bool, err error) {
   374  	return jsonLocalDbGetInto(j.tr, obj, id)
   375  }
   376  
   377  func (j JSONLocalDbTransaction) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) {
   378  	return jsonLocalDbPutObj(j.tr, id, aliases, obj)
   379  }
   380  
   381  func (j JSONLocalDbTransaction) Lookup(id DbKey) (*jsonw.Wrapper, error) {
   382  	return jsonLocalDbLookup(j.tr, id)
   383  }
   384  
   385  func (j JSONLocalDbTransaction) Commit() error {
   386  	return j.tr.Commit()
   387  }
   388  
   389  func (j JSONLocalDbTransaction) Discard() {
   390  	j.tr.Discard()
   391  }