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;
2using System.Collections.Generic;
3using System.IO;
4using System.Text.RegularExpressions;
5using System.Threading.Tasks;
6using Waher.Events;
18
20{
25 {
26 private static LedgerConfiguration instance = null;
27 internal static readonly Regex FromSaveUnsaved = new Regex(@"Waher[.]Persistence[.]Files[.]ObjectBTreeFile[.+]<SaveUnsaved>\w*[.]\w*",
28 RegexOptions.Compiled | RegexOptions.Singleline);
29 internal static readonly Regex FromUpdateObject = new Regex(@"Waher[.]Persistence[.]Files[.]ObjectBTreeFile[.+]<UpdateObject>\w*[.]\w*",
30 RegexOptions.Compiled | RegexOptions.Singleline);
31 internal static readonly Regex GatewayStartup = new Regex(@"Waher[.]IoTGateway[.]Gateway([.]Start|[.+]<Start>\w*[.]\w*)*",
32 RegexOptions.Compiled | RegexOptions.Singleline);
33 internal static readonly Regex LegalComponent = new Regex(@"Waher[.]Service[.]IoTBroker[.]Legal[.]LegalComponent[.](ApplyHandler|UpdateState|CreateContractHandler|SignContractHandler|ObsoleteContractHandler|DeleteContractHandler|UpdateContractHandler)",
34 RegexOptions.Compiled | RegexOptions.Singleline);
35 internal static readonly Regex PaiwiseComponent = new Regex(@"Waher[.]Service[.]IoTBroker[.]Paiwise[.]PaiwiseProcessor[.]GenerateContractualPaymentUri",
36 RegexOptions.Compiled | RegexOptions.Singleline);
37 internal static readonly Regex NeuroFeaturesComponent = new Regex(@"Waher[.]Service[.]IoTBroker[.]NeuroFeatures[.]NeuroFeaturesProcessor[.]CreateTokens",
38 RegexOptions.Compiled | RegexOptions.Singleline);
39 internal static readonly Regex UnitTests = new Regex(@"Waher[.]Service[.]IoTBroker[.]Test[.](LegalIdentitiesTests|SmartContractsTests)",
40 RegexOptions.Compiled | RegexOptions.Singleline);
41 private static readonly object[] approvedSources = new object[]
42 {
43 FromSaveUnsaved,
44 FromUpdateObject,
45 GatewayStartup,
46 typeof(LedgerConfiguration),
47 typeof(NeuroLedgerProvider),
48 typeof(NeuroLedgerModule),
49 LegalComponent,
50 PaiwiseComponent,
51 NeuroFeaturesComponent // Hard coded, to make sure other components cannot register new approved sources.
52 };
53 private static readonly object[] approvedSources2 = new object[]
54 {
55 FromSaveUnsaved,
56 FromUpdateObject,
57 GatewayStartup,
58 typeof(LedgerConfiguration),
59 typeof(NeuroLedgerProvider),
60 typeof(NeuroLedgerModule),
61 LegalComponent,
62 PaiwiseComponent,
63 NeuroFeaturesComponent, // Hard coded, to make sure other components cannot register new approved sources.
64 UnitTests
65 };
66
67 private string dataProtectionAgreementId = string.Empty;
68 private byte[] privateKey = null;
69 private byte[] salt = null;
70 private int collectionTimeSeconds = 5 * 60;
71 private int blockSizeThreshold = 1 * 1024 * 1024; // 1 MB
72 private Edwards448 ed448 = null;
73 private readonly SHA3_512 sha3_512 = new SHA3_512();
74
75 private HttpResource setLedgerProperties = null;
76
81 : base()
82 {
83 }
84
88 [DefaultValueNull]
89 public byte[] PrivateKey
90 {
91 get
92 {
93 Assert.CallFromSource(approvedSources);
94 return this.privateKey;
95 }
96
97 set
98 {
99 if (!(this.privateKey is null))
100 Assert.CallFromSource(approvedSources);
101
102 this.privateKey = value;
103 this.ed448 = new Edwards448(this.privateKey);
104 }
105 }
106
110 [DefaultValueNull]
111 public byte[] PublicKey
112 {
113 get
114 {
115 if (this.CheckKey() && this.ObjectId != Guid.Empty)
116 {
117 Task _ = Database.UpdateLazy(this);
118 }
119
120 return this.ed448.PublicKey;
121 }
122 }
123
127 [DefaultValueNull]
128 public byte[] Salt
129 {
130 get
131 {
132 Assert.CallFromSource(approvedSources);
133 return this.salt;
134 }
135
136 set
137 {
138 if (!(this.salt is null))
139 Assert.CallFromSource(approvedSources);
140
141 this.salt = value;
142 }
143 }
144
148 [DefaultValueStringEmpty]
150 {
151 get
152 {
153 Assert.CallFromSource(approvedSources);
154 return this.dataProtectionAgreementId;
155 }
156
157 set
158 {
159 if (!string.IsNullOrEmpty(this.dataProtectionAgreementId))
160 Assert.CallFromSource(approvedSources);
161
162 this.dataProtectionAgreementId = value;
163 }
164 }
165
169 public string SignatureAlgorithm
170 {
171 get
172 {
173 if (this.CheckKey())
174 {
175 Task _ = Database.UpdateLazy(this);
176 }
177
178 return this.ed448.CurveName;
179 }
180 }
181
182 private bool CheckKey()
183 {
184 bool Result = false;
185
186 if (this.privateKey is null)
187 {
188 this.privateKey = Gateway.NextBytes(56);
189 this.ed448 = new Edwards448(this.privateKey);
190 Result = true;
191 }
192
193 if (this.salt is null)
194 {
195 this.salt = Gateway.NextBytes(64);
196 Result = true;
197 }
198
199 return Result;
200 }
201
205 public string LedgerFolder => Path.Combine(Gateway.AppDataFolder, "Ledger");
206
211 {
212 get => this.collectionTimeSeconds;
213 set => this.collectionTimeSeconds = value;
214 }
215
220 {
221 get => this.blockSizeThreshold;
222 set => this.blockSizeThreshold = value;
223 }
224
228 public static LedgerConfiguration Instance => instance;
229
233 public override string Resource => "/Settings/Ledger.md";
234
238 public override int Priority => 350;
239
245 public override Task<string> Title(Language Language)
246 {
247 return Language.GetStringAsync(typeof(LedgerConfiguration), 1, "Neuro-Ledger");
248 }
249
254 public override void SetStaticInstance(ISystemConfiguration Configuration)
255 {
256 instance = Configuration as LedgerConfiguration;
257 }
258
262 public override async Task ConfigureSystem()
263 {
264 if (this.CheckKey())
265 await Database.Update(this);
266
268 {
269 string LedgerFolder = this.LedgerFolder;
270
272 TimeSpan.FromSeconds(this.collectionTimeSeconds), this.blockSizeThreshold, this.salt, "Default",
273 XmppConfiguration.Instance.BareJid, this.ed448, this.sha3_512.ComputeVariable, false);
274
275 Ledger.Register(Provider);
276
277 string[] RepairedCollections = DatabaseConfiguration.RepairedCollections;
278 string FileName;
279
280 if (!(RepairedCollections is null))
281 {
282 foreach (string Collection in RepairedCollections)
283 this.AddRepairNoteFile(Collection);
284 }
285
286 FileName = Path.Combine(this.RepairFolder, BlockReference.BlockReferencesCollection + ".txt");
287 if (File.Exists(FileName))
288 {
289 try
290 {
291 Log.Informational("Repairing collection from ledger.", BlockReference.BlockReferencesCollection);
292 await Provider.RepairRegistry();
293 Log.Informational("Repaired collection from ledger.", BlockReference.BlockReferencesCollection);
294
295 try
296 {
297 File.Delete(FileName);
298 }
299 catch (Exception ex)
300 {
301 Log.Error(ex, FileName);
302 }
303 }
304 catch (Exception ex2)
305 {
307 }
308 }
309
310 string[] ToRepair = Directory.GetFiles(this.RepairFolder, "*.txt", SearchOption.TopDirectoryOnly);
311 foreach (string FileName2 in ToRepair)
312 {
313 string CollectionName = Path.GetFileName(FileName2);
314 CollectionName = CollectionName.Substring(0, CollectionName.Length - 4);
315
316 try
317 {
318 Log.Informational("Repairing collection from ledger.", CollectionName);
319
320 if (CollectionName == BlockReference.BlockReferencesCollection)
321 await Provider.RepairRegistry();
322 else
323 await Provider.RepairCollection(CollectionName);
324
325 Log.Informational("Repaired collection from ledger.", CollectionName);
326
327 try
328 {
329 File.Delete(FileName2);
330 }
331 catch (Exception ex)
332 {
333 Log.Error(ex, FileName2);
334 }
335 }
336 catch (Exception ex2)
337 {
338 Log.Exception(ex2, FileName2);
339 }
340 }
341 }
342 else
343 Log.Warning("Ledger settings changed. Restart the system for changes to take effect.");
344 }
345
349 private string RepairFolder
350 {
351 get
352 {
353 string Result = Path.Combine(this.LedgerFolder, "Repair");
354 if (!Directory.Exists(Result))
355 Directory.CreateDirectory(Result);
356
357 return Result;
358 }
359 }
360
361 private string AddRepairNoteFile(string Collection)
362 {
363 string FileName = Path.Combine(this.RepairFolder, Collection + ".txt");
364
365 try
366 {
367 if (!File.Exists(FileName))
368 File.WriteAllText(FileName, DateTime.UtcNow.ToString());
369 }
370 catch (Exception ex)
371 {
372 Log.Exception(ex);
373 }
374
375 return FileName;
376 }
377
378 internal async Task Database_CollectionRepaired(object Sender, CollectionEventArgs e)
379 {
380 try
381 {
382 if (!(Ledger.Provider is NeuroLedgerProvider Provider))
383 return;
384
385 string FileName = this.AddRepairNoteFile(e.Collection);
386
387 lock (this.repairQueue)
388 {
389 if (this.repairing)
390 {
391 this.repairQueue.AddLast(FileName);
392 return;
393 }
394
395 this.repairing = true;
396 }
397
398 while (!string.IsNullOrEmpty(FileName))
399 {
400 string CollectionName = Path.GetFileName(FileName);
401 CollectionName = CollectionName.Substring(0, CollectionName.Length - 4);
402
403 if (CollectionName == BlockReference.BlockReferencesCollection)
404 await Provider.RepairRegistry();
405 else
406 await Provider.RepairCollection(CollectionName);
407
408 try
409 {
410 File.Delete(FileName);
411 }
412 catch (Exception ex)
413 {
414 Log.Exception(ex, FileName);
415 }
416
417 lock (this.repairQueue)
418 {
419 if (this.repairQueue.First is null)
420 {
421 FileName = null;
422 this.repairing = false;
423 }
424 else
425 {
426 FileName = this.repairQueue.First.Value;
427 this.repairQueue.RemoveFirst();
428 }
429 }
430 }
431 }
432 catch (Exception ex)
433 {
434 Log.Exception(ex);
435 }
436 }
437
438 private readonly LinkedList<string> repairQueue = new LinkedList<string>();
439 private bool repairing = false;
440
445 public override Task<bool> SimplifiedConfiguration()
446 {
447 return Task.FromResult(true);
448 }
449
454 public override Task InitSetup(HttpServer WebServer)
455 {
456 this.setLedgerProperties = WebServer.Register("/Settings/SetLedgerProperties", null, this.SetLedgerProperties, true, false, true);
457
458 return base.InitSetup(WebServer);
459 }
460
465 public override Task UnregisterSetup(HttpServer WebServer)
466 {
467 WebServer.Unregister(this.setLedgerProperties);
468
469 return base.UnregisterSetup(WebServer);
470 }
471
475 protected override string ConfigPrivilege => "Admin.NeuroLedger.Settings";
476
477 private async Task SetLedgerProperties(HttpRequest Request, HttpResponse Response)
478 {
479 Gateway.AssertUserAuthenticated(Request, this.ConfigPrivilege);
480
481 if (!Request.HasData)
482 throw new BadRequestException("Invalid request");
483
484 object Obj = await Request.DecodeDataAsync();
485 if (!(Obj is IDictionary<string, object> Data))
486 throw new UnsupportedMediaTypeException("Expected form.");
487
488 if (!Data.TryGetValue("collectionTime", out Obj) ||
489 !(Obj is string CollectionTimeStr) ||
490 !int.TryParse(CollectionTimeStr, out int CollectionTime) ||
491 CollectionTime <= 0 ||
492 CollectionTime > 3600)
493 {
494 throw new BadRequestException("Collection time can be between 1 and 3600 seconds.");
495 }
496
497 if (!Data.TryGetValue("blockSizeThreshold", out Obj) ||
498 !(Obj is string BlockSizeThresholdStr) ||
499 !int.TryParse(BlockSizeThresholdStr, out int BlockSizeThreshold) ||
500 BlockSizeThreshold <= 0 ||
501 BlockSizeThreshold > 1024 * 1024 * 1024)
502 {
503 throw new BadRequestException("Block Size Threshold can be between 1 and 1073741824 bytes (1 GB).");
504 }
505
506 this.collectionTimeSeconds = CollectionTime;
507 this.blockSizeThreshold = BlockSizeThreshold;
508
509 await Database.Update(this);
510
511 Response.StatusCode = 200;
512 }
513
520 public static byte[] Sign(byte[] Data)
521 {
522 if (DomainConfiguration.Instance is null)
523 Assert.CallFromSource(approvedSources2);
524 else
525 Assert.CallFromSource(approvedSources);
526
527 return instance?.ed448?.Sign(Data);
528 }
529
536 public static byte[] Sign(Stream Data)
537 {
538 if (DomainConfiguration.Instance is null)
539 Assert.CallFromSource(approvedSources2);
540 else
541 Assert.CallFromSource(approvedSources);
542
543 return instance?.ed448?.Sign(Data);
544 }
545
552 public static bool Verify(byte[] Data, byte[] Signature)
553 {
554 return instance?.ed448?.Verify(Data, instance?.PublicKey, Signature) ?? false;
555 }
556
561
565 public const string NEURO_LEDGER_MAXSIZE = nameof(NEURO_LEDGER_MAXSIZE);
566
571 public override Task<bool> EnvironmentConfiguration()
572 {
573 string Value = Environment.GetEnvironmentVariable(NEURO_LEDGER_COLLECTION);
574 if (!string.IsNullOrEmpty(Value))
575 {
576 if (!int.TryParse(Value, out int i))
577 {
578 this.LogEnvironmentVariableInvalidIntegerError(NEURO_LEDGER_COLLECTION, Value);
579 return Task.FromResult(false);
580 }
581
582 if (i < 1 || i > 3600)
583 {
584 this.LogEnvironmentVariableInvalidRangeError(1, 3600, NEURO_LEDGER_COLLECTION, i);
585 return Task.FromResult(false);
586 }
587
588 this.collectionTimeSeconds = i;
589 }
590
591 Value = Environment.GetEnvironmentVariable(NEURO_LEDGER_MAXSIZE);
592 if (!string.IsNullOrEmpty(Value))
593 {
594 if (!int.TryParse(Value, out int i))
595 {
596 this.LogEnvironmentVariableInvalidIntegerError(NEURO_LEDGER_MAXSIZE, Value);
597 return Task.FromResult(false);
598 }
599
600 if (i < 1 || i > 1073741824)
601 {
602 this.LogEnvironmentVariableInvalidRangeError(1, 1073741824, NEURO_LEDGER_MAXSIZE, i);
603 return Task.FromResult(false);
604 }
605
606 this.blockSizeThreshold = i;
607 }
608
609 return Task.FromResult(true);
610 }
611 }
612}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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
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:334
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
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:3041
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:3534
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:2369
static string[] RepairedCollections
Collections repaired during startup.
static DomainConfiguration Instance
Current instance of configuration.
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:18
bool HasData
If the request has data.
Definition: HttpRequest.cs:74
async Task< object > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:95
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
Implements an HTTP server.
Definition: HttpServer.cs:36
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
Definition: HttpServer.cs:1287
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1438
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:19
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:687
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
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 void CallFromSource(params string[] Sources)
Makes sure the call is made from one of the listed sources.
Definition: Assert.cs:39
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, byte[] Signature)
Verifies a signature of Data made by the EdDSA algorithm.
Definition: Edwards448.cs:129
override byte[] Sign(byte[] Data)
Creates a signature of Data using the EdDSA algorithm.
Definition: Edwards448.cs:106
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:13
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.
override string Resource
Resource to be redirected to, to perform the configuration.
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...