github.com/status-im/status-go@v1.1.0/services/mailservers/database.go (about)

     1  package mailservers
     2  
     3  import (
     4  	"database/sql"
     5  	"database/sql/driver"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/libp2p/go-libp2p/core/peer"
    13  	"github.com/multiformats/go-multiaddr"
    14  
    15  	"github.com/waku-org/go-waku/waku/v2/protocol/enr"
    16  	"github.com/waku-org/go-waku/waku/v2/utils"
    17  
    18  	"github.com/ethereum/go-ethereum/p2p/enode"
    19  
    20  	"github.com/status-im/status-go/protocol/transport"
    21  )
    22  
    23  func MustDecodeENR(enrStr string) *enode.Node {
    24  	node, err := enode.Parse(enode.ValidSchemes, enrStr)
    25  	if err != nil || node == nil {
    26  		panic("could not decode enr: " + enrStr)
    27  	}
    28  	return node
    29  }
    30  
    31  func MustDecodeMultiaddress(multiaddrsStr string) *multiaddr.Multiaddr {
    32  	maddr, err := multiaddr.NewMultiaddr(multiaddrsStr)
    33  	if err != nil || maddr == nil {
    34  		panic("could not decode multiaddr: " + multiaddrsStr)
    35  	}
    36  	return &maddr
    37  }
    38  
    39  type Mailserver struct {
    40  	ID     string               `json:"id"`
    41  	Name   string               `json:"name"`
    42  	Custom bool                 `json:"custom"`
    43  	ENR    *enode.Node          `json:"enr"`
    44  	Addr   *multiaddr.Multiaddr `json:"addr"`
    45  
    46  	// Deprecated: only used with WakuV1
    47  	Password       string `json:"password,omitempty"`
    48  	Fleet          string `json:"fleet"`
    49  	FailedRequests uint   `json:"-"`
    50  }
    51  
    52  func (m Mailserver) PeerInfo() (*peer.AddrInfo, error) {
    53  	var maddrs []multiaddr.Multiaddr
    54  
    55  	if m.ENR != nil {
    56  		addrInfo, err := enr.EnodeToPeerInfo(m.ENR)
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  		addrInfo.Addrs = utils.EncapsulatePeerID(addrInfo.ID, addrInfo.Addrs...)
    61  		maddrs = append(maddrs, addrInfo.Addrs...)
    62  	}
    63  
    64  	if m.Addr != nil {
    65  		maddrs = append(maddrs, *m.Addr)
    66  	}
    67  
    68  	p, err := peer.AddrInfosFromP2pAddrs(maddrs...)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	if len(p) != 1 {
    74  		return nil, errors.New("invalid mailserver setup")
    75  	}
    76  
    77  	return &p[0], nil
    78  }
    79  
    80  func (m Mailserver) PeerID() (peer.ID, error) {
    81  	p, err := m.PeerInfo()
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  	return p.ID, nil
    86  }
    87  
    88  func (m Mailserver) nullablePassword() (val sql.NullString) {
    89  	if m.Password != "" {
    90  		val.String = m.Password
    91  		val.Valid = true
    92  	}
    93  	return
    94  }
    95  
    96  type MailserverRequestGap struct {
    97  	ID     string `json:"id"`
    98  	ChatID string `json:"chatId"`
    99  	From   uint64 `json:"from"`
   100  	To     uint64 `json:"to"`
   101  }
   102  
   103  type MailserverTopic struct {
   104  	PubsubTopic  string   `json:"pubsubTopic"`
   105  	ContentTopic string   `json:"topic"`
   106  	Discovery    bool     `json:"discovery?"`
   107  	Negotiated   bool     `json:"negotiated?"`
   108  	ChatIDs      []string `json:"chat-ids"`
   109  	LastRequest  int      `json:"last-request"` // default is 1
   110  }
   111  
   112  type ChatRequestRange struct {
   113  	ChatID            string `json:"chat-id"`
   114  	LowestRequestFrom int    `json:"lowest-request-from"`
   115  	HighestRequestTo  int    `json:"highest-request-to"`
   116  }
   117  
   118  // sqlStringSlice helps to serialize a slice of strings into a single column using JSON serialization.
   119  type sqlStringSlice []string
   120  
   121  // Scan implements the Scanner interface.
   122  func (ss *sqlStringSlice) Scan(value interface{}) error {
   123  	if value == nil {
   124  		*ss = nil
   125  		return nil
   126  	}
   127  	src, ok := value.([]byte)
   128  	if !ok {
   129  		return errors.New("invalid value type, expected byte slice")
   130  	}
   131  	return json.Unmarshal(src, ss)
   132  }
   133  
   134  // Value implements the driver Valuer interface.
   135  func (ss sqlStringSlice) Value() (driver.Value, error) {
   136  	return json.Marshal(ss)
   137  }
   138  
   139  // Database sql wrapper for operations with mailserver objects.
   140  type Database struct {
   141  	db *sql.DB
   142  }
   143  
   144  func NewDB(db *sql.DB) *Database {
   145  	return &Database{db: db}
   146  }
   147  
   148  func (d *Database) Add(mailserver Mailserver) error {
   149  	// TODO: we are only storing the multiaddress.
   150  	// In a future PR we must allow storing multiple multiaddresses and ENR
   151  	_, err := d.db.Exec(`INSERT OR REPLACE INTO mailservers(
   152  			id,
   153  			name,
   154  			address,
   155  			password,
   156  			fleet
   157  		) VALUES (?, ?, ?, ?, ?)`,
   158  		mailserver.ID,
   159  		mailserver.Name,
   160  		(*mailserver.Addr).String(),
   161  		mailserver.nullablePassword(),
   162  		mailserver.Fleet,
   163  	)
   164  	return err
   165  }
   166  
   167  func (d *Database) Mailservers() ([]Mailserver, error) {
   168  	rows, err := d.db.Query(`SELECT id, name, address, password, fleet FROM mailservers`)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	defer rows.Close()
   173  	return toMailservers(rows)
   174  }
   175  
   176  func toMailservers(rows *sql.Rows) ([]Mailserver, error) {
   177  	var result []Mailserver
   178  
   179  	for rows.Next() {
   180  		var (
   181  			m        Mailserver
   182  			addrStr  string
   183  			password sql.NullString
   184  		)
   185  		if err := rows.Scan(
   186  			&m.ID,
   187  			&m.Name,
   188  			&addrStr,
   189  			&password,
   190  			&m.Fleet,
   191  		); err != nil {
   192  			return nil, err
   193  		}
   194  		m.Custom = true
   195  		if password.Valid {
   196  			m.Password = password.String
   197  		}
   198  
   199  		// TODO: we are only storing the multiaddress.
   200  		// In a future PR we must allow storing multiple multiaddresses and ENR
   201  		maddr, err := multiaddr.NewMultiaddr(addrStr)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  		m.Addr = &maddr
   206  
   207  		result = append(result, m)
   208  	}
   209  
   210  	return result, nil
   211  }
   212  
   213  func (d *Database) Delete(id string) error {
   214  	_, err := d.db.Exec(`DELETE FROM mailservers WHERE id = ?`, id)
   215  	return err
   216  }
   217  
   218  func (d *Database) AddGaps(gaps []MailserverRequestGap) error {
   219  	tx, err := d.db.Begin()
   220  	if err != nil {
   221  		return err
   222  	}
   223  	defer func() {
   224  		if err == nil {
   225  			err = tx.Commit()
   226  			return
   227  		}
   228  		_ = tx.Rollback()
   229  	}()
   230  
   231  	for _, gap := range gaps {
   232  
   233  		_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_request_gaps(
   234  				id,
   235  				chat_id,
   236  				gap_from,
   237  				gap_to
   238  			) VALUES (?, ?, ?, ?)`,
   239  			gap.ID,
   240  			gap.ChatID,
   241  			gap.From,
   242  			gap.To,
   243  		)
   244  		if err != nil {
   245  			return err
   246  		}
   247  
   248  	}
   249  	return nil
   250  }
   251  
   252  func (d *Database) RequestGaps(chatID string) ([]MailserverRequestGap, error) {
   253  	var result []MailserverRequestGap
   254  
   255  	rows, err := d.db.Query(`SELECT id, chat_id, gap_from, gap_to FROM mailserver_request_gaps WHERE chat_id = ?`, chatID)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	defer rows.Close()
   260  
   261  	for rows.Next() {
   262  		var m MailserverRequestGap
   263  		if err := rows.Scan(
   264  			&m.ID,
   265  			&m.ChatID,
   266  			&m.From,
   267  			&m.To,
   268  		); err != nil {
   269  			return nil, err
   270  		}
   271  		result = append(result, m)
   272  	}
   273  
   274  	return result, nil
   275  }
   276  
   277  func (d *Database) DeleteGaps(ids []string) error {
   278  	if len(ids) == 0 {
   279  		return nil
   280  	}
   281  	inVector := strings.Repeat("?, ", len(ids)-1) + "?"
   282  	query := fmt.Sprintf(`DELETE FROM mailserver_request_gaps WHERE id IN (%s)`, inVector) // nolint: gosec
   283  	idsArgs := make([]interface{}, 0, len(ids))
   284  	for _, id := range ids {
   285  		idsArgs = append(idsArgs, id)
   286  	}
   287  
   288  	_, err := d.db.Exec(query, idsArgs...)
   289  	return err
   290  }
   291  
   292  func (d *Database) DeleteGapsByChatID(chatID string) error {
   293  	_, err := d.db.Exec(`DELETE FROM mailserver_request_gaps WHERE chat_id = ?`, chatID)
   294  	return err
   295  }
   296  
   297  func (d *Database) AddTopic(topic MailserverTopic) error {
   298  
   299  	chatIDs := sqlStringSlice(topic.ChatIDs)
   300  	_, err := d.db.Exec(`INSERT OR REPLACE INTO mailserver_topics(
   301  			pubsub_topic,
   302  			topic,
   303  			chat_ids,
   304  			last_request,
   305  			discovery,
   306  			negotiated
   307  		) VALUES (?, ?, ?, ?, ?, ?)`,
   308  		topic.PubsubTopic,
   309  		topic.ContentTopic,
   310  		chatIDs,
   311  		topic.LastRequest,
   312  		topic.Discovery,
   313  		topic.Negotiated,
   314  	)
   315  	return err
   316  }
   317  
   318  func (d *Database) AddTopics(topics []MailserverTopic) (err error) {
   319  	var tx *sql.Tx
   320  	tx, err = d.db.Begin()
   321  	if err != nil {
   322  		return
   323  	}
   324  	defer func() {
   325  		if err == nil {
   326  			err = tx.Commit()
   327  			return
   328  		}
   329  		_ = tx.Rollback()
   330  	}()
   331  
   332  	for _, topic := range topics {
   333  		chatIDs := sqlStringSlice(topic.ChatIDs)
   334  		_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_topics(
   335  			  pubsub_topic,
   336  			  topic,
   337  			  chat_ids,
   338  			  last_request,
   339  			  discovery,
   340  			  negotiated
   341  		  ) VALUES (?, ?, ?, ?, ?, ?)`,
   342  			topic.PubsubTopic,
   343  			topic.ContentTopic,
   344  			chatIDs,
   345  			topic.LastRequest,
   346  			topic.Discovery,
   347  			topic.Negotiated,
   348  		)
   349  		if err != nil {
   350  			return
   351  		}
   352  	}
   353  	return
   354  }
   355  
   356  func (d *Database) Topics() ([]MailserverTopic, error) {
   357  	var result []MailserverTopic
   358  
   359  	rows, err := d.db.Query(`SELECT pubsub_topic, topic, chat_ids, last_request,discovery,negotiated FROM mailserver_topics`)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	defer rows.Close()
   364  
   365  	for rows.Next() {
   366  		var (
   367  			t       MailserverTopic
   368  			chatIDs sqlStringSlice
   369  		)
   370  		if err := rows.Scan(
   371  			&t.PubsubTopic,
   372  			&t.ContentTopic,
   373  			&chatIDs,
   374  			&t.LastRequest,
   375  			&t.Discovery,
   376  			&t.Negotiated,
   377  		); err != nil {
   378  			return nil, err
   379  		}
   380  		t.ChatIDs = chatIDs
   381  		result = append(result, t)
   382  	}
   383  
   384  	return result, nil
   385  }
   386  
   387  func (d *Database) ResetLastRequest(pubsubTopic, contentTopic string) error {
   388  	_, err := d.db.Exec("UPDATE mailserver_topics SET last_request = 0 WHERE pubsub_topic = ? AND topic = ?", pubsubTopic, contentTopic)
   389  	return err
   390  }
   391  
   392  func (d *Database) DeleteTopic(pubsubTopic, contentTopic string) error {
   393  	_, err := d.db.Exec(`DELETE FROM mailserver_topics WHERE pubsub_topic = ? AND topic = ?`, pubsubTopic, contentTopic)
   394  	return err
   395  }
   396  
   397  // SetTopics deletes all topics excepts the one set, or upsert those if
   398  // missing
   399  func (d *Database) SetTopics(filters []*transport.Filter) (err error) {
   400  	var tx *sql.Tx
   401  	tx, err = d.db.Begin()
   402  	if err != nil {
   403  		return err
   404  	}
   405  	defer func() {
   406  		if err == nil {
   407  			err = tx.Commit()
   408  			return
   409  		}
   410  		_ = tx.Rollback()
   411  	}()
   412  
   413  	if len(filters) == 0 {
   414  		return nil
   415  	}
   416  
   417  	contentTopicsPerPubsubTopic := make(map[string]map[string]struct{})
   418  	for _, filter := range filters {
   419  		contentTopics, ok := contentTopicsPerPubsubTopic[filter.PubsubTopic]
   420  		if !ok {
   421  			contentTopics = make(map[string]struct{})
   422  		}
   423  		contentTopics[filter.ContentTopic.String()] = struct{}{}
   424  		contentTopicsPerPubsubTopic[filter.PubsubTopic] = contentTopics
   425  	}
   426  
   427  	for pubsubTopic, contentTopics := range contentTopicsPerPubsubTopic {
   428  		topicsArgs := make([]interface{}, 0, len(contentTopics)+1)
   429  		topicsArgs = append(topicsArgs, pubsubTopic)
   430  		for ct := range contentTopics {
   431  			topicsArgs = append(topicsArgs, ct)
   432  		}
   433  
   434  		inVector := strings.Repeat("?, ", len(contentTopics)-1) + "?"
   435  
   436  		// Delete topics
   437  		query := "DELETE FROM mailserver_topics WHERE pubsub_topic = ? AND topic NOT IN (" + inVector + ")" // nolint: gosec
   438  		_, err = tx.Exec(query, topicsArgs...)
   439  	}
   440  
   441  	// Default to now - 1.day
   442  	lastRequest := (time.Now().Add(-24 * time.Hour)).Unix()
   443  	// Insert if not existing
   444  	for _, filter := range filters {
   445  		// fetch
   446  		var topic string
   447  		err = tx.QueryRow(`SELECT topic FROM mailserver_topics WHERE topic = ? AND pubsub_topic = ?`, filter.ContentTopic.String(), filter.PubsubTopic).Scan(&topic)
   448  		if err != nil && err != sql.ErrNoRows {
   449  			return
   450  		} else if err == sql.ErrNoRows {
   451  			// we insert the topic
   452  			_, err = tx.Exec(`INSERT INTO mailserver_topics(topic,pubsub_topic,last_request,discovery,negotiated) VALUES (?,?,?,?,?)`, filter.ContentTopic.String(), filter.PubsubTopic, lastRequest, filter.Discovery, filter.Negotiated)
   453  		}
   454  		if err != nil {
   455  			return
   456  		}
   457  	}
   458  
   459  	return
   460  }
   461  
   462  func (d *Database) AddChatRequestRange(req ChatRequestRange) error {
   463  	_, err := d.db.Exec(`INSERT OR REPLACE INTO mailserver_chat_request_ranges(
   464  			chat_id,
   465  			lowest_request_from,
   466  			highest_request_to
   467  		) VALUES (?, ?, ?)`,
   468  		req.ChatID,
   469  		req.LowestRequestFrom,
   470  		req.HighestRequestTo,
   471  	)
   472  	return err
   473  }
   474  
   475  func (d *Database) AddChatRequestRanges(reqs []ChatRequestRange) (err error) {
   476  	var tx *sql.Tx
   477  	tx, err = d.db.Begin()
   478  	if err != nil {
   479  		return err
   480  	}
   481  	defer func() {
   482  		if err == nil {
   483  			err = tx.Commit()
   484  			return
   485  		}
   486  		_ = tx.Rollback()
   487  	}()
   488  	for _, req := range reqs {
   489  
   490  		_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_chat_request_ranges(
   491  			chat_id,
   492  			lowest_request_from,
   493  			highest_request_to
   494  		) VALUES (?, ?, ?)`,
   495  			req.ChatID,
   496  			req.LowestRequestFrom,
   497  			req.HighestRequestTo,
   498  		)
   499  		if err != nil {
   500  			return
   501  		}
   502  	}
   503  	return
   504  }
   505  
   506  func (d *Database) ChatRequestRanges() ([]ChatRequestRange, error) {
   507  	var result []ChatRequestRange
   508  
   509  	rows, err := d.db.Query(`SELECT chat_id, lowest_request_from, highest_request_to FROM mailserver_chat_request_ranges`)
   510  	if err != nil {
   511  		return nil, err
   512  	}
   513  	defer rows.Close()
   514  
   515  	for rows.Next() {
   516  		var req ChatRequestRange
   517  		if err := rows.Scan(
   518  			&req.ChatID,
   519  			&req.LowestRequestFrom,
   520  			&req.HighestRequestTo,
   521  		); err != nil {
   522  			return nil, err
   523  		}
   524  		result = append(result, req)
   525  	}
   526  
   527  	return result, nil
   528  }
   529  
   530  func (d *Database) DeleteChatRequestRange(chatID string) error {
   531  	_, err := d.db.Exec(`DELETE FROM mailserver_chat_request_ranges WHERE chat_id = ?`, chatID)
   532  	return err
   533  }