-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Equal method for CrossSigningKey #444
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,9 @@ | |
package fclient | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"slices" | ||
|
||
"github.com/matrix-org/gomatrixserverlib" | ||
"github.com/matrix-org/gomatrixserverlib/spec" | ||
|
@@ -46,6 +48,57 @@ type CrossSigningKey struct { | |
|
||
func (s *CrossSigningKey) isCrossSigningBody() {} // implements CrossSigningBody | ||
|
||
func (s *CrossSigningKey) Equal(other *CrossSigningKey) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to document what the comparison is taking into account, i.e. there can be different equality implementations depending on the use-case. I presume here we care about the fact that a new object needs to be stored in the DB and we test for equality of all fields. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly. The |
||
if s == nil || other == nil { | ||
return false | ||
} | ||
if s.UserID != other.UserID { | ||
return false | ||
} | ||
if len(s.Usage) != len(other.Usage) { | ||
return false | ||
} | ||
|
||
// Make sure the slices are sorted before we compare them. | ||
if !slices.IsSorted(s.Usage) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessarily needed to check, but is slightly faster doing so. |
||
slices.Sort(s.Usage) | ||
} | ||
if !slices.IsSorted(other.Usage) { | ||
slices.Sort(other.Usage) | ||
} | ||
for i := range s.Usage { | ||
if s.Usage[i] != other.Usage[i] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this slice guaranteed to be ordered? If not you might get some false negatives. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point, but does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per spec no, but you want to be forwards compatible, otherwise you might need to prevent such cross signing keys from being uploaded. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 30cbdb4 |
||
return false | ||
} | ||
} | ||
if len(s.Keys) != len(other.Keys) { | ||
return false | ||
} | ||
for k, v := range s.Keys { | ||
if !bytes.Equal(other.Keys[k], v) { | ||
return false | ||
} | ||
} | ||
if len(s.Signatures) != len(other.Signatures) { | ||
return false | ||
} | ||
for k, v := range s.Signatures { | ||
otherV, ok := other.Signatures[k] | ||
if !ok { | ||
return false | ||
} | ||
if len(v) != len(otherV) { | ||
return false | ||
} | ||
for k2, v2 := range v { | ||
if !bytes.Equal(otherV[k2], v2) { | ||
return false | ||
} | ||
} | ||
} | ||
return true | ||
} | ||
|
||
type CrossSigningBody interface { | ||
isCrossSigningBody() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package fclient | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/matrix-org/gomatrixserverlib" | ||
"github.com/matrix-org/gomatrixserverlib/spec" | ||
) | ||
|
||
var tests = []struct { | ||
name string | ||
s *CrossSigningKey | ||
other *CrossSigningKey | ||
expect bool | ||
}{ | ||
{ | ||
name: "NilReceiver_ReturnsFalse", | ||
s: nil, | ||
other: &CrossSigningKey{}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "NilOther_ReturnsFalse", | ||
s: &CrossSigningKey{}, | ||
other: nil, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentUserID_ReturnsFalse", | ||
s: &CrossSigningKey{UserID: "user1"}, | ||
other: &CrossSigningKey{UserID: "user2"}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentUsageLength_ReturnsFalse", | ||
s: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster}}, | ||
other: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster, CrossSigningKeyPurposeSelfSigning}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "UnsortedUsages_ReturnsTrue", | ||
s: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeSelfSigning, CrossSigningKeyPurposeMaster}}, | ||
other: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster, CrossSigningKeyPurposeSelfSigning}}, | ||
expect: true, | ||
}, | ||
{ | ||
name: "UnsortedUsages_ReturnsTrue", | ||
s: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeSelfSigning, CrossSigningKeyPurposeMaster}}, | ||
other: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeSelfSigning, CrossSigningKeyPurposeMaster}}, | ||
expect: true, | ||
}, | ||
{ | ||
name: "DifferentUsageValues_ReturnsFalse", | ||
s: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster}}, | ||
other: &CrossSigningKey{Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeSelfSigning}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentKeysLength_ReturnsFalse", | ||
s: &CrossSigningKey{Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {}}}, | ||
other: &CrossSigningKey{Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {}, "key2": {}}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentKeysValues_ReturnsFalse", | ||
s: &CrossSigningKey{Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {}}}, | ||
other: &CrossSigningKey{Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {1}}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentSignaturesLength_ReturnsFalse", | ||
s: &CrossSigningKey{Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {}}}}, | ||
other: &CrossSigningKey{Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {}}, "sig2": {"key2": {}}}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "DifferentSignaturesValues_ReturnsFalse", | ||
s: &CrossSigningKey{Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {}}}}, | ||
other: &CrossSigningKey{Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {1}}}}, | ||
expect: false, | ||
}, | ||
{ | ||
name: "IdenticalKeys_ReturnsTrue", | ||
s: &CrossSigningKey{ | ||
UserID: "user1", | ||
Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster}, | ||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {}}, | ||
Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {}}}, | ||
}, | ||
other: &CrossSigningKey{ | ||
UserID: "user1", | ||
Usage: []CrossSigningKeyPurpose{CrossSigningKeyPurposeMaster}, | ||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"key1": {}}, | ||
Signatures: map[string]map[gomatrixserverlib.KeyID]spec.Base64Bytes{"sig1": {"key1": {}}}, | ||
}, | ||
expect: true, | ||
}, | ||
} | ||
|
||
func TestCrossSigningKeyEqual(t *testing.T) { | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := tt.s.Equal(tt.other); got != tt.expect { | ||
t.Errorf("Equal() = %v, want %v", got, tt.expect) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func BenchmarkEqual(b *testing.B) { | ||
|
||
for i := 0; i < b.N; i++ { | ||
for _, tt := range tests { | ||
if !tt.s.Equal(tt.other) && tt.expect { | ||
b.Fatal(tt.name, tt.s) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just use
reflect.DeepEqual
here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is does not allocate at all, while
reflect.DeepEqual
does. (not that it matters much on that endpoint)