Skip to content

Commit eee8fd5

Browse files
Pull in related events into the conflicted control events (#308)
* Pull in related events into the conflicted control events * Use t.Log * Give the test a sensible name * Don't visit the same auth event more than once when working out the full control set
1 parent 8d81804 commit eee8fd5

File tree

2 files changed

+114
-12
lines changed

2 files changed

+114
-12
lines changed

stateresolutionv2.go

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const (
3232

3333
type stateResolverV2 struct {
3434
authEventMap map[string]*Event // Map of all provided auth events
35+
conflictedEventMap map[string]*Event // Map of all provided conflicted events
3536
authPowerLevels map[string]int64 // A cache of user power levels
3637
powerLevelMainline []*Event // Power level events in mainline ordering
3738
powerLevelMainlinePos map[string]int // Power level event positions in mainline
@@ -81,6 +82,7 @@ func ResolveStateConflictsV2(
8182
conflictedOthers := make([]*Event, 0, len(conflicted))
8283
r := stateResolverV2{
8384
authEventMap: eventMapFromEvents(authEvents),
85+
conflictedEventMap: eventMapFromEvents(conflicted),
8486
authPowerLevels: make(map[string]int64, len(conflicted)+len(unconflicted)),
8587
powerLevelMainlinePos: make(map[string]int),
8688
resolvedThirdPartyInvites: make(map[string]*Event, len(conflicted)),
@@ -97,17 +99,57 @@ func ResolveStateConflictsV2(
9799
isUnconflicted[u.EventID()] = struct{}{}
98100
}
99101

100-
// Separate out control events from the rest of the events. This is necessary
101-
// because we perform topological ordering of the control events separately,
102-
// and then the mainline ordering of all other events depends on that
103-
// ordering.
104-
for _, p := range append(conflicted, authDifference...) {
105-
if _, unconflicted := isUnconflicted[p.EventID()]; !unconflicted {
106-
if isControlEvent(p) {
107-
conflictedControlEvents = append(conflictedControlEvents, p)
108-
} else {
109-
conflictedOthers = append(conflictedOthers, p)
102+
// Get the full conflicted set, that is the conflicted events and the
103+
// auth difference (events that don't appear in all auth chains).
104+
fullConflictedSet := append(conflicted, authDifference...)
105+
106+
// The full power set function returns the event and all of its auth
107+
// events that also happen to appear in the conflicted set. This will
108+
// effectively allow us to pull in all related events for any control
109+
// event, even if those related events are themselves not control events.
110+
visited := make(map[string]struct{}, len(conflicted)+len(authEvents))
111+
var fullControlSet func(event *Event) []*Event
112+
fullControlSet = func(event *Event) []*Event {
113+
events := []*Event{event}
114+
for _, authEventID := range event.AuthEventIDs() {
115+
if _, ok := visited[authEventID]; ok {
116+
continue
110117
}
118+
if event, ok := r.conflictedEventMap[authEventID]; ok {
119+
events = append(events, fullControlSet(event)...)
120+
}
121+
visited[authEventID] = struct{}{}
122+
}
123+
return events
124+
}
125+
126+
// First of all, work through the full conflicted set. Ignoring any
127+
// events which are unconflicted (from the auth difference, for example),
128+
// pull in the control events and any events directly related to them.
129+
conflictedPulledIn := make(map[string]struct{}, len(conflicted)+len(authEvents))
130+
for _, p := range fullConflictedSet {
131+
if _, unconflicted := isUnconflicted[p.EventID()]; unconflicted {
132+
continue
133+
}
134+
if isControlEvent(p) {
135+
relatedEvents := fullControlSet(p)
136+
for _, event := range relatedEvents {
137+
conflictedPulledIn[event.EventID()] = struct{}{}
138+
}
139+
conflictedControlEvents = append(conflictedControlEvents, relatedEvents...)
140+
}
141+
}
142+
143+
// Then work through the set again, this time looking for any events
144+
// that were left over from the last loop — that is, events that are
145+
// either not control events or weren't pulled in to the control set.
146+
for _, p := range fullConflictedSet {
147+
eventID := p.EventID()
148+
if _, unconflicted := isUnconflicted[eventID]; unconflicted || isControlEvent(p) {
149+
continue
150+
}
151+
if _, ok := conflictedPulledIn[eventID]; !ok {
152+
conflictedOthers = append(conflictedOthers, p)
111153
}
112154
}
113155

@@ -251,7 +293,7 @@ func (r *stateResolverV2) createPowerLevelMainline() []*Event {
251293
// that we can look up the event type.
252294
if authEvent, ok := r.authEventMap[authEventID]; ok {
253295
// Is the event a power event?
254-
if authEvent.Type() == MRoomPowerLevels {
296+
if authEvent.Type() == MRoomPowerLevels && authEvent.StateKeyEquals("") {
255297
// We found a power level event in the event's auth events - start
256298
// the iterator from this new event.
257299
iter(authEvent)
@@ -300,7 +342,7 @@ func (r *stateResolverV2) getFirstPowerLevelMainlineEvent(event *Event) (
300342
// that we can look up the event type.
301343
if authEvent, ok := r.authEventMap[authEventID]; ok {
302344
// Is the event a power level event?
303-
if authEvent.Type() == MRoomPowerLevels {
345+
if authEvent.Type() == MRoomPowerLevels && authEvent.StateKeyEquals("") {
304346
// Is the event in the mainline?
305347
if isIn, pos := isInMainline(authEvent); isIn {
306348
// It is - take a note of the event and position and stop the
@@ -343,6 +385,12 @@ func (r *stateResolverV2) authAndApplyEvents(events []*Event) {
343385
// Apply the newly authed event to the partial state. We need to do this
344386
// here so that the next loop will have partial state to auth against.
345387
r.applyEvents([]*Event{event})
388+
// If we've just applied an event that is going to change the contents of
389+
// the allower then we will need to update that too.
390+
switch event.Type() {
391+
case MRoomCreate, MRoomPowerLevels, MRoomJoinRules, MRoomMember:
392+
allower = newAllowerContext(r)
393+
}
346394
}
347395
}
348396

stateresolutionv2_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,60 @@ func TestReverseTopologicalEventSorting(t *testing.T) {
450450
}
451451
}
452452

453+
func TestStateResolutionOtherEventDoesntOverpowerPowerEvent(t *testing.T) {
454+
eventJSONs := []string{
455+
/* create event */ `{"auth_events":[],"content":{"creator":"@anon-20220512_124253-1:localhost:8800","room_version":"6"},"depth":1,"hashes":{"sha256":"ej3MHt4EnQemwqnfLhgwN6RBArYc5JnWcZt1PI3m4hE"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":[],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"7Pu9f39yDWJtl8msrnz+sPSBEA2jOJ4tJsZ1Zb6Bi+vZQMzMWwT/U6GZipxQqaeJr0TpVMa7zq/YhivArRRbAA"}},"state_key":"","type":"m.room.create"}`,
456+
/* first user joins */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8"],"content":{"displayname":"anon-20220512_124253-1","membership":"join"},"depth":2,"hashes":{"sha256":"L3aLzAakLKWzl9IlhjO6CAqAaANjyV6W5mI8XlD8XR8"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"1lRHwVx5kFAfeUdndh3/hhAe5S3uugA+FwPR2ZiXBxr4DkjcfDb4TRCobEv3G9IBWPPbQxKw20x3LlTsstunAw"}},"state_key":"@anon-20220512_124253-1:localhost:8800","type":"m.room.member"}`,
457+
/* power levels */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E"],"content":{"ban":50,"events":{"m.room.avatar":50,"m.room.canonical_alias":50,"m.room.history_visibility":100,"m.room.name":50,"m.room.power_levels":100},"events_default":0,"invite":50,"kick":50,"notifications":{"room":50},"redact":50,"state_default":50,"users":{"@anon-20220512_124253-1:localhost:8800":100},"users_default":0},"depth":3,"hashes":{"sha256":"6yG8CSKC31H0GJlUdSuML3XGZLrIL/hS5aF8n6kLWaU"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":["$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"4chQhuq4KYyJdQDA+ym/SswbSwtQYACvdFUPravvLHSzkJISCQ+6t76Hj90AgWo0TTiOIkJxsgmakkUiWQnYAg"}},"state_key":"","type":"m.room.power_levels"}`,
458+
/* join rules = public */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI","$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E"],"content":{"join_rule":"public"},"depth":4,"hashes":{"sha256":"YqSmumeFsCepwGoOFzcdQoHHM1aY8Ddk9r2XhYOM9wY"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":["$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"C2k2CVlsgXYYDI8XQtwR0su/e9ujrp4hVjZ9zI1f7EEGDV8r6BLR46Y0I858vuA+kkGNiOz+HxutxcZY26OFDw"}},"state_key":"","type":"m.room.join_rules"}`,
459+
/* history vis = shared */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI","$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E"],"content":{"history_visibility":"shared"},"depth":5,"hashes":{"sha256":"+PfJAGh4ZC2h44a91vvIjC1atM9zUqSUhX1P6n1o0hM"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":["$RTbObai9XOoujyGg2pz90sbOZHJ1807sF9ic-mqSGL8"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"Kmvs8/Mh4LllCO5BqJLmq7deRPb8UM07MOK9RYdZYSoqn0vhTOEet2zkPHVi8kFXCQR0Fwlx2qfN5RYSk1d/CA"}},"state_key":"","type":"m.room.history_visibility"}`,
460+
/* aliases */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI","$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E"],"content":{"alias":"#test-20220512_124253-2:localhost:8800"},"depth":6,"hashes":{"sha256":"PRniKNpqRwT8OhqqGLlWohAZwlBAW/Ls+tfzkFwWGWo"},"origin":"localhost:8800","origin_server_ts":1652359375504,"prev_events":["$NfX__HuszWh6DvwNaUZBY0ZYhEoYHRnkF4yJT3dfaww"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"NPZw/aGT+NykXTloRi/1SxalTdJBYmgwiy+SbsQkRGyKkAIqo3JkSgvUBb618ZtGxfJwgvsvQlBu53Tu+SAUCQ"}},"state_key":"","type":"m.room.canonical_alias"}`,
461+
/* second user joins */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$RTbObai9XOoujyGg2pz90sbOZHJ1807sF9ic-mqSGL8","$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI"],"content":{"avatar_url":"","displayname":"anon-20220512_124253-2","membership":"join"},"depth":7,"hashes":{"sha256":"BLec3G4mLa99dr8K1NaVvGh1pDWCOHZd10/mcVc7hMA"},"origin":"localhost:8800","origin_server_ts":1652359375689,"prev_events":["$oUu8vxS4Sikr6tUITnHbnMrW-8fQpJWnLfO0sNB7kW4"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-2:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"gyF1Qph/s1Z94Ne3QI42FsOLjiZs7DbEB6+vAu59XEY5SkoCdm5THqXfrIkbOIcebKcE2HntSjNZOyhGXSETBQ"}},"state_key":"@anon-20220512_124253-2:localhost:8800","type":"m.room.member","unsigned":{}}`,
462+
/* first user kicks second */ `{"auth_events":["$497roGiLBBI5Q2ZKPCoSegSi8f8sSfWJW9JLPGnlGw8","$i2hsVdh5QxBroLmgpo91TxPcHQzd9VnQKgoYwY66SxI","$00fae_PeYsZWsrtXYTSfauzH51QfRVe43ADCUIjtN1E","$Djpz6XCVAF39psdQSwgZdiYyDDwKEPBgs9M6Bmbw11s"],"content":{"displayname":"anon-20220512_124253-2","membership":"leave","reason":"testing"},"depth":8,"hashes":{"sha256":"I9EXGDXtPo6WRVpbr06ppeQYEJtEkx/pxsveNR8pmj0"},"origin":"localhost:8800","origin_server_ts":1652359375738,"prev_events":["$Djpz6XCVAF39psdQSwgZdiYyDDwKEPBgs9M6Bmbw11s"],"prev_state":[],"room_id":"!3CHu7khd0phWyTm5:localhost:8800","sender":"@anon-20220512_124253-1:localhost:8800","signatures":{"localhost:8800":{"ed25519:rhNBRg":"ho7JrdMV3FgFD94grYNmdgS7lbuenE180ATVGYlae14IH7IsS071Vg7HMjihGc+2KXiaM5Njwy9+9VUXvbiJBA"}},"state_key":"@anon-20220512_124253-2:localhost:8800","type":"m.room.member"}`,
463+
}
464+
events := make([]*Event, 0, len(eventJSONs))
465+
for _, eventJSON := range eventJSONs {
466+
event, err := NewEventFromTrustedJSON([]byte(eventJSON), false, RoomVersionV6)
467+
if err != nil {
468+
t.Fatal(err)
469+
}
470+
events = append(events, event)
471+
}
472+
conflicted, unconflicted := separate(events)
473+
t.Log("Unconflicted:")
474+
for _, v := range unconflicted {
475+
t.Log("-", v.EventID())
476+
t.Log(" ", v.Type(), *v.StateKey())
477+
t.Log(" ", string(v.Content()))
478+
}
479+
t.Log("Conflicted:")
480+
for _, v := range conflicted {
481+
t.Log("-", v.EventID())
482+
t.Log(" ", v.Type(), *v.StateKey())
483+
t.Log(" ", string(v.Content()))
484+
}
485+
result := ResolveStateConflictsV2(
486+
conflicted, // conflicted set
487+
unconflicted, // unconflicted set
488+
events, // full auth set
489+
nil, // auth difference
490+
)
491+
t.Log("Resolved:")
492+
for k, v := range result {
493+
t.Log("-", k, v.EventID())
494+
}
495+
found := false
496+
for _, v := range result {
497+
if v.EventID() == events[len(eventJSONs)-1].eventID {
498+
found = true
499+
break
500+
}
501+
}
502+
if !found {
503+
t.Fatal("Expected to find the last event in the resolved set")
504+
}
505+
}
506+
453507
func runStateResolutionV2(t *testing.T, additional []*Event, expected []string) {
454508
input := append(getBaseStateResV2Graph(), additional...)
455509
conflicted, unconflicted := separate(input)

0 commit comments

Comments
 (0)