github.com/status-im/status-go@v1.1.0/protocol/communities/persistence.go (about)

     1  package communities
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"database/sql"
     7  	"errors"
     8  	"fmt"
     9  	"math/big"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  
    16  	"github.com/status-im/status-go/eth-node/crypto"
    17  	"github.com/status-im/status-go/eth-node/types"
    18  	"github.com/status-im/status-go/protocol/common"
    19  	"github.com/status-im/status-go/protocol/common/shard"
    20  	"github.com/status-im/status-go/protocol/communities/token"
    21  	"github.com/status-im/status-go/protocol/encryption"
    22  	"github.com/status-im/status-go/protocol/protobuf"
    23  	"github.com/status-im/status-go/services/wallet/bigint"
    24  )
    25  
    26  type Persistence struct {
    27  	db *sql.DB
    28  
    29  	recordBundleToCommunity func(*CommunityRecordBundle) (*Community, error)
    30  }
    31  
    32  var ErrOldRequestToJoin = errors.New("old request to join")
    33  var ErrOldRequestToLeave = errors.New("old request to leave")
    34  var ErrOldShardInfo = errors.New("old shard info")
    35  
    36  type CommunityRecord struct {
    37  	id           []byte
    38  	privateKey   []byte
    39  	controlNode  []byte
    40  	description  []byte
    41  	joined       bool
    42  	joinedAt     int64
    43  	verified     bool
    44  	spectated    bool
    45  	muted        bool
    46  	mutedTill    time.Time
    47  	shardCluster *uint
    48  	shardIndex   *uint
    49  	lastOpenedAt int64
    50  }
    51  
    52  type EventsRecord struct {
    53  	id             []byte
    54  	rawEvents      []byte
    55  	rawDescription []byte
    56  }
    57  
    58  type RequestToJoinRecord struct {
    59  	id          []byte
    60  	publicKey   string
    61  	clock       int
    62  	ensName     string
    63  	chatID      string
    64  	communityID []byte
    65  	state       int
    66  }
    67  
    68  type CommunityRecordBundle struct {
    69  	community      *CommunityRecord
    70  	events         *EventsRecord
    71  	requestToJoin  *RequestToJoinRecord
    72  	installationID *string
    73  }
    74  
    75  type EncryptionKeysRequestRecord struct {
    76  	communityID    []byte
    77  	channelID      string
    78  	requestedAt    int64
    79  	requestedCount uint
    80  }
    81  
    82  const OR = " OR "
    83  const communitiesBaseQuery = `
    84  	SELECT
    85  		c.id, c.private_key, c.control_node, c.description, c.joined, c.joined_at, c.last_opened_at, c.spectated, c.verified, c.muted, c.muted_till,
    86  		csd.shard_cluster, csd.shard_index,
    87  		r.id, r.public_key, r.clock, r.ens_name, r.chat_id, r.state,
    88  		ae.raw_events, ae.raw_description,
    89  		ccn.installation_id
    90  	FROM communities_communities c
    91  	LEFT JOIN communities_shards csd ON c.id = csd.community_id
    92  	LEFT JOIN communities_requests_to_join r ON c.id = r.community_id AND r.public_key = ?
    93  	LEFT JOIN communities_events ae ON c.id = ae.id
    94  	LEFT JOIN communities_control_node ccn ON c.id = ccn.community_id`
    95  
    96  func scanCommunity(scanner func(dest ...any) error) (*CommunityRecordBundle, error) {
    97  	r := &CommunityRecordBundle{
    98  		community:      &CommunityRecord{},
    99  		events:         nil,
   100  		requestToJoin:  nil,
   101  		installationID: nil,
   102  	}
   103  
   104  	var mutedTill sql.NullTime
   105  	var cluster, index sql.NullInt64
   106  
   107  	var requestToJoinID []byte
   108  	var requestToJoinPublicKey, requestToJoinENSName, requestToJoinChatID sql.NullString
   109  	var requestToJoinClock, requestToJoinState sql.NullInt64
   110  
   111  	var events, eventsDescription []byte
   112  
   113  	var installationID sql.NullString
   114  
   115  	err := scanner(
   116  		&r.community.id,
   117  		&r.community.privateKey,
   118  		&r.community.controlNode,
   119  		&r.community.description,
   120  		&r.community.joined,
   121  		&r.community.joinedAt,
   122  		&r.community.lastOpenedAt,
   123  		&r.community.spectated,
   124  		&r.community.verified,
   125  		&r.community.muted,
   126  		&mutedTill,
   127  		&cluster,
   128  		&index,
   129  
   130  		&requestToJoinID,
   131  		&requestToJoinPublicKey,
   132  		&requestToJoinClock,
   133  		&requestToJoinENSName,
   134  		&requestToJoinChatID,
   135  		&requestToJoinState,
   136  
   137  		&events,
   138  		&eventsDescription,
   139  
   140  		&installationID,
   141  	)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	if mutedTill.Valid {
   147  		r.community.mutedTill = mutedTill.Time
   148  	}
   149  	if cluster.Valid {
   150  		clusterValue := uint(cluster.Int64)
   151  		r.community.shardCluster = &clusterValue
   152  	}
   153  	if index.Valid {
   154  		shardIndexValue := uint(index.Int64)
   155  		r.community.shardIndex = &shardIndexValue
   156  	}
   157  
   158  	if requestToJoinID != nil {
   159  		r.requestToJoin = &RequestToJoinRecord{
   160  			id:          requestToJoinID,
   161  			publicKey:   requestToJoinPublicKey.String,
   162  			clock:       int(requestToJoinClock.Int64),
   163  			ensName:     requestToJoinENSName.String,
   164  			chatID:      requestToJoinChatID.String,
   165  			communityID: r.community.id,
   166  			state:       int(requestToJoinState.Int64),
   167  		}
   168  	}
   169  
   170  	if events != nil {
   171  		r.events = &EventsRecord{
   172  			id:             r.community.id,
   173  			rawEvents:      events,
   174  			rawDescription: eventsDescription,
   175  		}
   176  	}
   177  
   178  	if installationID.Valid {
   179  		r.installationID = &installationID.String
   180  	}
   181  
   182  	return r, nil
   183  }
   184  
   185  func (p *Persistence) saveCommunity(r *CommunityRecord) error {
   186  	_, err := p.db.Exec(`
   187          INSERT INTO communities_communities (
   188              id, private_key, control_node, description,
   189              joined, joined_at, spectated, verified, muted, muted_till, last_opened_at
   190          ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
   191  		r.id, r.privateKey, r.controlNode, r.description,
   192  		r.joined, r.joinedAt, r.spectated, r.verified, r.muted, r.mutedTill, r.lastOpenedAt)
   193  	return err
   194  }
   195  
   196  func (p *Persistence) SaveCommunity(community *Community) error {
   197  	record, err := communityToRecord(community)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	return p.saveCommunity(record)
   202  }
   203  
   204  func (p *Persistence) DeleteCommunityEvents(id types.HexBytes) error {
   205  	_, err := p.db.Exec(`DELETE FROM communities_events WHERE id = ?;`, id)
   206  	return err
   207  }
   208  
   209  func (p *Persistence) saveCommunityEvents(r *EventsRecord) error {
   210  	_, err := p.db.Exec(`
   211  		INSERT INTO communities_events (
   212  			id, raw_events, raw_description
   213  		) VALUES (?, ?, ?);`,
   214  		r.id, r.rawEvents, r.rawDescription)
   215  	return err
   216  }
   217  
   218  func (p *Persistence) SaveCommunityEvents(community *Community) error {
   219  	record, err := communityToEventsRecord(community)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	return p.saveCommunityEvents(record)
   224  }
   225  
   226  func (p *Persistence) DeleteCommunity(id types.HexBytes) error {
   227  	_, err := p.db.Exec(`DELETE FROM communities_communities WHERE id = ?;
   228  						 DELETE FROM communities_events WHERE id = ?;
   229  						 DELETE FROM communities_shards WHERE community_id = ?`, id, id, id)
   230  	return err
   231  }
   232  
   233  func (p *Persistence) ShouldHandleSyncCommunitySettings(settings *protobuf.SyncCommunitySettings) (bool, error) {
   234  
   235  	qr := p.db.QueryRow(`SELECT * FROM communities_settings WHERE community_id = ? AND clock > ?`, settings.CommunityId, settings.Clock)
   236  	_, err := p.scanRowToStruct(qr.Scan)
   237  	switch err {
   238  	case sql.ErrNoRows:
   239  		// Query does not match, therefore clock value is not older than the new clock value or id was not found
   240  		return true, nil
   241  	case nil:
   242  		// Error is nil, therefore query matched and clock is older than the new clock
   243  		return false, nil
   244  	default:
   245  		// Error is not nil and is not sql.ErrNoRows, therefore pass out the error
   246  		return false, err
   247  	}
   248  }
   249  
   250  func (p *Persistence) ShouldHandleSyncCommunity(community *protobuf.SyncInstallationCommunity) (bool, error) {
   251  	// TODO see if there is a way to make this more elegant
   252  	// When the test for this function fails because the table has changed we should update sync functionality
   253  	qr := p.db.QueryRow(`SELECT id, private_key, description, joined, joined_at, verified, spectated, muted, muted_till, synced_at, last_opened_at FROM communities_communities WHERE id = ? AND synced_at > ?`, community.Id, community.Clock)
   254  	_, err := p.scanRowToStruct(qr.Scan)
   255  
   256  	switch err {
   257  	case sql.ErrNoRows:
   258  		// Query does not match, therefore synced_at value is not older than the new clock value or id was not found
   259  		return true, nil
   260  	case nil:
   261  		// Error is nil, therefore query matched and synced_at is older than the new clock
   262  		return false, nil
   263  	default:
   264  		// Error is not nil and is not sql.ErrNoRows, therefore pass out the error
   265  		return false, err
   266  	}
   267  }
   268  
   269  func (p *Persistence) queryCommunities(memberIdentity *ecdsa.PublicKey, query string) (response []*Community, err error) {
   270  	rows, err := p.db.Query(query, common.PubkeyToHex(memberIdentity))
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	return p.rowsToCommunities(rows)
   276  }
   277  
   278  func (p *Persistence) AllCommunities(memberIdentity *ecdsa.PublicKey) ([]*Community, error) {
   279  	return p.queryCommunities(memberIdentity, communitiesBaseQuery)
   280  }
   281  
   282  func (p *Persistence) JoinedCommunities(memberIdentity *ecdsa.PublicKey) ([]*Community, error) {
   283  	query := communitiesBaseQuery + ` WHERE c.joined`
   284  	return p.queryCommunities(memberIdentity, query)
   285  }
   286  
   287  func (p *Persistence) UpdateLastOpenedAt(communityID types.HexBytes, timestamp int64) error {
   288  	_, err := p.db.Exec(`UPDATE communities_communities SET last_opened_at = ? WHERE id = ?`, timestamp, communityID)
   289  	return err
   290  }
   291  
   292  func (p *Persistence) SpectatedCommunities(memberIdentity *ecdsa.PublicKey) ([]*Community, error) {
   293  	query := communitiesBaseQuery + ` WHERE c.spectated`
   294  	return p.queryCommunities(memberIdentity, query)
   295  }
   296  
   297  func (p *Persistence) rowsToCommunityRecords(rows *sql.Rows) (result []*CommunityRecordBundle, err error) {
   298  	defer func() {
   299  		if err != nil {
   300  			// Don't shadow original error
   301  			_ = rows.Close()
   302  			return
   303  
   304  		}
   305  		err = rows.Close()
   306  	}()
   307  
   308  	for rows.Next() {
   309  		r, err := scanCommunity(rows.Scan)
   310  		if err != nil {
   311  			return nil, err
   312  		}
   313  		result = append(result, r)
   314  	}
   315  
   316  	return result, nil
   317  }
   318  
   319  func (p *Persistence) rowsToCommunities(rows *sql.Rows) (comms []*Community, err error) {
   320  	records, err := p.rowsToCommunityRecords(rows)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  
   325  	for _, record := range records {
   326  		org, err := p.recordBundleToCommunity(record)
   327  		if err != nil {
   328  			return nil, err
   329  		}
   330  
   331  		comms = append(comms, org)
   332  	}
   333  
   334  	return comms, nil
   335  }
   336  
   337  func (p *Persistence) JoinedAndPendingCommunitiesWithRequests(memberIdentity *ecdsa.PublicKey) (comms []*Community, err error) {
   338  	query := communitiesBaseQuery + ` WHERE c.Joined OR r.state = ?`
   339  
   340  	rows, err := p.db.Query(query, common.PubkeyToHex(memberIdentity), RequestToJoinStatePending)
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	return p.rowsToCommunities(rows)
   346  }
   347  
   348  func (p *Persistence) DeletedCommunities(memberIdentity *ecdsa.PublicKey) (comms []*Community, err error) {
   349  	query := communitiesBaseQuery + ` WHERE NOT c.Joined AND r.state != ?`
   350  
   351  	rows, err := p.db.Query(query, common.PubkeyToHex(memberIdentity), RequestToJoinStatePending)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	return p.rowsToCommunities(rows)
   357  }
   358  
   359  func (p *Persistence) CommunitiesWithPrivateKey(memberIdentity *ecdsa.PublicKey) ([]*Community, error) {
   360  	query := communitiesBaseQuery + ` WHERE c.private_key IS NOT NULL`
   361  	return p.queryCommunities(memberIdentity, query)
   362  }
   363  
   364  func (p *Persistence) getByID(id []byte, memberIdentity *ecdsa.PublicKey) (*CommunityRecordBundle, error) {
   365  	r, err := scanCommunity(p.db.QueryRow(communitiesBaseQuery+` WHERE c.id = ?`, common.PubkeyToHex(memberIdentity), id).Scan)
   366  	return r, err
   367  }
   368  
   369  func (p *Persistence) GetByID(memberIdentity *ecdsa.PublicKey, id []byte) (*Community, error) {
   370  	r, err := p.getByID(id, memberIdentity)
   371  	if err == sql.ErrNoRows {
   372  		return nil, nil
   373  	}
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  
   378  	return p.recordBundleToCommunity(r)
   379  }
   380  
   381  func (p *Persistence) SaveRequestToJoin(request *RequestToJoin) (err error) {
   382  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   383  	if err != nil {
   384  		return err
   385  	}
   386  
   387  	defer func() {
   388  		if err == nil {
   389  			err = tx.Commit()
   390  			return
   391  		}
   392  		// don't shadow original error
   393  		_ = tx.Rollback()
   394  	}()
   395  
   396  	var clock uint64
   397  	// Fetch any existing request to join
   398  	err = tx.QueryRow(`SELECT clock FROM communities_requests_to_join WHERE state = ? AND public_key = ? AND community_id = ?`, RequestToJoinStatePending, request.PublicKey, request.CommunityID).Scan(&clock)
   399  	if err != nil && err != sql.ErrNoRows {
   400  		return err
   401  	}
   402  
   403  	// This is already processed
   404  	if clock >= request.Clock {
   405  		return ErrOldRequestToJoin
   406  	}
   407  
   408  	_, err = tx.Exec(`INSERT OR REPLACE INTO communities_requests_to_join(id,public_key,clock,ens_name,customization_color,chat_id,community_id,state,share_future_addresses) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, request.ID, request.PublicKey, request.Clock, request.ENSName, request.CustomizationColor, request.ChatID, request.CommunityID, request.State, request.ShareFutureAddresses)
   409  	return err
   410  }
   411  
   412  func (p *Persistence) SaveRequestToJoinRevealedAddresses(requestID types.HexBytes, revealedAccounts []*protobuf.RevealedAccount) (err error) {
   413  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   414  	if err != nil {
   415  		return
   416  	}
   417  	defer func() {
   418  		if err == nil {
   419  			err = tx.Commit()
   420  			return
   421  		}
   422  		// don't shadow original error
   423  		_ = tx.Rollback()
   424  	}()
   425  
   426  	query := `INSERT OR REPLACE INTO communities_requests_to_join_revealed_addresses (request_id, address, chain_ids, is_airdrop_address, signature) VALUES (?, ?, ?, ?, ?)`
   427  	stmt, err := tx.Prepare(query)
   428  	if err != nil {
   429  		return
   430  	}
   431  	defer stmt.Close()
   432  	for _, account := range revealedAccounts {
   433  
   434  		var chainIDs []string
   435  		for _, ID := range account.ChainIds {
   436  			chainIDs = append(chainIDs, strconv.Itoa(int(ID)))
   437  		}
   438  
   439  		_, err = stmt.Exec(
   440  			requestID,
   441  			account.Address,
   442  			strings.Join(chainIDs, ","),
   443  			account.IsAirdropAddress,
   444  			account.Signature,
   445  		)
   446  		if err != nil {
   447  			return
   448  		}
   449  	}
   450  	return
   451  }
   452  
   453  func (p *Persistence) SaveCheckChannelPermissionResponse(communityID string, chatID string, response *CheckChannelPermissionsResponse) error {
   454  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   455  	if err != nil {
   456  		return err
   457  	}
   458  
   459  	defer func() {
   460  		if err == nil {
   461  			err = tx.Commit()
   462  			return
   463  		}
   464  		// don't shadow original error
   465  		_ = tx.Rollback()
   466  	}()
   467  
   468  	viewOnlyPermissionIDs := make([]string, 0)
   469  	viewAndPostPermissionIDs := make([]string, 0)
   470  
   471  	for permissionID := range response.ViewOnlyPermissions.Permissions {
   472  		viewOnlyPermissionIDs = append(viewOnlyPermissionIDs, permissionID)
   473  	}
   474  	for permissionID := range response.ViewAndPostPermissions.Permissions {
   475  		viewAndPostPermissionIDs = append(viewAndPostPermissionIDs, permissionID)
   476  	}
   477  
   478  	_, err = tx.Exec(`INSERT INTO communities_check_channel_permission_responses (community_id,chat_id,view_only_permissions_satisfied,view_and_post_permissions_satisfied, view_only_permission_ids, view_and_post_permission_ids) VALUES (?, ?, ?, ?, ?, ?)`, communityID, chatID, response.ViewOnlyPermissions.Satisfied, response.ViewAndPostPermissions.Satisfied, strings.Join(viewOnlyPermissionIDs[:], ","), strings.Join(viewAndPostPermissionIDs[:], ","))
   479  	if err != nil {
   480  		return err
   481  	}
   482  
   483  	saveCriteriaResults := func(permissions map[string]*PermissionTokenCriteriaResult) error {
   484  		for permissionID, criteriaResult := range permissions {
   485  
   486  			criteria := make([]string, 0)
   487  			for _, val := range criteriaResult.Criteria {
   488  				criteria = append(criteria, strconv.FormatBool(val))
   489  			}
   490  
   491  			_, err = tx.Exec(`INSERT INTO communities_permission_token_criteria_results (permission_id,community_id, chat_id, criteria) VALUES (?, ?, ?, ?)`, permissionID, communityID, chatID, strings.Join(criteria[:], ","))
   492  			if err != nil {
   493  				return err
   494  			}
   495  		}
   496  		return nil
   497  	}
   498  
   499  	err = saveCriteriaResults(response.ViewOnlyPermissions.Permissions)
   500  	if err != nil {
   501  		return err
   502  	}
   503  	return saveCriteriaResults(response.ViewAndPostPermissions.Permissions)
   504  }
   505  
   506  func (p *Persistence) GetCheckChannelPermissionResponses(communityID string) (map[string]*CheckChannelPermissionsResponse, error) {
   507  
   508  	rows, err := p.db.Query(`SELECT chat_id, view_only_permissions_satisfied, view_and_post_permissions_satisfied, view_only_permission_ids, view_and_post_permission_ids FROM communities_check_channel_permission_responses WHERE community_id = ?`, communityID)
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  	defer rows.Close()
   513  
   514  	checkChannelPermissionResponses := make(map[string]*CheckChannelPermissionsResponse, 0)
   515  
   516  	for rows.Next() {
   517  
   518  		permissionResponse := &CheckChannelPermissionsResponse{
   519  			ViewOnlyPermissions: &CheckChannelViewOnlyPermissionsResult{
   520  				Satisfied:   false,
   521  				Permissions: make(map[string]*PermissionTokenCriteriaResult),
   522  			},
   523  			ViewAndPostPermissions: &CheckChannelViewAndPostPermissionsResult{
   524  				Satisfied:   false,
   525  				Permissions: make(map[string]*PermissionTokenCriteriaResult),
   526  			},
   527  		}
   528  
   529  		var chatID string
   530  		var viewOnlyPermissionIDsString string
   531  		var viewAndPostPermissionIDsString string
   532  
   533  		err := rows.Scan(&chatID, &permissionResponse.ViewOnlyPermissions.Satisfied, &permissionResponse.ViewAndPostPermissions.Satisfied, &viewOnlyPermissionIDsString, &viewAndPostPermissionIDsString)
   534  		if err != nil {
   535  			return nil, err
   536  		}
   537  
   538  		for _, permissionID := range strings.Split(viewOnlyPermissionIDsString, ",") {
   539  			if permissionID != "" {
   540  				permissionResponse.ViewOnlyPermissions.Permissions[permissionID] = &PermissionTokenCriteriaResult{Criteria: make([]bool, 0)}
   541  			}
   542  		}
   543  		for _, permissionID := range strings.Split(viewAndPostPermissionIDsString, ",") {
   544  			if permissionID != "" {
   545  				permissionResponse.ViewAndPostPermissions.Permissions[permissionID] = &PermissionTokenCriteriaResult{Criteria: make([]bool, 0)}
   546  			}
   547  		}
   548  		checkChannelPermissionResponses[chatID] = permissionResponse
   549  	}
   550  
   551  	addCriteriaResult := func(channelResponses map[string]*CheckChannelPermissionsResponse, permissions map[string]*PermissionTokenCriteriaResult, chatID string, viewOnly bool) error {
   552  		for permissionID := range permissions {
   553  			criteria, err := p.GetPermissionTokenCriteriaResult(permissionID, communityID, chatID)
   554  			if err != nil {
   555  				return err
   556  			}
   557  			if viewOnly {
   558  				channelResponses[chatID].ViewOnlyPermissions.Permissions[permissionID] = criteria
   559  			} else {
   560  				channelResponses[chatID].ViewAndPostPermissions.Permissions[permissionID] = criteria
   561  			}
   562  		}
   563  		return nil
   564  	}
   565  
   566  	for chatID, response := range checkChannelPermissionResponses {
   567  		err := addCriteriaResult(checkChannelPermissionResponses, response.ViewOnlyPermissions.Permissions, chatID, true)
   568  		if err != nil {
   569  			return nil, err
   570  		}
   571  		err = addCriteriaResult(checkChannelPermissionResponses, response.ViewAndPostPermissions.Permissions, chatID, false)
   572  		if err != nil {
   573  			return nil, err
   574  		}
   575  	}
   576  	return checkChannelPermissionResponses, nil
   577  }
   578  
   579  func (p *Persistence) GetPermissionTokenCriteriaResult(permissionID string, communityID string, chatID string) (*PermissionTokenCriteriaResult, error) {
   580  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  
   585  	defer func() {
   586  		if err == nil {
   587  			err = tx.Commit()
   588  			return
   589  		}
   590  		// don't shadow original error
   591  		_ = tx.Rollback()
   592  	}()
   593  
   594  	criteriaString := ""
   595  	err = tx.QueryRow(`SELECT criteria FROM communities_permission_token_criteria_results WHERE permission_id = ? AND community_id = ? AND chat_id = ?`, permissionID, communityID, chatID).Scan(&criteriaString)
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  
   600  	criteria := make([]bool, 0)
   601  	for _, r := range strings.Split(criteriaString, ",") {
   602  		if r == "" {
   603  			continue
   604  		}
   605  		val, err := strconv.ParseBool(r)
   606  		if err != nil {
   607  			return nil, err
   608  		}
   609  		criteria = append(criteria, val)
   610  	}
   611  
   612  	return &PermissionTokenCriteriaResult{Criteria: criteria}, nil
   613  }
   614  
   615  func (p *Persistence) RemoveRequestToJoinRevealedAddresses(requestID []byte) error {
   616  	_, err := p.db.Exec(`DELETE FROM communities_requests_to_join_revealed_addresses WHERE request_id = ?`, requestID)
   617  	return err
   618  }
   619  
   620  func (p *Persistence) GetRequestToJoinRevealedAddresses(requestID []byte) ([]*protobuf.RevealedAccount, error) {
   621  	revealedAccounts := make([]*protobuf.RevealedAccount, 0)
   622  	rows, err := p.db.Query(`SELECT address, chain_ids, is_airdrop_address, signature FROM communities_requests_to_join_revealed_addresses WHERE request_id = ?`, requestID)
   623  	if err != nil {
   624  		return nil, err
   625  	}
   626  	defer rows.Close()
   627  
   628  	for rows.Next() {
   629  		var address sql.NullString
   630  		var chainIDsStr sql.NullString
   631  		var isAirdropAddress sql.NullBool
   632  		var signature sql.RawBytes
   633  		err := rows.Scan(&address, &chainIDsStr, &isAirdropAddress, &signature)
   634  		if err != nil {
   635  			return nil, err
   636  		}
   637  
   638  		revealedAccount, err := toRevealedAccount(address, chainIDsStr, isAirdropAddress, signature)
   639  		if err != nil {
   640  			return nil, err
   641  		}
   642  
   643  		if revealedAccount == nil {
   644  			return nil, errors.New("invalid RequestToJoin RevealedAddresses data")
   645  		}
   646  		revealedAccounts = append(revealedAccounts, revealedAccount)
   647  	}
   648  	return revealedAccounts, nil
   649  }
   650  
   651  func (p *Persistence) SaveRequestToLeave(request *RequestToLeave) error {
   652  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   653  	if err != nil {
   654  		return err
   655  	}
   656  
   657  	defer func() {
   658  		if err == nil {
   659  			err = tx.Commit()
   660  			return
   661  		}
   662  		// don't shadow original error
   663  		_ = tx.Rollback()
   664  	}()
   665  
   666  	var clock uint64
   667  	// Fetch any existing request to leave
   668  	err = tx.QueryRow(`SELECT clock FROM communities_requests_to_leave WHERE public_key = ? AND community_id = ?`, request.PublicKey, request.CommunityID).Scan(&clock)
   669  	if err != nil && err != sql.ErrNoRows {
   670  		return err
   671  	}
   672  
   673  	// This is already processed
   674  	if clock >= request.Clock {
   675  		return ErrOldRequestToLeave
   676  	}
   677  
   678  	_, err = tx.Exec(`INSERT INTO communities_requests_to_leave(id,public_key,clock,community_id) VALUES (?, ?, ?, ?)`, request.ID, request.PublicKey, request.Clock, request.CommunityID)
   679  	return err
   680  }
   681  
   682  func (p *Persistence) CanceledRequestsToJoinForUser(pk string) ([]*RequestToJoin, error) {
   683  	var requests []*RequestToJoin
   684  	rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE state = ? AND public_key = ?`, RequestToJoinStateCanceled, pk)
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  	defer rows.Close()
   689  
   690  	for rows.Next() {
   691  		request := &RequestToJoin{}
   692  		err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   693  		if err != nil {
   694  			return nil, err
   695  		}
   696  		requests = append(requests, request)
   697  	}
   698  	return requests, nil
   699  }
   700  
   701  func (p *Persistence) RequestsToJoinForUserByState(pk string, state RequestToJoinState) ([]*RequestToJoin, error) {
   702  	var requests []*RequestToJoin
   703  	rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,chat_id,community_id,state,share_future_addresses FROM communities_requests_to_join WHERE state = ? AND public_key = ?`, state, pk)
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  	defer rows.Close()
   708  
   709  	for rows.Next() {
   710  		request := &RequestToJoin{}
   711  		err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   712  		if err != nil {
   713  			return nil, err
   714  		}
   715  		requests = append(requests, request)
   716  	}
   717  	return requests, nil
   718  }
   719  
   720  func (p *Persistence) HasPendingRequestsToJoinForUserAndCommunity(userPk string, communityID []byte) (bool, error) {
   721  	var count int
   722  	err := p.db.QueryRow(`SELECT count(1) FROM communities_requests_to_join WHERE state = ? AND public_key = ? AND community_id = ?`, RequestToJoinStatePending, userPk, communityID).Scan(&count)
   723  	if err != nil {
   724  		return false, err
   725  	}
   726  	return count > 0, nil
   727  }
   728  
   729  func (p *Persistence) RequestsToJoinForCommunityWithState(id []byte, state RequestToJoinState) ([]*RequestToJoin, error) {
   730  	var requests []*RequestToJoin
   731  	rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE state = ? AND community_id = ?`, state, id)
   732  	if err != nil {
   733  		return nil, err
   734  	}
   735  	defer rows.Close()
   736  
   737  	for rows.Next() {
   738  		request := &RequestToJoin{}
   739  		err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   740  		if err != nil {
   741  			return nil, err
   742  		}
   743  		requests = append(requests, request)
   744  	}
   745  	return requests, nil
   746  }
   747  
   748  func (p *Persistence) PendingRequestsToJoin() ([]*RequestToJoin, error) {
   749  	var requests []*RequestToJoin
   750  	rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE state = ?`, RequestToJoinStatePending)
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  	defer rows.Close()
   755  
   756  	for rows.Next() {
   757  		request := &RequestToJoin{}
   758  		err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   759  		if err != nil {
   760  			return nil, err
   761  		}
   762  		requests = append(requests, request)
   763  	}
   764  	return requests, nil
   765  }
   766  
   767  func (p *Persistence) PendingRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   768  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStatePending)
   769  }
   770  
   771  func (p *Persistence) DeclinedRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   772  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateDeclined)
   773  }
   774  
   775  func (p *Persistence) CanceledRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   776  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateCanceled)
   777  }
   778  
   779  func (p *Persistence) AcceptedRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   780  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateAccepted)
   781  }
   782  
   783  func (p *Persistence) AcceptedPendingRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   784  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateAcceptedPending)
   785  }
   786  
   787  func (p *Persistence) DeclinedPendingRequestsToJoinForCommunity(id []byte) ([]*RequestToJoin, error) {
   788  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateDeclinedPending)
   789  }
   790  
   791  func (p *Persistence) RequestsToJoinForCommunityAwaitingAddresses(id []byte) ([]*RequestToJoin, error) {
   792  	return p.RequestsToJoinForCommunityWithState(id, RequestToJoinStateAwaitingAddresses)
   793  }
   794  
   795  func (p *Persistence) SetRequestToJoinState(pk string, communityID []byte, state RequestToJoinState) error {
   796  	_, err := p.db.Exec(`UPDATE communities_requests_to_join SET state = ? WHERE community_id = ? AND public_key = ?`, state, communityID, pk)
   797  	return err
   798  }
   799  
   800  func (p *Persistence) DeletePendingRequestToJoin(id []byte) error {
   801  	_, err := p.db.Exec(`DELETE FROM communities_requests_to_join WHERE id = ?`, id)
   802  	if err != nil {
   803  		return err
   804  	}
   805  	_, err = p.db.Exec(`DELETE FROM communities_requests_to_join_revealed_addresses WHERE request_id = ?`, id)
   806  
   807  	return err
   808  }
   809  
   810  // UpdateClockInRequestToJoin method is used for testing
   811  func (p *Persistence) UpdateClockInRequestToJoin(id []byte, clock uint64) error {
   812  	_, err := p.db.Exec(`UPDATE communities_requests_to_join SET clock = ? WHERE id = ?`, clock, id)
   813  	return err
   814  }
   815  
   816  func (p *Persistence) SetMuted(communityID []byte, muted bool) error {
   817  	_, err := p.db.Exec(`UPDATE communities_communities SET muted = ? WHERE id = ?`, muted, communityID)
   818  	return err
   819  }
   820  
   821  func (p *Persistence) MuteCommunityTill(communityID []byte, mutedTill time.Time) error {
   822  	mutedTillFormatted := mutedTill.Format(time.RFC3339)
   823  	_, err := p.db.Exec(`UPDATE communities_communities SET muted_till = ? WHERE id = ?`, mutedTillFormatted, communityID)
   824  	return err
   825  }
   826  
   827  func (p *Persistence) GetRequestToJoin(id []byte) (*RequestToJoin, error) {
   828  	request := &RequestToJoin{}
   829  	err := p.db.QueryRow(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE id = ?`, id).Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   830  	if err != nil {
   831  		return nil, err
   832  	}
   833  
   834  	return request, nil
   835  }
   836  
   837  func (p *Persistence) GetNumberOfPendingRequestsToJoin(communityID types.HexBytes) (int, error) {
   838  	var count int
   839  	err := p.db.QueryRow(`SELECT count(1) FROM communities_requests_to_join WHERE community_id = ? AND state = ?`, communityID, RequestToJoinStatePending).Scan(&count)
   840  	if err != nil {
   841  		return 0, err
   842  	}
   843  	return count, nil
   844  }
   845  
   846  func (p *Persistence) GetRequestToJoinClockByPkAndCommunityID(pk string, communityID types.HexBytes) (uint64, error) {
   847  	var clock uint64
   848  
   849  	err := p.db.QueryRow(`
   850  		SELECT clock
   851  		FROM communities_requests_to_join
   852  		WHERE public_key = ? AND community_id = ?`, pk, communityID).Scan(&clock)
   853  	return clock, err
   854  }
   855  
   856  func (p *Persistence) GetRequestToJoinByPkAndCommunityID(pk string, communityID []byte) (*RequestToJoin, error) {
   857  	request := &RequestToJoin{}
   858  	err := p.db.QueryRow(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE public_key = ? AND community_id = ?`, pk, communityID).Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   859  	if err != nil {
   860  		return nil, err
   861  	}
   862  
   863  	return request, nil
   864  }
   865  
   866  func (p *Persistence) GetRequestToJoinByPk(pk string, communityID []byte, state RequestToJoinState) (*RequestToJoin, error) {
   867  	request := &RequestToJoin{}
   868  	err := p.db.QueryRow(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE public_key = ? AND community_id = ? AND state = ?`, pk, communityID, state).Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
   869  	if err != nil {
   870  		return nil, err
   871  	}
   872  
   873  	return request, nil
   874  }
   875  
   876  func (p *Persistence) SetSyncClock(id []byte, clock uint64) error {
   877  	_, err := p.db.Exec(`UPDATE communities_communities SET synced_at = ? WHERE id = ? AND synced_at < ?`, clock, id, clock)
   878  	return err
   879  }
   880  
   881  func (p *Persistence) SetPrivateKey(id []byte, privKey *ecdsa.PrivateKey) error {
   882  	_, err := p.db.Exec(`UPDATE communities_communities SET private_key = ? WHERE id = ?`, crypto.FromECDSA(privKey), id)
   883  	return err
   884  }
   885  
   886  func (p *Persistence) SaveWakuMessages(messages []*types.Message) (err error) {
   887  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
   888  	if err != nil {
   889  		return
   890  	}
   891  	defer func() {
   892  		if err == nil {
   893  			err = tx.Commit()
   894  			return
   895  		}
   896  		// don't shadow original error
   897  		_ = tx.Rollback()
   898  	}()
   899  	query := `INSERT OR REPLACE INTO waku_messages (sig, timestamp, topic, payload, padding, hash, third_party_id) VALUES (?, ?, ?, ?, ?, ?, ?)`
   900  	stmt, err := tx.Prepare(query)
   901  	if err != nil {
   902  		return
   903  	}
   904  	defer stmt.Close()
   905  	for _, msg := range messages {
   906  		_, err = stmt.Exec(
   907  			msg.Sig,
   908  			msg.Timestamp,
   909  			msg.Topic.String(),
   910  			msg.Payload,
   911  			msg.Padding,
   912  			types.Bytes2Hex(msg.Hash),
   913  			msg.ThirdPartyID,
   914  		)
   915  		if err != nil {
   916  			return
   917  		}
   918  	}
   919  	return
   920  }
   921  
   922  func (p *Persistence) SaveWakuMessage(message *types.Message) error {
   923  	_, err := p.db.Exec(`INSERT OR REPLACE INTO waku_messages (sig, timestamp, topic, payload, padding, hash, third_party_id) VALUES (?, ?, ?, ?, ?, ?, ?)`,
   924  		message.Sig,
   925  		message.Timestamp,
   926  		message.Topic.String(),
   927  		message.Payload,
   928  		message.Padding,
   929  		types.Bytes2Hex(message.Hash),
   930  		message.ThirdPartyID,
   931  	)
   932  	return err
   933  }
   934  
   935  func wakuMessageTimestampQuery(topics []types.TopicType) string {
   936  	query := " FROM waku_messages WHERE "
   937  	for i, topic := range topics {
   938  		query += `topic = "` + topic.String() + `"`
   939  		if i < len(topics)-1 {
   940  			query += OR
   941  		}
   942  	}
   943  	return query
   944  }
   945  
   946  func (p *Persistence) GetOldestWakuMessageTimestamp(topics []types.TopicType) (uint64, error) {
   947  	var timestamp sql.NullInt64
   948  	query := "SELECT MIN(timestamp)"
   949  	query += wakuMessageTimestampQuery(topics)
   950  	err := p.db.QueryRow(query).Scan(&timestamp)
   951  	return uint64(timestamp.Int64), err
   952  }
   953  
   954  func (p *Persistence) GetLatestWakuMessageTimestamp(topics []types.TopicType) (uint64, error) {
   955  	var timestamp sql.NullInt64
   956  	query := "SELECT MAX(timestamp)"
   957  	query += wakuMessageTimestampQuery(topics)
   958  	err := p.db.QueryRow(query).Scan(&timestamp)
   959  	return uint64(timestamp.Int64), err
   960  }
   961  
   962  func (p *Persistence) GetWakuMessagesByFilterTopic(topics []types.TopicType, from uint64, to uint64) ([]types.Message, error) {
   963  
   964  	query := "SELECT sig, timestamp, topic, payload, padding, hash, third_party_id FROM waku_messages WHERE timestamp >= " + fmt.Sprint(from) + " AND timestamp < " + fmt.Sprint(to) + " AND (" //nolint: gosec
   965  
   966  	for i, topic := range topics {
   967  		query += `topic = "` + topic.String() + `"`
   968  		if i < len(topics)-1 {
   969  			query += OR
   970  		}
   971  	}
   972  	query += ")"
   973  
   974  	rows, err := p.db.Query(query)
   975  	if err != nil {
   976  		return nil, err
   977  	}
   978  	defer rows.Close()
   979  	messages := []types.Message{}
   980  
   981  	for rows.Next() {
   982  		msg := types.Message{}
   983  		var topicStr string
   984  		var hashStr string
   985  		err := rows.Scan(&msg.Sig, &msg.Timestamp, &topicStr, &msg.Payload, &msg.Padding, &hashStr, &msg.ThirdPartyID)
   986  		if err != nil {
   987  			return nil, err
   988  		}
   989  		msg.Topic = types.StringToTopic(topicStr)
   990  		msg.Hash = types.Hex2Bytes(hashStr)
   991  		messages = append(messages, msg)
   992  	}
   993  
   994  	return messages, nil
   995  }
   996  
   997  func (p *Persistence) HasCommunityArchiveInfo(communityID types.HexBytes) (exists bool, err error) {
   998  	err = p.db.QueryRow(`SELECT EXISTS(SELECT 1 FROM communities_archive_info WHERE community_id = ?)`, communityID.String()).Scan(&exists)
   999  	return exists, err
  1000  }
  1001  
  1002  func (p *Persistence) GetLastSeenMagnetlink(communityID types.HexBytes) (string, error) {
  1003  	var magnetlinkURI string
  1004  	err := p.db.QueryRow(`SELECT last_magnetlink_uri FROM communities_archive_info WHERE community_id = ?`, communityID.String()).Scan(&magnetlinkURI)
  1005  	if err == sql.ErrNoRows {
  1006  		return "", nil
  1007  	}
  1008  	return magnetlinkURI, err
  1009  }
  1010  
  1011  func (p *Persistence) GetMagnetlinkMessageClock(communityID types.HexBytes) (uint64, error) {
  1012  	var magnetlinkClock uint64
  1013  	err := p.db.QueryRow(`SELECT magnetlink_clock FROM communities_archive_info WHERE community_id = ?`, communityID.String()).Scan(&magnetlinkClock)
  1014  	if err == sql.ErrNoRows {
  1015  		return 0, nil
  1016  	}
  1017  	return magnetlinkClock, err
  1018  }
  1019  
  1020  func (p *Persistence) SaveCommunityArchiveInfo(communityID types.HexBytes, clock uint64, lastArchiveEndDate uint64) error {
  1021  	_, err := p.db.Exec(`INSERT INTO communities_archive_info (magnetlink_clock, last_message_archive_end_date, community_id) VALUES (?, ?, ?)`,
  1022  		clock,
  1023  		lastArchiveEndDate,
  1024  		communityID.String())
  1025  	return err
  1026  }
  1027  
  1028  func (p *Persistence) UpdateMagnetlinkMessageClock(communityID types.HexBytes, clock uint64) error {
  1029  	_, err := p.db.Exec(`UPDATE communities_archive_info SET
  1030      magnetlink_clock = ?
  1031      WHERE community_id = ?`,
  1032  		clock,
  1033  		communityID.String())
  1034  	return err
  1035  }
  1036  
  1037  func (p *Persistence) UpdateLastSeenMagnetlink(communityID types.HexBytes, magnetlinkURI string) error {
  1038  	_, err := p.db.Exec(`UPDATE communities_archive_info SET
  1039      last_magnetlink_uri = ?
  1040      WHERE community_id = ?`,
  1041  		magnetlinkURI,
  1042  		communityID.String())
  1043  	return err
  1044  }
  1045  
  1046  func (p *Persistence) SaveLastMessageArchiveEndDate(communityID types.HexBytes, endDate uint64) error {
  1047  	_, err := p.db.Exec(`INSERT INTO communities_archive_info (last_message_archive_end_date, community_id) VALUES (?, ?)`,
  1048  		endDate,
  1049  		communityID.String())
  1050  	return err
  1051  }
  1052  
  1053  func (p *Persistence) UpdateLastMessageArchiveEndDate(communityID types.HexBytes, endDate uint64) error {
  1054  	_, err := p.db.Exec(`UPDATE communities_archive_info SET
  1055      last_message_archive_end_date = ?
  1056      WHERE community_id = ?`,
  1057  		endDate,
  1058  		communityID.String())
  1059  	return err
  1060  }
  1061  
  1062  func (p *Persistence) GetLastMessageArchiveEndDate(communityID types.HexBytes) (uint64, error) {
  1063  
  1064  	var lastMessageArchiveEndDate uint64
  1065  	err := p.db.QueryRow(`SELECT last_message_archive_end_date FROM communities_archive_info WHERE community_id = ?`, communityID.String()).Scan(&lastMessageArchiveEndDate)
  1066  	if err == sql.ErrNoRows {
  1067  		return 0, nil
  1068  	} else if err != nil {
  1069  		return 0, err
  1070  	}
  1071  	return lastMessageArchiveEndDate, nil
  1072  }
  1073  
  1074  func (p *Persistence) GetMessageArchiveIDsToImport(communityID types.HexBytes) ([]string, error) {
  1075  	rows, err := p.db.Query("SELECT hash FROM community_message_archive_hashes WHERE community_id = ? AND NOT(imported)", communityID.String())
  1076  	if err != nil {
  1077  		return nil, err
  1078  	}
  1079  	defer rows.Close()
  1080  	ids := []string{}
  1081  	for rows.Next() {
  1082  		id := ""
  1083  		err := rows.Scan(&id)
  1084  		if err != nil {
  1085  			return nil, err
  1086  		}
  1087  		ids = append(ids, id)
  1088  	}
  1089  	return ids, err
  1090  }
  1091  
  1092  func (p *Persistence) GetDownloadedMessageArchiveIDs(communityID types.HexBytes) ([]string, error) {
  1093  	rows, err := p.db.Query("SELECT hash FROM community_message_archive_hashes WHERE community_id = ?", communityID.String())
  1094  	if err != nil {
  1095  		return nil, err
  1096  	}
  1097  	defer rows.Close()
  1098  	ids := []string{}
  1099  	for rows.Next() {
  1100  		id := ""
  1101  		err := rows.Scan(&id)
  1102  		if err != nil {
  1103  			return nil, err
  1104  		}
  1105  		ids = append(ids, id)
  1106  	}
  1107  	return ids, err
  1108  }
  1109  
  1110  func (p *Persistence) SetMessageArchiveIDImported(communityID types.HexBytes, hash string, imported bool) error {
  1111  	_, err := p.db.Exec(`UPDATE community_message_archive_hashes SET imported = ? WHERE hash = ? AND community_id = ?`, imported, hash, communityID.String())
  1112  	return err
  1113  }
  1114  
  1115  func (p *Persistence) HasMessageArchiveID(communityID types.HexBytes, hash string) (exists bool, err error) {
  1116  	err = p.db.QueryRow(`SELECT EXISTS (SELECT 1 FROM community_message_archive_hashes WHERE community_id = ? AND hash = ?)`,
  1117  		communityID.String(),
  1118  		hash,
  1119  	).Scan(&exists)
  1120  	return exists, err
  1121  }
  1122  
  1123  func (p *Persistence) SaveMessageArchiveID(communityID types.HexBytes, hash string) error {
  1124  	_, err := p.db.Exec(`INSERT INTO community_message_archive_hashes (community_id, hash) VALUES (?, ?)`,
  1125  		communityID.String(),
  1126  		hash,
  1127  	)
  1128  	return err
  1129  }
  1130  
  1131  func (p *Persistence) GetCommunitiesSettings() ([]CommunitySettings, error) {
  1132  	rows, err := p.db.Query("SELECT community_id, message_archive_seeding_enabled, message_archive_fetching_enabled, clock FROM communities_settings")
  1133  	if err != nil {
  1134  		return nil, err
  1135  	}
  1136  	defer rows.Close()
  1137  	communitiesSettings := []CommunitySettings{}
  1138  
  1139  	for rows.Next() {
  1140  		settings := CommunitySettings{}
  1141  		err := rows.Scan(&settings.CommunityID, &settings.HistoryArchiveSupportEnabled, &settings.HistoryArchiveSupportEnabled, &settings.Clock)
  1142  		if err != nil {
  1143  			return nil, err
  1144  		}
  1145  		communitiesSettings = append(communitiesSettings, settings)
  1146  	}
  1147  	return communitiesSettings, err
  1148  }
  1149  
  1150  func (p *Persistence) CommunitySettingsExist(communityID types.HexBytes) (bool, error) {
  1151  	var count int
  1152  	err := p.db.QueryRow(`SELECT count(1) FROM communities_settings WHERE community_id = ?`, communityID.String()).Scan(&count)
  1153  	if err != nil {
  1154  		return false, err
  1155  	}
  1156  	return count > 0, nil
  1157  }
  1158  
  1159  func (p *Persistence) GetCommunitySettingsByID(communityID types.HexBytes) (*CommunitySettings, error) {
  1160  	settings := CommunitySettings{}
  1161  	err := p.db.QueryRow(`SELECT community_id, message_archive_seeding_enabled, message_archive_fetching_enabled, clock FROM communities_settings WHERE community_id = ?`, communityID.String()).Scan(&settings.CommunityID, &settings.HistoryArchiveSupportEnabled, &settings.HistoryArchiveSupportEnabled, &settings.Clock)
  1162  	if err == sql.ErrNoRows {
  1163  		return nil, nil
  1164  	} else if err != nil {
  1165  		return nil, err
  1166  	}
  1167  	return &settings, nil
  1168  }
  1169  
  1170  func (p *Persistence) DeleteCommunitySettings(communityID types.HexBytes) error {
  1171  	_, err := p.db.Exec("DELETE FROM communities_settings WHERE community_id = ?", communityID.String())
  1172  	return err
  1173  }
  1174  
  1175  func (p *Persistence) SaveCommunitySettings(communitySettings CommunitySettings) error {
  1176  	_, err := p.db.Exec(`INSERT INTO communities_settings (
  1177      community_id,
  1178      message_archive_seeding_enabled,
  1179      message_archive_fetching_enabled,
  1180      clock
  1181    ) VALUES (?, ?, ?, ?)`,
  1182  		communitySettings.CommunityID,
  1183  		communitySettings.HistoryArchiveSupportEnabled,
  1184  		communitySettings.HistoryArchiveSupportEnabled,
  1185  		communitySettings.Clock,
  1186  	)
  1187  	return err
  1188  }
  1189  
  1190  func (p *Persistence) UpdateCommunitySettings(communitySettings CommunitySettings) error {
  1191  	_, err := p.db.Exec(`UPDATE communities_settings SET
  1192      message_archive_seeding_enabled = ?,
  1193      message_archive_fetching_enabled = ?,
  1194      clock = ?
  1195      WHERE community_id = ?`,
  1196  		communitySettings.HistoryArchiveSupportEnabled,
  1197  		communitySettings.HistoryArchiveSupportEnabled,
  1198  		communitySettings.Clock,
  1199  		communitySettings.CommunityID,
  1200  	)
  1201  	return err
  1202  }
  1203  
  1204  func (p *Persistence) GetCommunityChatIDs(communityID types.HexBytes) ([]string, error) {
  1205  	rows, err := p.db.Query(`SELECT id FROM chats WHERE community_id = ?`, communityID.String())
  1206  	if err != nil {
  1207  		return nil, err
  1208  	}
  1209  	defer rows.Close()
  1210  
  1211  	ids := []string{}
  1212  	for rows.Next() {
  1213  		id := ""
  1214  		err := rows.Scan(&id)
  1215  		if err != nil {
  1216  			return nil, err
  1217  		}
  1218  		ids = append(ids, id)
  1219  	}
  1220  	return ids, nil
  1221  }
  1222  
  1223  func (p *Persistence) GetAllCommunityTokens() ([]*token.CommunityToken, error) {
  1224  	rows, err := p.db.Query(`SELECT community_id, address, type, name, symbol, description, supply_str,
  1225  	infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals,
  1226  	deployer, privileges_level, tx_hash, version FROM community_tokens`)
  1227  	if err != nil {
  1228  		return nil, err
  1229  	}
  1230  	defer rows.Close()
  1231  
  1232  	return p.getCommunityTokensInternal(rows)
  1233  }
  1234  
  1235  func (p *Persistence) GetCommunityTokens(communityID string) ([]*token.CommunityToken, error) {
  1236  	rows, err := p.db.Query(`SELECT community_id, address, type, name, symbol, description, supply_str,
  1237  	infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals,
  1238  	deployer, privileges_level, tx_hash, version
  1239  	FROM community_tokens WHERE community_id = ?`, communityID)
  1240  	if err != nil {
  1241  		return nil, err
  1242  	}
  1243  	defer rows.Close()
  1244  
  1245  	return p.getCommunityTokensInternal(rows)
  1246  }
  1247  
  1248  func (p *Persistence) GetCommunityToken(communityID string, chainID int, address string) (*token.CommunityToken, error) {
  1249  	token := token.CommunityToken{}
  1250  	var supplyStr string
  1251  	err := p.db.QueryRow(`SELECT community_id, address, type, name, symbol, description, supply_str, infinite_supply,
  1252  		transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level, tx_hash, version
  1253  		FROM community_tokens WHERE community_id = ? AND chain_id = ? AND address = ?`, communityID, chainID, address).Scan(&token.CommunityID, &token.Address, &token.TokenType, &token.Name,
  1254  		&token.Symbol, &token.Description, &supplyStr, &token.InfiniteSupply, &token.Transferable,
  1255  		&token.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
  1256  		&token.Deployer, &token.PrivilegesLevel, &token.TransactionHash, &token.Version)
  1257  	if err == sql.ErrNoRows {
  1258  		return nil, nil
  1259  	} else if err != nil {
  1260  		return nil, err
  1261  	}
  1262  	supplyBigInt, ok := new(big.Int).SetString(supplyStr, 10)
  1263  	if ok {
  1264  		token.Supply = &bigint.BigInt{Int: supplyBigInt}
  1265  	} else {
  1266  		token.Supply = &bigint.BigInt{Int: big.NewInt(0)}
  1267  	}
  1268  	return &token, nil
  1269  }
  1270  
  1271  func (p *Persistence) GetCommunityTokenByChainAndAddress(chainID int, address string) (*token.CommunityToken, error) {
  1272  	token := token.CommunityToken{}
  1273  	var supplyStr string
  1274  	err := p.db.QueryRow(`SELECT community_id, address, type, name, symbol, description, supply_str, infinite_supply,
  1275  		transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level, tx_hash, version
  1276  		FROM community_tokens WHERE chain_id = ? AND address = ?`, chainID, address).Scan(&token.CommunityID, &token.Address, &token.TokenType, &token.Name,
  1277  		&token.Symbol, &token.Description, &supplyStr, &token.InfiniteSupply, &token.Transferable,
  1278  		&token.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
  1279  		&token.Deployer, &token.PrivilegesLevel, &token.TransactionHash, &token.Version)
  1280  	if err == sql.ErrNoRows {
  1281  		return nil, nil
  1282  	} else if err != nil {
  1283  		return nil, err
  1284  	}
  1285  	supplyBigInt, ok := new(big.Int).SetString(supplyStr, 10)
  1286  	if ok {
  1287  		token.Supply = &bigint.BigInt{Int: supplyBigInt}
  1288  	} else {
  1289  		token.Supply = &bigint.BigInt{Int: big.NewInt(0)}
  1290  	}
  1291  	return &token, nil
  1292  }
  1293  
  1294  func (p *Persistence) getCommunityTokensInternal(rows *sql.Rows) ([]*token.CommunityToken, error) {
  1295  	tokens := []*token.CommunityToken{}
  1296  
  1297  	for rows.Next() {
  1298  		token := token.CommunityToken{}
  1299  		var supplyStr string
  1300  		err := rows.Scan(&token.CommunityID, &token.Address, &token.TokenType, &token.Name,
  1301  			&token.Symbol, &token.Description, &supplyStr, &token.InfiniteSupply, &token.Transferable,
  1302  			&token.RemoteSelfDestruct, &token.ChainID, &token.DeployState, &token.Base64Image, &token.Decimals,
  1303  			&token.Deployer, &token.PrivilegesLevel, &token.TransactionHash, &token.Version)
  1304  		if err != nil {
  1305  			return nil, err
  1306  		}
  1307  		supplyBigInt, ok := new(big.Int).SetString(supplyStr, 10)
  1308  		if ok {
  1309  			token.Supply = &bigint.BigInt{Int: supplyBigInt}
  1310  		} else {
  1311  			token.Supply = &bigint.BigInt{Int: big.NewInt(0)}
  1312  		}
  1313  
  1314  		tokens = append(tokens, &token)
  1315  	}
  1316  	return tokens, nil
  1317  }
  1318  
  1319  func (p *Persistence) HasCommunityToken(communityID string, address string, chainID int) (bool, error) {
  1320  	var count int
  1321  	err := p.db.QueryRow(`SELECT count(1) FROM community_tokens WHERE community_id = ? AND address = ? AND chain_id = ?`, communityID, address, chainID).Scan(&count)
  1322  	if err != nil {
  1323  		return false, err
  1324  	}
  1325  	return count > 0, nil
  1326  }
  1327  
  1328  func (p *Persistence) AddCommunityToken(token *token.CommunityToken) error {
  1329  	_, err := p.db.Exec(`INSERT INTO community_tokens (community_id, address, type, name, symbol, description, supply_str,
  1330  		infinite_supply, transferable, remote_self_destruct, chain_id, deploy_state, image_base64, decimals, deployer, privileges_level, tx_hash, version)
  1331  		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, token.CommunityID, token.Address, token.TokenType, token.Name,
  1332  		token.Symbol, token.Description, token.Supply.String(), token.InfiniteSupply, token.Transferable, token.RemoteSelfDestruct,
  1333  		token.ChainID, token.DeployState, token.Base64Image, token.Decimals, token.Deployer, token.PrivilegesLevel, token.TransactionHash, token.Version)
  1334  	return err
  1335  }
  1336  
  1337  func (p *Persistence) UpdateCommunityTokenState(chainID int, contractAddress string, deployState token.DeployState) error {
  1338  	_, err := p.db.Exec(`UPDATE community_tokens SET deploy_state = ? WHERE address = ? AND chain_id = ?`, deployState, contractAddress, chainID)
  1339  	return err
  1340  }
  1341  
  1342  func (p *Persistence) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
  1343  	_, err := p.db.Exec(`UPDATE community_tokens SET address = ? WHERE address = ? AND chain_id = ?`, newContractAddress, oldContractAddress, chainID)
  1344  	return err
  1345  }
  1346  
  1347  func (p *Persistence) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
  1348  	_, err := p.db.Exec(`UPDATE community_tokens SET supply_str = ? WHERE address = ? AND chain_id = ?`, supply.String(), contractAddress, chainID)
  1349  	return err
  1350  }
  1351  
  1352  func (p *Persistence) RemoveCommunityToken(chainID int, contractAddress string) error {
  1353  	_, err := p.db.Exec(`DELETE FROM community_tokens WHERE chain_id = ? AND address = ?`, chainID, contractAddress)
  1354  	return err
  1355  }
  1356  
  1357  func (p *Persistence) GetCommunityGrant(communityID string) ([]byte, uint64, error) {
  1358  	var grant []byte
  1359  	var clock uint64
  1360  
  1361  	err := p.db.QueryRow(`SELECT grant, clock FROM community_grants WHERE community_id = ?`, communityID).Scan(&grant, &clock)
  1362  	if err == sql.ErrNoRows {
  1363  		return []byte{}, 0, nil
  1364  	} else if err != nil {
  1365  		return []byte{}, 0, err
  1366  	}
  1367  
  1368  	return grant, clock, nil
  1369  }
  1370  
  1371  func (p *Persistence) SaveCommunityGrant(communityID string, grant []byte, clock uint64) error {
  1372  	_, err := p.db.Exec(`INSERT OR REPLACE INTO community_grants(community_id, grant, clock) VALUES (?, ?, ?)`,
  1373  		communityID, grant, clock)
  1374  	return err
  1375  }
  1376  
  1377  func (p *Persistence) RemoveCommunityGrant(communityID string) error {
  1378  	_, err := p.db.Exec(`DELETE FROM community_grants WHERE community_id = ?`, communityID)
  1379  	return err
  1380  }
  1381  
  1382  func decodeWrappedCommunityDescription(wrappedDescriptionBytes []byte) (*protobuf.CommunityDescription, error) {
  1383  	metadata := &protobuf.ApplicationMetadataMessage{}
  1384  
  1385  	err := proto.Unmarshal(wrappedDescriptionBytes, metadata)
  1386  	if err != nil {
  1387  		return nil, err
  1388  	}
  1389  
  1390  	description := &protobuf.CommunityDescription{}
  1391  
  1392  	err = proto.Unmarshal(metadata.Payload, description)
  1393  	if err != nil {
  1394  		return nil, err
  1395  	}
  1396  
  1397  	return description, nil
  1398  }
  1399  
  1400  func decodeEventsData(eventsBytes []byte, eventsDescriptionBytes []byte) (*EventsData, error) {
  1401  	if len(eventsDescriptionBytes) == 0 {
  1402  		return nil, nil
  1403  	}
  1404  	var events []CommunityEvent
  1405  	if eventsBytes != nil {
  1406  		var err error
  1407  		events, err = communityEventsFromJSONEncodedBytes(eventsBytes)
  1408  		if err != nil {
  1409  			return nil, err
  1410  		}
  1411  	}
  1412  
  1413  	return &EventsData{
  1414  		EventsBaseCommunityDescription: eventsDescriptionBytes,
  1415  		Events:                         events,
  1416  	}, nil
  1417  }
  1418  
  1419  func (p *Persistence) GetCommunityRequestsToJoinWithRevealedAddresses(communityID []byte) ([]*RequestToJoin, error) {
  1420  	requests := []*RequestToJoin{}
  1421  	rows, err := p.db.Query(`
  1422  	SELECT r.id, r.public_key, r.clock, r.ens_name, r.customization_color, r.chat_id, r.state, r.community_id, r.share_future_addresses,
  1423  		a.address, a.chain_ids, a.is_airdrop_address, a.signature
  1424  	FROM communities_requests_to_join r
  1425  	LEFT JOIN communities_requests_to_join_revealed_addresses a ON r.id = a.request_id
  1426  	WHERE r.community_id = ? AND r.state != ?`, communityID, RequestToJoinStateAwaitingAddresses)
  1427  
  1428  	if err != nil {
  1429  		return nil, err
  1430  	}
  1431  
  1432  	defer rows.Close()
  1433  
  1434  	prevRequest := &RequestToJoin{}
  1435  	for rows.Next() {
  1436  		request := &RequestToJoin{}
  1437  		var address sql.NullString
  1438  		var chainIDsStr sql.NullString
  1439  		var isAirdropAddress sql.NullBool
  1440  		var signature sql.RawBytes
  1441  
  1442  		err = rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.State, &request.CommunityID, &request.ShareFutureAddresses,
  1443  			&address, &chainIDsStr, &isAirdropAddress, &signature)
  1444  		if err != nil {
  1445  			return nil, err
  1446  		}
  1447  
  1448  		revealedAccount, err := toRevealedAccount(address, chainIDsStr, isAirdropAddress, signature)
  1449  		if err != nil {
  1450  			return nil, err
  1451  		}
  1452  
  1453  		if types.EncodeHex(prevRequest.ID) == types.EncodeHex(request.ID) {
  1454  			if revealedAccount != nil {
  1455  				prevRequest.RevealedAccounts = append(prevRequest.RevealedAccounts, revealedAccount)
  1456  			}
  1457  		} else {
  1458  			if revealedAccount != nil {
  1459  				request.RevealedAccounts = []*protobuf.RevealedAccount{
  1460  					revealedAccount,
  1461  				}
  1462  			}
  1463  			requests = append(requests, request)
  1464  			prevRequest = request
  1465  		}
  1466  	}
  1467  
  1468  	return requests, nil
  1469  }
  1470  
  1471  func toRevealedAccount(rawAddress sql.NullString, rawChainIDsStr sql.NullString, isAirdropAddress sql.NullBool, rawSignature sql.RawBytes) (*protobuf.RevealedAccount, error) {
  1472  	if !rawAddress.Valid {
  1473  		return nil, nil
  1474  	}
  1475  
  1476  	address := rawAddress.String
  1477  	if address == "" {
  1478  		return nil, nil
  1479  	}
  1480  
  1481  	chainIDsStr := ""
  1482  	if rawChainIDsStr.Valid {
  1483  		chainIDsStr = rawChainIDsStr.String
  1484  	}
  1485  
  1486  	chainIDs := make([]uint64, 0)
  1487  	for _, chainIDstr := range strings.Split(chainIDsStr, ",") {
  1488  		if chainIDstr != "" {
  1489  			chainID, err := strconv.Atoi(chainIDstr)
  1490  			if err != nil {
  1491  				return nil, err
  1492  			}
  1493  			chainIDs = append(chainIDs, uint64(chainID))
  1494  		}
  1495  	}
  1496  
  1497  	revealedAccount := &protobuf.RevealedAccount{
  1498  		Address:          address,
  1499  		ChainIds:         chainIDs,
  1500  		IsAirdropAddress: false,
  1501  		Signature:        rawSignature,
  1502  	}
  1503  	if isAirdropAddress.Valid {
  1504  		revealedAccount.IsAirdropAddress = isAirdropAddress.Bool
  1505  	}
  1506  	return revealedAccount, nil
  1507  }
  1508  
  1509  type communityToValidate struct {
  1510  	id         []byte
  1511  	clock      uint64
  1512  	payload    []byte
  1513  	validateAt uint64
  1514  	signer     []byte
  1515  }
  1516  
  1517  func (p *Persistence) SaveCommunityToValidate(c communityToValidate) error {
  1518  	_, err := p.db.Exec(`
  1519  		INSERT INTO communities_validate_signer (id, clock, payload, validate_at, signer) VALUES (?, ?, ?, ?, ?)`, c.id, c.clock, c.payload, c.validateAt, c.signer)
  1520  	return err
  1521  }
  1522  
  1523  func (p *Persistence) getCommunitiesToValidateCount() (int, error) {
  1524  
  1525  	var count int
  1526  	qr := p.db.QueryRow(`SELECT COUNT(1) FROM communities_validate_signer`)
  1527  	err := qr.Scan(&count)
  1528  
  1529  	return count, err
  1530  
  1531  }
  1532  
  1533  func (p *Persistence) getCommunitiesToValidate() (map[string][]communityToValidate, error) {
  1534  	communitiesToValidate := make(map[string][]communityToValidate)
  1535  	rows, err := p.db.Query(`SELECT id, clock, payload, signer FROM communities_validate_signer WHERE validate_at <= ? ORDER BY clock DESC`, time.Now().UnixNano())
  1536  	if err != nil {
  1537  		return nil, err
  1538  	}
  1539  	defer rows.Close()
  1540  
  1541  	for rows.Next() {
  1542  		communityToValidate := communityToValidate{}
  1543  		err := rows.Scan(&communityToValidate.id, &communityToValidate.clock, &communityToValidate.payload, &communityToValidate.signer)
  1544  		if err != nil {
  1545  			return nil, err
  1546  		}
  1547  		communitiesToValidate[types.EncodeHex(communityToValidate.id)] = append(communitiesToValidate[types.EncodeHex(communityToValidate.id)], communityToValidate)
  1548  	}
  1549  
  1550  	return communitiesToValidate, nil
  1551  
  1552  }
  1553  
  1554  func (p *Persistence) getCommunityToValidateByID(communityID types.HexBytes) ([]communityToValidate, error) {
  1555  	communityToValidateArray := []communityToValidate{}
  1556  	rows, err := p.db.Query(`SELECT id, clock, payload, signer FROM communities_validate_signer WHERE id = ? AND validate_at <= ? ORDER BY clock DESC`, communityID, time.Now().UnixNano())
  1557  
  1558  	if err != nil {
  1559  		return nil, err
  1560  	}
  1561  
  1562  	defer rows.Close()
  1563  
  1564  	for rows.Next() {
  1565  		communityToValidate := communityToValidate{}
  1566  		err := rows.Scan(&communityToValidate.id, &communityToValidate.clock, &communityToValidate.payload, &communityToValidate.signer)
  1567  		if err != nil {
  1568  			return nil, err
  1569  		}
  1570  
  1571  		communityToValidateArray = append(communityToValidateArray, communityToValidate)
  1572  	}
  1573  
  1574  	return communityToValidateArray, nil
  1575  
  1576  }
  1577  
  1578  func (p *Persistence) DeleteCommunitiesToValidateByCommunityID(communityID []byte) error {
  1579  	_, err := p.db.Exec(`DELETE FROM communities_validate_signer WHERE id = ?`, communityID)
  1580  	return err
  1581  }
  1582  
  1583  func (p *Persistence) DeleteCommunityToValidate(communityID []byte, clock uint64) error {
  1584  	_, err := p.db.Exec(`DELETE FROM communities_validate_signer WHERE id = ? AND clock = ?`, communityID, clock)
  1585  	return err
  1586  }
  1587  
  1588  func (p *Persistence) GetSyncControlNode(communityID types.HexBytes) (*protobuf.SyncCommunityControlNode, error) {
  1589  	result := &protobuf.SyncCommunityControlNode{}
  1590  
  1591  	err := p.db.QueryRow(`
  1592          SELECT clock, installation_id
  1593          FROM communities_control_node
  1594          WHERE community_id = ?
  1595      `, communityID).Scan(&result.Clock, &result.InstallationId)
  1596  
  1597  	if err != nil {
  1598  		if err == sql.ErrNoRows {
  1599  			return nil, nil
  1600  		}
  1601  		return nil, err
  1602  	}
  1603  
  1604  	return result, nil
  1605  }
  1606  
  1607  func (p *Persistence) SaveSyncControlNode(communityID types.HexBytes, clock uint64, installationID string) error {
  1608  	_, err := p.db.Exec(
  1609  		`INSERT INTO communities_control_node (
  1610  			community_id,
  1611  			clock,
  1612  			installation_id
  1613  		) VALUES (?, ?, ?)`,
  1614  		communityID,
  1615  		clock,
  1616  		installationID,
  1617  	)
  1618  	return err
  1619  }
  1620  
  1621  func (p *Persistence) GetCommunityRequestToJoinWithRevealedAddresses(pubKey string, communityID []byte) (*RequestToJoin, error) {
  1622  	requestToJoin, err := p.GetRequestToJoinByPkAndCommunityID(pubKey, communityID)
  1623  	if err != nil {
  1624  		return nil, err
  1625  	}
  1626  
  1627  	revealedAccounts, err := p.GetRequestToJoinRevealedAddresses(requestToJoin.ID)
  1628  	if err != nil {
  1629  		return nil, err
  1630  	}
  1631  
  1632  	requestToJoin.RevealedAccounts = revealedAccounts
  1633  
  1634  	return requestToJoin, nil
  1635  }
  1636  
  1637  func (p *Persistence) SaveRequestsToJoin(requests []*RequestToJoin) (err error) {
  1638  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
  1639  	if err != nil {
  1640  		return err
  1641  	}
  1642  
  1643  	defer func() {
  1644  		if err != nil {
  1645  			// Rollback the transaction on error
  1646  			_ = tx.Rollback()
  1647  		}
  1648  	}()
  1649  
  1650  	stmt, err := tx.Prepare(`INSERT OR REPLACE INTO communities_requests_to_join(id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses)
  1651  		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
  1652  	if err != nil {
  1653  		return err
  1654  	}
  1655  	defer stmt.Close()
  1656  
  1657  	for _, request := range requests {
  1658  		var clock uint64
  1659  		// Fetch any existing request to join
  1660  		err = tx.QueryRow(`SELECT clock FROM communities_requests_to_join WHERE public_key = ? AND community_id = ?`, request.PublicKey, request.CommunityID).Scan(&clock)
  1661  		if err != nil && err != sql.ErrNoRows {
  1662  			return err
  1663  		}
  1664  
  1665  		if clock >= request.Clock {
  1666  			return ErrOldRequestToJoin
  1667  		}
  1668  
  1669  		_, err = stmt.Exec(request.ID, request.PublicKey, request.Clock, request.ENSName, request.CustomizationColor, request.ChatID, request.CommunityID, request.State, request.ShareFutureAddresses)
  1670  		if err != nil {
  1671  			return err
  1672  		}
  1673  	}
  1674  
  1675  	err = tx.Commit()
  1676  	return err
  1677  }
  1678  
  1679  func (p *Persistence) GetCuratedCommunities() (*CuratedCommunities, error) {
  1680  	rows, err := p.db.Query("SELECT community_id, featured FROM curated_communities")
  1681  	if err != nil {
  1682  		return nil, err
  1683  	}
  1684  	defer rows.Close()
  1685  
  1686  	result := &CuratedCommunities{
  1687  		ContractCommunities:         []string{},
  1688  		ContractFeaturedCommunities: []string{},
  1689  	}
  1690  	for rows.Next() {
  1691  		var communityID string
  1692  		var featured bool
  1693  		if err := rows.Scan(&communityID, &featured); err != nil {
  1694  			return nil, err
  1695  		}
  1696  		result.ContractCommunities = append(result.ContractCommunities, communityID)
  1697  		if featured {
  1698  			result.ContractFeaturedCommunities = append(result.ContractFeaturedCommunities, communityID)
  1699  		}
  1700  	}
  1701  
  1702  	return result, nil
  1703  }
  1704  
  1705  func (p *Persistence) SetCuratedCommunities(communities *CuratedCommunities) error {
  1706  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
  1707  	if err != nil {
  1708  		return err
  1709  	}
  1710  
  1711  	defer func() {
  1712  		if err == nil {
  1713  			err = tx.Commit()
  1714  			return
  1715  		}
  1716  		// don't shadow original error
  1717  		_ = tx.Rollback()
  1718  	}()
  1719  
  1720  	// Clear the existing communities
  1721  	if _, err = tx.Exec("DELETE FROM curated_communities"); err != nil {
  1722  		return err
  1723  	}
  1724  
  1725  	stmt, err := tx.Prepare("INSERT INTO curated_communities (community_id, featured) VALUES (?, ?)")
  1726  	if err != nil {
  1727  		return err
  1728  	}
  1729  	defer stmt.Close()
  1730  
  1731  	featuredMap := make(map[string]bool)
  1732  	for _, community := range communities.ContractFeaturedCommunities {
  1733  		featuredMap[community] = true
  1734  	}
  1735  
  1736  	for _, community := range communities.ContractCommunities {
  1737  		_, err := stmt.Exec(community, featuredMap[community])
  1738  		if err != nil {
  1739  			return err
  1740  		}
  1741  	}
  1742  
  1743  	return nil
  1744  }
  1745  
  1746  func (p *Persistence) AllNonApprovedCommunitiesRequestsToJoin() ([]*RequestToJoin, error) {
  1747  	nonApprovedRequestsToJoin := []*RequestToJoin{}
  1748  	rows, err := p.db.Query(`SELECT id,public_key,clock,ens_name,customization_color,chat_id,community_id,state, share_future_addresses FROM communities_requests_to_join WHERE state != ?`, RequestToJoinStateAccepted)
  1749  
  1750  	if err == sql.ErrNoRows {
  1751  		return nonApprovedRequestsToJoin, nil
  1752  	} else if err != nil {
  1753  		return nil, err
  1754  	}
  1755  
  1756  	defer rows.Close()
  1757  
  1758  	for rows.Next() {
  1759  		request := &RequestToJoin{}
  1760  		err := rows.Scan(&request.ID, &request.PublicKey, &request.Clock, &request.ENSName, &request.CustomizationColor, &request.ChatID, &request.CommunityID, &request.State, &request.ShareFutureAddresses)
  1761  		if err != nil {
  1762  			return nil, err
  1763  		}
  1764  		nonApprovedRequestsToJoin = append(nonApprovedRequestsToJoin, request)
  1765  	}
  1766  	return nonApprovedRequestsToJoin, nil
  1767  }
  1768  
  1769  func (p *Persistence) SaveCommunityShard(communityID types.HexBytes, shard *shard.Shard, clock uint64) error {
  1770  	var cluster, index *uint16
  1771  
  1772  	if shard != nil {
  1773  		cluster = &shard.Cluster
  1774  		index = &shard.Index
  1775  	}
  1776  
  1777  	result, err := p.db.Exec(`
  1778  		INSERT INTO communities_shards (community_id, shard_cluster, shard_index, clock)
  1779  		VALUES (?, ?, ?, ?)
  1780  		ON CONFLICT(community_id)
  1781  		DO UPDATE SET
  1782  			shard_cluster = CASE WHEN excluded.clock > communities_shards.clock THEN excluded.shard_cluster ELSE communities_shards.shard_cluster END,
  1783  			shard_index = CASE WHEN excluded.clock > communities_shards.clock THEN excluded.shard_index ELSE communities_shards.shard_index END,
  1784  			clock = CASE WHEN excluded.clock > communities_shards.clock THEN excluded.clock ELSE communities_shards.clock END
  1785  		WHERE excluded.clock > communities_shards.clock OR communities_shards.community_id IS NULL`,
  1786  		communityID, cluster, index, clock)
  1787  
  1788  	if err != nil {
  1789  		return err
  1790  	}
  1791  
  1792  	rowsAffected, err := result.RowsAffected()
  1793  	if err != nil {
  1794  		return err
  1795  	}
  1796  
  1797  	if rowsAffected == 0 {
  1798  		return ErrOldShardInfo
  1799  	}
  1800  	return nil
  1801  }
  1802  
  1803  // if data will not be found, will return sql.ErrNoRows. Must be handled on the caller side
  1804  func (p *Persistence) GetCommunityShard(communityID types.HexBytes) (*shard.Shard, error) {
  1805  	var cluster sql.NullInt64
  1806  	var index sql.NullInt64
  1807  	err := p.db.QueryRow(`SELECT shard_cluster, shard_index FROM communities_shards WHERE community_id = ?`,
  1808  		communityID).Scan(&cluster, &index)
  1809  
  1810  	if err != nil {
  1811  		return nil, err
  1812  	}
  1813  
  1814  	if !cluster.Valid || !index.Valid {
  1815  		return nil, nil
  1816  	}
  1817  
  1818  	return &shard.Shard{
  1819  		Cluster: uint16(cluster.Int64),
  1820  		Index:   uint16(index.Int64),
  1821  	}, nil
  1822  }
  1823  
  1824  func (p *Persistence) DeleteCommunityShard(communityID types.HexBytes) error {
  1825  	_, err := p.db.Exec(`DELETE FROM communities_shards WHERE community_id = ?`, communityID)
  1826  	return err
  1827  }
  1828  
  1829  func (p *Persistence) GetAppliedCommunityEvents(communityID types.HexBytes) (map[string]uint64, error) {
  1830  	rows, err := p.db.Query(`SELECT event_type_id, clock FROM applied_community_events WHERE community_id = ?`, communityID.String())
  1831  	if err != nil {
  1832  		return nil, err
  1833  	}
  1834  	defer rows.Close()
  1835  
  1836  	result := map[string]uint64{}
  1837  
  1838  	eventTypeID := ""
  1839  	clock := uint64(0)
  1840  
  1841  	for rows.Next() {
  1842  		err := rows.Scan(&eventTypeID, &clock)
  1843  		if err != nil {
  1844  			return nil, err
  1845  		}
  1846  		result[eventTypeID] = clock
  1847  	}
  1848  
  1849  	return result, nil
  1850  }
  1851  
  1852  func (p *Persistence) UpsertAppliedCommunityEvents(communityID types.HexBytes, processedEvents map[string]uint64) error {
  1853  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
  1854  	if err != nil {
  1855  		return err
  1856  	}
  1857  
  1858  	defer func() {
  1859  		if err == nil {
  1860  			err = tx.Commit()
  1861  			return
  1862  		}
  1863  		// don't shadow original error
  1864  		_ = tx.Rollback()
  1865  	}()
  1866  
  1867  	for eventTypeID, newClock := range processedEvents {
  1868  		var currentClock uint64
  1869  		err = tx.QueryRow(`
  1870  			SELECT clock
  1871  			FROM applied_community_events
  1872  			WHERE community_id = ? AND event_type_id = ?`,
  1873  			communityID.String(), eventTypeID).Scan(&currentClock)
  1874  
  1875  		if err != nil && err != sql.ErrNoRows {
  1876  			return err
  1877  		}
  1878  
  1879  		if newClock > currentClock {
  1880  			_, err = tx.Exec(`
  1881  				INSERT OR REPLACE INTO applied_community_events(community_id, event_type_id, clock)
  1882  				VALUES (?, ?, ?)`,
  1883  				communityID.String(), eventTypeID, newClock)
  1884  			if err != nil {
  1885  				return err
  1886  			}
  1887  		}
  1888  	}
  1889  	return err
  1890  }
  1891  
  1892  func (p *Persistence) InvalidateDecryptedCommunityCacheForKeys(keys []*encryption.HashRatchetInfo) error {
  1893  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
  1894  	if err != nil {
  1895  		return err
  1896  	}
  1897  
  1898  	defer func() {
  1899  		if err == nil {
  1900  			err = tx.Commit()
  1901  			return
  1902  		}
  1903  		// don't shadow original error
  1904  		_ = tx.Rollback()
  1905  	}()
  1906  
  1907  	if len(keys) == 0 {
  1908  		return nil
  1909  	}
  1910  	idsArgs := make([]interface{}, 0, len(keys))
  1911  	for _, k := range keys {
  1912  		idsArgs = append(idsArgs, k.KeyID)
  1913  	}
  1914  
  1915  	inVector := strings.Repeat("?, ", len(keys)-1) + "?"
  1916  
  1917  	query := "SELECT DISTINCT(community_id) FROM encrypted_community_description_missing_keys WHERE key_id IN (" + inVector + ")" // nolint: gosec
  1918  
  1919  	var communityIDs []interface{}
  1920  	rows, err := tx.Query(query, idsArgs...)
  1921  	if err != nil {
  1922  		return err
  1923  	}
  1924  	defer rows.Close()
  1925  
  1926  	for rows.Next() {
  1927  		var communityID []byte
  1928  		err = rows.Scan(&communityID)
  1929  		if err != nil {
  1930  			return err
  1931  		}
  1932  		communityIDs = append(communityIDs, communityID)
  1933  	}
  1934  	if len(communityIDs) == 0 {
  1935  		return nil
  1936  	}
  1937  
  1938  	inVector = strings.Repeat("?, ", len(communityIDs)-1) + "?"
  1939  
  1940  	query = "DELETE FROM encrypted_community_description_cache WHERE community_id IN (" + inVector + ")" //nolint: gosec
  1941  	_, err = tx.Exec(query, communityIDs...)
  1942  
  1943  	return err
  1944  }
  1945  
  1946  func (p *Persistence) SaveDecryptedCommunityDescription(communityID []byte, missingKeys []*CommunityPrivateDataFailedToDecrypt, description *protobuf.CommunityDescription) error {
  1947  	if description == nil {
  1948  		return nil
  1949  	}
  1950  	marshaledDescription, err := proto.Marshal(description)
  1951  	if err != nil {
  1952  		return err
  1953  	}
  1954  	tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
  1955  	if err != nil {
  1956  		return err
  1957  	}
  1958  
  1959  	defer func() {
  1960  		if err == nil {
  1961  			err = tx.Commit()
  1962  			return
  1963  		}
  1964  		// don't shadow original error
  1965  		_ = tx.Rollback()
  1966  	}()
  1967  	previousCommunity, err := p.getDecryptedCommunityDescriptionByID(tx, communityID)
  1968  	if err != nil {
  1969  		return err
  1970  	}
  1971  
  1972  	if previousCommunity != nil && previousCommunity.Clock >= description.Clock {
  1973  		return nil
  1974  	}
  1975  
  1976  	insertCommunity := "INSERT INTO encrypted_community_description_cache (community_id, clock, description) VALUES (?, ?, ?);"
  1977  	_, err = tx.Exec(insertCommunity, communityID, description.Clock, marshaledDescription)
  1978  	if err != nil {
  1979  		return err
  1980  	}
  1981  	for _, key := range missingKeys {
  1982  		insertKey := "INSERT INTO encrypted_community_description_missing_keys (community_id, key_id) VALUES(?, ?)"
  1983  		_, err = tx.Exec(insertKey, communityID, key.KeyID)
  1984  		if err != nil {
  1985  			return err
  1986  		}
  1987  	}
  1988  
  1989  	return nil
  1990  }
  1991  
  1992  func (p *Persistence) GetDecryptedCommunityDescription(communityID []byte, clock uint64) (*protobuf.CommunityDescription, error) {
  1993  	return p.getDecryptedCommunityDescriptionByIDAndClock(communityID, clock)
  1994  }
  1995  
  1996  func (p *Persistence) getDecryptedCommunityDescriptionByIDAndClock(communityID []byte, clock uint64) (*protobuf.CommunityDescription, error) {
  1997  	query := "SELECT description FROM encrypted_community_description_cache WHERE community_id = ? AND clock = ?"
  1998  
  1999  	qr := p.db.QueryRow(query, communityID, clock)
  2000  
  2001  	var descriptionBytes []byte
  2002  
  2003  	err := qr.Scan(&descriptionBytes)
  2004  	switch err {
  2005  	case sql.ErrNoRows:
  2006  		return nil, nil
  2007  	case nil:
  2008  		var communityDescription protobuf.CommunityDescription
  2009  		err := proto.Unmarshal(descriptionBytes, &communityDescription)
  2010  		if err != nil {
  2011  			return nil, err
  2012  		}
  2013  		return &communityDescription, nil
  2014  	default:
  2015  		return nil, err
  2016  	}
  2017  }
  2018  
  2019  func (p *Persistence) getDecryptedCommunityDescriptionByID(tx *sql.Tx, communityID []byte) (*protobuf.CommunityDescription, error) {
  2020  	query := "SELECT description FROM encrypted_community_description_cache WHERE community_id = ?"
  2021  
  2022  	qr := tx.QueryRow(query, communityID)
  2023  
  2024  	var descriptionBytes []byte
  2025  
  2026  	err := qr.Scan(&descriptionBytes)
  2027  	switch err {
  2028  	case sql.ErrNoRows:
  2029  		return nil, nil
  2030  	case nil:
  2031  		var communityDescription protobuf.CommunityDescription
  2032  		err := proto.Unmarshal(descriptionBytes, &communityDescription)
  2033  		if err != nil {
  2034  			return nil, err
  2035  		}
  2036  		return &communityDescription, nil
  2037  	default:
  2038  		return nil, err
  2039  	}
  2040  }
  2041  
  2042  func (p *Persistence) GetCommunityRequestsToJoinRevealedAddresses(communityID []byte) (map[string][]*protobuf.RevealedAccount, error) {
  2043  	accounts := make(map[string][]*protobuf.RevealedAccount)
  2044  
  2045  	rows, err := p.db.Query(`
  2046  	SELECT r.public_key,
  2047  		a.address, a.chain_ids, a.is_airdrop_address, a.signature
  2048  	FROM communities_requests_to_join r
  2049  	LEFT JOIN communities_requests_to_join_revealed_addresses a ON r.id = a.request_id
  2050  	WHERE r.community_id = ? AND r.state = ?`, communityID, RequestToJoinStateAccepted)
  2051  
  2052  	if err != nil {
  2053  		if err == sql.ErrNoRows {
  2054  			return accounts, nil
  2055  		}
  2056  		return nil, err
  2057  	}
  2058  
  2059  	defer rows.Close()
  2060  
  2061  	for rows.Next() {
  2062  		var rawPublicKey sql.NullString
  2063  		var address sql.NullString
  2064  		var chainIDsStr sql.NullString
  2065  		var isAirdropAddress sql.NullBool
  2066  		var signature sql.RawBytes
  2067  
  2068  		err = rows.Scan(&rawPublicKey, &address, &chainIDsStr, &isAirdropAddress, &signature)
  2069  		if err != nil {
  2070  			return nil, err
  2071  		}
  2072  
  2073  		if !rawPublicKey.Valid {
  2074  			return nil, errors.New("GetCommunityRequestsToJoinRevealedAddresses: invalid public key")
  2075  		}
  2076  
  2077  		publicKey := rawPublicKey.String
  2078  
  2079  		revealedAccount, err := toRevealedAccount(address, chainIDsStr, isAirdropAddress, signature)
  2080  		if err != nil {
  2081  			return nil, err
  2082  		}
  2083  
  2084  		if revealedAccount == nil {
  2085  			continue
  2086  		}
  2087  
  2088  		if _, exists := accounts[publicKey]; !exists {
  2089  			accounts[publicKey] = []*protobuf.RevealedAccount{revealedAccount}
  2090  		} else {
  2091  			accounts[publicKey] = append(accounts[publicKey], revealedAccount)
  2092  		}
  2093  	}
  2094  
  2095  	return accounts, nil
  2096  }
  2097  
  2098  func (p *Persistence) GetEncryptionKeyRequests(communityID []byte, channelIDs map[string]struct{}) (map[string]*EncryptionKeysRequestRecord, error) {
  2099  	result := map[string]*EncryptionKeysRequestRecord{}
  2100  
  2101  	query := "SELECT channel_id, requested_at, requested_count FROM community_encryption_keys_requests WHERE community_id = ? AND channel_id IN (?" + strings.Repeat(",?", len(channelIDs)-1) + ")"
  2102  
  2103  	args := make([]interface{}, 0, len(channelIDs)+1)
  2104  	args = append(args, communityID)
  2105  	for channelID := range channelIDs {
  2106  		args = append(args, channelID)
  2107  	}
  2108  
  2109  	rows, err := p.db.Query(query, args...)
  2110  	if err != nil {
  2111  		return nil, err
  2112  	}
  2113  	defer rows.Close()
  2114  
  2115  	for rows.Next() {
  2116  		var channelID string
  2117  		var requestedAt int64
  2118  		var requestedCount uint
  2119  		err := rows.Scan(&channelID, &requestedAt, &requestedCount)
  2120  		if err != nil {
  2121  			return nil, err
  2122  		}
  2123  		result[channelID] = &EncryptionKeysRequestRecord{
  2124  			communityID:    communityID,
  2125  			channelID:      channelID,
  2126  			requestedAt:    requestedAt,
  2127  			requestedCount: requestedCount,
  2128  		}
  2129  	}
  2130  
  2131  	err = rows.Err()
  2132  	if err != nil {
  2133  		return nil, err
  2134  	}
  2135  
  2136  	return result, nil
  2137  }
  2138  
  2139  func (p *Persistence) UpdateAndPruneEncryptionKeyRequests(communityID types.HexBytes, channelIDs []string, requestedAt int64) error {
  2140  	tx, err := p.db.Begin()
  2141  	if err != nil {
  2142  		return err
  2143  	}
  2144  
  2145  	defer func() {
  2146  		if err == nil {
  2147  			err = tx.Commit()
  2148  			return
  2149  		}
  2150  		// don't shadow original error
  2151  		_ = tx.Rollback()
  2152  	}()
  2153  
  2154  	if len(channelIDs) == 0 {
  2155  		deleteQuery := "DELETE FROM community_encryption_keys_requests WHERE community_id = ?"
  2156  		_, err = tx.Exec(deleteQuery, communityID)
  2157  		return err
  2158  	}
  2159  
  2160  	// Delete entries that do not match the channelIDs list
  2161  	deleteQuery := "DELETE FROM community_encryption_keys_requests WHERE community_id = ? AND channel_id NOT IN (?" + strings.Repeat(",?", len(channelIDs)-1) + ")"
  2162  	args := make([]interface{}, 0, len(channelIDs)+1)
  2163  	args = append(args, communityID)
  2164  	for _, channelID := range channelIDs {
  2165  		args = append(args, channelID)
  2166  	}
  2167  	_, err = tx.Exec(deleteQuery, args...)
  2168  	if err != nil {
  2169  		return err
  2170  	}
  2171  
  2172  	stmt, err := tx.Prepare(`
  2173          INSERT INTO community_encryption_keys_requests (community_id, channel_id, requested_at, requested_count)
  2174          VALUES (?, ?, ?, 1)
  2175          ON CONFLICT(community_id, channel_id)
  2176          DO UPDATE SET
  2177              requested_at = excluded.requested_at,
  2178              requested_count = community_encryption_keys_requests.requested_count + 1
  2179          WHERE excluded.requested_at > community_encryption_keys_requests.requested_at
  2180      `)
  2181  	if err != nil {
  2182  		return err
  2183  	}
  2184  	defer stmt.Close()
  2185  
  2186  	for _, channelID := range channelIDs {
  2187  		_, err := stmt.Exec(communityID, channelID, requestedAt)
  2188  		if err != nil {
  2189  			return err
  2190  		}
  2191  	}
  2192  
  2193  	return nil
  2194  }