on master help sign in
Extract gossip predicates, flatten validation, simplify event handling
f4d37c2iko
client/App.fs+44−43
@@ -37,9 +37,11 @@ module App =
37 | Identified of identity: Identity * conn: ConnStatus
37 | Identified of identity: Identity * conn: ConnStatus
38
38
39 type DecryptedEvent =
39 type DecryptedEvent =
40− | IncomingChat of senderPubkey: string * message: ChatMessage
40+ | ChatMsg of ChatMessage
41− | IncomingDeliveryAck of senderPubkey: string * ackIds: string list
41+ | DeliveryAck of ackIds: string list
42− | IncomingProfile of senderPubkey: string * profile: Profile
42+ | ProfileUpdate of Profile
43+
44+ type IncomingEvent = { Sender: string; Event: DecryptedEvent }
43
45
44 type PollReceived = { Events: int; Messages: int; Errors: int; FirstError: string option }
46 type PollReceived = { Events: int; Messages: int; Errors: int; FirstError: string option }
45
47
@@ -83,7 +85,7 @@ module App =
83 | SendFailed of string
85 | SendFailed of string
84 | SendOk
86 | SendOk
85 | DoSaveContact
87 | DoSaveContact
86− | PollResult of events: DecryptedEvent list * status: PollStatus * cursor: int64
88+ | PollResult of events: IncomingEvent list * status: PollStatus * cursor: int64
87 | CopyPubKey
89 | CopyPubKey
88 | DismissError
90 | DismissError
89 | StateLoaded of StateLoadResult
91 | StateLoaded of StateLoadResult
@@ -130,13 +132,10 @@ module App =
130
132
131 let private contactName (contacts: Map<string, Contact>) (pk: string) =
133 let private contactName (contacts: Map<string, Contact>) (pk: string) =
132 let fallback = truncKey pk
134 let fallback = truncKey pk
133− contacts
134− |> Map.tryFind pk
135+ match Map.tryFind pk contacts with
135− |> Option.map (fun c ->
136− if c.Nickname <> fallback then c.Nickname
136+ | Some c when c.Nickname <> fallback -> c.Nickname
137− elif c.DisplayName <> "" then c.DisplayName
137+ | Some c when c.DisplayName <> "" -> c.DisplayName
138− else fallback)
139− |> Option.defaultValue fallback
138+ | _ -> fallback
140
139
141 let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
140 let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
142 messages |> Map.tryFind pk |> Option.defaultValue []
141 messages |> Map.tryFind pk |> Option.defaultValue []
@@ -157,17 +156,18 @@ module App =
157 let private withProfile (p: Profile) (c: Contact) =
156 let private withProfile (p: Profile) (c: Contact) =
158 { c with DisplayName = p.DisplayName; Bio = p.Bio; PhotoBase64 = p.PhotoBase64 }
157 { c with DisplayName = p.DisplayName; Bio = p.Bio; PhotoBase64 = p.PhotoBase64 }
159
158
160− let private applyEvent (m: Model) = function
159+ let private applyEvent (m: Model) (evt: IncomingEvent) =
161− | IncomingChat(senderPubkey, message) ->
160+ match evt.Event with
161+ | ChatMsg message ->
162 { m with
162 { m with
163 Contacts =
163 Contacts =
164− if Map.containsKey senderPubkey m.Contacts then m.Contacts
164+ if Map.containsKey evt.Sender m.Contacts then m.Contacts
165− else Map.add senderPubkey (newContact senderPubkey (truncKey senderPubkey)) m.Contacts
165+ else Map.add evt.Sender (newContact evt.Sender (truncKey evt.Sender)) m.Contacts
166− Messages = m.Messages |> appendMessage senderPubkey message }
166+ Messages = m.Messages |> appendMessage evt.Sender message }
167− | IncomingDeliveryAck(senderPubkey, ackIds) ->
167+ | DeliveryAck ackIds ->
168− { m with Messages = m.Messages |> markDelivered senderPubkey ackIds }
168+ { m with Messages = m.Messages |> markDelivered evt.Sender ackIds }
169− | IncomingProfile(senderPubkey, profile) ->
169+ | ProfileUpdate profile ->
170− { m with Contacts = m.Contacts |> Map.change senderPubkey (Option.map (withProfile profile)) }
170+ { m with Contacts = m.Contacts |> Map.change evt.Sender (Option.map (withProfile profile)) }
171
171
172 let private trySession model =
172 let private trySession model =
173 match model.Auth with
173 match model.Auth with
@@ -363,17 +363,18 @@ module App =
363 }
363 }
364 |> Async.Start)
364 |> Async.Start)
365
365
366+ [<RequireQualifiedAccess>]
366 type private Envelope =
367 type private Envelope =
367 | TextMessage of id: string * body: string
368 | TextMessage of id: string * body: string
368 | DeliveryAck of ackIds: string list
369 | DeliveryAck of ackIds: string list
369 | ProfileMessage of profile: Profile
370 | ProfileMessage of profile: Profile
370
371
371 let private serializeEnvelope = function
372 let private serializeEnvelope = function
372− | TextMessage(id, body) ->
373+ | Envelope.TextMessage(id, body) ->
373 JsonSerializer.Serialize({| ``type`` = "text"; id = id; body = body |})
374 JsonSerializer.Serialize({| ``type`` = "text"; id = id; body = body |})
374− | DeliveryAck ackIds ->
375+ | Envelope.DeliveryAck ackIds ->
375 JsonSerializer.Serialize({| ``type`` = "delivery.ack"; id = Guid.NewGuid().ToString(); ack_ids = ackIds |})
376 JsonSerializer.Serialize({| ``type`` = "delivery.ack"; id = Guid.NewGuid().ToString(); ack_ids = ackIds |})
376− | ProfileMessage profile ->
377+ | Envelope.ProfileMessage profile ->
377 JsonSerializer.Serialize({| ``type`` = "profile"; id = Guid.NewGuid().ToString(); timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); display_name = profile.DisplayName; bio = profile.Bio; photo = profile.PhotoBase64 |})
378 JsonSerializer.Serialize({| ``type`` = "profile"; id = Guid.NewGuid().ToString(); timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); display_name = profile.DisplayName; bio = profile.Bio; photo = profile.PhotoBase64 |})
378
379
379 let private sendEnvelope (session: Session) (recipientHex: string) (envelope: Envelope) =
380 let private sendEnvelope (session: Session) (recipientHex: string) (envelope: Envelope) =
@@ -394,49 +395,49 @@ module App =
394 let root = doc.RootElement
395 let root = doc.RootElement
395 match root.GetProperty("type").GetString() with
396 match root.GetProperty("type").GetString() with
396 | "text" ->
397 | "text" ->
397− Some(TextMessage(root.GetProperty("id").GetString(), root.GetProperty("body").GetString()))
398+ Some(Envelope.TextMessage(root.GetProperty("id").GetString(), root.GetProperty("body").GetString()))
398 | "delivery.ack" ->
399 | "delivery.ack" ->
399 let ackIds = root.GetProperty("ack_ids").EnumerateArray() |> Seq.map _.GetString() |> Seq.toList
400 let ackIds = root.GetProperty("ack_ids").EnumerateArray() |> Seq.map _.GetString() |> Seq.toList
400− Some(DeliveryAck ackIds)
401+ Some(Envelope.DeliveryAck ackIds)
401 | "profile" ->
402 | "profile" ->
402 let displayName = root.GetProperty("display_name").GetString()
403 let displayName = root.GetProperty("display_name").GetString()
403 let bio = tryGetJsonString root "bio" |> Option.defaultValue ""
404 let bio = tryGetJsonString root "bio" |> Option.defaultValue ""
404 let photo = tryGetJsonString root "photo" |> Option.defaultValue ""
405 let photo = tryGetJsonString root "photo" |> Option.defaultValue ""
405− Some(ProfileMessage { DisplayName = displayName; Bio = bio; PhotoBase64 = photo })
406+ Some(Envelope.ProfileMessage { DisplayName = displayName; Bio = bio; PhotoBase64 = photo })
406 | _ -> None
407 | _ -> None
407
408
409+ let private toDecryptedEvent timestamp = function
410+ | Envelope.TextMessage(id, body) ->
411+ ChatMsg
412+ { Id = id; Body = body
413+ Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp)
414+ IsOutgoing = false
415+ Status = DeliveryStatus.Delivered }
416+ | Envelope.DeliveryAck ackIds -> DeliveryAck ackIds
417+ | Envelope.ProfileMessage profile -> ProfileUpdate profile
418+
408 let private decryptEvent (privKey: byte[]) (payload: EventPayload) =
419 let private decryptEvent (privKey: byte[]) (payload: EventPayload) =
409 try
420 try
410 let blob = Crypto.fromHex payload.EncryptedBlob
421 let blob = Crypto.fromHex payload.EncryptedBlob
411−
412 match Crypto.decrypt privKey blob with
422 match Crypto.decrypt privKey blob with
413 | Some(plaintext, senderHex) ->
423 | Some(plaintext, senderHex) ->
414 match parseEnvelope plaintext with
424 match parseEnvelope plaintext with
415− | Some(TextMessage(id, body)) ->
425+ | Some envelope -> Ok { Sender = senderHex; Event = toDecryptedEvent payload.Timestamp envelope }
416− Ok(IncomingChat(senderHex,
417− { Id = id; Body = body
418− Timestamp = DateTimeOffset.FromUnixTimeSeconds(payload.Timestamp)
419− IsOutgoing = false
420− Status = DeliveryStatus.Delivered }))
421− | Some(DeliveryAck ackIds) ->
422− Ok(IncomingDeliveryAck(senderHex, ackIds))
423− | Some(ProfileMessage profile) ->
424− Ok(IncomingProfile(senderHex, profile))
425 | None -> Error "unknown envelope type"
426 | None -> Error "unknown envelope type"
426 | None -> Error "decrypt failed"
427 | None -> Error "decrypt failed"
427 with ex ->
428 with ex ->
428 Error $"parse: {ex.Message}"
429 Error $"parse: {ex.Message}"
429
430
430− let private sendDeliveryAcks (session: Session) (events: DecryptedEvent list) =
431+ let private sendDeliveryAcks (session: Session) (events: IncomingEvent list) =
431 async {
432 async {
432 let chatsBySender =
433 let chatsBySender =
433 events
434 events
434− |> List.choose (function IncomingChat(senderPubkey, msg) -> Some(senderPubkey, msg.Id) | _ -> None)
435+ |> List.choose (fun evt -> match evt.Event with ChatMsg msg -> Some(evt.Sender, msg.Id) | _ -> None)
435 |> List.groupBy fst
436 |> List.groupBy fst
436 |> List.map (fun (sender, pairs) -> sender, List.map snd pairs)
437 |> List.map (fun (sender, pairs) -> sender, List.map snd pairs)
437
438
438 for sender, msgIds in chatsBySender do
439 for sender, msgIds in chatsBySender do
439− try do! sendEnvelope session sender (DeliveryAck msgIds) |> Async.Ignore
440+ try do! sendEnvelope session sender (Envelope.DeliveryAck msgIds) |> Async.Ignore
440 with _ -> ()
441 with _ -> ()
441 }
442 }
442
443
@@ -454,7 +455,7 @@ module App =
454 | CmdSend(session, recipientHex, text, messageId) ->
455 | CmdSend(session, recipientHex, text, messageId) ->
455 asyncCmd (async {
456 asyncCmd (async {
456 try
457 try
457− let! status = sendEnvelope session recipientHex (TextMessage(messageId, text))
458+ let! status = sendEnvelope session recipientHex (Envelope.TextMessage(messageId, text))
458 match messageStatusToError status with
459 match messageStatusToError status with
459 | Some err -> return SendFailed err
460 | Some err -> return SendFailed err
460 | None -> return SendOk
461 | None -> return SendOk
@@ -484,7 +485,7 @@ module App =
484
485
485 do! sendDeliveryAcks session events
486 do! sendDeliveryAcks session events
486
487
487− let chatCount = events |> List.sumBy (function IncomingChat _ -> 1 | _ -> 0)
488+ let chatCount = events |> List.sumBy (fun e -> match e.Event with ChatMsg _ -> 1 | _ -> 0)
488 let status = Received { Events = response.Events.Length; Messages = chatCount; Errors = errors.Length; FirstError = List.tryHead errors }
489 let status = Received { Events = response.Events.Length; Messages = chatCount; Errors = errors.Length; FirstError = List.tryHead errors }
489 return PollResult(events, status, response.Cursor)
490 return PollResult(events, status, response.Cursor)
490 with
491 with
@@ -536,7 +537,7 @@ module App =
536 Cmd.ofEffect (fun _ ->
537 Cmd.ofEffect (fun _ ->
537 async {
538 async {
538 for recipientHex in recipientHexes do
539 for recipientHex in recipientHexes do
539− try do! sendEnvelope session recipientHex (ProfileMessage profile) |> Async.Ignore
540+ try do! sendEnvelope session recipientHex (Envelope.ProfileMessage profile) |> Async.Ignore
540 with _ -> ()
541 with _ -> ()
541 } |> Async.Start)
542 } |> Async.Start)
542
543
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4
@@ -166,11 +166,11 @@
166 </data>
166 </data>
167 <key>Skrepka.dll</key>
167 <key>Skrepka.dll</key>
168 <data>
168 <data>
169− OjZtkz0VbSsZAPLqo/EwqAhSduU=
169+ q+RBTy5zY6JI9PxhrcWbfRvoL18=
170 </data>
170 </data>
171 <key>Skrepka.pdb</key>
171 <key>Skrepka.pdb</key>
172 <data>
172 <data>
173− 288OaA4YI1RkDT0OJaEP/3YzPMc=
173+ NYWrt6DxlK+IUv/o6TjGg/EXk0I=
174 </data>
174 </data>
175 <key>Sodium.Core.dll</key>
175 <key>Sodium.Core.dll</key>
176 <data>
176 <data>
@@ -1393,14 +1393,14 @@
1393 <dict>
1393 <dict>
1394 <key>hash2</key>
1394 <key>hash2</key>
1395 <data>
1395 <data>
1396− GATWVaegaBDKB/p62UIV/s9+8HLonMfOJQDIur7CkO8=
1396+ zGao9cc9uvzxMlIB65ZxAxYwkb+5koDw9sBTW54bflw=
1397 </data>
1397 </data>
1398 </dict>
1398 </dict>
1399 <key>Skrepka.pdb</key>
1399 <key>Skrepka.pdb</key>
1400 <dict>
1400 <dict>
1401 <key>hash2</key>
1401 <key>hash2</key>
1402 <data>
1402 <data>
1403− xqtzoyoQh4wNTDI5bDqxqXQt8uNenDqsbW18iqrMsVs=
1403+ iNV/dAt3PmF/Vbjcomf5Qozaj2oQDkDdwcsenxrta+M=
1404 </data>
1404 </data>
1405 </dict>
1405 </dict>
1406 <key>Sodium.Core.dll</key>
1406 <key>Sodium.Core.dll</key>
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2
@@ -251,14 +251,14 @@
251 <dict>
251 <dict>
252 <key>hash2</key>
252 <key>hash2</key>
253 <data>
253 <data>
254− p4a7Jz8sihEyuUmS+Y+uK2NzEHAlqdpvMP8DN36P+GY=
254+ siIFHcakElVsJvGoDeaziJTyMHVboq2iWuRQU4ojFSs=
255 </data>
255 </data>
256 </dict>
256 </dict>
257 <key>MonoBundle/Skrepka.pdb</key>
257 <key>MonoBundle/Skrepka.pdb</key>
258 <dict>
258 <dict>
259 <key>hash2</key>
259 <key>hash2</key>
260 <data>
260 <data>
261− pmSRC/TvVv/BifxqaC+vH+uL3c1xcWkV07N+dOnKDCw=
261+ oQAGpHBMs/0ySIEif7Oklne5RyNCSo42UIKWMeP3w5I=
262 </data>
262 </data>
263 </dict>
263 </dict>
264 <key>MonoBundle/Sodium.Core.dll</key>
264 <key>MonoBundle/Sodium.Core.dll</key>
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+c2421eb18cd299aa1aa49d08e6aac0aff8c6c2b0")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+dddd66f0bf97c47fa53d616a7c0df16842361244")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−42712c0ef894cb13fa15e474d67e6d25426158a6759d2d78deb6c136776fb18b
1+76de582629b12d03b2d9bbd1d57cf53dfb188fbf4e6496f51ba6519a0f83d3b2
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.fsproj.FileListAbsolute.txt−23
@@ -334,26 +334,3 @@
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
336/Users/iko/Developer/skrepka/client/bin/Debug/net10.0-ios/iossimulator-arm64/LiteDB.dll
336/Users/iko/Developer/skrepka/client/bin/Debug/net10.0-ios/iossimulator-arm64/LiteDB.dll
337−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.stamp
338−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.MauiControls.stamp
339−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/FSharp.Core.stamp
340−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/LiteDB.stamp
341−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stamp
342−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.stamp
343−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stamp
344−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stamp
345−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stamp
346−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stamp
347−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stamp
348−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stamp
349−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.stamp
350−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Options.stamp
351−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Primitives.stamp
352−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stamp
353−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.stamp
354−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stamp
355−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.stamp
356−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Essentials.stamp
357−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Graphics.stamp
358−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Sodium.Core.stamp
359−/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xml
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/c2421eb18cd299aa1aa49d08e6aac0aff8c6c2b0/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/dddd66f0bf97c47fa53d616a7c0df16842361244/*"}}
client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+c2421eb18cd299aa1aa49d08e6aac0aff8c6c2b0")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+dddd66f0bf97c47fa53d616a7c0df16842361244")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−306144550fbf863e667cabc76cbd65bf3da202cb8745fb510e5ee9d5bdd466b3
1+0ee050de77dfa27f84bf6e8bcff1935f31e0cbcc80fd35a7cd647eb5ffb919b9
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/c2421eb18cd299aa1aa49d08e6aac0aff8c6c2b0/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/dddd66f0bf97c47fa53d616a7c0df16842361244/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.

server.knot+15−13
@@ -78,6 +78,10 @@ impl FromJSON GossipEventType where
78 else if s == "offline" then GossipOffline {}
78 else if s == "offline" then GossipOffline {}
79 else GossipUnknown {}
79 else GossipUnknown {}
80
80
81+isKnownEvent = \et -> case et of GossipUnknown {} -> False {}; _ -> True {}
82+isOnlineEvent = \et -> case et of GossipOnline {} -> True {}; _ -> False {}
83+isOfflineEvent = \et -> case et of GossipOffline {} -> True {}; _ -> False {}
84+
81type GossipEvent = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, timestamp: Timestamp, ttl: Int<S>}
85type GossipEvent = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, timestamp: Timestamp, ttl: Int<S>}
82
86
83type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
87type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
@@ -514,14 +518,12 @@ countMessagesFor = \messages toKey t ->
514validGossipEvents : [GossipEvent] -> Timestamp -> [ValidGossipEntry]
518validGossipEvents : [GossipEvent] -> Timestamp -> [ValidGossipEntry]
515validGossipEvents = \events t -> do
519validGossipEvents = \events t -> do
516 e <- events
520 e <- events
517− case e.eventType of
521+ where isKnownEvent e.eventType
518− GossipUnknown {} -> []
519− _ -> do
520− where e.server != serverHostN && e.timestamp <= t + clockSkewTolerance
522+ where e.server != serverHostN && e.timestamp <= t + clockSkewTolerance
521− let ttl = clampTtl e.ttl
523+ let ttl = clampTtl e.ttl
522− let expiresAt = e.timestamp + ttl * 1000
524+ let expiresAt = e.timestamp + ttl * 1000
523− where expiresAt > t
525+ where expiresAt > t
524− yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, expiresAt: expiresAt, timestamp: e.timestamp, ttl: ttl}
526+ yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, expiresAt: expiresAt, timestamp: e.timestamp, ttl: ttl}
525
527
526sendToEach : [ServerName] -> (ServerName -> (a {})) -> a {}
528sendToEach : [ServerName] -> (ServerName -> (a {})) -> a {}
527sendToEach = \servers action ->
529sendToEach = \servers action ->
@@ -895,7 +897,7 @@ serve = \req -> case req of
895 m <- ackedMessages
897 m <- ackedMessages
896 case m.origin of
898 case m.origin of
897 FederatedOrigin {server} -> yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: server}
899 FederatedOrigin {server} -> yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: server}
898− LocalOrigin {} -> []
900+ _ -> []
899 ackedIds <- *ackedIds
901 ackedIds <- *ackedIds
900 let combinedAckedIds = union ackedIds ackedFederated
902 let combinedAckedIds = union ackedIds ackedFederated
901 let evictedEntries = let excessCount = count combinedAckedIds - maxAckedIds in
903 let evictedEntries = let excessCount = count combinedAckedIds - maxAckedIds in
@@ -908,9 +910,9 @@ serve = \req -> case req of
908 when federationEnabled (do
910 when federationEnabled (do
909 sendAcksAndClean pendingAcks
911 sendAcksAndClean pendingAcks
910 retryEvictedAcks evictedEntries pendingAcks)
912 retryEvictedAcks evictedEntries pendingAcks)
911− AckUnauthorized {} -> yield {}
913+ _ -> yield {}
912 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
914 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
913− yield (respond {ok: case result.ack of AckDone _ -> True {}; AckUnauthorized {} -> False {}})
915+ yield (respond {ok: case result.ack of AckDone _ -> True {}; _ -> False {}})
914
916
915 LookupPresence {pubkey, authorization, respond} -> do
917 LookupPresence {pubkey, authorization, respond} -> do
916 t <- now
918 t <- now
@@ -943,8 +945,8 @@ serve = \req -> case req of
943 then rejectFederation t respond
945 then rejectFederation t respond
944 else do
946 else do
945 let allValidEntries = validGossipEvents events t
947 let allValidEntries = validGossipEvents events t
946− let onlineEntries = do e <- allValidEntries; case e.eventType of GossipOnline {} -> yield e; _ -> []
948+ let onlineEntries = do e <- allValidEntries; where isOnlineEvent e.eventType; yield e
947− let trustedOffline = do e <- allValidEntries; case e.eventType of GossipOffline {} -> do where e.server == fromServer; yield e; _ -> []
949+ let trustedOffline = do e <- allValidEntries; where isOfflineEvent e.eventType && e.server == fromServer; yield e
948 let filteredOnline = resolveByTimestamp onlineEntries trustedOffline
950 let filteredOnline = resolveByTimestamp onlineEntries trustedOffline
949 let filteredOffline = resolveByTimestamp trustedOffline onlineEntries
951 let filteredOffline = resolveByTimestamp trustedOffline onlineEntries
950 let deduped = mergeByPubkeyServer [] filteredOnline
952 let deduped = mergeByPubkeyServer [] filteredOnline