github.com/status-im/status-go@v1.1.0/multiaccounts/settings/database.go (about)

     1  package settings
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"encoding/json"
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/ethereum/go-ethereum/log"
    12  
    13  	"github.com/status-im/status-go/common/dbsetup"
    14  	"github.com/status-im/status-go/eth-node/types"
    15  	"github.com/status-im/status-go/multiaccounts/errors"
    16  	"github.com/status-im/status-go/nodecfg"
    17  	"github.com/status-im/status-go/params"
    18  	"github.com/status-im/status-go/sqlite"
    19  )
    20  
    21  type Notifier func(SettingField, interface{})
    22  
    23  var (
    24  	// dbInstances holds a map of singleton instances of Database
    25  	dbInstances map[string]*Database
    26  
    27  	// mutex guards the instantiation of the dbInstances values, to prevent any concurrent instantiations
    28  	mutex sync.Mutex
    29  )
    30  
    31  // Database sql wrapper for operations with browser objects.
    32  type Database struct {
    33  	db                   *sql.DB
    34  	SyncQueue            chan SyncSettingField
    35  	changesSubscriptions []chan *SyncSettingField
    36  	notifier             Notifier
    37  }
    38  
    39  // MakeNewDB ensures that a singleton instance of Database is returned per sqlite db file
    40  func MakeNewDB(db *sql.DB) (*Database, error) {
    41  	filename, err := dbsetup.GetDBFilename(db)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	d := &Database{
    47  		db:        db,
    48  		SyncQueue: make(chan SyncSettingField, 100),
    49  	}
    50  
    51  	// An empty filename means that the sqlite database is held in memory
    52  	// In this case we don't want to restrict the instantiation
    53  	if filename == "" {
    54  		return d, nil
    55  	}
    56  
    57  	// Lock to protect the map from concurrent access
    58  	mutex.Lock()
    59  	defer mutex.Unlock()
    60  
    61  	// init dbInstances if it hasn't been already
    62  	if dbInstances == nil {
    63  		dbInstances = map[string]*Database{}
    64  	}
    65  
    66  	// If we haven't seen this database file before make an instance
    67  	if _, ok := dbInstances[filename]; !ok {
    68  		dbInstances[filename] = d
    69  	}
    70  
    71  	// Check if the current dbInstance is closed, if closed assign new Database
    72  	if err := dbInstances[filename].db.Ping(); err != nil {
    73  		dbInstances[filename] = d
    74  	}
    75  
    76  	return dbInstances[filename], nil
    77  }
    78  
    79  func (db *Database) GetDB() *sql.DB {
    80  	return db.db
    81  }
    82  
    83  func (db *Database) GetSyncQueue() chan SyncSettingField {
    84  	return db.SyncQueue
    85  }
    86  
    87  func (db *Database) GetChangesSubscriptions() []chan *SyncSettingField {
    88  	return db.changesSubscriptions
    89  }
    90  
    91  func (db *Database) GetNotifier() Notifier {
    92  	return db.notifier
    93  }
    94  
    95  func (db *Database) SetSettingsNotifier(n Notifier) {
    96  	db.notifier = n
    97  }
    98  
    99  // TODO remove photoPath from settings
   100  func (db *Database) CreateSettings(s Settings, n params.NodeConfig) error {
   101  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	defer func() {
   107  		if err == nil {
   108  			err = tx.Commit()
   109  			return
   110  		}
   111  		// don't shadow original error
   112  		_ = tx.Rollback()
   113  	}()
   114  
   115  	_, err = tx.Exec(`
   116  INSERT INTO settings (
   117    address,
   118    currency,
   119    current_network,
   120    dapps_address,
   121    device_name,
   122    preferred_name,
   123    display_name,
   124    bio,
   125    eip1581_address,
   126    installation_id,
   127    key_uid,
   128    keycard_instance_uid,
   129    keycard_paired_on,
   130    keycard_pairing,
   131    latest_derived_path,
   132    mnemonic,
   133    name,
   134    networks,
   135    photo_path,
   136    preview_privacy,
   137    public_key,
   138    signing_phrase,
   139    wallet_root_address,
   140    synthetic_id,
   141    current_user_status,
   142    profile_pictures_show_to,
   143    profile_pictures_visibility,
   144    url_unfurling_mode,
   145    mnemonic_was_not_shown,
   146    wallet_token_preferences_group_by_community,
   147    wallet_collectible_preferences_group_by_collection,
   148    wallet_collectible_preferences_group_by_community,
   149    test_networks_enabled,
   150    fleet
   151  ) VALUES (
   152  ?,?,?,?,?,?,?,?,?,?,?,?,?,?,
   153  ?,?,?,?,?,?,?,?,?,'id',?,?,?,?,?,?,?,?,?,?)`,
   154  		s.Address,
   155  		s.Currency,
   156  		s.CurrentNetwork,
   157  		s.DappsAddress,
   158  		s.DeviceName,
   159  		s.PreferredName,
   160  		s.DisplayName,
   161  		s.Bio,
   162  		s.EIP1581Address,
   163  		s.InstallationID,
   164  		s.KeyUID,
   165  		s.KeycardInstanceUID,
   166  		s.KeycardPairedOn,
   167  		s.KeycardPairing,
   168  		s.LatestDerivedPath,
   169  		s.Mnemonic,
   170  		s.Name,
   171  		s.Networks,
   172  		s.PhotoPath,
   173  		s.PreviewPrivacy,
   174  		s.PublicKey,
   175  		s.SigningPhrase,
   176  		s.WalletRootAddress,
   177  		s.CurrentUserStatus,
   178  		s.ProfilePicturesShowTo,
   179  		s.ProfilePicturesVisibility,
   180  		s.URLUnfurlingMode,
   181  		s.MnemonicWasNotShown,
   182  		s.TokenGroupByCommunity,
   183  		s.CollectibleGroupByCollection,
   184  		s.CollectibleGroupByCommunity,
   185  		s.TestNetworksEnabled,
   186  		s.Fleet,
   187  	)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	if s.DisplayName != "" {
   193  		now := time.Now().Unix()
   194  		query := db.buildUpdateSyncClockQueryForField(DisplayName)
   195  		_, err := tx.Exec(query, uint64(now), uint64(now))
   196  		if err != nil {
   197  			return err
   198  		}
   199  	}
   200  
   201  	return nodecfg.SaveConfigWithTx(tx, &n)
   202  }
   203  
   204  func (db *Database) getSettingFieldFromReactName(reactName string) (SettingField, error) {
   205  	for _, s := range SettingFieldRegister {
   206  		if s.GetReactName() == reactName {
   207  			return s, nil
   208  		}
   209  	}
   210  	return SettingField{}, errors.ErrInvalidConfig
   211  }
   212  
   213  func (db *Database) makeSelectRow(setting SettingField) *sql.Row {
   214  	query := "SELECT %s FROM settings WHERE synthetic_id = 'id'"
   215  	query = fmt.Sprintf(query, setting.GetDBName())
   216  	return db.db.QueryRow(query)
   217  }
   218  
   219  func (db *Database) makeSelectString(setting SettingField) (string, error) {
   220  	var result sql.NullString
   221  	err := db.makeSelectRow(setting).Scan(&result)
   222  	if err == sql.ErrNoRows {
   223  		return "", nil
   224  	}
   225  	if result.Valid {
   226  		return result.String, nil
   227  	}
   228  	return "", err
   229  }
   230  
   231  func (db *Database) saveSetting(setting SettingField, value interface{}) error {
   232  	query := "UPDATE settings SET %s = ? WHERE synthetic_id = 'id'"
   233  	query = fmt.Sprintf(query, setting.GetDBName())
   234  
   235  	update, err := db.db.Prepare(query)
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	_, err = update.Exec(value)
   241  
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	if db.notifier != nil {
   247  		db.notifier(setting, value)
   248  	}
   249  
   250  	return nil
   251  }
   252  
   253  func (db *Database) parseSaveAndSyncSetting(sf SettingField, value interface{}) (err error) {
   254  	if sf.ValueCastHandler() != nil {
   255  		value, err = sf.ValueCastHandler()(value)
   256  		if err != nil {
   257  			return err
   258  		}
   259  	}
   260  
   261  	if sf.ValueHandler() != nil {
   262  		value, err = sf.ValueHandler()(value)
   263  		if err != nil {
   264  			return err
   265  		}
   266  	}
   267  
   268  	// TODO(samyoul) this is ugly as hell need a more elegant solution
   269  	if NodeConfig.GetReactName() == sf.GetReactName() {
   270  		if err = nodecfg.SaveNodeConfig(db.db, value.(*params.NodeConfig)); err != nil {
   271  			return err
   272  		}
   273  		value = nil
   274  	}
   275  
   276  	err = db.saveSetting(sf, value)
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	if sf.GetDBName() == DBColumnMnemonic {
   282  		mnemonicRemoved := value == nil || value.(string) == ""
   283  		err = db.saveSetting(MnemonicRemoved, mnemonicRemoved)
   284  		if err != nil {
   285  			return err
   286  		}
   287  		sf = MnemonicRemoved
   288  		value = mnemonicRemoved
   289  	}
   290  
   291  	if sf.CanSync(FromInterface) {
   292  		db.SyncQueue <- SyncSettingField{sf, value}
   293  	}
   294  
   295  	db.postChangesToSubscribers(&SyncSettingField{sf, value})
   296  
   297  	return nil
   298  }
   299  
   300  // SaveSetting stores data from any non-sync source
   301  // If the field requires syncing the field data is pushed on to the SyncQueue
   302  func (db *Database) SaveSetting(setting string, value interface{}) error {
   303  	sf, err := db.getSettingFieldFromReactName(setting)
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	return db.parseSaveAndSyncSetting(sf, value)
   309  }
   310  
   311  // SaveSettingField is identical in functionality to SaveSetting, except the setting parameter is a SettingField and
   312  // doesn't require any SettingFieldRegister lookup.
   313  // This func is useful if you already know the SettingField to save
   314  func (db *Database) SaveSettingField(sf SettingField, value interface{}) error {
   315  	return db.parseSaveAndSyncSetting(sf, value)
   316  }
   317  
   318  func (db *Database) DeleteMnemonic() error {
   319  	return db.saveSetting(Mnemonic, nil)
   320  }
   321  
   322  // SaveSyncSetting stores setting data from a sync protobuf source, note it does not call SettingField.ValueHandler()
   323  // nor does this function attempt to write to the Database.SyncQueue,
   324  // yet it still writes to Database.changesSubscriptions.
   325  func (db *Database) SaveSyncSetting(setting SettingField, value interface{}, clock uint64) error {
   326  	ls, err := db.GetSettingLastSynced(setting)
   327  	if err != nil {
   328  		return err
   329  	}
   330  	if clock <= ls {
   331  		return errors.ErrNewClockOlderThanCurrent
   332  	}
   333  
   334  	err = db.SetSettingLastSynced(setting, clock)
   335  	if err != nil {
   336  		return err
   337  	}
   338  
   339  	err = db.saveSetting(setting, value)
   340  	if err != nil {
   341  		return err
   342  	}
   343  
   344  	db.postChangesToSubscribers(&SyncSettingField{setting, value})
   345  	return nil
   346  }
   347  
   348  func (db *Database) GetSettingLastSynced(setting SettingField) (result uint64, err error) {
   349  	query := "SELECT %s FROM settings_sync_clock WHERE synthetic_id = 'id'"
   350  	query = fmt.Sprintf(query, setting.GetDBName())
   351  
   352  	err = db.db.QueryRow(query).Scan(&result)
   353  	if err != nil {
   354  		return 0, err
   355  	}
   356  
   357  	return result, nil
   358  }
   359  
   360  func (db *Database) buildUpdateSyncClockQueryForField(setting SettingField) string {
   361  	query := "UPDATE settings_sync_clock SET %s = ? WHERE synthetic_id = 'id' AND %s < ?"
   362  	return fmt.Sprintf(query, setting.GetDBName(), setting.GetDBName())
   363  }
   364  
   365  func (db *Database) SetSettingLastSynced(setting SettingField, clock uint64) error {
   366  	query := db.buildUpdateSyncClockQueryForField(setting)
   367  
   368  	_, err := db.db.Exec(query, clock, clock)
   369  	return err
   370  }
   371  
   372  func (db *Database) GetSettings() (Settings, error) {
   373  	var s Settings
   374  	err := db.db.QueryRow(`
   375  	SELECT
   376  		address, anon_metrics_should_send, chaos_mode, currency, current_network,
   377  		custom_bootnodes, custom_bootnodes_enabled, dapps_address, display_name, bio, eip1581_address, fleet,
   378  		hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing,
   379  		last_updated, latest_derived_path, link_preview_request_enabled, link_previews_enabled_sites, log_level,
   380  		mnemonic, mnemonic_removed, name, networks, notifications_enabled, push_notifications_server_enabled,
   381  		push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications,
   382  		push_notifications_block_mentions, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key,
   383  		remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers,
   384  		syncing_on_mobile_network, default_sync_period, use_mailservers, messages_from_contacts_only, usernames, appearance,
   385  		profile_pictures_show_to, profile_pictures_visibility, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens,
   386  		waku_bloom_filter_mode, webview_allow_permission_requests, current_user_status, send_status_updates, gif_recents,
   387  		gif_favorites, opensea_enabled, last_backup, backup_enabled, telemetry_server_url, auto_message_enabled, gif_api_key,
   388  		test_networks_enabled, mutual_contact_enabled, profile_migration_needed, is_goerli_enabled, wallet_token_preferences_group_by_community, url_unfurling_mode,
   389  		mnemonic_was_not_shown, wallet_show_community_asset_when_sending_tokens, wallet_display_assets_below_balance,
   390  		wallet_display_assets_below_balance_threshold, wallet_collectible_preferences_group_by_collection, wallet_collectible_preferences_group_by_community, 
   391  		peer_syncing_enabled
   392  	FROM
   393  		settings
   394  	WHERE
   395  		synthetic_id = 'id'`).Scan(
   396  		&s.Address,
   397  		&s.AnonMetricsShouldSend,
   398  		&s.ChaosMode,
   399  		&s.Currency,
   400  		&s.CurrentNetwork,
   401  		&s.CustomBootnodes,
   402  		&s.CustomBootnodesEnabled,
   403  		&s.DappsAddress,
   404  		&s.DisplayName,
   405  		&s.Bio,
   406  		&s.EIP1581Address,
   407  		&s.Fleet,
   408  		&s.HideHomeTooltip,
   409  		&s.InstallationID,
   410  		&s.KeyUID,
   411  		&s.KeycardInstanceUID,
   412  		&s.KeycardPairedOn,
   413  		&s.KeycardPairing,
   414  		&s.LastUpdated,
   415  		&s.LatestDerivedPath,
   416  		&s.LinkPreviewRequestEnabled,
   417  		&s.LinkPreviewsEnabledSites,
   418  		&s.LogLevel,
   419  		&s.Mnemonic,
   420  		&s.MnemonicRemoved,
   421  		&s.Name,
   422  		&s.Networks,
   423  		&s.NotificationsEnabled,
   424  		&s.PushNotificationsServerEnabled,
   425  		&s.PushNotificationsFromContactsOnly,
   426  		&s.RemotePushNotificationsEnabled,
   427  		&s.SendPushNotifications,
   428  		&s.PushNotificationsBlockMentions,
   429  		&s.PhotoPath,
   430  		&s.PinnedMailserver,
   431  		&s.PreferredName,
   432  		&s.PreviewPrivacy,
   433  		&s.PublicKey,
   434  		&s.RememberSyncingChoice,
   435  		&s.SigningPhrase,
   436  		&s.StickerPacksInstalled,
   437  		&s.StickerPacksPending,
   438  		&s.StickersRecentStickers,
   439  		&s.SyncingOnMobileNetwork,
   440  		&s.DefaultSyncPeriod,
   441  		&s.UseMailservers,
   442  		&s.MessagesFromContactsOnly,
   443  		&s.Usernames,
   444  		&s.Appearance,
   445  		&s.ProfilePicturesShowTo,
   446  		&s.ProfilePicturesVisibility,
   447  		&s.WalletRootAddress,
   448  		&s.WalletSetUpPassed,
   449  		&s.WalletVisibleTokens,
   450  		&s.WakuBloomFilterMode,
   451  		&s.WebViewAllowPermissionRequests,
   452  		&sqlite.JSONBlob{Data: &s.CurrentUserStatus},
   453  		&s.SendStatusUpdates,
   454  		&sqlite.JSONBlob{Data: &s.GifRecents},
   455  		&sqlite.JSONBlob{Data: &s.GifFavorites},
   456  		&s.OpenseaEnabled,
   457  		&s.LastBackup,
   458  		&s.BackupEnabled,
   459  		&s.TelemetryServerURL,
   460  		&s.AutoMessageEnabled,
   461  		&s.GifAPIKey,
   462  		&s.TestNetworksEnabled,
   463  		&s.MutualContactEnabled,
   464  		&s.ProfileMigrationNeeded,
   465  		&s.IsGoerliEnabled,
   466  		&s.TokenGroupByCommunity,
   467  		&s.URLUnfurlingMode,
   468  		&s.MnemonicWasNotShown,
   469  		&s.ShowCommunityAssetWhenSendingTokens,
   470  		&s.DisplayAssetsBelowBalance,
   471  		&s.DisplayAssetsBelowBalanceThreshold,
   472  		&s.CollectibleGroupByCollection,
   473  		&s.CollectibleGroupByCommunity,
   474  		&s.PeerSyncingEnabled,
   475  	)
   476  
   477  	return s, err
   478  }
   479  
   480  // We should remove this and realated things once mobile team starts usign `settings_notifications` package
   481  func (db *Database) GetNotificationsEnabled() (result bool, err error) {
   482  	err = db.makeSelectRow(NotificationsEnabled).Scan(&result)
   483  	if err == sql.ErrNoRows {
   484  		return result, nil
   485  	}
   486  	return result, err
   487  }
   488  
   489  func (db *Database) GetProfilePicturesVisibility() (result int, err error) {
   490  	err = db.makeSelectRow(ProfilePicturesVisibility).Scan(&result)
   491  	if err == sql.ErrNoRows {
   492  		return result, nil
   493  	}
   494  	return result, err
   495  }
   496  
   497  func (db *Database) GetPublicKey() (string, error) {
   498  	return db.makeSelectString(PublicKey)
   499  }
   500  
   501  func (db *Database) GetFleet() (string, error) {
   502  	return db.makeSelectString(Fleet)
   503  }
   504  
   505  func (db *Database) GetDappsAddress() (rst types.Address, err error) {
   506  	err = db.makeSelectRow(DappsAddress).Scan(&rst)
   507  	if err == sql.ErrNoRows {
   508  		return rst, nil
   509  	}
   510  	return
   511  }
   512  
   513  func (db *Database) GetPinnedMailservers() (rst map[string]string, err error) {
   514  	rst = make(map[string]string)
   515  	var pinnedMailservers string
   516  	err = db.db.QueryRow("SELECT COALESCE(pinned_mailservers, '') FROM settings WHERE synthetic_id = 'id'").Scan(&pinnedMailservers)
   517  	if err == sql.ErrNoRows || pinnedMailservers == "" {
   518  		return rst, nil
   519  	}
   520  
   521  	err = json.Unmarshal([]byte(pinnedMailservers), &rst)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  	return
   526  }
   527  
   528  func (db *Database) CanUseMailservers() (result bool, err error) {
   529  	err = db.makeSelectRow(UseMailservers).Scan(&result)
   530  	if err == sql.ErrNoRows {
   531  		return result, nil
   532  	}
   533  	return result, err
   534  }
   535  
   536  func (db *Database) CanSyncOnMobileNetwork() (result bool, err error) {
   537  	err = db.makeSelectRow(SyncingOnMobileNetwork).Scan(&result)
   538  	if err == sql.ErrNoRows {
   539  		return result, nil
   540  	}
   541  	return result, err
   542  }
   543  
   544  func (db *Database) GetDefaultSyncPeriod() (result uint32, err error) {
   545  	err = db.makeSelectRow(DefaultSyncPeriod).Scan(&result)
   546  	if err == sql.ErrNoRows {
   547  		return result, nil
   548  	}
   549  	return result, err
   550  }
   551  
   552  func (db *Database) GetMessagesFromContactsOnly() (result bool, err error) {
   553  	err = db.makeSelectRow(MessagesFromContactsOnly).Scan(&result)
   554  	if err == sql.ErrNoRows {
   555  		return result, nil
   556  	}
   557  	return result, err
   558  }
   559  
   560  func (db *Database) GetProfilePicturesShowTo() (result int64, err error) {
   561  	err = db.makeSelectRow(ProfilePicturesShowTo).Scan(&result)
   562  	if err == sql.ErrNoRows {
   563  		return result, nil
   564  	}
   565  	return result, err
   566  }
   567  
   568  func (db *Database) GetLatestDerivedPath() (result uint, err error) {
   569  	err = db.makeSelectRow(LatestDerivedPath).Scan(&result)
   570  	return
   571  }
   572  
   573  func (db *Database) GetCurrentStatus(status interface{}) error {
   574  	err := db.makeSelectRow(CurrentUserStatus).Scan(&sqlite.JSONBlob{Data: &status})
   575  	if err == sql.ErrNoRows {
   576  		return nil
   577  	}
   578  	return err
   579  }
   580  
   581  func (db *Database) ShouldBroadcastUserStatus() (result bool, err error) {
   582  	err = db.makeSelectRow(SendStatusUpdates).Scan(&result)
   583  	// If the `send_status_updates` value is nil the sql.ErrNoRows will be returned
   584  	// because this feature is opt out, `true` should be returned in the case where no value is found
   585  	if err == sql.ErrNoRows {
   586  		return true, nil
   587  	}
   588  	return result, err
   589  }
   590  
   591  func (db *Database) BackupEnabled() (result bool, err error) {
   592  	err = db.makeSelectRow(BackupEnabled).Scan(&result)
   593  	if err == sql.ErrNoRows {
   594  		return true, nil
   595  	}
   596  	return result, err
   597  }
   598  
   599  func (db *Database) AutoMessageEnabled() (result bool, err error) {
   600  	err = db.makeSelectRow(AutoMessageEnabled).Scan(&result)
   601  	if err == sql.ErrNoRows {
   602  		return true, nil
   603  	}
   604  	return result, err
   605  }
   606  
   607  func (db *Database) LastBackup() (result uint64, err error) {
   608  	err = db.makeSelectRow(LastBackup).Scan(&result)
   609  	if err == sql.ErrNoRows {
   610  		return 0, nil
   611  	}
   612  	return result, err
   613  }
   614  
   615  func (db *Database) SetLastBackup(time uint64) error {
   616  	return db.SaveSettingField(LastBackup, time)
   617  }
   618  
   619  func (db *Database) SetBackupFetched(fetched bool) error {
   620  	return db.SaveSettingField(BackupFetched, fetched)
   621  }
   622  
   623  func (db *Database) BackupFetched() (result bool, err error) {
   624  	err = db.makeSelectRow(BackupFetched).Scan(&result)
   625  	if err == sql.ErrNoRows {
   626  		return true, nil
   627  	}
   628  	return result, err
   629  }
   630  
   631  func (db *Database) ENSName() (string, error) {
   632  	return db.makeSelectString(PreferredName)
   633  }
   634  
   635  func (db *Database) DeviceName() (string, error) {
   636  	return db.makeSelectString(DeviceName)
   637  }
   638  
   639  func (db *Database) DisplayName() (string, error) {
   640  	return db.makeSelectString(DisplayName)
   641  }
   642  
   643  func (db *Database) Bio() (string, error) {
   644  	return db.makeSelectString(Bio)
   645  }
   646  
   647  func (db *Database) Mnemonic() (string, error) {
   648  	return db.makeSelectString(Mnemonic)
   649  }
   650  
   651  func (db *Database) MnemonicRemoved() (result bool, err error) {
   652  	err = db.makeSelectRow(MnemonicRemoved).Scan(&result)
   653  	if err == sql.ErrNoRows {
   654  		return result, nil
   655  	}
   656  	return result, err
   657  }
   658  
   659  func (db *Database) GetMnemonicWasNotShown() (result bool, err error) {
   660  	err = db.makeSelectRow(MnemonicWasNotShown).Scan(&result)
   661  	if err == sql.ErrNoRows {
   662  		return result, nil
   663  	}
   664  	return result, err
   665  }
   666  
   667  func (db *Database) GifAPIKey() (string, error) {
   668  	return db.makeSelectString(GifAPIKey)
   669  }
   670  
   671  func (db *Database) MutualContactEnabled() (result bool, err error) {
   672  	err = db.makeSelectRow(MutualContactEnabled).Scan(&result)
   673  	return result, err
   674  }
   675  
   676  func (db *Database) GifRecents() (recents json.RawMessage, err error) {
   677  	err = db.makeSelectRow(GifRecents).Scan(&sqlite.JSONBlob{Data: &recents})
   678  	if err == sql.ErrNoRows {
   679  		return nil, err
   680  	}
   681  	return recents, nil
   682  }
   683  
   684  func (db *Database) GifFavorites() (favorites json.RawMessage, err error) {
   685  	err = db.makeSelectRow(GifFavourites).Scan(&sqlite.JSONBlob{Data: &favorites})
   686  	if err == sql.ErrNoRows {
   687  		return nil, err
   688  	}
   689  	return favorites, nil
   690  }
   691  
   692  func (db *Database) GetPreferredUsername() (string, error) {
   693  	return db.makeSelectString(PreferredName)
   694  }
   695  
   696  func (db *Database) GetCurrency() (string, error) {
   697  	return db.makeSelectString(Currency)
   698  }
   699  
   700  func (db *Database) GetInstalledStickerPacks() (rst *json.RawMessage, err error) {
   701  	err = db.makeSelectRow(StickersPacksInstalled).Scan(&rst)
   702  	return
   703  }
   704  
   705  func (db *Database) GetPendingStickerPacks() (rst *json.RawMessage, err error) {
   706  	err = db.makeSelectRow(StickersPacksPending).Scan(&rst)
   707  	return
   708  }
   709  
   710  func (db *Database) GetRecentStickers() (rst *json.RawMessage, err error) {
   711  	err = db.makeSelectRow(StickersRecentStickers).Scan(&rst)
   712  	return
   713  }
   714  
   715  func (db *Database) SetPinnedMailservers(mailservers map[string]string) error {
   716  	return db.SaveSettingField(PinnedMailservers, mailservers)
   717  }
   718  
   719  func (db *Database) SetUseMailservers(value bool) error {
   720  	return db.SaveSettingField(UseMailservers, value)
   721  }
   722  
   723  func (db *Database) GetWalletRootAddress() (rst types.Address, err error) {
   724  	err = db.makeSelectRow(WalletRootAddress).Scan(&rst)
   725  	if err == sql.ErrNoRows {
   726  		return rst, nil
   727  	}
   728  	return
   729  }
   730  
   731  func (db *Database) GetEIP1581Address() (rst types.Address, err error) {
   732  	err = db.makeSelectRow(EIP1581Address).Scan(&rst)
   733  	if err == sql.ErrNoRows {
   734  		return rst, nil
   735  	}
   736  	return
   737  }
   738  
   739  func (db *Database) GetMasterAddress() (rst types.Address, err error) {
   740  	err = db.makeSelectRow(MasterAddress).Scan(&rst)
   741  	if err == sql.ErrNoRows {
   742  		return rst, nil
   743  	}
   744  	return
   745  }
   746  
   747  func (db *Database) GetTestNetworksEnabled() (result bool, err error) {
   748  	err = db.makeSelectRow(TestNetworksEnabled).Scan(&result)
   749  	if err == sql.ErrNoRows {
   750  		return result, nil
   751  	}
   752  	return result, err
   753  }
   754  
   755  func (db *Database) GetIsGoerliEnabled() (result bool, err error) {
   756  	err = db.makeSelectRow(IsGoerliEnabled).Scan(&result)
   757  	if err == sql.ErrNoRows {
   758  		return result, nil
   759  	}
   760  	return result, err
   761  }
   762  
   763  func (db *Database) SetPeerSyncingEnabled(value bool) error {
   764  	return db.SaveSettingField(PeerSyncingEnabled, value)
   765  }
   766  
   767  func (db *Database) SetSyncingOnMobileNetwork(value bool) error {
   768  	err := db.SaveSettingField(SyncingOnMobileNetwork, value)
   769  	if err != nil {
   770  		return err
   771  	}
   772  	return db.SaveSettingField(RememberSyncingChoice, true)
   773  }
   774  
   775  func (db *Database) GetPeerSyncingEnabled() (result bool, err error) {
   776  	err = db.makeSelectRow(PeerSyncingEnabled).Scan(&result)
   777  	if err == sql.ErrNoRows {
   778  		return result, nil
   779  	}
   780  	return result, err
   781  }
   782  
   783  func (db *Database) GetTokenGroupByCommunity() (result bool, err error) {
   784  	err = db.makeSelectRow(TokenGroupByCommunity).Scan(&result)
   785  	if err == sql.ErrNoRows {
   786  		return result, nil
   787  	}
   788  	return result, err
   789  }
   790  
   791  func (db *Database) SetTokenGroupByCommunity(value bool) error {
   792  	return db.SaveSettingField(TokenGroupByCommunity, value)
   793  }
   794  
   795  func (db *Database) GetCollectibleGroupByCollection() (result bool, err error) {
   796  	err = db.makeSelectRow(CollectibleGroupByCollection).Scan(&result)
   797  	if err == sql.ErrNoRows {
   798  		return result, nil
   799  	}
   800  	return result, err
   801  }
   802  
   803  func (db *Database) SetCollectibleGroupByCollection(value bool) error {
   804  	return db.SaveSettingField(CollectibleGroupByCollection, value)
   805  }
   806  
   807  func (db *Database) GetCollectibleGroupByCommunity() (result bool, err error) {
   808  	err = db.makeSelectRow(CollectibleGroupByCommunity).Scan(&result)
   809  	if err == sql.ErrNoRows {
   810  		return result, nil
   811  	}
   812  	return result, err
   813  }
   814  
   815  func (db *Database) SetCollectibleGroupByCommunity(value bool) error {
   816  	return db.SaveSettingField(CollectibleGroupByCommunity, value)
   817  }
   818  
   819  func (db *Database) GetTelemetryServerURL() (string, error) {
   820  	return db.makeSelectString(TelemetryServerURL)
   821  }
   822  
   823  func (db *Database) ProfileMigrationNeeded() (result bool, err error) {
   824  	err = db.makeSelectRow(ProfileMigrationNeeded).Scan(&result)
   825  	return result, err
   826  }
   827  
   828  func (db *Database) URLUnfurlingMode() (result int64, err error) {
   829  	err = db.makeSelectRow(URLUnfurlingMode).Scan(&result)
   830  	if err == sql.ErrNoRows {
   831  		return result, nil
   832  	}
   833  	return result, err
   834  }
   835  
   836  func (db *Database) SubscribeToChanges() chan *SyncSettingField {
   837  	s := make(chan *SyncSettingField, 100)
   838  	db.changesSubscriptions = append(db.changesSubscriptions, s)
   839  	return s
   840  }
   841  
   842  func (db *Database) postChangesToSubscribers(change *SyncSettingField) {
   843  	// Publish on channels, drop if buffer is full
   844  	for _, s := range db.changesSubscriptions {
   845  		select {
   846  		case s <- change:
   847  		default:
   848  			log.Warn("settings changes subscription channel full, dropping message")
   849  		}
   850  	}
   851  }
   852  
   853  func (db *Database) MnemonicWasShown() error {
   854  	return db.SaveSettingField(MnemonicWasNotShown, false)
   855  }