From 3f31c4d5cad169c7de68d23b5e09f677955c4ff9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 4 Mar 2020 11:37:23 +0000 Subject: [PATCH] Try to establish auth difference for state res v2 --- go.mod | 2 +- go.sum | 2 + roomserver/state/state.go | 142 ++++++++++++++++++++++++++++++++++---- 3 files changed, 133 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 638691e57..03cbd7d8e 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/lib/pq v1.2.0 github.com/matrix-org/dugong v0.0.0-20171220115018-ea0a4690a0d5 github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26 - github.com/matrix-org/gomatrixserverlib v0.0.0-20200303114503-f4a1756605cf + github.com/matrix-org/gomatrixserverlib v0.0.0-20200304113232-f827b01e450c github.com/matrix-org/naffka v0.0.0-20200127221512-0716baaabaf1 github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5 github.com/mattn/go-sqlite3 v2.0.2+incompatible diff --git a/go.sum b/go.sum index ad1379fe6..ccfb79e8a 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/matrix-org/gomatrixserverlib v0.0.0-20200228114906-771d251ca458 h1:xC github.com/matrix-org/gomatrixserverlib v0.0.0-20200228114906-771d251ca458/go.mod h1:FsKa2pWE/bpQql9H7U4boOPXFoJX/QcqaZZ6ijLkaZI= github.com/matrix-org/gomatrixserverlib v0.0.0-20200303114503-f4a1756605cf h1:v1wIWdlIICrOyua4oSsw5jrqTJGmRibIL9Mpo1Ro4uc= github.com/matrix-org/gomatrixserverlib v0.0.0-20200303114503-f4a1756605cf/go.mod h1:FsKa2pWE/bpQql9H7U4boOPXFoJX/QcqaZZ6ijLkaZI= +github.com/matrix-org/gomatrixserverlib v0.0.0-20200304113232-f827b01e450c h1:cQtAr/WkUam8xzWoYaoQVVb+7cqSpjdbG6srtWke1qk= +github.com/matrix-org/gomatrixserverlib v0.0.0-20200304113232-f827b01e450c/go.mod h1:FsKa2pWE/bpQql9H7U4boOPXFoJX/QcqaZZ6ijLkaZI= github.com/matrix-org/naffka v0.0.0-20200127221512-0716baaabaf1 h1:osLoFdOy+ChQqVUn2PeTDETFftVkl4w9t/OW18g3lnk= github.com/matrix-org/naffka v0.0.0-20200127221512-0716baaabaf1/go.mod h1:cXoYQIENbdWIQHt1SyCo6Bl3C3raHwJ0wgVrXHSqf+A= github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5 h1:W7l5CP4V7wPyPb4tYE11dbmeAOwtFQBTW0rf4OonOS8= diff --git a/roomserver/state/state.go b/roomserver/state/state.go index 9b1e0f647..4449cc83c 100644 --- a/roomserver/state/state.go +++ b/roomserver/state/state.go @@ -557,7 +557,6 @@ func (v StateResolution) calculateAndStoreStateAfterManyEvents( func (v StateResolution) calculateStateAfterManyEvents( ctx context.Context, - prevStates []types.StateAtEvent, ) (state []types.StateEntry, algorithm string, conflictLength int, err error) { var combined []types.StateEntry @@ -608,6 +607,19 @@ func (v StateResolution) calculateStateAfterManyEvents( return } +func (v StateResolution) resolveConflicts( + ctx context.Context, + notConflicted, conflicted []types.StateEntry, +) ([]types.StateEntry, error) { + switch v.version { + case StateResolutionAlgorithmV1: + return v.resolveConflictsV1(ctx, notConflicted, conflicted) + case StateResolutionAlgorithmV2: + return v.resolveConflictsV2(ctx, notConflicted, conflicted) + } + return nil, errors.New("unsupported state resolution algorithm") +} + // resolveConflicts resolves a list of conflicted state entries. It takes two lists. // The first is a list of all state entries that are not conflicted. // The second is a list of all state entries that are conflicted @@ -615,7 +627,7 @@ func (v StateResolution) calculateStateAfterManyEvents( // Returns a list that combines the entries without conflicts with the result of state resolution for the entries with conflicts. // The returned list is sorted by state key tuple. // Returns an error if there was a problem talking to the database. -func (v StateResolution) resolveConflicts( +func (v StateResolution) resolveConflictsV1( ctx context.Context, notConflicted, conflicted []types.StateEntry, ) ([]types.StateEntry, error) { @@ -655,18 +667,124 @@ func (v StateResolution) resolveConflicts( } // Resolve the conflicts. - var resolvedEvents []gomatrixserverlib.Event - switch v.version { - case StateResolutionAlgorithmV1: - fmt.Println("using room version 1") - resolvedEvents = gomatrixserverlib.ResolveStateConflicts(conflictedEvents, authEvents) - case StateResolutionAlgorithmV2: - fmt.Println("using room version 2") - resolvedEvents = gomatrixserverlib.ResolveStateConflictsV2(conflictedEvents, nil, authEvents) - default: - return nil, errors.New("unsupported room version") + resolvedEvents := gomatrixserverlib.ResolveStateConflicts(conflictedEvents, authEvents) + + // Map from the full events back to numeric state entries. + for _, resolvedEvent := range resolvedEvents { + entry, ok := eventIDMap[resolvedEvent.EventID()] + if !ok { + panic(fmt.Errorf("Missing state entry for event ID %q", resolvedEvent.EventID())) + } + notConflicted = append(notConflicted, entry) } + // Sort the result so it can be searched. + sort.Sort(shared.StateEntrySorter(notConflicted)) + return notConflicted, nil +} + +// resolveConflicts resolves a list of conflicted state entries. It takes two lists. +// The first is a list of all state entries that are not conflicted. +// The second is a list of all state entries that are conflicted +// A state entry is conflicted when there is more than one numeric event ID for the same state key tuple. +// Returns a list that combines the entries without conflicts with the result of state resolution for the entries with conflicts. +// The returned list is sorted by state key tuple. +// Returns an error if there was a problem talking to the database. +func (v StateResolution) resolveConflictsV2( + ctx context.Context, + notConflicted, conflicted []types.StateEntry, +) ([]types.StateEntry, error) { + + // Load the non-conflicted events + nonConflictedEvents, _, err := v.loadStateEvents(ctx, notConflicted) + if err != nil { + return nil, err + } + + // Load the conflicted events + conflictedEvents, eventIDMap, err := v.loadStateEvents(ctx, conflicted) + if err != nil { + return nil, err + } + + // For each conflicted event, we will add a new set of auth events. Auth + // events may be duplicated across these sets but that's OK. + authSets := make(map[string][]gomatrixserverlib.Event) + var authEvents []gomatrixserverlib.Event + var authDifference []gomatrixserverlib.Event + + // For each conflicted event, let's try and get the needed auth events. + for _, conflictedEvent := range conflictedEvents { + // Work out which auth events we need to load. + key := conflictedEvent.EventID() + needed := gomatrixserverlib.StateNeededForAuth([]gomatrixserverlib.Event{conflictedEvent}) + + // Find the numeric IDs for the necessary state keys. + var neededStateKeys []string + neededStateKeys = append(neededStateKeys, needed.Member...) + neededStateKeys = append(neededStateKeys, needed.ThirdPartyInvite...) + stateKeyNIDMap, err := v.db.EventStateKeyNIDs(ctx, neededStateKeys) + if err != nil { + return nil, err + } + + // Load the necessary auth events. + tuplesNeeded := v.stateKeyTuplesNeeded(stateKeyNIDMap, needed) + var authEntries []types.StateEntry + for _, tuple := range tuplesNeeded { + if eventNID, ok := shared.StateEntryMap(notConflicted).Lookup(tuple); ok { + authEntries = append(authEntries, types.StateEntry{ + StateKeyTuple: tuple, + EventNID: eventNID, + }) + } + } + + // Store the newly found auth events in the auth set for this event. + authSets[key], _, err = v.loadStateEvents(ctx, authEntries) + if err != nil { + return nil, err + } + authEvents = append(authEvents, authSets[key]...) + } + + // This function helps us to work out whether an event exists in one of the + // auth sets. + isInAuthList := func(k string, event gomatrixserverlib.Event) bool { + for _, e := range authSets[k] { + if e.EventID() == event.EventID() { + return true + } + } + return false + } + + // This function works out if an event exists in all of the auth sets. + isInAllAuthLists := func(event gomatrixserverlib.Event) bool { + found := true + for k := range authSets { + found = found && isInAuthList(k, event) + } + return found + } + + // Look through all of the auth events that we've been given and work out if + // there are any events which don't appear in all of the auth sets. If they + // don't then we add them to the auth difference. + for _, event := range authEvents { + if !isInAllAuthLists(event) { + authDifference = append(authDifference, event) + } + } + + // Resolve the conflicts. + resolvedEvents := gomatrixserverlib.ResolveStateConflictsV2( + conflictedEvents, + nonConflictedEvents, + authEvents, + authDifference, + ) + // Map from the full events back to numeric state entries. for _, resolvedEvent := range resolvedEvents { entry, ok := eventIDMap[resolvedEvent.EventID()]