github.com/status-im/status-go@v1.1.0/protocol/communities/manager.go (about) 1 package communities 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/ecdsa" 7 "database/sql" 8 "encoding/hex" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/golang/protobuf/proto" 18 19 "github.com/google/uuid" 20 "github.com/pkg/errors" 21 "go.uber.org/zap" 22 23 gethcommon "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/common/hexutil" 25 "github.com/status-im/status-go/account" 26 utils "github.com/status-im/status-go/common" 27 "github.com/status-im/status-go/eth-node/crypto" 28 "github.com/status-im/status-go/eth-node/types" 29 "github.com/status-im/status-go/images" 30 multiaccountscommon "github.com/status-im/status-go/multiaccounts/common" 31 "github.com/status-im/status-go/params" 32 "github.com/status-im/status-go/protocol/common" 33 "github.com/status-im/status-go/protocol/common/shard" 34 community_token "github.com/status-im/status-go/protocol/communities/token" 35 "github.com/status-im/status-go/protocol/encryption" 36 "github.com/status-im/status-go/protocol/ens" 37 "github.com/status-im/status-go/protocol/protobuf" 38 "github.com/status-im/status-go/protocol/requests" 39 "github.com/status-im/status-go/protocol/transport" 40 "github.com/status-im/status-go/rpc/network" 41 "github.com/status-im/status-go/server" 42 "github.com/status-im/status-go/services/wallet/bigint" 43 walletcommon "github.com/status-im/status-go/services/wallet/common" 44 "github.com/status-im/status-go/services/wallet/thirdparty" 45 "github.com/status-im/status-go/services/wallet/token" 46 "github.com/status-im/status-go/signal" 47 "github.com/status-im/status-go/transactions" 48 ) 49 50 type Publisher interface { 51 publish(subscription *Subscription) 52 } 53 54 var defaultAnnounceList = [][]string{ 55 {"udp://tracker.opentrackr.org:1337/announce"}, 56 {"udp://tracker.openbittorrent.com:6969/announce"}, 57 } 58 var pieceLength = 100 * 1024 59 60 const maxArchiveSizeInBytes = 30000000 61 62 var maxNbMembers = 5000 63 var maxNbPendingRequestedMembers = 100 64 65 var memberPermissionsCheckInterval = 8 * time.Hour 66 var validateInterval = 2 * time.Minute 67 68 // Used for testing only 69 func SetValidateInterval(duration time.Duration) { 70 validateInterval = duration 71 } 72 func SetMaxNbMembers(maxNb int) { 73 maxNbMembers = maxNb 74 } 75 func SetMaxNbPendingRequestedMembers(maxNb int) { 76 maxNbPendingRequestedMembers = maxNb 77 } 78 79 // errors 80 var ( 81 ErrTorrentTimedout = errors.New("torrent has timed out") 82 ErrCommunityRequestAlreadyRejected = errors.New("that user was already rejected from the community") 83 ErrInvalidClock = errors.New("invalid clock to cancel request to join") 84 ) 85 86 type Manager struct { 87 persistence *Persistence 88 encryptor *encryption.Protocol 89 ensSubscription chan []*ens.VerificationRecord 90 subscriptions []chan *Subscription 91 ensVerifier *ens.Verifier 92 ownerVerifier OwnerVerifier 93 identity *ecdsa.PrivateKey 94 installationID string 95 accountsManager account.Manager 96 tokenManager TokenManager 97 collectiblesManager CollectiblesManager 98 logger *zap.Logger 99 transport *transport.Transport 100 timesource common.TimeSource 101 quit chan struct{} 102 walletConfig *params.WalletConfig 103 communityTokensService CommunityTokensServiceInterface 104 membersReevaluationTasks sync.Map // stores `membersReevaluationTask` 105 forceMembersReevaluation map[string]chan struct{} 106 stopped bool 107 RekeyInterval time.Duration 108 PermissionChecker PermissionChecker 109 keyDistributor KeyDistributor 110 communityLock *CommunityLock 111 mediaServer server.MediaServerInterface 112 } 113 114 type CommunityLock struct { 115 logger *zap.Logger 116 locks map[string]*sync.Mutex 117 mutex sync.Mutex 118 } 119 120 func NewCommunityLock(logger *zap.Logger) *CommunityLock { 121 return &CommunityLock{ 122 logger: logger.Named("CommunityLock"), 123 locks: make(map[string]*sync.Mutex), 124 } 125 } 126 127 func (c *CommunityLock) Lock(communityID types.HexBytes) { 128 c.mutex.Lock() 129 communityIDStr := types.EncodeHex(communityID) 130 lock, ok := c.locks[communityIDStr] 131 if !ok { 132 lock = &sync.Mutex{} 133 c.locks[communityIDStr] = lock 134 } 135 c.mutex.Unlock() 136 137 lock.Lock() 138 } 139 140 func (c *CommunityLock) Unlock(communityID types.HexBytes) { 141 c.mutex.Lock() 142 communityIDStr := types.EncodeHex(communityID) 143 lock, ok := c.locks[communityIDStr] 144 c.mutex.Unlock() 145 146 if ok { 147 lock.Unlock() 148 } else { 149 c.logger.Warn("trying to unlock a non-existent lock", zap.String("communityID", communityIDStr)) 150 } 151 } 152 153 func (c *CommunityLock) Init() { 154 c.locks = make(map[string]*sync.Mutex) 155 } 156 157 type HistoryArchiveDownloadTask struct { 158 CancelChan chan struct{} 159 Waiter sync.WaitGroup 160 m sync.RWMutex 161 Cancelled bool 162 } 163 164 type HistoryArchiveDownloadTaskInfo struct { 165 TotalDownloadedArchivesCount int 166 TotalArchivesCount int 167 Cancelled bool 168 } 169 170 type ArchiveFileService interface { 171 CreateHistoryArchiveTorrentFromMessages(communityID types.HexBytes, messages []*types.Message, topics []types.TopicType, startDate time.Time, endDate time.Time, partition time.Duration, encrypt bool) ([]string, error) 172 CreateHistoryArchiveTorrentFromDB(communityID types.HexBytes, topics []types.TopicType, startDate time.Time, endDate time.Time, partition time.Duration, encrypt bool) ([]string, error) 173 SaveMessageArchiveID(communityID types.HexBytes, hash string) error 174 GetMessageArchiveIDsToImport(communityID types.HexBytes) ([]string, error) 175 SetMessageArchiveIDImported(communityID types.HexBytes, hash string, imported bool) error 176 ExtractMessagesFromHistoryArchive(communityID types.HexBytes, archiveID string) ([]*protobuf.WakuMessage, error) 177 GetHistoryArchiveMagnetlink(communityID types.HexBytes) (string, error) 178 LoadHistoryArchiveIndexFromFile(myKey *ecdsa.PrivateKey, communityID types.HexBytes) (*protobuf.WakuMessageArchiveIndex, error) 179 } 180 181 type ArchiveService interface { 182 ArchiveFileService 183 184 SetOnline(bool) 185 SetTorrentConfig(*params.TorrentConfig) 186 StartTorrentClient() error 187 Stop() error 188 IsReady() bool 189 GetCommunityChatsFilters(communityID types.HexBytes) ([]*transport.Filter, error) 190 GetCommunityChatsTopics(communityID types.HexBytes) ([]types.TopicType, error) 191 GetHistoryArchivePartitionStartTimestamp(communityID types.HexBytes) (uint64, error) 192 CreateAndSeedHistoryArchive(communityID types.HexBytes, topics []types.TopicType, startDate time.Time, endDate time.Time, partition time.Duration, encrypt bool) error 193 StartHistoryArchiveTasksInterval(community *Community, interval time.Duration) 194 StopHistoryArchiveTasksInterval(communityID types.HexBytes) 195 SeedHistoryArchiveTorrent(communityID types.HexBytes) error 196 UnseedHistoryArchiveTorrent(communityID types.HexBytes) 197 IsSeedingHistoryArchiveTorrent(communityID types.HexBytes) bool 198 GetHistoryArchiveDownloadTask(communityID string) *HistoryArchiveDownloadTask 199 AddHistoryArchiveDownloadTask(communityID string, task *HistoryArchiveDownloadTask) 200 DownloadHistoryArchivesByMagnetlink(communityID types.HexBytes, magnetlink string, cancelTask chan struct{}) (*HistoryArchiveDownloadTaskInfo, error) 201 TorrentFileExists(communityID string) bool 202 } 203 204 type ArchiveManagerConfig struct { 205 TorrentConfig *params.TorrentConfig 206 Logger *zap.Logger 207 Persistence *Persistence 208 Transport *transport.Transport 209 Identity *ecdsa.PrivateKey 210 Encryptor *encryption.Protocol 211 Publisher Publisher 212 } 213 214 func (t *HistoryArchiveDownloadTask) IsCancelled() bool { 215 t.m.RLock() 216 defer t.m.RUnlock() 217 return t.Cancelled 218 } 219 220 func (t *HistoryArchiveDownloadTask) Cancel() { 221 t.m.Lock() 222 defer t.m.Unlock() 223 t.Cancelled = true 224 close(t.CancelChan) 225 } 226 227 type membersReevaluationTask struct { 228 lastStartTime time.Time 229 lastSuccessTime time.Time 230 onDemandRequestTime time.Time 231 mutex sync.Mutex 232 } 233 234 type managerOptions struct { 235 accountsManager account.Manager 236 tokenManager TokenManager 237 collectiblesManager CollectiblesManager 238 walletConfig *params.WalletConfig 239 communityTokensService CommunityTokensServiceInterface 240 permissionChecker PermissionChecker 241 242 // allowForcingCommunityMembersReevaluation indicates whether we should allow forcing community members reevaluation. 243 // This will allow using `force` argument in ScheduleMembersReevaluation. 244 // Should only be used in tests. 245 allowForcingCommunityMembersReevaluation bool 246 } 247 248 type TokenManager interface { 249 GetBalancesByChain(ctx context.Context, accounts, tokens []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) 250 GetCachedBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) 251 FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address gethcommon.Address) *token.Token 252 GetAllChainIDs() ([]uint64, error) 253 } 254 255 type CollectibleContractData struct { 256 TotalSupply *bigint.BigInt 257 Transferable bool 258 RemoteBurnable bool 259 InfiniteSupply bool 260 } 261 262 type AssetContractData struct { 263 TotalSupply *bigint.BigInt 264 InfiniteSupply bool 265 } 266 267 type CommunityTokensServiceInterface interface { 268 GetCollectibleContractData(chainID uint64, contractAddress string) (*CollectibleContractData, error) 269 SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error) 270 GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error) 271 SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error) 272 DeploymentSignatureDigest(chainID uint64, addressFrom string, communityID string) ([]byte, error) 273 ProcessCommunityTokenAction(message *protobuf.CommunityTokenAction) error 274 } 275 276 type DefaultTokenManager struct { 277 tokenManager *token.Manager 278 networkManager network.ManagerInterface 279 } 280 281 func NewDefaultTokenManager(tm *token.Manager, nm network.ManagerInterface) *DefaultTokenManager { 282 return &DefaultTokenManager{tokenManager: tm, networkManager: nm} 283 } 284 285 type BalancesByChain = map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big 286 287 func (m *DefaultTokenManager) GetAllChainIDs() ([]uint64, error) { 288 networks, err := m.networkManager.Get(false) 289 if err != nil { 290 return nil, err 291 } 292 293 areTestNetworksEnabled, err := m.networkManager.GetTestNetworksEnabled() 294 if err != nil { 295 return nil, err 296 } 297 298 chainIDs := make([]uint64, 0) 299 for _, network := range networks { 300 if areTestNetworksEnabled == network.IsTest { 301 chainIDs = append(chainIDs, network.ChainID) 302 } 303 } 304 return chainIDs, nil 305 } 306 307 type CollectiblesManager interface { 308 FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) 309 FetchCachedBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) 310 GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) 311 FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletcommon.ChainID, contractAddress gethcommon.Address) (*thirdparty.CollectibleContractOwnership, error) 312 } 313 314 func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) { 315 clients, err := m.tokenManager.RPCClient.EthClients(chainIDs) 316 if err != nil { 317 return nil, err 318 } 319 320 resp, err := m.tokenManager.GetBalancesByChain(context.Background(), clients, accounts, tokenAddresses) 321 return resp, err 322 } 323 324 func (m *DefaultTokenManager) GetCachedBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) { 325 resp, err := m.tokenManager.GetCachedBalancesByChain(accounts, tokenAddresses, chainIDs) 326 if err != nil { 327 return resp, err 328 } 329 330 return resp, nil 331 } 332 333 func (m *DefaultTokenManager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address gethcommon.Address) *token.Token { 334 return m.tokenManager.FindOrCreateTokenByAddress(ctx, chainID, address) 335 } 336 337 type ManagerOption func(*managerOptions) 338 339 func WithAccountManager(accountsManager account.Manager) ManagerOption { 340 return func(opts *managerOptions) { 341 opts.accountsManager = accountsManager 342 } 343 } 344 345 func WithPermissionChecker(permissionChecker PermissionChecker) ManagerOption { 346 return func(opts *managerOptions) { 347 opts.permissionChecker = permissionChecker 348 } 349 } 350 351 func WithCollectiblesManager(collectiblesManager CollectiblesManager) ManagerOption { 352 return func(opts *managerOptions) { 353 opts.collectiblesManager = collectiblesManager 354 } 355 } 356 357 func WithTokenManager(tokenManager TokenManager) ManagerOption { 358 return func(opts *managerOptions) { 359 opts.tokenManager = tokenManager 360 } 361 } 362 363 func WithWalletConfig(walletConfig *params.WalletConfig) ManagerOption { 364 return func(opts *managerOptions) { 365 opts.walletConfig = walletConfig 366 } 367 } 368 369 func WithCommunityTokensService(communityTokensService CommunityTokensServiceInterface) ManagerOption { 370 return func(opts *managerOptions) { 371 opts.communityTokensService = communityTokensService 372 } 373 } 374 375 func WithAllowForcingCommunityMembersReevaluation(enabled bool) ManagerOption { 376 return func(opts *managerOptions) { 377 opts.allowForcingCommunityMembersReevaluation = enabled 378 } 379 } 380 381 type OwnerVerifier interface { 382 SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error) 383 } 384 385 func NewManager( 386 identity *ecdsa.PrivateKey, 387 installationID string, 388 db *sql.DB, 389 encryptor *encryption.Protocol, 390 logger *zap.Logger, 391 ensverifier *ens.Verifier, 392 ownerVerifier OwnerVerifier, 393 transport *transport.Transport, 394 timesource common.TimeSource, 395 keyDistributor KeyDistributor, 396 mediaServer server.MediaServerInterface, 397 opts ...ManagerOption, 398 ) (*Manager, error) { 399 if identity == nil { 400 return nil, errors.New("empty identity") 401 } 402 403 if timesource == nil { 404 return nil, errors.New("no timesource") 405 } 406 407 var err error 408 if logger == nil { 409 if logger, err = zap.NewDevelopment(); err != nil { 410 return nil, errors.Wrap(err, "failed to create a logger") 411 } 412 } 413 414 managerConfig := managerOptions{} 415 for _, opt := range opts { 416 opt(&managerConfig) 417 } 418 419 manager := &Manager{ 420 logger: logger, 421 encryptor: encryptor, 422 identity: identity, 423 installationID: installationID, 424 ownerVerifier: ownerVerifier, 425 quit: make(chan struct{}), 426 transport: transport, 427 timesource: timesource, 428 keyDistributor: keyDistributor, 429 communityLock: NewCommunityLock(logger), 430 mediaServer: mediaServer, 431 } 432 433 manager.persistence = &Persistence{ 434 db: db, 435 recordBundleToCommunity: manager.dbRecordBundleToCommunity, 436 } 437 438 if managerConfig.accountsManager != nil { 439 manager.accountsManager = managerConfig.accountsManager 440 } 441 442 if managerConfig.collectiblesManager != nil { 443 manager.collectiblesManager = managerConfig.collectiblesManager 444 } 445 446 if managerConfig.tokenManager != nil { 447 manager.tokenManager = managerConfig.tokenManager 448 } 449 450 if managerConfig.walletConfig != nil { 451 manager.walletConfig = managerConfig.walletConfig 452 } 453 454 if managerConfig.communityTokensService != nil { 455 manager.communityTokensService = managerConfig.communityTokensService 456 } 457 458 if ensverifier != nil { 459 sub := ensverifier.Subscribe() 460 manager.ensSubscription = sub 461 manager.ensVerifier = ensverifier 462 } 463 464 if managerConfig.permissionChecker != nil { 465 manager.PermissionChecker = managerConfig.permissionChecker 466 } else { 467 manager.PermissionChecker = &DefaultPermissionChecker{ 468 tokenManager: manager.tokenManager, 469 collectiblesManager: manager.collectiblesManager, 470 logger: logger, 471 ensVerifier: ensverifier, 472 } 473 } 474 475 if managerConfig.allowForcingCommunityMembersReevaluation { 476 manager.logger.Warn("allowing forcing community members reevaluation, this should only be used in test environment") 477 manager.forceMembersReevaluation = make(map[string]chan struct{}, 10) 478 } 479 480 return manager, nil 481 } 482 483 type Subscription struct { 484 Community *Community 485 CreatingHistoryArchivesSignal *signal.CreatingHistoryArchivesSignal 486 HistoryArchivesCreatedSignal *signal.HistoryArchivesCreatedSignal 487 NoHistoryArchivesCreatedSignal *signal.NoHistoryArchivesCreatedSignal 488 HistoryArchivesSeedingSignal *signal.HistoryArchivesSeedingSignal 489 HistoryArchivesUnseededSignal *signal.HistoryArchivesUnseededSignal 490 HistoryArchiveDownloadedSignal *signal.HistoryArchiveDownloadedSignal 491 DownloadingHistoryArchivesStartedSignal *signal.DownloadingHistoryArchivesStartedSignal 492 DownloadingHistoryArchivesFinishedSignal *signal.DownloadingHistoryArchivesFinishedSignal 493 ImportingHistoryArchiveMessagesSignal *signal.ImportingHistoryArchiveMessagesSignal 494 CommunityEventsMessage *CommunityEventsMessage 495 AcceptedRequestsToJoin []types.HexBytes 496 RejectedRequestsToJoin []types.HexBytes 497 CommunityPrivilegedMemberSyncMessage *CommunityPrivilegedMemberSyncMessage 498 TokenCommunityValidated *CommunityResponse 499 } 500 501 type CommunityResponse struct { 502 Community *Community `json:"community"` 503 Changes *CommunityChanges `json:"changes"` 504 RequestsToJoin []*RequestToJoin `json:"requestsToJoin"` 505 FailedToDecrypt []*CommunityPrivateDataFailedToDecrypt `json:"-"` 506 } 507 508 func (m *Manager) SetMediaServer(mediaServer server.MediaServerInterface) { 509 m.mediaServer = mediaServer 510 } 511 512 func (m *Manager) Subscribe() chan *Subscription { 513 subscription := make(chan *Subscription, 100) 514 m.subscriptions = append(m.subscriptions, subscription) 515 return subscription 516 } 517 518 func (m *Manager) Start() error { 519 m.stopped = false 520 m.communityLock.Init() 521 if m.ensVerifier != nil { 522 m.runENSVerificationLoop() 523 } 524 525 if m.ownerVerifier != nil { 526 m.runOwnerVerificationLoop() 527 } 528 529 go func() { 530 _ = m.fillMissingCommunityTokens() 531 }() 532 533 return nil 534 } 535 536 func (m *Manager) runENSVerificationLoop() { 537 go func() { 538 for { 539 select { 540 case <-m.quit: 541 m.logger.Debug("quitting ens verification loop") 542 return 543 case records, more := <-m.ensSubscription: 544 if !more { 545 m.logger.Debug("no more ens records, quitting") 546 return 547 } 548 m.logger.Info("received records", zap.Any("records", records)) 549 } 550 } 551 }() 552 } 553 554 // This function is mostly a way to fix any community that is missing tokens in its description 555 func (m *Manager) fillMissingCommunityTokens() error { 556 controlledCommunities, err := m.Controlled() 557 if err != nil { 558 m.logger.Error("failed to retrieve orgs", zap.Error(err)) 559 return err 560 } 561 562 unlock := func() { 563 for _, c := range controlledCommunities { 564 m.communityLock.Unlock(c.ID()) 565 } 566 } 567 for _, c := range controlledCommunities { 568 m.communityLock.Lock(c.ID()) 569 } 570 defer unlock() 571 572 for _, community := range controlledCommunities { 573 tokens, err := m.GetCommunityTokens(community.IDString()) 574 if err != nil { 575 m.logger.Error("failed to retrieve community tokens", zap.Error(err)) 576 return err 577 } 578 579 for _, token := range tokens { 580 if token.DeployState != community_token.Deployed { 581 continue 582 } 583 tokenMetadata := &protobuf.CommunityTokenMetadata{ 584 ContractAddresses: map[uint64]string{uint64(token.ChainID): token.Address}, 585 Description: token.Description, 586 Image: token.Base64Image, 587 Symbol: token.Symbol, 588 TokenType: token.TokenType, 589 Name: token.Name, 590 Decimals: uint32(token.Decimals), 591 Version: token.Version, 592 } 593 modified, err := community.UpsertCommunityTokensMetadata(tokenMetadata) 594 if err != nil { 595 m.logger.Error("failed to add token metadata to the description", zap.Error(err)) 596 return err 597 } 598 if modified { 599 err = m.saveAndPublish(community) 600 if err != nil { 601 m.logger.Error("failed to save the new community", zap.Error(err)) 602 return err 603 } 604 } 605 } 606 } 607 return nil 608 } 609 610 // Only for testing 611 func (m *Manager) CommunitiesToValidate() (map[string][]communityToValidate, error) { // nolint: golint 612 return m.persistence.getCommunitiesToValidate() 613 } 614 615 func (m *Manager) runOwnerVerificationLoop() { 616 m.logger.Info("starting owner verification loop") 617 go func() { 618 for { 619 select { 620 case <-m.quit: 621 m.logger.Debug("quitting owner verification loop") 622 return 623 case <-time.After(validateInterval): 624 // If ownerverifier is nil, we skip, this is useful for testing 625 if m.ownerVerifier == nil { 626 continue 627 } 628 629 communitiesToValidate, err := m.persistence.getCommunitiesToValidate() 630 631 if err != nil { 632 m.logger.Error("failed to fetch communities to validate", zap.Error(err)) 633 continue 634 } 635 for id, communities := range communitiesToValidate { 636 m.logger.Info("validating communities", zap.String("id", id), zap.Int("count", len(communities))) 637 638 _, _ = m.validateCommunity(communities) 639 } 640 } 641 } 642 }() 643 } 644 645 func (m *Manager) ValidateCommunityByID(communityID types.HexBytes) (*CommunityResponse, error) { 646 communitiesToValidate, err := m.persistence.getCommunityToValidateByID(communityID) 647 if err != nil { 648 m.logger.Error("failed to validate community by ID", zap.String("id", communityID.String()), zap.Error(err)) 649 return nil, err 650 } 651 return m.validateCommunity(communitiesToValidate) 652 653 } 654 655 func (m *Manager) validateCommunity(communityToValidateData []communityToValidate) (*CommunityResponse, error) { 656 for _, community := range communityToValidateData { 657 signer, description, err := UnwrapCommunityDescriptionMessage(community.payload) 658 if err != nil { 659 m.logger.Error("failed to unwrap community", zap.Error(err)) 660 continue 661 } 662 663 chainID := CommunityDescriptionTokenOwnerChainID(description) 664 if chainID == 0 { 665 // This should not happen 666 m.logger.Error("chain id is 0, ignoring") 667 continue 668 } 669 670 m.logger.Info("validating community", zap.String("id", types.EncodeHex(community.id)), zap.String("signer", common.PubkeyToHex(signer))) 671 672 ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 673 defer cancel() 674 675 owner, err := m.ownerVerifier.SafeGetSignerPubKey(ctx, chainID, types.EncodeHex(community.id)) 676 if err != nil { 677 m.logger.Error("failed to get owner", zap.Error(err)) 678 continue 679 } 680 681 ownerPK, err := common.HexToPubkey(owner) 682 if err != nil { 683 m.logger.Error("failed to convert pk string to ecdsa", zap.Error(err)) 684 continue 685 } 686 687 // TODO: handle shards 688 response, err := m.HandleCommunityDescriptionMessage(signer, description, community.payload, ownerPK, nil) 689 if err != nil { 690 m.logger.Error("failed to handle community", zap.Error(err)) 691 err = m.persistence.DeleteCommunityToValidate(community.id, community.clock) 692 if err != nil { 693 m.logger.Error("failed to delete community to validate", zap.Error(err)) 694 } 695 continue 696 } 697 698 if response != nil { 699 700 m.logger.Info("community validated", zap.String("id", types.EncodeHex(community.id)), zap.String("signer", common.PubkeyToHex(signer))) 701 m.publish(&Subscription{TokenCommunityValidated: response}) 702 err := m.persistence.DeleteCommunitiesToValidateByCommunityID(community.id) 703 if err != nil { 704 m.logger.Error("failed to delete communities to validate", zap.Error(err)) 705 } 706 return response, nil 707 } 708 } 709 710 return nil, nil 711 } 712 713 func (m *Manager) Stop() error { 714 m.stopped = true 715 close(m.quit) 716 for _, c := range m.subscriptions { 717 close(c) 718 } 719 return nil 720 } 721 722 func (m *Manager) publish(subscription *Subscription) { 723 if m.stopped { 724 return 725 } 726 for _, s := range m.subscriptions { 727 select { 728 case s <- subscription: 729 default: 730 m.logger.Warn("subscription channel full, dropping message") 731 } 732 } 733 } 734 735 func (m *Manager) All() ([]*Community, error) { 736 return m.persistence.AllCommunities(&m.identity.PublicKey) 737 } 738 739 type CommunityShard struct { 740 CommunityID string `json:"communityID"` 741 Shard *shard.Shard `json:"shard"` 742 } 743 744 type CuratedCommunities struct { 745 ContractCommunities []string 746 ContractFeaturedCommunities []string 747 } 748 749 type KnownCommunitiesResponse struct { 750 ContractCommunities []string `json:"contractCommunities"` 751 ContractFeaturedCommunities []string `json:"contractFeaturedCommunities"` 752 Descriptions map[string]*Community `json:"communities"` 753 UnknownCommunities []string `json:"unknownCommunities"` 754 } 755 756 func (m *Manager) GetStoredDescriptionForCommunities(communityIDs []string) (*KnownCommunitiesResponse, error) { 757 response := &KnownCommunitiesResponse{ 758 Descriptions: make(map[string]*Community), 759 } 760 761 for i := range communityIDs { 762 communityID := communityIDs[i] 763 communityIDBytes, err := types.DecodeHex(communityID) 764 if err != nil { 765 return nil, err 766 } 767 768 community, err := m.GetByID(types.HexBytes(communityIDBytes)) 769 if err != nil && err != ErrOrgNotFound { 770 return nil, err 771 } 772 773 if community != nil { 774 response.Descriptions[community.IDString()] = community 775 } else { 776 response.UnknownCommunities = append(response.UnknownCommunities, communityID) 777 } 778 779 response.ContractCommunities = append(response.ContractCommunities, communityID) 780 } 781 782 return response, nil 783 } 784 785 func (m *Manager) Joined() ([]*Community, error) { 786 return m.persistence.JoinedCommunities(&m.identity.PublicKey) 787 } 788 789 func (m *Manager) Spectated() ([]*Community, error) { 790 return m.persistence.SpectatedCommunities(&m.identity.PublicKey) 791 } 792 793 func (m *Manager) CommunityUpdateLastOpenedAt(communityID types.HexBytes, timestamp int64) (*Community, error) { 794 m.communityLock.Lock(communityID) 795 defer m.communityLock.Unlock(communityID) 796 797 community, err := m.GetByID(communityID) 798 if err != nil { 799 return nil, err 800 } 801 802 err = m.persistence.UpdateLastOpenedAt(community.ID(), timestamp) 803 if err != nil { 804 return nil, err 805 } 806 community.UpdateLastOpenedAt(timestamp) 807 return community, nil 808 } 809 810 func (m *Manager) JoinedAndPendingCommunitiesWithRequests() ([]*Community, error) { 811 return m.persistence.JoinedAndPendingCommunitiesWithRequests(&m.identity.PublicKey) 812 } 813 814 func (m *Manager) DeletedCommunities() ([]*Community, error) { 815 return m.persistence.DeletedCommunities(&m.identity.PublicKey) 816 } 817 818 func (m *Manager) Controlled() ([]*Community, error) { 819 communities, err := m.persistence.CommunitiesWithPrivateKey(&m.identity.PublicKey) 820 if err != nil { 821 return nil, err 822 } 823 824 controlled := make([]*Community, 0, len(communities)) 825 826 for _, c := range communities { 827 if c.IsControlNode() { 828 controlled = append(controlled, c) 829 } 830 } 831 832 return controlled, nil 833 } 834 835 // CreateCommunity takes a description, generates an ID for it, saves it and return it 836 func (m *Manager) CreateCommunity(request *requests.CreateCommunity, publish bool) (*Community, error) { 837 838 description, err := request.ToCommunityDescription() 839 if err != nil { 840 return nil, err 841 } 842 843 description.Members = make(map[string]*protobuf.CommunityMember) 844 description.Members[common.PubkeyToHex(&m.identity.PublicKey)] = &protobuf.CommunityMember{Roles: []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_OWNER}} 845 846 err = ValidateCommunityDescription(description) 847 if err != nil { 848 return nil, err 849 } 850 851 description.Clock = 1 852 853 key, err := crypto.GenerateKey() 854 if err != nil { 855 return nil, err 856 } 857 858 description.ID = types.EncodeHex(crypto.CompressPubkey(&key.PublicKey)) 859 860 config := Config{ 861 ID: &key.PublicKey, 862 PrivateKey: key, 863 ControlNode: &key.PublicKey, 864 ControlDevice: true, 865 Logger: m.logger, 866 Joined: true, 867 JoinedAt: time.Now().Unix(), 868 MemberIdentity: m.identity, 869 CommunityDescription: description, 870 Shard: nil, 871 LastOpenedAt: 0, 872 } 873 874 var descriptionEncryptor DescriptionEncryptor 875 if m.encryptor != nil { 876 descriptionEncryptor = m 877 } 878 community, err := New(config, m.timesource, descriptionEncryptor, m.mediaServer) 879 if err != nil { 880 return nil, err 881 } 882 883 // We join any community we create 884 community.Join() 885 886 err = m.persistence.SaveCommunity(community) 887 if err != nil { 888 return nil, err 889 } 890 891 // Save grant for own community 892 grant, err := community.BuildGrant(&m.identity.PublicKey, "") 893 if err != nil { 894 return nil, err 895 } 896 err = m.persistence.SaveCommunityGrant(community.IDString(), grant, uint64(time.Now().UnixMilli())) 897 if err != nil { 898 return nil, err 899 } 900 901 // Mark this device as the control node 902 syncControlNode := &protobuf.SyncCommunityControlNode{ 903 Clock: 1, 904 InstallationId: m.installationID, 905 } 906 err = m.SaveSyncControlNode(community.ID(), syncControlNode) 907 if err != nil { 908 return nil, err 909 } 910 911 if publish { 912 m.publish(&Subscription{Community: community}) 913 } 914 915 return community, nil 916 } 917 918 func (m *Manager) CreateCommunityTokenPermission(request *requests.CreateCommunityTokenPermission) (*Community, *CommunityChanges, error) { 919 m.communityLock.Lock(request.CommunityID) 920 defer m.communityLock.Unlock(request.CommunityID) 921 922 community, err := m.GetByID(request.CommunityID) 923 if err != nil { 924 return nil, nil, err 925 } 926 927 // ensure key is generated before marshaling, 928 // as it requires key to encrypt description 929 if community.IsControlNode() && m.encryptor != nil { 930 key, err := m.encryptor.GenerateHashRatchetKey(community.ID()) 931 if err != nil { 932 return nil, nil, err 933 } 934 keyID, err := key.GetKeyID() 935 if err != nil { 936 return nil, nil, err 937 } 938 m.logger.Info("generate key for token", zap.String("group-id", types.Bytes2Hex(community.ID())), zap.String("key-id", types.Bytes2Hex(keyID))) 939 } 940 941 community, changes, err := m.createCommunityTokenPermission(request, community) 942 if err != nil { 943 return nil, nil, err 944 } 945 946 err = m.saveAndPublish(community) 947 if err != nil { 948 return nil, nil, err 949 } 950 951 return community, changes, nil 952 } 953 954 func (m *Manager) EditCommunityTokenPermission(request *requests.EditCommunityTokenPermission) (*Community, *CommunityChanges, error) { 955 m.communityLock.Lock(request.CommunityID) 956 defer m.communityLock.Unlock(request.CommunityID) 957 958 community, err := m.GetByID(request.CommunityID) 959 if err != nil { 960 return nil, nil, err 961 } 962 963 tokenPermission := request.ToCommunityTokenPermission() 964 965 changes, err := community.UpsertTokenPermission(&tokenPermission) 966 if err != nil { 967 return nil, nil, err 968 } 969 970 err = m.saveAndPublish(community) 971 if err != nil { 972 return nil, nil, err 973 } 974 975 return community, changes, nil 976 } 977 978 type reevaluateMemberRole struct { 979 old protobuf.CommunityMember_Roles 980 new protobuf.CommunityMember_Roles 981 } 982 983 func (rmr reevaluateMemberRole) hasChanged() bool { 984 return rmr.old != rmr.new 985 } 986 987 func (rmr reevaluateMemberRole) isPrivileged() bool { 988 return rmr.new != protobuf.CommunityMember_ROLE_NONE 989 } 990 991 func (rmr reevaluateMemberRole) hasChangedToPrivileged() bool { 992 return rmr.hasChanged() && rmr.old == protobuf.CommunityMember_ROLE_NONE 993 } 994 995 func (rmr reevaluateMemberRole) hasChangedPrivilegedRole() bool { 996 return (rmr.old == protobuf.CommunityMember_ROLE_ADMIN && rmr.new == protobuf.CommunityMember_ROLE_TOKEN_MASTER) || 997 (rmr.old == protobuf.CommunityMember_ROLE_TOKEN_MASTER && rmr.new == protobuf.CommunityMember_ROLE_ADMIN) 998 } 999 1000 type reevaluateMembersResult struct { 1001 membersToRemove map[string]struct{} 1002 membersRoles map[string]*reevaluateMemberRole 1003 membersToRemoveFromChannels map[string]map[string]struct{} 1004 membersToAddToChannels map[string]map[string]protobuf.CommunityMember_ChannelRole 1005 } 1006 1007 func (rmr *reevaluateMembersResult) newPrivilegedRoles() (map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) { 1008 result := map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey{} 1009 1010 for memberKey, roles := range rmr.membersRoles { 1011 if roles.hasChangedToPrivileged() || roles.hasChangedPrivilegedRole() { 1012 memberPubKey, err := common.HexToPubkey(memberKey) 1013 if err != nil { 1014 return nil, err 1015 } 1016 if result[roles.new] == nil { 1017 result[roles.new] = []*ecdsa.PublicKey{} 1018 } 1019 result[roles.new] = append(result[roles.new], memberPubKey) 1020 } 1021 } 1022 1023 return result, nil 1024 } 1025 1026 // Fetch all owners for all collectibles. 1027 func (m *Manager) fetchCollectiblesOwners(collectibles map[walletcommon.ChainID]map[gethcommon.Address]struct{}) (CollectiblesOwners, error) { 1028 if m.collectiblesManager == nil { 1029 return nil, errors.New("no collectibles manager") 1030 } 1031 1032 collectiblesOwners := make(CollectiblesOwners) 1033 for chainID, contractAddresses := range collectibles { 1034 collectiblesOwners[chainID] = make(map[gethcommon.Address]*thirdparty.CollectibleContractOwnership) 1035 1036 for contractAddress := range contractAddresses { 1037 ownership, err := m.collectiblesManager.FetchCollectibleOwnersByContractAddress(context.Background(), chainID, contractAddress) 1038 if err != nil { 1039 return nil, err 1040 } 1041 collectiblesOwners[chainID][contractAddress] = ownership 1042 } 1043 } 1044 return collectiblesOwners, nil 1045 } 1046 1047 // use it only for testing purposes 1048 func (m *Manager) ReevaluateMembers(communityID types.HexBytes) (*Community, map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) { 1049 return m.reevaluateMembers(communityID) 1050 } 1051 1052 // First, the community is read from the database, 1053 // then the members are reevaluated, and only then 1054 // the community is locked and changes are applied. 1055 // NOTE: Changes made to the same community 1056 // while reevaluation is ongoing are respected 1057 // and do not affect the result of this function. 1058 // If permissions are changed in the meantime, 1059 // they will be accommodated with the next reevaluation. 1060 func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) { 1061 community, err := m.GetByID(communityID) 1062 if err != nil { 1063 return nil, nil, err 1064 } 1065 1066 if !community.IsControlNode() { 1067 return nil, nil, ErrNotEnoughPermissions 1068 } 1069 1070 communityPermissionsPreParsedData, channelPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions()) 1071 1072 // Optimization: Fetch all collectibles owners before members iteration to avoid asking providers for the same collectibles. 1073 collectiblesOwners, err := m.fetchCollectiblesOwners(CollectibleAddressesFromPreParsedPermissionsData(communityPermissionsPreParsedData, channelPermissionsPreParsedData)) 1074 if err != nil { 1075 return nil, nil, err 1076 } 1077 1078 result := &reevaluateMembersResult{ 1079 membersToRemove: map[string]struct{}{}, 1080 membersRoles: map[string]*reevaluateMemberRole{}, 1081 membersToRemoveFromChannels: map[string]map[string]struct{}{}, 1082 membersToAddToChannels: map[string]map[string]protobuf.CommunityMember_ChannelRole{}, 1083 } 1084 1085 membersAccounts, err := m.persistence.GetCommunityRequestsToJoinRevealedAddresses(community.ID()) 1086 if err != nil { 1087 return nil, nil, err 1088 } 1089 1090 for memberKey := range community.Members() { 1091 memberPubKey, err := common.HexToPubkey(memberKey) 1092 if err != nil { 1093 return nil, nil, err 1094 } 1095 1096 if memberKey == common.PubkeyToHex(&m.identity.PublicKey) || community.IsMemberOwner(memberPubKey) { 1097 continue 1098 } 1099 1100 revealedAccount, memberHasWallet := membersAccounts[memberKey] 1101 if !memberHasWallet { 1102 result.membersToRemove[memberKey] = struct{}{} 1103 continue 1104 } 1105 1106 accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccount) 1107 1108 result.membersRoles[memberKey] = &reevaluateMemberRole{ 1109 old: community.MemberRole(memberPubKey), 1110 new: protobuf.CommunityMember_ROLE_NONE, 1111 } 1112 1113 becomeTokenMasterPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER] 1114 if becomeTokenMasterPermissions != nil { 1115 permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeTokenMasterPermissions, accountsAndChainIDs, true, collectiblesOwners) 1116 if err != nil { 1117 return nil, nil, err 1118 } 1119 1120 if permissionResponse.Satisfied { 1121 result.membersRoles[memberKey].new = protobuf.CommunityMember_ROLE_TOKEN_MASTER 1122 // Skip further validation if user has TokenMaster permissions 1123 continue 1124 } 1125 } 1126 1127 becomeAdminPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN] 1128 if becomeAdminPermissions != nil { 1129 permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeAdminPermissions, accountsAndChainIDs, true, collectiblesOwners) 1130 if err != nil { 1131 return nil, nil, err 1132 } 1133 1134 if permissionResponse.Satisfied { 1135 result.membersRoles[memberKey].new = protobuf.CommunityMember_ROLE_ADMIN 1136 // Skip further validation if user has Admin permissions 1137 continue 1138 } 1139 } 1140 1141 becomeMemberPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER] 1142 if becomeMemberPermissions != nil { 1143 permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeMemberPermissions, accountsAndChainIDs, true, collectiblesOwners) 1144 if err != nil { 1145 return nil, nil, err 1146 } 1147 1148 if !permissionResponse.Satisfied { 1149 result.membersToRemove[memberKey] = struct{}{} 1150 // Skip channels validation if user has been removed 1151 continue 1152 } 1153 } 1154 1155 addToChannels, removeFromChannels, err := m.reevaluateMemberChannelsPermissions(community, memberPubKey, channelPermissionsPreParsedData, accountsAndChainIDs, collectiblesOwners) 1156 if err != nil { 1157 return nil, nil, err 1158 } 1159 result.membersToAddToChannels[memberKey] = addToChannels 1160 result.membersToRemoveFromChannels[memberKey] = removeFromChannels 1161 } 1162 1163 newPrivilegedRoles, err := result.newPrivilegedRoles() 1164 if err != nil { 1165 return nil, nil, err 1166 } 1167 1168 // Note: community itself may have changed in the meantime of permissions reevaluation. 1169 community, err = m.applyReevaluateMembersResult(communityID, result) 1170 if err != nil { 1171 return nil, nil, err 1172 } 1173 1174 return community, newPrivilegedRoles, m.saveAndPublish(community) 1175 } 1176 1177 // Apply results on the most up-to-date community. 1178 func (m *Manager) applyReevaluateMembersResult(communityID types.HexBytes, result *reevaluateMembersResult) (*Community, error) { 1179 m.communityLock.Lock(communityID) 1180 defer m.communityLock.Unlock(communityID) 1181 1182 community, err := m.GetByID(communityID) 1183 if err != nil { 1184 return nil, err 1185 } 1186 1187 if !community.IsControlNode() { 1188 return nil, ErrNotEnoughPermissions 1189 } 1190 1191 // Remove members. 1192 for memberKey := range result.membersToRemove { 1193 memberPubKey, err := common.HexToPubkey(memberKey) 1194 if err != nil { 1195 return nil, err 1196 } 1197 _, err = community.RemoveUserFromOrg(memberPubKey) 1198 if err != nil { 1199 return nil, err 1200 } 1201 } 1202 1203 // Ensure members have proper roles. 1204 for memberKey, roles := range result.membersRoles { 1205 memberPubKey, err := common.HexToPubkey(memberKey) 1206 if err != nil { 1207 return nil, err 1208 } 1209 1210 if !community.HasMember(memberPubKey) { 1211 continue 1212 } 1213 1214 _, err = community.SetRoleToMember(memberPubKey, roles.new) 1215 if err != nil { 1216 return nil, err 1217 } 1218 1219 // Ensure privileged members can post in all chats. 1220 if roles.isPrivileged() { 1221 for channelID := range community.Chats() { 1222 _, err = community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{roles.new}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 1223 if err != nil { 1224 return nil, err 1225 } 1226 } 1227 } 1228 } 1229 1230 // Remove members from channels. 1231 for memberKey, channels := range result.membersToRemoveFromChannels { 1232 memberPubKey, err := common.HexToPubkey(memberKey) 1233 if err != nil { 1234 return nil, err 1235 } 1236 1237 for channelID := range channels { 1238 _, err = community.RemoveUserFromChat(memberPubKey, channelID) 1239 if err != nil { 1240 return nil, err 1241 } 1242 } 1243 } 1244 1245 // Add unprivileged members to channels. 1246 for memberKey, channels := range result.membersToAddToChannels { 1247 memberPubKey, err := common.HexToPubkey(memberKey) 1248 if err != nil { 1249 return nil, err 1250 } 1251 1252 if !community.HasMember(memberPubKey) { 1253 continue 1254 } 1255 1256 for channelID, channelRole := range channels { 1257 _, err = community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_NONE}, channelRole) 1258 if err != nil { 1259 return nil, err 1260 } 1261 } 1262 } 1263 1264 return community, nil 1265 } 1266 1267 func (m *Manager) reevaluateMemberChannelsPermissions(community *Community, memberPubKey *ecdsa.PublicKey, 1268 channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, collectiblesOwners CollectiblesOwners) (map[string]protobuf.CommunityMember_ChannelRole, map[string]struct{}, error) { 1269 1270 addToChannels := map[string]protobuf.CommunityMember_ChannelRole{} 1271 removeFromChannels := map[string]struct{}{} 1272 1273 // check which permissions we satisfy and which not 1274 channelPermissionsCheckResult, err := m.checkChannelsPermissionsWithPreFetchedData(channelPermissionsPreParsedData, accountsAndChainIDs, true, collectiblesOwners) 1275 if err != nil { 1276 return nil, nil, err 1277 } 1278 1279 for channelID := range community.Chats() { 1280 channelPermissionsCheckResult, hasChannelPermission := channelPermissionsCheckResult[community.ChatID(channelID)] 1281 1282 // ensure member is added if channel has no permissions 1283 if !hasChannelPermission { 1284 addToChannels[channelID] = protobuf.CommunityMember_CHANNEL_ROLE_POSTER 1285 continue 1286 } 1287 1288 viewAndPostSatisfied, viewAndPostPermissionExists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL] 1289 viewOnlySatisfied, viewOnlyPermissionExists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL] 1290 1291 satisfied := false 1292 channelRole := protobuf.CommunityMember_CHANNEL_ROLE_VIEWER 1293 if viewAndPostPermissionExists && viewAndPostSatisfied { 1294 satisfied = viewAndPostSatisfied 1295 channelRole = protobuf.CommunityMember_CHANNEL_ROLE_POSTER 1296 } else if !satisfied && viewOnlyPermissionExists { 1297 satisfied = viewOnlySatisfied 1298 } 1299 1300 if satisfied { 1301 addToChannels[channelID] = channelRole 1302 } else { 1303 removeFromChannels[channelID] = struct{}{} 1304 } 1305 } 1306 1307 return addToChannels, removeFromChannels, nil 1308 } 1309 1310 func (m *Manager) checkChannelsPermissionsImpl(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) { 1311 checkPermissions := func(channelsPermissionPreParsedData *PreParsedCommunityPermissionsData) (*CheckPermissionsResponse, error) { 1312 if collectiblesOwners != nil { 1313 return m.PermissionChecker.CheckPermissionsWithPreFetchedData(channelsPermissionPreParsedData, accountsAndChainIDs, true, collectiblesOwners) 1314 } else { 1315 return m.PermissionChecker.CheckPermissions(channelsPermissionPreParsedData, accountsAndChainIDs, true) 1316 } 1317 } 1318 1319 channelPermissionsCheckResult := make(map[string]map[protobuf.CommunityTokenPermission_Type]bool) 1320 for _, channelsPermissionPreParsedData := range channelsPermissionsPreParsedData { 1321 permissionResponse, err := checkPermissions(channelsPermissionPreParsedData) 1322 if err != nil { 1323 return channelPermissionsCheckResult, err 1324 } 1325 // Note: in `PreParsedCommunityPermissionsData` for channels there will be only one permission 1326 // no need to iterate over `Permissions` 1327 for _, chatId := range channelsPermissionPreParsedData.Permissions[0].ChatIds { 1328 if _, exists := channelPermissionsCheckResult[chatId]; !exists { 1329 channelPermissionsCheckResult[chatId] = make(map[protobuf.CommunityTokenPermission_Type]bool) 1330 } 1331 satisfied, exists := channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type] 1332 if exists && satisfied { 1333 continue 1334 } 1335 channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type] = permissionResponse.Satisfied 1336 } 1337 } 1338 return channelPermissionsCheckResult, nil 1339 } 1340 1341 func (m *Manager) checkChannelsPermissionsWithPreFetchedData(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) { 1342 return m.checkChannelsPermissionsImpl(channelsPermissionsPreParsedData, accountsAndChainIDs, shortcircuit, collectiblesOwners) 1343 } 1344 1345 func (m *Manager) checkChannelsPermissions(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) { 1346 return m.checkChannelsPermissionsImpl(channelsPermissionsPreParsedData, accountsAndChainIDs, shortcircuit, nil) 1347 } 1348 1349 func (m *Manager) StartMembersReevaluationLoop(communityID types.HexBytes, reevaluateOnStart bool) { 1350 go m.reevaluateMembersLoop(communityID, reevaluateOnStart) 1351 } 1352 1353 func (m *Manager) reevaluateMembersLoop(communityID types.HexBytes, reevaluateOnStart bool) { 1354 1355 if _, exists := m.membersReevaluationTasks.Load(communityID.String()); exists { 1356 return 1357 } 1358 1359 m.membersReevaluationTasks.Store(communityID.String(), &membersReevaluationTask{}) 1360 defer m.membersReevaluationTasks.Delete(communityID.String()) 1361 1362 var forceReevaluation chan struct{} 1363 if m.forceMembersReevaluation != nil { 1364 forceReevaluation = make(chan struct{}, 10) 1365 m.forceMembersReevaluation[communityID.String()] = forceReevaluation 1366 } 1367 1368 type criticalError struct { 1369 error 1370 } 1371 1372 shouldReevaluate := func(task *membersReevaluationTask, force bool) bool { 1373 task.mutex.Lock() 1374 defer task.mutex.Unlock() 1375 1376 // Ensure reevaluation is performed not more often than once per 5 minutes 1377 if !force && task.lastSuccessTime.After(time.Now().Add(-5*time.Minute)) { 1378 return false 1379 } 1380 1381 if !task.lastSuccessTime.Before(time.Now().Add(-memberPermissionsCheckInterval)) && 1382 !task.lastStartTime.Before(task.onDemandRequestTime) { 1383 return false 1384 } 1385 1386 return true 1387 } 1388 1389 reevaluateMembers := func(force bool) (err error) { 1390 t, exists := m.membersReevaluationTasks.Load(communityID.String()) 1391 if !exists { 1392 return criticalError{ 1393 error: errors.New("missing task"), 1394 } 1395 } 1396 task, ok := t.(*membersReevaluationTask) 1397 if !ok { 1398 return criticalError{ 1399 error: errors.New("invalid task type"), 1400 } 1401 } 1402 1403 if !shouldReevaluate(task, force) { 1404 return nil 1405 } 1406 1407 task.mutex.Lock() 1408 task.lastStartTime = time.Now() 1409 task.mutex.Unlock() 1410 1411 err = m.reevaluateCommunityMembersPermissions(communityID) 1412 if err != nil { 1413 if errors.Is(err, ErrOrgNotFound) { 1414 return criticalError{ 1415 error: err, 1416 } 1417 } 1418 return err 1419 } 1420 1421 task.mutex.Lock() 1422 task.lastSuccessTime = time.Now() 1423 task.mutex.Unlock() 1424 1425 m.logger.Info("reevaluation finished", zap.String("communityID", communityID.String()), zap.Duration("elapsed", task.lastSuccessTime.Sub(task.lastStartTime))) 1426 return nil 1427 } 1428 1429 ticker := time.NewTicker(10 * time.Second) 1430 defer ticker.Stop() 1431 1432 reevaluate := reevaluateOnStart 1433 force := false 1434 1435 for { 1436 if reevaluate { 1437 err := reevaluateMembers(force) 1438 if err != nil { 1439 var criticalError *criticalError 1440 if errors.As(err, &criticalError) { 1441 return 1442 } 1443 } 1444 } 1445 1446 force = false 1447 reevaluate = false 1448 1449 select { 1450 case <-ticker.C: 1451 reevaluate = true 1452 continue 1453 1454 case <-forceReevaluation: 1455 reevaluate = true 1456 force = true 1457 continue 1458 1459 case <-m.quit: 1460 return 1461 } 1462 } 1463 } 1464 1465 func (m *Manager) ForceMembersReevaluation(communityID types.HexBytes) error { 1466 if m.forceMembersReevaluation == nil { 1467 return errors.New("forcing members reevaluation is not allowed") 1468 } 1469 return m.scheduleMembersReevaluation(communityID, true) 1470 } 1471 1472 func (m *Manager) ScheduleMembersReevaluation(communityID types.HexBytes) error { 1473 return m.scheduleMembersReevaluation(communityID, false) 1474 } 1475 1476 func (m *Manager) scheduleMembersReevaluation(communityID types.HexBytes, forceImmediateReevaluation bool) error { 1477 t, exists := m.membersReevaluationTasks.Load(communityID.String()) 1478 if !exists { 1479 // No reevaluation task yet. We start the loop which will create it 1480 m.StartMembersReevaluationLoop(communityID, true) 1481 return nil 1482 } 1483 1484 task, ok := t.(*membersReevaluationTask) 1485 if !ok { 1486 return errors.New("invalid task type") 1487 } 1488 task.mutex.Lock() 1489 defer task.mutex.Unlock() 1490 task.onDemandRequestTime = time.Now() 1491 1492 if forceImmediateReevaluation { 1493 m.forceMembersReevaluation[communityID.String()] <- struct{}{} 1494 } 1495 1496 return nil 1497 } 1498 1499 func (m *Manager) DeleteCommunityTokenPermission(request *requests.DeleteCommunityTokenPermission) (*Community, *CommunityChanges, error) { 1500 m.communityLock.Lock(request.CommunityID) 1501 defer m.communityLock.Unlock(request.CommunityID) 1502 1503 community, err := m.GetByID(request.CommunityID) 1504 if err != nil { 1505 return nil, nil, err 1506 } 1507 1508 changes, err := community.DeleteTokenPermission(request.PermissionID) 1509 if err != nil { 1510 return nil, nil, err 1511 } 1512 1513 err = m.saveAndPublish(community) 1514 if err != nil { 1515 return nil, nil, err 1516 } 1517 1518 return community, changes, nil 1519 } 1520 1521 func (m *Manager) reevaluateCommunityMembersPermissions(communityID types.HexBytes) error { 1522 // Publish when the reevluation started since it can take a while 1523 signal.SendCommunityMemberReevaluationStarted(types.EncodeHex(communityID)) 1524 1525 community, newPrivilegedMembers, err := m.reevaluateMembers(communityID) 1526 1527 // Publish the reevaluation ending, even if it errored 1528 // A possible improvement would be to pass the error here 1529 signal.SendCommunityMemberReevaluationEnded(types.EncodeHex(communityID)) 1530 1531 if err != nil { 1532 return err 1533 } 1534 1535 return m.ShareRequestsToJoinWithPrivilegedMembers(community, newPrivilegedMembers) 1536 } 1537 1538 func (m *Manager) DeleteCommunity(id types.HexBytes) error { 1539 m.communityLock.Lock(id) 1540 defer m.communityLock.Unlock(id) 1541 1542 err := m.persistence.DeleteCommunity(id) 1543 if err != nil { 1544 return err 1545 } 1546 return m.persistence.DeleteCommunitySettings(id) 1547 } 1548 1549 func (m *Manager) updateShard(community *Community, shard *shard.Shard, clock uint64) error { 1550 community.config.Shard = shard 1551 if shard == nil { 1552 return m.persistence.DeleteCommunityShard(community.ID()) 1553 } 1554 1555 return m.persistence.SaveCommunityShard(community.ID(), shard, clock) 1556 } 1557 1558 func (m *Manager) UpdateShard(community *Community, shard *shard.Shard, clock uint64) error { 1559 m.communityLock.Lock(community.ID()) 1560 defer m.communityLock.Unlock(community.ID()) 1561 1562 return m.updateShard(community, shard, clock) 1563 } 1564 1565 // SetShard assigns a shard to a community 1566 func (m *Manager) SetShard(communityID types.HexBytes, shard *shard.Shard) (*Community, error) { 1567 m.communityLock.Lock(communityID) 1568 defer m.communityLock.Unlock(communityID) 1569 1570 community, err := m.GetByID(communityID) 1571 if err != nil { 1572 return nil, err 1573 } 1574 1575 community.increaseClock() 1576 1577 err = m.updateShard(community, shard, community.Clock()) 1578 if err != nil { 1579 return nil, err 1580 } 1581 1582 err = m.saveAndPublish(community) 1583 if err != nil { 1584 return nil, err 1585 } 1586 1587 return community, nil 1588 } 1589 1590 func (m *Manager) UpdatePubsubTopicPrivateKey(topic string, privKey *ecdsa.PrivateKey) error { 1591 if privKey != nil { 1592 return m.transport.StorePubsubTopicKey(topic, privKey) 1593 } 1594 1595 return m.transport.RemovePubsubTopicKey(topic) 1596 } 1597 1598 // EditCommunity takes a description, updates the community with the description, 1599 // saves it and returns it 1600 func (m *Manager) EditCommunity(request *requests.EditCommunity) (*Community, error) { 1601 m.communityLock.Lock(request.CommunityID) 1602 defer m.communityLock.Unlock(request.CommunityID) 1603 1604 community, err := m.GetByID(request.CommunityID) 1605 if err != nil { 1606 return nil, err 1607 } 1608 1609 newDescription, err := request.ToCommunityDescription() 1610 if err != nil { 1611 return nil, fmt.Errorf("can't create community description: %v", err) 1612 } 1613 1614 // If permissions weren't explicitly set on original request, use existing ones 1615 if newDescription.Permissions.Access == protobuf.CommunityPermissions_UNKNOWN_ACCESS { 1616 newDescription.Permissions.Access = community.config.CommunityDescription.Permissions.Access 1617 } 1618 // Use existing images for the entries that were not updated 1619 // NOTE: This will NOT allow deletion of the community image; it will need to 1620 // be handled separately. 1621 for imageName := range community.config.CommunityDescription.Identity.Images { 1622 _, exists := newDescription.Identity.Images[imageName] 1623 if !exists { 1624 // If no image was set in ToCommunityDescription then Images is nil. 1625 if newDescription.Identity.Images == nil { 1626 newDescription.Identity.Images = make(map[string]*protobuf.IdentityImage) 1627 } 1628 newDescription.Identity.Images[imageName] = community.config.CommunityDescription.Identity.Images[imageName] 1629 } 1630 } 1631 // TODO: handle delete image (if needed) 1632 1633 err = ValidateCommunityDescription(newDescription) 1634 if err != nil { 1635 return nil, err 1636 } 1637 1638 if !(community.IsControlNode() || community.hasPermissionToSendCommunityEvent(protobuf.CommunityEvent_COMMUNITY_EDIT)) { 1639 return nil, ErrNotAuthorized 1640 } 1641 1642 // Edit the community values 1643 community.Edit(newDescription) 1644 if err != nil { 1645 return nil, err 1646 } 1647 1648 if community.IsControlNode() { 1649 community.increaseClock() 1650 } else { 1651 err := community.addNewCommunityEvent(community.ToCommunityEditCommunityEvent(newDescription)) 1652 if err != nil { 1653 return nil, err 1654 } 1655 } 1656 1657 err = m.saveAndPublish(community) 1658 if err != nil { 1659 return nil, err 1660 } 1661 1662 return community, nil 1663 } 1664 1665 func (m *Manager) RemovePrivateKey(id types.HexBytes) (*Community, error) { 1666 m.communityLock.Lock(id) 1667 defer m.communityLock.Unlock(id) 1668 1669 community, err := m.GetByID(id) 1670 if err != nil { 1671 return community, err 1672 } 1673 1674 if !community.IsControlNode() { 1675 return community, ErrNotControlNode 1676 } 1677 1678 community.config.PrivateKey = nil 1679 err = m.persistence.SaveCommunity(community) 1680 if err != nil { 1681 return community, err 1682 } 1683 return community, nil 1684 } 1685 1686 func (m *Manager) ExportCommunity(id types.HexBytes) (*ecdsa.PrivateKey, error) { 1687 community, err := m.GetByID(id) 1688 if err != nil { 1689 return nil, err 1690 } 1691 1692 if !community.IsControlNode() { 1693 return nil, ErrNotControlNode 1694 } 1695 1696 return community.config.PrivateKey, nil 1697 } 1698 1699 func (m *Manager) ImportCommunity(key *ecdsa.PrivateKey, clock uint64) (*Community, error) { 1700 communityID := crypto.CompressPubkey(&key.PublicKey) 1701 1702 m.communityLock.Lock(communityID) 1703 defer m.communityLock.Unlock(communityID) 1704 1705 community, err := m.GetByID(communityID) 1706 if err != nil && err != ErrOrgNotFound { 1707 return nil, err 1708 } 1709 1710 if community == nil { 1711 createCommunityRequest := requests.CreateCommunity{ 1712 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 1713 Name: "unknown imported", 1714 } 1715 1716 description, err := createCommunityRequest.ToCommunityDescription() 1717 if err != nil { 1718 return nil, err 1719 } 1720 1721 err = ValidateCommunityDescription(description) 1722 if err != nil { 1723 return nil, err 1724 } 1725 1726 description.Clock = 1 1727 description.ID = types.EncodeHex(communityID) 1728 1729 config := Config{ 1730 ID: &key.PublicKey, 1731 PrivateKey: key, 1732 ControlNode: &key.PublicKey, 1733 ControlDevice: true, 1734 Logger: m.logger, 1735 Joined: true, 1736 JoinedAt: time.Now().Unix(), 1737 MemberIdentity: m.identity, 1738 CommunityDescription: description, 1739 LastOpenedAt: 0, 1740 } 1741 1742 var descriptionEncryptor DescriptionEncryptor 1743 if m.encryptor != nil { 1744 descriptionEncryptor = m 1745 } 1746 community, err = New(config, m.timesource, descriptionEncryptor, m.mediaServer) 1747 if err != nil { 1748 return nil, err 1749 } 1750 } else { 1751 community.config.PrivateKey = key 1752 community.config.ControlDevice = true 1753 } 1754 1755 community.Join() 1756 err = m.persistence.SaveCommunity(community) 1757 if err != nil { 1758 return nil, err 1759 } 1760 1761 // Save grant for own community 1762 grant, err := community.BuildGrant(&m.identity.PublicKey, "") 1763 if err != nil { 1764 return nil, err 1765 } 1766 err = m.persistence.SaveCommunityGrant(community.IDString(), grant, uint64(time.Now().UnixMilli())) 1767 if err != nil { 1768 return nil, err 1769 } 1770 1771 // Mark this device as the control node 1772 syncControlNode := &protobuf.SyncCommunityControlNode{ 1773 Clock: clock, 1774 InstallationId: m.installationID, 1775 } 1776 err = m.SaveSyncControlNode(community.ID(), syncControlNode) 1777 if err != nil { 1778 return nil, err 1779 } 1780 1781 return community, nil 1782 } 1783 1784 func (m *Manager) CreateChat(communityID types.HexBytes, chat *protobuf.CommunityChat, publish bool, thirdPartyID string) (*CommunityChanges, error) { 1785 m.communityLock.Lock(communityID) 1786 defer m.communityLock.Unlock(communityID) 1787 1788 community, err := m.GetByID(communityID) 1789 if err != nil { 1790 return nil, err 1791 } 1792 chatID := uuid.New().String() 1793 if thirdPartyID != "" { 1794 chatID = chatID + thirdPartyID 1795 } 1796 1797 changes, err := community.CreateChat(chatID, chat) 1798 if err != nil { 1799 return nil, err 1800 } 1801 1802 err = m.saveAndPublish(community) 1803 if err != nil { 1804 return nil, err 1805 } 1806 1807 return changes, nil 1808 } 1809 1810 func (m *Manager) EditChat(communityID types.HexBytes, chatID string, chat *protobuf.CommunityChat) (*Community, *CommunityChanges, error) { 1811 m.communityLock.Lock(communityID) 1812 defer m.communityLock.Unlock(communityID) 1813 1814 community, err := m.GetByID(communityID) 1815 if err != nil { 1816 return nil, nil, err 1817 } 1818 1819 // Remove communityID prefix from chatID if exists 1820 if strings.HasPrefix(chatID, communityID.String()) { 1821 chatID = strings.TrimPrefix(chatID, communityID.String()) 1822 } 1823 1824 oldChat, err := community.GetChat(chatID) 1825 if err != nil { 1826 return nil, nil, err 1827 } 1828 1829 // We can't edit permissions and members with an Edit, so we set to what we had, otherwise they will be lost 1830 chat.Permissions = oldChat.Permissions 1831 chat.Members = oldChat.Members 1832 1833 changes, err := community.EditChat(chatID, chat) 1834 if err != nil { 1835 return nil, nil, err 1836 } 1837 1838 err = m.saveAndPublish(community) 1839 if err != nil { 1840 return nil, nil, err 1841 } 1842 1843 return community, changes, nil 1844 } 1845 1846 func (m *Manager) DeleteChat(communityID types.HexBytes, chatID string) (*Community, *CommunityChanges, error) { 1847 m.communityLock.Lock(communityID) 1848 defer m.communityLock.Unlock(communityID) 1849 1850 community, err := m.GetByID(communityID) 1851 if err != nil { 1852 return nil, nil, err 1853 } 1854 1855 // Check for channel permissions 1856 changes := community.emptyCommunityChanges() 1857 for tokenPermissionID, tokenPermission := range community.tokenPermissions() { 1858 chats := tokenPermission.ChatIdsAsMap() 1859 _, hasChat := chats[chatID] 1860 if !hasChat { 1861 continue 1862 } 1863 1864 if len(chats) == 1 { 1865 // Delete channel permission, if there is only one channel 1866 deletePermissionChanges, err := community.DeleteTokenPermission(tokenPermissionID) 1867 if err != nil { 1868 return nil, nil, err 1869 } 1870 changes.Merge(deletePermissionChanges) 1871 } else { 1872 // Remove the channel from the permission, if there are other channels 1873 delete(chats, chatID) 1874 1875 var chatIDs []string 1876 for chatID := range chats { 1877 chatIDs = append(chatIDs, chatID) 1878 } 1879 tokenPermission.ChatIds = chatIDs 1880 1881 updatePermissionChanges, err := community.UpsertTokenPermission(tokenPermission.CommunityTokenPermission) 1882 if err != nil { 1883 return nil, nil, err 1884 } 1885 changes.Merge(updatePermissionChanges) 1886 } 1887 } 1888 1889 // Remove communityID prefix from chatID if exists 1890 if strings.HasPrefix(chatID, communityID.String()) { 1891 chatID = strings.TrimPrefix(chatID, communityID.String()) 1892 } 1893 1894 deleteChanges, err := community.DeleteChat(chatID) 1895 if err != nil { 1896 return nil, nil, err 1897 } 1898 changes.Merge(deleteChanges) 1899 1900 err = m.saveAndPublish(community) 1901 if err != nil { 1902 return nil, nil, err 1903 } 1904 1905 return community, changes, nil 1906 } 1907 1908 func (m *Manager) CreateCategory(request *requests.CreateCommunityCategory, publish bool) (*Community, *CommunityChanges, error) { 1909 m.communityLock.Lock(request.CommunityID) 1910 defer m.communityLock.Unlock(request.CommunityID) 1911 1912 community, err := m.GetByID(request.CommunityID) 1913 if err != nil { 1914 return nil, nil, err 1915 } 1916 1917 categoryID := uuid.New().String() 1918 if request.ThirdPartyID != "" { 1919 categoryID = categoryID + request.ThirdPartyID 1920 } 1921 1922 // Remove communityID prefix from chatID if exists 1923 for i, cid := range request.ChatIDs { 1924 if strings.HasPrefix(cid, request.CommunityID.String()) { 1925 request.ChatIDs[i] = strings.TrimPrefix(cid, request.CommunityID.String()) 1926 } 1927 } 1928 1929 changes, err := community.CreateCategory(categoryID, request.CategoryName, request.ChatIDs) 1930 if err != nil { 1931 return nil, nil, err 1932 } 1933 1934 err = m.saveAndPublish(community) 1935 if err != nil { 1936 return nil, nil, err 1937 } 1938 1939 return community, changes, nil 1940 } 1941 1942 func (m *Manager) EditCategory(request *requests.EditCommunityCategory) (*Community, *CommunityChanges, error) { 1943 m.communityLock.Lock(request.CommunityID) 1944 defer m.communityLock.Unlock(request.CommunityID) 1945 1946 community, err := m.GetByID(request.CommunityID) 1947 if err != nil { 1948 return nil, nil, err 1949 } 1950 1951 // Remove communityID prefix from chatID if exists 1952 for i, cid := range request.ChatIDs { 1953 if strings.HasPrefix(cid, request.CommunityID.String()) { 1954 request.ChatIDs[i] = strings.TrimPrefix(cid, request.CommunityID.String()) 1955 } 1956 } 1957 1958 changes, err := community.EditCategory(request.CategoryID, request.CategoryName, request.ChatIDs) 1959 if err != nil { 1960 return nil, nil, err 1961 } 1962 1963 err = m.saveAndPublish(community) 1964 if err != nil { 1965 return nil, nil, err 1966 } 1967 1968 return community, changes, nil 1969 } 1970 1971 func (m *Manager) EditChatFirstMessageTimestamp(communityID types.HexBytes, chatID string, timestamp uint32) (*Community, *CommunityChanges, error) { 1972 m.communityLock.Lock(communityID) 1973 defer m.communityLock.Unlock(communityID) 1974 1975 community, err := m.GetByID(communityID) 1976 if err != nil { 1977 return nil, nil, err 1978 } 1979 1980 // Remove communityID prefix from chatID if exists 1981 if strings.HasPrefix(chatID, communityID.String()) { 1982 chatID = strings.TrimPrefix(chatID, communityID.String()) 1983 } 1984 1985 changes, err := community.UpdateChatFirstMessageTimestamp(chatID, timestamp) 1986 if err != nil { 1987 return nil, nil, err 1988 } 1989 1990 err = m.persistence.SaveCommunity(community) 1991 if err != nil { 1992 return nil, nil, err 1993 } 1994 1995 // Advertise changes 1996 m.publish(&Subscription{Community: community}) 1997 1998 return community, changes, nil 1999 } 2000 2001 func (m *Manager) ReorderCategories(request *requests.ReorderCommunityCategories) (*Community, *CommunityChanges, error) { 2002 m.communityLock.Lock(request.CommunityID) 2003 defer m.communityLock.Unlock(request.CommunityID) 2004 2005 community, err := m.GetByID(request.CommunityID) 2006 if err != nil { 2007 return nil, nil, err 2008 } 2009 2010 changes, err := community.ReorderCategories(request.CategoryID, request.Position) 2011 if err != nil { 2012 return nil, nil, err 2013 } 2014 2015 err = m.saveAndPublish(community) 2016 if err != nil { 2017 return nil, nil, err 2018 } 2019 2020 return community, changes, nil 2021 } 2022 2023 func (m *Manager) ReorderChat(request *requests.ReorderCommunityChat) (*Community, *CommunityChanges, error) { 2024 m.communityLock.Lock(request.CommunityID) 2025 defer m.communityLock.Unlock(request.CommunityID) 2026 2027 community, err := m.GetByID(request.CommunityID) 2028 if err != nil { 2029 return nil, nil, err 2030 } 2031 2032 // Remove communityID prefix from chatID if exists 2033 if strings.HasPrefix(request.ChatID, request.CommunityID.String()) { 2034 request.ChatID = strings.TrimPrefix(request.ChatID, request.CommunityID.String()) 2035 } 2036 2037 changes, err := community.ReorderChat(request.CategoryID, request.ChatID, request.Position) 2038 if err != nil { 2039 return nil, nil, err 2040 } 2041 2042 err = m.saveAndPublish(community) 2043 if err != nil { 2044 return nil, nil, err 2045 } 2046 2047 return community, changes, nil 2048 } 2049 2050 func (m *Manager) DeleteCategory(request *requests.DeleteCommunityCategory) (*Community, *CommunityChanges, error) { 2051 m.communityLock.Lock(request.CommunityID) 2052 defer m.communityLock.Unlock(request.CommunityID) 2053 2054 community, err := m.GetByID(request.CommunityID) 2055 if err != nil { 2056 return nil, nil, err 2057 } 2058 2059 changes, err := community.DeleteCategory(request.CategoryID) 2060 if err != nil { 2061 return nil, nil, err 2062 } 2063 2064 err = m.saveAndPublish(community) 2065 if err != nil { 2066 return nil, nil, err 2067 } 2068 2069 return changes.Community, changes, nil 2070 } 2071 2072 func (m *Manager) GenerateRequestsToJoinForAutoApprovalOnNewOwnership(communityID types.HexBytes, kickedMembers map[string]*protobuf.CommunityMember) ([]*RequestToJoin, error) { 2073 var requestsToJoin []*RequestToJoin 2074 clock := uint64(time.Now().Unix()) 2075 for pubKeyStr := range kickedMembers { 2076 requestToJoin := &RequestToJoin{ 2077 PublicKey: pubKeyStr, 2078 Clock: clock, 2079 CommunityID: communityID, 2080 State: RequestToJoinStateAwaitingAddresses, 2081 Our: true, 2082 RevealedAccounts: make([]*protobuf.RevealedAccount, 0), 2083 } 2084 2085 requestToJoin.CalculateID() 2086 2087 requestsToJoin = append(requestsToJoin, requestToJoin) 2088 } 2089 2090 return requestsToJoin, m.persistence.SaveRequestsToJoin(requestsToJoin) 2091 } 2092 2093 func (m *Manager) Queue(signer *ecdsa.PublicKey, community *Community, clock uint64, payload []byte) error { 2094 2095 m.logger.Info("queuing community", zap.String("id", community.IDString()), zap.String("signer", common.PubkeyToHex(signer))) 2096 2097 communityToValidate := communityToValidate{ 2098 id: community.ID(), 2099 clock: clock, 2100 payload: payload, 2101 validateAt: uint64(time.Now().UnixNano()), 2102 signer: crypto.CompressPubkey(signer), 2103 } 2104 err := m.persistence.SaveCommunityToValidate(communityToValidate) 2105 if err != nil { 2106 m.logger.Error("failed to save community", zap.Error(err)) 2107 return err 2108 } 2109 2110 return nil 2111 } 2112 2113 func (m *Manager) HandleCommunityDescriptionMessage(signer *ecdsa.PublicKey, description *protobuf.CommunityDescription, payload []byte, verifiedOwner *ecdsa.PublicKey, communityShard *protobuf.Shard) (*CommunityResponse, error) { 2114 m.logger.Debug("HandleCommunityDescriptionMessage", zap.String("communityID", description.ID), zap.Uint64("clock", description.Clock)) 2115 2116 if signer == nil { 2117 return nil, errors.New("signer can't be nil") 2118 } 2119 2120 var id []byte 2121 var err error 2122 if len(description.ID) != 0 { 2123 id, err = types.DecodeHex(description.ID) 2124 if err != nil { 2125 return nil, err 2126 } 2127 } else { 2128 // Backward compatibility 2129 id = crypto.CompressPubkey(signer) 2130 } 2131 2132 failedToDecrypt, processedDescription, err := m.preprocessDescription(id, description) 2133 if err != nil { 2134 return nil, err 2135 } 2136 m.communityLock.Lock(id) 2137 defer m.communityLock.Unlock(id) 2138 community, err := m.GetByID(id) 2139 if err != nil && err != ErrOrgNotFound { 2140 return nil, err 2141 } 2142 2143 // We don't process failed to decrypt if the whole metadata is encrypted 2144 // and we joined the community already 2145 if community != nil && community.Joined() && len(failedToDecrypt) != 0 && processedDescription != nil && len(processedDescription.Members) == 0 { 2146 return &CommunityResponse{FailedToDecrypt: failedToDecrypt}, nil 2147 } 2148 2149 // We should queue only if the community has a token owner, and the owner has been verified 2150 hasTokenOwnership := HasTokenOwnership(processedDescription) 2151 shouldQueue := hasTokenOwnership && verifiedOwner == nil 2152 2153 if community == nil { 2154 pubKey, err := crypto.DecompressPubkey(id) 2155 if err != nil { 2156 return nil, err 2157 } 2158 var cShard *shard.Shard 2159 if communityShard == nil { 2160 cShard = &shard.Shard{Cluster: shard.MainStatusShardCluster, Index: shard.DefaultShardIndex} 2161 } else { 2162 cShard = shard.FromProtobuff(communityShard) 2163 } 2164 config := Config{ 2165 CommunityDescription: processedDescription, 2166 Logger: m.logger, 2167 CommunityDescriptionProtocolMessage: payload, 2168 MemberIdentity: m.identity, 2169 ID: pubKey, 2170 ControlNode: signer, 2171 Shard: cShard, 2172 } 2173 2174 var descriptionEncryptor DescriptionEncryptor 2175 if m.encryptor != nil { 2176 descriptionEncryptor = m 2177 } 2178 community, err = New(config, m.timesource, descriptionEncryptor, m.mediaServer) 2179 if err != nil { 2180 return nil, err 2181 } 2182 2183 // A new community, we need to check if we need to validate async. 2184 // That would be the case if it has a contract. We queue everything and process separately. 2185 if shouldQueue { 2186 return nil, m.Queue(signer, community, processedDescription.Clock, payload) 2187 } 2188 } else { 2189 // only queue if already known control node is different than the signer 2190 // and if the clock is greater 2191 shouldQueue = shouldQueue && !common.IsPubKeyEqual(community.ControlNode(), signer) && 2192 community.config.CommunityDescription.Clock < processedDescription.Clock 2193 if shouldQueue { 2194 return nil, m.Queue(signer, community, processedDescription.Clock, payload) 2195 } 2196 } 2197 2198 if hasTokenOwnership && verifiedOwner != nil { 2199 // Override verified owner 2200 m.logger.Info("updating verified owner", 2201 zap.String("communityID", community.IDString()), 2202 zap.String("verifiedOwner", common.PubkeyToHex(verifiedOwner)), 2203 zap.String("signer", common.PubkeyToHex(signer)), 2204 zap.String("controlNode", common.PubkeyToHex(community.ControlNode())), 2205 ) 2206 2207 // If we are not the verified owner anymore, drop the private key 2208 if !common.IsPubKeyEqual(verifiedOwner, &m.identity.PublicKey) { 2209 community.config.PrivateKey = nil 2210 } 2211 2212 // new control node will be set in the 'UpdateCommunityDescription' 2213 if !common.IsPubKeyEqual(verifiedOwner, signer) { 2214 return nil, ErrNotAuthorized 2215 } 2216 } else if !common.IsPubKeyEqual(community.ControlNode(), signer) { 2217 return nil, ErrNotAuthorized 2218 } 2219 2220 r, err := m.handleCommunityDescriptionMessageCommon(community, processedDescription, payload, verifiedOwner) 2221 if err != nil { 2222 return nil, err 2223 } 2224 r.FailedToDecrypt = failedToDecrypt 2225 return r, nil 2226 } 2227 2228 func (m *Manager) NewHashRatchetKeys(keys []*encryption.HashRatchetInfo) error { 2229 return m.persistence.InvalidateDecryptedCommunityCacheForKeys(keys) 2230 } 2231 2232 func (m *Manager) preprocessDescription(id types.HexBytes, description *protobuf.CommunityDescription) ([]*CommunityPrivateDataFailedToDecrypt, *protobuf.CommunityDescription, error) { 2233 decryptedCommunity, err := m.persistence.GetDecryptedCommunityDescription(id, description.Clock) 2234 if err != nil { 2235 return nil, nil, err 2236 } 2237 if decryptedCommunity != nil { 2238 return nil, decryptedCommunity, nil 2239 } 2240 2241 response, err := decryptDescription(id, m, description, m.logger) 2242 if err != nil { 2243 return response, description, err 2244 } 2245 2246 upgradeTokenPermissions(description) 2247 2248 // Workaround for https://github.com/status-im/status-desktop/issues/12188 2249 hydrateChannelsMembers(description) 2250 2251 return response, description, m.persistence.SaveDecryptedCommunityDescription(id, response, description) 2252 } 2253 2254 func (m *Manager) handleCommunityDescriptionMessageCommon(community *Community, description *protobuf.CommunityDescription, payload []byte, newControlNode *ecdsa.PublicKey) (*CommunityResponse, error) { 2255 prevClock := community.config.CommunityDescription.Clock 2256 prevResendAccountsClock := community.config.CommunityDescription.ResendAccountsClock 2257 2258 changes, err := community.UpdateCommunityDescription(description, payload, newControlNode) 2259 if err != nil { 2260 return nil, err 2261 } 2262 2263 if err = m.handleCommunityTokensMetadata(community); err != nil { 2264 return nil, err 2265 } 2266 2267 hasCommunityArchiveInfo, err := m.persistence.HasCommunityArchiveInfo(community.ID()) 2268 if err != nil { 2269 return nil, err 2270 } 2271 2272 cdMagnetlinkClock := community.config.CommunityDescription.ArchiveMagnetlinkClock 2273 if !hasCommunityArchiveInfo { 2274 err = m.persistence.SaveCommunityArchiveInfo(community.ID(), cdMagnetlinkClock, 0) 2275 if err != nil { 2276 return nil, err 2277 } 2278 } else { 2279 magnetlinkClock, err := m.persistence.GetMagnetlinkMessageClock(community.ID()) 2280 if err != nil { 2281 return nil, err 2282 } 2283 if cdMagnetlinkClock > magnetlinkClock { 2284 err = m.persistence.UpdateMagnetlinkMessageClock(community.ID(), cdMagnetlinkClock) 2285 if err != nil { 2286 return nil, err 2287 } 2288 } 2289 } 2290 2291 pkString := common.PubkeyToHex(&m.identity.PublicKey) 2292 if m.tokenManager != nil && description.CommunityTokensMetadata != nil && len(description.CommunityTokensMetadata) > 0 { 2293 for _, tokenMetadata := range description.CommunityTokensMetadata { 2294 if tokenMetadata.TokenType != protobuf.CommunityTokenType_ERC20 { 2295 continue 2296 } 2297 2298 for chainID, address := range tokenMetadata.ContractAddresses { 2299 _ = m.tokenManager.FindOrCreateTokenByAddress(context.Background(), chainID, gethcommon.HexToAddress(address)) 2300 } 2301 } 2302 } 2303 2304 // If the community require membership, we set whether we should leave/join the community after a state change 2305 if community.ManualAccept() || community.AutoAccept() { 2306 if changes.HasNewMember(pkString) { 2307 hasPendingRequest, err := m.persistence.HasPendingRequestsToJoinForUserAndCommunity(pkString, changes.Community.ID()) 2308 if err != nil { 2309 return nil, err 2310 } 2311 // If there's any pending request, we should join the community 2312 // automatically 2313 changes.ShouldMemberJoin = hasPendingRequest 2314 } 2315 2316 if changes.HasMemberLeft(pkString) && community.Joined() { 2317 softKick := !changes.IsMemberBanned(pkString) && 2318 (changes.ControlNodeChanged != nil || prevResendAccountsClock < community.Description().ResendAccountsClock) 2319 2320 // If we joined previously the community, that means we have been kicked 2321 changes.MemberKicked = !softKick 2322 // soft kick previously joined member on community owner change or on ResendAccountsClock change 2323 changes.MemberSoftKicked = softKick 2324 } 2325 } 2326 2327 if description.Clock > prevClock { 2328 err = m.persistence.DeleteCommunityEvents(community.ID()) 2329 if err != nil { 2330 return nil, err 2331 } 2332 community.config.EventsData = nil 2333 } 2334 2335 // Set Joined if we are part of the member list 2336 if !community.Joined() && community.hasMember(&m.identity.PublicKey) { 2337 changes.ShouldMemberJoin = true 2338 } 2339 2340 err = m.persistence.SaveCommunity(community) 2341 if err != nil { 2342 return nil, err 2343 } 2344 2345 // We mark our requests as completed, though maybe we should mark 2346 // any request for any user that has been added as completed 2347 if err := m.markRequestToJoinAsAccepted(&m.identity.PublicKey, community); err != nil { 2348 return nil, err 2349 } 2350 // Check if there's a change and we should be joining 2351 2352 return &CommunityResponse{ 2353 Community: community, 2354 Changes: changes, 2355 }, nil 2356 } 2357 2358 func (m *Manager) signEvents(community *Community) error { 2359 for i := range community.config.EventsData.Events { 2360 communityEvent := &community.config.EventsData.Events[i] 2361 if communityEvent.Signature == nil || len(communityEvent.Signature) == 0 { 2362 err := communityEvent.Sign(m.identity) 2363 if err != nil { 2364 return err 2365 } 2366 } 2367 } 2368 return nil 2369 } 2370 2371 func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message *protobuf.CommunityEventsMessage) (*CommunityResponse, error) { 2372 if signer == nil { 2373 return nil, errors.New("signer can't be nil") 2374 } 2375 2376 eventsMessage, err := CommunityEventsMessageFromProtobuf(message) 2377 if err != nil { 2378 return nil, err 2379 } 2380 2381 m.communityLock.Lock(eventsMessage.CommunityID) 2382 defer m.communityLock.Unlock(eventsMessage.CommunityID) 2383 2384 community, err := m.GetByID(eventsMessage.CommunityID) 2385 if err != nil { 2386 return nil, err 2387 } 2388 2389 if !community.IsPrivilegedMember(signer) { 2390 return nil, errors.New("user has not permissions to send events") 2391 } 2392 2393 originCommunity := community.CreateDeepCopy() 2394 2395 var lastlyAppliedEvents map[string]uint64 2396 if community.IsControlNode() { 2397 lastlyAppliedEvents, err = m.persistence.GetAppliedCommunityEvents(community.ID()) 2398 if err != nil { 2399 return nil, err 2400 } 2401 } 2402 2403 additionalCommunityResponse, err := m.handleCommunityEventsAndMetadata(community, eventsMessage, lastlyAppliedEvents) 2404 if err != nil { 2405 return nil, err 2406 } 2407 2408 // Control node applies events and publish updated CommunityDescription 2409 if community.IsControlNode() { 2410 appliedEvents := map[string]uint64{} 2411 if community.config.EventsData != nil { 2412 for _, event := range community.config.EventsData.Events { 2413 appliedEvents[event.EventTypeID()] = event.CommunityEventClock 2414 } 2415 } 2416 community.config.EventsData = nil // clear events, they are already applied 2417 community.increaseClock() 2418 2419 if m.keyDistributor != nil { 2420 encryptionKeyActions := EvaluateCommunityEncryptionKeyActions(originCommunity, community) 2421 err := m.keyDistributor.Generate(community, encryptionKeyActions) 2422 if err != nil { 2423 return nil, err 2424 } 2425 } 2426 2427 err = m.persistence.SaveCommunity(community) 2428 if err != nil { 2429 return nil, err 2430 } 2431 2432 err = m.persistence.UpsertAppliedCommunityEvents(community.ID(), appliedEvents) 2433 if err != nil { 2434 return nil, err 2435 } 2436 2437 m.publish(&Subscription{Community: community}) 2438 } else { 2439 err = m.persistence.SaveCommunity(community) 2440 if err != nil { 2441 return nil, err 2442 } 2443 err := m.persistence.SaveCommunityEvents(community) 2444 if err != nil { 2445 return nil, err 2446 } 2447 } 2448 2449 return &CommunityResponse{ 2450 Community: community, 2451 Changes: EvaluateCommunityChanges(originCommunity, community), 2452 RequestsToJoin: additionalCommunityResponse.RequestsToJoin, 2453 }, nil 2454 } 2455 2456 func (m *Manager) handleAdditionalAdminChanges(community *Community) (*CommunityResponse, error) { 2457 communityResponse := CommunityResponse{ 2458 RequestsToJoin: make([]*RequestToJoin, 0), 2459 } 2460 2461 if !(community.IsControlNode() || community.HasPermissionToSendCommunityEvents()) { 2462 // we're a normal user/member node, so there's nothing for us to do here 2463 return &communityResponse, nil 2464 } 2465 2466 if community.config.EventsData == nil { 2467 return &communityResponse, nil 2468 } 2469 2470 handledMembers := map[string]struct{}{} 2471 2472 for i := len(community.config.EventsData.Events) - 1; i >= 0; i-- { 2473 communityEvent := &community.config.EventsData.Events[i] 2474 if _, handled := handledMembers[communityEvent.MemberToAction]; handled { 2475 continue 2476 } 2477 switch communityEvent.Type { 2478 case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT: 2479 handledMembers[communityEvent.MemberToAction] = struct{}{} 2480 requestsToJoin, err := m.handleCommunityEventRequestAccepted(community, communityEvent) 2481 if err != nil { 2482 return nil, err 2483 } 2484 if requestsToJoin != nil { 2485 communityResponse.RequestsToJoin = append(communityResponse.RequestsToJoin, requestsToJoin...) 2486 } 2487 2488 case protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT: 2489 handledMembers[communityEvent.MemberToAction] = struct{}{} 2490 requestsToJoin, err := m.handleCommunityEventRequestRejected(community, communityEvent) 2491 if err != nil { 2492 return nil, err 2493 } 2494 if requestsToJoin != nil { 2495 communityResponse.RequestsToJoin = append(communityResponse.RequestsToJoin, requestsToJoin...) 2496 } 2497 2498 default: 2499 } 2500 } 2501 return &communityResponse, nil 2502 } 2503 2504 func (m *Manager) saveOrUpdateRequestToJoin(communityID types.HexBytes, requestToJoin *RequestToJoin) (bool, error) { 2505 updated := false 2506 2507 existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID) 2508 if err != nil && err != sql.ErrNoRows { 2509 return updated, err 2510 } 2511 2512 if existingRequestToJoin != nil { 2513 // node already knows about this request to join, so let's compare clocks 2514 // and update it if necessary 2515 if existingRequestToJoin.Clock <= requestToJoin.Clock { 2516 pk, err := common.HexToPubkey(existingRequestToJoin.PublicKey) 2517 if err != nil { 2518 return updated, err 2519 } 2520 err = m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), communityID, requestToJoin.State) 2521 if err != nil { 2522 return updated, err 2523 } 2524 updated = true 2525 } 2526 } else { 2527 err := m.persistence.SaveRequestToJoin(requestToJoin) 2528 if err != nil { 2529 return updated, err 2530 } 2531 } 2532 2533 return updated, nil 2534 } 2535 2536 func (m *Manager) handleCommunityEventRequestAccepted(community *Community, communityEvent *CommunityEvent) ([]*RequestToJoin, error) { 2537 acceptedRequestsToJoin := make([]types.HexBytes, 0) 2538 2539 requestsToJoin := make([]*RequestToJoin, 0) 2540 2541 signer := communityEvent.MemberToAction 2542 request := communityEvent.RequestToJoin 2543 2544 requestToJoin := &RequestToJoin{ 2545 PublicKey: signer, 2546 Clock: request.Clock, 2547 ENSName: request.EnsName, 2548 CommunityID: request.CommunityId, 2549 State: RequestToJoinStateAcceptedPending, 2550 CustomizationColor: multiaccountscommon.IDToColorFallbackToBlue(request.CustomizationColor), 2551 } 2552 requestToJoin.CalculateID() 2553 2554 existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID) 2555 if err != nil && err != sql.ErrNoRows { 2556 return nil, err 2557 } 2558 2559 if existingRequestToJoin != nil { 2560 alreadyProcessedByControlNode := existingRequestToJoin.State == RequestToJoinStateAccepted 2561 if alreadyProcessedByControlNode || existingRequestToJoin.State == RequestToJoinStateCanceled { 2562 return requestsToJoin, nil 2563 } 2564 } 2565 2566 requestUpdated, err := m.saveOrUpdateRequestToJoin(community.ID(), requestToJoin) 2567 if err != nil { 2568 return nil, err 2569 } 2570 2571 // If request to join exists in control node, add request to acceptedRequestsToJoin. 2572 // Otherwise keep the request as RequestToJoinStateAcceptedPending, 2573 // as privileged users don't have revealed addresses. This can happen if control node received 2574 // community event message before user request to join. 2575 if community.IsControlNode() && requestUpdated { 2576 acceptedRequestsToJoin = append(acceptedRequestsToJoin, requestToJoin.ID) 2577 } 2578 2579 requestsToJoin = append(requestsToJoin, requestToJoin) 2580 2581 if community.IsControlNode() { 2582 m.publish(&Subscription{AcceptedRequestsToJoin: acceptedRequestsToJoin}) 2583 } 2584 return requestsToJoin, nil 2585 } 2586 2587 func (m *Manager) handleCommunityEventRequestRejected(community *Community, communityEvent *CommunityEvent) ([]*RequestToJoin, error) { 2588 rejectedRequestsToJoin := make([]types.HexBytes, 0) 2589 2590 requestsToJoin := make([]*RequestToJoin, 0) 2591 2592 signer := communityEvent.MemberToAction 2593 request := communityEvent.RequestToJoin 2594 2595 requestToJoin := &RequestToJoin{ 2596 PublicKey: signer, 2597 Clock: request.Clock, 2598 ENSName: request.EnsName, 2599 CommunityID: request.CommunityId, 2600 State: RequestToJoinStateDeclinedPending, 2601 CustomizationColor: multiaccountscommon.IDToColorFallbackToBlue(request.CustomizationColor), 2602 } 2603 requestToJoin.CalculateID() 2604 2605 existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID) 2606 if err != nil && err != sql.ErrNoRows { 2607 return nil, err 2608 } 2609 2610 if existingRequestToJoin != nil { 2611 alreadyProcessedByControlNode := existingRequestToJoin.State == RequestToJoinStateDeclined 2612 if alreadyProcessedByControlNode || existingRequestToJoin.State == RequestToJoinStateCanceled { 2613 return requestsToJoin, nil 2614 } 2615 } 2616 2617 requestUpdated, err := m.saveOrUpdateRequestToJoin(community.ID(), requestToJoin) 2618 if err != nil { 2619 return nil, err 2620 } 2621 // If request to join exists in control node, add request to rejectedRequestsToJoin. 2622 // Otherwise keep the request as RequestToJoinStateDeclinedPending, 2623 // as privileged users don't have revealed addresses. This can happen if control node received 2624 // community event message before user request to join. 2625 if community.IsControlNode() && requestUpdated { 2626 rejectedRequestsToJoin = append(rejectedRequestsToJoin, requestToJoin.ID) 2627 } 2628 2629 requestsToJoin = append(requestsToJoin, requestToJoin) 2630 2631 if community.IsControlNode() { 2632 m.publish(&Subscription{RejectedRequestsToJoin: rejectedRequestsToJoin}) 2633 } 2634 return requestsToJoin, nil 2635 } 2636 2637 // markRequestToJoinAsAccepted marks all the pending requests to join as completed 2638 // if we are members 2639 func (m *Manager) markRequestToJoinAsAccepted(pk *ecdsa.PublicKey, community *Community) error { 2640 if community.HasMember(pk) { 2641 return m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), community.ID(), RequestToJoinStateAccepted) 2642 } 2643 return nil 2644 } 2645 2646 func (m *Manager) markRequestToJoinAsCanceled(pk *ecdsa.PublicKey, community *Community) error { 2647 return m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), community.ID(), RequestToJoinStateCanceled) 2648 } 2649 2650 func (m *Manager) markRequestToJoinAsAcceptedPending(pk *ecdsa.PublicKey, community *Community) error { 2651 return m.persistence.SetRequestToJoinState(common.PubkeyToHex(pk), community.ID(), RequestToJoinStateAcceptedPending) 2652 } 2653 2654 func (m *Manager) DeletePendingRequestToJoin(request *RequestToJoin) error { 2655 m.communityLock.Lock(request.CommunityID) 2656 defer m.communityLock.Unlock(request.CommunityID) 2657 2658 community, err := m.GetByID(request.CommunityID) 2659 if err != nil { 2660 return err 2661 } 2662 2663 err = m.persistence.DeletePendingRequestToJoin(request.ID) 2664 if err != nil { 2665 return err 2666 } 2667 2668 if community.IsControlNode() { 2669 err = m.saveAndPublish(community) 2670 if err != nil { 2671 return err 2672 } 2673 } 2674 2675 return nil 2676 } 2677 2678 func (m *Manager) UpdateClockInRequestToJoin(id types.HexBytes, clock uint64) error { 2679 return m.persistence.UpdateClockInRequestToJoin(id, clock) 2680 } 2681 2682 func (m *Manager) SetMuted(id types.HexBytes, muted bool) error { 2683 m.communityLock.Lock(id) 2684 defer m.communityLock.Unlock(id) 2685 2686 return m.persistence.SetMuted(id, muted) 2687 } 2688 2689 func (m *Manager) MuteCommunityTill(communityID []byte, muteTill time.Time) error { 2690 m.communityLock.Lock(communityID) 2691 defer m.communityLock.Unlock(communityID) 2692 2693 return m.persistence.MuteCommunityTill(communityID, muteTill) 2694 } 2695 func (m *Manager) CancelRequestToJoin(request *requests.CancelRequestToJoinCommunity) (*RequestToJoin, *Community, error) { 2696 dbRequest, err := m.persistence.GetRequestToJoin(request.ID) 2697 if err != nil { 2698 return nil, nil, err 2699 } 2700 2701 community, err := m.GetByID(dbRequest.CommunityID) 2702 if err != nil { 2703 return nil, nil, err 2704 } 2705 2706 pk, err := common.HexToPubkey(dbRequest.PublicKey) 2707 if err != nil { 2708 return nil, nil, err 2709 } 2710 2711 dbRequest.State = RequestToJoinStateCanceled 2712 if err := m.markRequestToJoinAsCanceled(pk, community); err != nil { 2713 return nil, nil, err 2714 } 2715 2716 return dbRequest, community, nil 2717 } 2718 2719 func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Address) (*CheckPermissionToJoinResponse, error) { 2720 community, err := m.GetByID(id) 2721 if err != nil { 2722 return nil, err 2723 } 2724 2725 return m.PermissionChecker.CheckPermissionToJoin(community, addresses) 2726 } 2727 2728 func (m *Manager) accountsSatisfyPermissionsToJoin( 2729 communityPermissionsPreParsedData map[protobuf.CommunityTokenPermission_Type]*PreParsedCommunityPermissionsData, 2730 accountsAndChainIDs []*AccountChainIDsCombination) (bool, protobuf.CommunityMember_Roles, error) { 2731 2732 if m.accountsHasPrivilegedPermission(communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER], accountsAndChainIDs) { 2733 return true, protobuf.CommunityMember_ROLE_TOKEN_MASTER, nil 2734 } 2735 if m.accountsHasPrivilegedPermission(communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN], accountsAndChainIDs) { 2736 return true, protobuf.CommunityMember_ROLE_ADMIN, nil 2737 } 2738 2739 preParsedBecomeMemberPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER] 2740 if preParsedBecomeMemberPermissions != nil { 2741 permissionResponse, err := m.PermissionChecker.CheckPermissions(preParsedBecomeMemberPermissions, accountsAndChainIDs, true) 2742 if err != nil { 2743 return false, protobuf.CommunityMember_ROLE_NONE, err 2744 } 2745 2746 return permissionResponse.Satisfied, protobuf.CommunityMember_ROLE_NONE, nil 2747 } 2748 2749 return true, protobuf.CommunityMember_ROLE_NONE, nil 2750 } 2751 2752 func (m *Manager) accountsSatisfyPermissionsToJoinChannels( 2753 community *Community, 2754 channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, 2755 accountsAndChainIDs []*AccountChainIDsCombination) (map[string]*protobuf.CommunityChat, map[string]*protobuf.CommunityChat, error) { 2756 2757 viewChats := make(map[string]*protobuf.CommunityChat) 2758 viewAndPostChats := make(map[string]*protobuf.CommunityChat) 2759 2760 if len(channelPermissionsPreParsedData) == 0 { 2761 for channelID, channel := range community.config.CommunityDescription.Chats { 2762 viewAndPostChats[channelID] = channel 2763 } 2764 2765 return viewChats, viewAndPostChats, nil 2766 } 2767 2768 // check which permissions we satisfy and which not 2769 channelPermissionsCheckResult, err := m.checkChannelsPermissions(channelPermissionsPreParsedData, accountsAndChainIDs, true) 2770 if err != nil { 2771 m.logger.Warn("check channel permission failed: %v", zap.Error(err)) 2772 return viewChats, viewAndPostChats, err 2773 } 2774 2775 for channelID, channel := range community.config.CommunityDescription.Chats { 2776 chatID := community.ChatID(channelID) 2777 channelPermissionsCheckResult, exists := channelPermissionsCheckResult[chatID] 2778 2779 if !exists { 2780 viewAndPostChats[channelID] = channel 2781 continue 2782 } 2783 2784 viewAndPostSatisfied, exists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL] 2785 if exists && viewAndPostSatisfied { 2786 delete(viewChats, channelID) 2787 viewAndPostChats[channelID] = channel 2788 continue 2789 } 2790 2791 viewOnlySatisfied, exists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL] 2792 if exists && viewOnlySatisfied { 2793 if _, exists := viewAndPostChats[channelID]; !exists { 2794 viewChats[channelID] = channel 2795 } 2796 } 2797 } 2798 2799 return viewChats, viewAndPostChats, nil 2800 } 2801 2802 func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, error) { 2803 m.communityLock.Lock(dbRequest.CommunityID) 2804 defer m.communityLock.Unlock(dbRequest.CommunityID) 2805 2806 pk, err := common.HexToPubkey(dbRequest.PublicKey) 2807 if err != nil { 2808 return nil, err 2809 } 2810 2811 community, err := m.GetByID(dbRequest.CommunityID) 2812 if err != nil { 2813 return nil, err 2814 } 2815 2816 if community.IsControlNode() { 2817 revealedAccounts, err := m.persistence.GetRequestToJoinRevealedAddresses(dbRequest.ID) 2818 if err != nil { 2819 return nil, err 2820 } 2821 2822 accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts) 2823 2824 communityPermissionsPreParsedData, channelPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions()) 2825 2826 permissionsSatisfied, role, err := m.accountsSatisfyPermissionsToJoin(communityPermissionsPreParsedData, accountsAndChainIDs) 2827 if err != nil { 2828 return nil, err 2829 } 2830 2831 if !permissionsSatisfied { 2832 return community, ErrNoPermissionToJoin 2833 } 2834 2835 memberRoles := []protobuf.CommunityMember_Roles{} 2836 if role != protobuf.CommunityMember_ROLE_NONE { 2837 memberRoles = []protobuf.CommunityMember_Roles{role} 2838 } 2839 2840 _, err = community.AddMember(pk, memberRoles, dbRequest.Clock) 2841 if err != nil { 2842 return nil, err 2843 } 2844 2845 viewChannels, postChannels, err := m.accountsSatisfyPermissionsToJoinChannels(community, channelPermissionsPreParsedData, accountsAndChainIDs) 2846 if err != nil { 2847 return nil, err 2848 } 2849 2850 for channelID := range viewChannels { 2851 _, err = community.AddMemberToChat(channelID, pk, memberRoles, protobuf.CommunityMember_CHANNEL_ROLE_VIEWER) 2852 if err != nil { 2853 return nil, err 2854 } 2855 } 2856 2857 for channelID := range postChannels { 2858 _, err = community.AddMemberToChat(channelID, pk, memberRoles, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 2859 if err != nil { 2860 return nil, err 2861 } 2862 } 2863 2864 dbRequest.State = RequestToJoinStateAccepted 2865 if err := m.markRequestToJoinAsAccepted(pk, community); err != nil { 2866 return nil, err 2867 } 2868 2869 dbRequest.RevealedAccounts = revealedAccounts 2870 if err = m.shareAcceptedRequestToJoinWithPrivilegedMembers(community, dbRequest); err != nil { 2871 return nil, err 2872 } 2873 2874 // if accepted member has a privilege role, share with him requests to join 2875 memberRole := community.MemberRole(pk) 2876 if memberRole == protobuf.CommunityMember_ROLE_OWNER || memberRole == protobuf.CommunityMember_ROLE_ADMIN || 2877 memberRole == protobuf.CommunityMember_ROLE_TOKEN_MASTER { 2878 2879 newPrivilegedMember := make(map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) 2880 newPrivilegedMember[memberRole] = []*ecdsa.PublicKey{pk} 2881 if err = m.ShareRequestsToJoinWithPrivilegedMembers(community, newPrivilegedMember); err != nil { 2882 return nil, err 2883 } 2884 } 2885 } else if community.hasPermissionToSendCommunityEvent(protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT) { 2886 err := community.addNewCommunityEvent(community.ToCommunityRequestToJoinAcceptCommunityEvent(dbRequest.PublicKey, dbRequest.ToCommunityRequestToJoinProtobuf())) 2887 if err != nil { 2888 return nil, err 2889 } 2890 2891 dbRequest.State = RequestToJoinStateAcceptedPending 2892 if err := m.markRequestToJoinAsAcceptedPending(pk, community); err != nil { 2893 return nil, err 2894 } 2895 } else { 2896 return nil, ErrNotAuthorized 2897 } 2898 2899 err = m.saveAndPublish(community) 2900 if err != nil { 2901 return nil, err 2902 } 2903 2904 return community, nil 2905 } 2906 2907 func (m *Manager) GetRequestToJoin(ID types.HexBytes) (*RequestToJoin, error) { 2908 return m.persistence.GetRequestToJoin(ID) 2909 } 2910 2911 func (m *Manager) DeclineRequestToJoin(dbRequest *RequestToJoin) (*Community, error) { 2912 m.communityLock.Lock(dbRequest.CommunityID) 2913 defer m.communityLock.Unlock(dbRequest.CommunityID) 2914 2915 community, err := m.GetByID(dbRequest.CommunityID) 2916 if err != nil { 2917 return nil, err 2918 } 2919 2920 adminEventCreated, err := community.DeclineRequestToJoin(dbRequest) 2921 if err != nil { 2922 return nil, err 2923 } 2924 2925 requestToJoinState := RequestToJoinStateDeclined 2926 if adminEventCreated { 2927 requestToJoinState = RequestToJoinStateDeclinedPending // can only be declined by control node 2928 } 2929 2930 dbRequest.State = requestToJoinState 2931 err = m.persistence.SetRequestToJoinState(dbRequest.PublicKey, dbRequest.CommunityID, requestToJoinState) 2932 if err != nil { 2933 return nil, err 2934 } 2935 2936 err = m.saveAndPublish(community) 2937 if err != nil { 2938 return nil, err 2939 } 2940 2941 return community, nil 2942 } 2943 2944 func (m *Manager) shouldUserRetainDeclined(signer *ecdsa.PublicKey, community *Community, requestClock uint64) (bool, error) { 2945 requestID := CalculateRequestID(common.PubkeyToHex(signer), types.HexBytes(community.IDString())) 2946 request, err := m.persistence.GetRequestToJoin(requestID) 2947 if err != nil { 2948 if err == sql.ErrNoRows { 2949 return false, nil 2950 } 2951 return false, err 2952 } 2953 2954 return request.ShouldRetainDeclined(requestClock) 2955 } 2956 2957 func (m *Manager) HandleCommunityCancelRequestToJoin(signer *ecdsa.PublicKey, request *protobuf.CommunityCancelRequestToJoin) (*RequestToJoin, error) { 2958 m.communityLock.Lock(request.CommunityId) 2959 defer m.communityLock.Unlock(request.CommunityId) 2960 2961 community, err := m.GetByID(request.CommunityId) 2962 if err != nil { 2963 return nil, err 2964 } 2965 2966 previousRequestToJoin, err := m.GetRequestToJoinByPkAndCommunityID(signer, community.ID()) 2967 if err != nil { 2968 return nil, err 2969 } 2970 2971 if request.Clock <= previousRequestToJoin.Clock { 2972 return nil, ErrInvalidClock 2973 } 2974 2975 retainDeclined, err := m.shouldUserRetainDeclined(signer, community, request.Clock) 2976 if err != nil { 2977 return nil, err 2978 } 2979 if retainDeclined { 2980 return nil, ErrCommunityRequestAlreadyRejected 2981 } 2982 2983 err = m.markRequestToJoinAsCanceled(signer, community) 2984 if err != nil { 2985 return nil, err 2986 } 2987 2988 requestToJoin, err := m.persistence.GetRequestToJoinByPk(common.PubkeyToHex(signer), community.ID(), RequestToJoinStateCanceled) 2989 if err != nil { 2990 return nil, err 2991 } 2992 2993 if community.HasMember(signer) { 2994 _, err = community.RemoveUserFromOrg(signer) 2995 if err != nil { 2996 return nil, err 2997 } 2998 2999 err = m.saveAndPublish(community) 3000 if err != nil { 3001 return nil, err 3002 } 3003 } 3004 3005 return requestToJoin, nil 3006 } 3007 3008 func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver *ecdsa.PublicKey, request *protobuf.CommunityRequestToJoin) (*Community, *RequestToJoin, error) { 3009 community, err := m.GetByID(request.CommunityId) 3010 if err != nil { 3011 return nil, nil, err 3012 } 3013 3014 err = community.ValidateRequestToJoin(signer, request) 3015 if err != nil { 3016 return nil, nil, err 3017 } 3018 3019 nbPendingRequestsToJoin, err := m.persistence.GetNumberOfPendingRequestsToJoin(community.ID()) 3020 if err != nil { 3021 return nil, nil, err 3022 } 3023 if nbPendingRequestsToJoin >= maxNbPendingRequestedMembers { 3024 return nil, nil, errors.New("max number of requests to join reached") 3025 } 3026 3027 requestToJoin := &RequestToJoin{ 3028 PublicKey: common.PubkeyToHex(signer), 3029 Clock: request.Clock, 3030 ENSName: request.EnsName, 3031 CommunityID: request.CommunityId, 3032 State: RequestToJoinStatePending, 3033 RevealedAccounts: request.RevealedAccounts, 3034 CustomizationColor: multiaccountscommon.IDToColorFallbackToBlue(request.CustomizationColor), 3035 } 3036 requestToJoin.CalculateID() 3037 3038 existingRequestToJoin, err := m.persistence.GetRequestToJoin(requestToJoin.ID) 3039 if err != nil && err != sql.ErrNoRows { 3040 return nil, nil, err 3041 } 3042 3043 if existingRequestToJoin == nil { 3044 err = m.SaveRequestToJoin(requestToJoin) 3045 if err != nil { 3046 return nil, nil, err 3047 } 3048 } else { 3049 retainDeclined, err := existingRequestToJoin.ShouldRetainDeclined(request.Clock) 3050 if err != nil { 3051 return nil, nil, err 3052 } 3053 if retainDeclined { 3054 return nil, nil, ErrCommunityRequestAlreadyRejected 3055 } 3056 3057 switch existingRequestToJoin.State { 3058 case RequestToJoinStatePending, RequestToJoinStateDeclined, RequestToJoinStateCanceled: 3059 // Another request have been received, save request back to pending state 3060 err = m.SaveRequestToJoin(requestToJoin) 3061 if err != nil { 3062 return nil, nil, err 3063 } 3064 case RequestToJoinStateAccepted: 3065 // if member leaved the community and tries to request to join again 3066 if !community.HasMember(signer) { 3067 err = m.SaveRequestToJoin(requestToJoin) 3068 if err != nil { 3069 return nil, nil, err 3070 } 3071 } 3072 } 3073 } 3074 3075 if community.IsControlNode() { 3076 // verify if revealed addresses indeed belong to requester 3077 for _, revealedAccount := range request.RevealedAccounts { 3078 recoverParams := account.RecoverParams{ 3079 Message: types.EncodeHex(crypto.Keccak256(crypto.CompressPubkey(signer), community.ID(), requestToJoin.ID)), 3080 Signature: types.EncodeHex(revealedAccount.Signature), 3081 } 3082 3083 matching, err := m.accountsManager.CanRecover(recoverParams, types.HexToAddress(revealedAccount.Address)) 3084 if err != nil { 3085 return nil, nil, err 3086 } 3087 if !matching { 3088 // if ownership of only one wallet address cannot be verified, 3089 // we mark the request as cancelled and stop 3090 requestToJoin.State = RequestToJoinStateDeclined 3091 return community, requestToJoin, nil 3092 } 3093 } 3094 3095 // Save revealed addresses + signatures so they can later be added 3096 // to the control node's local table of known revealed addresses 3097 err = m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts) 3098 if err != nil { 3099 return nil, nil, err 3100 } 3101 3102 if existingRequestToJoin != nil { 3103 // request to join was already processed by privileged user 3104 // and waits to get confirmation for its decision 3105 if existingRequestToJoin.State == RequestToJoinStateDeclinedPending { 3106 requestToJoin.State = RequestToJoinStateDeclined 3107 return community, requestToJoin, nil 3108 } else if existingRequestToJoin.State == RequestToJoinStateAcceptedPending { 3109 requestToJoin.State = RequestToJoinStateAccepted 3110 return community, requestToJoin, nil 3111 3112 } else if existingRequestToJoin.State == RequestToJoinStateAwaitingAddresses { 3113 // community ownership changed, accept request automatically 3114 requestToJoin.State = RequestToJoinStateAccepted 3115 return community, requestToJoin, nil 3116 } 3117 } 3118 3119 // Check if we reached the limit, if we did, change the community setting to be On Request 3120 if community.AutoAccept() && community.MembersCount() >= maxNbMembers { 3121 community.EditPermissionAccess(protobuf.CommunityPermissions_MANUAL_ACCEPT) 3122 err = m.saveAndPublish(community) 3123 if err != nil { 3124 return nil, nil, err 3125 } 3126 } 3127 3128 // If user is already a member, then accept request automatically 3129 // It may happen when member removes itself from community and then tries to rejoin 3130 // More specifically, CommunityRequestToLeave may be delivered later than CommunityRequestToJoin, or not delivered at all 3131 acceptAutomatically := community.AutoAccept() || community.HasMember(signer) 3132 if acceptAutomatically { 3133 // Don't check permissions here, 3134 // it will be done further in the processing pipeline. 3135 requestToJoin.State = RequestToJoinStateAccepted 3136 return community, requestToJoin, nil 3137 } 3138 } 3139 3140 return community, requestToJoin, nil 3141 } 3142 3143 func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, request *protobuf.CommunityEditSharedAddresses) error { 3144 m.communityLock.Lock(request.CommunityId) 3145 defer m.communityLock.Unlock(request.CommunityId) 3146 3147 community, err := m.GetByID(request.CommunityId) 3148 if err != nil { 3149 return err 3150 } 3151 3152 if !community.IsControlNode() { 3153 return ErrNotOwner 3154 } 3155 3156 publicKey := common.PubkeyToHex(signer) 3157 3158 if err := community.ValidateEditSharedAddresses(publicKey, request); err != nil { 3159 return err 3160 } 3161 3162 community.UpdateMemberLastUpdateClock(publicKey, request.Clock) 3163 // verify if revealed addresses indeed belong to requester 3164 for _, revealedAccount := range request.RevealedAccounts { 3165 recoverParams := account.RecoverParams{ 3166 Message: types.EncodeHex(crypto.Keccak256(crypto.CompressPubkey(signer), community.ID())), 3167 Signature: types.EncodeHex(revealedAccount.Signature), 3168 } 3169 3170 matching, err := m.accountsManager.CanRecover(recoverParams, types.HexToAddress(revealedAccount.Address)) 3171 if err != nil { 3172 return err 3173 } 3174 if !matching { 3175 // if ownership of only one wallet address cannot be verified we stop 3176 return errors.New("wrong wallet address used") 3177 } 3178 } 3179 3180 err = m.handleCommunityEditSharedAddresses(publicKey, request.CommunityId, request.RevealedAccounts, request.Clock) 3181 if err != nil { 3182 return err 3183 } 3184 3185 err = m.persistence.SaveCommunity(community) 3186 if err != nil { 3187 return err 3188 } 3189 3190 if community.IsControlNode() { 3191 m.publish(&Subscription{Community: community}) 3192 } 3193 3194 subscriptionMsg := &CommunityPrivilegedMemberSyncMessage{ 3195 Receivers: community.GetTokenMasterMembers(), 3196 CommunityPrivilegedUserSyncMessage: &protobuf.CommunityPrivilegedUserSyncMessage{ 3197 Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES, 3198 CommunityId: community.ID(), 3199 SyncEditSharedAddresses: &protobuf.SyncCommunityEditSharedAddresses{ 3200 PublicKey: common.PubkeyToHex(signer), 3201 EditSharedAddress: request, 3202 }, 3203 }, 3204 } 3205 3206 m.publish(&Subscription{CommunityPrivilegedMemberSyncMessage: subscriptionMsg}) 3207 3208 return nil 3209 } 3210 3211 func (m *Manager) handleCommunityEditSharedAddresses(publicKey string, communityID types.HexBytes, revealedAccounts []*protobuf.RevealedAccount, clock uint64) error { 3212 requestToJoinID := CalculateRequestID(publicKey, communityID) 3213 err := m.UpdateClockInRequestToJoin(requestToJoinID, clock) 3214 if err != nil { 3215 return err 3216 } 3217 3218 err = m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoinID) 3219 if err != nil { 3220 return err 3221 } 3222 3223 return m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoinID, revealedAccounts) 3224 } 3225 3226 func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, requirementsChainIDs map[uint64]bool) []uint64 { 3227 3228 revealedAccountsChainIDs := make([]uint64, 0) 3229 revealedAccountsChainIDsMap := make(map[uint64]bool) 3230 3231 // we want all chainIDs provided by revealed addresses that also exist 3232 // in the token requirements 3233 for _, accountAndChainIDs := range accountsAndChainIDs { 3234 for _, chainID := range accountAndChainIDs.ChainIDs { 3235 if requirementsChainIDs[chainID] && !revealedAccountsChainIDsMap[chainID] { 3236 revealedAccountsChainIDsMap[chainID] = true 3237 revealedAccountsChainIDs = append(revealedAccountsChainIDs, chainID) 3238 } 3239 } 3240 } 3241 return revealedAccountsChainIDs 3242 } 3243 3244 type CollectiblesByChain = map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress 3245 3246 func (m *Manager) GetOwnedERC721Tokens(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error) { 3247 if m.collectiblesManager == nil { 3248 return nil, errors.New("no collectibles manager") 3249 } 3250 3251 ctx := context.Background() 3252 3253 ownedERC721Tokens := make(CollectiblesByChain) 3254 3255 for chainID, erc721Tokens := range tokenRequirements { 3256 3257 skipChain := true 3258 for _, cID := range chainIDs { 3259 if chainID == cID { 3260 skipChain = false 3261 } 3262 } 3263 3264 if skipChain { 3265 continue 3266 } 3267 3268 contractAddresses := make([]gethcommon.Address, 0) 3269 for contractAddress := range erc721Tokens { 3270 contractAddresses = append(contractAddresses, gethcommon.HexToAddress(contractAddress)) 3271 } 3272 3273 if _, exists := ownedERC721Tokens[chainID]; !exists { 3274 ownedERC721Tokens[chainID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress) 3275 } 3276 3277 for _, owner := range walletAddresses { 3278 balances, err := m.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, walletcommon.ChainID(chainID), owner, contractAddresses) 3279 if err != nil { 3280 m.logger.Info("couldn't fetch owner assets", zap.Error(err)) 3281 return nil, err 3282 } 3283 ownedERC721Tokens[chainID][owner] = balances 3284 } 3285 } 3286 return ownedERC721Tokens, nil 3287 } 3288 3289 func (m *Manager) CheckChannelPermissions(communityID types.HexBytes, chatID string, addresses []gethcommon.Address) (*CheckChannelPermissionsResponse, error) { 3290 m.communityLock.Lock(communityID) 3291 defer m.communityLock.Unlock(communityID) 3292 3293 community, err := m.GetByID(communityID) 3294 if err != nil { 3295 return nil, err 3296 } 3297 3298 if chatID == "" { 3299 return nil, errors.New(fmt.Sprintf("couldn't check channel permissions, invalid chat id: %s", chatID)) 3300 } 3301 3302 viewOnlyPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL) 3303 viewAndPostPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL) 3304 viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions) 3305 viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions) 3306 3307 allChainIDs, err := m.tokenManager.GetAllChainIDs() 3308 if err != nil { 3309 return nil, err 3310 } 3311 accountsAndChainIDs := combineAddressesAndChainIDs(addresses, allChainIDs) 3312 3313 response, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountsAndChainIDs, false) 3314 if err != nil { 3315 return nil, err 3316 } 3317 3318 err = m.persistence.SaveCheckChannelPermissionResponse(communityID.String(), chatID, response) 3319 if err != nil { 3320 return nil, err 3321 } 3322 return response, nil 3323 } 3324 3325 type CheckChannelPermissionsResponse struct { 3326 ViewOnlyPermissions *CheckChannelViewOnlyPermissionsResult `json:"viewOnlyPermissions"` 3327 ViewAndPostPermissions *CheckChannelViewAndPostPermissionsResult `json:"viewAndPostPermissions"` 3328 } 3329 3330 type CheckChannelViewOnlyPermissionsResult struct { 3331 Satisfied bool `json:"satisfied"` 3332 Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"` 3333 } 3334 3335 type CheckChannelViewAndPostPermissionsResult struct { 3336 Satisfied bool `json:"satisfied"` 3337 Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"` 3338 } 3339 3340 func computeViewOnlySatisfied(hasViewOnlyPermissions bool, hasViewAndPostPermissions bool, checkedViewOnlySatisfied bool, checkedViewAndPostSatisified bool) bool { 3341 if (hasViewAndPostPermissions && !hasViewOnlyPermissions) || (hasViewOnlyPermissions && hasViewAndPostPermissions && checkedViewAndPostSatisified) { 3342 return checkedViewAndPostSatisified 3343 } else { 3344 return checkedViewOnlySatisfied 3345 } 3346 } 3347 3348 func computeViewAndPostSatisfied(hasViewOnlyPermissions bool, hasViewAndPostPermissions bool, checkedViewAndPostSatisified bool) bool { 3349 if hasViewOnlyPermissions && !hasViewAndPostPermissions { 3350 return false 3351 } else { 3352 return checkedViewAndPostSatisified 3353 } 3354 } 3355 3356 func (m *Manager) checkChannelPermissions(viewOnlyPreParsedPermissions *PreParsedCommunityPermissionsData, viewAndPostPreParsedPermissions *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckChannelPermissionsResponse, error) { 3357 viewOnlyPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewOnlyPreParsedPermissions, accountsAndChainIDs, shortcircuit) 3358 if err != nil { 3359 return nil, err 3360 } 3361 3362 viewAndPostPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewAndPostPreParsedPermissions, accountsAndChainIDs, shortcircuit) 3363 if err != nil { 3364 return nil, err 3365 } 3366 3367 hasViewOnlyPermissions := viewOnlyPreParsedPermissions != nil 3368 hasViewAndPostPermissions := viewAndPostPreParsedPermissions != nil 3369 3370 return computeCheckChannelPermissionsResponse(hasViewOnlyPermissions, hasViewAndPostPermissions, 3371 viewOnlyPermissionsResponse, viewAndPostPermissionsResponse), 3372 nil 3373 } 3374 3375 func computeCheckChannelPermissionsResponse(hasViewOnlyPermissions bool, hasViewAndPostPermissions bool, 3376 viewOnlyPermissionsResponse *CheckPermissionsResponse, viewAndPostPermissionsResponse *CheckPermissionsResponse) *CheckChannelPermissionsResponse { 3377 3378 response := &CheckChannelPermissionsResponse{ 3379 ViewOnlyPermissions: &CheckChannelViewOnlyPermissionsResult{ 3380 Satisfied: false, 3381 Permissions: make(map[string]*PermissionTokenCriteriaResult), 3382 }, 3383 ViewAndPostPermissions: &CheckChannelViewAndPostPermissionsResult{ 3384 Satisfied: false, 3385 Permissions: make(map[string]*PermissionTokenCriteriaResult), 3386 }, 3387 } 3388 3389 viewOnlySatisfied := !hasViewOnlyPermissions || viewOnlyPermissionsResponse.Satisfied 3390 viewAndPostSatisfied := !hasViewAndPostPermissions || viewAndPostPermissionsResponse.Satisfied 3391 3392 response.ViewOnlyPermissions.Satisfied = computeViewOnlySatisfied(hasViewOnlyPermissions, hasViewAndPostPermissions, 3393 viewOnlySatisfied, viewAndPostSatisfied) 3394 if viewOnlyPermissionsResponse != nil { 3395 response.ViewOnlyPermissions.Permissions = viewOnlyPermissionsResponse.Permissions 3396 } 3397 3398 response.ViewAndPostPermissions.Satisfied = computeViewAndPostSatisfied(hasViewOnlyPermissions, hasViewAndPostPermissions, 3399 viewAndPostSatisfied) 3400 3401 if viewAndPostPermissionsResponse != nil { 3402 response.ViewAndPostPermissions.Permissions = viewAndPostPermissionsResponse.Permissions 3403 } 3404 3405 return response 3406 } 3407 3408 func (m *Manager) CheckAllChannelsPermissions(communityID types.HexBytes, addresses []gethcommon.Address) (*CheckAllChannelsPermissionsResponse, error) { 3409 3410 community, err := m.GetByID(communityID) 3411 if err != nil { 3412 return nil, err 3413 } 3414 channels := community.Chats() 3415 3416 allChainIDs, err := m.tokenManager.GetAllChainIDs() 3417 if err != nil { 3418 return nil, err 3419 } 3420 accountsAndChainIDs := combineAddressesAndChainIDs(addresses, allChainIDs) 3421 3422 _, channelsPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions()) 3423 3424 channelPermissionsCheckResult := make(map[string]map[protobuf.CommunityTokenPermission_Type]*CheckPermissionsResponse) 3425 3426 for permissionId, channelsPermissionPreParsedData := range channelsPermissionsPreParsedData { 3427 permissionResponse, err := m.PermissionChecker.CheckPermissions(channelsPermissionPreParsedData, accountsAndChainIDs, false) 3428 if err != nil { 3429 return nil, err 3430 } 3431 3432 // Note: in `PreParsedCommunityPermissionsData` for channels there will be only one permission for channels 3433 for _, chatId := range channelsPermissionPreParsedData.Permissions[0].ChatIds { 3434 if _, exists := channelPermissionsCheckResult[chatId]; !exists { 3435 channelPermissionsCheckResult[chatId] = make(map[protobuf.CommunityTokenPermission_Type]*CheckPermissionsResponse) 3436 } 3437 storedPermissionResponse, exists := channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type] 3438 if !exists { 3439 channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type] = 3440 permissionResponse 3441 } else { 3442 channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type].Permissions[permissionId] = 3443 permissionResponse.Permissions[permissionId] 3444 channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type].Satisfied = 3445 storedPermissionResponse.Satisfied || permissionResponse.Satisfied 3446 } 3447 } 3448 } 3449 3450 response := &CheckAllChannelsPermissionsResponse{ 3451 Channels: make(map[string]*CheckChannelPermissionsResponse), 3452 } 3453 3454 for channelID := range channels { 3455 chatId := community.ChatID(channelID) 3456 3457 channelCheckPermissionsResponse, exists := channelPermissionsCheckResult[chatId] 3458 3459 var channelPermissionsResponse *CheckChannelPermissionsResponse 3460 if !exists { 3461 channelPermissionsResponse = computeCheckChannelPermissionsResponse(false, false, nil, nil) 3462 } else { 3463 viewPermissionsResponse, viewExists := channelCheckPermissionsResponse[protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL] 3464 postPermissionsResponse, postExists := channelCheckPermissionsResponse[protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL] 3465 channelPermissionsResponse = computeCheckChannelPermissionsResponse(viewExists, postExists, viewPermissionsResponse, postPermissionsResponse) 3466 } 3467 3468 err = m.persistence.SaveCheckChannelPermissionResponse(community.IDString(), chatId, channelPermissionsResponse) 3469 if err != nil { 3470 return nil, err 3471 } 3472 response.Channels[chatId] = channelPermissionsResponse 3473 } 3474 return response, nil 3475 } 3476 3477 func (m *Manager) GetCheckChannelPermissionResponses(communityID types.HexBytes) (*CheckAllChannelsPermissionsResponse, error) { 3478 3479 response, err := m.persistence.GetCheckChannelPermissionResponses(communityID.String()) 3480 if err != nil { 3481 return nil, err 3482 } 3483 return &CheckAllChannelsPermissionsResponse{Channels: response}, nil 3484 } 3485 3486 type CheckAllChannelsPermissionsResponse struct { 3487 Channels map[string]*CheckChannelPermissionsResponse `json:"channels"` 3488 } 3489 3490 func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey, request *protobuf.CommunityRequestToJoinResponse) (*RequestToJoin, error) { 3491 m.communityLock.Lock(request.CommunityId) 3492 defer m.communityLock.Unlock(request.CommunityId) 3493 3494 pkString := common.PubkeyToHex(&m.identity.PublicKey) 3495 3496 community, err := m.GetByID(request.CommunityId) 3497 if err != nil { 3498 return nil, err 3499 } 3500 3501 communityDescriptionBytes, err := proto.Marshal(request.Community) 3502 if err != nil { 3503 return nil, err 3504 } 3505 3506 // We need to wrap `request.Community` in an `ApplicationMetadataMessage` 3507 // of type `CommunityDescription` because `UpdateCommunityDescription` expects this. 3508 // 3509 // This is merely for marsheling/unmarsheling, hence we attaching a `Signature` 3510 // is not needed. 3511 metadataMessage := &protobuf.ApplicationMetadataMessage{ 3512 Payload: communityDescriptionBytes, 3513 Type: protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, 3514 } 3515 3516 appMetadataMsg, err := proto.Marshal(metadataMessage) 3517 if err != nil { 3518 return nil, err 3519 } 3520 3521 isControlNodeSigner := common.IsPubKeyEqual(community.ControlNode(), signer) 3522 if !isControlNodeSigner { 3523 m.logger.Debug("signer is not control node", zap.String("signer", common.PubkeyToHex(signer)), zap.String("controlNode", common.PubkeyToHex(community.ControlNode()))) 3524 return nil, ErrNotAuthorized 3525 } 3526 3527 _, processedDescription, err := m.preprocessDescription(community.ID(), request.Community) 3528 if err != nil { 3529 return nil, err 3530 } 3531 3532 _, err = community.UpdateCommunityDescription(processedDescription, appMetadataMsg, nil) 3533 if err != nil { 3534 return nil, err 3535 } 3536 3537 if err = m.handleCommunityTokensMetadata(community); err != nil { 3538 return nil, err 3539 } 3540 3541 if community.Encrypted() && len(request.Grant) > 0 { 3542 _, err = m.HandleCommunityGrant(community, request.Grant, request.Clock) 3543 if err != nil && err != ErrGrantOlder && err != ErrGrantExpired { 3544 m.logger.Error("Error handling a community grant", zap.Error(err)) 3545 } 3546 } 3547 3548 err = m.persistence.SaveCommunity(community) 3549 3550 if err != nil { 3551 return nil, err 3552 } 3553 3554 if request.Accepted { 3555 err = m.markRequestToJoinAsAccepted(&m.identity.PublicKey, community) 3556 if err != nil { 3557 return nil, err 3558 } 3559 } else { 3560 3561 err = m.persistence.SetRequestToJoinState(pkString, community.ID(), RequestToJoinStateDeclined) 3562 if err != nil { 3563 return nil, err 3564 } 3565 } 3566 3567 return m.persistence.GetRequestToJoinByPkAndCommunityID(pkString, community.ID()) 3568 } 3569 3570 func (m *Manager) HandleCommunityRequestToLeave(signer *ecdsa.PublicKey, proto *protobuf.CommunityRequestToLeave) error { 3571 requestToLeave := NewRequestToLeave(common.PubkeyToHex(signer), proto) 3572 if err := m.persistence.SaveRequestToLeave(requestToLeave); err != nil { 3573 return err 3574 } 3575 3576 // Ensure corresponding requestToJoin clock is older than requestToLeave 3577 requestToJoin, err := m.persistence.GetRequestToJoin(requestToLeave.ID) 3578 if err != nil { 3579 return err 3580 } 3581 if requestToJoin.Clock > requestToLeave.Clock { 3582 return ErrOldRequestToLeave 3583 } 3584 3585 return nil 3586 } 3587 3588 func UnwrapCommunityDescriptionMessage(payload []byte) (*ecdsa.PublicKey, *protobuf.CommunityDescription, error) { 3589 3590 applicationMetadataMessage := &protobuf.ApplicationMetadataMessage{} 3591 err := proto.Unmarshal(payload, applicationMetadataMessage) 3592 if err != nil { 3593 return nil, nil, err 3594 } 3595 if applicationMetadataMessage.Type != protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION { 3596 return nil, nil, ErrInvalidMessage 3597 } 3598 signer, err := utils.RecoverKey(applicationMetadataMessage) 3599 if err != nil { 3600 return nil, nil, err 3601 } 3602 3603 description := &protobuf.CommunityDescription{} 3604 3605 err = proto.Unmarshal(applicationMetadataMessage.Payload, description) 3606 if err != nil { 3607 return nil, nil, err 3608 } 3609 3610 return signer, description, nil 3611 } 3612 3613 func (m *Manager) JoinCommunity(id types.HexBytes, forceJoin bool) (*Community, error) { 3614 m.communityLock.Lock(id) 3615 defer m.communityLock.Unlock(id) 3616 3617 community, err := m.GetByID(id) 3618 if err != nil { 3619 return nil, err 3620 } 3621 if !forceJoin && community.Joined() { 3622 // Nothing to do, we are already joined 3623 return community, ErrOrgAlreadyJoined 3624 } 3625 community.Join() 3626 err = m.persistence.SaveCommunity(community) 3627 if err != nil { 3628 return nil, err 3629 } 3630 return community, nil 3631 } 3632 3633 func (m *Manager) SpectateCommunity(id types.HexBytes) (*Community, error) { 3634 m.communityLock.Lock(id) 3635 defer m.communityLock.Unlock(id) 3636 3637 community, err := m.GetByID(id) 3638 if err != nil { 3639 return nil, err 3640 } 3641 community.Spectate() 3642 if err = m.persistence.SaveCommunity(community); err != nil { 3643 return nil, err 3644 } 3645 return community, nil 3646 } 3647 3648 func (m *Manager) GetMagnetlinkMessageClock(communityID types.HexBytes) (uint64, error) { 3649 return m.persistence.GetMagnetlinkMessageClock(communityID) 3650 } 3651 3652 func (m *Manager) GetCommunityRequestToJoinClock(pk *ecdsa.PublicKey, communityID string) (uint64, error) { 3653 communityIDBytes, err := types.DecodeHex(communityID) 3654 if err != nil { 3655 return 0, err 3656 } 3657 3658 joinClock, err := m.persistence.GetRequestToJoinClockByPkAndCommunityID(common.PubkeyToHex(pk), communityIDBytes) 3659 3660 if errors.Is(err, sql.ErrNoRows) { 3661 return 0, nil 3662 } else if err != nil { 3663 return 0, err 3664 } 3665 3666 return joinClock, nil 3667 } 3668 3669 func (m *Manager) GetRequestToJoinByPkAndCommunityID(pk *ecdsa.PublicKey, communityID []byte) (*RequestToJoin, error) { 3670 return m.persistence.GetRequestToJoinByPkAndCommunityID(common.PubkeyToHex(pk), communityID) 3671 } 3672 3673 func (m *Manager) UpdateCommunityDescriptionMagnetlinkMessageClock(communityID types.HexBytes, clock uint64) error { 3674 m.communityLock.Lock(communityID) 3675 defer m.communityLock.Unlock(communityID) 3676 3677 community, err := m.GetByIDString(communityID.String()) 3678 if err != nil { 3679 return err 3680 } 3681 community.config.CommunityDescription.ArchiveMagnetlinkClock = clock 3682 return m.persistence.SaveCommunity(community) 3683 } 3684 3685 func (m *Manager) UpdateMagnetlinkMessageClock(communityID types.HexBytes, clock uint64) error { 3686 return m.persistence.UpdateMagnetlinkMessageClock(communityID, clock) 3687 } 3688 3689 func (m *Manager) UpdateLastSeenMagnetlink(communityID types.HexBytes, magnetlinkURI string) error { 3690 return m.persistence.UpdateLastSeenMagnetlink(communityID, magnetlinkURI) 3691 } 3692 3693 func (m *Manager) GetLastSeenMagnetlink(communityID types.HexBytes) (string, error) { 3694 return m.persistence.GetLastSeenMagnetlink(communityID) 3695 } 3696 3697 func (m *Manager) LeaveCommunity(id types.HexBytes) (*Community, error) { 3698 m.communityLock.Lock(id) 3699 defer m.communityLock.Unlock(id) 3700 3701 community, err := m.GetByID(id) 3702 if err != nil { 3703 return nil, err 3704 } 3705 3706 community.RemoveOurselvesFromOrg(&m.identity.PublicKey) 3707 community.Leave() 3708 3709 if err = m.persistence.SaveCommunity(community); err != nil { 3710 return nil, err 3711 } 3712 3713 return community, nil 3714 } 3715 3716 // Same as LeaveCommunity, but we have an option to stay spectating 3717 func (m *Manager) KickedOutOfCommunity(id types.HexBytes, spectateMode bool) (*Community, error) { 3718 m.communityLock.Lock(id) 3719 defer m.communityLock.Unlock(id) 3720 3721 community, err := m.GetByID(id) 3722 if err != nil { 3723 return nil, err 3724 } 3725 3726 community.RemoveOurselvesFromOrg(&m.identity.PublicKey) 3727 community.Leave() 3728 if spectateMode { 3729 community.Spectate() 3730 } 3731 3732 if err = m.persistence.SaveCommunity(community); err != nil { 3733 return nil, err 3734 } 3735 3736 return community, nil 3737 } 3738 3739 func (m *Manager) AddMemberOwnerToCommunity(communityID types.HexBytes, pk *ecdsa.PublicKey) (*Community, error) { 3740 m.communityLock.Lock(communityID) 3741 defer m.communityLock.Unlock(communityID) 3742 3743 community, err := m.GetByID(communityID) 3744 if err != nil { 3745 return nil, err 3746 } 3747 3748 _, err = community.AddMember(pk, []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_OWNER}, community.Clock()) 3749 if err != nil { 3750 return nil, err 3751 } 3752 3753 err = m.persistence.SaveCommunity(community) 3754 if err != nil { 3755 return nil, err 3756 } 3757 3758 m.publish(&Subscription{Community: community}) 3759 return community, nil 3760 } 3761 3762 func (m *Manager) RemoveUserFromCommunity(id types.HexBytes, pk *ecdsa.PublicKey) (*Community, error) { 3763 m.communityLock.Lock(id) 3764 defer m.communityLock.Unlock(id) 3765 3766 community, err := m.GetByID(id) 3767 if err != nil { 3768 return nil, err 3769 } 3770 3771 _, err = community.RemoveUserFromOrg(pk) 3772 if err != nil { 3773 return nil, err 3774 } 3775 3776 err = m.saveAndPublish(community) 3777 if err != nil { 3778 return nil, err 3779 } 3780 3781 return community, nil 3782 } 3783 3784 func (m *Manager) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunity) (*Community, error) { 3785 m.communityLock.Lock(request.CommunityID) 3786 defer m.communityLock.Unlock(request.CommunityID) 3787 3788 id := request.CommunityID 3789 publicKey, err := common.HexToPubkey(request.User.String()) 3790 if err != nil { 3791 return nil, err 3792 } 3793 3794 community, err := m.GetByID(id) 3795 if err != nil { 3796 return nil, err 3797 } 3798 3799 _, err = community.UnbanUserFromCommunity(publicKey) 3800 if err != nil { 3801 return nil, err 3802 } 3803 3804 err = m.saveAndPublish(community) 3805 if err != nil { 3806 return nil, err 3807 } 3808 3809 return community, nil 3810 } 3811 3812 func (m *Manager) AddRoleToMember(request *requests.AddRoleToMember) (*Community, error) { 3813 m.communityLock.Lock(request.CommunityID) 3814 defer m.communityLock.Unlock(request.CommunityID) 3815 3816 id := request.CommunityID 3817 publicKey, err := common.HexToPubkey(request.User.String()) 3818 if err != nil { 3819 return nil, err 3820 } 3821 3822 community, err := m.GetByID(id) 3823 if err != nil { 3824 return nil, err 3825 } 3826 3827 if !community.hasMember(publicKey) { 3828 return nil, ErrMemberNotFound 3829 } 3830 3831 _, err = community.AddRoleToMember(publicKey, request.Role) 3832 if err != nil { 3833 return nil, err 3834 } 3835 3836 err = m.persistence.SaveCommunity(community) 3837 if err != nil { 3838 return nil, err 3839 } 3840 3841 m.publish(&Subscription{Community: community}) 3842 3843 return community, nil 3844 } 3845 3846 func (m *Manager) RemoveRoleFromMember(request *requests.RemoveRoleFromMember) (*Community, error) { 3847 m.communityLock.Lock(request.CommunityID) 3848 defer m.communityLock.Unlock(request.CommunityID) 3849 3850 id := request.CommunityID 3851 publicKey, err := common.HexToPubkey(request.User.String()) 3852 if err != nil { 3853 return nil, err 3854 } 3855 3856 community, err := m.GetByID(id) 3857 if err != nil { 3858 return nil, err 3859 } 3860 3861 if !community.hasMember(publicKey) { 3862 return nil, ErrMemberNotFound 3863 } 3864 3865 _, err = community.RemoveRoleFromMember(publicKey, request.Role) 3866 if err != nil { 3867 return nil, err 3868 } 3869 3870 err = m.persistence.SaveCommunity(community) 3871 if err != nil { 3872 return nil, err 3873 } 3874 3875 m.publish(&Subscription{Community: community}) 3876 3877 return community, nil 3878 } 3879 3880 func (m *Manager) BanUserFromCommunity(request *requests.BanUserFromCommunity) (*Community, error) { 3881 m.communityLock.Lock(request.CommunityID) 3882 defer m.communityLock.Unlock(request.CommunityID) 3883 3884 id := request.CommunityID 3885 3886 publicKey, err := common.HexToPubkey(request.User.String()) 3887 if err != nil { 3888 return nil, err 3889 } 3890 3891 community, err := m.GetByID(id) 3892 if err != nil { 3893 return nil, err 3894 } 3895 3896 _, err = community.BanUserFromCommunity(publicKey, &protobuf.CommunityBanInfo{DeleteAllMessages: request.DeleteAllMessages}) 3897 if err != nil { 3898 return nil, err 3899 } 3900 3901 err = m.saveAndPublish(community) 3902 if err != nil { 3903 return nil, err 3904 } 3905 3906 return community, nil 3907 } 3908 3909 func (m *Manager) dbRecordBundleToCommunity(r *CommunityRecordBundle) (*Community, error) { 3910 var descriptionEncryptor DescriptionEncryptor 3911 if m.encryptor != nil { 3912 descriptionEncryptor = m 3913 } 3914 3915 initializer := func(community *Community) error { 3916 _, description, err := m.preprocessDescription(community.ID(), community.config.CommunityDescription) 3917 if err != nil { 3918 return err 3919 } 3920 3921 community.config.CommunityDescription = description 3922 3923 if community.config.EventsData != nil { 3924 eventsDescription, err := validateAndGetEventsMessageCommunityDescription(community.config.EventsData.EventsBaseCommunityDescription, community.ControlNode()) 3925 if err != nil { 3926 m.logger.Error("invalid EventsBaseCommunityDescription", zap.Error(err)) 3927 } 3928 if eventsDescription != nil && eventsDescription.Clock == community.Clock() { 3929 community.applyEvents() 3930 } 3931 } 3932 3933 if m.transport != nil && m.transport.WakuVersion() == 2 { 3934 topic := community.PubsubTopic() 3935 privKey, err := m.transport.RetrievePubsubTopicKey(topic) 3936 if err != nil { 3937 return err 3938 } 3939 community.config.PubsubTopicPrivateKey = privKey 3940 } 3941 3942 return nil 3943 } 3944 3945 return recordBundleToCommunity( 3946 r, 3947 m.identity, 3948 m.installationID, 3949 m.logger, 3950 m.timesource, 3951 descriptionEncryptor, 3952 m.mediaServer, 3953 initializer, 3954 ) 3955 } 3956 3957 func (m *Manager) GetByID(id []byte) (*Community, error) { 3958 community, err := m.persistence.GetByID(&m.identity.PublicKey, id) 3959 if err != nil { 3960 return nil, err 3961 } 3962 if community == nil { 3963 return nil, ErrOrgNotFound 3964 } 3965 return community, nil 3966 } 3967 3968 func (m *Manager) GetByIDString(idString string) (*Community, error) { 3969 id, err := types.DecodeHex(idString) 3970 if err != nil { 3971 return nil, err 3972 } 3973 return m.GetByID(id) 3974 } 3975 3976 func (m *Manager) GetCommunityShard(communityID types.HexBytes) (*shard.Shard, error) { 3977 return m.persistence.GetCommunityShard(communityID) 3978 } 3979 3980 func (m *Manager) SaveCommunityShard(communityID types.HexBytes, shard *shard.Shard, clock uint64) error { 3981 m.communityLock.Lock(communityID) 3982 defer m.communityLock.Unlock(communityID) 3983 3984 return m.persistence.SaveCommunityShard(communityID, shard, clock) 3985 } 3986 3987 func (m *Manager) DeleteCommunityShard(communityID types.HexBytes) error { 3988 m.communityLock.Lock(communityID) 3989 defer m.communityLock.Unlock(communityID) 3990 3991 return m.persistence.DeleteCommunityShard(communityID) 3992 } 3993 3994 func (m *Manager) SaveRequestToJoinRevealedAddresses(requestID types.HexBytes, revealedAccounts []*protobuf.RevealedAccount) error { 3995 return m.persistence.SaveRequestToJoinRevealedAddresses(requestID, revealedAccounts) 3996 } 3997 3998 func (m *Manager) RemoveRequestToJoinRevealedAddresses(requestID types.HexBytes) error { 3999 return m.persistence.RemoveRequestToJoinRevealedAddresses(requestID) 4000 } 4001 4002 func (m *Manager) SaveRequestToJoinAndCommunity(requestToJoin *RequestToJoin, community *Community) (*Community, *RequestToJoin, error) { 4003 if err := m.persistence.SaveRequestToJoin(requestToJoin); err != nil { 4004 return nil, nil, err 4005 } 4006 community.config.RequestedToJoinAt = uint64(time.Now().Unix()) 4007 community.AddRequestToJoin(requestToJoin) 4008 4009 // Save revealed addresses to our own table so that we can retrieve them later when editing 4010 if err := m.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts); err != nil { 4011 return nil, nil, err 4012 } 4013 4014 return community, requestToJoin, nil 4015 } 4016 4017 func (m *Manager) CreateRequestToJoin(request *requests.RequestToJoinCommunity, customizationColor multiaccountscommon.CustomizationColor) *RequestToJoin { 4018 clock := uint64(time.Now().Unix()) 4019 requestToJoin := &RequestToJoin{ 4020 PublicKey: common.PubkeyToHex(&m.identity.PublicKey), 4021 Clock: clock, 4022 ENSName: request.ENSName, 4023 CommunityID: request.CommunityID, 4024 State: RequestToJoinStatePending, 4025 Our: true, 4026 RevealedAccounts: make([]*protobuf.RevealedAccount, 0), 4027 CustomizationColor: customizationColor, 4028 ShareFutureAddresses: request.ShareFutureAddresses, 4029 } 4030 4031 requestToJoin.CalculateID() 4032 4033 addSignature := len(request.Signatures) == len(request.AddressesToReveal) 4034 for i := range request.AddressesToReveal { 4035 revealedAcc := &protobuf.RevealedAccount{ 4036 Address: request.AddressesToReveal[i], 4037 IsAirdropAddress: types.HexToAddress(request.AddressesToReveal[i]) == types.HexToAddress(request.AirdropAddress), 4038 } 4039 4040 if addSignature { 4041 revealedAcc.Signature = request.Signatures[i] 4042 } 4043 4044 requestToJoin.RevealedAccounts = append(requestToJoin.RevealedAccounts, revealedAcc) 4045 } 4046 4047 return requestToJoin 4048 } 4049 4050 func (m *Manager) SaveRequestToJoin(request *RequestToJoin) error { 4051 return m.persistence.SaveRequestToJoin(request) 4052 } 4053 4054 func (m *Manager) CanceledRequestsToJoinForUser(pk *ecdsa.PublicKey) ([]*RequestToJoin, error) { 4055 return m.persistence.CanceledRequestsToJoinForUser(common.PubkeyToHex(pk)) 4056 } 4057 4058 func (m *Manager) PendingRequestsToJoin() ([]*RequestToJoin, error) { 4059 return m.persistence.PendingRequestsToJoin() 4060 } 4061 4062 func (m *Manager) PendingRequestsToJoinForUser(pk *ecdsa.PublicKey) ([]*RequestToJoin, error) { 4063 return m.persistence.RequestsToJoinForUserByState(common.PubkeyToHex(pk), RequestToJoinStatePending) 4064 } 4065 4066 func (m *Manager) PendingRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4067 m.logger.Info("fetching pending invitations", zap.String("community-id", id.String())) 4068 return m.persistence.PendingRequestsToJoinForCommunity(id) 4069 } 4070 4071 func (m *Manager) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4072 m.logger.Info("fetching declined invitations", zap.String("community-id", id.String())) 4073 return m.persistence.DeclinedRequestsToJoinForCommunity(id) 4074 } 4075 4076 func (m *Manager) CanceledRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4077 m.logger.Info("fetching canceled invitations", zap.String("community-id", id.String())) 4078 return m.persistence.CanceledRequestsToJoinForCommunity(id) 4079 } 4080 4081 func (m *Manager) AcceptedRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4082 m.logger.Info("fetching canceled invitations", zap.String("community-id", id.String())) 4083 return m.persistence.AcceptedRequestsToJoinForCommunity(id) 4084 } 4085 4086 func (m *Manager) AcceptedPendingRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4087 return m.persistence.AcceptedPendingRequestsToJoinForCommunity(id) 4088 } 4089 4090 func (m *Manager) DeclinedPendingRequestsToJoinForCommunity(id types.HexBytes) ([]*RequestToJoin, error) { 4091 return m.persistence.DeclinedPendingRequestsToJoinForCommunity(id) 4092 } 4093 4094 func (m *Manager) AllNonApprovedCommunitiesRequestsToJoin() ([]*RequestToJoin, error) { 4095 m.logger.Info("fetching all non-approved invitations for all communities") 4096 return m.persistence.AllNonApprovedCommunitiesRequestsToJoin() 4097 } 4098 4099 func (m *Manager) RequestsToJoinForCommunityAwaitingAddresses(id types.HexBytes) ([]*RequestToJoin, error) { 4100 m.logger.Info("fetching ownership changed invitations", zap.String("community-id", id.String())) 4101 return m.persistence.RequestsToJoinForCommunityAwaitingAddresses(id) 4102 } 4103 4104 func (m *Manager) CanPost(pk *ecdsa.PublicKey, communityID string, chatID string, messageType protobuf.ApplicationMetadataMessage_Type) (bool, error) { 4105 community, err := m.GetByIDString(communityID) 4106 if err != nil { 4107 return false, err 4108 } 4109 return community.CanPost(pk, chatID, messageType) 4110 } 4111 4112 func (m *Manager) IsEncrypted(communityID string) (bool, error) { 4113 community, err := m.GetByIDString(communityID) 4114 if err != nil { 4115 return false, err 4116 } 4117 4118 return community.Encrypted(), nil 4119 } 4120 4121 func (m *Manager) IsChannelEncrypted(communityID string, chatID string) (bool, error) { 4122 community, err := m.GetByIDString(communityID) 4123 if err != nil { 4124 return false, err 4125 } 4126 4127 channelID := strings.TrimPrefix(chatID, communityID) 4128 return community.ChannelEncrypted(channelID), nil 4129 } 4130 4131 func (m *Manager) ShouldHandleSyncCommunity(community *protobuf.SyncInstallationCommunity) (bool, error) { 4132 return m.persistence.ShouldHandleSyncCommunity(community) 4133 } 4134 4135 func (m *Manager) ShouldHandleSyncCommunitySettings(communitySettings *protobuf.SyncCommunitySettings) (bool, error) { 4136 return m.persistence.ShouldHandleSyncCommunitySettings(communitySettings) 4137 } 4138 4139 func (m *Manager) HandleSyncCommunitySettings(syncCommunitySettings *protobuf.SyncCommunitySettings) (*CommunitySettings, error) { 4140 id, err := types.DecodeHex(syncCommunitySettings.CommunityId) 4141 if err != nil { 4142 return nil, err 4143 } 4144 4145 settings, err := m.persistence.GetCommunitySettingsByID(id) 4146 if err != nil { 4147 return nil, err 4148 } 4149 4150 if settings == nil { 4151 settings = &CommunitySettings{ 4152 CommunityID: syncCommunitySettings.CommunityId, 4153 HistoryArchiveSupportEnabled: syncCommunitySettings.HistoryArchiveSupportEnabled, 4154 Clock: syncCommunitySettings.Clock, 4155 } 4156 } 4157 4158 if syncCommunitySettings.Clock > settings.Clock { 4159 settings.CommunityID = syncCommunitySettings.CommunityId 4160 settings.HistoryArchiveSupportEnabled = syncCommunitySettings.HistoryArchiveSupportEnabled 4161 settings.Clock = syncCommunitySettings.Clock 4162 } 4163 4164 err = m.persistence.SaveCommunitySettings(*settings) 4165 if err != nil { 4166 return nil, err 4167 } 4168 return settings, nil 4169 } 4170 4171 func (m *Manager) SetSyncClock(id []byte, clock uint64) error { 4172 return m.persistence.SetSyncClock(id, clock) 4173 } 4174 4175 func (m *Manager) SetPrivateKey(id []byte, privKey *ecdsa.PrivateKey) error { 4176 return m.persistence.SetPrivateKey(id, privKey) 4177 } 4178 4179 func (m *Manager) GetSyncedRawCommunity(id []byte) (*RawCommunityRow, error) { 4180 return m.persistence.getSyncedRawCommunity(id) 4181 } 4182 4183 func (m *Manager) GetCommunitySettingsByID(id types.HexBytes) (*CommunitySettings, error) { 4184 return m.persistence.GetCommunitySettingsByID(id) 4185 } 4186 4187 func (m *Manager) GetCommunitiesSettings() ([]CommunitySettings, error) { 4188 return m.persistence.GetCommunitiesSettings() 4189 } 4190 4191 func (m *Manager) SaveCommunitySettings(settings CommunitySettings) error { 4192 return m.persistence.SaveCommunitySettings(settings) 4193 } 4194 4195 func (m *Manager) CommunitySettingsExist(id types.HexBytes) (bool, error) { 4196 return m.persistence.CommunitySettingsExist(id) 4197 } 4198 4199 func (m *Manager) DeleteCommunitySettings(id types.HexBytes) error { 4200 return m.persistence.DeleteCommunitySettings(id) 4201 } 4202 4203 func (m *Manager) UpdateCommunitySettings(settings CommunitySettings) error { 4204 return m.persistence.UpdateCommunitySettings(settings) 4205 } 4206 4207 func (m *Manager) GetOwnedCommunitiesChatIDs() (map[string]bool, error) { 4208 ownedCommunities, err := m.Controlled() 4209 if err != nil { 4210 return nil, err 4211 } 4212 4213 chatIDs := make(map[string]bool) 4214 for _, c := range ownedCommunities { 4215 if c.Joined() { 4216 for _, id := range c.ChatIDs() { 4217 chatIDs[id] = true 4218 } 4219 } 4220 } 4221 return chatIDs, nil 4222 } 4223 4224 func (m *Manager) StoreWakuMessage(message *types.Message) error { 4225 return m.persistence.SaveWakuMessage(message) 4226 } 4227 4228 func (m *Manager) StoreWakuMessages(messages []*types.Message) error { 4229 return m.persistence.SaveWakuMessages(messages) 4230 } 4231 4232 func (m *Manager) GetLatestWakuMessageTimestamp(topics []types.TopicType) (uint64, error) { 4233 return m.persistence.GetLatestWakuMessageTimestamp(topics) 4234 } 4235 4236 func (m *Manager) GetCommunityToken(communityID string, chainID int, address string) (*community_token.CommunityToken, error) { 4237 return m.persistence.GetCommunityToken(communityID, chainID, address) 4238 } 4239 4240 func (m *Manager) GetCommunityTokenByChainAndAddress(chainID int, address string) (*community_token.CommunityToken, error) { 4241 return m.persistence.GetCommunityTokenByChainAndAddress(chainID, address) 4242 } 4243 4244 func (m *Manager) GetCommunityTokens(communityID string) ([]*community_token.CommunityToken, error) { 4245 return m.persistence.GetCommunityTokens(communityID) 4246 } 4247 4248 func (m *Manager) GetAllCommunityTokens() ([]*community_token.CommunityToken, error) { 4249 return m.persistence.GetAllCommunityTokens() 4250 } 4251 4252 func (m *Manager) GetCommunityGrant(communityID string) ([]byte, uint64, error) { 4253 return m.persistence.GetCommunityGrant(communityID) 4254 } 4255 4256 func (m *Manager) ImageToBase64(uri string) string { 4257 if uri == "" { 4258 return "" 4259 } 4260 file, err := os.Open(uri) 4261 if err != nil { 4262 m.logger.Error(err.Error()) 4263 return "" 4264 } 4265 defer file.Close() 4266 4267 payload, err := ioutil.ReadAll(file) 4268 if err != nil { 4269 m.logger.Error(err.Error()) 4270 return "" 4271 } 4272 base64img, err := images.GetPayloadDataURI(payload) 4273 if err != nil { 4274 m.logger.Error(err.Error()) 4275 return "" 4276 } 4277 return base64img 4278 } 4279 4280 func (m *Manager) SaveCommunityToken(token *community_token.CommunityToken, croppedImage *images.CroppedImage) (*community_token.CommunityToken, error) { 4281 4282 _, err := m.GetByIDString(token.CommunityID) 4283 if err != nil { 4284 return nil, err 4285 } 4286 4287 if croppedImage != nil && croppedImage.ImagePath != "" { 4288 bytes, err := images.OpenAndAdjustImage(*croppedImage, true) 4289 if err != nil { 4290 return nil, err 4291 } 4292 4293 base64img, err := images.GetPayloadDataURI(bytes) 4294 if err != nil { 4295 return nil, err 4296 } 4297 token.Base64Image = base64img 4298 } else if !images.IsPayloadDataURI(token.Base64Image) { 4299 // if image is already base64 do not convert (owner and master tokens have already base64 image) 4300 token.Base64Image = m.ImageToBase64(token.Base64Image) 4301 } 4302 4303 return token, m.persistence.AddCommunityToken(token) 4304 } 4305 4306 func (m *Manager) AddCommunityToken(token *community_token.CommunityToken, clock uint64) (*Community, error) { 4307 if token == nil { 4308 return nil, errors.New("Token is absent in database") 4309 } 4310 4311 communityID, err := types.DecodeHex(token.CommunityID) 4312 if err != nil { 4313 return nil, err 4314 } 4315 4316 m.communityLock.Lock(communityID) 4317 defer m.communityLock.Unlock(communityID) 4318 4319 community, err := m.GetByID(communityID) 4320 if err != nil { 4321 return nil, err 4322 } 4323 4324 if !community.MemberCanManageToken(&m.identity.PublicKey, token) { 4325 return nil, ErrInvalidManageTokensPermission 4326 } 4327 4328 tokenMetadata := &protobuf.CommunityTokenMetadata{ 4329 ContractAddresses: map[uint64]string{uint64(token.ChainID): token.Address}, 4330 Description: token.Description, 4331 Image: token.Base64Image, 4332 Symbol: token.Symbol, 4333 TokenType: token.TokenType, 4334 Name: token.Name, 4335 Decimals: uint32(token.Decimals), 4336 Version: token.Version, 4337 } 4338 _, err = community.AddCommunityTokensMetadata(tokenMetadata) 4339 if err != nil { 4340 return nil, err 4341 } 4342 4343 if community.IsControlNode() && (token.PrivilegesLevel == community_token.MasterLevel || token.PrivilegesLevel == community_token.OwnerLevel) { 4344 permissionType := protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER 4345 if token.PrivilegesLevel == community_token.MasterLevel { 4346 permissionType = protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER 4347 } 4348 4349 contractAddresses := make(map[uint64]string) 4350 contractAddresses[uint64(token.ChainID)] = token.Address 4351 4352 tokenCriteria := &protobuf.TokenCriteria{ 4353 ContractAddresses: contractAddresses, 4354 Type: protobuf.CommunityTokenType_ERC721, 4355 Symbol: token.Symbol, 4356 Name: token.Name, 4357 Amount: "1", 4358 AmountInWei: "1", 4359 Decimals: uint64(0), 4360 } 4361 4362 request := &requests.CreateCommunityTokenPermission{ 4363 CommunityID: community.ID(), 4364 Type: permissionType, 4365 TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, 4366 IsPrivate: true, 4367 ChatIds: []string{}, 4368 } 4369 4370 community, _, err = m.createCommunityTokenPermission(request, community) 4371 if err != nil { 4372 return nil, err 4373 } 4374 4375 if token.PrivilegesLevel == community_token.OwnerLevel { 4376 _, err = m.promoteSelfToControlNode(community, clock) 4377 if err != nil { 4378 return nil, err 4379 } 4380 } 4381 } 4382 4383 return community, m.saveAndPublish(community) 4384 } 4385 4386 func (m *Manager) UpdateCommunityTokenState(chainID int, contractAddress string, deployState community_token.DeployState) error { 4387 return m.persistence.UpdateCommunityTokenState(chainID, contractAddress, deployState) 4388 } 4389 4390 func (m *Manager) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error { 4391 return m.persistence.UpdateCommunityTokenAddress(chainID, oldContractAddress, newContractAddress) 4392 } 4393 4394 func (m *Manager) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error { 4395 return m.persistence.UpdateCommunityTokenSupply(chainID, contractAddress, supply) 4396 } 4397 4398 func (m *Manager) RemoveCommunityToken(chainID int, contractAddress string) error { 4399 return m.persistence.RemoveCommunityToken(chainID, contractAddress) 4400 } 4401 4402 func (m *Manager) SetCommunityActiveMembersCount(communityID string, activeMembersCount uint64) error { 4403 id, err := types.DecodeHex(communityID) 4404 if err != nil { 4405 return err 4406 } 4407 4408 m.communityLock.Lock(id) 4409 defer m.communityLock.Unlock(id) 4410 4411 community, err := m.GetByID(id) 4412 if err != nil { 4413 return err 4414 } 4415 4416 updated, err := community.SetActiveMembersCount(activeMembersCount) 4417 if err != nil { 4418 return err 4419 } 4420 4421 if updated { 4422 if err = m.persistence.SaveCommunity(community); err != nil { 4423 return err 4424 } 4425 4426 m.publish(&Subscription{Community: community}) 4427 } 4428 4429 return nil 4430 } 4431 4432 func combineAddressesAndChainIDs(addresses []gethcommon.Address, chainIDs []uint64) []*AccountChainIDsCombination { 4433 combinations := make([]*AccountChainIDsCombination, 0) 4434 for _, address := range addresses { 4435 combinations = append(combinations, &AccountChainIDsCombination{ 4436 Address: address, 4437 ChainIDs: chainIDs, 4438 }) 4439 } 4440 return combinations 4441 } 4442 4443 func revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts []*protobuf.RevealedAccount) []*AccountChainIDsCombination { 4444 accountsAndChainIDs := make([]*AccountChainIDsCombination, 0) 4445 for _, revealedAccount := range revealedAccounts { 4446 accountsAndChainIDs = append(accountsAndChainIDs, &AccountChainIDsCombination{ 4447 Address: gethcommon.HexToAddress(revealedAccount.Address), 4448 ChainIDs: revealedAccount.ChainIds, 4449 }) 4450 } 4451 return accountsAndChainIDs 4452 } 4453 4454 func (m *Manager) accountsHasPrivilegedPermission(preParsedCommunityPermissionData *PreParsedCommunityPermissionsData, accounts []*AccountChainIDsCombination) bool { 4455 if preParsedCommunityPermissionData != nil { 4456 permissionResponse, err := m.PermissionChecker.CheckPermissions(preParsedCommunityPermissionData, accounts, true) 4457 if err != nil { 4458 m.logger.Warn("check privileged permission failed: %v", zap.Error(err)) 4459 return false 4460 } 4461 return permissionResponse.Satisfied 4462 } 4463 return false 4464 } 4465 4466 func (m *Manager) saveAndPublish(community *Community) error { 4467 err := m.persistence.SaveCommunity(community) 4468 if err != nil { 4469 return err 4470 } 4471 4472 if community.IsControlNode() { 4473 m.publish(&Subscription{Community: community}) 4474 return nil 4475 } 4476 4477 if community.HasPermissionToSendCommunityEvents() { 4478 err := m.signEvents(community) 4479 if err != nil { 4480 return err 4481 } 4482 err = m.persistence.SaveCommunityEvents(community) 4483 if err != nil { 4484 return err 4485 } 4486 4487 m.publish(&Subscription{CommunityEventsMessage: community.toCommunityEventsMessage()}) 4488 return nil 4489 } 4490 4491 return nil 4492 } 4493 4494 func (m *Manager) GetRevealedAddresses(communityID types.HexBytes, memberPk string) ([]*protobuf.RevealedAccount, error) { 4495 logger := m.logger.Named("GetRevealedAddresses") 4496 4497 requestID := CalculateRequestID(memberPk, communityID) 4498 response, err := m.persistence.GetRequestToJoinRevealedAddresses(requestID) 4499 4500 revealedAddresses := make([]string, len(response)) 4501 for i, acc := range response { 4502 revealedAddresses[i] = acc.Address 4503 } 4504 logger.Debug("Revealed addresses", zap.Any("Addresses:", revealedAddresses)) 4505 4506 return response, err 4507 } 4508 4509 func (m *Manager) handleCommunityTokensMetadata(community *Community) error { 4510 communityID := community.IDString() 4511 communityTokens := community.CommunityTokensMetadata() 4512 4513 if len(communityTokens) == 0 { 4514 return nil 4515 } 4516 for _, tokenMetadata := range communityTokens { 4517 for chainID, address := range tokenMetadata.ContractAddresses { 4518 exists, err := m.persistence.HasCommunityToken(communityID, address, int(chainID)) 4519 if err != nil { 4520 return err 4521 } 4522 if !exists { 4523 // Fetch community token to make sure it's stored in the DB, discard result 4524 communityToken, err := m.FetchCommunityToken(community, tokenMetadata, chainID, address) 4525 if err != nil { 4526 return err 4527 } 4528 4529 err = m.persistence.AddCommunityToken(communityToken) 4530 if err != nil { 4531 return err 4532 } 4533 } 4534 } 4535 } 4536 return nil 4537 } 4538 4539 func (m *Manager) HandleCommunityGrant(community *Community, grant []byte, clock uint64) (uint64, error) { 4540 _, oldClock, err := m.GetCommunityGrant(community.IDString()) 4541 if err != nil { 4542 return 0, err 4543 } 4544 4545 if oldClock >= clock { 4546 return 0, ErrGrantOlder 4547 } 4548 4549 verifiedGrant, err := community.VerifyGrantSignature(grant) 4550 if err != nil { 4551 return 0, err 4552 } 4553 4554 if !bytes.Equal(verifiedGrant.MemberId, crypto.CompressPubkey(&m.identity.PublicKey)) { 4555 return 0, ErrGrantMemberPublicKeyIsDifferent 4556 } 4557 4558 return clock - oldClock, m.persistence.SaveCommunityGrant(community.IDString(), grant, clock) 4559 } 4560 4561 func (m *Manager) FetchCommunityToken(community *Community, tokenMetadata *protobuf.CommunityTokenMetadata, chainID uint64, contractAddress string) (*community_token.CommunityToken, error) { 4562 communityID := community.IDString() 4563 4564 communityToken := &community_token.CommunityToken{ 4565 CommunityID: communityID, 4566 Address: contractAddress, 4567 TokenType: tokenMetadata.TokenType, 4568 Name: tokenMetadata.Name, 4569 Symbol: tokenMetadata.Symbol, 4570 Description: tokenMetadata.Description, 4571 Transferable: true, 4572 RemoteSelfDestruct: false, 4573 ChainID: int(chainID), 4574 DeployState: community_token.Deployed, 4575 Base64Image: tokenMetadata.Image, 4576 Decimals: int(tokenMetadata.Decimals), 4577 Version: tokenMetadata.Version, 4578 } 4579 4580 switch tokenMetadata.TokenType { 4581 case protobuf.CommunityTokenType_ERC721: 4582 contractData, err := m.communityTokensService.GetCollectibleContractData(chainID, contractAddress) 4583 if err != nil { 4584 return nil, err 4585 } 4586 4587 communityToken.Supply = contractData.TotalSupply 4588 communityToken.Transferable = contractData.Transferable 4589 communityToken.RemoteSelfDestruct = contractData.RemoteBurnable 4590 communityToken.InfiniteSupply = contractData.InfiniteSupply 4591 4592 case protobuf.CommunityTokenType_ERC20: 4593 contractData, err := m.communityTokensService.GetAssetContractData(chainID, contractAddress) 4594 if err != nil { 4595 return nil, err 4596 } 4597 4598 communityToken.Supply = contractData.TotalSupply 4599 communityToken.InfiniteSupply = contractData.InfiniteSupply 4600 } 4601 4602 communityToken.PrivilegesLevel = getPrivilegesLevel(chainID, contractAddress, community.TokenPermissions()) 4603 4604 return communityToken, nil 4605 } 4606 4607 func getPrivilegesLevel(chainID uint64, tokenAddress string, tokenPermissions map[string]*CommunityTokenPermission) community_token.PrivilegesLevel { 4608 for _, permission := range tokenPermissions { 4609 if permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER || permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER { 4610 for _, tokenCriteria := range permission.TokenCriteria { 4611 value, exist := tokenCriteria.ContractAddresses[chainID] 4612 if exist && value == tokenAddress { 4613 if permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER { 4614 return community_token.OwnerLevel 4615 } 4616 return community_token.MasterLevel 4617 } 4618 } 4619 } 4620 } 4621 return community_token.CommunityLevel 4622 } 4623 4624 func (m *Manager) ValidateCommunityPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage) error { 4625 if message == nil { 4626 return errors.New("invalid CommunityPrivilegedUserSyncMessage message") 4627 } 4628 4629 if message.CommunityId == nil || len(message.CommunityId) == 0 { 4630 return errors.New("invalid CommunityId in CommunityPrivilegedUserSyncMessage message") 4631 } 4632 4633 switch message.Type { 4634 case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN: 4635 fallthrough 4636 case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN: 4637 if message.RequestToJoin == nil || len(message.RequestToJoin) == 0 { 4638 return errors.New("invalid request to join in CommunityPrivilegedUserSyncMessage message") 4639 } 4640 4641 for _, requestToJoinProto := range message.RequestToJoin { 4642 if len(requestToJoinProto.CommunityId) == 0 { 4643 return errors.New("no communityId in request to join in CommunityPrivilegedUserSyncMessage message") 4644 } 4645 } 4646 case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN: 4647 if message.SyncRequestsToJoin == nil || len(message.SyncRequestsToJoin) == 0 { 4648 return errors.New("invalid sync requests to join in CommunityPrivilegedUserSyncMessage message") 4649 } 4650 case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES: 4651 if message.SyncEditSharedAddresses == nil || len(message.CommunityId) == 0 || 4652 len(message.SyncEditSharedAddresses.PublicKey) == 0 || message.SyncEditSharedAddresses.EditSharedAddress == nil { 4653 return errors.New("invalid edit shared adresses in CommunityPrivilegedUserSyncMessage message") 4654 } 4655 } 4656 4657 return nil 4658 } 4659 4660 func (m *Manager) createCommunityTokenPermission(request *requests.CreateCommunityTokenPermission, community *Community) (*Community, *CommunityChanges, error) { 4661 if community == nil { 4662 return nil, nil, ErrOrgNotFound 4663 } 4664 4665 tokenPermission := request.ToCommunityTokenPermission() 4666 tokenPermission.Id = uuid.New().String() 4667 changes, err := community.UpsertTokenPermission(&tokenPermission) 4668 if err != nil { 4669 return nil, nil, err 4670 } 4671 4672 return community, changes, nil 4673 4674 } 4675 4676 func (m *Manager) RemoveUsersWithoutRevealedAccounts(community *Community, clock uint64) (*CommunityChanges, error) { 4677 membersAccounts, err := m.persistence.GetCommunityRequestsToJoinRevealedAddresses(community.ID()) 4678 if err != nil { 4679 return nil, err 4680 } 4681 4682 myPk := common.PubkeyToHex(&m.identity.PublicKey) 4683 membersToRemove := []string{} 4684 for pk := range community.Members() { 4685 if myPk == pk { 4686 continue 4687 } 4688 if _, exists := membersAccounts[pk]; !exists { 4689 membersToRemove = append(membersToRemove, pk) 4690 } 4691 } 4692 4693 if len(membersToRemove) > 0 { 4694 community.SetResendAccountsClock(clock) 4695 } 4696 4697 return community.RemoveMembersFromOrg(membersToRemove), nil 4698 } 4699 4700 func (m *Manager) PromoteSelfToControlNode(community *Community, clock uint64) (*CommunityChanges, error) { 4701 if community == nil { 4702 return nil, ErrOrgNotFound 4703 } 4704 4705 m.communityLock.Lock(community.ID()) 4706 defer m.communityLock.Unlock(community.ID()) 4707 4708 ownerChanged, err := m.promoteSelfToControlNode(community, clock) 4709 if err != nil { 4710 return nil, err 4711 } 4712 4713 if ownerChanged { 4714 return community.RemoveAllUsersFromOrg(), m.saveAndPublish(community) 4715 } 4716 4717 // if control node device was changed, check that we own all members revealed accounts 4718 // members without revealed accounts will be soft kicked 4719 changes, err := m.RemoveUsersWithoutRevealedAccounts(community, clock) 4720 if err != nil { 4721 return nil, err 4722 } 4723 4724 return changes, m.saveAndPublish(community) 4725 } 4726 4727 func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) (bool, error) { 4728 ownerChanged := false 4729 community.setPrivateKey(m.identity) 4730 if !community.ControlNode().Equal(&m.identity.PublicKey) { 4731 ownerChanged = true 4732 community.setControlNode(&m.identity.PublicKey) 4733 } 4734 4735 // Mark this device as the control node 4736 syncControlNode := &protobuf.SyncCommunityControlNode{ 4737 Clock: clock, 4738 InstallationId: m.installationID, 4739 } 4740 4741 err := m.SaveSyncControlNode(community.ID(), syncControlNode) 4742 if err != nil { 4743 return false, err 4744 } 4745 community.config.ControlDevice = true 4746 4747 if exists := community.HasMember(&m.identity.PublicKey); !exists { 4748 ownerRole := []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_OWNER} 4749 _, err = community.AddMember(&m.identity.PublicKey, ownerRole, community.Clock()) 4750 if err != nil { 4751 return false, err 4752 } 4753 4754 for channelID := range community.Chats() { 4755 _, err = community.AddMemberToChat(channelID, &m.identity.PublicKey, ownerRole, protobuf.CommunityMember_CHANNEL_ROLE_POSTER) 4756 if err != nil { 4757 return false, err 4758 } 4759 } 4760 } else { 4761 _, err = community.AddRoleToMember(&m.identity.PublicKey, protobuf.CommunityMember_ROLE_OWNER) 4762 } 4763 4764 if err != nil { 4765 return false, err 4766 } 4767 4768 err = m.handleCommunityEvents(community) 4769 if err != nil { 4770 return false, err 4771 } 4772 4773 community.increaseClock() 4774 4775 return ownerChanged, nil 4776 } 4777 4778 func (m *Manager) handleCommunityEventsAndMetadata(community *Community, eventsMessage *CommunityEventsMessage, 4779 lastlyAppliedEvents map[string]uint64) (*CommunityResponse, error) { 4780 err := community.processEvents(eventsMessage, lastlyAppliedEvents) 4781 if err != nil { 4782 return nil, err 4783 } 4784 4785 additionalCommunityResponse, err := m.handleAdditionalAdminChanges(community) 4786 if err != nil { 4787 return nil, err 4788 } 4789 4790 if err = m.handleCommunityTokensMetadata(community); err != nil { 4791 return nil, err 4792 } 4793 4794 return additionalCommunityResponse, err 4795 } 4796 4797 func (m *Manager) handleCommunityEvents(community *Community) error { 4798 if community.config.EventsData == nil { 4799 return nil 4800 } 4801 4802 lastlyAppliedEvents, err := m.persistence.GetAppliedCommunityEvents(community.ID()) 4803 if err != nil { 4804 return err 4805 } 4806 4807 _, err = m.handleCommunityEventsAndMetadata(community, community.toCommunityEventsMessage(), lastlyAppliedEvents) 4808 if err != nil { 4809 return err 4810 } 4811 4812 appliedEvents := map[string]uint64{} 4813 if community.config.EventsData != nil { 4814 for _, event := range community.config.EventsData.Events { 4815 appliedEvents[event.EventTypeID()] = event.CommunityEventClock 4816 } 4817 } 4818 4819 community.config.EventsData = nil // clear events, they are already applied 4820 community.increaseClock() 4821 4822 err = m.persistence.SaveCommunity(community) 4823 if err != nil { 4824 return err 4825 } 4826 4827 err = m.persistence.UpsertAppliedCommunityEvents(community.ID(), appliedEvents) 4828 if err != nil { 4829 return err 4830 } 4831 4832 m.publish(&Subscription{Community: community}) 4833 4834 return nil 4835 } 4836 4837 func (m *Manager) ShareRequestsToJoinWithPrivilegedMembers(community *Community, privilegedMembers map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) error { 4838 if len(privilegedMembers) == 0 { 4839 return nil 4840 } 4841 4842 requestsToJoin, err := m.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) 4843 if err != nil { 4844 return err 4845 } 4846 4847 var syncRequestsWithoutRevealedAccounts []*protobuf.SyncCommunityRequestsToJoin 4848 var syncRequestsWithRevealedAccounts []*protobuf.SyncCommunityRequestsToJoin 4849 for _, request := range requestsToJoin { 4850 // if shared request to join is not approved by control node - do not send revealed accounts. 4851 // revealed accounts will be sent as soon as control node accepts request to join 4852 if request.State != RequestToJoinStateAccepted { 4853 request.RevealedAccounts = []*protobuf.RevealedAccount{} 4854 } 4855 syncRequestsWithRevealedAccounts = append(syncRequestsWithRevealedAccounts, request.ToSyncProtobuf()) 4856 requestProtoWithoutAccounts := request.ToSyncProtobuf() 4857 requestProtoWithoutAccounts.RevealedAccounts = []*protobuf.RevealedAccount{} 4858 syncRequestsWithoutRevealedAccounts = append(syncRequestsWithoutRevealedAccounts, requestProtoWithoutAccounts) 4859 } 4860 4861 syncMsgWithoutRevealedAccounts := &protobuf.CommunityPrivilegedUserSyncMessage{ 4862 Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN, 4863 CommunityId: community.ID(), 4864 SyncRequestsToJoin: syncRequestsWithoutRevealedAccounts, 4865 } 4866 4867 syncMsgWitRevealedAccounts := &protobuf.CommunityPrivilegedUserSyncMessage{ 4868 Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN, 4869 CommunityId: community.ID(), 4870 SyncRequestsToJoin: syncRequestsWithRevealedAccounts, 4871 } 4872 4873 subscriptionMsg := &CommunityPrivilegedMemberSyncMessage{} 4874 4875 for role, members := range privilegedMembers { 4876 if len(members) == 0 { 4877 continue 4878 } 4879 4880 subscriptionMsg.Receivers = members 4881 4882 switch role { 4883 case protobuf.CommunityMember_ROLE_ADMIN: 4884 subscriptionMsg.CommunityPrivilegedUserSyncMessage = syncMsgWithoutRevealedAccounts 4885 case protobuf.CommunityMember_ROLE_OWNER: 4886 continue 4887 case protobuf.CommunityMember_ROLE_TOKEN_MASTER: 4888 subscriptionMsg.CommunityPrivilegedUserSyncMessage = syncMsgWitRevealedAccounts 4889 } 4890 4891 m.publish(&Subscription{CommunityPrivilegedMemberSyncMessage: subscriptionMsg}) 4892 } 4893 4894 return nil 4895 } 4896 4897 func (m *Manager) shareAcceptedRequestToJoinWithPrivilegedMembers(community *Community, requestsToJoin *RequestToJoin) error { 4898 pk, err := common.HexToPubkey(requestsToJoin.PublicKey) 4899 if err != nil { 4900 return err 4901 } 4902 4903 acceptedRequestsToJoinWithoutRevealedAccounts := make(map[string]*protobuf.CommunityRequestToJoin) 4904 acceptedRequestsToJoinWithRevealedAccounts := make(map[string]*protobuf.CommunityRequestToJoin) 4905 4906 acceptedRequestsToJoinWithRevealedAccounts[requestsToJoin.PublicKey] = requestsToJoin.ToCommunityRequestToJoinProtobuf() 4907 requestsToJoin.RevealedAccounts = make([]*protobuf.RevealedAccount, 0) 4908 acceptedRequestsToJoinWithoutRevealedAccounts[requestsToJoin.PublicKey] = requestsToJoin.ToCommunityRequestToJoinProtobuf() 4909 4910 msgWithRevealedAccounts := &protobuf.CommunityPrivilegedUserSyncMessage{ 4911 Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN, 4912 CommunityId: community.ID(), 4913 RequestToJoin: acceptedRequestsToJoinWithRevealedAccounts, 4914 } 4915 4916 msgWithoutRevealedAccounts := &protobuf.CommunityPrivilegedUserSyncMessage{ 4917 Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN, 4918 CommunityId: community.ID(), 4919 RequestToJoin: acceptedRequestsToJoinWithoutRevealedAccounts, 4920 } 4921 4922 // do not sent to ourself and to the accepted user 4923 skipMembers := make(map[string]struct{}) 4924 skipMembers[common.PubkeyToHex(&m.identity.PublicKey)] = struct{}{} 4925 skipMembers[common.PubkeyToHex(pk)] = struct{}{} 4926 4927 subscriptionMsg := &CommunityPrivilegedMemberSyncMessage{} 4928 4929 fileredPrivilegedMembers := community.GetFilteredPrivilegedMembers(skipMembers) 4930 for role, members := range fileredPrivilegedMembers { 4931 if len(members) == 0 { 4932 continue 4933 } 4934 4935 subscriptionMsg.Receivers = members 4936 4937 switch role { 4938 case protobuf.CommunityMember_ROLE_ADMIN: 4939 subscriptionMsg.CommunityPrivilegedUserSyncMessage = msgWithoutRevealedAccounts 4940 case protobuf.CommunityMember_ROLE_OWNER: 4941 fallthrough 4942 case protobuf.CommunityMember_ROLE_TOKEN_MASTER: 4943 subscriptionMsg.CommunityPrivilegedUserSyncMessage = msgWithRevealedAccounts 4944 } 4945 4946 m.publish(&Subscription{CommunityPrivilegedMemberSyncMessage: subscriptionMsg}) 4947 } 4948 4949 return nil 4950 } 4951 4952 func (m *Manager) GetCommunityRequestsToJoinWithRevealedAddresses(communityID types.HexBytes) ([]*RequestToJoin, error) { 4953 return m.persistence.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 4954 } 4955 4956 func (m *Manager) SaveCommunity(community *Community) error { 4957 return m.persistence.SaveCommunity(community) 4958 } 4959 4960 func (m *Manager) CreateCommunityTokenDeploymentSignature(ctx context.Context, chainID uint64, addressFrom string, communityID string) ([]byte, error) { 4961 community, err := m.GetByIDString(communityID) 4962 if err != nil { 4963 return nil, err 4964 } 4965 if !community.IsControlNode() { 4966 return nil, ErrNotControlNode 4967 } 4968 digest, err := m.communityTokensService.DeploymentSignatureDigest(chainID, addressFrom, communityID) 4969 if err != nil { 4970 return nil, err 4971 } 4972 return crypto.Sign(digest, community.PrivateKey()) 4973 } 4974 4975 func (m *Manager) GetSyncControlNode(id types.HexBytes) (*protobuf.SyncCommunityControlNode, error) { 4976 return m.persistence.GetSyncControlNode(id) 4977 } 4978 4979 func (m *Manager) SaveSyncControlNode(id types.HexBytes, syncControlNode *protobuf.SyncCommunityControlNode) error { 4980 return m.persistence.SaveSyncControlNode(id, syncControlNode.Clock, syncControlNode.InstallationId) 4981 } 4982 4983 func (m *Manager) SetSyncControlNode(id types.HexBytes, syncControlNode *protobuf.SyncCommunityControlNode) error { 4984 existingSyncControlNode, err := m.GetSyncControlNode(id) 4985 if err != nil { 4986 return err 4987 } 4988 4989 if existingSyncControlNode == nil || existingSyncControlNode.Clock < syncControlNode.Clock { 4990 return m.SaveSyncControlNode(id, syncControlNode) 4991 } 4992 4993 return nil 4994 } 4995 4996 func (m *Manager) GetCommunityRequestToJoinWithRevealedAddresses(pubKey string, communityID types.HexBytes) (*RequestToJoin, error) { 4997 return m.persistence.GetCommunityRequestToJoinWithRevealedAddresses(pubKey, communityID) 4998 } 4999 5000 func (m *Manager) SafeGetSignerPubKey(chainID uint64, communityID string) (string, error) { 5001 ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 5002 defer cancel() 5003 5004 return m.ownerVerifier.SafeGetSignerPubKey(ctx, chainID, communityID) 5005 } 5006 5007 func (m *Manager) GetCuratedCommunities() (*CuratedCommunities, error) { 5008 return m.persistence.GetCuratedCommunities() 5009 } 5010 5011 func (m *Manager) SetCuratedCommunities(communities *CuratedCommunities) error { 5012 return m.persistence.SetCuratedCommunities(communities) 5013 } 5014 5015 func (m *Manager) encryptCommunityDescriptionImpl(groupID []byte, d *protobuf.CommunityDescription) (string, []byte, error) { 5016 payload, err := proto.Marshal(d) 5017 if err != nil { 5018 return "", nil, err 5019 } 5020 5021 encryptedPayload, ratchet, newSeqNo, err := m.encryptor.EncryptWithHashRatchet(groupID, payload) 5022 if err == encryption.ErrNoEncryptionKey { 5023 _, err := m.encryptor.GenerateHashRatchetKey(groupID) 5024 if err != nil { 5025 return "", nil, err 5026 } 5027 encryptedPayload, ratchet, newSeqNo, err = m.encryptor.EncryptWithHashRatchet(groupID, payload) 5028 if err != nil { 5029 return "", nil, err 5030 } 5031 5032 } else if err != nil { 5033 return "", nil, err 5034 } 5035 5036 keyID, err := ratchet.GetKeyID() 5037 if err != nil { 5038 return "", nil, err 5039 } 5040 5041 m.logger.Debug("encrypting community description", 5042 zap.Any("community", d), 5043 zap.String("groupID", types.Bytes2Hex(groupID)), 5044 zap.String("keyID", types.Bytes2Hex(keyID))) 5045 5046 keyIDSeqNo := fmt.Sprintf("%s%d", hex.EncodeToString(keyID), newSeqNo) 5047 5048 return keyIDSeqNo, encryptedPayload, nil 5049 } 5050 5051 func (m *Manager) encryptCommunityDescription(community *Community, d *protobuf.CommunityDescription) (string, []byte, error) { 5052 return m.encryptCommunityDescriptionImpl(community.ID(), d) 5053 } 5054 5055 func (m *Manager) encryptCommunityDescriptionChannel(community *Community, channelID string, d *protobuf.CommunityDescription) (string, []byte, error) { 5056 return m.encryptCommunityDescriptionImpl([]byte(community.IDString()+channelID), d) 5057 } 5058 5059 // TODO: add collectiblesManager to messenger intance 5060 func (m *Manager) GetCollectiblesManager() CollectiblesManager { 5061 return m.collectiblesManager 5062 } 5063 5064 type DecryptCommunityResponse struct { 5065 Decrypted bool 5066 Description *protobuf.CommunityDescription 5067 KeyID []byte 5068 GroupID []byte 5069 } 5070 5071 func (m *Manager) decryptCommunityDescription(keyIDSeqNo string, d []byte) (*DecryptCommunityResponse, error) { 5072 const hashHexLength = 64 5073 if len(keyIDSeqNo) <= hashHexLength { 5074 return nil, errors.New("invalid keyIDSeqNo") 5075 } 5076 5077 keyID, err := hex.DecodeString(keyIDSeqNo[:hashHexLength]) 5078 if err != nil { 5079 return nil, err 5080 } 5081 5082 seqNo, err := strconv.ParseUint(keyIDSeqNo[hashHexLength:], 10, 32) 5083 if err != nil { 5084 return nil, err 5085 } 5086 5087 decryptedPayload, err := m.encryptor.DecryptWithHashRatchet(keyID, uint32(seqNo), d) 5088 if err == encryption.ErrNoRatchetKey { 5089 return &DecryptCommunityResponse{ 5090 KeyID: keyID, 5091 }, err 5092 5093 } 5094 if err != nil { 5095 return nil, err 5096 } 5097 5098 var description protobuf.CommunityDescription 5099 err = proto.Unmarshal(decryptedPayload, &description) 5100 if err != nil { 5101 return nil, err 5102 } 5103 5104 decryptCommunityResponse := &DecryptCommunityResponse{ 5105 Decrypted: true, 5106 KeyID: keyID, 5107 Description: &description, 5108 } 5109 return decryptCommunityResponse, nil 5110 } 5111 5112 // GetPersistence returns the instantiated *Persistence used by the Manager 5113 func (m *Manager) GetPersistence() *Persistence { 5114 return m.persistence 5115 } 5116 5117 func ToLinkPreveiwThumbnail(image images.IdentityImage) (*common.LinkPreviewThumbnail, error) { 5118 thumbnail := &common.LinkPreviewThumbnail{} 5119 5120 if image.IsEmpty() { 5121 return nil, nil 5122 } 5123 5124 width, height, err := images.GetImageDimensions(image.Payload) 5125 if err != nil { 5126 return nil, fmt.Errorf("failed to get image dimensions: %w", err) 5127 } 5128 5129 dataURI, err := image.GetDataURI() 5130 if err != nil { 5131 return nil, fmt.Errorf("failed to get data uri: %w", err) 5132 } 5133 5134 thumbnail.Width = width 5135 thumbnail.Height = height 5136 thumbnail.DataURI = dataURI 5137 return thumbnail, nil 5138 } 5139 5140 func (c *Community) ToStatusLinkPreview() (*common.StatusCommunityLinkPreview, error) { 5141 communityLinkPreview := &common.StatusCommunityLinkPreview{} 5142 if image, ok := c.Images()[images.SmallDimName]; ok { 5143 thumbnail, err := ToLinkPreveiwThumbnail(images.IdentityImage{Payload: image.Payload}) 5144 if err != nil { 5145 c.config.Logger.Warn("unfurling status link: failed to set community thumbnail", zap.Error(err)) 5146 } 5147 communityLinkPreview.Icon = *thumbnail 5148 } 5149 5150 if image, ok := c.Images()[images.BannerIdentityName]; ok { 5151 thumbnail, err := ToLinkPreveiwThumbnail(images.IdentityImage{Payload: image.Payload}) 5152 if err != nil { 5153 c.config.Logger.Warn("unfurling status link: failed to set community thumbnail", zap.Error(err)) 5154 } 5155 communityLinkPreview.Banner = *thumbnail 5156 } 5157 5158 communityLinkPreview.CommunityID = c.IDString() 5159 communityLinkPreview.DisplayName = c.Name() 5160 communityLinkPreview.Description = c.DescriptionText() 5161 communityLinkPreview.MembersCount = uint32(c.MembersCount()) 5162 communityLinkPreview.Color = c.Color() 5163 5164 return communityLinkPreview, nil 5165 } 5166 5167 func (m *Manager) determineChannelsForHRKeysRequest(c *Community, now int64) ([]string, error) { 5168 result := []string{} 5169 5170 channelsWithMissingKeys := func() map[string]struct{} { 5171 r := map[string]struct{}{} 5172 for id := range c.Chats() { 5173 if c.HasMissingEncryptionKey(id) { 5174 r[id] = struct{}{} 5175 } 5176 } 5177 return r 5178 }() 5179 5180 if len(channelsWithMissingKeys) == 0 { 5181 return result, nil 5182 } 5183 5184 requests, err := m.persistence.GetEncryptionKeyRequests(c.ID(), channelsWithMissingKeys) 5185 if err != nil { 5186 return nil, err 5187 } 5188 5189 for channelID := range channelsWithMissingKeys { 5190 request, ok := requests[channelID] 5191 if !ok { 5192 // If there's no prior request, ask for encryption key now 5193 result = append(result, channelID) 5194 continue 5195 } 5196 5197 // Exponential backoff formula: initial delay * 2^(requestCount - 1) 5198 initialDelay := int64(10 * 60 * 1000) // 10 minutes in milliseconds 5199 backoffDuration := initialDelay * (1 << (request.requestedCount - 1)) 5200 nextRequestTime := request.requestedAt + backoffDuration 5201 5202 if now >= nextRequestTime { 5203 result = append(result, channelID) 5204 } 5205 } 5206 5207 return result, nil 5208 } 5209 5210 type CommunityWithChannelIDs struct { 5211 Community *Community 5212 ChannelIDs []string 5213 } 5214 5215 // DetermineChannelsForHRKeysRequest identifies channels in a community that 5216 // should ask for encryption keys based on their current state and past request records, 5217 // as determined by exponential backoff. 5218 func (m *Manager) DetermineChannelsForHRKeysRequest() ([]*CommunityWithChannelIDs, error) { 5219 communities, err := m.Joined() 5220 if err != nil { 5221 return nil, err 5222 } 5223 5224 result := []*CommunityWithChannelIDs{} 5225 now := time.Now().UnixMilli() 5226 5227 for _, c := range communities { 5228 if c.IsControlNode() { 5229 continue 5230 } 5231 5232 channelsToRequest, err := m.determineChannelsForHRKeysRequest(c, now) 5233 if err != nil { 5234 return nil, err 5235 } 5236 5237 if len(channelsToRequest) > 0 { 5238 result = append(result, &CommunityWithChannelIDs{ 5239 Community: c, 5240 ChannelIDs: channelsToRequest, 5241 }) 5242 } 5243 } 5244 5245 return result, nil 5246 } 5247 5248 func (m *Manager) updateEncryptionKeysRequests(communityID types.HexBytes, channelIDs []string, now int64) error { 5249 return m.persistence.UpdateAndPruneEncryptionKeyRequests(communityID, channelIDs, now) 5250 } 5251 5252 func (m *Manager) UpdateEncryptionKeysRequests(communityID types.HexBytes, channelIDs []string) error { 5253 return m.updateEncryptionKeysRequests(communityID, channelIDs, time.Now().UnixMilli()) 5254 }