Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
LedgerConfiguration.cs
1using System;
3using System.IO;
4using System.Text.RegularExpressions;
5using System.Threading.Tasks;
6using Waher.Content;
7using Waher.Events;
19
21{
26 {
27 private static LedgerConfiguration instance = null;
28 internal static readonly Regex FromSaveUnsaved = new Regex(@"Waher[.]Persistence[.]Files[.]ObjectBTreeFile[.+]<?SaveUnsaved>?\w*[.]\w*",
29 RegexOptions.Compiled | RegexOptions.Singleline);
30 internal static readonly Regex FromUpdateObject = new Regex(@"Waher[.]Persistence[.]Files[.]ObjectBTreeFile[.+]<?UpdateObject>?\w*[.]\w*",
31 RegexOptions.Compiled | RegexOptions.Singleline);
32 internal static readonly Regex GatewayStartup = new Regex(@"Waher[.]IoTGateway[.]Gateway([.]Start|[.+]<?Start>?\w*[.]\w*)*",
33 RegexOptions.Compiled | RegexOptions.Singleline);
34 internal static readonly string LegalComponent = "Waher.Service.IoTBroker.Legal.LegalComponent";
35 internal static readonly string PaiwiseComponent = "Waher.Service.IoTBroker.Paiwise.PaiwiseProcessor";
36 internal static readonly string NeuroFeaturesComponent = "Waher.Service.IoTBroker.NeuroFeatures.NeuroFeaturesProcessor";
37 internal static readonly string EDalerComponent = "Waher.Service.IoTBroker.EDaler.EDalerProcessor";
38 internal static readonly string MarketplaceComponent = "Waher.Service.IoTBroker.Marketplace.MarketplaceProcessor";
39 internal static readonly string SetContractState = "Waher.Service.IoTBroker.WebServices.SetContractState";
40 internal static readonly Regex UnitTests = new Regex(@"Waher[.]Service[.]IoTBroker[.]Test[.](LegalIdentitiesTests|SmartContractsTests|AgentTests|EDalerTests|EDalerFederatedTests|MarketplaceTests|NeuroFeaturesTests|QuickLoginTests)",
41 RegexOptions.Compiled | RegexOptions.Singleline);
42 private static readonly ICallStackCheck[] approvedSources = Assert.Convert(new object[]
43 {
44 FromSaveUnsaved,
45 FromUpdateObject,
46 GatewayStartup,
47 typeof(LedgerConfiguration),
48 typeof(NeuroLedgerProvider),
49 typeof(NeuroLedgerModule),
50 LegalComponent,
51 PaiwiseComponent,
52 NeuroFeaturesComponent,
53 EDalerComponent,
54 SetContractState,
55 MarketplaceComponent // Hard coded, to make sure other components cannot register new approved sources.
56 });
57 private static readonly ICallStackCheck[] approvedSourcesTest = Assert.Convert(new object[]
58 {
59 FromSaveUnsaved,
60 FromUpdateObject,
61 GatewayStartup,
62 typeof(LedgerConfiguration),
63 typeof(NeuroLedgerProvider),
64 typeof(NeuroLedgerModule),
65 LegalComponent,
66 PaiwiseComponent,
67 NeuroFeaturesComponent,
68 EDalerComponent,
69 SetContractState,
70 MarketplaceComponent, // Hard coded, to make sure other components cannot register new approved sources.
71 "System.Threading.PortableThreadPool+WorkerThread",
72 UnitTests
73 });
74
75 private string dataProtectionAgreementId = string.Empty;
76 private byte[] privateKey = null;
77 private byte[] salt = null;
78 private int collectionTimeSeconds = 5 * 60;
79 private int blockSizeThreshold = 1 * 1024 * 1024; // 1 MB
80 private Edwards448 ed448 = null;
81 private readonly SHA3_512 sha3_512 = new SHA3_512();
82
83 private HttpResource setLedgerProperties = null;
84
89 : base()
90 {
91 }
92
96 [DefaultValueNull]
97 public byte[] PrivateKey
98 {
99 get
100 {
101 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
102 return this.privateKey;
103 }
104
105 set
106 {
107 if (!(this.privateKey is null))
108 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
109
110 this.privateKey = value;
111 this.ed448 = new Edwards448(this.privateKey);
112 }
113 }
114
118 [DefaultValueNull]
119 public byte[] PublicKey
120 {
121 get
122 {
123 if (this.CheckKey() && this.ObjectId != Guid.Empty)
124 {
125 Task _ = Database.UpdateLazy(this);
126 }
127
128 return this.ed448.PublicKey;
129 }
130 }
131
135 [DefaultValueNull]
136 public byte[] Salt
137 {
138 get
139 {
140 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
141 return this.salt;
142 }
143
144 set
145 {
146 if (!(this.salt is null))
147 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
148
149 this.salt = value;
150 }
151 }
152
156 [DefaultValueStringEmpty]
158 {
159 get
160 {
161 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
162 return this.dataProtectionAgreementId;
163 }
164
165 set
166 {
167 if (!string.IsNullOrEmpty(this.dataProtectionAgreementId))
168 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
169
170 this.dataProtectionAgreementId = value;
171 }
172 }
173
177 public string SignatureAlgorithm
178 {
179 get
180 {
181 if (this.CheckKey())
182 {
183 Task _ = Database.UpdateLazy(this);
184 }
185
186 return this.ed448.CurveName;
187 }
188 }
189
190 private bool CheckKey()
191 {
192 bool Result = false;
193
194 if (this.privateKey is null)
195 {
196 this.privateKey = Gateway.NextBytes(56);
197 this.ed448 = new Edwards448(this.privateKey);
198 Result = true;
199 }
200
201 if (this.salt is null)
202 {
203 this.salt = Gateway.NextBytes(64);
204 Result = true;
205 }
206
207 return Result;
208 }
209
213 public string LedgerFolder => Path.Combine(Gateway.AppDataFolder, "Ledger");
214
219 {
220 get => this.collectionTimeSeconds;
221 set => this.collectionTimeSeconds = value;
222 }
223
228 {
229 get => this.blockSizeThreshold;
230 set => this.blockSizeThreshold = value;
231 }
232
236 public static LedgerConfiguration Instance => instance;
237
241 public override string Resource => "/Settings/Ledger.md";
242
246 public override int Priority => 350;
247
253 public override Task<string> Title(Language Language)
254 {
255 return Language.GetStringAsync(typeof(LedgerConfiguration), 1, "Neuro-Ledger");
256 }
257
262 public override void SetStaticInstance(ISystemConfiguration Configuration)
263 {
264 instance = Configuration as LedgerConfiguration;
265 }
266
270 public override async Task ConfigureSystem()
271 {
272 if (this.CheckKey())
273 await Database.Update(this);
274
276 {
277 string LedgerFolder = this.LedgerFolder;
278
280 TimeSpan.FromSeconds(this.collectionTimeSeconds), this.blockSizeThreshold, this.salt, "Default",
281 XmppConfiguration.Instance.BareJid, this.ed448, this.sha3_512.ComputeVariable, false);
282
283 Ledger.Register(Provider);
284
285 string[] RepairedCollections = DatabaseConfiguration.RepairedCollections;
286 string FileName;
287
288 if (!(RepairedCollections is null))
289 {
290 foreach (string Collection in RepairedCollections)
291 this.AddRepairNoteFile(Collection);
292 }
293
294 FileName = Path.Combine(this.RepairFolder, BlockReference.BlockReferencesCollection + ".txt");
295 if (File.Exists(FileName))
296 {
297 try
298 {
299 Log.Informational("Repairing collection from ledger.", BlockReference.BlockReferencesCollection);
300 await Provider.RepairRegistry();
301 Log.Informational("Repaired collection from ledger.", BlockReference.BlockReferencesCollection);
302
303 try
304 {
305 File.Delete(FileName);
306 }
307 catch (Exception ex)
308 {
309 Log.Error(ex, FileName);
310 }
311 }
312 catch (Exception ex2)
313 {
315 }
316 }
317
318 string[] ToRepair = Directory.GetFiles(this.RepairFolder, "*.txt", SearchOption.TopDirectoryOnly);
319 foreach (string FileName2 in ToRepair)
320 {
321 string CollectionName = Path.GetFileName(FileName2);
322 CollectionName = CollectionName[..^4];
323
324 try
325 {
326 Log.Informational("Repairing collection from ledger.", CollectionName);
327
328 if (CollectionName == BlockReference.BlockReferencesCollection)
329 await Provider.RepairRegistry();
330 else
331 await Provider.RepairCollection(CollectionName);
332
333 Log.Informational("Repaired collection from ledger.", CollectionName);
334
335 try
336 {
337 File.Delete(FileName2);
338 }
339 catch (Exception ex)
340 {
341 Log.Error(ex, FileName2);
342 }
343 }
344 catch (Exception ex2)
345 {
346 Log.Exception(ex2, FileName2);
347 }
348 }
349 }
350 else
351 Log.Warning("Ledger settings changed. Restart the system for changes to take effect.");
352 }
353
357 private string RepairFolder
358 {
359 get
360 {
361 string Result = Path.Combine(this.LedgerFolder, "Repair");
362 if (!Directory.Exists(Result))
363 Directory.CreateDirectory(Result);
364
365 return Result;
366 }
367 }
368
369 private string AddRepairNoteFile(string Collection)
370 {
371 string FileName = Path.Combine(this.RepairFolder, Collection + ".txt");
372
373 try
374 {
375 if (!File.Exists(FileName))
376 File.WriteAllText(FileName, DateTime.UtcNow.ToString());
377 }
378 catch (Exception ex)
379 {
380 Log.Exception(ex);
381 }
382
383 return FileName;
384 }
385
386 internal async Task Database_CollectionRepaired(object Sender, CollectionEventArgs e)
387 {
388 try
389 {
390 if (!(Ledger.Provider is NeuroLedgerProvider Provider))
391 return;
392
393 string FileName = this.AddRepairNoteFile(e.Collection);
394
395 lock (this.repairQueue)
396 {
397 if (this.repairing)
398 {
399 this.repairQueue.AddLast(FileName);
400 return;
401 }
402
403 this.repairing = true;
404 }
405
406 while (!string.IsNullOrEmpty(FileName))
407 {
408 string CollectionName = Path.GetFileName(FileName);
409 CollectionName = CollectionName[..^4];
410
411 if (CollectionName == BlockReference.BlockReferencesCollection)
412 await Provider.RepairRegistry();
413 else
414 await Provider.RepairCollection(CollectionName);
415
416 try
417 {
418 File.Delete(FileName);
419 }
420 catch (Exception ex)
421 {
422 Log.Exception(ex, FileName);
423 }
424
425 lock (this.repairQueue)
426 {
427 if (this.repairQueue.First is null)
428 {
429 FileName = null;
430 this.repairing = false;
431 }
432 else
433 {
434 FileName = this.repairQueue.First.Value;
435 this.repairQueue.RemoveFirst();
436 }
437 }
438 }
439 }
440 catch (Exception ex)
441 {
442 Log.Exception(ex);
443 }
444 }
445
446 private readonly LinkedList<string> repairQueue = new LinkedList<string>();
447 private bool repairing = false;
448
453 public override Task<bool> SimplifiedConfiguration()
454 {
455 return Task.FromResult(true);
456 }
457
462 public override Task InitSetup(HttpServer WebServer)
463 {
464 this.setLedgerProperties = WebServer.Register("/Settings/SetLedgerProperties", null, this.SetLedgerProperties, true, false, true);
465
466 return base.InitSetup(WebServer);
467 }
468
473 public override Task UnregisterSetup(HttpServer WebServer)
474 {
475 WebServer.Unregister(this.setLedgerProperties);
476
477 return base.UnregisterSetup(WebServer);
478 }
479
483 protected override string ConfigPrivilege => "Admin.NeuroLedger.Settings";
484
485 private async Task SetLedgerProperties(HttpRequest Request, HttpResponse Response)
486 {
487 Gateway.AssertUserAuthenticated(Request, this.ConfigPrivilege);
488
489 if (!Request.HasData)
490 {
491 await Response.SendResponse(new BadRequestException());
492 return;
493 }
494
495 ContentResponse Content = await Request.DecodeDataAsync();
496 if (Content.HasError || !(Content.Decoded is IDictionary<string, object> Data))
497 {
498 await Response.SendResponse(new UnsupportedMediaTypeException("Expected form."));
499 return;
500 }
501
502 if (!Data.TryGetValue("collectionTime", out object Obj) ||
503 !(Obj is string CollectionTimeStr) ||
504 !int.TryParse(CollectionTimeStr, out int CollectionTime) ||
505 CollectionTime <= 0 ||
506 CollectionTime > 3600)
507 {
508 await Response.SendResponse(new BadRequestException("Collection time can be between 1 and 3600 seconds."));
509 return;
510 }
511
512 if (!Data.TryGetValue("blockSizeThreshold", out Obj) ||
513 !(Obj is string BlockSizeThresholdStr) ||
514 !int.TryParse(BlockSizeThresholdStr, out int BlockSizeThreshold) ||
515 BlockSizeThreshold <= 0 ||
516 BlockSizeThreshold > 1024 * 1024 * 1024)
517 {
518 await Response.SendResponse(new BadRequestException("Block Size Threshold can be between 1 and 1073741824 bytes (1 GB)."));
519 return;
520 }
521
522 this.collectionTimeSeconds = CollectionTime;
523 this.blockSizeThreshold = BlockSizeThreshold;
524
525 await Database.Update(this);
526
527 Response.StatusCode = 200;
528 Response.StatusMessage = "OK";
529 }
530
537 public static byte[] Sign(byte[] Data)
538 {
539 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
540 return instance?.ed448?.Sign(Data);
541 }
542
549 public static byte[] Sign(Stream Data)
550 {
551 Assert.CallFromSource((DomainConfiguration.Instance?.UseDomainName ?? false) ? approvedSources : approvedSourcesTest);
552 return instance?.ed448?.Sign(Data);
553 }
554
561 public static bool Verify(byte[] Data, byte[] Signature)
562 {
563 return instance?.ed448?.Verify(Data, instance?.PublicKey, Signature) ?? false;
564 }
565
570
574 public const string NEURO_LEDGER_MAXSIZE = nameof(NEURO_LEDGER_MAXSIZE);
575
580 public override Task<bool> EnvironmentConfiguration()
581 {
582 string Value = Environment.GetEnvironmentVariable(NEURO_LEDGER_COLLECTION);
583 if (!string.IsNullOrEmpty(Value))
584 {
585 if (!int.TryParse(Value, out int i))
586 {
587 this.LogEnvironmentVariableInvalidIntegerError(NEURO_LEDGER_COLLECTION, Value);
588 return Task.FromResult(false);
589 }
590
591 if (i < 1 || i > 3600)
592 {
593 this.LogEnvironmentVariableInvalidRangeError(1, 3600, NEURO_LEDGER_COLLECTION, i);
594 return Task.FromResult(false);
595 }
596
597 this.collectionTimeSeconds = i;
598 }
599
600 Value = Environment.GetEnvironmentVariable(NEURO_LEDGER_MAXSIZE);
601 if (!string.IsNullOrEmpty(Value))
602 {
603 if (!int.TryParse(Value, out int i))
604 {
605 this.LogEnvironmentVariableInvalidIntegerError(NEURO_LEDGER_MAXSIZE, Value);
606 return Task.FromResult(false);
607 }
608
609 if (i < 1 || i > 1073741824)
610 {
611 this.LogEnvironmentVariableInvalidRangeError(1, 1073741824, NEURO_LEDGER_MAXSIZE, i);
612 return Task.FromResult(false);
613 }
614
615 this.blockSizeThreshold = i;
616 }
617
618 return Task.FromResult(true);
619 }
620 }
621}
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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:1657
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:576
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:692
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:344
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static IUser AssertUserAuthenticated(HttpRequest Request, string Privilege)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3868
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
static string[] RepairedCollections
Collections repaired during startup.
static DomainConfiguration Instance
Current instance of configuration.
bool UseDomainName
If the server uses a domain name.
Abstract base class for system configurations.
void LogEnvironmentVariableInvalidRangeError(int Min, int Max, string EnvironmentVariable, object Value)
Logs an error to the event log, telling the operator an environment variable value is not within a va...
void LogEnvironmentVariableInvalidIntegerError(string EnvironmentVariable, object Value)
Logs an error to the event log, telling the operator an environment variable value is not a valid int...
static XmppConfiguration Instance
Current instance of configuration.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Represents an HTTP request.
Definition: HttpRequest.cs:22
bool HasData
If the request has data.
Definition: HttpRequest.cs:113
async Task< ContentResponse > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:139
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Implements an HTTP server.
Definition: HttpServer.cs:41
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1568
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1743
The server is refusing to service the request because the entity of the request is in a format not su...
Event arguments for collection events.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task UpdateLazy(object Object)
Updates an object in the database, if unlocked. If locked, object will be updated at next opportunity...
Definition: Database.cs:1261
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
Static interface for ledger persistence. In order to work, a ledger provider has to be assigned to it...
Definition: Ledger.cs:14
static void Register(ILedgerProvider LedgerProvider)
Registers a ledger provider for use from the static Ledger class, throughout the lifetime of the appl...
Definition: Ledger.cs:25
static bool Locked
If the datbase provider has been locked for the rest of the run-time of the application.
Definition: Ledger.cs:112
static bool HasProvider
If a ledger provider is registered.
Definition: Ledger.cs:105
static ILedgerProvider Provider
Registered ledger provider.
Definition: Ledger.cs:83
async Task RepairRegistry()
Make sure block reference objects match existing blocks.
Contains a reference to a block in the ledger.
const string BlockReferencesCollection
Collection housing all block references.
Contains information about a language.
Definition: Language.cs:17
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Definition: Language.cs:209
Static class containing methods that can be used to make sure calls are made from appropriate locatio...
Definition: Assert.cs:15
static ICallStackCheck[] Convert(params object[] Sources)
Converts an array of objects into an array of ICallStackCheck objects, assuming each listed source is...
Definition: Assert.cs:99
static void CallFromSource(params string[] Sources)
Makes sure the call is made from one of the listed sources.
Definition: Assert.cs:54
Edwards448 Elliptic Curve, as defined in RFC7748 and RFC8032: https://tools.ietf.org/html/rfc7748 htt...
Definition: Edwards448.cs:17
override bool Verify(byte[] Data, byte[] PublicKey, bool BigEndian, byte[] Signature)
Verifies a signature of Data made by the EdDSA algorithm.
Definition: Edwards448.cs:127
override byte[] Sign(byte[] Data, bool BigEndian)
Creates a signature of Data using the EdDSA algorithm.
Definition: Edwards448.cs:102
override string CurveName
Name of curve.
Definition: Edwards448.cs:64
virtual byte[] PublicKey
Encoded public key
Implements the SHA3-512 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_512.cs:9
override Task< bool > SimplifiedConfiguration()
Simplified configuration by configuring simple default values.
override int Priority
Priority of the setting. Configurations are sorted in ascending order.
override Task InitSetup(HttpServer WebServer)
Initializes the setup object.
static bool Verify(byte[] Data, byte[] Signature)
Verifies a digital signature, supposedly made by the ledger.
const string NEURO_LEDGER_COLLECTION
Collection time in seconds.
override void SetStaticInstance(ISystemConfiguration Configuration)
Sets the static instance of the configuration.
int CollectionTimeSeconds
Maximum time during which entries in a block are being collected, in seconds.
override Task< bool > EnvironmentConfiguration()
Environment configuration by configuring values available in environment variables.
int BlockSizeThreshold
Blocks are generated before the collection time elapses, if reaching this size.
static byte[] Sign(byte[] Data)
Signs data with the private key of the ledger.
static byte[] Sign(Stream Data)
Signs data with the private key of the ledger.
override async Task ConfigureSystem()
Is called during startup to configure the system.
string DataProtectionAgreementId
Data Protection Agreement to use before allowing access to the Neuro-Ledger.
static LedgerConfiguration Instance
Current instance of configuration.
string SignatureAlgorithm
Name of signature algorithm.
override Task UnregisterSetup(HttpServer WebServer)
Unregisters the setup object.
byte[] PrivateKey
Private key used for signatures.
override string ConfigPrivilege
Minimum required privilege for a user to be allowed to change the configuration defined by the class.
override Task< string > Title(Language Language)
Gets a title for the system configuration.
byte[] PublicKey
Public key used to validate signatures.
byte[] Salt
Private key used for signatures.
const string NEURO_LEDGER_MAXSIZE
Maximum size of blocks, in bytes.
Interface for system configurations. The gateway will scan all module for system configuration classe...
Interface for call stack checks.
Definition: ImplTypes.g.cs:58