Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
NeuroLedgerClient.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Reflection;
5using System.Text;
6using System.Threading.Tasks;
7using System.Xml;
8using Waher.Content;
12using Waher.Events;
33
35{
40 {
44 public const string NeuroLedgerNamespace = "http://waher.se/NL";
45
49 public const int DefaultTimeoutMs = 2 * 60 * 1000;
50
51 private static readonly BlockAdded blockAddedRef = new BlockAdded();
52 private Cache<string, PeerStatus> peerStatus;
53 private readonly Dictionary<string, NodeStatus> nodeStatus = new Dictionary<string, NodeStatus>();
54 private readonly Dictionary<string, bool> loading = new Dictionary<string, bool>();
55 private readonly BlockResource blockResource;
56 private readonly HTTP.HttpResource blockListResource;
57 private readonly HTTP.HttpResource multiGetResource;
58 private readonly HTTP.HttpServer webServer;
59 private readonly EndpointSecurity e2eEncryption;
60 private readonly bool internalScheduler;
61 private readonly ILedgerExternalEvents externalEvents;
62 private NeuroLedgerProvider provider;
63 private PepClient pepClient = null;
64 private Scheduler scheduler;
65
73 public NeuroLedgerClient(XmppClient Client, EndpointSecurity E2eEncryption, HTTP.HttpServer WebServer, NeuroLedgerProvider Provider)
74 : base(Client)
75 {
76 this.provider = Provider;
77 this.webServer = WebServer;
78 this.e2eEncryption = E2eEncryption;
79 this.externalEvents = Provider.ExternalEvents;
80
81 this.peerStatus = new Cache<string, PeerStatus>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromDays(1), true);
82
83 if (Types.TryGetModuleParameter("Scheduler", out object Obj) && Obj is Scheduler Scheduler)
84 {
85 this.scheduler = Scheduler;
86 this.internalScheduler = false;
87 }
88 else
89 {
90 this.scheduler = new Scheduler();
91 this.internalScheduler = true;
92 }
93
94 this.webServer.Register(this.blockResource = new BlockResource("/NL/B", this.provider, this.client, this, this.webServer, "User"));
95 this.blockListResource = this.webServer.Register(new BlockListResource("/NL/L", this.client, this.webServer, "User"));
96 this.multiGetResource = this.webServer.Register(new MultiGetResource("/NL/MG", this.client, this.webServer, "User"));
97
98 Client.RegisterIqGetHandler("getBlockReferences", NeuroLedgerNamespace, this.GetBlockReferences, true);
99
100 foreach (IXmppExtension Extension in Client.Extensions)
101 {
102 if (Extension is PepClient PepClient)
103 {
104 this.pepClient = PepClient;
105
106 this.pepClient.RegisterHandler(typeof(BlockAdded), this.BlockAddedReceived);
107 this.pepClient.RegisterHandler(typeof(BlockDeleted), this.BlockDeletedReceived);
108 break;
109 }
110 }
111
112 Provider.BlockAdded += this.BlockAdded;
113 Provider.BlockDeleted += this.BlockDeleted;
114
115 this.client.OnStateChanged += this.Client_OnStateChanged;
116 this.client.OnPresence += this.Client_OnPresence;
117
118 if (this.client.State == XmppState.Connected)
119 {
120 Task _ = this.ResendPresences();
121 }
122 }
123
127 public NeuroLedgerProvider Provider => this.provider;
128
129 private async Task ResendPresences()
130 {
131 foreach (RosterItem Item in this.client.Roster)
132 {
133 foreach (PresenceEventArgs e in Item.Resources)
134 await this.Client_OnPresence(this.client, e);
135 }
136 }
137
138 private async Task Client_OnPresence(object Sender, PresenceEventArgs e)
139 {
140 PeerStatus PeerStatus = await this.GetPeerStatus(e);
141 if (PeerStatus is null)
142 return;
143
144 Task _ = Task.Run(() => this.SynchronizePeer(PeerStatus, false, null));
145 }
146
154 public async Task<string> SynchronizePeer(PeerStatus PeerStatus, bool StartFromBeginning, CallbackAsync<SynchronizationStatus> Callback)
155 {
156 try
157 {
159 return "Peer not a Neuro-Ledger node.";
160
162
163 lock (this.nodeStatus)
164 {
165 if (!this.nodeStatus.TryGetValue(PeerStatus.BareJid, out NodeStatus))
166 NodeStatus = null;
167 }
168
169 if (NodeStatus is null)
170 {
171 NodeStatus = await Database.FindFirstDeleteRest<NodeStatus>(new FilterFieldEqualTo("BareJid", PeerStatus.BareJid));
172 if (NodeStatus is null)
173 {
174 NodeStatus = new NodeStatus()
175 {
176 BareJid = PeerStatus.BareJid
177 };
178
180 }
181 }
182
183 NodeStatus ToDelete = null;
184
185 lock (this.nodeStatus)
186 {
187 if (this.nodeStatus.TryGetValue(PeerStatus.BareJid, out NodeStatus NodeStatus2))
188 {
189 if (NodeStatus2 != NodeStatus)
190 {
191 ToDelete = NodeStatus;
192 NodeStatus = NodeStatus2;
193 }
194 }
195 else
196 this.nodeStatus[PeerStatus.BareJid] = NodeStatus;
197 }
198
199 if (!(ToDelete is null))
200 await Database.Delete(ToDelete);
201
202 if (StartFromBeginning && !string.IsNullOrEmpty(NodeStatus.LastBlockId))
203 {
204 NodeStatus.LastBlockId = string.Empty;
206 }
207
208 return await this.SynchBlocksAsync(PeerStatus, NodeStatus, Callback, Callback is null, false);
209 }
210 catch (Exception ex)
211 {
212 return ex.Message;
213 }
214 }
215
221 public async Task<PeerStatus> GetPeerStatus(PresenceEventArgs e)
222 {
223 if (!e.IsOnline || e.FromBareJID == e.Client.BareJID || e.FromBareJID == e.From || this.peerStatus is null)
224 return null;
225
227 return null;
228
229 if (!this.peerStatus.TryGetValue(e.From, out PeerStatus PeerStatus))
230 {
231 PeerStatus = new PeerStatus()
232 {
233 FullJid = e.From,
234 BareJid = e.FromBareJID
235 };
236
237 this.peerStatus[e.From] = PeerStatus;
238 }
239
243 {
244 return null;
245 }
246
250 {
251 PeerStatus.RequestingCapabilitiesFunction = e.EntityCapabilityHashFunction;
252 PeerStatus.RequestingCapabilitiesNode = e.EntityCapabilityNode;
253 PeerStatus.RequestingCapabilitiesVersion = e.EntityCapabilityVersion;
254
255 try
256 {
257 string Node = e.EntityCapabilityNode + "#" + e.EntityCapabilityVersion;
259
260 string[] Features = new string[e2.Features.Count];
261 e2.Features.Keys.CopyTo(Features, 0);
262
263 PeerStatus.CapabilitiesFunction = e.EntityCapabilityHashFunction;
264 PeerStatus.CapabilitiesNode = e.EntityCapabilityNode;
265 PeerStatus.CapabilitiesVersion = e.EntityCapabilityVersion;
266 PeerStatus.Features = Features;
267 PeerStatus.IsNeuroLedger = e2.Features.ContainsKey(NeuroLedgerNamespace);
268 }
269 finally
270 {
271 PeerStatus.RequestingCapabilitiesFunction = string.Empty;
272 PeerStatus.RequestingCapabilitiesNode = string.Empty;
273 PeerStatus.RequestingCapabilitiesVersion = string.Empty;
274 }
275 }
276
277 return PeerStatus;
278 }
279
285 {
286 lock (PeerStatus)
287 {
289 {
290 PeerStatus.IsSynchronizing = false;
292 }
293 }
294 }
295
296 private async Task<string> SynchBlocksAsync(PeerStatus PeerStatus, NodeStatus NodeStatus, CallbackAsync<SynchronizationStatus> Callback,
297 bool IgnoreIfRunning, bool Repopulate)
298 {
299 int SynchCounter;
300
301 lock (PeerStatus)
302 {
304 {
305 if (IgnoreIfRunning)
306 return string.Empty;
307 }
308
309 PeerStatus.IsSynchronizing = true;
310 SynchCounter = ++PeerStatus.SynchCounter;
311 }
312
314 DateTime LastReported = DateTime.Now;
315 int MaxCount = 1000;
316 bool More;
317
318 try
319 {
320 List<BlockReference> Blocks = new List<BlockReference>();
321 Dictionary<string, bool> Collections = new Dictionary<string, bool>();
322 string LastId = NodeStatus.LastBlockId;
323 List<KeyValuePair<BlockReference, string>> Bulk = new List<KeyValuePair<BlockReference, string>>();
324 ulong BulkSize = 0;
325 int Count;
326
327 do
328 {
329 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
330
331 XmlElement Result = await this.GetBlockList(NodeStatus.BareJid, LastId, MaxCount);
332
333 if (Result is null || Result.LocalName != "blockReferences" || Result.NamespaceURI != NeuroLedgerNamespace)
334 break;
335
336 Count = 0;
337 foreach (XmlNode N in Result.ChildNodes)
338 {
339 if (PeerStatus.SynchCounter != SynchCounter || this.ClientDisposed)
340 return null;
341
342 if (N is XmlElement E2 && E2.LocalName == "ref" && E2.NamespaceURI == NeuroLedgerNamespace)
343 {
344 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
345
346 string Id = XML.Attribute(E2, "id");
347 BlockAdded Event = (BlockAdded)blockAddedRef.Parse(E2);
348
349 BlockReference Ref = await this.GetReference(Event, Status, !Repopulate);
350 if (!(Ref is null))
351 {
352 Collections[Ref.Collection] = true;
353
354 Bulk.Add(new KeyValuePair<BlockReference, string>(Ref, Event.Url));
355 BulkSize += Ref.Bytes;
356
357 if (Bulk.Count >= 100 || BulkSize >= 16 * 1024 * 1024)
358 {
359 await this.BulkLoad(PeerStatus, Bulk.ToArray(), LastReported, Status, Callback, false, Blocks);
360 Bulk.Clear();
361 BulkSize = 0;
362 }
363 }
364
365 Count++;
366 LastId = Id;
367 }
368 }
369
370 More = Count >= MaxCount;
371 }
372 while (More && !this.ClientDisposed);
373
374 if (Bulk.Count > 0)
375 await this.BulkLoad(PeerStatus, Bulk.ToArray(), LastReported, Status, Callback, false, Blocks);
376
377 if (Blocks.Count > 0)
378 {
379 Blocks.Reverse();
380
381 List<BlockReference> Subset = new List<BlockReference>();
382 List<ObjectState> ObjectsInBlock = new List<ObjectState>();
383 bool Cleared;
384
385 foreach (string Collection in Collections.Keys)
386 {
387 foreach (BlockReference Ref in Blocks)
388 {
389 if (Ref.Collection != Collection)
390 continue;
391
392 if (Ref.Status != BlockStatus.Valid || Ref.AccessDenied)
393 continue;
394
395 Subset.Add(Ref);
396 }
397
398 if (Subset.Count == 0)
399 continue;
400
401 IPersistentDictionary PersistentDictionary = await Database.GetDictionary(Collection);
402 await PersistentDictionary.ClearAsync();
403
404 CachedStringDictionary ObjectEvents = new CachedStringDictionary(100000, PersistentDictionary);
405 try
406 {
407 await Database.StartBulk();
408 try
409 {
410 Cleared = false;
411 Count = 0;
412
413 foreach (BlockReference Ref in Subset)
414 {
415 using (BlockEnumerator TempBlockEnumerator = new BlockEnumerator(Ref, this.provider))
416 {
417 using (ObjectEnumerator<GenericObject> e = await ObjectEnumerator<GenericObject>.Create(TempBlockEnumerator, this.provider))
418 {
419 while (await e.MoveNextAsync())
420 ObjectsInBlock.Add(new ObjectState(e.CurrentEntry.Type, e.Current));
421 }
422
423 ObjectsInBlock.Reverse();
424
425 foreach (ObjectState ObjectState in ObjectsInBlock)
426 {
427 Status.Events++;
428 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
429
430 if (ObjectState.Type == EntryType.Clear)
431 {
432 Cleared = true;
433 break;
434 }
435
436 try
437 {
438 string Key = ObjectState.Object.ObjectId.ToString();
439
440 if (await ObjectEvents.ContainsKeyAsync(Key))
441 continue; // Only latest is of importance.
442
443 await ObjectEvents.AddAsync(Key, ObjectState);
444 }
445 catch (Exception ex)
446 {
447 Log.Error("Unable to enumerate objects in block properly when repairing collection:\r\n\r\n" +
448 ex.Message, TempBlockEnumerator.Current.FileName, string.Empty, string.Empty,
449 EventLevel.Major, string.Empty, string.Empty, Log.CleanStackTrace(ex.StackTrace),
450 new KeyValuePair<string, object>("Collection", Collection));
451
452 break;
453 }
454
455 if (++Count >= 100)
456 {
457 await Database.EndBulk();
458 await Database.StartBulk();
459 Count = 0;
460 }
461 }
462
463 ObjectsInBlock.Clear();
464 }
465
466 if (Cleared)
467 break;
468 }
469
470 if (Cleared)
471 {
472 await Database.Provider.Clear(Collection);
473 this.externalEvents?.RaiseCollectionCleared(Collection);
474 }
475 }
476 finally
477 {
478 await Database.EndBulk();
479 }
480
481 Tuple<uint, uint, uint, uint> Counts = await this.provider.Process(ObjectEvents, Collection);
482
483 Status.ObjectsAdded += Counts.Item1;
484 Status.ObjectsUpdated += Counts.Item2;
485 Status.ObjectsDeleted += Counts.Item3;
486 Status.ObjectErrors += Counts.Item4;
487
488 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
489 }
490 finally
491 {
492 await ObjectEvents.ClearAsync();
493 ObjectEvents.DeleteAndDispose();
494
495 Subset.Clear();
496 }
497 }
498
499 NodeStatus.LastBlockId = LastId;
501 }
502
503 foreach (BlockReference Ref in await Database.Find<BlockReference>(new FilterAnd(
504 new FilterFieldEqualTo("FileName", string.Empty),
505 new FilterFieldEqualTo("Creator", NodeStatus.BareJid),
506 new FilterFieldEqualTo("AccessDenied", false))))
507 {
508 if (Ref.Sources is null)
509 continue;
510
511 foreach (string Source in Ref.Sources)
512 {
513 if (PeerStatus.SynchCounter != SynchCounter || this.ClientDisposed)
514 return null;
515
516 try
517 {
518 Uri Uri = new Uri(Source);
519 if (Uri.Authority == NodeStatus.BareJid)
520 {
521 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
522 await this.RetrieveBlock(Ref, Source, Status, true);
523 break;
524 }
525 }
526 catch (Exception ex)
527 {
528 Log.Exception(ex);
529 Status.IncBlockError(NodeStatus.BareJid, ex.Message + " (1)");
530 }
531 }
532 }
533
534 foreach (BlockReference Ref in await Database.Find<BlockReference>(new FilterAnd(
535 new FilterFieldEqualTo("Creator", NodeStatus.BareJid),
536 new FilterFieldEqualTo("AccessDenied", false),
537 new FilterFieldEqualTo("Unpacked", false))))
538 {
539 if (Ref.Sources is null)
540 continue;
541
542 foreach (string Source in Ref.Sources)
543 {
544 if (PeerStatus.SynchCounter != SynchCounter || this.ClientDisposed)
545 return null;
546
547 try
548 {
549 Uri Uri = new Uri(Source);
550 if (Uri.Authority == NodeStatus.BareJid)
551 {
552 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
553 await this.RetrieveBlock(Ref, Source, Status, true);
554 break;
555 }
556 }
557 catch (Exception ex)
558 {
559 Log.Exception(ex);
560 Status.IncBlockError(NodeStatus.BareJid, ex.Message + " (2)");
561 }
562 }
563 }
564 }
565 catch (Exception ex)
566 {
567 Log.Exception(ex);
568 Status.IncBlockError(NodeStatus.BareJid, ex.Message + " (3)");
569 }
570 finally
571 {
572 if (SynchCounter == PeerStatus.SynchCounter)
573 {
574 lock (PeerStatus)
575 {
576 PeerStatus.IsSynchronizing = false;
577 }
578
579 Status.Done = true;
580 await this.UpdateStatus(DateTime.MinValue, Status, Callback);
581 }
582 }
583
584 return string.Empty;
585 }
586
587 private async Task BulkLoad(PeerStatus PeerStatus, KeyValuePair<BlockReference, string>[] Resources,
588 DateTime LastReported, SynchronizationStatus Status, CallbackAsync<SynchronizationStatus> Callback,
589 bool Unpack, List<BlockReference> Blocks)
590 {
591 string Msg;
592 int Processed = 0;
593
594 try
595 {
596 StringBuilder Csv = new StringBuilder();
597
598 foreach (KeyValuePair<BlockReference, string> Item in Resources)
599 {
600 await this.client.Information("Retrieving block (" + Item.Key.Collection + "): " + Item.Value);
601
602 Csv.Append(Item.Value);
603 Csv.AppendLine(",application/octet-stream");
604 }
605
606 Uri Uri = new Uri("httpx://" + PeerStatus.BareJid + "/NL/MG");
607 KeyValuePair<byte[], string> Response = await InternetContent.PostAsync(Uri,
608 Encoding.UTF8.GetBytes(Csv.ToString()), "text/csv; charset=utf-8",
609 new KeyValuePair<string, string>("Accept", "multipart/mixed"));
610
611 object Obj = await InternetContent.DecodeAsync(Response.Value, Response.Key, Uri);
612
613 if (Obj is MixedContent Result)
614 {
615 int i = 0;
616 int c = Resources.Length;
617
618 foreach (EmbeddedContent Content in Result.Content)
619 {
620 if (i >= c)
621 break;
622
623 LastReported = await this.UpdateStatus(LastReported, Status, Callback);
624 Processed++;
625
626 KeyValuePair<BlockReference, string> P = Resources[i++];
627 BlockReference Ref = P.Key;
628 string Url = P.Value;
629
630 if (!int.TryParse(Content.Description, out int StatusCode))
631 {
632 Status?.IncBlockError(Url, "Invalid content description: " + Content.Description);
633 continue;
634 }
635
636 if (StatusCode == 200)
637 {
638 byte[] Block = Content.TransferDecoded;
639 await this.client.Information("Block retrieved (" + Ref.Collection + "): " + Url);
640
641 if (this.provider is null)
642 {
643 Log.Warning("Bulk loading of blocks aborted. Neuro-Ledger closed.");
644 return;
645 }
646
647 using (MemoryStream ms = new MemoryStream(Block))
648 {
649 await this.provider.AddBlockFile(ms, P.Key);
650 }
651
652 await this.client.Information("Block added (" + Ref.Collection + "): " + Url);
653
654 if (Ref.AccessDenied)
655 {
656 Ref.AccessDenied = false;
657 await Database.Update(Ref);
658 }
659
660 if (!(Status is null))
661 {
662 Status.NrLoaded++;
663 Status.LoadedBytes += Ref.Bytes;
664 }
665
666 if (Unpack)
667 await this.UnpackObjects(Ref, Status);
668
669 Blocks.Add(Ref);
670 }
671 else if (StatusCode == HTTP.ForbiddenException.Code ||
672 StatusCode == HTTP.FailedDependencyException.Code)
673 {
674 await this.client.Information("Access to block denied (" + Ref.Collection + "): " + Url + ". Cause: " + Content.Decoded?.ToString());
675
676 if (!Ref.AccessDenied)
677 {
678 Ref.AccessDenied = true;
679 await Database.Update(Ref);
680 }
681
682 if (!(Status is null))
683 Status.NrDenied++;
684 }
685 else if (StatusCode == HTTP.NotFoundException.Code)
686 {
687 await this.client.Warning(Msg = "Block not found (" + Ref.Collection + "): " + Url + ". Cause: " + Content.Decoded?.ToString());
688
689 if (Ref.Creator == this.provider.ExternalIdentity &&
690 Content.Decoded is string ErrorMsg &&
692 {
693 await Database.Delete(Ref);
694
695 if (!(Status is null))
696 Status.NrBlocks--;
697 }
698 else
699 Status?.IncBlockError(Url, Msg);
700 }
701 else if (!this.ClientDisposed && !this.client.Disposed)
702 {
703 await this.client.Warning(Msg = "Unable to retrieve block. Trying again later. Error reported: " + StatusCode.ToString() + ", " + Content.Decoded?.ToString());
704 this.scheduler?.Add(DateTime.Now.AddHours(1), this.RetryProcessing, new object[] { Ref, Url });
705
706 Status?.IncBlockError(Url, Msg);
707 }
708 }
709 }
710 }
711 catch (Exception ex)
712 {
713 await this.client.Error(Msg = "Unable to retrieve blocks. Error reported: " + ex.Message);
714 Status?.IncBlockError(PeerStatus.BareJid, Msg, (uint)(Resources.Length - Processed));
715 }
716 }
717
718 private async Task<XmlElement> GetBlockList(string Jid, string LastBlockId, int MaxCount)
719 {
720 StringBuilder Url = new StringBuilder();
721
722 Url.Append("httpx://");
723 Url.Append(Jid);
724 Url.Append("/NL/L?Max=");
725 Url.Append(MaxCount.ToString());
726
727 if (!string.IsNullOrEmpty(LastBlockId))
728 {
729 Url.Append("&Last=");
730 Url.Append(LastBlockId);
731 }
732
733 KeyValuePair<string, TemporaryStream> Resource = await InternetContent.GetTempStreamAsync(new Uri(Url.ToString()), DefaultTimeoutMs,
734 new KeyValuePair<string, string>("Accept", XmlCodec.DefaultContentType));
735
736 using (TemporaryStream File = Resource.Value)
737 {
738 XmlDocument Doc = new XmlDocument();
739
740 File.Position = 0;
741 Doc.Load(File);
742
743 return Doc.DocumentElement;
744 }
745 }
746
747 private async Task<DateTime> UpdateStatus(DateTime LastReported, SynchronizationStatus Status, CallbackAsync<SynchronizationStatus> Callback)
748 {
749 DateTime TP = DateTime.Now;
750 if ((TP - LastReported).TotalSeconds > 1)
751 {
752 LastReported = TP;
753 if (!(Callback is null))
754 {
755 try
756 {
757 await Callback(Status);
758 }
759 catch (Exception ex)
760 {
761 Status?.IncBlockError(string.Empty, ex.Message);
762 }
763 }
764 }
765
766 return LastReported;
767 }
768
769 private async Task GetBlockReferences(object Sender, IqEventArgs e)
770 {
771 int Max = XML.Attribute(e.Query, "max", 50);
772
773 if (Max <= 0)
774 {
775 await e.IqError(new BadRequestException("Maximum number of records must be positive.", e.IQ));
776 return;
777 }
778
779 RosterItem Item = (Sender as XmppClient)?.GetRosterItem(e.FromBareJid);
780 if (Item is null || (Item.State != SubscriptionState.Both && Item.State != SubscriptionState.From))
781 {
782 await e.IqError(new ForbiddenException("You must have an active presence subscription to the node to request information.", e.IQ));
783 return;
784 }
785
786 if (Max > 50)
787 Max = 50;
788
789 IEnumerable<BlockReference> Blocks;
790 FilterCustom<BlockReference> CollectionFilter = new FilterCustom<BlockReference>((Ref) => Item.IsInGroup(Ref.Collection));
791 string Last = XML.Attribute(e.Query, "last");
792 int i;
793
794 if (string.IsNullOrEmpty(Last))
795 Blocks = await Database.Find<BlockReference>(0, Max + 1, CollectionFilter, "ObjectId");
796 else
797 {
798 Blocks = await Database.Find<BlockReference>(0, Max + 1,
799 new FilterAnd(new FilterFieldGreaterThan("ObjectId", Last), CollectionFilter), "ObjectId");
800 }
801
802 i = 0;
803 foreach (BlockReference Ref in Blocks)
804 i++;
805
806 StringBuilder Xml = new StringBuilder();
807
808 Xml.Append("<blockReferences xmlns=\"");
809 Xml.Append(NeuroLedgerNamespace);
810 Xml.Append('"');
811
812 if (i > Max)
813 Xml.Append(" more=\"true\"");
814
815 if (i > 0)
816 {
817 Xml.Append('>');
818
819 foreach (BlockReference Ref in Blocks)
820 {
821 if (--Max < 0)
822 break;
823
824 Xml.Append("<ref id='");
825 Xml.Append(Ref.ObjectId);
826
827 Xml.Append("' d='");
828 Xml.Append(Convert.ToBase64String(Ref.Digest));
829 Xml.Append("' s='");
830 Xml.Append(Convert.ToBase64String(Ref.Signature));
831
832 if (!(Ref.Link is null))
833 {
834 Xml.Append("' l='");
835 Xml.Append(Convert.ToBase64String(Ref.Link));
836 }
837
838 Xml.Append("' cn='");
839 Xml.Append(XML.Encode(Ref.Collection));
840 Xml.Append("' cr='");
841 Xml.Append(XML.Encode(Ref.Creator));
842 Xml.Append("' ct='");
843 Xml.Append(XML.Encode(Ref.Created));
844
845 if (Ref.Updated != DateTime.MinValue)
846 {
847 Xml.Append("' u='");
848 Xml.Append(XML.Encode(Ref.Updated));
849 }
850
851 if (Ref.Expires != DateTime.MaxValue)
852 {
853 Xml.Append("' x='");
854 Xml.Append(XML.Encode(Ref.Expires));
855 }
856
857 if (Ref.Status != BlockStatus.Valid)
858 {
859 Xml.Append("' t='");
860 Xml.Append(Ref.Status.ToString());
861 }
862
863 Xml.Append("' r='");
864 Xml.Append("httpx://");
865 Xml.Append(this.client.BareJID);
866 Xml.Append("/NL/B/");
867 Xml.Append(Base64Url.Encode(Ref.Digest));
868 Xml.Append("' b='");
869 Xml.Append(Ref.Bytes.ToString());
870 Xml.Append("'/>");
871 }
872
873 Xml.Append("</blockReferences>");
874 }
875 else
876 Xml.Append("/>");
877
878 await e.IqResult(Xml.ToString());
879 }
880
881 private Task Client_OnStateChanged(object Sender, XmppState NewState)
882 {
883 if (NewState == XmppState.Connected)
884 Task.Run(() => this.CheckOutgoingEvents());
885
886 return Task.CompletedTask;
887 }
888
889 private async Task CheckOutgoingEvents()
890 {
891 try
892 {
893 IEnumerable<OutgoingEvent> Events;
894
895 try
896 {
897 Events = await Database.Find<OutgoingEvent>();
898 }
899 catch (Exception ex)
900 {
901 Log.Exception(ex);
902 await Database.Clear("OutgoingPEP");
903 return;
904 }
905
906 foreach (OutgoingEvent Event in Events)
907 {
908 if (this.pepClient is null)
909 break;
910
911 await this.pepClient.PublishAsync(Event.Event);
912 await Database.Delete(Event);
913 }
914 }
915 catch (Exception ex)
916 {
917 Log.Exception(ex);
918 }
919 }
920
924 public override string[] Extensions => new string[] { };
925
929 public override void Dispose()
930 {
931 if (!(this.provider is null))
932 {
933 this.webServer.Unregister(this.blockResource);
934 this.webServer.Unregister(this.blockListResource);
935 this.webServer.Unregister(this.multiGetResource);
936
937 this.provider.BlockAdded -= this.BlockAdded;
938 this.provider.BlockDeleted -= this.BlockDeleted;
939
940 this.client.OnStateChanged -= this.Client_OnStateChanged;
941 this.client.OnPresence -= this.Client_OnPresence;
942 this.client.UnregisterIqGetHandler("getBlockReferences", NeuroLedgerNamespace, this.GetBlockReferences, true);
943
944 this.pepClient.UnregisterHandler(typeof(BlockAdded), this.BlockAddedReceived);
945 this.pepClient.UnregisterHandler(typeof(BlockDeleted), this.BlockDeletedReceived);
946
947 this.provider.BlockAdded -= this.BlockAdded;
948 this.provider.BlockDeleted -= this.BlockDeleted;
949
950 if (this.internalScheduler)
951 this.scheduler?.Dispose();
952
953 this.scheduler = null;
954
955 this.peerStatus?.Dispose();
956 this.peerStatus = null;
957
958 this.provider = null;
959 this.pepClient = null;
960
961 base.Dispose();
962 }
963 }
964
965 private async Task Queue(NeuroLedgerPepEvent Event)
966 {
967 try
968 {
970 {
971 Event = Event
972 });
973 }
974 catch (Exception ex)
975 {
976 Log.Exception(ex);
977 }
978 }
979
980 private Task BlockAdded(object Sender, BlockReferenceEventArgs e)
981 {
983 Task T;
984
985 this.Fill(Event, e);
986
987 if (this.pepClient.Client.State == XmppState.Connected)
988 {
989 this.pepClient.Publish(Event, (sender, e2) =>
990 {
991 if (!e2.Ok)
992 T = this.Queue(Event);
993
994 return Task.CompletedTask;
995
996 }, null);
997 }
998 else
999 T = this.Queue(Event);
1000
1001 return Task.CompletedTask;
1002 }
1003
1004 private void Fill(BlockEvent Event, BlockReferenceEventArgs e)
1005 {
1006 BlockReference Block = e.Block;
1007
1008 Event.Collection = Block.Collection;
1009 Event.Created = Block.Created;
1010 Event.Creator = Block.Creator;
1011 Event.Digest = Block.Digest;
1012 Event.Expires = Block.Expires;
1013 Event.Link = Block.Link;
1014 Event.Signature = Block.Signature;
1015 Event.Status = Block.Status;
1016 Event.Updated = Block.Updated;
1017 Event.Bytes = Block.Bytes;
1018 Event.Url = "httpx://" + this.client.BareJID + "/NL/B/" + Base64Url.Encode(Block.Digest);
1019 }
1020
1021 private Task BlockDeleted(object Sender, BlockReferenceEventArgs e)
1022 {
1024 Task T;
1025
1026 this.Fill(Event, e);
1027
1028 if (this.pepClient.Client.State == XmppState.Connected)
1029 {
1030 this.pepClient.Publish(Event, (sender, e2) =>
1031 {
1032 if (!e2.Ok)
1033 T = this.Queue(Event);
1034
1035 return Task.CompletedTask;
1036
1037 }, null);
1038 }
1039 else
1040 T = this.Queue(Event);
1041
1042 return Task.CompletedTask;
1043 }
1044
1045 private async Task BlockAddedReceived(object Sender, PersonalEventNotificationEventArgs e)
1046 {
1048 {
1049 lock (this.processingQueue)
1050 {
1051 if (this.processing)
1052 {
1053 this.processingQueue.AddLast(Event);
1054 return;
1055 }
1056
1057 this.processing = true;
1058 }
1059
1060 do
1061 {
1062 try
1063 {
1064 await this.Process(Event, null);
1065 }
1066 catch (ForbiddenException)
1067 {
1068 // Node not authorized to access blocks from the corresponding collection. Ignore error.
1069 }
1070 catch (Exception ex)
1071 {
1072 Log.Exception(ex);
1073 }
1074
1075 lock (this.processingQueue)
1076 {
1077 if (this.processingQueue.First is null)
1078 {
1079 Event = null;
1080 this.processing = false;
1081 }
1082 else
1083 {
1084 Event = this.processingQueue.First.Value;
1085 this.processingQueue.RemoveFirst();
1086 }
1087 }
1088 }
1089 while (!(Event is null));
1090 }
1091 }
1092
1093 private readonly LinkedList<BlockAdded> processingQueue = new LinkedList<BlockAdded>();
1094 private bool processing = false;
1095
1096 private async Task Process(BlockAdded Event, SynchronizationStatus Status)
1097 {
1098 BlockReference Ref = await this.GetReference(Event, Status, true);
1099 if (!(Ref is null))
1100 await this.RetrieveBlock(Ref, Event.Url, Status, true);
1101 }
1102
1103 private async Task<BlockReference> GetReference(BlockAdded Event, SynchronizationStatus Status, bool NullIfNotChanged)
1104 {
1105 if (!(Status is null))
1106 {
1107 Status.NrBlocks++;
1108 Status.TotalBytes += Event.Bytes;
1109
1110 if (Event.Created < Status.First)
1111 Status.First = Event.Created;
1112
1113 if (Event.Created > Status.Last)
1114 Status.Last = Event.Created;
1115 }
1116
1118
1119 if (Ref is null)
1120 {
1121 Ref = new BlockReference()
1122 {
1123 Collection = Event.Collection,
1124 Created = Event.Created,
1125 Creator = Event.Creator,
1126 Digest = Event.Digest,
1127 Expires = Event.Expires,
1128 Link = Event.Link,
1129 Signature = Event.Signature,
1130 Status = Event.Status,
1131 Updated = Event.Updated,
1132 Bytes = Event.Bytes,
1133 Sources = new string[] { Event.Url },
1134 AccessDenied = false,
1135 Unpacked = false
1136 };
1137
1138 await Database.Insert(Ref);
1139
1140 if (!(Status is null))
1141 Status.NrNew++;
1142
1143 return Ref;
1144 }
1145 else
1146 {
1147 bool Updated = false;
1148 bool Changed = false;
1149
1150 if (Ref.Creator != this.provider?.ExternalIdentity &&
1151 (Ref.Collection != Event.Collection ||
1152 !NeuroLedgerProvider.AreEqual(Ref.Created, Event.Created) ||
1153 !NeuroLedgerProvider.AreEqual(Ref.Updated, Event.Updated) ||
1154 !NeuroLedgerProvider.AreEqual(Ref.Expires, Event.Expires) ||
1155 Ref.Creator != Event.Creator ||
1156 !Compare(Ref.Digest, Event.Digest) ||
1157 !Compare(Ref.Link, Event.Link) ||
1158 !Compare(Ref.Signature, Event.Signature) ||
1159 Ref.Status != Event.Status ||
1160 Ref.Bytes != Event.Bytes ||
1161 Ref.AccessDenied))
1162 {
1163 Ref.Collection = Event.Collection;
1164 Ref.Created = Event.Created;
1165 Ref.Creator = Event.Creator;
1166 Ref.Expires = Event.Expires;
1167 Ref.Digest = Event.Digest;
1168 Ref.Link = Event.Link;
1169 Ref.Signature = Event.Signature;
1170 Ref.Status = Event.Status;
1171 Ref.Updated = Event.Updated;
1172 Ref.Bytes = Event.Bytes;
1173 Ref.AccessDenied = false;
1174
1175 Changed = true;
1176 }
1177
1178 if (Ref.Sources is null || Array.IndexOf(Ref.Sources, Event.Url) < 0)
1179 {
1180 int c = Ref.Sources?.Length ?? 0;
1181
1182 string[] s = new string[c + 1];
1183 if (c > 0)
1184 Array.Copy(Ref.Sources, 0, s, 0, c);
1185
1186 s[c] = Event.Url;
1187 Ref.Sources = s;
1188 Updated = true;
1189 }
1190
1191 if (Updated || Changed)
1192 {
1193 await Database.Update(Ref);
1194
1195 if (!(Status is null))
1196 Status.NrUpdated++;
1197
1198 if (Changed || !NullIfNotChanged)
1199 return Ref;
1200 else
1201 return null;
1202 }
1203 else if (string.IsNullOrEmpty(Ref.FileName) || !NullIfNotChanged)
1204 return Ref;
1205 else
1206 return null;
1207 }
1208 }
1209
1210 private static bool Compare(byte[] A1, byte[] A2)
1211 {
1212 if ((A1 is null) ^ (A2 is null))
1213 return false;
1214
1215 if (A1 is null)
1216 return true;
1217
1218 int i, c = A1.Length;
1219 if (A2.Length != c)
1220 return false;
1221
1222 for (i = 0; i < c; i++)
1223 {
1224 if (A1[i] != A2[i])
1225 return false;
1226 }
1227
1228 return true;
1229 }
1230
1231 internal async Task<bool> RetrieveBlock(BlockReference Ref, string Url, SynchronizationStatus Status, bool Unpack)
1232 {
1233 string FileName = this.provider?.GetFullFileName(Ref.FileName);
1234
1235 if (string.IsNullOrEmpty(Ref.FileName) || !File.Exists(FileName))
1236 {
1237 string Key = Convert.ToBase64String(Ref.Digest);
1238
1239 lock (this.loading)
1240 {
1241 if (this.loading.ContainsKey(Key))
1242 return true;
1243
1244 this.loading[Key] = true;
1245 }
1246
1247 KeyValuePair<string, TemporaryStream> Resource;
1248 TemporaryStream File = null;
1249
1250 try
1251 {
1252 await this.client.Information("Retrieving block (" + Ref.Collection + "): " + Url);
1253
1254 Resource = await InternetContent.GetTempStreamAsync(new Uri(Url), DefaultTimeoutMs,
1255 new KeyValuePair<string, string>("Accept", "application/octet-stream"));
1256 File = Resource.Value;
1257
1258 await this.client.Information("Block retrieved (" + Ref.Collection + "): " + Url);
1259
1260 await this.provider.AddBlockFile(File, Ref);
1261
1262 await this.client.Information("Block added (" + Ref.Collection + "): " + Url);
1263
1264 if (Ref.AccessDenied)
1265 {
1266 Ref.AccessDenied = false;
1267 await Database.Update(Ref);
1268 }
1269
1270 if (!(Status is null))
1271 {
1272 Status.NrLoaded++;
1273 Status.LoadedBytes += Ref.Bytes;
1274 }
1275
1276 if (Unpack)
1277 await this.UnpackObjects(Ref, Status);
1278
1279 return true;
1280 }
1281 catch (HTTP.ForbiddenException ex)
1282 {
1283 string ErrorMessage = await this.GetErrorMessage(ex);
1284
1285 await this.client.Information("Access to block denied (" + Ref.Collection + "): " + Url + ". Cause: " + ErrorMessage);
1286
1287 if (!Ref.AccessDenied)
1288 {
1289 Ref.AccessDenied = true;
1290 await Database.Update(Ref);
1291 }
1292
1293 if (!(Status is null))
1294 Status.NrDenied++;
1295
1296 return false;
1297 }
1298 catch (HTTP.FailedDependencyException ex)
1299 {
1300 string ErrorMessage = await this.GetErrorMessage(ex);
1301
1302 await this.client.Information("Access to block denied (" + Ref.Collection + "): " + Url + ". Cause: " + ErrorMessage);
1303
1304 if (!Ref.AccessDenied)
1305 {
1306 Ref.AccessDenied = true;
1307 await Database.Update(Ref);
1308 }
1309
1310 if (!(Status is null))
1311 Status.NrDenied++;
1312
1313 return false;
1314 }
1315 catch (HTTP.NotFoundException ex)
1316 {
1317 string ErrorMessage = await this.GetErrorMessage(ex);
1318
1320 {
1321 await this.client.Warning("Block not found (" + Ref.Collection + "): " + Url + ". Cause: " + ErrorMessage);
1322
1323 await Database.Delete(Ref);
1324
1325 if (!(Status is null))
1326 Status.NrBlocks--;
1327 }
1328
1329 return false;
1330 }
1331 catch (Exception ex)
1332 {
1333 await this.client.Warning("Unable to retrieve block. Trying again later. Error reported: " + ex.Message + " (" + ex.GetType().FullName + ")");
1334 this.scheduler?.Add(DateTime.Now.AddHours(1), this.RetryProcessing, new object[] { Ref, Url });
1335
1336 Status?.IncBlockError(Url, ex.Message);
1337
1338 return false;
1339 }
1340 finally
1341 {
1342 lock (this.loading)
1343 {
1344 this.loading.Remove(Key);
1345 }
1346
1347 File?.Dispose();
1348 }
1349 }
1350 else
1351 return true;
1352 }
1353
1354 private async Task<string> GetErrorMessage(HTTP.HttpException ex)
1355 {
1356 object ContentObject = await ex.GetContentObjectAsync();
1357 if (ContentObject is string s)
1358 return s;
1359 else if (!(ex.Content is null))
1360 return Encoding.UTF8.GetString(ex.Content);
1361 else
1362 return ex.Message;
1363 }
1364
1365 private async Task UnpackObjects(BlockReference Ref, SynchronizationStatus Status)
1366 {
1367 using (BlockEnumerator TempBlockEnumerator = new BlockEnumerator(Ref, this.provider))
1368 {
1369 using (ObjectEnumerator<GenericObject> e = await ObjectEnumerator<GenericObject>.Create(TempBlockEnumerator, this.provider))
1370 {
1371 Dictionary<string, bool> UnpackType = new Dictionary<string, bool>();
1372 int NrAdded = 0;
1373 int NrUpdated = 0;
1374 int NrDeleted = 0;
1375 int NrErrors = 0;
1376 int NrIgnored = 0;
1377
1378 await Database.StartBulk();
1379 try
1380 {
1381 while (await e.MoveNextAsync())
1382 {
1383 GenericObject Obj = e.Current;
1384
1385 if (!UnpackType.TryGetValue(Obj.TypeName, out bool UnpackObject))
1386 {
1387 Type T = Types.GetType(Obj.TypeName);
1388 UnpackObject = (T is null) || !(T.GetCustomAttribute(typeof(ArchivingTimeAttribute)) is null);
1389 UnpackType[Obj.TypeName] = UnpackObject;
1390 }
1391
1392 if (!UnpackObject)
1393 {
1394 NrIgnored++;
1395 continue;
1396 }
1397
1398 switch (e.CurrentEntry.Type)
1399 {
1400 case EntryType.New:
1401 try
1402 {
1403 await Database.Provider.Insert(Obj); // Calling Provider method avoids raising event.
1404 NrAdded++;
1405
1406 this.externalEvents?.RaiseEntryAdded(Obj);
1407
1408 if (!(Status is null))
1409 Status.ObjectsAdded++;
1410 }
1412 {
1413 // Object already exists, perhaps in a newer state. Keep that version for now.
1414 }
1415 catch (Exception ex)
1416 {
1417 await this.client.Error(ex.Message);
1418 NrErrors++; // Object already exists locally.
1419
1420 if (!(Status is null))
1421 Status.ObjectErrors++;
1422 }
1423 break;
1424
1425 case EntryType.Update:
1426 try
1427 {
1428 await Database.Provider.Update(Obj); // Calling Provider method avoids raising event.
1429 NrUpdated++;
1430
1431 this.externalEvents?.RaiseEntryUpdated(Obj);
1432
1433 if (!(Status is null))
1434 Status.ObjectsUpdated++;
1435 }
1436 catch (KeyNotFoundException)
1437 {
1438 try
1439 {
1440 await Database.Provider.Insert(Obj); // Calling Provider method avoids raising event.
1441 NrAdded++;
1442
1443 this.externalEvents?.RaiseEntryAdded(Obj);
1444
1445 if (!(Status is null))
1446 Status.ObjectsAdded++;
1447 }
1448 catch (Exception ex)
1449 {
1450 await this.client.Error(ex.Message);
1451 NrErrors++;
1452
1453 if (!(Status is null))
1454 Status.ObjectErrors++;
1455 }
1456 }
1457 catch (Exception ex)
1458 {
1459 await this.client.Error(ex.Message);
1460 NrErrors++;
1461
1462 if (!(Status is null))
1463 Status.ObjectErrors++;
1464 }
1465 break;
1466
1467 case EntryType.Delete:
1468 try
1469 {
1470 await Database.Provider.Delete(Obj); // Calling Provider method avoids raising event.
1471 NrDeleted++;
1472
1473 this.externalEvents?.RaiseEntryDeleted(Obj);
1474
1475 if (!(Status is null))
1476 Status.ObjectsDeleted++;
1477 }
1478 catch (KeyNotFoundException)
1479 {
1480 // Already deleted.
1481 }
1482 catch (Exception ex)
1483 {
1484 await this.client.Error(ex.Message);
1485 NrErrors++; // Object does not exist locally.
1486
1487 if (!(Status is null))
1488 Status.ObjectErrors++;
1489 }
1490 break;
1491
1492 case EntryType.Clear:
1493 //await Database.Provider.Clear(Ref.Collection); // Calling Provider method avoids raising event.
1494 //this.client.Information("Collection cleared: " + Ref.Collection);
1495 //
1496 //NrAdded = 0;
1497 //NrUpdated = 0;
1498 //NrDeleted = 0;
1499 //NrErrors = 0;
1500
1501 // TODO: Clear only objects from corresponding collection & creator. Also raise corresponding events.
1502 continue;
1503
1504 default:
1505 continue;
1506 }
1507 }
1508 }
1509 finally
1510 {
1511 await Database.EndBulk();
1512 }
1513
1514 if (NrAdded > 0)
1515 await this.client.Information("Number of objects added to " + Ref.Collection + ": " + NrAdded.ToString());
1516
1517 if (NrUpdated > 0)
1518 await this.client.Information("Number of objects updated in " + Ref.Collection + ": " + NrUpdated.ToString());
1519
1520 if (NrDeleted > 0)
1521 await this.client.Information("Number of objects deleted from " + Ref.Collection + ": " + NrDeleted.ToString());
1522
1523 if (NrErrors > 0)
1524 await this.client.Information("Number of errors related to " + Ref.Collection + ": " + NrErrors.ToString());
1525
1526 if (NrIgnored > 0)
1527 await this.client.Information("Number of objects ignored in " + Ref.Collection + ": " + NrIgnored.ToString());
1528 }
1529 }
1530
1531 if (!Ref.Unpacked)
1532 {
1533 Ref.Unpacked = true;
1534 await Database.Update(Ref);
1535 }
1536 }
1537
1538 private async void RetryProcessing(object State)
1539 {
1540 try
1541 {
1542 if (this.ClientDisposed)
1543 return;
1544
1545 object[] P = (object[])State;
1546 BlockReference Ref = (BlockReference)P[0];
1547 string Url = (string)P[1];
1548
1549 RosterItem Item = this.client[Ref.Creator];
1550 if (Item is null)
1551 return;
1552
1553 foreach (PresenceEventArgs e in Item.Resources)
1554 {
1555 PeerStatus PeerStatus = await this.GetPeerStatus(e);
1556 if (!(PeerStatus is null) && PeerStatus.IsNeuroLedger)
1557 {
1558 await this.RetrieveBlock(Ref, Url, null, true);
1559 return;
1560 }
1561 }
1562 }
1563 catch (Exception ex)
1564 {
1565 Log.Exception(ex);
1566 }
1567 }
1568
1569 private Task BlockDeletedReceived(object Sender, PersonalEventNotificationEventArgs e)
1570 {
1571 if (e.PersonalEvent is BlockDeleted)
1572 {
1573 // TODO: Delete BlockReference
1574 // TODO: Delete Objects
1575 }
1576
1577 return Task.CompletedTask;
1578 }
1579
1583 public Task ProcessBlockRequest(HTTP.HeaderFields.HttpFieldAccept Accept, HTTP.HttpResponse Response, BlockReference Ref)
1584 {
1585 return this.blockResource.ProcessRequest(Accept, Response, Ref);
1586 }
1587
1588 }
1589}
Static class that does BASE64URL encoding (using URL and filename safe alphabet), as defined in RFC46...
Definition: Base64Url.cs:11
static string Encode(byte[] Data)
Converts a binary block of data to a Base64URL-encoded string.
Definition: Base64Url.cs:48
Static class managing encoding and decoding of internet content.
static Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
static Task< object > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
static Task< KeyValuePair< string, TemporaryStream > > GetTempStreamAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, given its URI.
Represents content embedded in other content.
string Description
Content-Description of embedded object, if defined.
object Decoded
Decoded body of embedded object. ContentType defines how TransferDecoded is transformed into Decoded.
byte[] TransferDecoded
Transformed body of embedded object. TransferEncoding defines how Raw is transformed into TransferDec...
Represents mixed content, encoded with multipart/mixed
Definition: MixedContent.cs:7
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
XML encoder/decoder.
Definition: XmlCodec.cs:16
const string DefaultContentType
Default content type for XML documents.
Definition: XmlCodec.cs:27
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
Class representing an event.
Definition: Event.cs:10
Event(DateTime Timestamp, EventType Type, string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Class representing an event.
Definition: Event.cs:38
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:184
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:566
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
Task Information(string Comment)
Called to inform the viewer of something.
Task Warning(string Warning)
Called to inform the viewer of a warning state.
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Implements an HTTP server.
Definition: HttpServer.cs:36
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
async Task IqError(string Xml)
Returns an error response to the current request.
Definition: IqEventArgs.cs:208
async Task IqResult(string Xml)
Returns a response to the current request.
Definition: IqEventArgs.cs:194
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:119
string FromBareJid
Bare version of the "from" JID.
Definition: IqEventArgs.cs:157
Event arguments for presence events.
string EntityCapabilityHashFunction
Hash function used in calculation of entity capabilities version of sender. Entity capabilities are d...
string EntityCapabilityNode
Node of entity capabilities of sender. Entity capabilities are defined in XEP-0115: http://xmpp....
bool HasEntityCapabilities
If the presence stanza includes entity capabilities information. Entity capabilities are defined in X...
string EntityCapabilityVersion
Version of entity capabilities of sender. Entity capabilities are defined in XEP-0115: http://xmpp....
string From
From where the presence was received.
string FromBareJID
Bare JID of resource sending the presence.
XmppClient Client
XMPP Client. Is null if event raised by a component.
async Task< string > SynchronizePeer(PeerStatus PeerStatus, bool StartFromBeginning, CallbackAsync< SynchronizationStatus > Callback)
Synchronizes blocks hosted by a peer.
const int DefaultTimeoutMs
Default timeout = 2 minutes.
Task ProcessBlockRequest(HTTP.HeaderFields.HttpFieldAccept Accept, HTTP.HttpResponse Response, BlockReference Ref)
TODO
NeuroLedgerProvider Provider
Neuro-Ledger provider.
override string[] Extensions
Implemented extensions.
void StopSynchronization(PeerStatus PeerStatus)
Stops synchronization, if such is in progress.
NeuroLedgerClient(XmppClient Client, EndpointSecurity E2eEncryption, HTTP.HttpServer WebServer, NeuroLedgerProvider Provider)
Neuro-Ledger XMPP Client
async Task< PeerStatus > GetPeerStatus(PresenceEventArgs e)
Gets status information related to a peer.
Contains information about current synchronization status for a node in the network.
Definition: NodeStatus.cs:13
Contains information about current synchronization status for a peer in the network.
Definition: PeerStatus.cs:13
int SynchCounter
Synchronization Counter
Definition: PeerStatus.cs:137
bool IsSynchronizing
If a synchronizating process is underway.
Definition: PeerStatus.cs:128
string RequestingCapabilitiesFunction
Entity RequestingCapabilities Function being requested
Definition: PeerStatus.cs:101
string RequestingCapabilitiesNode
Entity RequestingCapabilities Node being requested
Definition: PeerStatus.cs:92
string CapabilitiesFunction
Entity Capabilities Function
Definition: PeerStatus.cs:74
string RequestingCapabilitiesVersion
Entity Capabilities Version being requested
Definition: PeerStatus.cs:83
bool IsNeuroLedger
If the peer is a Neuro-Ledger node.
Definition: PeerStatus.cs:119
string CapabilitiesVersion
Entity Capabilities Version
Definition: PeerStatus.cs:56
string CapabilitiesNode
Entity Capabilities Node
Definition: PeerStatus.cs:65
Event raised when a block has been added.
Definition: BlockAdded.cs:14
Event raised when a block has been deleted.
Definition: BlockDeleted.cs:14
Abstract base class for Neuro-Ledger block PEP events.
Definition: BlockEvent.cs:13
Abstract base class for Neuro-Ledger PEP events.
Provides authenticated and authorized clients with lists of available blocks.
Provides authenticated and authorized clients with binary blocks.
const string ErrorMsg_BlockFileHasBeenRemoved
Block file has been removed.
Allows a client to get multiple resources in one call
Class managing end-to-end encryption.
IPersonalEvent PersonalEvent
Parsed personal event, if appropriate type was found.
Client managing the Personal Eventing Protocol (XEP-0163). https://xmpp.org/extensions/xep-0163....
Definition: PepClient.cs:19
Task Publish(string Node, EventHandlerAsync< ItemResultEventArgs > Callback, object State)
Publishes an item on a node.
Definition: PepClient.cs:110
void RegisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Registers an event handler of a specific type of personal events.
Definition: PepClient.cs:345
async Task< string > PublishAsync(string Node)
Publishes an item on a node.
Definition: PepClient.cs:162
bool UnregisterHandler(Type PersonalEventType, EventHandlerAsync< PersonalEventNotificationEventArgs > Handler)
Unregisters an event handler of a specific type of personal events.
Definition: PepClient.cs:380
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
Contains information about an item of an entity.
Definition: Item.cs:11
The sender has sent a stanza containing XML that does not conform to the appropriate schema or that c...
The requesting entity does not possess the necessary permissions to perform an action that only certa...
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Get handler.
Definition: XmppClient.cs:2778
IXmppExtension[] Extensions
Registered extensions.
Definition: XmppClient.cs:7302
bool Disposed
If the client has been disposed.
Definition: XmppClient.cs:1192
Task< ServiceDiscoveryEventArgs > ServiceDiscoveryAsync(string To)
Performs an asynchronous service discovery request
Definition: XmppClient.cs:6003
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Get handler.
Definition: XmppClient.cs:2717
RosterItem[] Roster
Items in the roster.
Definition: XmppClient.cs:4671
Base class for XMPP Extensions.
bool ClientDisposed
If the client has been disposed.
XmppClient client
XMPP Client used by the extension.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
XmppClient Client
XMPP Client.
This attribute defines that objects of this type can be archived, and the time objects can be archive...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static Task< IPersistentDictionary > GetDictionary(string Collection)
Gets a persistent dictionary containing objects in a collection.
Definition: Database.cs:1542
static Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
Definition: Database.cs:1494
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
Definition: Database.cs:156
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
Definition: Database.cs:1486
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
static async Task Clear(string CollectionName)
Clears a collection of all objects.
Definition: Database.cs:1206
An attempt to insert a key was done, but the key was already there.
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
Custom filter used to filter objects using an external expression.
Definition: FilterCustom.cs:10
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field greater than a given value.
byte[] Link
Link to updated block (in case Status shows the block has been updated).
Definition: BlockHeader.cs:120
string Creator
Creator of the block.
Definition: BlockHeader.cs:55
BlockStatus Status
Claimed status of block.
Definition: BlockHeader.cs:108
DateTime Created
When the block was created.
Definition: BlockHeader.cs:75
DateTime Expires
When the block expires.
Definition: BlockHeader.cs:97
DateTime Updated
When the block was updated (in case Status shows the block has been updated or deleted).
Definition: BlockHeader.cs:87
Event arguments for block reference events.
Optimizes a persistent IPersistentDictionary using a cache.
EntryType Type
Entry Type
Definition: Entry.cs:32
async Task AddBlockFile(Stream File, BlockReference BlockReference)
Adds a block file to the ledger.
static bool AreEqual(DateTime TP1, DateTime TP2)
Compares two timestamps, to the millisecond (but not tick) level.
ILedgerExternalEvents ExternalEvents
Interface for reporting external events.
static Task< BlockReference > FindReference(byte[] Digest)
Finds a BlockReference object related to a block, given its digest.
async Task< Tuple< uint, uint, uint, uint > > Process(CachedStringDictionary Records, string CollectionName)
Processes an ordered set of records containing ObjectState objects in a cached string dictionary (for...
Enumeratres through objects available in a series of blocks.
T Current
Gets the element in the collection at the current position of the enumerator.
async Task< bool > MoveNextAsync()
Advances the enumerator to the next element of the collection.
static async Task< ObjectEnumerator< T > > Create(IAsyncEnumerator< BlockReference > BlockEnumerator, NeuroLedgerProvider Provider)
Creates an object enumerator from a block enumerator.
Represents an object state.
Definition: ObjectState.cs:11
Contains a reference to a block in the ledger.
bool AccessDenied
If access to the block was denied.
bool Unpacked
If objects in the block have been unpacked.
Generic object. Contains a sequence of properties.
Implements an in-memory cache.
Definition: Cache.cs:15
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:583
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override void Dispose(bool disposing)
Releases the unmanaged resources used by the System.IO.Stream and optionally releases the managed res...
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:26
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:46
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
Definition: Scheduler.cs:66
Task Update(object Object)
Updates an object in the database.
Task Delete(object Object)
Deletes an object in the database.
Task Clear(string CollectionName)
Clears a collection of all objects.
Task Insert(object Object)
Inserts an object into the database.
Interface for proxy for reporting changes to the ledger from external sources.
Persistent dictionary that can contain more entries than possible in the internal memory.
Task ClearAsync()
Clears the dictionary.
EventLevel
Event level.
Definition: EventLevel.cs:7
SubscriptionState
State of a presence subscription.
Definition: RosterItem.cs:16
XmppState
State of XMPP connection.
Definition: XmppState.cs:7
BlockStatus
Status of the block.
Definition: BlockHeader.cs:12
EntryType
Ledger entry type.
Definition: ILedgerEntry.cs:9