github.com/status-im/status-go@v1.1.0/protocol/v1/membership_update_message_test.go (about) 1 package protocol 2 3 import ( 4 "testing" 5 6 "github.com/golang/protobuf/proto" 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 10 "github.com/status-im/status-go/eth-node/crypto" 11 "github.com/status-im/status-go/protocol/protobuf" 12 ) 13 14 var ( 15 testMembershipUpdateMessageStruct = MembershipUpdateMessage{ 16 ChatID: "chat-id", 17 Events: []MembershipUpdateEvent{ 18 { 19 Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, 20 Name: "thathata", 21 ChatID: "chat-id", 22 ClockValue: 156897373998501, 23 }, 24 { 25 Type: protobuf.MembershipUpdateEvent_MEMBERS_ADDED, 26 Members: []string{"0x04aebe2bb01a988abe7d978662f21de7760486119876c680e5a559e38e086a2df6dad41c4e4d9079c03db3bced6cb70fca76afc5650e50ea19b81572046a813534"}, 27 ChatID: "chat-id", 28 ClockValue: 156897373998502, 29 }, 30 }, 31 } 32 ) 33 34 func TestSignMembershipUpdate(t *testing.T) { 35 key, err := crypto.HexToECDSA("838fbdd1b670209a258b90af25653a018bc582c44c56e6290a973eebbeb15732") 36 require.NoError(t, err) 37 event := &testMembershipUpdateMessageStruct.Events[0] 38 err = event.Sign(key) 39 require.NoError(t, err) 40 41 encodedEvent, err := proto.Marshal(event.ToProtobuf()) 42 require.NoError(t, err) 43 44 var signatureMaterial []byte 45 signatureMaterial = append(signatureMaterial, []byte(testMembershipUpdateMessageStruct.ChatID)...) 46 signatureMaterial = crypto.Keccak256(append(signatureMaterial, encodedEvent...)) 47 expected, err := crypto.Sign(signatureMaterial, key) 48 require.NoError(t, err) 49 require.Equal(t, encodedEvent, event.RawPayload) 50 require.Equal(t, expected, event.Signature) 51 52 // Sign the other event 53 err = testMembershipUpdateMessageStruct.Events[1].Sign(key) 54 require.NoError(t, err) 55 56 // Encode message 57 encodedMessage, err := testMembershipUpdateMessageStruct.ToProtobuf() 58 require.NoError(t, err) 59 // Verify it 60 verifiedMessage, err := MembershipUpdateMessageFromProtobuf(encodedMessage) 61 require.NoError(t, err) 62 require.Equal(t, verifiedMessage, &testMembershipUpdateMessageStruct) 63 } 64 65 func TestGroupCreator(t *testing.T) { 66 key, err := crypto.GenerateKey() 67 require.NoError(t, err) 68 g, err := NewGroupWithCreator("abc", "#fa6565", 20, key) 69 require.NoError(t, err) 70 creator, err := g.Creator() 71 require.NoError(t, err) 72 require.Equal(t, publicKeyToString(&key.PublicKey), creator) 73 } 74 75 func TestGroupProcessEvent(t *testing.T) { 76 createGroup := func(admins, members, joined []string, name string, color string, image string) Group { 77 return Group{ 78 name: name, 79 color: color, 80 image: []byte(image), 81 admins: newStringSetFromSlice(admins), 82 members: newStringSetFromSlice(members), 83 } 84 } 85 86 const emptyName = "" 87 const emptyColor = "" 88 const emptyImage = "" 89 90 testCases := []struct { 91 Name string 92 Group Group 93 Result Group 94 From string 95 Event MembershipUpdateEvent 96 }{ 97 { 98 Name: "chat-created event", 99 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 100 Result: createGroup([]string{"0xabc"}, []string{"0xabc"}, []string{"0xabc"}, "some-name", "#7cda00", emptyImage), 101 From: "0xabc", 102 Event: NewChatCreatedEvent("some-name", "#7cda00", 0), 103 }, 104 { 105 Name: "name-changed event", 106 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 107 Result: createGroup(nil, nil, nil, "some-name", emptyColor, emptyImage), 108 From: "0xabc", 109 Event: NewNameChangedEvent("some-name", 0), 110 }, 111 { 112 Name: "color-changed event", 113 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 114 Result: createGroup(nil, nil, nil, emptyName, "#7cda00", emptyImage), 115 From: "0xabc", 116 Event: NewColorChangedEvent("#7cda00", 0), 117 }, 118 { 119 Name: "image-changed event", 120 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 121 Result: createGroup(nil, nil, nil, emptyName, emptyColor, "123"), 122 From: "0xabc", 123 Event: NewImageChangedEvent([]byte("123"), 0), 124 }, 125 { 126 Name: "admins-added event", 127 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 128 Result: createGroup([]string{"0xabc", "0x123"}, nil, nil, emptyName, emptyColor, emptyImage), 129 From: "0xabc", 130 Event: NewAdminsAddedEvent([]string{"0xabc", "0x123"}, 0), 131 }, 132 { 133 Name: "admin-removed event", 134 Group: createGroup([]string{"0xabc", "0xdef"}, nil, nil, emptyName, emptyColor, emptyImage), 135 Result: createGroup([]string{"0xdef"}, nil, nil, emptyName, emptyColor, emptyImage), 136 From: "0xabc", 137 Event: NewAdminRemovedEvent("0xabc", 0), 138 }, 139 { 140 Name: "members-added event", 141 Group: createGroup(nil, nil, nil, emptyName, emptyColor, emptyImage), 142 Result: createGroup(nil, []string{"0xabc", "0xdef"}, nil, emptyName, emptyColor, emptyImage), 143 From: "0xabc", 144 Event: NewMembersAddedEvent([]string{"0xabc", "0xdef"}, 0), 145 }, 146 { 147 Name: "member-removed event", 148 Group: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xdef", "0xabc"}, emptyName, emptyColor, emptyImage), 149 Result: createGroup(nil, []string{"0xdef"}, []string{"0xdef"}, emptyName, emptyColor, emptyImage), 150 From: "0xabc", 151 Event: NewMemberRemovedEvent("0xabc", 0), 152 }, 153 { 154 Name: "member-joined event", 155 Group: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xabc"}, emptyName, emptyColor, emptyImage), 156 Result: createGroup(nil, []string{"0xabc", "0xdef"}, []string{"0xabc", "0xdef"}, emptyName, emptyColor, emptyImage), 157 From: "0xdef", 158 Event: NewMemberJoinedEvent(0), 159 }, 160 } 161 162 for _, tc := range testCases { 163 t.Run(tc.Name, func(t *testing.T) { 164 g := tc.Group 165 tc.Event.From = tc.From 166 g.processEvent(tc.Event) 167 require.EqualValues(t, tc.Result, g) 168 }) 169 } 170 } 171 172 func TestGroupValidateEvent(t *testing.T) { 173 createGroup := func(admins, members []string) Group { 174 return Group{ 175 admins: newStringSetFromSlice(admins), 176 members: newStringSetFromSlice(members), 177 } 178 } 179 testCases := []struct { 180 Name string 181 From string 182 Group Group 183 Event MembershipUpdateEvent 184 Result bool 185 }{ 186 { 187 Name: "chat-created with empty admins and members", 188 Group: createGroup(nil, nil), 189 From: "0xabc", 190 Event: NewChatCreatedEvent("test", "#fa6565", 0), 191 Result: true, 192 }, 193 { 194 Name: "chat-created with existing admins", 195 Group: createGroup([]string{"0xabc"}, nil), 196 From: "0xabc", 197 Event: NewChatCreatedEvent("test", "#fa6565", 0), 198 Result: false, 199 }, 200 { 201 Name: "chat-created with existing members", 202 Group: createGroup(nil, []string{"0xabc"}), 203 From: "0xabc", 204 Event: NewChatCreatedEvent("test", "#fa6565", 0), 205 Result: false, 206 }, 207 { 208 Name: "name-changed allowed because from is admin", 209 From: "0xabc", 210 Group: createGroup([]string{"0xabc"}, nil), 211 Event: NewNameChangedEvent("new-name", 0), 212 Result: true, 213 }, 214 { 215 Name: "name-changed allowed because from is member", 216 From: "0x123", 217 Group: createGroup([]string{"0xabc"}, []string{"0x123"}), 218 Event: NewNameChangedEvent("new-name", 0), 219 Result: true, 220 }, 221 { 222 Name: "color-changed allowed because from is admin", 223 From: "0xabc", 224 Group: createGroup([]string{"0xabc"}, nil), 225 Event: NewColorChangedEvent("#7cda00", 0), 226 Result: true, 227 }, 228 { 229 Name: "color-changed allowed because from is member", 230 From: "0x123", 231 Group: createGroup([]string{"0xabc"}, []string{"0x123"}), 232 Event: NewColorChangedEvent("#7cda00", 0), 233 Result: true, 234 }, 235 { 236 Name: "image-changed allowed because from is admin", 237 From: "0xabc", 238 Group: createGroup([]string{"0xabc"}, nil), 239 Event: NewImageChangedEvent([]byte{1, 2, 3}, 0), 240 Result: true, 241 }, 242 { 243 Name: "image-changed not allowed for non-admins", 244 From: "0xabc", 245 Group: createGroup(nil, nil), 246 Event: NewImageChangedEvent([]byte{1, 2, 3}, 0), 247 Result: false, 248 }, 249 { 250 Name: "members-added allowed because from is admin", 251 From: "0xabc", 252 Group: createGroup([]string{"0xabc"}, nil), 253 Event: NewMembersAddedEvent([]string{"0x123"}, 0), 254 Result: true, 255 }, 256 { 257 Name: "members-added not allowed because from is member", 258 From: "0x123", 259 Group: createGroup([]string{"0xabc"}, []string{"0x123"}), 260 Event: NewMembersAddedEvent([]string{"0x123"}, 0), 261 Result: true, 262 }, 263 { 264 Name: "member-removed allowed because removing themselves", 265 From: "0xabc", 266 Group: createGroup(nil, nil), 267 Event: NewMemberRemovedEvent("0xabc", 0), 268 Result: true, 269 }, 270 { 271 Name: "member-removed allowed because from is admin", 272 From: "0xabc", 273 Group: createGroup([]string{"0xabc"}, []string{"0x123"}), 274 Event: NewMemberRemovedEvent("0x123", 0), 275 Result: true, 276 }, 277 { 278 Name: "member-removed not allowed for non-admins", 279 From: "0x123", 280 Group: createGroup([]string{"0xabc"}, []string{"0x123", "0x456"}), 281 Event: NewMemberRemovedEvent("0x456", 0), 282 Result: false, 283 }, 284 { 285 Name: "member-joined must be in members", 286 From: "0xabc", 287 Group: createGroup(nil, []string{"0xabc"}), 288 Event: NewMemberJoinedEvent(0), 289 Result: true, 290 }, 291 { 292 Name: "member-joined not valid because not in members", 293 From: "0xabc", 294 Group: createGroup(nil, nil), 295 Event: NewMemberJoinedEvent(0), 296 Result: false, 297 }, 298 { 299 Name: "member-joined not valid because from differs from the event", 300 From: "0xdef", 301 Group: createGroup(nil, nil), 302 Event: NewMemberJoinedEvent(0), 303 Result: false, 304 }, 305 { 306 Name: "admins-added allowed because originating from other admin", 307 From: "0xabc", 308 Group: createGroup([]string{"0xabc", "0x123"}, []string{"0xdef", "0xghi"}), 309 Event: NewAdminsAddedEvent([]string{"0xdef"}, 0), 310 Result: true, 311 }, 312 { 313 Name: "admins-added not allowed because not from admin", 314 From: "0xabc", 315 Group: createGroup([]string{"0x123"}, []string{"0xdef", "0xghi"}), 316 Event: NewAdminsAddedEvent([]string{"0xdef"}, 0), 317 Result: false, 318 }, 319 { 320 Name: "admins-added not allowed because not in members", 321 From: "0xabc", 322 Group: createGroup([]string{"0xabc", "0x123"}, []string{"0xghi"}), 323 Event: NewAdminsAddedEvent([]string{"0xdef"}, 0), 324 Result: false, 325 }, 326 { 327 Name: "admin-removed allowed because is admin and removes themselves", 328 From: "0xabc", 329 Group: createGroup([]string{"0xabc"}, nil), 330 Event: NewAdminRemovedEvent("0xabc", 0), 331 Result: true, 332 }, 333 { 334 Name: "admin-removed not allowed because not themselves", 335 From: "0xabc", 336 Group: createGroup([]string{"0xabc", "0xdef"}, nil), 337 Event: NewAdminRemovedEvent("0xdef", 0), 338 Result: false, 339 }, 340 { 341 Name: "admin-removed not allowed because not admin", 342 From: "0xdef", 343 Group: createGroup([]string{"0xabc"}, nil), 344 Event: NewAdminRemovedEvent("0xabc", 0), 345 Result: false, 346 }, 347 } 348 349 for _, tc := range testCases { 350 t.Run(tc.Name, func(t *testing.T) { 351 tc.Event.From = tc.From 352 result := tc.Group.validateEvent(tc.Event) 353 assert.Equal(t, tc.Result, result) 354 }) 355 } 356 } 357 358 func TestMembershipUpdateEventEqual(t *testing.T) { 359 u1 := MembershipUpdateEvent{ 360 Type: protobuf.MembershipUpdateEvent_CHAT_CREATED, 361 ClockValue: 1, 362 Members: []string{"0xabc"}, 363 Name: "abc", 364 Signature: []byte("signature"), 365 } 366 require.True(t, u1.Equal(u1)) 367 368 // Verify equality breaking. 369 u2 := u1 370 u2.Signature = []byte("different-signature") 371 require.False(t, u1.Equal(u2)) 372 } 373 374 func TestAbridgedEventsNameChanged(t *testing.T) { 375 var clock uint64 = 0 376 creator, err := crypto.GenerateKey() 377 require.NoError(t, err) 378 creatorID := publicKeyToString(&creator.PublicKey) 379 380 g, err := NewGroupWithCreator("name-0", "#fa6565", clock, creator) 381 require.NoError(t, err) 382 clock++ 383 384 // Full events is only a single one 385 require.Len(t, g.Events(), 1) 386 // same as abridged 387 require.Len(t, g.AbridgedEvents(), 1) 388 389 // We change name of the chat 390 nameChangedEvent1 := NewNameChangedEvent("name-1", clock) 391 nameChangedEvent1.From = creatorID 392 nameChangedEvent1.ChatID = g.chatID 393 err = g.ProcessEvent(nameChangedEvent1) 394 require.NoError(t, err) 395 clock++ 396 397 // We change name of the chat again 398 nameChangedEvent2 := NewNameChangedEvent("name-2", clock) 399 nameChangedEvent2.From = creatorID 400 nameChangedEvent2.ChatID = g.chatID 401 err = g.ProcessEvent(nameChangedEvent2) 402 require.NoError(t, err) 403 404 // Full events is 3 events 405 require.Len(t, g.Events(), 3) 406 // While abridged should exclude the first name-1 event 407 require.Len(t, g.AbridgedEvents(), 2) 408 require.Equal(t, g.AbridgedEvents()[1].Name, "name-2") 409 } 410 411 func TestAbridgedEventsMembers(t *testing.T) { 412 var clock uint64 = 0 413 creator, err := crypto.GenerateKey() 414 require.NoError(t, err) 415 creatorID := publicKeyToString(&creator.PublicKey) 416 417 member1, err := crypto.GenerateKey() 418 require.NoError(t, err) 419 member1ID := publicKeyToString(&member1.PublicKey) 420 421 member2, err := crypto.GenerateKey() 422 require.NoError(t, err) 423 member2ID := publicKeyToString(&member2.PublicKey) 424 425 member3, err := crypto.GenerateKey() 426 require.NoError(t, err) 427 member3ID := publicKeyToString(&member3.PublicKey) 428 429 member4, err := crypto.GenerateKey() 430 require.NoError(t, err) 431 member4ID := publicKeyToString(&member4.PublicKey) 432 433 g, err := NewGroupWithCreator("name-0", "#fa6565", clock, creator) 434 require.NoError(t, err) 435 clock++ 436 437 // Full events is only a single one 438 require.Len(t, g.Events(), 1) 439 // same as abridged 440 require.Len(t, g.AbridgedEvents(), 1) 441 442 // Add three new members 443 event := NewMembersAddedEvent([]string{member1ID, member2ID, member3ID}, clock) 444 event.From = creatorID 445 event.ChatID = g.chatID 446 err = g.ProcessEvent(event) 447 require.NoError(t, err) 448 clock++ 449 450 require.Len(t, g.Events(), 2) 451 // All the events are relevant here, so it should be the same 452 require.Len(t, g.AbridgedEvents(), 2) 453 454 // We remove one of the users 455 event = NewMemberRemovedEvent(member3ID, clock) 456 event.From = creatorID 457 event.ChatID = g.chatID 458 err = g.ProcessEvent(event) 459 require.NoError(t, err) 460 clock++ 461 462 require.Len(t, g.Events(), 3) 463 // All the events are relevant here, so it should be the same 464 require.Len(t, g.AbridgedEvents(), 3) 465 466 // Add a new member 467 event = NewMembersAddedEvent([]string{member4ID}, clock) 468 event.From = creatorID 469 event.ChatID = g.chatID 470 err = g.ProcessEvent(event) 471 require.NoError(t, err) 472 clock++ 473 474 require.Len(t, g.Events(), 4) 475 // All the events are relevant here, so it should be the same 476 require.Len(t, g.AbridgedEvents(), 4) 477 478 // We remove the member just added 479 event = NewMemberRemovedEvent(member4ID, clock) 480 event.From = creatorID 481 event.ChatID = g.chatID 482 err = g.ProcessEvent(event) 483 require.NoError(t, err) 484 485 require.Len(t, g.Events(), 5) 486 // The previous two events, should be removed, because they have no impact 487 // on the chat history 488 abridgedEvents := g.AbridgedEvents() 489 require.Len(t, abridgedEvents, 3) 490 491 require.Equal(t, uint64(0), abridgedEvents[0].ClockValue) 492 require.Equal(t, uint64(1), abridgedEvents[1].ClockValue) 493 require.Equal(t, uint64(2), abridgedEvents[2].ClockValue) 494 } 495 496 func TestAbridgedEventsAdmins(t *testing.T) { 497 var clock uint64 = 0 498 creator, err := crypto.GenerateKey() 499 require.NoError(t, err) 500 creatorID := publicKeyToString(&creator.PublicKey) 501 502 member1, err := crypto.GenerateKey() 503 require.NoError(t, err) 504 member1ID := publicKeyToString(&member1.PublicKey) 505 506 member2, err := crypto.GenerateKey() 507 require.NoError(t, err) 508 member2ID := publicKeyToString(&member2.PublicKey) 509 510 member3, err := crypto.GenerateKey() 511 require.NoError(t, err) 512 member3ID := publicKeyToString(&member3.PublicKey) 513 514 g, err := NewGroupWithCreator("name-0", "#fa6565", clock, creator) 515 require.NoError(t, err) 516 clock++ 517 518 // Full events is only a single one 519 require.Len(t, g.Events(), 1) 520 // same as abridged 521 require.Len(t, g.AbridgedEvents(), 1) 522 523 // Add three new members 524 event := NewMembersAddedEvent([]string{member1ID, member2ID, member3ID}, clock) 525 event.From = creatorID 526 event.ChatID = g.chatID 527 err = g.ProcessEvent(event) 528 require.NoError(t, err) 529 clock++ 530 531 require.Len(t, g.Events(), 2) 532 // All the events are relevant here, so it should be the same 533 require.Len(t, g.AbridgedEvents(), 2) 534 535 // Make two of them admins 536 event = NewAdminsAddedEvent([]string{member1ID, member2ID}, clock) 537 event.From = creatorID 538 event.ChatID = g.chatID 539 err = g.ProcessEvent(event) 540 require.NoError(t, err) 541 542 require.Len(t, g.Events(), 3) 543 // All the events are relevant here, so it should be the same 544 require.Len(t, g.AbridgedEvents(), 3) 545 } 546 547 func TestWasEverMember(t *testing.T) { 548 key, err := crypto.GenerateKey() 549 require.NoError(t, err) 550 g, err := NewGroupWithCreator("abc", "#fa6565", 20, key) 551 require.NoError(t, err) 552 553 wasMember, err := g.WasEverMember(publicKeyToString(&key.PublicKey)) 554 require.NoError(t, err) 555 require.True(t, wasMember) 556 557 key2, err := crypto.GenerateKey() 558 require.NoError(t, err) 559 560 wasMember, err = g.WasEverMember(publicKeyToString(&key2.PublicKey)) 561 require.NoError(t, err) 562 require.False(t, wasMember) 563 564 // Add a new member 565 event := NewMembersAddedEvent([]string{publicKeyToString(&key2.PublicKey)}, 21) 566 event.From = publicKeyToString(&key.PublicKey) 567 event.ChatID = g.chatID 568 err = g.ProcessEvent(event) 569 require.NoError(t, err) 570 571 wasMember, err = g.WasEverMember(publicKeyToString(&key2.PublicKey)) 572 require.NoError(t, err) 573 require.True(t, wasMember) 574 }