github.com/status-im/status-go@v1.1.0/protocol/communities/persistence_test.go (about) 1 package communities 2 3 import ( 4 "crypto/ecdsa" 5 "database/sql" 6 "math/big" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/golang/protobuf/proto" 12 "github.com/stretchr/testify/suite" 13 14 "github.com/status-im/status-go/appdatabase" 15 "github.com/status-im/status-go/eth-node/crypto" 16 "github.com/status-im/status-go/eth-node/types" 17 "github.com/status-im/status-go/protocol/common" 18 "github.com/status-im/status-go/protocol/common/shard" 19 "github.com/status-im/status-go/protocol/communities/token" 20 "github.com/status-im/status-go/protocol/encryption" 21 "github.com/status-im/status-go/protocol/protobuf" 22 "github.com/status-im/status-go/protocol/sqlite" 23 "github.com/status-im/status-go/services/wallet/bigint" 24 "github.com/status-im/status-go/t/helpers" 25 ) 26 27 func TestPersistenceSuite(t *testing.T) { 28 suite.Run(t, new(PersistenceSuite)) 29 } 30 31 type PersistenceSuite struct { 32 suite.Suite 33 34 db *Persistence 35 identity *ecdsa.PrivateKey 36 } 37 38 func (s *PersistenceSuite) SetupTest() { 39 s.db = nil 40 41 db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{}) 42 s.Require().NoError(err, "creating sqlite db instance") 43 44 err = sqlite.Migrate(db) 45 s.Require().NoError(err, "protocol migrate") 46 47 s.identity, err = crypto.GenerateKey() 48 s.Require().NoError(err) 49 50 s.db = &Persistence{db: db, recordBundleToCommunity: func(r *CommunityRecordBundle) (*Community, error) { 51 return recordBundleToCommunity(r, s.identity, "", nil, &TimeSourceStub{}, &DescriptionEncryptorMock{}, nil, nil) 52 }} 53 } 54 55 func (s *PersistenceSuite) TestSaveCommunity() { 56 communities, err := s.db.AllCommunities(&s.identity.PublicKey) 57 s.Require().NoError(err) 58 s.Require().Len(communities, 0) 59 60 community := Community{ 61 config: &Config{ 62 PrivateKey: s.identity, 63 ControlNode: &s.identity.PublicKey, 64 ControlDevice: true, 65 ID: &s.identity.PublicKey, 66 Joined: true, 67 Spectated: true, 68 Verified: true, 69 Muted: true, 70 MuteTill: time.Time{}, 71 CommunityDescription: &protobuf.CommunityDescription{}, 72 }, 73 } 74 s.Require().NoError(s.db.SaveCommunity(&community)) 75 76 communities, err = s.db.AllCommunities(&s.identity.PublicKey) 77 s.Require().NoError(err) 78 s.Require().Len(communities, 1) 79 s.Equal(types.HexBytes(crypto.CompressPubkey(&s.identity.PublicKey)), communities[0].ID()) 80 s.Equal(true, communities[0].Joined()) 81 s.Equal(true, communities[0].Spectated()) 82 s.Equal(true, communities[0].Verified()) 83 s.Equal(true, communities[0].Muted()) 84 s.Equal(time.Time{}, communities[0].MuteTill()) 85 } 86 87 func (s *PersistenceSuite) TestShouldHandleSyncCommunity() { 88 sc := &protobuf.SyncInstallationCommunity{ 89 Id: []byte("0x123456"), 90 Description: []byte("this is a description"), 91 Joined: true, 92 Verified: true, 93 Clock: uint64(time.Now().Unix()), 94 } 95 96 // check an empty db to see if a community should be synced 97 should, err := s.db.ShouldHandleSyncCommunity(sc) 98 s.Require().NoError(err, "SaveSyncCommunity") 99 s.True(should) 100 101 // add a new community to the db 102 err = s.db.saveRawCommunityRow(fromSyncCommunityProtobuf(sc)) 103 s.Require().NoError(err, "saveRawCommunityRow") 104 105 rcrs, err := s.db.getAllCommunitiesRaw() 106 s.Require().NoError(err, "should have no error from getAllCommunitiesRaw") 107 s.Len(rcrs, 1, "length of all communities raw should be 1") 108 109 // check again to see is the community should be synced 110 sc.Clock-- 111 should, err = s.db.ShouldHandleSyncCommunity(sc) 112 s.Require().NoError(err, "SaveSyncCommunity") 113 s.False(should) 114 115 // check again to see is the community should be synced 116 sc.Clock++ 117 sc.Clock++ 118 should, err = s.db.ShouldHandleSyncCommunity(sc) 119 s.Require().NoError(err, "SaveSyncCommunity") 120 s.True(should) 121 } 122 123 func (s *PersistenceSuite) TestSetSyncClock() { 124 sc := &protobuf.SyncInstallationCommunity{ 125 Id: []byte("0x123456"), 126 Description: []byte("this is a description"), 127 Joined: true, 128 Verified: true, 129 } 130 131 // add a new community to the db 132 err := s.db.saveRawCommunityRow(fromSyncCommunityProtobuf(sc)) 133 s.Require().NoError(err, "saveRawCommunityRow") 134 135 // retrieve row from db synced_at must be zero 136 rcr, err := s.db.getRawCommunityRow(sc.Id) 137 s.Require().NoError(err, "getRawCommunityRow") 138 s.Require().Zero(rcr.SyncedAt, "synced_at must be zero value") 139 140 // Set the synced_at value 141 clock := uint64(time.Now().Unix()) 142 err = s.db.SetSyncClock(sc.Id, clock) 143 s.Require().NoError(err, "SetSyncClock") 144 145 // Retrieve row from db and check clock matches synced_at value 146 rcr, err = s.db.getRawCommunityRow(sc.Id) 147 s.Require().NoError(err, "getRawCommunityRow") 148 s.Require().Equal(clock, rcr.SyncedAt, "synced_at must equal the value of the clock") 149 150 // Set Synced At with an older clock value 151 olderClock := clock - uint64(256) 152 err = s.db.SetSyncClock(sc.Id, olderClock) 153 s.Require().NoError(err, "SetSyncClock") 154 155 // Retrieve row from db and check olderClock matches synced_at value 156 rcr, err = s.db.getRawCommunityRow(sc.Id) 157 s.Require().NoError(err, "getRawCommunityRow") 158 s.Require().NotEqual(olderClock, rcr.SyncedAt, "synced_at must not equal the value of the olderClock value") 159 160 // Set Synced At with a newer clock value 161 newerClock := clock + uint64(512) 162 err = s.db.SetSyncClock(sc.Id, newerClock) 163 s.Require().NoError(err, "SetSyncClock") 164 165 // Retrieve row from db and check olderClock matches synced_at value 166 rcr, err = s.db.getRawCommunityRow(sc.Id) 167 s.Require().NoError(err, "getRawCommunityRow") 168 s.Equal(newerClock, rcr.SyncedAt, "synced_at must equal the value of the newerClock value") 169 } 170 171 func (s *PersistenceSuite) TestSetPrivateKey() { 172 sc := &protobuf.SyncInstallationCommunity{ 173 Id: []byte("0x123456"), 174 Description: []byte("this is a description"), 175 Joined: true, 176 Verified: true, 177 } 178 179 // add a new community to the db with no private key 180 err := s.db.saveRawCommunityRow(fromSyncCommunityProtobuf(sc)) 181 s.Require().NoError(err, "saveRawCommunityRow") 182 183 // retrieve row from db, private key must be zero 184 rcr, err := s.db.getRawCommunityRow(sc.Id) 185 s.Require().NoError(err, "getRawCommunityRow") 186 s.Zero(rcr.PrivateKey, "private key must be zero value") 187 188 // Set private key 189 err = s.db.SetPrivateKey(sc.Id, s.identity) 190 s.Require().NoError(err, "SetPrivateKey") 191 192 // retrieve row from db again, private key must match the given key 193 rcr, err = s.db.getRawCommunityRow(sc.Id) 194 s.Require().NoError(err, "getRawCommunityRow") 195 s.Equal(crypto.FromECDSA(s.identity), rcr.PrivateKey, "private key must match given key") 196 } 197 198 func (s *PersistenceSuite) TestJoinedAndPendingCommunitiesWithRequests() { 199 clock := uint64(time.Now().Unix()) 200 201 // Add a new community that we have joined 202 com := s.makeNewCommunity(s.identity) 203 com.Join() 204 sc, err := com.ToSyncInstallationCommunityProtobuf(clock, nil, nil) 205 s.Require().NoError(err, "Community.ToSyncInstallationCommunityProtobuf shouldn't give any error") 206 err = s.db.saveRawCommunityRow(fromSyncCommunityProtobuf(sc)) 207 s.Require().NoError(err, "saveRawCommunityRow") 208 209 // Add a new community that we have requested to join, but not yet joined 210 com2 := s.makeNewCommunity(s.identity) 211 err = s.db.SaveCommunity(com2) 212 s.Require().NoError(err, "SaveCommunity shouldn't give any error") 213 214 rtj := &RequestToJoin{ 215 ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}, 216 PublicKey: common.PubkeyToHex(&s.identity.PublicKey), 217 Clock: clock, 218 CommunityID: com2.ID(), 219 State: RequestToJoinStatePending, 220 } 221 err = s.db.SaveRequestToJoin(rtj) 222 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 223 224 comms, err := s.db.JoinedAndPendingCommunitiesWithRequests(&s.identity.PublicKey) 225 s.Require().NoError(err, "JoinedAndPendingCommunitiesWithRequests shouldn't give any error") 226 s.Len(comms, 2, "Should have 2 communities") 227 228 for _, comm := range comms { 229 switch comm.IDString() { 230 case com.IDString(): 231 s.Len(comm.RequestsToJoin(), 0, "Should have no RequestsToJoin") 232 case com2.IDString(): 233 rtjs := comm.RequestsToJoin() 234 s.Len(rtjs, 1, "Should have one RequestsToJoin") 235 s.Equal(rtjs[0], rtj, "RequestToJoin should match the Request stored in the db") 236 } 237 } 238 } 239 240 func (s *PersistenceSuite) TestSaveRequestToLeave() { 241 rtl := &RequestToLeave{ 242 ID: []byte("0x123456"), 243 PublicKey: "0xffffff", 244 Clock: 2, 245 CommunityID: []byte("0x654321"), 246 } 247 248 err := s.db.SaveRequestToLeave(rtl) 249 s.Require().NoError(err) 250 251 // older clocks should not be saved 252 rtl.Clock = 1 253 err = s.db.SaveRequestToLeave(rtl) 254 s.Error(err) 255 } 256 257 func (s *PersistenceSuite) makeNewCommunity(identity *ecdsa.PrivateKey) *Community { 258 comPrivKey, err := crypto.GenerateKey() 259 s.Require().NoError(err, "crypto.GenerateKey shouldn't give any error") 260 261 com, err := New(Config{ 262 MemberIdentity: identity, 263 PrivateKey: comPrivKey, 264 ControlNode: &comPrivKey.PublicKey, 265 ControlDevice: true, 266 ID: &comPrivKey.PublicKey, 267 }, &TimeSourceStub{}, &DescriptionEncryptorMock{}, nil) 268 s.NoError(err, "New shouldn't give any error") 269 270 md, err := com.MarshaledDescription() 271 s.Require().NoError(err, "Community.MarshaledDescription shouldn't give any error") 272 com.config.CommunityDescriptionProtocolMessage = md 273 274 return com 275 } 276 277 func (s *PersistenceSuite) TestGetSyncedRawCommunity() { 278 sc := &protobuf.SyncInstallationCommunity{ 279 Id: []byte("0x123456"), 280 Description: []byte("this is a description"), 281 Joined: true, 282 Verified: true, 283 Spectated: true, 284 } 285 286 // add a new community to the db 287 err := s.db.saveRawCommunityRowWithoutSyncedAt(fromSyncCommunityProtobuf(sc)) 288 s.Require().NoError(err, "saveRawCommunityRow") 289 290 // retrieve row from db synced_at must be zero 291 rcr, err := s.db.getRawCommunityRow(sc.Id) 292 s.Require().NoError(err, "getRawCommunityRow") 293 s.Zero(rcr.SyncedAt, "synced_at must be zero value") 294 295 // retrieve synced row from db, should fail 296 src, err := s.db.getSyncedRawCommunity(sc.Id) 297 s.EqualError(err, sql.ErrNoRows.Error()) 298 s.Nil(src) 299 300 // Set the synced_at value 301 clock := uint64(time.Now().Unix()) 302 err = s.db.SetSyncClock(sc.Id, clock) 303 s.Require().NoError(err, "SetSyncClock") 304 305 // retrieve row from db synced_at must not be zero 306 rcr, err = s.db.getRawCommunityRow(sc.Id) 307 s.Require().NoError(err, "getRawCommunityRow") 308 s.NotZero(rcr.SyncedAt, "synced_at must be zero value") 309 310 // retrieve synced row from db, should succeed 311 src, err = s.db.getSyncedRawCommunity(sc.Id) 312 s.Require().NoError(err) 313 s.NotNil(src) 314 s.Equal(clock, src.SyncedAt) 315 } 316 317 func (s *PersistenceSuite) TestGetCommunitiesSettings() { 318 settings := []CommunitySettings{ 319 {CommunityID: "0x01", HistoryArchiveSupportEnabled: false}, 320 {CommunityID: "0x02", HistoryArchiveSupportEnabled: true}, 321 {CommunityID: "0x03", HistoryArchiveSupportEnabled: false}, 322 } 323 324 for i := range settings { 325 stg := settings[i] 326 err := s.db.SaveCommunitySettings(stg) 327 s.Require().NoError(err) 328 } 329 330 rst, err := s.db.GetCommunitiesSettings() 331 s.Require().NoError(err) 332 s.Equal(settings, rst) 333 } 334 335 func (s *PersistenceSuite) TestSaveCommunitySettings() { 336 settings := CommunitySettings{CommunityID: "0x01", HistoryArchiveSupportEnabled: false} 337 err := s.db.SaveCommunitySettings(settings) 338 s.Require().NoError(err) 339 rst, err := s.db.GetCommunitiesSettings() 340 s.Require().NoError(err) 341 s.Equal(1, len(rst)) 342 } 343 344 func (s *PersistenceSuite) TestDeleteCommunitySettings() { 345 settings := CommunitySettings{CommunityID: "0x01", HistoryArchiveSupportEnabled: false} 346 347 err := s.db.SaveCommunitySettings(settings) 348 s.Require().NoError(err) 349 350 rst, err := s.db.GetCommunitiesSettings() 351 s.Require().NoError(err) 352 s.Equal(1, len(rst)) 353 s.Require().NoError(s.db.DeleteCommunitySettings(types.HexBytes{0x01})) 354 rst2, err := s.db.GetCommunitiesSettings() 355 s.Require().NoError(err) 356 s.Equal(0, len(rst2)) 357 } 358 359 func (s *PersistenceSuite) TestUpdateCommunitySettings() { 360 settings := []CommunitySettings{ 361 {CommunityID: "0x01", HistoryArchiveSupportEnabled: true}, 362 {CommunityID: "0x02", HistoryArchiveSupportEnabled: false}, 363 } 364 365 s.Require().NoError(s.db.SaveCommunitySettings(settings[0])) 366 s.Require().NoError(s.db.SaveCommunitySettings(settings[1])) 367 368 settings[0].HistoryArchiveSupportEnabled = true 369 settings[1].HistoryArchiveSupportEnabled = false 370 371 s.Require().NoError(s.db.UpdateCommunitySettings(settings[0])) 372 s.Require().NoError(s.db.UpdateCommunitySettings(settings[1])) 373 374 rst, err := s.db.GetCommunitiesSettings() 375 s.Require().NoError(err) 376 s.Equal(settings, rst) 377 } 378 379 func (s *PersistenceSuite) TestGetCommunityToken() { 380 tokens, err := s.db.GetCommunityTokens("123") 381 s.Require().NoError(err) 382 s.Require().Len(tokens, 0) 383 384 tokenERC721 := token.CommunityToken{ 385 CommunityID: "123", 386 TokenType: protobuf.CommunityTokenType_ERC721, 387 Address: "0x123", 388 Name: "StatusToken", 389 Symbol: "STT", 390 Description: "desc", 391 Supply: &bigint.BigInt{Int: big.NewInt(123)}, 392 InfiniteSupply: false, 393 Transferable: true, 394 RemoteSelfDestruct: true, 395 ChainID: 1, 396 DeployState: token.InProgress, 397 Base64Image: "ABCD", 398 TransactionHash: "0x1234", 399 Version: "1.0.0", 400 } 401 402 err = s.db.AddCommunityToken(&tokenERC721) 403 s.Require().NoError(err) 404 405 token, err := s.db.GetCommunityToken("123", 1, "0x123") 406 s.Require().NoError(err) 407 s.Require().Equal(&tokenERC721, token) 408 } 409 410 func (s *PersistenceSuite) TestGetCommunityTokens() { 411 tokens, err := s.db.GetCommunityTokens("123") 412 s.Require().NoError(err) 413 s.Require().Len(tokens, 0) 414 415 tokenERC721 := token.CommunityToken{ 416 CommunityID: "123", 417 TokenType: protobuf.CommunityTokenType_ERC721, 418 Address: "0x123", 419 Name: "StatusToken", 420 Symbol: "STT", 421 Description: "desc", 422 Supply: &bigint.BigInt{Int: big.NewInt(123)}, 423 InfiniteSupply: false, 424 Transferable: true, 425 RemoteSelfDestruct: true, 426 ChainID: 1, 427 DeployState: token.InProgress, 428 Base64Image: "ABCD", 429 Deployer: "0xDep1", 430 PrivilegesLevel: token.OwnerLevel, 431 TransactionHash: "0x1234", 432 Version: "1.0.0", 433 } 434 435 tokenERC20 := token.CommunityToken{ 436 CommunityID: "345", 437 TokenType: protobuf.CommunityTokenType_ERC20, 438 Address: "0x345", 439 Name: "StatusToken", 440 Symbol: "STT", 441 Description: "desc", 442 Supply: &bigint.BigInt{Int: big.NewInt(345)}, 443 InfiniteSupply: false, 444 Transferable: true, 445 RemoteSelfDestruct: true, 446 ChainID: 2, 447 DeployState: token.Failed, 448 Base64Image: "QWERTY", 449 Decimals: 21, 450 Deployer: "0xDep2", 451 PrivilegesLevel: token.CommunityLevel, 452 TransactionHash: "0x123456", 453 Version: "2.0.0", 454 } 455 456 err = s.db.AddCommunityToken(&tokenERC721) 457 s.Require().NoError(err) 458 err = s.db.AddCommunityToken(&tokenERC20) 459 s.Require().NoError(err) 460 461 tokens, err = s.db.GetCommunityTokens("123") 462 s.Require().NoError(err) 463 s.Require().Len(tokens, 1) 464 s.Require().Equal(tokenERC721, *tokens[0]) 465 466 err = s.db.UpdateCommunityTokenState(1, "0x123", token.Deployed) 467 s.Require().NoError(err) 468 tokens, err = s.db.GetCommunityTokens("123") 469 s.Require().NoError(err) 470 s.Require().Len(tokens, 1) 471 s.Require().Equal(token.Deployed, tokens[0].DeployState) 472 473 tokens, err = s.db.GetCommunityTokens("345") 474 s.Require().NoError(err) 475 s.Require().Len(tokens, 1) 476 s.Require().Equal(tokenERC20, *tokens[0]) 477 478 err = s.db.UpdateCommunityTokenAddress(1, "0x123", "0x123-newAddr") 479 s.Require().NoError(err) 480 tokens, err = s.db.GetCommunityTokens("123") 481 s.Require().NoError(err) 482 s.Require().Len(tokens, 1) 483 s.Require().Equal("0x123-newAddr", tokens[0].Address) 484 } 485 486 func (s *PersistenceSuite) TestSaveCheckChannelPermissionResponse() { 487 488 viewAndPostPermissionResults := make(map[string]*PermissionTokenCriteriaResult) 489 viewAndPostPermissionResults["one"] = &PermissionTokenCriteriaResult{ 490 Criteria: []bool{true, true, true, true}, 491 } 492 viewAndPostPermissionResults["two"] = &PermissionTokenCriteriaResult{ 493 Criteria: []bool{false}, 494 } 495 chatID := "some-chat-id" 496 communityID := "some-community-id" 497 498 checkChannelPermissionResponse := &CheckChannelPermissionsResponse{ 499 ViewOnlyPermissions: &CheckChannelViewOnlyPermissionsResult{ 500 Satisfied: true, 501 Permissions: make(map[string]*PermissionTokenCriteriaResult), 502 }, 503 ViewAndPostPermissions: &CheckChannelViewAndPostPermissionsResult{ 504 Satisfied: true, 505 Permissions: viewAndPostPermissionResults, 506 }, 507 } 508 509 err := s.db.SaveCheckChannelPermissionResponse(communityID, chatID, checkChannelPermissionResponse) 510 s.Require().NoError(err) 511 512 responses, err := s.db.GetCheckChannelPermissionResponses(communityID) 513 s.Require().NoError(err) 514 s.Require().Len(responses, 1) 515 s.Require().NotNil(responses[chatID]) 516 s.Require().True(responses[chatID].ViewOnlyPermissions.Satisfied) 517 s.Require().Len(responses[chatID].ViewOnlyPermissions.Permissions, 0) 518 s.Require().True(responses[chatID].ViewAndPostPermissions.Satisfied) 519 s.Require().Len(responses[chatID].ViewAndPostPermissions.Permissions, 2) 520 s.Require().Equal(responses[chatID].ViewAndPostPermissions.Permissions["one"].Criteria, []bool{true, true, true, true}) 521 s.Require().Equal(responses[chatID].ViewAndPostPermissions.Permissions["two"].Criteria, []bool{false}) 522 } 523 524 func (s *PersistenceSuite) TestGetCommunityRequestsToJoinWithRevealedAddresses() { 525 clock := uint64(time.Now().Unix()) 526 communityID := types.HexBytes{7, 7, 7, 7, 7, 7, 7, 7} 527 revealedAddresses := []string{"address1", "address2", "address3"} 528 chainIds := []uint64{1, 2} 529 530 // No data in database 531 rtjResult, err := s.db.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 532 s.Require().NoError(err, "GetCommunityRequestsToJoinWithRevealedAddresses shouldn't give any error") 533 s.Require().Len(rtjResult, 0) 534 535 // RTJ with 2 revealed Addresses 536 expectedRtj1 := &RequestToJoin{ 537 ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}, 538 PublicKey: common.PubkeyToHex(&s.identity.PublicKey), 539 Clock: clock, 540 CommunityID: communityID, 541 State: RequestToJoinStateAccepted, 542 RevealedAccounts: []*protobuf.RevealedAccount{ 543 { 544 Address: revealedAddresses[0], 545 }, 546 { 547 Address: revealedAddresses[1], 548 }, 549 }, 550 } 551 err = s.db.SaveRequestToJoin(expectedRtj1) 552 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 553 554 err = s.db.SaveRequestToJoinRevealedAddresses(expectedRtj1.ID, expectedRtj1.RevealedAccounts) 555 s.Require().NoError(err, "SaveRequestToJoinRevealedAddresses shouldn't give any error") 556 557 rtjResult, err = s.db.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 558 s.Require().NoError(err, "GetCommunityRequestsToJoinWithRevealedAddresses shouldn't give any error") 559 s.Require().Len(rtjResult, 1) 560 s.Require().Equal(expectedRtj1.ID, rtjResult[0].ID) 561 s.Require().Equal(expectedRtj1.PublicKey, rtjResult[0].PublicKey) 562 s.Require().Equal(expectedRtj1.Clock, rtjResult[0].Clock) 563 s.Require().Equal(expectedRtj1.CommunityID, rtjResult[0].CommunityID) 564 s.Require().Len(rtjResult[0].RevealedAccounts, 2) 565 566 for index, account := range rtjResult[0].RevealedAccounts { 567 s.Require().Equal(revealedAddresses[index], account.Address) 568 } 569 570 // RTJ with 1 revealed Address, ChainIds, IsAirdropAddress and Signature 571 signature := []byte("test") 572 expectedRtj2 := &RequestToJoin{ 573 ID: types.HexBytes{8, 7, 6, 5, 4, 3, 2, 1}, 574 PublicKey: common.PubkeyToHex(&s.identity.PublicKey), 575 Clock: clock, 576 CommunityID: communityID, 577 State: RequestToJoinStateAccepted, 578 RevealedAccounts: []*protobuf.RevealedAccount{ 579 { 580 Address: revealedAddresses[2], 581 ChainIds: chainIds, 582 IsAirdropAddress: true, 583 Signature: signature, 584 }, 585 }, 586 } 587 err = s.db.SaveRequestToJoin(expectedRtj2) 588 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 589 590 err = s.db.SaveRequestToJoinRevealedAddresses(expectedRtj2.ID, expectedRtj2.RevealedAccounts) 591 s.Require().NoError(err, "SaveRequestToJoinRevealedAddresses shouldn't give any error") 592 593 rtjResult, err = s.db.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 594 s.Require().NoError(err, "GetCommunityRequestsToJoinWithRevealedAddresses shouldn't give any error") 595 s.Require().Len(rtjResult, 2) 596 597 s.Require().Len(rtjResult[1].RevealedAccounts, 1) 598 s.Require().Equal(revealedAddresses[2], rtjResult[1].RevealedAccounts[0].Address) 599 s.Require().Equal(chainIds, rtjResult[1].RevealedAccounts[0].ChainIds) 600 s.Require().Equal(true, rtjResult[1].RevealedAccounts[0].IsAirdropAddress) 601 s.Require().Equal(rtjResult[1].RevealedAccounts[0].Signature, signature) 602 603 // RTJ without RevealedAccounts 604 expectedRtjWithoutRevealedAccounts := &RequestToJoin{ 605 ID: types.HexBytes{1, 6, 6, 6, 6, 6, 6, 6}, 606 PublicKey: common.PubkeyToHex(&s.identity.PublicKey), 607 Clock: clock, 608 CommunityID: communityID, 609 State: RequestToJoinStateAccepted, 610 } 611 err = s.db.SaveRequestToJoin(expectedRtjWithoutRevealedAccounts) 612 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 613 614 rtjResult, err = s.db.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 615 s.Require().NoError(err, "GetCommunityRequestsToJoinWithRevealedAddresses shouldn't give any error") 616 s.Require().Len(rtjResult, 3) 617 618 s.Require().Len(rtjResult[2].RevealedAccounts, 0) 619 620 // RTJ with RevealedAccount but with empty Address 621 expectedRtjWithEmptyAddress := &RequestToJoin{ 622 ID: types.HexBytes{2, 6, 6, 6, 6, 6, 6, 6}, 623 PublicKey: common.PubkeyToHex(&s.identity.PublicKey), 624 Clock: clock, 625 CommunityID: communityID, 626 State: RequestToJoinStateAccepted, 627 RevealedAccounts: []*protobuf.RevealedAccount{ 628 { 629 Address: "", 630 }, 631 }, 632 } 633 err = s.db.SaveRequestToJoin(expectedRtjWithEmptyAddress) 634 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 635 636 rtjResult, err = s.db.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) 637 s.Require().NoError(err, "GetCommunityRequestsToJoinWithRevealedAddresses shouldn't give any error") 638 s.Require().Len(rtjResult, 4) 639 s.Require().Len(rtjResult[3].RevealedAccounts, 0) 640 } 641 642 func (s *PersistenceSuite) TestCuratedCommunities() { 643 communities, err := s.db.GetCuratedCommunities() 644 s.Require().NoError(err) 645 s.Require().Empty(communities.ContractCommunities) 646 s.Require().Empty(communities.ContractFeaturedCommunities) 647 648 setCommunities := &CuratedCommunities{ 649 ContractCommunities: []string{"x", "d"}, 650 ContractFeaturedCommunities: []string{"x"}, 651 } 652 653 err = s.db.SetCuratedCommunities(setCommunities) 654 s.Require().NoError(err) 655 656 communities, err = s.db.GetCuratedCommunities() 657 s.Require().NoError(err) 658 s.Require().True(reflect.DeepEqual(communities, setCommunities)) 659 660 setCommunities = &CuratedCommunities{ 661 ContractCommunities: []string{"p", "a", "t", "r", "y", "k"}, 662 ContractFeaturedCommunities: []string{"p", "k"}, 663 } 664 665 err = s.db.SetCuratedCommunities(setCommunities) 666 s.Require().NoError(err) 667 668 communities, err = s.db.GetCuratedCommunities() 669 s.Require().NoError(err) 670 s.Require().True(reflect.DeepEqual(communities, setCommunities)) 671 } 672 673 func (s *PersistenceSuite) TestGetCommunityRequestToJoinWithRevealedAddresses() { 674 clock := uint64(time.Now().Unix()) 675 communityID := types.HexBytes{7, 7, 7, 7, 7, 7, 7, 7} 676 revealedAddresses := []string{"address1", "address2", "address3"} 677 chainIds := []uint64{1, 2} 678 publicKey := common.PubkeyToHex(&s.identity.PublicKey) 679 signature := []byte("test") 680 681 // No data in database 682 _, err := s.db.GetCommunityRequestToJoinWithRevealedAddresses(publicKey, communityID) 683 s.Require().ErrorIs(err, sql.ErrNoRows) 684 685 // RTJ with 2 withoutRevealed Addresses 686 expectedRtj := &RequestToJoin{ 687 ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}, 688 PublicKey: publicKey, 689 Clock: clock, 690 CommunityID: communityID, 691 State: RequestToJoinStateAccepted, 692 RevealedAccounts: []*protobuf.RevealedAccount{ 693 { 694 Address: revealedAddresses[2], 695 ChainIds: chainIds, 696 IsAirdropAddress: true, 697 Signature: signature, 698 }, 699 }, 700 } 701 err = s.db.SaveRequestToJoin(expectedRtj) 702 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 703 704 // check that there will be no error if revealed account is absent 705 rtjResult, err := s.db.GetCommunityRequestToJoinWithRevealedAddresses(publicKey, communityID) 706 s.Require().NoError(err, "RevealedAccounts empty, shouldn't give any error") 707 708 s.Require().Len(rtjResult.RevealedAccounts, 0) 709 710 // save revealed accounts for previous request to join 711 err = s.db.SaveRequestToJoinRevealedAddresses(expectedRtj.ID, expectedRtj.RevealedAccounts) 712 s.Require().NoError(err) 713 714 rtjResult, err = s.db.GetCommunityRequestToJoinWithRevealedAddresses(publicKey, communityID) 715 s.Require().NoError(err) 716 s.Require().Equal(expectedRtj.ID, rtjResult.ID) 717 s.Require().Equal(expectedRtj.PublicKey, rtjResult.PublicKey) 718 s.Require().Equal(expectedRtj.Clock, rtjResult.Clock) 719 s.Require().Equal(expectedRtj.CommunityID, rtjResult.CommunityID) 720 s.Require().Len(rtjResult.RevealedAccounts, 1) 721 } 722 723 func (s *PersistenceSuite) TestAllNonApprovedCommunitiesRequestsToJoin() { 724 // check on empty db 725 result, err := s.db.AllNonApprovedCommunitiesRequestsToJoin() 726 s.Require().NoError(err) 727 s.Require().Len(result, 0) 728 729 identity, err := crypto.GenerateKey() 730 s.Require().NoError(err, "crypto.GenerateKey shouldn't give any error") 731 732 clock := uint64(time.Now().Unix()) 733 734 // add a new community 735 community := s.makeNewCommunity(identity) 736 s.Require().NoError(err) 737 738 // add requests to join to the community 739 allStates := []RequestToJoinState{ 740 RequestToJoinStatePending, 741 RequestToJoinStateDeclined, 742 RequestToJoinStateAccepted, 743 RequestToJoinStateCanceled, 744 RequestToJoinStateAcceptedPending, 745 RequestToJoinStateDeclinedPending, 746 RequestToJoinStateAwaitingAddresses, 747 } 748 749 for i := range allStates { 750 identity, err := crypto.GenerateKey() 751 s.Require().NoError(err) 752 753 rtj := &RequestToJoin{ 754 ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, byte(i)}, 755 PublicKey: common.PubkeyToHex(&identity.PublicKey), 756 Clock: clock, 757 CommunityID: community.ID(), 758 State: allStates[i], 759 } 760 err = s.db.SaveRequestToJoin(rtj) 761 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 762 } 763 764 result, err = s.db.AllNonApprovedCommunitiesRequestsToJoin() 765 s.Require().NoError(err) 766 s.Require().Len(result, 6) // all except RequestToJoinStateAccepted 767 } 768 769 func (s *PersistenceSuite) TestSaveShardInfo() { 770 communityID := types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8} 771 clock := uint64(1) 772 // get non existing community shard 773 resultShard, err := s.db.GetCommunityShard(communityID) 774 s.Require().Error(err, sql.ErrNoRows) 775 s.Require().Nil(resultShard) 776 777 // shard info is nil 778 err = s.db.SaveCommunityShard(communityID, nil, clock) 779 s.Require().NoError(err) 780 781 // save shard info with the same clock 782 err = s.db.SaveCommunityShard(communityID, nil, clock) 783 s.Require().Error(err, ErrOldShardInfo) 784 785 resultShard, err = s.db.GetCommunityShard(communityID) 786 s.Require().NoError(err) 787 s.Require().Nil(resultShard) 788 789 // not nil shard 790 expectedShard := &shard.Shard{ 791 Cluster: 1, 792 Index: 2, 793 } 794 795 // save shard info with the same clock and check that data was not modified 796 err = s.db.SaveCommunityShard(communityID, expectedShard, clock) 797 s.Require().Error(err, ErrOldShardInfo) 798 resultShard, err = s.db.GetCommunityShard(communityID) 799 s.Require().NoError(err) 800 s.Require().Nil(resultShard) 801 802 // update the clock and save the shard info 803 clock += clock 804 err = s.db.SaveCommunityShard(communityID, expectedShard, clock) 805 s.Require().NoError(err) 806 resultShard, err = s.db.GetCommunityShard(communityID) 807 s.Require().NoError(err) 808 s.Require().NotNil(resultShard) 809 s.Require().Equal(expectedShard, resultShard) 810 811 // check shard deleting 812 err = s.db.DeleteCommunityShard(communityID) 813 s.Require().NoError(err) 814 resultShard, err = s.db.GetCommunityShard(communityID) 815 s.Require().Error(err, sql.ErrNoRows) 816 s.Require().Nil(resultShard) 817 } 818 819 func (s *PersistenceSuite) TestGetCommunityToValidateByID() { 820 communityID := types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8} 821 822 result, err := s.db.getCommunityToValidateByID(communityID) 823 s.Require().NoError(err) 824 s.Require().Len(result, 0) 825 } 826 827 func (s *PersistenceSuite) TestProcessedCommunityEvents() { 828 community := types.HexBytes{1} 829 events, err := s.db.GetAppliedCommunityEvents(community) 830 s.Require().NoError(err) 831 s.Require().Empty(events) 832 833 err = s.db.UpsertAppliedCommunityEvents(community, map[string]uint64{"a": 1, "b": 10}) 834 s.Require().NoError(err) 835 836 events, err = s.db.GetAppliedCommunityEvents(community) 837 s.Require().NoError(err) 838 s.Require().Len(events, 2) 839 s.Require().True(reflect.DeepEqual(events, map[string]uint64{"a": 1, "b": 10})) 840 841 err = s.db.UpsertAppliedCommunityEvents(community, map[string]uint64{"a": 2, "b": 8, "c": 1}) 842 s.Require().NoError(err) 843 844 events, err = s.db.GetAppliedCommunityEvents(community) 845 s.Require().NoError(err) 846 s.Require().Len(events, 3) 847 s.Require().True(reflect.DeepEqual(events, map[string]uint64{"a": 2, "b": 10, "c": 1})) 848 } 849 850 func (s *PersistenceSuite) TestDecryptedCommunityCache() { 851 communityDescription := &protobuf.CommunityDescription{ 852 Clock: 1000, 853 } 854 keyID1 := []byte("key-id-1") 855 keyID2 := []byte("key-id-2") 856 missingKeys := []*CommunityPrivateDataFailedToDecrypt{ 857 {KeyID: keyID1}, 858 {KeyID: keyID2}, 859 } 860 communityID := []byte("id") 861 err := s.db.SaveDecryptedCommunityDescription(communityID, missingKeys, communityDescription) 862 s.Require().NoError(err) 863 864 // Can be retrieved 865 retrievedCommunity, err := s.db.GetDecryptedCommunityDescription(communityID, 1000) 866 s.Require().NoError(err) 867 s.Require().True(proto.Equal(communityDescription, retrievedCommunity)) 868 869 // Retrieving a random one doesn't throw an error 870 retrievedCommunity, err = s.db.GetDecryptedCommunityDescription([]byte("non-existent-id"), 1000) 871 s.Require().NoError(err) 872 s.Require().Nil(retrievedCommunity) 873 874 // Retrieving a random one doesn't throw an error 875 retrievedCommunity, err = s.db.GetDecryptedCommunityDescription(communityID, 999) 876 s.Require().NoError(err) 877 s.Require().Nil(retrievedCommunity) 878 879 // invalidating the cache 880 err = s.db.InvalidateDecryptedCommunityCacheForKeys([]*encryption.HashRatchetInfo{{KeyID: keyID1}}) 881 s.Require().NoError(err) 882 883 // community cannot be retrieved anymore 884 retrievedCommunity, err = s.db.GetDecryptedCommunityDescription(communityID, 1000) 885 s.Require().NoError(err) 886 s.Require().Nil(retrievedCommunity) 887 888 // make sure everything is cleaned up 889 890 qr := s.db.db.QueryRow("SELECT COUNT(*) FROM encrypted_community_description_missing_keys") 891 892 var count int 893 894 err = qr.Scan(&count) 895 s.Require().NoError(err) 896 s.Require().Equal(count, 0) 897 898 } 899 900 func (s *PersistenceSuite) TestDecryptedCommunityCacheClock() { 901 communityDescription := &protobuf.CommunityDescription{ 902 Clock: 1000, 903 } 904 keyID1 := []byte("key-id-1") 905 keyID2 := []byte("key-id-2") 906 keyID3 := []byte("key-id-3") 907 908 missingKeys := []*CommunityPrivateDataFailedToDecrypt{ 909 {KeyID: keyID1}, 910 {KeyID: keyID2}, 911 } 912 communityID := []byte("id") 913 err := s.db.SaveDecryptedCommunityDescription(communityID, missingKeys, communityDescription) 914 s.Require().NoError(err) 915 916 // Can be retrieved 917 retrievedCommunity, err := s.db.GetDecryptedCommunityDescription(communityID, 1000) 918 s.Require().NoError(err) 919 s.Require().True(proto.Equal(communityDescription, retrievedCommunity)) 920 921 // Save an earlier community 922 communityDescription.Clock = 999 923 err = s.db.SaveDecryptedCommunityDescription(communityID, missingKeys, communityDescription) 924 s.Require().NoError(err) 925 926 // The old one should be retrieved 927 retrievedCommunity, err = s.db.GetDecryptedCommunityDescription(communityID, 1000) 928 s.Require().NoError(err) 929 s.Require().NotNil(retrievedCommunity) 930 s.Require().Equal(uint64(1000), retrievedCommunity.Clock) 931 932 // Save a later community, with a single key 933 missingKeys = []*CommunityPrivateDataFailedToDecrypt{ 934 {KeyID: keyID3}, 935 } 936 937 communityDescription.Clock = 1001 938 err = s.db.SaveDecryptedCommunityDescription(communityID, missingKeys, communityDescription) 939 s.Require().NoError(err) 940 941 // The new one should be retrieved 942 retrievedCommunity, err = s.db.GetDecryptedCommunityDescription(communityID, 1001) 943 s.Require().NoError(err) 944 s.Require().Equal(uint64(1001), retrievedCommunity.Clock) 945 946 // Make sure the previous two are cleaned up and there's only one left 947 qr := s.db.db.QueryRow("SELECT COUNT(*) FROM encrypted_community_description_missing_keys") 948 949 var count int 950 951 err = qr.Scan(&count) 952 s.Require().NoError(err) 953 s.Require().Equal(count, 1) 954 } 955 956 func (s *PersistenceSuite) TestGetCommunityRequestsToJoinRevealedAddresses() { 957 clock := uint64(time.Now().Unix()) 958 communityID := types.HexBytes{7, 7, 7, 7, 7, 7, 7, 7} 959 revealedAddress := "address1" 960 chainIds := []uint64{1, 2} 961 publicKey := common.PubkeyToHex(&s.identity.PublicKey) 962 signature := []byte("test") 963 964 // No data in database 965 accounts, err := s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID) 966 s.Require().NoError(err) 967 _, exists := accounts[publicKey] 968 s.Require().False(exists) 969 970 expectedRtj := &RequestToJoin{ 971 ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}, 972 PublicKey: publicKey, 973 Clock: clock, 974 CommunityID: communityID, 975 State: RequestToJoinStateAccepted, 976 RevealedAccounts: []*protobuf.RevealedAccount{ 977 { 978 Address: revealedAddress, 979 ChainIds: chainIds, 980 IsAirdropAddress: true, 981 Signature: signature, 982 }, 983 }, 984 } 985 986 // Request to join was stored without revealed account 987 err = s.db.SaveRequestToJoin(expectedRtj) 988 s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") 989 990 // revealed account is absent 991 accounts, err = s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID) 992 s.Require().NoError(err, "RevealedAccounts empty, shouldn't give any error") 993 994 _, exists = accounts[publicKey] 995 s.Require().False(exists) 996 997 // save revealed accounts for the previous request to join 998 err = s.db.SaveRequestToJoinRevealedAddresses(expectedRtj.ID, expectedRtj.RevealedAccounts) 999 s.Require().NoError(err) 1000 1001 accounts, err = s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID) 1002 s.Require().NoError(err) 1003 memberAccounts, exists := accounts[publicKey] 1004 s.Require().True(exists) 1005 s.Require().Len(memberAccounts, 1) 1006 }