Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions eventV1.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ func (e *eventV1) AuthEventIDs() []string {
return result
}

func (e *eventV1) PrevStateEventIDs() []string {
panic("not implemented in this room version")
}

func (e *eventV1) OriginServerTS() spec.Timestamp {
return e.eventFields.OriginServerTS
}
Expand Down
29 changes: 25 additions & 4 deletions eventV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import (

type eventV2 struct {
eventV1
PrevEvents []string `json:"prev_events"`
AuthEvents []string `json:"auth_events"`
PrevEvents []string `json:"prev_events"`
AuthEvents []string `json:"auth_events"`
PrevStateEvents []string `json:"prev_state_events"`
}

func (e *eventV2) PrevEventIDs() []string {
Expand All @@ -27,6 +28,10 @@ func (e *eventV2) AuthEventIDs() []string {
return e.AuthEvents
}

func (e *eventV2) PrevStateEventIDs() []string {
return e.PrevStateEvents
}

// MarshalJSON implements json.Marshaller
func (e *eventV2) MarshalJSON() ([]byte, error) {
if e.eventJSON == nil {
Expand Down Expand Up @@ -185,6 +190,8 @@ func newEventFromUntrustedJSONV2(eventJSON []byte, roomVersion IRoomVersion) (PD
return res, err
}

// This has to exist outside the RoomVersion interface due to init cycles caused
// when you try to MustGetRoomVersion inside CheckFields. Ideally we'd refactor that...
var lenientByteLimitRoomVersions = map[RoomVersion]struct{}{
RoomVersionV1: {},
RoomVersionV2: {},
Expand All @@ -204,10 +211,24 @@ var lenientByteLimitRoomVersions = map[RoomVersion]struct{}{
"org.matrix.msc3667": {},
}

var stateDAGRoomVersions = map[RoomVersion]struct{}{
RoomVersionStateDAGs: {},
}

func CheckFields(input PDU) error { // nolint: gocyclo
if input.AuthEventIDs() == nil || input.PrevEventIDs() == nil {
return errors.New("gomatrixserverlib: auth events and prev events must not be nil")
// don't check auth event IDs in auth DAG rooms as it doesn't exist.
_, stateDAGs := stateDAGRoomVersions[input.Version()]
if !stateDAGs && input.AuthEventIDs() == nil {
return errors.New("gomatrixserverlib: auth events must not be nil")
}
if input.PrevEventIDs() == nil {
return errors.New("gomatrixserverlib: prev events must not be nil")
}
if stateDAGs && input.PrevStateEventIDs() == nil {
// create event should be []
return errors.New("gomatrixserverlib: prev_state_events must not be nil")
}

if l := len(input.JSON()); l > maxEventLength {
return EventValidationError{
Code: EventValidationTooLarge,
Expand Down
2 changes: 1 addition & 1 deletion eventV2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func TestCheckFields(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for roomVersion := range roomVersionMeta {
if roomVersion == RoomVersionPseudoIDs {
if roomVersion == RoomVersionPseudoIDs || roomVersion == RoomVersionStateDAGs {
continue
}
t.Run(tt.name+"-"+string(roomVersion), func(t *testing.T) {
Expand Down
12 changes: 12 additions & 0 deletions event_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type EventBuilder struct {
// The events needed to authenticate this event. This can be
// either []eventReference for room v1/v2, and []string for room v3 onwards.
AuthEvents interface{} `json:"auth_events"`
// Previous state events, for MSC4242 state dag rooms only. Pointer to allow [] to encode for the create event.
PrevStateEvents *[]string `json:"prev_state_events,omitempty"`
// The event ID of the event being redacted if this event is a "m.room.redaction".
Redacts string `json:"redacts,omitempty"`
// The depth of the event, This should be one greater than the maximum depth of the previous events.
Expand Down Expand Up @@ -145,6 +147,10 @@ func (eb *EventBuilder) Build(
if eb.version == nil {
return nil, fmt.Errorf("EventBuilder.Build: unknown version, did you create this via NewEventBuilder?")
}
isStateDAGs := eb.version.StateDAGs()
if !isStateDAGs && eb.PrevStateEvents != nil {
return nil, fmt.Errorf("prev_state_events can only be set on RoomVersionStateDAGs")
}

eventFormat := eb.version.EventFormat()
eventIDFormat := eb.version.EventIDFormat()
Expand Down Expand Up @@ -213,6 +219,12 @@ func (eb *EventBuilder) Build(
}
}

if isStateDAGs {
if eventJSON, err = sjson.DeleteBytes(eventJSON, "auth_events"); err != nil {
return
}
}

if eventJSON, err = addContentHashesToEvent(eventJSON); err != nil {
return
}
Expand Down
36 changes: 35 additions & 1 deletion eventversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type IRoomVersion interface {
CheckCreateEvent(event PDU, sender spec.UserID, knownRoomVersion KnownRoomVersionFunc) error
DomainlessRoomIDs() bool
PrivilegedCreators() bool
StateDAGs() bool
}

type KnownRoomVersionFunc func(RoomVersion) bool
Expand Down Expand Up @@ -69,6 +70,7 @@ const (
RoomVersionV12 RoomVersion = "12"
RoomVersionPseudoIDs RoomVersion = "org.matrix.msc4014"
RoomVersionHydra RoomVersion = "org.matrix.hydra.11"
RoomVersionStateDAGs RoomVersion = "org.matrix.msc4242.12"
)

// Event format constants.
Expand Down Expand Up @@ -417,6 +419,31 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
domainlessRoomID: true,
privilegedCreators: true,
},
// Based on v12
RoomVersionStateDAGs: RoomVersionImpl{
ver: RoomVersionStateDAGs,
stable: true,
stateResAlgorithm: StateResV2_1,
eventFormat: EventFormatV2,
eventIDFormat: EventIDFormatV3,
redactionAlgorithm: redactEventJSONVStateDAGs,
signatureValidityCheckFunc: StrictValiditySignatureCheck,
canonicalJSONCheck: verifyEnforcedCanonicalJSON,
checkPowerLevelEvent: checkPowerLevelEventV3,
restrictedJoinServernameFunc: extractAuthorisedViaServerName,
checkRestrictedJoin: checkRestrictedJoin,
parsePowerLevelsFunc: parseIntegerPowerLevels,
checkKnockingAllowedFunc: checkKnocking,
checkRestrictedJoinAllowedFunc: allowRestrictedJoins,
checkCreateEvent: checkCreateEventV3,
// v3 versions relax the room ID check as the room ID has no domain now.
newEventFromUntrustedJSONFunc: newEventFromUntrustedJSONV3,
newEventFromTrustedJSONFunc: newEventFromTrustedJSONV3,
newEventFromTrustedJSONWithEventIDFunc: newEventFromTrustedJSONWithEventIDV3,
domainlessRoomID: true,
privilegedCreators: true,
stateDAGs: true,
},
}

// RoomVersions returns information about room versions currently
Expand Down Expand Up @@ -501,7 +528,9 @@ type RoomVersionImpl struct {
// whether auth_events should include the create event
domainlessRoomID bool
// creators have infinite PL
privilegedCreators bool
privilegedCreators bool
// Events form two graphs, a state DAG and an event DAG.
stateDAGs bool
checkRestrictedJoin func(ctx context.Context, localServerName spec.ServerName, roomQuerier RestrictedRoomJoinQuerier, roomID spec.RoomID, senderID spec.SenderID, privilegedCreators bool) (string, error)
restrictedJoinServernameFunc func(content []byte) (spec.ServerName, error)
checkRestrictedJoinAllowedFunc func() error
Expand All @@ -525,6 +554,10 @@ func (v RoomVersionImpl) DomainlessRoomIDs() bool {
return v.domainlessRoomID
}

func (v RoomVersionImpl) StateDAGs() bool {
return v.stateDAGs
}

func (v RoomVersionImpl) PrivilegedCreators() bool {
return v.privilegedCreators
}
Expand Down Expand Up @@ -630,6 +663,7 @@ func (v RoomVersionImpl) NewEventBuilderFromProtoEvent(pe *ProtoEvent) *EventBui
eb.StateKey = pe.StateKey
eb.Type = pe.Type
eb.Unsigned = pe.Unsigned
eb.PrevStateEvents = pe.PrevStateEvents
return eb
}

Expand Down
8 changes: 8 additions & 0 deletions fclient/federationtypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ type MissingEvents struct {
EarliestEvents []string `json:"earliest_events"`
// The event IDs to retrieve the previous events for.
LatestEvents []string `json:"latest_events"`
// If true, walks the state DAG. Only for MSC4242 State DAG rooms.
StateDAG bool `json:"org.matrix.msc4242.state_dag"`
}

// A RespMissingEvents is the content of a response to GET /_matrix/federation/v1/get_missing_events/{roomID}
Expand Down Expand Up @@ -269,6 +271,8 @@ type RespSendJoin struct {
StateEvents gomatrixserverlib.EventJSONs `json:"state"`
// A list of events needed to authenticate the state events.
AuthEvents gomatrixserverlib.EventJSONs `json:"auth_chain"`
// MSC4242: The entire state DAG for the room
StateDAG gomatrixserverlib.EventJSONs `json:"state_dag"`
// The server that originated the event.
Origin spec.ServerName `json:"origin"`
// The returned join event from the remote server. Used for restricted joins,
Expand Down Expand Up @@ -318,6 +322,9 @@ func (r RespSendJoin) MarshalJSON() ([]byte, error) {
if len(fields.StateEvents) == 0 {
fields.StateEvents = gomatrixserverlib.EventJSONs{}
}
if len(r.StateDAG) > 0 {
fields.StateDAG = r.StateDAG
}

if !r.MembersOmitted {
return json.Marshal(fields)
Expand Down Expand Up @@ -350,6 +357,7 @@ type RespMakeKnock struct {
type respSendJoinFields struct {
StateEvents gomatrixserverlib.EventJSONs `json:"state"`
AuthEvents gomatrixserverlib.EventJSONs `json:"auth_chain"`
StateDAG gomatrixserverlib.EventJSONs `json:"state_dag,omitempty"`
Origin spec.ServerName `json:"origin"`
Event spec.RawJSON `json:"event,omitempty"`
}
Expand Down
4 changes: 3 additions & 1 deletion join.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ type ProtoEvent struct {
PrevEvents interface{} `json:"prev_events"`
// The events needed to authenticate this event. This can be
// either []eventReference for room v1/v2, and []string for room v3 onwards.
AuthEvents interface{} `json:"auth_events"`
AuthEvents interface{} `json:"auth_events,omitempty"`
// Previous state events, for MSC4242 state dag rooms. Pointer to allow [] to encode for the create event.
PrevStateEvents *[]string `json:"prev_state_events,omitempty"`
// The event ID of the event being redacted if this event is a "m.room.redaction".
Redacts string `json:"redacts,omitempty"`
// The depth of the event, This should be one greater than the maximum depth of the previous events.
Expand Down
1 change: 1 addition & 0 deletions pdu.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type PDU interface {
JSON() []byte // TODO: remove
AuthEventIDs() []string // TODO: remove
ToHeaderedJSON() ([]byte, error) // TODO: remove
PrevStateEventIDs() []string
// IsSticky returns true if the event is *currently* considered "sticky" given the received time.
// Sticky events are annotated as sticky and carry strong delivery guarantees to clients (and
// therefore servers). `received` should be specified as the time the event was received by the
Expand Down
33 changes: 32 additions & 1 deletion redactevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func redactEventJSONV1(eventJSON []byte) ([]byte, error) {
}

type unredactableEvent interface {
*unredactableEventFieldsV1 | *unredactableEventFieldsV2
*unredactableEventFieldsV1 | *unredactableEventFieldsV2 | *unredactableEventFieldsVStateDAGs
GetType() string
GetContent() map[string]interface{}
SetContent(map[string]interface{})
Expand Down Expand Up @@ -172,3 +172,34 @@ func redactEventJSON[T unredactableEvent](eventJSON []byte, unredactableEvent T,
// Return the redacted event encoded as JSON.
return json.Marshal(&unredactableEvent)
}

type unredactableEventFieldsVStateDAGs struct {
EventID spec.RawJSON `json:"event_id,omitempty"`
Type string `json:"type"`
RoomID spec.RawJSON `json:"room_id,omitempty"`
Sender spec.RawJSON `json:"sender,omitempty"`
StateKey spec.RawJSON `json:"state_key,omitempty"`
Content map[string]interface{} `json:"content"`
Hashes spec.RawJSON `json:"hashes,omitempty"`
Signatures spec.RawJSON `json:"signatures,omitempty"`
Depth spec.RawJSON `json:"depth,omitempty"`
PrevEvents spec.RawJSON `json:"prev_events,omitempty"`
PrevStateEvents spec.RawJSON `json:"prev_state_events,omitempty"`
OriginServerTS spec.RawJSON `json:"origin_server_ts,omitempty"`
}

func (u *unredactableEventFieldsVStateDAGs) GetType() string {
return u.Type
}

func (u *unredactableEventFieldsVStateDAGs) GetContent() map[string]interface{} {
return u.Content
}

func (u *unredactableEventFieldsVStateDAGs) SetContent(content map[string]interface{}) {
u.Content = content
}

func redactEventJSONVStateDAGs(eventJSON []byte) ([]byte, error) {
return redactEventJSON(eventJSON, &unredactableEventFieldsVStateDAGs{}, unredactableContentFieldsV5)
}
Loading