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(×tamp) 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(×tamp) 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(¤tClock) 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 }