From f6e7c291035f90a8686aa26c048af7d25ac3008a Mon Sep 17 00:00:00 2001 From: Rafael Teixeira Date: Tue, 16 Apr 2024 21:04:46 -0300 Subject: [PATCH] WIP - Totally refactored (incompatible) API --- .../Abstractions/Globals.cs | 25 +- .../Abstractions/RestAbstractChain.cs | 48 ++- .../Abstractions/RestAbstractNode.cs | 140 ++++----- .../Abstractions/RestNetwork.cs | 8 +- .../ApiException.cs} | 10 +- .../InterlockingsImplementation.cs | 18 +- .../RecordsAsJsonStoreImplementation.cs} | 26 +- .../RecordsStoreImplementation.cs} | 28 +- ...RestInterlockings.cs => IInterlockings.cs} | 8 +- ...IRestRecords.cs => IRecordsAsJsonStore.cs} | 14 +- .../Interfaces/IRecordsStore.cs | 42 +++ .../Interfaces/IRestChain.cs | 19 +- .../Interfaces/IRestRecordsAsJson.cs | 46 --- .../InterlockLedger.Rest.Client.csproj | 19 +- .../Models/AppsModel.cs | 4 +- .../Models/CertificatePermitModel.cs | 15 +- .../Models/ChainCreatedModel.cs | 44 +++ .../Models/ChainCreationModel.cs | 44 ++- .../Models/ChainIdModel.cs | 12 +- .../Models/ChainSummaryModel.cs | 6 +- .../Models/ColorJsonConverter.cs | 2 +- .../Models/DocumentDetailsModel.cs | 110 ------- .../Models/Enumerations/RecordType.cs | 10 +- .../Models/ExportedKeyFile.cs | 40 +++ .../Models/ForceInterlockModel.cs | 11 +- .../Models/InterlockingRecordModel.cs | 4 +- .../Models/KeyModel.cs | 10 +- ...{MessageModel.cs => KeyPermitBaseModel.cs} | 41 ++- .../Models/KeyPermitModel.cs | 39 +-- .../Models/NewRecordModel.cs | 2 +- .../Models/NewRecordModelAsJson.cs | 2 +- .../Models/NodeCommonModel.cs | 20 +- .../Models/NodeDetailsModel.cs | 4 +- .../Models/PeerModel.cs | 4 +- .../Models/RecordModel.cs | 2 +- .../Models/RecordModelAsJson.cs | 2 +- .../Models/RecordModelBase.cs | 6 +- .../Models/{ => Types}/AppPermissions.cs | 4 +- .../Models/{ => Types}/PublishedApp.cs | 22 +- .../Models/{ => Types}/Versions.cs | 12 +- .../Models/VersionJsonConverter.cs | 4 +- .../Extensions}/X509Certificate2Extensions.cs | 13 +- .../DocumentRegistryImplementation.cs | 28 +- .../JsonStoreImplementation.cs | 21 +- .../OpaqueStoreImplementation.cs | 18 +- .../Interfaces/IDocumentRegistry.cs} | 40 ++- .../{V6_0 => V13_7}/Interfaces/IJsonStore.cs | 10 +- .../Interfaces/INodeWithDocumentRegistry.cs | 2 +- .../Interfaces/IOpaqueStore.cs | 12 +- .../V13_7/Models/AllowedReadersRecordModel.cs | 43 +++ .../Models/DocumentsBeginTransactionModel.cs | 4 +- .../Models/DocumentsMetadataModel.cs | 4 +- .../Models/DocumentsTransactionModel.cs | 16 +- .../Models/DocumentsUploadConfiguration.cs | 8 +- .../Models/EncryptedTextModel.cs | 10 +- .../Models/EncryptedTextModelExtensions.cs | 8 +- .../Models/JsonDocumentModel.cs | 6 +- .../Models/OpaqueRecordModel.cs | 28 +- .../Models/PageOfAllowedReadersRecordModel.cs | 6 +- .../Models/PageOfOpaqueRecordsModel.cs | 4 +- .../Models/ReaderModel.cs} | 12 +- .../{V6_0 => V13_7}/Models/ReadingKeyModel.cs | 10 +- .../RestChainV13_7.cs} | 14 +- .../RestNodeV13_7.cs} | 18 +- .../V6_0/Interfaces/IDocumentRegistry.cs | 76 ----- .../V6_0/RestNodeV6_0.cs | 50 --- .../V7_6/RestChainV7_6.cs | 51 ---- rest_client/AbstractUsing.cs | 287 +++++++++--------- rest_client/Program.cs | 21 +- rest_client/Properties/launchSettings.json | 2 +- rest_client/UsingV13_7.cs | 100 ++++++ rest_client/UsingV7_6.cs | 74 ----- 72 files changed, 867 insertions(+), 1056 deletions(-) rename InterlockLedger.Rest.Client/{V6_0/Interfaces/IRestChainV6_0.cs => Exceptions/ApiException.cs} (93%) rename InterlockLedger.Rest.Client/{Abstractions => Implementations}/InterlockingsImplementation.cs (77%) rename InterlockLedger.Rest.Client/{Abstractions/RecordsAsJsonImplementation.cs => Implementations/RecordsAsJsonStoreImplementation.cs} (69%) rename InterlockLedger.Rest.Client/{Abstractions/RecordsImplementation.cs => Implementations/RecordsStoreImplementation.cs} (63%) rename InterlockLedger.Rest.Client/Interfaces/{IRestInterlockings.cs => IInterlockings.cs} (87%) rename InterlockLedger.Rest.Client/Interfaces/{IRestRecords.cs => IRecordsAsJsonStore.cs} (81%) create mode 100644 InterlockLedger.Rest.Client/Interfaces/IRecordsStore.cs delete mode 100644 InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs rename rest_client/UsingV6_0.cs => InterlockLedger.Rest.Client/Models/CertificatePermitModel.cs (81%) create mode 100644 InterlockLedger.Rest.Client/Models/ChainCreatedModel.cs delete mode 100644 InterlockLedger.Rest.Client/Models/DocumentDetailsModel.cs create mode 100644 InterlockLedger.Rest.Client/Models/ExportedKeyFile.cs rename InterlockLedger.Rest.Client/Models/{MessageModel.cs => KeyPermitBaseModel.cs} (67%) rename InterlockLedger.Rest.Client/Models/{ => Types}/AppPermissions.cs (96%) rename InterlockLedger.Rest.Client/Models/{ => Types}/PublishedApp.cs (85%) rename InterlockLedger.Rest.Client/Models/{ => Types}/Versions.cs (90%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7/Extensions}/X509Certificate2Extensions.cs (90%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7/Implementations}/DocumentRegistryImplementation.cs (77%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7/Implementations}/JsonStoreImplementation.cs (70%) rename InterlockLedger.Rest.Client/{V7_6 => V13_7/Implementations}/OpaqueStoreImplementation.cs (78%) rename InterlockLedger.Rest.Client/{V7_6/RestNodeV7_6.cs => V13_7/Interfaces/IDocumentRegistry.cs} (50%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Interfaces/IJsonStore.cs (85%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Interfaces/INodeWithDocumentRegistry.cs (97%) rename InterlockLedger.Rest.Client/{V7_6 => V13_7}/Interfaces/IOpaqueStore.cs (75%) create mode 100644 InterlockLedger.Rest.Client/V13_7/Models/AllowedReadersRecordModel.cs rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/DocumentsBeginTransactionModel.cs (98%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/DocumentsMetadataModel.cs (98%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/DocumentsTransactionModel.cs (91%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/DocumentsUploadConfiguration.cs (94%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/EncryptedTextModel.cs (90%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/EncryptedTextModelExtensions.cs (93%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/JsonDocumentModel.cs (93%) rename InterlockLedger.Rest.Client/{V7_6 => V13_7}/Models/OpaqueRecordModel.cs (79%) rename rest_client/KnownVersions.cs => InterlockLedger.Rest.Client/V13_7/Models/PageOfAllowedReadersRecordModel.cs (93%) rename InterlockLedger.Rest.Client/{V7_6 => V13_7}/Models/PageOfOpaqueRecordsModel.cs (97%) rename InterlockLedger.Rest.Client/{V7_6/Interfaces/IRestChainV7_6.cs => V13_7/Models/ReaderModel.cs} (88%) rename InterlockLedger.Rest.Client/{V6_0 => V13_7}/Models/ReadingKeyModel.cs (89%) rename InterlockLedger.Rest.Client/{V6_0/RestChainV6_0.cs => V13_7/RestChainV13_7.cs} (81%) rename InterlockLedger.Rest.Client/{V6_0/RestAbstractNodeWithDocumentRegistry.cs => V13_7/RestNodeV13_7.cs} (74%) delete mode 100644 InterlockLedger.Rest.Client/V6_0/Interfaces/IDocumentRegistry.cs delete mode 100644 InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs delete mode 100644 InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs create mode 100644 rest_client/UsingV13_7.cs delete mode 100644 rest_client/UsingV7_6.cs diff --git a/InterlockLedger.Rest.Client/Abstractions/Globals.cs b/InterlockLedger.Rest.Client/Abstractions/Globals.cs index 885d01b..a306e58 100644 --- a/InterlockLedger.Rest.Client/Abstractions/Globals.cs +++ b/InterlockLedger.Rest.Client/Abstractions/Globals.cs @@ -30,18 +30,17 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client +namespace InterlockLedger.Rest.Client; + +internal static class Globals { - internal static class Globals - { - public static readonly JsonSerializerOptions JsonSettings = new() { - AllowTrailingCommas = true, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Skip, - WriteIndented = true, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace, - }; - } + public static readonly JsonSerializerOptions JsonSettings = new() { + AllowTrailingCommas = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Skip, + WriteIndented = true, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace, + }; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/RestAbstractChain.cs b/InterlockLedger.Rest.Client/Abstractions/RestAbstractChain.cs index 8a47cf3..de7e066 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RestAbstractChain.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RestAbstractChain.cs @@ -35,47 +35,43 @@ namespace InterlockLedger.Rest.Client.Abstractions; [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] -public abstract class RestAbstractChain : IRestChain +public abstract class RestAbstractChain : IRestChain where T : IRestChain { public string Id { get; } - IRestInterlockings IRestChain.Interlockings => _interlockings; - public string Name { get; } + public IInterlockings Interlockings { get; } + public string? Name { get; } - IRestRecords IRestChain.Records => _records; + public IRecordsStore Records { get; } - IRestRecordsAsJson IRestChain.RecordsAsJson => _recordsAsJson; + public IRecordsAsJsonStore RecordsAsJson { get; } - Task> IRestChain.GetActiveAppsAsync() => _rest.GetAsync>($"/chain/{Id}/activeApps"); + public Task?> GetActiveAppsAsync() => _node.GetAsync>($"/chain/{Id}/activeApps"); - Task> IRestChain.GetPermittedKeysAsync() => _rest.GetAsync>($"/chain/{Id}/key"); + public Task?> GetPermittedKeysAsync() => _node.GetAsync>($"/chain/{Id}/key"); - Task IRestChain.GetSummaryAsync() => _rest.GetAsync($"/chain/{Id}"); + public Task GetSummaryAsync() => _node.GetAsync($"/chain/{Id}"); - Task> IRestChain.PermitAppsAsync(params ulong[] appsToPermit) - => _rest.PostAsync>($"/chain/{Id}/activeApps", appsToPermit); + public Task?> PermitAppsAsync(params ulong[] appsToPermit) + => _node.PostAsync>($"/chain/{Id}/activeApps", appsToPermit); - Task> IRestChain.PermitKeysAsync(params KeyPermitModel[] keysToPermit) - => _rest.PostAsync>($"/chain/{Id}/key", keysToPermit); + public Task?> PermitKeysAsync(params KeyPermitModel[] keysToPermit) + => _node.PostAsync>($"/chain/{Id}/key", keysToPermit); - public Task> RecordsFromAsync(ulong firstSerial) - => _rest.GetAsync>($"records@{Id}?firstSerial={firstSerial}"); + public Task?> RecordsFromAsync(ulong firstSerial) + => _node.GetAsync>($"records@{Id}?firstSerial={firstSerial}"); public override string ToString() => $"Chain '{Name}' #{Id}"; - internal readonly IRestNodeInternals _rest; + internal readonly RestAbstractNode _node; - internal RestAbstractChain(IRestNodeInternals rest, ChainIdModel chainId) { - _rest = rest.Required(); - Id = chainId.Required().Id; + internal RestAbstractChain(RestAbstractNode node, ChainIdModel chainId) { + _node = node.Required(); + Id = chainId.Required().Id.Required(); Name = chainId.Name; - _records = new RecordsImplementation(this); - _recordsAsJson = new RecordsAsJsonImplementation(this); - _interlockings = new InterlockingsImplementation(this); + Records = new RecordsStoreImplementation(this); + RecordsAsJson = new RecordsAsJsonStoreImplementation(this); + Interlockings = new InterlockingsImplementation(this); } - private readonly IRestInterlockings _interlockings; - private readonly IRestRecords _records; - private readonly IRestRecordsAsJson _recordsAsJson; - - private string GetDebuggerDisplay() => $"{this} at {_rest}"; + private string GetDebuggerDisplay() => $"{this} at {_node}"; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs b/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs index 0c52fef..e74df10 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs @@ -30,6 +30,8 @@ // // ****************************************************************************************************************************** + + using System.Diagnostics; using System.Net; using System.Net.Mime; @@ -39,7 +41,7 @@ namespace InterlockLedger.Rest.Client.Abstractions; [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] -public abstract class RestAbstractNode : IRestNodeInternals where T : IRestChain +public abstract class RestAbstractNode where T : IRestChain { public RestAbstractNode(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId = NetworkPredefinedPorts.MainNet, string address = "localhost") : this(x509Certificate, (ushort)networkId, address) { } @@ -47,7 +49,7 @@ public RestAbstractNode(X509Certificate2 x509Certificate, NetworkPredefinedPorts public RestAbstractNode(X509Certificate2 x509Certificate, ushort port, string address = "localhost") { _certificate = x509Certificate; BaseUri = new Uri($"https://{address}:{port}/", UriKind.Absolute); - Network = new RestNetwork(this); + Network = new RestNetwork(this); } public RestAbstractNode(string certFile, string certPassword, NetworkPredefinedPorts networkId = NetworkPredefinedPorts.MainNet, string address = "localhost") @@ -59,41 +61,25 @@ public RestAbstractNode(string certFile, string certPassword, ushort port, strin public Uri BaseUri { get; } public X509Certificate2 Certificate => _certificate; public string CertificateName => _certificate.FriendlyName.WithDefault(_certificate.Subject); - public RestNetwork Network { get; } + public RestNetwork Network { get; } - public Task> AddMirrorsOfAsync(IEnumerable newMirrors) + public Task?> AddMirrorsOfAsync(IEnumerable newMirrors) => PostAsync>("/mirrors", newMirrors); - Task IRestNodeInternals.CallApiPlainDocAsync(string url, string method, string accept) - => CallApiPlainDocAsync(url, method, accept); - - Task IRestNodeInternals.CallApiRawDocAsync(string url, string method, string accept) - => CallApiRawDocAsync(url, method, accept); - - public Task CreateChainAsync(ChainCreationModel model) + public Task CreateChainAsync(ChainCreationModel model) => PostAsync($"/chain", model); - Task IRestNodeInternals.GetAsync(string url) => GetAsync(url); - - public async Task> GetChainsAsync() => (await GetAsync>("/chain")).Select(c => BuildChain(c)); + public async Task> GetChainsAsync() => (await GetAsync>("/chain")).Safe().Select(c => BuildChain(c)); - public Task GetDetailsAsync() => GetAsync("/"); + public Task GetDetailsAsync() => GetAsync("/"); - public async Task> GetMirrorsAsync() => (await GetAsync>("/mirrors")).Select(c => BuildChain(c)); + public async Task> GetMirrorsAsync() => (await GetAsync>("/mirrors")).Safe().Select(c => BuildChain(c)); - public Task> GetPeersAsync() => GetAsync>("/peers"); + public Task?> GetPeersAsync() => GetAsync>("/peers"); - public async Task> InterlocksOfAsync(string chain) + public async Task?> InterlocksOfAsync(string chain) => await GetAsync>($"/interlockings/{chain}"); - Task IRestNodeInternals.PostAsync(string url, object body) => PostAsync(url, body); - - Task IRestNodeInternals.PostRawAsync(string url, byte[] body, string contentType) - => PostRawAsync(url, body, contentType); - - Task IRestNodeInternals.PostStreamAsync(string url, Stream body, string contentType) - => PostStreamAsync(url, body, contentType); - public override string ToString() => $"Node [{BaseUri}] connected with certificate '{CertificateName}'"; protected internal readonly X509Certificate2 _certificate; @@ -102,12 +88,35 @@ protected internal static async Task GetResponseAsync(HttpWebRe try { return (HttpWebResponse)await req.GetResponseAsync().ConfigureAwait(false); } catch (WebException e) { - return (HttpWebResponse)e.Response - ?? throw new InvalidOperationException($"Could not retrieve response at {req.Address}", e); + return e.Response as HttpWebResponse ?? throw new InvalidOperationException($"Could not retrieve response at {req.Address}", e); + } + } + + protected internal static async Task GetStringResponseAsync(HttpWebRequest req) + => (await IfOkOrCreatedReturnAsync(await GetResponseAsync(req), ReadAsStringAsync)).WithDefault(string.Empty); + + private static async Task IfOkOrCreatedReturnAsync(HttpWebResponse resp, Func> buildResult) { + return resp.StatusCode switch { + HttpStatusCode.OK or HttpStatusCode.Created => await buildResult(resp), + HttpStatusCode.NotFound => default, + _ => await CheckMessageAsync(resp) + }; + + static async Task CheckMessageAsync(HttpWebResponse resp) { + string content = await ReadAsStringAsync(resp).ConfigureAwait(false); + if (content.Safe().Contains("outstanding", StringComparison.OrdinalIgnoreCase)) + return default; + else { + string exceptionMessage = $"{resp.StatusCode}(#{(int)resp.StatusCode}){Environment.NewLine}{content}"; + return resp.StatusCode switch { + HttpStatusCode.Unauthorized => throw new SecurityException(exceptionMessage), + HttpStatusCode.Forbidden => throw new SecurityException(exceptionMessage), + _ => throw new ApiException(exceptionMessage), + }; + } } } - protected internal static async Task GetStringResponseAsync(HttpWebRequest req) => await ReadAsStringAsync(await GetResponseAsync(req)); protected internal static async Task ReadAsStringAsync(HttpWebResponse resp) { if (resp == null) @@ -124,12 +133,13 @@ protected internal async Task CallApiAsync(string url, string method, st protected internal async Task CallApiPlainDocAsync(string url, string method, string accept = "plain/text") => await GetStringResponseAsync(PrepareRequest(url, method, accept)); - protected internal async Task CallApiRawDocAsync(string url, string method, string accept = "*") + protected internal async Task CallApiRawDocAsync(string url, string method, string accept = "*") => await GetRawResponseAsync(PrepareRequest(url, method, accept)); - protected internal async Task GetAsync(string url) => Deserialize(await CallApiAsync(url, "GET").ConfigureAwait(false)); + protected internal async Task GetAsync(string url) + => Deserialize(await CallApiAsync(url, "GET").ConfigureAwait(false)); - protected internal async Task<(string Name, string ContentType, Stream Content)> GetFileReadStreamAsync(string url, string accept = "*/*", string method = "GET") { + protected internal async Task<(string Name, string ContentType, Stream Content)?> GetFileReadStreamAsync(string url, string accept = "*/*", string method = "GET") { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentException($"'{nameof(url)}' cannot be null or empty", nameof(url)); if (string.IsNullOrWhiteSpace(accept)) @@ -137,22 +147,25 @@ protected internal async Task CallApiRawDocAsync(string url, s if (string.IsNullOrWhiteSpace(method)) throw new ArgumentException($"'{nameof(method)}' cannot be null or empty", nameof(method)); var resp = await GetResponseAsync(PrepareRequest(url, method, accept)); - return (ParseFileName(resp), resp.ContentType, resp.GetResponseStream()); + return resp.StatusCode == HttpStatusCode.OK ? (ParseFileName(resp), resp.ContentType, resp.GetResponseStream()) : null; } - protected internal async Task<(ulong AppId, ulong PayloadTypeId, Stream Content)> GetOpaqueStreamAsync(string url) { + protected internal async Task<(ulong AppId, ulong PayloadTypeId, DateTimeOffset? CreatedAt, Stream Content)?> GetOpaqueStreamAsync(string url) { var resp = await GetResponseAsync(PrepareRequest(url.Required(), "GET", "application/octet-stream")); - return (ulong.Parse(resp.Headers["x-app-id"].WithDefault("0")), ulong.Parse(resp.Headers["x-payload-type-id"].WithDefault("0")), resp.GetResponseStream()); + if (resp.StatusCode != HttpStatusCode.OK) + return null; + DateTimeOffset? createdAt = DateTimeOffset.TryParse(resp.Headers["x-created-at"], out var parsedCreatedAt) ? parsedCreatedAt : null; + return (ulong.Parse(resp.Headers["x-app-id"].WithDefault("0")), ulong.Parse(resp.Headers["x-payload-type-id"].WithDefault("0")), createdAt, resp.GetResponseStream()); } - protected internal async Task PostAsync(string url, object body) + protected internal async Task PostAsync(string url, object? body) => Deserialize(await GetStringResponseAsync(PreparePostRequest(url, body, accept: "application/json"))); - protected internal async Task PostRawAsync(string url, byte[] body, string contentType) + protected internal async Task PostRawAsync(string url, byte[] body, string contentType) => Deserialize(await GetStringResponseAsync(PreparePostRawRequest(url, body, accept: "application/json", contentType))); - protected internal async Task PostStreamAsync(string url, Stream body, string contentType) { + protected internal async Task PostStreamAsync(string url, Stream body, string contentType) { var resp = await GetResponseAsync(PreparePostStreamRequest(url, body, accept: "*/*", contentType)); - return await IfOkOrCreatedReturnAsync(resp, async () => Deserialize(await ReadAsStringAsync(resp))); + return await IfOkOrCreatedReturnAsync(resp, async (rs) => Deserialize(await ReadAsStringAsync(rs))); } protected internal HttpWebRequest PrepareRequest(string url, string method, string accept) { @@ -169,7 +182,7 @@ protected internal HttpWebRequest PrepareRequest(string url, string method, stri return req; } - protected internal bool ServerCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + protected internal bool ServerCertificateValidation(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) => true; private static readonly Encoding _utf8WithoutBOM = new UTF8Encoding(false); @@ -181,27 +194,21 @@ private static byte[] ArrayConcat(byte[] firstBuffer, Memory secondBuffer, return concatBuffer; } - private static async Task CheckMessageAsync(HttpWebResponse resp) { - string content = await ReadAsStringAsync(resp).ConfigureAwait(false); - return content.Contains("outstanding", StringComparison.OrdinalIgnoreCase) - ? default(TR) - : throw new InvalidDataException($"API error: {resp.StatusCode}(#{(int)resp.StatusCode}) {resp.StatusDescription}{Environment.NewLine}{content}"); - } - - private static TR Deserialize(string s) { + private static TR? Deserialize(string s) { try { return JsonSerializer.Deserialize(s, Globals.JsonSettings); } catch (Exception e) { Console.Error.WriteLine(typeof(TR).FullName); - Console.Error.WriteLine(e); - return default(TR); + Console.Error.WriteLine(s); + Console.Error.WriteLine(e.Message); + return default; } } private static X509Certificate2 GetCertFromFile(string certPath, string certPassword) => new(certPath, certPassword, X509KeyStorageFlags.PersistKeySet); - private static async Task GetRawResponseAsync(HttpWebRequest req) { + private static async Task GetRawResponseAsync(HttpWebRequest req) { var response = await GetResponseAsync(req); if (response is null) return null; @@ -218,13 +225,7 @@ private static async Task GetRawResponseAsync(HttpWebRequest r return new RawDocumentModel(resp.ContentType, fullBuffer, ParseFileName(resp)); } - private static async Task IfOkOrCreatedReturnAsync(HttpWebResponse resp, Func> buildResult) => resp.StatusCode switch { - HttpStatusCode.OK or HttpStatusCode.Created => await buildResult(), - HttpStatusCode.NotFound => default, - HttpStatusCode.Unauthorized => throw new SecurityException(nameof(HttpStatusCode.Unauthorized)), - HttpStatusCode.Forbidden => throw new SecurityException(nameof(HttpStatusCode.Forbidden)), - _ => await CheckMessageAsync(resp) - }; + private static string ParseFileName(HttpWebResponse resp) { var disposition = resp.GetResponseHeader("Content-Disposition"); @@ -232,10 +233,10 @@ private static string ParseFileName(HttpWebResponse resp) { var filename = header.FileName; if (header.Parameters.ContainsKey("filename*")) { filename = header.Parameters["filename*"]; - if (filename.StartsWith("UTF-8''")) - return WebUtility.UrlDecode(filename[7..]); + if (filename.Safe().StartsWith("UTF-8''")) + return WebUtility.UrlDecode(filename![7..]); } - return filename; + return filename.WithDefault("?"); } private string GetDebuggerDisplay() => ToString(); @@ -250,7 +251,7 @@ private HttpWebRequest PreparePostRawRequest(string url, byte[] body, string acc return request; } - private HttpWebRequest PreparePostRequest(string url, object body, string accept) { + private HttpWebRequest PreparePostRequest(string url, object? body, string accept) { var request = PrepareRequest(url, "POST", accept); request.ContentType = "application/json; charset=utf-8"; using (var stream = request.GetRequestStream()) { @@ -271,19 +272,4 @@ private HttpWebRequest PreparePostStreamRequest(string url, Stream body, string } return request; } -} - -internal interface IRestNodeInternals -{ - Task CallApiPlainDocAsync(string url, string method, string accept = "text/plain"); - - Task CallApiRawDocAsync(string url, string method, string accept = "*/*"); - - Task GetAsync(string url); - - Task PostAsync(string url, object body); - - Task PostRawAsync(string url, byte[] body, string contentType); - - Task PostStreamAsync(string url, Stream body, string contentType); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/RestNetwork.cs b/InterlockLedger.Rest.Client/Abstractions/RestNetwork.cs index 8e3e14a..381c679 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RestNetwork.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RestNetwork.cs @@ -32,11 +32,11 @@ namespace InterlockLedger.Rest.Client.Abstractions; -public class RestNetwork +public class RestNetwork where T: IRestChain { - public Task GetAppsAsync() => _rest.GetAsync("/apps"); + public Task GetAppsAsync() => _node.GetAsync("/apps"); - internal RestNetwork(IRestNodeInternals rest) => _rest = rest ?? throw new ArgumentNullException(nameof(rest)); + internal RestNetwork(RestAbstractNode node) => _node = node.Required(); - private readonly IRestNodeInternals _rest; + private readonly RestAbstractNode _node; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/Interfaces/IRestChainV6_0.cs b/InterlockLedger.Rest.Client/Exceptions/ApiException.cs similarity index 93% rename from InterlockLedger.Rest.Client/V6_0/Interfaces/IRestChainV6_0.cs rename to InterlockLedger.Rest.Client/Exceptions/ApiException.cs index 1e44de8..551e4cb 100644 --- a/InterlockLedger.Rest.Client/V6_0/Interfaces/IRestChainV6_0.cs +++ b/InterlockLedger.Rest.Client/Exceptions/ApiException.cs @@ -1,5 +1,5 @@ // ****************************************************************************************************************************** -// +// // Copyright (c) 2018-2022 InterlockLedger Network // All rights reserved. // @@ -30,9 +30,7 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; - -public interface IRestChainV6_0 : IRestChain +namespace InterlockLedger.Rest.Client; +public class ApiException(string? message) : Exception(message) { - IJsonStore JsonStore { get; } -} \ No newline at end of file +} diff --git a/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs b/InterlockLedger.Rest.Client/Implementations/InterlockingsImplementation.cs similarity index 77% rename from InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs rename to InterlockLedger.Rest.Client/Implementations/InterlockingsImplementation.cs index 4c571bc..c433564 100644 --- a/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs +++ b/InterlockLedger.Rest.Client/Implementations/InterlockingsImplementation.cs @@ -32,21 +32,21 @@ namespace InterlockLedger.Rest.Client.Abstractions; -internal sealed class InterlockingsImplementation : IRestInterlockings +internal sealed class InterlockingsImplementation : IInterlockings where T: IRestChain { - public InterlockingsImplementation(RestAbstractChain parent) { + public InterlockingsImplementation(RestAbstractChain parent) { _parent = parent.Required(); - _rest = _parent._rest; + _node = _parent._node; _id = _parent.Id; } - public Task ForceInterlockAsync(ForceInterlockModel model) - => _rest.PostAsync($"/chain/{_id}/interlockings", model); + public Task ForceInterlockAsync(ForceInterlockModel model) + => _node.PostAsync($"/chain/{_id}/interlockings", model); - public Task> GetInterlocksAsync(ushort page = 0, byte pageSize = 10) - => _rest.GetAsync>($"/chain/{_id}/interlockings?page={page}&pageSize={pageSize}"); + public Task?> GetInterlocksAsync(ushort page = 0, byte pageSize = 10) + => _node.GetAsync>($"/chain/{_id}/interlockings?page={page}&pageSize={pageSize}"); private readonly string _id; - private readonly RestAbstractChain _parent; - private readonly IRestNodeInternals _rest; + private readonly RestAbstractChain _parent; + private readonly RestAbstractNode _node; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs b/InterlockLedger.Rest.Client/Implementations/RecordsAsJsonStoreImplementation.cs similarity index 69% rename from InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs rename to InterlockLedger.Rest.Client/Implementations/RecordsAsJsonStoreImplementation.cs index 8466297..2b065fd 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs +++ b/InterlockLedger.Rest.Client/Implementations/RecordsAsJsonStoreImplementation.cs @@ -32,30 +32,30 @@ namespace InterlockLedger.Rest.Client.Abstractions; -internal sealed class RecordsAsJsonImplementation : IRestRecordsAsJson +internal sealed class RecordsAsJsonStoreImplementation : IRecordsAsJsonStore where T : IRestChain { - public RecordsAsJsonImplementation(RestAbstractChain parent) { + public RecordsAsJsonStoreImplementation(RestAbstractChain parent) { _parent = parent.Required(); - _rest = _parent._rest; + _node = _parent._node; _id = _parent.Id; } - public Task AddAsync(NewRecordModelAsJson model) + public Task AddAsync(NewRecordModelAsJson model) => AddAsync(model.ApplicationId, model.PayloadTagId, model.Type, model.Json); - public Task AddAsync(ulong applicationId, ulong payloadTagId, object payload) + public Task AddAsync(ulong applicationId, ulong payloadTagId, object? payload) => AddAsync(applicationId, payloadTagId, RecordType.Data, payload); - public Task AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload) - => _rest.PostAsync($"records@{_id}/asJson?applicationId={applicationId}&payloadTagId={payloadTagId}&type={type}", payload); + public Task AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object? payload) + => _node.PostAsync($"records@{_id}/asJson?applicationId={applicationId}&payloadTagId={payloadTagId}&type={type}", payload); - public Task> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) - => _rest.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); + public Task?> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) + => _node.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); - public Task> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) - => _rest.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); + public Task?> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) + => _node.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); private readonly string _id; - private readonly RestAbstractChain _parent; - private readonly IRestNodeInternals _rest; + private readonly RestAbstractChain _parent; + private readonly RestAbstractNode _node; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs b/InterlockLedger.Rest.Client/Implementations/RecordsStoreImplementation.cs similarity index 63% rename from InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs rename to InterlockLedger.Rest.Client/Implementations/RecordsStoreImplementation.cs index 81b11e1..1f5504a 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs +++ b/InterlockLedger.Rest.Client/Implementations/RecordsStoreImplementation.cs @@ -32,30 +32,24 @@ namespace InterlockLedger.Rest.Client.Abstractions; -internal sealed class RecordsImplementation : IRestRecords +internal sealed class RecordsStoreImplementation : IRecordsStore where T: IRestChain { - public RecordsImplementation(RestAbstractChain parent) { + public RecordsStoreImplementation(RestAbstractChain parent) { _parent = parent.Required(); - _rest = _parent._rest; + _node = _parent._node; _id = _parent.Id; } - public Task AddRecordAsync(NewRecordModel model) - => _rest.PostAsync($"records@{_id}", model); + public Task AddRecordAsync(NewRecordModel model) + => _node.PostAsync($"records@{_id}", model); - public Task AddRecordAsync(ulong applicationId, ulong payloadTagId, byte[] bytes) - => AddRecordAsync(applicationId, payloadTagId, RecordType.Data, bytes); + public Task?> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) + => _node.GetAsync>($"records@{_id}?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); - public Task AddRecordAsync(ulong applicationId, ulong payloadTagId, RecordType type, byte[] bytes) - => _rest.PostRawAsync($"records@{_id}/with?applicationId={applicationId}&payloadTagId={payloadTagId}&type={type}", bytes, "application/interlockledger"); - - public Task> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) - => _rest.GetAsync>($"records@{_id}?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); - - public Task> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) - => _rest.GetAsync>($"records@{_id}?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); + public Task?> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false) + => _node.GetAsync>($"records@{_id}?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}"); private readonly string _id; - private readonly RestAbstractChain _parent; - private readonly IRestNodeInternals _rest; + private readonly RestAbstractChain _parent; + private readonly RestAbstractNode _node; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Interfaces/IRestInterlockings.cs b/InterlockLedger.Rest.Client/Interfaces/IInterlockings.cs similarity index 87% rename from InterlockLedger.Rest.Client/Interfaces/IRestInterlockings.cs rename to InterlockLedger.Rest.Client/Interfaces/IInterlockings.cs index 32dbc9f..1919e57 100644 --- a/InterlockLedger.Rest.Client/Interfaces/IRestInterlockings.cs +++ b/InterlockLedger.Rest.Client/Interfaces/IInterlockings.cs @@ -32,12 +32,12 @@ namespace InterlockLedger.Rest.Client.Abstractions; -public interface IRestInterlockings +public interface IInterlockings { - Task ForceInterlockAsync(string targetChain) + Task ForceInterlockAsync(string targetChain) => ForceInterlockAsync(new ForceInterlockModel(targetChain)); - Task ForceInterlockAsync(ForceInterlockModel model); + Task ForceInterlockAsync(ForceInterlockModel model); - Task> GetInterlocksAsync(ushort page = 0, byte pageSize = 10); + Task?> GetInterlocksAsync(ushort page = 0, byte pageSize = 10); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs b/InterlockLedger.Rest.Client/Interfaces/IRecordsAsJsonStore.cs similarity index 81% rename from InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs rename to InterlockLedger.Rest.Client/Interfaces/IRecordsAsJsonStore.cs index 0167be4..c5250b5 100644 --- a/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs +++ b/InterlockLedger.Rest.Client/Interfaces/IRecordsAsJsonStore.cs @@ -30,17 +30,17 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.Abstractions; +namespace InterlockLedger.Rest.Client; -public interface IRestRecords +public interface IRecordsAsJsonStore { - Task AddRecordAsync(NewRecordModel model); + Task AddAsync(NewRecordModelAsJson model); - Task AddRecordAsync(ulong applicationId, ulong payloadTagId, byte[] bytes); + Task AddAsync(ulong applicationId, ulong payloadTagId, object payload); - Task AddRecordAsync(ulong applicationId, ulong payloadTagId, RecordType type, byte[] bytes); + Task AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload); - Task> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); + Task?> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); - Task> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); + Task?> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Interfaces/IRecordsStore.cs b/InterlockLedger.Rest.Client/Interfaces/IRecordsStore.cs new file mode 100644 index 0000000..0e5d762 --- /dev/null +++ b/InterlockLedger.Rest.Client/Interfaces/IRecordsStore.cs @@ -0,0 +1,42 @@ +// ****************************************************************************************************************************** +// +// Copyright (c) 2018-2022 InterlockLedger Network +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ****************************************************************************************************************************** + +namespace InterlockLedger.Rest.Client.Abstractions; + +public interface IRecordsStore +{ + Task AddRecordAsync(NewRecordModel model); + + Task?> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); + + Task?> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Interfaces/IRestChain.cs b/InterlockLedger.Rest.Client/Interfaces/IRestChain.cs index ed69389..c07c177 100644 --- a/InterlockLedger.Rest.Client/Interfaces/IRestChain.cs +++ b/InterlockLedger.Rest.Client/Interfaces/IRestChain.cs @@ -35,18 +35,19 @@ namespace InterlockLedger.Rest.Client.Abstractions; public interface IRestChain { string Id { get; } - IRestInterlockings Interlockings { get; } - string Name { get; } - IRestRecords Records { get; } - IRestRecordsAsJson RecordsAsJson { get; } + string? Name { get; } - Task> GetActiveAppsAsync(); + IInterlockings Interlockings { get; } + IRecordsStore Records { get; } + IRecordsAsJsonStore RecordsAsJson { get; } - Task> GetPermittedKeysAsync(); + Task?> GetActiveAppsAsync(); - Task GetSummaryAsync(); + Task?> GetPermittedKeysAsync(); - Task> PermitAppsAsync(params ulong[] appsToPermit); + Task GetSummaryAsync(); - Task> PermitKeysAsync(params KeyPermitModel[] keysToPermit); + Task?> PermitAppsAsync(params ulong[] appsToPermit); + + Task?> PermitKeysAsync(params KeyPermitModel[] keysToPermit); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs b/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs deleted file mode 100644 index bfca2a9..0000000 --- a/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs +++ /dev/null @@ -1,46 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** - -namespace InterlockLedger.Rest.Client; - -public interface IRestRecordsAsJson -{ - Task AddAsync(NewRecordModelAsJson model); - - Task AddAsync(ulong applicationId, ulong payloadTagId, object payload); - - Task AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload); - - Task> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); - - Task> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false); -} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj b/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj index 377ea0e..9279060 100644 --- a/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj +++ b/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj @@ -2,26 +2,25 @@ net8.0 - 13.6.0 + 13.7.0 true Copyright (c) 2018-2024 InterlockLedger Network This library implements a C# wrapper around the InterlockLedger Node REST API. - - BSD 3-Clause License - https://interlockledger.network/ https://github.com/interlockledger/interlockledger-rest-client-csharp.git git true REST Client InterlockLedger - Implement new API features of IL2 Node + Update to last changes in node v13.7.0 il2.png + README.md + BSD-3-Clause InterlockLedger Network InterlockLedger - LICENSE Enable + Enable @@ -54,10 +53,10 @@ True - - - - + + True + + diff --git a/InterlockLedger.Rest.Client/Models/AppsModel.cs b/InterlockLedger.Rest.Client/Models/AppsModel.cs index e13e8a3..d2fcd87 100644 --- a/InterlockLedger.Rest.Client/Models/AppsModel.cs +++ b/InterlockLedger.Rest.Client/Models/AppsModel.cs @@ -37,10 +37,10 @@ public class AppsModel /// /// Network name /// - public string Network { get; set; } + public string? Network { get; set; } /// /// Currently valid apps for this network /// - public IEnumerable ValidApps { get; set; } + public IEnumerable ValidApps { get; set; } = []; } \ No newline at end of file diff --git a/rest_client/UsingV6_0.cs b/InterlockLedger.Rest.Client/Models/CertificatePermitModel.cs similarity index 81% rename from rest_client/UsingV6_0.cs rename to InterlockLedger.Rest.Client/Models/CertificatePermitModel.cs index 7d8e6ce..3cfddb8 100644 --- a/rest_client/UsingV6_0.cs +++ b/InterlockLedger.Rest.Client/Models/CertificatePermitModel.cs @@ -30,14 +30,15 @@ // // ****************************************************************************************************************************** -using InterlockLedger.Rest.Client.Abstractions; -using InterlockLedger.Rest.Client.V6_0; +using System.Diagnostics.CodeAnalysis; -namespace rest_client; +namespace InterlockLedger.Rest.Client; -public class UsingV6_0(RestAbstractNode node) : AbstractUsing(node) +[method: SetsRequiredMembers] +public class CertificatePermitModel(byte[] certificateInX509, IEnumerable permissions, KeyPurpose[] purposes) : KeyPermitBaseModel(permissions, purposes) { - protected override string Version => "6.0"; - - protected override Task DoExtraExercisesAsync(RestAbstractNode node, bool write) => ExerciseJsonDocumentAsync(node, write); + /// + /// Certificate in X509 bytes + /// + public required byte[] CertificateInX509 { get; set; } = certificateInX509; } diff --git a/InterlockLedger.Rest.Client/Models/ChainCreatedModel.cs b/InterlockLedger.Rest.Client/Models/ChainCreatedModel.cs new file mode 100644 index 0000000..6880847 --- /dev/null +++ b/InterlockLedger.Rest.Client/Models/ChainCreatedModel.cs @@ -0,0 +1,44 @@ +// ****************************************************************************************************************************** +// +// Copyright (c) 2018-2022 InterlockLedger Network +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ****************************************************************************************************************************** + +namespace InterlockLedger.Rest.Client; + +/// +/// Chain created +/// +public class ChainCreatedModel : ChainIdModel +{ + /// + /// Emergency key file names + /// + public List KeyFiles { get; set; } = []; +} diff --git a/InterlockLedger.Rest.Client/Models/ChainCreationModel.cs b/InterlockLedger.Rest.Client/Models/ChainCreationModel.cs index 3389094..e95f87b 100644 --- a/InterlockLedger.Rest.Client/Models/ChainCreationModel.cs +++ b/InterlockLedger.Rest.Client/Models/ChainCreationModel.cs @@ -32,17 +32,6 @@ namespace InterlockLedger.Rest.Client; -/// -/// Chain created -/// -public class ChainCreatedModel : ChainIdModel -{ - /// - /// Emergency key file names - /// - public List KeyFiles { get; set; } -} - /// /// Chain creation parameters /// @@ -51,17 +40,21 @@ public class ChainCreationModel /// /// List of additional apps (only the numeric ids) /// - public List AdditionalApps { get; set; } + public List AdditionalApps { get; set; } = []; + /// + /// API certificates to authorize with corresponding permissions + /// + public IEnumerable? ApiCertificates { get; set; } /// /// Description (perhaps intended primary usage) [Optional] /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Emergency closing key password [Required] /// - public string EmergencyClosingKeyPassword { get; set; } + public string? EmergencyClosingKeyPassword { get; set; } /// /// Emergency closing key strength of key (default: ExtraStrong) @@ -69,14 +62,14 @@ public class ChainCreationModel public KeyStrength EmergencyClosingKeyStrength { get; set; } = KeyStrength.ExtraStrong; /// - /// Keys algorithm (default: RSA) + /// Keys algorithm (default: EdDSA) /// - public Algorithms KeysAlgorithm { get; set; } = Algorithms.RSA; + public Algorithms KeysAlgorithm { get; set; } = Algorithms.EdDSA; /// /// App/Key management key password [Required] /// - public string ManagementKeyPassword { get; set; } + public string? ManagementKeyPassword { get; set; } /// /// App/Key management strength of key (default: Strong) @@ -86,22 +79,21 @@ public class ChainCreationModel /// /// Name [Required] /// - public string Name { get; set; } + public required string Name { get; set; } /// /// Operating key strength of key (default: Normal) /// public KeyStrength OperatingKeyStrength { get; set; } = KeyStrength.Normal; + /// + /// Keys algorithm (default: EdDSA) + /// + public Algorithms OperatingKeyAlgorithm { get; set; } = Algorithms.EdDSA; + + /// /// Parent record Id [Optional] /// - public string Parent { get; set; } + public string? Parent { get; set; } } - -public class ExportedKeyFile -{ - public byte[] KeyFileBytes { get; set; } - public string KeyFileName { get; set; } - public string KeyName { get; set; } -} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/ChainIdModel.cs b/InterlockLedger.Rest.Client/Models/ChainIdModel.cs index 8a414d3..f1c3e32 100644 --- a/InterlockLedger.Rest.Client/Models/ChainIdModel.cs +++ b/InterlockLedger.Rest.Client/Models/ChainIdModel.cs @@ -40,12 +40,12 @@ public class ChainIdModel : IComparable, IEquatable /// /// Unique record id /// - public string Id { get; set; } + public required string? Id { get; set; } /// /// Name [Optional] /// - public string Name { get; set; } + public string? Name { get; set; } public static bool operator !=(ChainIdModel left, ChainIdModel right) => !(left == right); @@ -78,25 +78,25 @@ public class ChainIdModel : IComparable, IEquatable /// This instance follows other in the sort order. /// /// - public int CompareTo(ChainIdModel other) => Id.CompareTo(other?.Id); + public int CompareTo(ChainIdModel? other) => Id is null ? -1 : Id.CompareTo(other?.Id) ; /// /// Compares this ChainIdModel to other object /// /// /// True if obj is a ChainIdModel and is equal to this - public override bool Equals(object obj) => Equals(obj as ChainIdModel); + public override bool Equals(object? obj) => Equals(obj as ChainIdModel); /// /// Compares this ChainIdModel to other instance /// /// /// True if other is equal to this - public bool Equals(ChainIdModel other) => other != null && Id == other.Id; + public bool Equals(ChainIdModel? other) => other is not null && Id == other.Id; /// Calculate the hash /// A hash code for the current instance - public override int GetHashCode() => Id?.GetHashCode() ?? 0; + public override int GetHashCode() =>HashCode.Combine(Id); public override string ToString() => $"Chain '{Name}' #{Id}"; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs b/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs index bfa359f..8211e6c 100644 --- a/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs +++ b/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs @@ -37,12 +37,12 @@ public sealed partial class ChainSummaryModel : ChainIdModel /// /// List of active apps (only the numeric ids) /// - public List ActiveApps { get; set; } + public List ActiveApps { get; set; } = []; /// /// Description (perhaps intended primary usage) [Optional] /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Is this record not able to accept new records? @@ -82,7 +82,7 @@ public ulong[] LicensedApps { /// /// Composite licensing status /// - public string LicensingStatus { get; set; } + public string? LicensingStatus { get; set; } /// /// Size in bytes the chain occupies in storage diff --git a/InterlockLedger.Rest.Client/Models/ColorJsonConverter.cs b/InterlockLedger.Rest.Client/Models/ColorJsonConverter.cs index 078c8cc..d2e5b62 100644 --- a/InterlockLedger.Rest.Client/Models/ColorJsonConverter.cs +++ b/InterlockLedger.Rest.Client/Models/ColorJsonConverter.cs @@ -38,7 +38,7 @@ internal class ColorJsonConverter : JsonConverter { public override Color Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.TokenType switch { - JsonTokenType.String => (Color)_colorConverter.ConvertFromInvariantString(reader.GetString()), + JsonTokenType.String => (Color)(_colorConverter.ConvertFromInvariantString(reader.GetString().Required()) ?? Color.Transparent), _ => Color.Transparent }; diff --git a/InterlockLedger.Rest.Client/Models/DocumentDetailsModel.cs b/InterlockLedger.Rest.Client/Models/DocumentDetailsModel.cs deleted file mode 100644 index 0275314..0000000 --- a/InterlockLedger.Rest.Client/Models/DocumentDetailsModel.cs +++ /dev/null @@ -1,110 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** - -namespace InterlockLedger.Rest.Client; - -public class DocumentBaseModel -{ - /// - /// Cipher algorithm used to cipher the document - /// - public CipherAlgorithms Cipher { get; set; } - - [JsonIgnore] - public bool IsCiphered => Cipher != CipherAlgorithms.None && !string.IsNullOrWhiteSpace(KeyId); - - /// - /// Unique id of key that ciphers this - /// - public string KeyId { get; set; } - - /// - /// Document name (may be a file name with an extension) - /// - public string Name { get; set; } - - /// - /// A reference to a previous version of this document (ChainId and RecordNumber) - /// - public string PreviousVersion { get; set; } -} - -public class DocumentDetailsModel : DocumentBaseModel -{ - /// - /// Document content type (mime-type) - /// - public string ContentType { get; set; } - - /// - /// Unique id of the document - /// -- derived from its content, so the same content stored in different chains - /// -- will have the same FileId - /// - public string FileId { get; set; } - - [JsonIgnore] - public bool IsPlainText => ContentType == "plain/text"; - - /// - /// Compound id for this document as stored in this chain - /// - public string PhysicalDocumentID { get; set; } - - public override string ToString() => $"Document '{Name}' [{ContentType}] {FileId}"; -} - -public class DocumentUploadModel : DocumentBaseModel -{ - public DocumentUploadModel(string name, string contentType) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Document must have a Name", nameof(name)); - if (string.IsNullOrWhiteSpace(contentType)) - throw new ArgumentException("Document must have a ContentType", nameof(contentType)); - Name = name; - ContentType = contentType; - } - - /// - /// Document content type (mime-type) - /// - public string ContentType { get; } - - public string ToQueryString() { - var sb = new StringBuilder($"?cipher={Cipher}&name={Name}"); - if (!string.IsNullOrWhiteSpace(KeyId)) - sb.Append("&keyId=").Append(KeyId); - if (!string.IsNullOrWhiteSpace(PreviousVersion)) - sb.Append("&previousVersion=").Append(PreviousVersion); - return sb.ToString(); - } -} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/Enumerations/RecordType.cs b/InterlockLedger.Rest.Client/Models/Enumerations/RecordType.cs index 3238a95..e39a740 100644 --- a/InterlockLedger.Rest.Client/Models/Enumerations/RecordType.cs +++ b/InterlockLedger.Rest.Client/Models/Enumerations/RecordType.cs @@ -35,9 +35,9 @@ namespace InterlockLedger.Rest.Client; [JsonConverter(typeof(JsonStringEnumConverter))] public enum RecordType { - Data, - Root, - Closing, - EmergencyClosing, - Corrupted + Root = 0, + Data = 1, + Closing = 2, + EmergencyClosing = 3, + Corrupted = 255 } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/ExportedKeyFile.cs b/InterlockLedger.Rest.Client/Models/ExportedKeyFile.cs new file mode 100644 index 0000000..8460a09 --- /dev/null +++ b/InterlockLedger.Rest.Client/Models/ExportedKeyFile.cs @@ -0,0 +1,40 @@ +// ****************************************************************************************************************************** +// +// Copyright (c) 2018-2022 InterlockLedger Network +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ****************************************************************************************************************************** + +namespace InterlockLedger.Rest.Client; + +public class ExportedKeyFile +{ + public required byte[] KeyFileBytes { get; set; } + public required string KeyFileName { get; set; } + public required string KeyName { get; set; } +} diff --git a/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs b/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs index 66f3eb5..08d572e 100644 --- a/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs +++ b/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs @@ -32,26 +32,23 @@ namespace InterlockLedger.Rest.Client; -public class ForceInterlockModel +public class ForceInterlockModel(string targetChain) { - public ForceInterlockModel() { } - - public ForceInterlockModel(string targetChain) => TargetChain = targetChain.Required(); /// /// Hash algorithm to use. Default: SHA256 /// - public HashAlgorithms? HashAlgorithm { get; set; } + public HashAlgorithms? HashAlgorithm { get; init; } /// /// Required minimum of the serial of the last record in target chain whose hash will be pulled. Default: 0 /// - public ulong? MinSerial { get; set; } + public ulong? MinSerial { get; init; } /// /// Id of chain to be interlocked /// - public string TargetChain { get; set; } + public string TargetChain { get; set; } = targetChain.Required(); public override string ToString() => $"force interlock on {TargetChain} @{MinSerial ?? 0ul}+ using {HashAlgorithm ?? HashAlgorithms.SHA256}"; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/InterlockingRecordModel.cs b/InterlockLedger.Rest.Client/Models/InterlockingRecordModel.cs index c9eb179..d9161d2 100644 --- a/InterlockLedger.Rest.Client/Models/InterlockingRecordModel.cs +++ b/InterlockLedger.Rest.Client/Models/InterlockingRecordModel.cs @@ -40,12 +40,12 @@ public class InterlockingRecordModel : RecordModel /// /// Interlocked Chain /// - public string InterlockedChainId { get; set; } + public required string InterlockedChainId { get; set; } /// /// Interlock Record Hash /// - public string InterlockedRecordHash { get; set; } + public required string InterlockedRecordHash { get; set; } /// /// Interlocked Record Offset diff --git a/InterlockLedger.Rest.Client/Models/KeyModel.cs b/InterlockLedger.Rest.Client/Models/KeyModel.cs index f7ca81f..afcfa73 100644 --- a/InterlockLedger.Rest.Client/Models/KeyModel.cs +++ b/InterlockLedger.Rest.Client/Models/KeyModel.cs @@ -42,27 +42,27 @@ public class KeyModel /// /// Unique key id /// - public string Id { get; set; } + public required string Id { get; set; } /// /// Key name /// - public string Name { get; set; } + public required string Name { get; set; } /// /// List of Apps and Corresponding Actions to be permitted by numbers /// - public IEnumerable Permissions { get; set; } + public required IEnumerable Permissions { get; set; } /// /// Key public key /// - public string PublicKey { get; set; } + public required string PublicKey { get; set; } /// /// Key valid purposes /// - public IEnumerable Purposes { get; set; } + public required IEnumerable Purposes { get; set; } public override string ToString() => $"Key '{Name}' {Id}{_indent}Purposes: [{_displayablePurposes}]{_indent}{_actionsFor}"; diff --git a/InterlockLedger.Rest.Client/Models/MessageModel.cs b/InterlockLedger.Rest.Client/Models/KeyPermitBaseModel.cs similarity index 67% rename from InterlockLedger.Rest.Client/Models/MessageModel.cs rename to InterlockLedger.Rest.Client/Models/KeyPermitBaseModel.cs index cfb86b5..5b7f86e 100644 --- a/InterlockLedger.Rest.Client/Models/MessageModel.cs +++ b/InterlockLedger.Rest.Client/Models/KeyPermitBaseModel.cs @@ -30,37 +30,36 @@ // // ****************************************************************************************************************************** +using System.Diagnostics.CodeAnalysis; + namespace InterlockLedger.Rest.Client; -/// -/// Generic message -/// -public class MessageModel +public abstract class KeyPermitBaseModel { /// - /// Application id this record is associated with - /// - public ulong ApplicationId { get; set; } - - /// - /// Chain id - /// - public string ChainId { get; set; } - - /// - /// Message type + /// Key name, ignored on CertificatePermitModel /// - public string MessageType { get; set; } + public string? Name { get; set; } /// - /// Message payload + /// App actions to be permitted by number /// - public byte[] Payload { get; set; } + public required IEnumerable Permissions { get; set; } /// - /// Message payload as text (may not help much) + /// Key valid purposes /// - public string PayloadAsText { get; set; } + public required KeyPurpose[] Purposes { get; set; } - public override string ToString() => $"Message {MessageType} Chain {ChainId} App {ApplicationId} : {PayloadAsText}"; + [SetsRequiredMembers] + public KeyPermitBaseModel(IEnumerable permissions, KeyPurpose[] purposes, string? name = null) + { + if (permissions.None()) + throw new InvalidDataException("This key doesn't have at least one action to be permitted"); + Permissions = permissions.Required(); + Purposes = purposes.Required(); + if (!(purposes.Contains(KeyPurpose.Action) && purposes.Contains(KeyPurpose.Protocol))) + throw new InvalidDataException("This key doesn't have the required purposes to be permitted"); + Name = name; + } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs b/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs index 3e5147b..b23bad3 100644 --- a/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs +++ b/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs @@ -30,48 +30,27 @@ // // ****************************************************************************************************************************** +using System.Diagnostics.CodeAnalysis; + namespace InterlockLedger.Rest.Client; -public class KeyPermitModel + +[method: SetsRequiredMembers] + +public class KeyPermitModel(string id, string name, string publicKey, IEnumerable permissions, params KeyPurpose[] purposes) : KeyPermitBaseModel(permissions, purposes, name.Required()) { + [SetsRequiredMembers] public KeyPermitModel(string id, string name, string publicKey, ulong app, IEnumerable appActions, params KeyPurpose[] purposes) : this(id, name, publicKey, [new(app, appActions)], purposes) { } - public KeyPermitModel(string id, string name, string publicKey, IEnumerable permissions, params KeyPurpose[] purposes) { - Permissions = permissions.Required(); - if (!permissions.Any()) - throw new InvalidDataException("This key doesn't have at least one action to be permitted"); - Id = id.Required(); - Name = name.Required(); - PublicKey = publicKey.Required(); - Purposes = purposes.Required(); - if (!(purposes.Contains(KeyPurpose.Action) && purposes.Contains(KeyPurpose.Protocol))) - throw new InvalidDataException("This key doesn't have the required purposes to be permitted"); - } - /// /// Unique key id /// - public string Id { get; set; } - - /// - /// Key name - /// - public string Name { get; set; } - - /// - /// List of Apps and Corresponding Actions to be permitted by numbers - /// - public IEnumerable Permissions { get; set; } + public string Id { get; set; } = id.Required(); /// /// Key public key /// - public string PublicKey { get; set; } - - /// - /// Key valid purposes - /// - public KeyPurpose[] Purposes { get; set; } + public string PublicKey { get; set; } = publicKey.Required(); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/NewRecordModel.cs b/InterlockLedger.Rest.Client/Models/NewRecordModel.cs index 41d68b1..c289fd2 100644 --- a/InterlockLedger.Rest.Client/Models/NewRecordModel.cs +++ b/InterlockLedger.Rest.Client/Models/NewRecordModel.cs @@ -37,5 +37,5 @@ public class NewRecordModel : NewRecordModelBase /// /// The payload's bytes /// - public byte[] PayloadBytes { get; set; } + public required byte[] PayloadBytes { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/NewRecordModelAsJson.cs b/InterlockLedger.Rest.Client/Models/NewRecordModelAsJson.cs index 159ce6b..23b20ee 100644 --- a/InterlockLedger.Rest.Client/Models/NewRecordModelAsJson.cs +++ b/InterlockLedger.Rest.Client/Models/NewRecordModelAsJson.cs @@ -37,7 +37,7 @@ public class NewRecordModelAsJson : NewRecordModelBase /// /// The payload data matching the metadata for PayloadTagId /// - public object Json { get; set; } + public object? Json { get; set; } /// /// The tag id for the payload, as registered for the application diff --git a/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs b/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs index 3860a30..4a40fa0 100644 --- a/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs +++ b/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs @@ -48,45 +48,45 @@ public class NodeCommonModel /// /// Unique node id /// - public string Id { get; set; } + public required string Id { get; set; } /// /// Node name /// - public string Name { get; set; } + public string? Name { get; set; } /// /// Network this node participates on /// - public string Network { get; set; } + public required string Network { get; set; } /// /// Node owner id [Optional] /// - public string OwnerId { get; set; } + public string? OwnerId { get; set; } /// /// Node owner name [Optional] /// - public string OwnerName { get; set; } + public string? OwnerName { get; set; } /// /// Peer address in the cannonical form like 'ilkl-minerva://node.il2:32025' /// - public string PeerAddress { get; set; } + public string? PeerAddress { get; set; } /// /// List of active roles running in the node /// - public IEnumerable Roles { get; set; } + public IEnumerable Roles { get; set; } = []; - public virtual string ResolvedPeerAddress => PeerAddress; + public virtual string? ResolvedPeerAddress => PeerAddress; /// /// Version of software running the Node /// - public Versions SoftwareVersions { get; set; } + public Versions? SoftwareVersions { get; set; } public override string ToString() => $""" Node '{Name}' [{Id}] at {ResolvedPeerAddress} @@ -99,7 +99,7 @@ Running il2 Node#{SoftwareVersions?.NodeVersion} {Extras} """; protected static readonly IEnumerable _empty = []; - protected virtual string Extras { get; } + protected virtual string Extras { get; } = string.Empty; private static string Fancy(Color color) => color.IsNamedColor ? color.Name : "#" + color.Name.Remove(0, 2).ToUpperInvariant(); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs b/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs index ec36c0e..dbb34de 100644 --- a/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs +++ b/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs @@ -40,12 +40,12 @@ public class NodeDetailsModel : NodeCommonModel /// /// List of owned chains, only the ids /// - public IEnumerable Chains { get; set; } + public IEnumerable Chains { get; set; } = []; /// /// Other properties the node may have /// - public Dictionary Extensions { get; set; } + public Dictionary Extensions { get; set; } = []; private static string ToLine(KeyValuePair x) => $"{x.Key}: {x.Value}"; diff --git a/InterlockLedger.Rest.Client/Models/PeerModel.cs b/InterlockLedger.Rest.Client/Models/PeerModel.cs index 049b08e..05d9ca3 100644 --- a/InterlockLedger.Rest.Client/Models/PeerModel.cs +++ b/InterlockLedger.Rest.Client/Models/PeerModel.cs @@ -40,7 +40,7 @@ public sealed class PeerModel : NodeCommonModel /// /// Network address to contact the peer /// - public string Address { get; set; } + public required string Address { get; set; } /// /// Port the peer is listening @@ -50,7 +50,7 @@ public sealed class PeerModel : NodeCommonModel /// /// Network protocol the peer is listening /// - public string Protocol { get; set; } + public string? Protocol { get; set; } public override string ResolvedPeerAddress => $"ilkl-{Network.ToLowerInvariant()}://{Address}:{Port}"; protected override string Extras => string.Empty; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/RecordModel.cs b/InterlockLedger.Rest.Client/Models/RecordModel.cs index a366465..27b613f 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModel.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModel.cs @@ -37,6 +37,6 @@ public class RecordModel : RecordModelBase /// /// The payload's bytes /// - public byte[] PayloadBytes { get; set; } + public byte[]? PayloadBytes { get; set; } public override string ToString() => JsonSerializer.Serialize(this, Globals.JsonSettings); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs b/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs index 7ab6196..24b31b7 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs @@ -40,6 +40,6 @@ public class RecordModelAsJson : RecordModelBase /// /// The payload's bytes /// - public object Payload { get; set; } + public object? Payload { get; set; } public override string ToString() => JsonSerializer.Serialize(this, Globals.JsonSettings); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/RecordModelBase.cs b/InterlockLedger.Rest.Client/Models/RecordModelBase.cs index 0d486df..7f61fcf 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModelBase.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModelBase.cs @@ -45,7 +45,7 @@ public abstract class RecordModelBase /// /// chain id that owns this record /// - public string ChainId { get; set; } + public required string ChainId { get; set; } /// /// Time of record creation @@ -55,7 +55,7 @@ public abstract class RecordModelBase /// /// IL2 Network /// - public string Network { get; set; } + public required string Network { get; set; } /// /// The payload's TagId @@ -65,7 +65,7 @@ public abstract class RecordModelBase /// /// Record universal reference [Network]:[ChainId]@[Serial] /// - public string Reference { get; set; } + public required string Reference { get; set; } /// /// Record serial number. diff --git a/InterlockLedger.Rest.Client/Models/AppPermissions.cs b/InterlockLedger.Rest.Client/Models/Types/AppPermissions.cs similarity index 96% rename from InterlockLedger.Rest.Client/Models/AppPermissions.cs rename to InterlockLedger.Rest.Client/Models/Types/AppPermissions.cs index 72afbc1..71c9582 100644 --- a/InterlockLedger.Rest.Client/Models/AppPermissions.cs +++ b/InterlockLedger.Rest.Client/Models/Types/AppPermissions.cs @@ -67,10 +67,10 @@ public override string ToString() { public class Converter : JsonConverter { - public override AppPermissions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AppPermissions? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.TokenType switch { JsonTokenType.Null => null, - JsonTokenType.String => new AppPermissions(reader.GetString()), + JsonTokenType.String => new AppPermissions(reader.GetString().Required()), _ => throw new InvalidCastException($"TokenType should be Null or String but is {reader.TokenType}") }; diff --git a/InterlockLedger.Rest.Client/Models/PublishedApp.cs b/InterlockLedger.Rest.Client/Models/Types/PublishedApp.cs similarity index 85% rename from InterlockLedger.Rest.Client/Models/PublishedApp.cs rename to InterlockLedger.Rest.Client/Models/Types/PublishedApp.cs index 76ef882..94f68d0 100644 --- a/InterlockLedger.Rest.Client/Models/PublishedApp.cs +++ b/InterlockLedger.Rest.Client/Models/Types/PublishedApp.cs @@ -36,17 +36,17 @@ public partial class PublishedApp : IComparable, IEquatable Safe($"{PublisherName}.{Name}#{AppVersion}"); - public string Description { get; set; } + public string? Description { get; set; } public ulong Id { get; set; } // TODO: map public IEnumerable DataModels { get; set; } - public string Name { get; set; } + public string? Name { get; set; } - public string PublisherId { get; set; } - public string PublisherName { get; set; } - public IEnumerable ReservedILTagIds { get; set; } + public string? PublisherId { get; set; } + public string? PublisherName { get; set; } + public IEnumerable ReservedILTagIds { get; set; } = []; public DateTimeOffset Start { get; set; } public ushort Version { get; set; } @@ -62,15 +62,15 @@ public partial class PublishedApp : IComparable, IEquatable=(PublishedApp left, PublishedApp right) => left is null ? right is null : left.CompareTo(right) >= 0; - public int CompareTo(PublishedApp other) { - if (other == null) return 1; + public int CompareTo(PublishedApp? other) { + if (other is null) return 1; var idCompare = Id.CompareTo(other.Id); - return idCompare == 0 ? AppVersion.CompareTo(other.AppVersion) : idCompare; + return idCompare != 0 ? idCompare : AppVersion?.CompareTo(other.AppVersion) ?? -1; } - public override bool Equals(object obj) => Equals(obj as PublishedApp); + public override bool Equals(object? obj) => Equals(obj as PublishedApp); - public bool Equals(PublishedApp other) => other != null && AppVersion.Equals(other.AppVersion) && Id == other.Id; + public bool Equals(PublishedApp? other) => other is not null && Id == other.Id && AppVersion == other.AppVersion; public override int GetHashCode() => HashCode.Combine(AppVersion, Id); diff --git a/InterlockLedger.Rest.Client/Models/Versions.cs b/InterlockLedger.Rest.Client/Models/Types/Versions.cs similarity index 90% rename from InterlockLedger.Rest.Client/Models/Versions.cs rename to InterlockLedger.Rest.Client/Models/Types/Versions.cs index 7387ed5..749c601 100644 --- a/InterlockLedger.Rest.Client/Models/Versions.cs +++ b/InterlockLedger.Rest.Client/Models/Types/Versions.cs @@ -40,29 +40,29 @@ public class Versions /// /// Core libraries and il2apps version /// - public string CoreLibs { get; set; } + public string? CoreLibs { get; set; } /// /// InterlockLedger-Tags library version /// - public string Tags { get; set; } + public string? Tags { get; set; } /// /// Interlockledger node daemon version [Deprecated] /// - public string Node { get; set; } + public string? Node { get; set; } /// /// Interlockledger node daemon version /// - public string Main { get; set; } + public string? Main { get; set; } /// /// Resolved node version, either Main or Node /// - public string NodeVersion => Main.WithDefault(Node).WithDefault("?"); + public string NodeVersion => Main.WithDefault(Node.WithDefault("?")); /// /// Peer2Peer connectivity library version /// - public string Peer2peer { get; set; } + public string? Peer2peer { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/VersionJsonConverter.cs b/InterlockLedger.Rest.Client/Models/VersionJsonConverter.cs index 4a02508..e8f3b28 100644 --- a/InterlockLedger.Rest.Client/Models/VersionJsonConverter.cs +++ b/InterlockLedger.Rest.Client/Models/VersionJsonConverter.cs @@ -34,10 +34,10 @@ namespace InterlockLedger.Rest.Client; internal class VersionJsonConverter : JsonConverter { - public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.TokenType switch { JsonTokenType.Null => null, - JsonTokenType.String => Version.Parse(reader.GetString()), + JsonTokenType.String => Version.Parse(reader.GetString().Required()), _ => throw new InvalidDataException("Badly formatted version") }; diff --git a/InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs b/InterlockLedger.Rest.Client/V13_7/Extensions/X509Certificate2Extensions.cs similarity index 90% rename from InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs rename to InterlockLedger.Rest.Client/V13_7/Extensions/X509Certificate2Extensions.cs index e66f2f0..52c1238 100644 --- a/InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs +++ b/InterlockLedger.Rest.Client/V13_7/Extensions/X509Certificate2Extensions.cs @@ -30,19 +30,21 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public static class X509Certificate2Extensions { public static string ToKeyId(this X509Certificate2 certificate) => "Key!" + certificate.GetCertHash().ToSafeBase64() + "#SHA1"; - public static string ToPubKeyHash(this X509Certificate2 certificate) { + public static string? ToPubKeyHash(this X509Certificate2 certificate) { var pubKeyRSA = certificate?.GetRSAPublicKey(); if (pubKeyRSA is null) return null; var pubKeyRSAParameters = pubKeyRSA.ExportParameters(includePrivateParameters: false); var modulus = pubKeyRSAParameters.Modulus; var exponent = pubKeyRSAParameters.Exponent; + if (modulus == null || exponent == null) + return null; var modulusTag = PseudoTag(16, modulus); var exponentTag = PseudoTag(16, exponent); var pubKeyRSAParametersTag = PseudoTag(40, modulusTag, exponentTag); @@ -51,10 +53,7 @@ public static string ToPubKeyHash(this X509Certificate2 certificate) { static byte[] PseudoTag(ulong tagId, params byte[][] parts) => [.. tagId.ILIntEncode(), .. ((ulong)parts.Sum(b => b.Length)).ILIntEncode(), .. parts.SelectMany(b => b)]; - static byte[] HashSha256(byte[] data) { - using var hasher = SHA256.Create(); - hasher.Initialize(); - return hasher.ComputeHash(data); - } + static byte[] HashSha256(byte[] data) + => SHA256.HashData(data); } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs b/InterlockLedger.Rest.Client/V13_7/Implementations/DocumentRegistryImplementation.cs similarity index 77% rename from InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs rename to InterlockLedger.Rest.Client/V13_7/Implementations/DocumentRegistryImplementation.cs index d966c4e..58880b6 100644 --- a/InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs +++ b/InterlockLedger.Rest.Client/V13_7/Implementations/DocumentRegistryImplementation.cs @@ -1,4 +1,4 @@ -// ****************************************************************************************************************************** +// ****************************************************************************************************************************** // // Copyright (c) 2018-2022 InterlockLedger Network // All rights reserved. @@ -29,20 +29,18 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V6_0; + +namespace InterlockLedger.Rest.Client.V13_7; internal class DocumentRegistryImplementation(RestAbstractNode node) : IDocumentRegistry where ChainType : IRestChain { private readonly RestAbstractNode _node = node.Required(); - Uri IDocumentRegistry.BaseUri => _node.BaseUri; - - Task IDocumentRegistry.GetDocumentsUploadConfigurationAsync() + public Task GetDocumentsUploadConfigurationAsync() => _node.GetAsync("/documents/configuration"); - public async Task> ListKnownChainsAcceptingTransactionsAsync() { + public async Task?> ListKnownChainsAcceptingTransactionsAsync() { var result = new List(); foreach (IRestChain chain in (await _node.GetChainsAsync().ConfigureAwait(false)).Safe()) { var summary = await chain.GetSummaryAsync().ConfigureAwait(false); @@ -56,30 +54,30 @@ public async Task> ListKnownChainsAcceptingTransactionsAsync return result; } - public async Task> ListKnownChainsAsync() + public async Task?> ListKnownChainsAsync() => (await _node.GetChainsAsync().ConfigureAwait(false)).Safe().Select(ch => ch.Id); - Task IDocumentRegistry.RetrieveMetadataAsync(string locator) + public Task RetrieveMetadataAsync(string locator) => _node.GetAsync(FromLocator(locator.Required(), "metadata")); - Task<(string Name, string ContentType, Stream Content)> IDocumentRegistry.RetrieveSingleAsync(string locator, int index) + Task<(string Name, string ContentType, Stream Content)?> IDocumentRegistry.RetrieveSingleAsync(string locator, int index) => _node.GetFileReadStreamAsync(FromLocator(locator.Required(), index)); - Task<(string Name, string ContentType, Stream Content)> IDocumentRegistry.RetrieveZipAsync(string locator) + Task<(string Name, string ContentType, Stream Content)?> IDocumentRegistry.RetrieveZipAsync(string locator) => _node.GetFileReadStreamAsync(FromLocator(locator.Required(), "zip"), accept: "application/zip"); - Task IDocumentRegistry.TransactionAddItemAsync(string transactionId, string path, string name, string comment, string contentType, Stream source) + public Task TransactionAddItemAsync(string transactionId, string path, string name, string? comment, string contentType, Stream source) => _node.PostStreamAsync($"/documents/transaction/{transactionId.Required()}?name={HttpUtility.UrlEncode(name.Required())}&comment={HttpUtility.UrlEncode(comment)}", source.Required(), contentType.Required()); - async Task IDocumentRegistry.TransactionBeginAsync(DocumentsBeginTransactionModel transactionStart) { + public async Task TransactionBeginAsync(DocumentsBeginTransactionModel transactionStart) { await ValidateChainAsync(transactionStart.Required().Chain); return await _node.PostAsync("/documents/transaction", transactionStart); } - Task IDocumentRegistry.TransactionCommitAsync(string transactionId) + public Task TransactionCommitAsync(string transactionId) => _node.PostAsync($"/documents/transaction/{transactionId.Required()}/commit", null); - Task IDocumentRegistry.TransactionStatusAsync(string transactionId) + public Task TransactionStatusAsync(string transactionId) => _node.GetAsync($"/documents/transaction/{transactionId.Required()}"); private static string FromLocator(string locator, T selector) => $"/documents/{HttpUtility.UrlEncode(locator)}/{selector}"; diff --git a/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs b/InterlockLedger.Rest.Client/V13_7/Implementations/JsonStoreImplementation.cs similarity index 70% rename from InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs rename to InterlockLedger.Rest.Client/V13_7/Implementations/JsonStoreImplementation.cs index b121eeb..99b0612 100644 --- a/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs +++ b/InterlockLedger.Rest.Client/V13_7/Implementations/JsonStoreImplementation.cs @@ -30,20 +30,23 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; -internal sealed class JsonStoreImplementation : IJsonStore +internal class JsonStoreImplementation : IJsonStore { - public JsonStoreImplementation(RestAbstractChain parent) { + public JsonStoreImplementation(RestChainV13_7 parent) { _parent = parent.Required(); - _rest = _parent._rest; + _node = _parent._node; _id = _parent.Id; } - public Task RetrieveAsync(ulong serial) - => _rest.GetAsync($"/jsonDocuments@{_id}/{serial}"); + public Task RetrieveAsync(ulong serial) + => _node.GetAsync($"/jsonDocuments@{_id}/{serial}"); - private readonly string _id; - private readonly RestAbstractChain _parent; - private readonly IRestNodeInternals _rest; + public Task RetrieveAllowedReadersAsync(string chain, string? contextId = null, bool lastToFirst = false, int page = 0, int pageSize = 10) + => _node.GetAsync($"/jsonDocuments@{_id}/allow?lastToFirst={lastToFirst}&page={page}&pageSize={pageSize}&contextId={contextId}"); + + protected readonly string _id; + private readonly RestChainV13_7 _parent; + protected readonly RestAbstractNode _node; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs b/InterlockLedger.Rest.Client/V13_7/Implementations/OpaqueStoreImplementation.cs similarity index 78% rename from InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs rename to InterlockLedger.Rest.Client/V13_7/Implementations/OpaqueStoreImplementation.cs index 21ee057..846c865 100644 --- a/InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs +++ b/InterlockLedger.Rest.Client/V13_7/Implementations/OpaqueStoreImplementation.cs @@ -29,31 +29,29 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** -#nullable enable -using InterlockLedger.Rest.Client.V6_0; -namespace InterlockLedger.Rest.Client.V7_6; +namespace InterlockLedger.Rest.Client.V13_7; internal class OpaqueStoreImplementation : IOpaqueStore { - public OpaqueStoreImplementation(RestAbstractNode node, RestChainV7_6 chain) { + public OpaqueStoreImplementation(RestAbstractNode node, RestChainV13_7 chain) { _chain = chain.Required(); _node = node; _id = _chain.Id; } - public Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, Stream source) + public Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, Stream source) => _node.PostStreamAsync($"/opaque/{_id}?appId={appId}&payloadTypeId={payloadTypeId}&lastChangedRecordSerial={lastChangedRecordSerial}", source.Required(), contentType: "application/octet-stream"); - public Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, byte[] bytes) { + public Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, byte[] bytes) { using var body = new MemoryStream(bytes); return AddRecordAsync(appId, payloadTypeId, lastChangedRecordSerial, body); } - public Task<(ulong AppId, ulong PayloadTypeId, Stream Content)> RetrieveSinglePayloadAsync(ulong serial) + public Task<(ulong AppId, ulong PayloadTypeId, DateTimeOffset? CreatedAt, Stream Content)?> RetrieveSinglePayloadAsync(ulong serial) => _node.GetOpaqueStreamAsync($"/opaque/{_id}@{serial}"); - public Task QueryRecordsFromAsync(ulong appId, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, ulong[]? payloadTypeIds = null, ulong? howMany = null) { + public Task QueryRecordsFromAsync(ulong appId, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, ulong[]? payloadTypeIds = null, ulong? howMany = null) { string url = $"/opaque/{_id}/asJson/query?appId={appId}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}"; if (howMany != null) { url += $"&howMany={howMany}"; @@ -68,6 +66,6 @@ public Task QueryRecordsFromAsync(ulong appId, ushort private readonly string _id; - private readonly RestChainV7_6 _chain; - private readonly RestAbstractNode _node; + private readonly RestChainV13_7 _chain; + private readonly RestAbstractNode _node; } diff --git a/InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs b/InterlockLedger.Rest.Client/V13_7/Interfaces/IDocumentRegistry.cs similarity index 50% rename from InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs rename to InterlockLedger.Rest.Client/V13_7/Interfaces/IDocumentRegistry.cs index dd24566..e172f2f 100644 --- a/InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs +++ b/InterlockLedger.Rest.Client/V13_7/Interfaces/IDocumentRegistry.cs @@ -30,23 +30,37 @@ // // ****************************************************************************************************************************** -using InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; -namespace InterlockLedger.Rest.Client.V7_6; - -public class RestNodeV7_6 : RestAbstractNodeWithDocumentRegistry +public interface IDocumentRegistry { - public RestNodeV7_6(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) - : base(x509Certificate, networkId, address) { } + Task GetDocumentsUploadConfigurationAsync(); + + Task?> ListKnownChainsAcceptingTransactionsAsync(); + + Task?> ListKnownChainsAsync(); + + Task RetrieveMetadataAsync(string locator); + + Task<(string Name, string ContentType, Stream Content)?> RetrieveSingleAsync(string locator, int index); + + Task<(string Name, string ContentType, Stream Content)?> RetrieveZipAsync(string locator); + + Task TransactionAddItemAsync(string transactionId, string path, string name, string? comment, string contentType, Stream source); - public RestNodeV7_6(X509Certificate2 x509Certificate, ushort port, string address) - : base(x509Certificate, port, address) { } + Task TransactionAddItemAsync(string transactionId, DirectoryInfo baseDirectory, FileInfo file, string? comment, string contentType) + => !file.Required().FullName.StartsWith(baseDirectory.Required().FullName, StringComparison.InvariantCultureIgnoreCase) + ? throw new ArgumentException(message: $"File ´{file.FullName}´ is not inside directory '{baseDirectory.FullName}' ") + : TransactionAddItemAsync(transactionId.Required(), + Path.GetRelativePath(baseDirectory.FullName, file.DirectoryName.Required()), + file.Name, + comment, + contentType.Required(), + file.OpenRead()); - public RestNodeV7_6(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) - : base(certFile, certPassword, networkId, address) { } + Task TransactionBeginAsync(DocumentsBeginTransactionModel transactionStart); - public RestNodeV7_6(string certFile, string certPassword, ushort port, string address) - : base(certFile, certPassword, port, address) { } - protected internal override RestChainV7_6 BuildChain(ChainIdModel c) => new(this, c.Required()); + Task TransactionCommitAsync(string transactionId); + Task TransactionStatusAsync(string transactionId); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/Interfaces/IJsonStore.cs b/InterlockLedger.Rest.Client/V13_7/Interfaces/IJsonStore.cs similarity index 85% rename from InterlockLedger.Rest.Client/V6_0/Interfaces/IJsonStore.cs rename to InterlockLedger.Rest.Client/V13_7/Interfaces/IJsonStore.cs index 30b19a3..5a5cd60 100644 --- a/InterlockLedger.Rest.Client/V6_0/Interfaces/IJsonStore.cs +++ b/InterlockLedger.Rest.Client/V13_7/Interfaces/IJsonStore.cs @@ -30,9 +30,11 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; -public interface IJsonStore +public interface IJsonStore { - Task RetrieveAsync(ulong serial); -} \ No newline at end of file + Task RetrieveAsync(ulong serial); + + Task RetrieveAllowedReadersAsync(string chain, string? contextId = null, bool lastToFirst = false, int page = 0, int pageSize = 10); +} diff --git a/InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs b/InterlockLedger.Rest.Client/V13_7/Interfaces/INodeWithDocumentRegistry.cs similarity index 97% rename from InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs rename to InterlockLedger.Rest.Client/V13_7/Interfaces/INodeWithDocumentRegistry.cs index 51d28aa..559749e 100644 --- a/InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs +++ b/InterlockLedger.Rest.Client/V13_7/Interfaces/INodeWithDocumentRegistry.cs @@ -30,7 +30,7 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public interface INodeWithDocumentRegistry { diff --git a/InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.cs b/InterlockLedger.Rest.Client/V13_7/Interfaces/IOpaqueStore.cs similarity index 75% rename from InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.cs rename to InterlockLedger.Rest.Client/V13_7/Interfaces/IOpaqueStore.cs index 2013cf7..ce99966 100644 --- a/InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.cs +++ b/InterlockLedger.Rest.Client/V13_7/Interfaces/IOpaqueStore.cs @@ -31,14 +31,12 @@ // ****************************************************************************************************************************** -using InterlockLedger.Rest.Client.V6_0; - -namespace InterlockLedger.Rest.Client.V7_6; +namespace InterlockLedger.Rest.Client.V13_7; public interface IOpaqueStore { - Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, Stream source); - Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, byte[] bytes); - Task QueryRecordsFromAsync(ulong appId, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, ulong[] payloadTypeIds = null, ulong? howMany = null); - Task<(ulong AppId, ulong PayloadTypeId, Stream Content)> RetrieveSinglePayloadAsync(ulong serial); + Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, Stream source); + Task AddRecordAsync(ulong appId, ulong payloadTypeId, ulong lastChangedRecordSerial, byte[] bytes); + Task QueryRecordsFromAsync(ulong appId, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, ulong[]? payloadTypeIds = null, ulong? howMany = null); + Task<(ulong AppId, ulong PayloadTypeId, DateTimeOffset? CreatedAt, Stream Content)?> RetrieveSinglePayloadAsync(ulong serial); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V13_7/Models/AllowedReadersRecordModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/AllowedReadersRecordModel.cs new file mode 100644 index 0000000..71a1cab --- /dev/null +++ b/InterlockLedger.Rest.Client/V13_7/Models/AllowedReadersRecordModel.cs @@ -0,0 +1,43 @@ +// ****************************************************************************************************************************** +// +// Copyright (c) 2018-2022 InterlockLedger Network +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ****************************************************************************************************************************** + +namespace InterlockLedger.Rest.Client.V13_7; + +/// +/// Simplified model for allowed readers record +/// +public class AllowedReadersRecordModel +{ + public required string RecordReference { get; set; } + public string? ContextId { get; set; } + public IEnumerable? Readers { get; set; } +} diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsBeginTransactionModel.cs similarity index 98% rename from InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/DocumentsBeginTransactionModel.cs index 7f95d4f..0395db8 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsBeginTransactionModel.cs @@ -30,9 +30,9 @@ // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V6_0; + +namespace InterlockLedger.Rest.Client.V13_7; /// /// To specify parameters for starting a transaction to store many documents in a single InterlockLedger record diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsMetadataModel.cs similarity index 98% rename from InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/DocumentsMetadataModel.cs index 73ca7ca..46d0917 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsMetadataModel.cs @@ -30,9 +30,9 @@ // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V6_0; + +namespace InterlockLedger.Rest.Client.V13_7; /// /// Model for metadata associated to a Multi-Document Storage Locator diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsTransactionModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsTransactionModel.cs similarity index 91% rename from InterlockLedger.Rest.Client/V6_0/Models/DocumentsTransactionModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/DocumentsTransactionModel.cs index dec7001..5ed46d8 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsTransactionModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsTransactionModel.cs @@ -30,7 +30,7 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public class DocumentsTransactionModel { @@ -42,12 +42,12 @@ public class DocumentsTransactionModel /// /// Id of chain where the transaction data will be stored /// - public string Chain { get; set; } + public required string Chain { get; set; } /// /// Any additional information about the set of documents to be stored /// - public string Comment { get; set; } + public string? Comment { get; set; } /// /// Compression algorithm can be: @@ -58,7 +58,7 @@ public class DocumentsTransactionModel ///
ZSTDCompression of the data using the ZStandard from Facebook (In the future)
/// ///
- public string Compression { get; set; } + public string? Compression { get; set; } /// /// Total count of uploaded documents for this transaction @@ -68,12 +68,12 @@ public class DocumentsTransactionModel /// /// Names of documents already uploaded /// - public IEnumerable DocumentNames { get; set; } + public IEnumerable DocumentNames { get; set; } = []; /// /// The encryption descriptor in the &lt;pbe&gt;-&lt;hash&gt;-&lt;cipher&gt;-&lt;level&gt; format /// - public string Encryption { get; set; } + public string? Encryption { get; set; } /// /// If the publically viewable PublicDirectory field should be created @@ -83,7 +83,7 @@ public class DocumentsTransactionModel /// /// Locator for the previous version of this set /// - public string Previous { get; set; } + public string? Previous { get; set; } /// /// The transaction will be aborted if not completed until this timeout @@ -93,5 +93,5 @@ public class DocumentsTransactionModel /// /// Id of the transaction to use when uploading each file and committing the transaction /// - public string TransactionId { get; set; } + public string? TransactionId { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsUploadConfiguration.cs b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsUploadConfiguration.cs similarity index 94% rename from InterlockLedger.Rest.Client/V6_0/Models/DocumentsUploadConfiguration.cs rename to InterlockLedger.Rest.Client/V13_7/Models/DocumentsUploadConfiguration.cs index d818058..ed0d1dc 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsUploadConfiguration.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/DocumentsUploadConfiguration.cs @@ -30,17 +30,17 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public sealed record DocumentsUploadConfiguration { public DocumentsUploadConfiguration() { } - public string DefaultCompression { get; set; } - public string DefaultEncryption { get; set; } + public string? DefaultCompression { get; set; } + public string? DefaultEncryption { get; set; } public long FileSizeLimit { get; set; } public int? Iterations { get; set; } - public IEnumerable PermittedContentTypes { get; set; } + public IEnumerable PermittedContentTypes { get; set; } = []; public ushort TimeOutInMinutes { get; set; } public override string ToString() diff --git a/InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModel.cs similarity index 90% rename from InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModel.cs index 0c8a063..a846171 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModel.cs @@ -30,17 +30,15 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public sealed class EncryptedTextModel { - public EncryptedTextModel() { } + public required string Cipher { get; set; } - public string Cipher { get; set; } + public required byte[] CipherText { get; set; } - public byte[] CipherText { get; set; } - - public IEnumerable ReadingKeys { get; set; } + public required IEnumerable ReadingKeys { get; set; } public override string ToString() => $"Encrypted Json with {Cipher} for {ReadingKeys?.Count()} keys with content \"{CipherText.ToSafeBase64().Ellipsis(135)}\""; diff --git a/InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModelExtensions.cs b/InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModelExtensions.cs similarity index 93% rename from InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModelExtensions.cs rename to InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModelExtensions.cs index 088bbd9..c60c703 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/EncryptedTextModelExtensions.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/EncryptedTextModelExtensions.cs @@ -30,17 +30,17 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public static class EncryptedTextModelExtensions { - public static string DecodedWith(this EncryptedTextModel model, X509Certificate2 certificate) { + public static string? DecodedWith(this EncryptedTextModel model, X509Certificate2 certificate) { if (certificate is null) return "ERROR: No key provided to decode EncryptedText"; if (!certificate.HasPrivateKey) return "ERROR: Certificate has no private key to be able to decode EncryptedText"; string certKeyId = certificate.ToKeyId(); - string pubKeyHash = certificate.ToPubKeyHash(); + string? pubKeyHash = certificate.ToPubKeyHash(); if (pubKeyHash is null) return "ERROR: Non-RSA certificate is not currently supported"; if (model.ReadingKeys.SkipNulls().None()) @@ -54,6 +54,8 @@ public static string DecodedWith(this EncryptedTextModel model, X509Certificate2 if (model.CipherText.None()) return null; using var rsaAlgo = certificate.GetRSAPrivateKey(); + if (rsaAlgo is null || authorizedKey.EncryptedKey is null || authorizedKey.EncryptedIV is null) + return "ERROR: Could not use private key"; var aesKey = RSADecrypt(rsaAlgo, authorizedKey.EncryptedKey); var aesIV = RSADecrypt(rsaAlgo, authorizedKey.EncryptedIV); var jsonBytes = AES256Decrypt(model.CipherText, aesKey, aesIV); diff --git a/InterlockLedger.Rest.Client/V6_0/Models/JsonDocumentModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/JsonDocumentModel.cs similarity index 93% rename from InterlockLedger.Rest.Client/V6_0/Models/JsonDocumentModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/JsonDocumentModel.cs index 50f2195..a8612a4 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/JsonDocumentModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/JsonDocumentModel.cs @@ -30,17 +30,17 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public sealed class JsonDocumentModel : RecordModelBase { /// /// Encrypted Blob /// - public EncryptedTextModel EncryptedJson { get; set; } + public EncryptedTextModel? EncryptedJson { get; set; } /// /// Stored JsonDocument /// - public string JsonText { get; set; } + public string? JsonText { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/OpaqueRecordModel.cs similarity index 79% rename from InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/OpaqueRecordModel.cs index 88a1495..14fa54c 100644 --- a/InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/OpaqueRecordModel.cs @@ -1,4 +1,4 @@ -// ****************************************************************************************************************************** +// ****************************************************************************************************************************** // // Copyright (c) 2018-2022 InterlockLedger Network // All rights reserved. @@ -29,16 +29,15 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V7_6; + +namespace InterlockLedger.Rest.Client.V13_7; /// /// Generic opaque record /// public class OpaqueRecordModel { - /// /// Network the chain that contains this record belongs to /// @@ -61,9 +60,26 @@ public class OpaqueRecordModel public ulong ApplicationId { get; set; } /// - /// The payload's TagId + /// The payload's TagId - DEPRECATED: Misnomer - Use PayloadTypeId instead + /// + [Obsolete("Misnomer - Use PayloadTypeId instead")] + public ulong? PayloadTagId { + get => PayloadTypeId; + set { + if (PayloadTypeId == 0 && value.HasValue && value.Value > 0) + PayloadTypeId = value.Value; + } + } + + /// + /// The payload's TypeId + /// + public ulong PayloadTypeId { get; set; } + + /// + /// The opaque payload length in bytes; /// - public ulong PayloadTagId { get; set; } + public int PayloadLength { get; } /// /// Time of record creation diff --git a/rest_client/KnownVersions.cs b/InterlockLedger.Rest.Client/V13_7/Models/PageOfAllowedReadersRecordModel.cs similarity index 93% rename from rest_client/KnownVersions.cs rename to InterlockLedger.Rest.Client/V13_7/Models/PageOfAllowedReadersRecordModel.cs index 90fe4de..757f1a5 100644 --- a/rest_client/KnownVersions.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/PageOfAllowedReadersRecordModel.cs @@ -30,10 +30,8 @@ // // ****************************************************************************************************************************** -namespace rest_client; +namespace InterlockLedger.Rest.Client.V13_7; -public enum KnownVersions +public class PageOfAllowedReadersRecordModel : PageOf { - V6_0, - V7_6 } diff --git a/InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/PageOfOpaqueRecordsModel.cs similarity index 97% rename from InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/PageOfOpaqueRecordsModel.cs index 904cbdc..7102a0c 100644 --- a/InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/PageOfOpaqueRecordsModel.cs @@ -29,9 +29,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V7_6; + +namespace InterlockLedger.Rest.Client.V13_7; public class PageOfOpaqueRecordsModel : PageOf { diff --git a/InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs b/InterlockLedger.Rest.Client/V13_7/Models/ReaderModel.cs similarity index 88% rename from InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs rename to InterlockLedger.Rest.Client/V13_7/Models/ReaderModel.cs index e7f0167..684fcfe 100644 --- a/InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/ReaderModel.cs @@ -1,4 +1,4 @@ -// ****************************************************************************************************************************** +// ****************************************************************************************************************************** // // Copyright (c) 2018-2022 InterlockLedger Network // All rights reserved. @@ -30,9 +30,11 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V7_6; +namespace InterlockLedger.Rest.Client.V13_7; -public interface IRestChainV7_6 : V6_0.IRestChainV6_0 +public class ReaderModel { - IOpaqueStore OpaqueStore { get; } -} \ No newline at end of file + public string? Name { get; set; } + + public string? PublicKey { get; set; } +} diff --git a/InterlockLedger.Rest.Client/V6_0/Models/ReadingKeyModel.cs b/InterlockLedger.Rest.Client/V13_7/Models/ReadingKeyModel.cs similarity index 89% rename from InterlockLedger.Rest.Client/V6_0/Models/ReadingKeyModel.cs rename to InterlockLedger.Rest.Client/V13_7/Models/ReadingKeyModel.cs index 782ce0d..328a6a2 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/ReadingKeyModel.cs +++ b/InterlockLedger.Rest.Client/V13_7/Models/ReadingKeyModel.cs @@ -30,15 +30,15 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; public sealed class ReadingKeyModel { - public byte[] EncryptedIV { get; set; } + public byte[]? EncryptedIV { get; set; } - public byte[] EncryptedKey { get; set; } + public byte[]? EncryptedKey { get; set; } - public string PublicKeyHash { get; set; } + public string? PublicKeyHash { get; set; } - public string ReaderId { get; set; } + public string? ReaderId { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs b/InterlockLedger.Rest.Client/V13_7/RestChainV13_7.cs similarity index 81% rename from InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs rename to InterlockLedger.Rest.Client/V13_7/RestChainV13_7.cs index 28f66f0..700d416 100644 --- a/InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs +++ b/InterlockLedger.Rest.Client/V13_7/RestChainV13_7.cs @@ -30,14 +30,16 @@ // // ****************************************************************************************************************************** -namespace InterlockLedger.Rest.Client.V6_0 +namespace InterlockLedger.Rest.Client.V13_7; + +public class RestChainV13_7 : RestAbstractChain { - public class RestChainV6_0 : RestAbstractChain, IRestChainV6_0 - { - IJsonStore IRestChainV6_0.JsonStore => _jsonStore; + public IJsonStore JsonStore { get; } - internal RestChainV6_0(RestNodeV6_0 rest, ChainIdModel chainId) : base(rest, chainId) => _jsonStore = new JsonStoreImplementation(this); + public IOpaqueStore OpaqueStore { get; } - private readonly IJsonStore _jsonStore; + internal RestChainV13_7(RestNodeV13_7 node, ChainIdModel chainId) : base(node, chainId) { + JsonStore = new JsonStoreImplementation(this); + OpaqueStore = new OpaqueStoreImplementation(node, this); } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs b/InterlockLedger.Rest.Client/V13_7/RestNodeV13_7.cs similarity index 74% rename from InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs rename to InterlockLedger.Rest.Client/V13_7/RestNodeV13_7.cs index e8df1f1..f00c783 100644 --- a/InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs +++ b/InterlockLedger.Rest.Client/V13_7/RestNodeV13_7.cs @@ -29,25 +29,25 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** -#nullable enable -namespace InterlockLedger.Rest.Client.V6_0; +namespace InterlockLedger.Rest.Client.V13_7; -public abstract class RestAbstractNodeWithDocumentRegistry : RestAbstractNode, INodeWithDocumentRegistry where T : IRestChain +public class RestNodeV13_7 : RestAbstractNode, INodeWithDocumentRegistry { - public RestAbstractNodeWithDocumentRegistry(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) + public RestNodeV13_7(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) : base(x509Certificate, networkId, address) { } - public RestAbstractNodeWithDocumentRegistry(X509Certificate2 x509Certificate, ushort port, string address) + public RestNodeV13_7(X509Certificate2 x509Certificate, ushort port, string address) : base(x509Certificate, port, address) { } - public RestAbstractNodeWithDocumentRegistry(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) + public RestNodeV13_7(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) : base(certFile, certPassword, networkId, address) { } - public RestAbstractNodeWithDocumentRegistry(string certFile, string certPassword, ushort port, string address) + public RestNodeV13_7(string certFile, string certPassword, ushort port, string address) : base(certFile, certPassword, port, address) { } - public IDocumentRegistry DocumentRegistry => _documentRegistry ??= new DocumentRegistryImplementation(this); + protected internal override RestChainV13_7 BuildChain(ChainIdModel c) => new(this, c.Required()); + public IDocumentRegistry DocumentRegistry => _documentRegistry ??= new DocumentRegistryImplementation(this); private IDocumentRegistry? _documentRegistry; -} +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/Interfaces/IDocumentRegistry.cs b/InterlockLedger.Rest.Client/V6_0/Interfaces/IDocumentRegistry.cs deleted file mode 100644 index 62daba2..0000000 --- a/InterlockLedger.Rest.Client/V6_0/Interfaces/IDocumentRegistry.cs +++ /dev/null @@ -1,76 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** - -namespace InterlockLedger.Rest.Client.V6_0; - -public interface IDocumentRegistry -{ - Uri BaseUri { get; } - - Task GetDocumentsUploadConfigurationAsync(); - - Task> ListKnownChainsAcceptingTransactionsAsync(); - - Task> ListKnownChainsAsync(); - - Task RetrieveMetadataAsync(string locator); - - Task<(string Name, string ContentType, Stream Content)> RetrieveSingleAsync(string locator, int index); - - Task<(string Name, string ContentType, Stream Content)> RetrieveZipAsync(string locator); - - Task TransactionAddItemAsync(string transactionId, string path, string name, string comment, string contentType, Stream source); - - Task TransactionAddItemAsync(string transactionId, DirectoryInfo baseDirectory, FileInfo file, string comment, string contentType) - => string.IsNullOrWhiteSpace(transactionId) - ? throw new ArgumentNullException(nameof(transactionId)) - : baseDirectory is null - ? throw new ArgumentNullException(nameof(baseDirectory)) - : file is null - ? throw new ArgumentNullException(nameof(file)) - : string.IsNullOrWhiteSpace(contentType) - ? throw new ArgumentNullException(nameof(contentType)) - : !file.FullName.StartsWith(baseDirectory.FullName, StringComparison.InvariantCultureIgnoreCase) - ? throw new ArgumentException($"File ´{file.FullName}´ is not inside directory '{baseDirectory.FullName}' ") - : TransactionAddItemAsync(transactionId, - Path.GetRelativePath(baseDirectory.FullName, file.DirectoryName), - file.Name, - comment, - contentType, - file.OpenRead()); - - Task TransactionBeginAsync(DocumentsBeginTransactionModel transactionStart); - - Task TransactionCommitAsync(string transactionId); - - Task TransactionStatusAsync(string transactionId); -} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs b/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs deleted file mode 100644 index 2307526..0000000 --- a/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** -#nullable enable - -namespace InterlockLedger.Rest.Client.V6_0; - -public class RestNodeV6_0 : RestAbstractNodeWithDocumentRegistry -{ - public RestNodeV6_0(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) - : base(x509Certificate, networkId, address) { } - - public RestNodeV6_0(X509Certificate2 x509Certificate, ushort port, string address) - : base(x509Certificate, port, address) { } - - public RestNodeV6_0(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) - : base(certFile, certPassword, networkId, address) { } - - public RestNodeV6_0(string certFile, string certPassword, ushort port, string address) - : base(certFile, certPassword, port, address) { } - protected internal override RestChainV6_0 BuildChain(ChainIdModel c) => new(this, c.Required()); -} diff --git a/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs b/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs deleted file mode 100644 index 452d688..0000000 --- a/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs +++ /dev/null @@ -1,51 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** - -using InterlockLedger.Rest.Client.V6_0; - -namespace InterlockLedger.Rest.Client.V7_6 -{ - public class RestChainV7_6 : RestAbstractChain, IRestChainV7_6 - { - public IJsonStore JsonStore => _jsonStore; - - public IOpaqueStore OpaqueStore => _opaqueStore; - - internal RestChainV7_6(RestNodeV7_6 node, ChainIdModel chainId) : base(node, chainId) { - _jsonStore = new JsonStoreImplementation(this); - _opaqueStore = new OpaqueStoreImplementation(node, this); - } - - private readonly IJsonStore _jsonStore; - private readonly IOpaqueStore _opaqueStore; - } -} \ No newline at end of file diff --git a/rest_client/AbstractUsing.cs b/rest_client/AbstractUsing.cs index 706f8d1..08f6e9f 100644 --- a/rest_client/AbstractUsing.cs +++ b/rest_client/AbstractUsing.cs @@ -33,7 +33,7 @@ using System.IO.Compression; using InterlockLedger.Rest.Client; using InterlockLedger.Rest.Client.Abstractions; -using InterlockLedger.Rest.Client.V6_0; +using InterlockLedger.Rest.Client.V13_7; namespace rest_client; public abstract class AbstractUsing(RestAbstractNode node) where T : IRestChain @@ -49,48 +49,23 @@ protected Task AddRecordAsJsonAsync(T chain, ulong appId, ulo protected Task AddRecordAsync(T chain, ulong appId, params byte[] payload) => chain.Records.AddRecordAsync(new NewRecordModel() { ApplicationId = appId, PayloadBytes = payload }); - protected KeyPermitModel BuildKey() - => new( - id: "Key!U0y4av1fQGnOkC_1RkZLd4gE8vVSGVGJO5o1pzprQHo", - name: "InterlockLedger Documenter", - publicKey: "PubKey!KPkBERD5AQiuLtsWMFr3H6HtQVUMky1wFzL0TQF3VC-X24G4gjFqcrHHawNxNgDiw21YS8Fx6o1ornUOHqJPvIpYX1H2T2bqbIsIMNgyO4H234Ahken7SadTlnRPw92_sRpqprBobfuX9f9K6iM-SUJ2WY_6U4bAG4HdsFRV4yqfdDhrCAedBUs8O9qyne6vHFN8CiTEcapfQE7K-StPlW2wVmLdIXov2FdfYdJpFLXbbkgBCdkAZl2Oc86PRVzPkqD5dzl86QNZGZxhq2ngQ1UXASUQVh4tV5XqXQoe7xgeiE-1O82oWZWOvH6xdHjY9sMFyY3Mhjz8_MrI_0_DBEH7Pikmhp0LlyucyUA6dz4G_e13Xmyty2LDeqyYNhYORuZu2ev7zIEPvclpKeztC5gmJdCdcXZf_Omigb6I20HiggFBBrTGIjxJ_5xvpfb8DZCB6jqG5deTqybkjDJYPkA0TeoswKlwncT6mmZ3RdNNxoojUEX0TcBfSioKrnWRqGZ6Yc5wPFIvZ2REU6NP5gJv53FYe2yGAFygvWM1t2wBpWb6bx4h4BFKbfHPcCdmPqJHF0WQdMd7rtryENICHh9ozcVHtpHUtGdwoqV8gmeav836canWcXhKWQILiTiLpGAMa7FuUmPUr3K3q0c2rAy0IYXigjHvujTMz_0aGYqZoHD726gb4RADAQAB#RSA", - new AppPermissions(4).ToEnumerable(), - KeyPurpose.Protocol, - KeyPurpose.Action); + private static readonly KeyPermitModel _keyToPermit = new(id: "Key!U0y4av1fQGnOkC_1RkZLd4gE8vVSGVGJO5o1pzprQHo", + name: "InterlockLedger Documenter", + publicKey: "PubKey!KPkBERD5AQiuLtsWMFr3H6HtQVUMky1wFzL0TQF3VC-X24G4gjFqcrHHawNxNgDiw21YS8Fx6o1ornUOHqJPvIpYX1H2T2bqbIsIMNgyO4H234Ahken7SadTlnRPw92_sRpqprBobfuX9f9K6iM-SUJ2WY_6U4bAG4HdsFRV4yqfdDhrCAedBUs8O9qyne6vHFN8CiTEcapfQE7K-StPlW2wVmLdIXov2FdfYdJpFLXbbkgBCdkAZl2Oc86PRVzPkqD5dzl86QNZGZxhq2ngQ1UXASUQVh4tV5XqXQoe7xgeiE-1O82oWZWOvH6xdHjY9sMFyY3Mhjz8_MrI_0_DBEH7Pikmhp0LlyucyUA6dz4G_e13Xmyty2LDeqyYNhYORuZu2ev7zIEPvclpKeztC5gmJdCdcXZf_Omigb6I20HiggFBBrTGIjxJ_5xvpfb8DZCB6jqG5deTqybkjDJYPkA0TeoswKlwncT6mmZ3RdNNxoojUEX0TcBfSioKrnWRqGZ6Yc5wPFIvZ2REU6NP5gJv53FYe2yGAFygvWM1t2wBpWb6bx4h4BFKbfHPcCdmPqJHF0WQdMd7rtryENICHh9ozcVHtpHUtGdwoqV8gmeav836canWcXhKWQILiTiLpGAMa7FuUmPUr3K3q0c2rAy0IYXigjHvujTMz_0aGYqZoHD726gb4RADAQAB#RSA", + new AppPermissions(4).ToEnumerable(), + KeyPurpose.Protocol, + KeyPurpose.Action); - protected void DisplayOtherNodeInfo() { - if (_node is INodeWithDocumentRegistry node) - Console.WriteLine($" {node.DocumentRegistry.GetDocumentsUploadConfigurationAsync().Result}"); - } - - protected abstract Task DoExtraExercisesAsync(RestAbstractNode node, bool write); - - protected void Dump(string document) => Console.WriteLine($"----{Environment.NewLine}{document}{Environment.NewLine}----"); + protected abstract Task DoExtraExercisesAsync(RestAbstractNode node, T chain, bool write); protected internal async Task ExerciseAsync(bool write) { Console.WriteLine($"Client connected to {_node.BaseUri} using certificate '{_node.CertificateName}' via API version {Version}"); - if (write) { - Console.WriteLine("-- Create Chain:"); - try { - var chain = await _node.CreateChainAsync(new ChainCreationModel { - Name = "Rest Created Test Chain", - Description = "Just a test", - EmergencyClosingKeyPassword = "password", - ManagementKeyPassword = "password", - ManagementKeyStrength = KeyStrength.ExtraStrong, - KeysAlgorithm = Algorithms.RSA, - AdditionalApps = [4, 8] - }); - Console.WriteLine(chain); - } catch (InvalidOperationException) { - throw; - } catch (Exception e) { - Console.WriteLine(e); - } - Console.WriteLine(); - } + var documentRegistry = (_node is INodeWithDocumentRegistry node) ? node.DocumentRegistry : null; + if (write) + await CreateOneChainAsync(); Console.WriteLine(await _node.GetDetailsAsync()); - DisplayOtherNodeInfo(); + if (documentRegistry is not null) + Console.WriteLine($" {documentRegistry.GetDocumentsUploadConfigurationAsync().Result}"); var apps = await _node.Network.GetAppsAsync(); Console.WriteLine($"-- Valid apps for network {apps.Network}:"); foreach (var app in apps.ValidApps.OrderByDescending(a => (a.Id, a.AppVersion)).DistinctBy(a => a.Id).OrderBy(a => a.Id)) @@ -105,26 +80,99 @@ protected internal async Task ExerciseAsync(bool write) { Console.WriteLine(); Console.WriteLine("-- Chains:"); foreach (var chain in await _node.GetChainsAsync()) - await ExerciseChainAsync(_node, chain, transact: write); + await ExerciseChainAsync(_node, chain, write); Console.WriteLine(); Console.WriteLine("-- Mirrors:"); foreach (var chain in await _node.GetMirrorsAsync()) - await ExerciseChainAsync(_node, chain); + await ExerciseChainAsync(_node, chain, write: false); Console.WriteLine(); if (write) { - Console.WriteLine("-- Create Mirror:"); + await CreateMirrorsAsync(); + if (documentRegistry is not null) + await ExerciseDocumentRegistryAsync(documentRegistry); + } + } + + private async Task CreateMirrorsAsync() { + Console.WriteLine("-- Create Mirror:"); + try { + foreach (var chain in await _node.AddMirrorsOfAsync(_newMirrors)) + Console.WriteLine(chain); + } catch (Exception e) { + Console.WriteLine(e.Message); + } + Console.WriteLine(); + } + + private async Task CreateOneChainAsync() { + Console.WriteLine("-- Create Chain:"); + try { + var chain = await _node.CreateChainAsync(new ChainCreationModel { + Name = "Rest Created Test Chain", + Description = "Just a test", + EmergencyClosingKeyPassword = "password", + ManagementKeyPassword = "password", + ManagementKeyStrength = KeyStrength.ExtraStrong, + KeysAlgorithm = Algorithms.RSA, + AdditionalApps = [4, 8, 13] + }); + Console.WriteLine(chain); + } catch (Exception e) { + Console.WriteLine(e.Message); + } + Console.WriteLine(); + } + + private static async Task ExerciseDocumentRegistryAsync(IDocumentRegistry documentRegistry) { + var chains = await documentRegistry.ListKnownChainsAcceptingTransactionsAsync(); + Console.WriteLine($"-- Exercise Document Registry (MultiDocuments)"); + Console.WriteLine($"---- {chains.Count()} chains accepting MD transactions"); + var chainId = chains.FirstOrDefault(); + if (chainId is not null) { try { - foreach (var chain in await _node.AddMirrorsOfAsync(_newMirrors)) - Console.WriteLine(chain); + Console.WriteLine(); + Console.WriteLine(" Trying to begin a transaction:"); + var trx = await documentRegistry.TransactionBeginAsync(new DocumentsBeginTransactionModel { + Chain = chainId, + Comment = "C# REST client testing", + Compression = "BROTLI", + Encryption = "PBKDF2-SHA256-AES256-LOW", + Password = _password + }); + Console.WriteLine(trx); + Console.WriteLine(" Trying to store a nice document:"); + await documentRegistry.TransactionAddItemAsync(trx.TransactionId, "/", "Simple Test 1 Razão.txt", "First file", "text/plain", new MemoryStream(Content, writable: false)); + Console.WriteLine(await documentRegistry.TransactionStatusAsync(trx.TransactionId)); + await documentRegistry.TransactionAddItemAsync(trx.TransactionId, "/", "Simple Test 2 Emoção.txt", "Second file", "text/plain", new MemoryStream(Content, writable: false)); + Console.WriteLine(await documentRegistry.TransactionStatusAsync(trx.TransactionId)); + var locator = await documentRegistry.TransactionCommitAsync(trx.TransactionId); + Console.WriteLine($" Documents locator: '{locator}'"); + Console.WriteLine($" {documentRegistry.RetrieveMetadataAsync(locator)}"); + var secondFile = await documentRegistry.RetrieveSingleAsync(locator, 1); + if (secondFile is null) + Console.WriteLine("Could not retrieve second file"); + else { + using var streamReader = new StreamReader(secondFile.Value.Content); + Console.WriteLine($" Retrieved second file {secondFile.Value.Name} : '{streamReader.ReadToEnd()}'"); + var blobFile = await documentRegistry.RetrieveZipAsync(locator); + if (blobFile is null) + Console.WriteLine("Could not retrieve zip file"); + else { + using var stream = blobFile.Value.Content; + var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false, Encoding.UTF8); + Console.WriteLine($" Blob file '{blobFile.Value.Name}' contains {zip.Entries.Count} entries."); + foreach (var entry in zip.Entries) + Console.WriteLine($" - {entry.FullName}"); + } + } } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } - Console.WriteLine(); } - await DoExtraExercisesAsync(_node, write); } - protected async Task ExerciseChainAsync(RestAbstractNode node, T chain, bool transact = false) { + + protected async Task ExerciseChainAsync(RestAbstractNode node, T chain, bool write) { Console.WriteLine(chain); var summary = await chain.GetSummaryAsync(); @@ -159,39 +207,27 @@ protected async Task ExerciseChainAsync(RestAbstractNode node, T chain, bool Console.WriteLine(" RecordsAsJson:"); foreach (var record in (await chain.RecordsAsJson.FromToAsync(0, 2)).Safe().Items) Console.WriteLine($" {record}"); - if (transact) { - await TryToAddNiceUnpackedRecordAsync(chain); + if (write) { await TryToAddNiceRecordAsync(chain); await TryToAddNiceJsonRecordAsync(chain); - await TryToAddBadlyEncodedUnpackedRecordAsync(chain); await TryToAddBadRecordAsync(chain); await TryToPermitApp4Async(chain); - await TryToStoreNiceDocumentsAsync(chain); await TryToForceInterlockAsync(chain); await TryToPermitKeyAsync(chain); } Console.WriteLine(); + await DoExtraExercisesAsync(_node, chain, write); } - protected async Task TryToAddBadlyEncodedUnpackedRecordAsync(T chain) { - try { - Console.WriteLine(); - Console.WriteLine(" Trying to add a badly encoded unpacked record:"); - var record = await chain.Records.AddRecordAsync(1, 300, [10, 5, 0, 0, 20, 5, 4, 0, 1, 2, 3]); - Console.WriteLine($" {record}"); - } catch (Exception e) { - Console.WriteLine(e); - } - } protected async Task TryToAddBadRecordAsync(T chain) { try { Console.WriteLine(); Console.WriteLine(" Trying to add a bad record:"); - var record = await AddRecordAsync(chain, 1, 0); + var record = await AddRecordAsync(chain, 1, 30); Console.WriteLine($" {record}"); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } @@ -202,7 +238,7 @@ protected async Task TryToAddNiceJsonRecordAsync(T chain) { var record = await AddRecordAsJsonAsync(chain, 1, 300, new { TagId = 300, Version = 1, Apps = new ulong[] { 1, 2, 3 } }); Console.WriteLine($" {record}"); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } @@ -213,18 +249,7 @@ protected async Task TryToAddNiceRecordAsync(T chain) { var record = await AddRecordAsync(chain, 1, 248, 52, 10, 5, 0, 0, 20, 5, 4, 0, 1, 2, 3); Console.WriteLine($" {record}"); } catch (Exception e) { - Console.WriteLine(e); - } - } - - protected async Task TryToAddNiceUnpackedRecordAsync(T chain) { - try { - Console.WriteLine(); - Console.WriteLine(" Trying to add a nice unpacked record:"); - var record = await chain.Records.AddRecordAsync(1, 300, [5, 0, 0, 20, 5, 4, 0, 1, 2, 3]); - Console.WriteLine($" {record}"); - } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } @@ -232,102 +257,68 @@ protected async Task TryToForceInterlockAsync(T chain) { try { Console.WriteLine(); Console.WriteLine(" Trying to force an interlock:"); - var interlock = await chain.Interlockings.ForceInterlockAsync(new ForceInterlockModel() { HashAlgorithm = HashAlgorithms.Copy, MinSerial = 1, TargetChain = "72_1DyspOtgOpg5XG2ihe7M0xCb2DhrZIQWv3-Bivy4" }); + var interlock = await chain.Interlockings.ForceInterlockAsync(new ForceInterlockModel("72_1DyspOtgOpg5XG2ihe7M0xCb2DhrZIQWv3-Bivy4") { HashAlgorithm = HashAlgorithms.Copy, MinSerial = 1 }); Console.WriteLine($" {interlock}"); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } protected async Task TryToPermitApp4Async(T chain) { try { var apps = await chain.PermitAppsAsync(4); - Console.WriteLine($" Permit app 4: {string.Join(", ", apps)}"); + Console.WriteLine($" Permit app 4: [{string.Join(", ", apps)}]"); Console.WriteLine(); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } protected async Task TryToPermitKeyAsync(T chain) { try { Console.WriteLine(); - Console.WriteLine(" Trying to permit some keys:"); - foreach (var key in await chain.PermitKeysAsync(BuildKey())) + Console.WriteLine($" Trying to permit key: {_keyToPermit.Name} ({_keyToPermit.Id})"); + foreach (var key in await chain.PermitKeysAsync(_keyToPermit)) Console.WriteLine($" {key}"); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } - protected async Task TryToStoreNiceDocumentsAsync(T chain) { - if (_node is IDocumentRegistry docsApp) - try { - Console.WriteLine(); - Console.WriteLine(" Trying to begin a transaction:"); - var trx = await docsApp.TransactionBeginAsync(new DocumentsBeginTransactionModel { - Chain = chain.Id, - Comment = "C# REST client testing", - Compression = "BROTLI", - Encryption = "PBKDF2-SHA256-AES256-LOW", - Password = _password - }); - Console.WriteLine(trx); - Console.WriteLine(" Trying to store a nice document:"); - await docsApp.TransactionAddItemAsync(trx.TransactionId, "/", "Simple Test 1 Razão.txt", "First file", "text/plain", new MemoryStream(Content, writable: false)); - Console.WriteLine(await docsApp.TransactionStatusAsync(trx.TransactionId)); - await docsApp.TransactionAddItemAsync(trx.TransactionId, "/", "Simple Test 2 Emoção.txt", "Second file", "text/plain", new MemoryStream(Content, writable: false)); - Console.WriteLine(await docsApp.TransactionStatusAsync(trx.TransactionId)); - var locator = await docsApp.TransactionCommitAsync(trx.TransactionId); - Console.WriteLine($" Documents locator: '{locator}'"); - Console.WriteLine($" {docsApp.RetrieveMetadataAsync(locator)}"); - var secondFile = await docsApp.RetrieveSingleAsync(locator, 1); - using var streamReader = new StreamReader(secondFile.Content); - Console.WriteLine($" Retrieved second file {secondFile.Name} : '{streamReader.ReadToEnd()}'"); - var blobFile = await docsApp.RetrieveZipAsync(locator); - using var stream = blobFile.Content; - var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false, Encoding.UTF8); - Console.WriteLine($" Blob file '{blobFile.Name}' contains {zip.Entries.Count} entries."); - foreach (var entry in zip.Entries) - Console.WriteLine($" - {entry.FullName}"); - } catch (Exception e) { - Console.WriteLine(e); + protected static async Task ReadSomeJsonDocumentsRecordsAsync(X509Certificate2 certificate, TJson jsonStore, IRecordsStore recordsStore, string chainId, ulong payloadTagId, string payloadTypeName, Func retrieveAndDumpAsync) where TJson : IJsonStore { + ulong[] filteredRecords = await FindJsonRecords(recordsStore, payloadTagId); + if (filteredRecords.SafeAny()) { + Console.WriteLine($"{Environment.NewLine}==== {payloadTypeName} from {chainId}: {filteredRecords.Length} records"); + foreach (var serial in filteredRecords) { + await retrieveAndDumpAsync(certificate, jsonStore, chainId, serial); } + } else { + Console.WriteLine($"No {payloadTypeName} found in {chainId}"); + } + + static async Task FindJsonRecords(IRecordsStore recordsStore, ulong payloadTagId) { + var records = (await recordsStore.RecordsFromAsync(0, pageSize: 0, lastToFirst: true, ommitPayload: true))?.Items; + return records.Safe() + .Where(r => r.ApplicationId == 8ul && r.PayloadTagId == payloadTagId) + .Take(3) + .Select(r => r.Serial) + .ToArray(); + } + } - protected internal static async Task ExerciseJsonDocumentAsync(RestAbstractNode node, bool write) { - try { - var chains = (await node.GetChainsAsync()).Where(c => c is IRestChainV6_0).Cast(); - foreach (var chain in chains) { - if (write) { - // Add something - } - var records = (await chain.Records.RecordsFromAsync(0, pageSize: 0))?.Items; - if (records.SafeAny()) { - var filteredRecords = records.Where(r => r.ApplicationId == 8ul && r.PayloadTagId == 2100) - .Select(r => r.Serial) - .ToArray(); - Console.WriteLine($"{Environment.NewLine}==== JSON from {chain.Id}: {filteredRecords.Length} records"); - foreach (var serial in filteredRecords) { - var json = await chain.JsonStore.RetrieveAsync(serial); - Console.WriteLine($"{Environment.NewLine}Json at {chain.Id}@{serial}:"); - if (json is null) - Console.WriteLine("-- Could not retrieve it!"); - else if (json.JsonText.IsValidJson()) - Console.WriteLine($"-- {json.JsonText.Ellipsis(300)}"); - else if (json.EncryptedJson is null) - Console.WriteLine("-- Invalid format!"); - else { - Console.WriteLine($"-- {json.EncryptedJson}"); - Console.WriteLine($"-- {json.EncryptedJson.DecodedWith(node.Certificate)}"); - } - } - } else { - Console.WriteLine($"No jsonDocuments found in {chain.Id}"); - } - } - } catch (Exception e) { - Console.WriteLine(e); + protected static async Task RetrieveAndDumpJsonDocumentsAsync(X509Certificate2 certificate, TJson jsonStore, string chainId, ulong serial) where TJson : IJsonStore { + var json = await jsonStore.RetrieveAsync(serial); + Console.WriteLine($"{Environment.NewLine}Json at {chainId}@{serial}:"); + if (json is null) + Console.WriteLine("-- Could not retrieve it!"); + else if (json.JsonText.IsValidJson()) + Console.WriteLine($"-- {json.JsonText.Ellipsis(300)}"); + else if (json.EncryptedJson is null) + Console.WriteLine("-- Invalid format!"); + else { + Console.WriteLine($"-- {json.EncryptedJson}"); + Console.WriteLine($"-- {json.EncryptedJson.DecodedWith(certificate)}"); } } diff --git a/rest_client/Program.cs b/rest_client/Program.cs index 04c26fa..103a950 100644 --- a/rest_client/Program.cs +++ b/rest_client/Program.cs @@ -31,8 +31,7 @@ // ****************************************************************************************************************************** using Cocona; -using InterlockLedger.Rest.Client.V6_0; -using InterlockLedger.Rest.Client.V7_6; +using InterlockLedger.Rest.Client.V13_7; using rest_client; @@ -40,27 +39,15 @@ [Argument(Description = "Password to open file containing client certificate")] string certificatePassword, [Argument(Description = "Port to access the node API (ex.: 32032)")] ushort apiPort, [Argument(Description = "Address to access the node API (ex.: node.il2, localhost)")] string restURL, - [Option(Description = "Try to write into the node (Default: false)")] bool writeable = false, - [Option(Description = "API version to exercise in the node")] KnownVersions version = KnownVersions.V7_6) => { + [Option(Description = "Try to write into the node (Default: false)")] bool writeable = false) => { try { var pathToCertificatePfxFileInfo = new FileInfo(pathToCertificatePfxFile); if (!pathToCertificatePfxFileInfo.Exists) { Console.WriteLine($"File {pathToCertificatePfxFile} not found!"); return 2; } - switch (version) { - case KnownVersions.V6_0: - var client6_0 = new RestNodeV6_0(pathToCertificatePfxFileInfo.FullName, certificatePassword, apiPort, restURL); - new UsingV6_0(client6_0).ExerciseAsync(writeable).Wait(); - break; - case KnownVersions.V7_6: - var client7_6 = new RestNodeV7_6(pathToCertificatePfxFileInfo.FullName, certificatePassword, apiPort, restURL); - new UsingV7_6(client7_6).ExerciseAsync(writeable).Wait(); - break; - default: - Console.WriteLine($"No known API version chosen '{version}'"); - break; - } + var client = new RestNodeV13_7(pathToCertificatePfxFileInfo.FullName, certificatePassword, apiPort, restURL); + new UsingV13_7(client).ExerciseAsync(writeable).Wait(); return 0; } catch (Exception e) { Console.WriteLine(e); diff --git a/rest_client/Properties/launchSettings.json b/rest_client/Properties/launchSettings.json index 1ad5126..cfbcfcb 100644 --- a/rest_client/Properties/launchSettings.json +++ b/rest_client/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "rest_client": { "commandName": "Project", - "commandLineArgs": "D:\\vault\\clients\\minerva-debug\\certificates\\rest.api.pfx DebugKey 32024 node.il2" + "commandLineArgs": "D:\\vault\\clients\\minerva-debug\\certificates\\rest.api.pfx DebugKey 32024 node.il2 --writeable" }, "rest_client Help": { "commandName": "Project", diff --git a/rest_client/UsingV13_7.cs b/rest_client/UsingV13_7.cs new file mode 100644 index 0000000..bcf0c08 --- /dev/null +++ b/rest_client/UsingV13_7.cs @@ -0,0 +1,100 @@ +// ****************************************************************************************************************************** +// +// Copyright (c) 2018-2022 InterlockLedger Network +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ****************************************************************************************************************************** + +using InterlockLedger.Rest.Client.Abstractions; +using InterlockLedger.Rest.Client.V13_7; + +namespace rest_client; + +public class UsingV13_7(RestAbstractNode node) : AbstractUsing(node) +{ + protected override string Version => "13.7"; + + protected override async Task DoExtraExercisesAsync(RestAbstractNode node, RestChainV13_7 chain, bool write) { + await ExerciseJsonDocumentsV13_7Async(chain, node.Certificate, write); + await ExerciseOpaqueRecordsAsync(chain, write); + } + + private static async Task ExerciseJsonDocumentsV13_7Async(RestChainV13_7 chain, X509Certificate2 certificate, bool write) { + try { + Console.WriteLine(" JsonDocuments:"); + if (write) { + // Add something + } + await ReadSomeJsonDocumentsRecordsAsync(certificate, chain.JsonStore, chain.Records, chain.Id, 2100, "jsonDocuments", RetrieveAndDumpJsonDocumentsAsync); + var query = await chain.JsonStore.RetrieveAllowedReadersAsync(chain.Id); + if (query.TotalNumberOfPages > 0) { + Console.WriteLine($" RetrieveAllowedReadersAsync retrieved first page of {query.TotalNumberOfPages} pages with {query.Items.Count()} items"); + Console.WriteLine(query.First()?.AsJson()); + } else { + Console.WriteLine($" RetrieveAllowedReadersAsync retrieved no data"); + } + } catch (Exception e) { + Console.WriteLine(e.Message); + } + Console.WriteLine(); + } + + private static async Task ExerciseOpaqueRecordsAsync(RestChainV13_7 chain, bool write) { + Console.WriteLine(" OpaqueRecords:"); + var opaqueStore = chain.OpaqueStore; + var query = await opaqueStore.QueryRecordsFromAsync(appId: 13); + Console.WriteLine($" LastChangedRecordSerial {query.LastChangedRecordSerial} for {chain.Id}"); + try { + ulong serialToRetrieve = 0; + if (write) { + Console.WriteLine(" Trying to add an opaque payload #13,100"); + var result = await opaqueStore.AddRecordAsync(appId: 13, payloadTypeId: 100, query.LastChangedRecordSerial, [1, 2, 3, 4]); + serialToRetrieve = result.Serial; + } else { + serialToRetrieve = query.First()?.Serial ?? 0; + } + var response = await opaqueStore.RetrieveSinglePayloadAsync(serialToRetrieve); + if (response.HasValue) { + Console.WriteLine($" Retrieved AppId: {response.Value.AppId}"); + Console.WriteLine($" Retrieved PayloadTypeId: {response.Value.PayloadTypeId}"); + Console.WriteLine($" Retrieved CreatedAt: {response.Value.CreatedAt}"); + Console.WriteLine($" Retrieved Bytes: {response.Value.Content.BigEndianReadUInt():x}"); + } else { + Console.WriteLine(" Could not retrieve opaque payload"); + } + } catch (Exception ex) { + if (ex is AggregateException ae) { + Console.WriteLine(ae.Message); + } else { + Console.WriteLine(ex.Message); + } + } + Console.WriteLine(); + } + +} \ No newline at end of file diff --git a/rest_client/UsingV7_6.cs b/rest_client/UsingV7_6.cs deleted file mode 100644 index 6395835..0000000 --- a/rest_client/UsingV7_6.cs +++ /dev/null @@ -1,74 +0,0 @@ -// ****************************************************************************************************************************** -// -// Copyright (c) 2018-2022 InterlockLedger Network -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met -// -// * Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES, LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ****************************************************************************************************************************** - -using InterlockLedger.Rest.Client.Abstractions; -using InterlockLedger.Rest.Client.V7_6; - -namespace rest_client; - -public class UsingV7_6(RestAbstractNode node) : AbstractUsing(node) -{ - protected override string Version => "7.6"; - - protected override async Task DoExtraExercisesAsync(RestAbstractNode node, bool write) { - await ExerciseJsonDocumentAsync(node, write); - await ExerciseOpaqueRecordsAsync(node, write); - } - - private async Task ExerciseOpaqueRecordsAsync(RestAbstractNode node, bool write) { - var chains = await node.GetChainsAsync(); - foreach (var chain in chains) { - var opaqueStore = chain.OpaqueStore; - var query = await opaqueStore.QueryRecordsFromAsync(appId: 13); - Console.WriteLine($"LastChangedRecordSerial {query.LastChangedRecordSerial} for {chain.Id}"); - try { - ulong serialToRetrieve = 0; - if (write) { - var result = await opaqueStore.AddRecordAsync(appId: 13, payloadTypeId: 100, query.LastChangedRecordSerial, [1, 2, 3, 4]); - serialToRetrieve = result.Serial; - } else { - serialToRetrieve = query.First()?.Serial ?? 0; - } - var response = await opaqueStore.RetrieveSinglePayloadAsync(serialToRetrieve); - Console.WriteLine($"Retrieved AppId: {response.AppId}"); - Console.WriteLine($"Retrieved PayloadTypeId: {response.PayloadTypeId}"); - Console.WriteLine($"Retrieved Bytes: {response.Content.BigEndianReadUInt():x}"); - } catch (Exception ex) { - if (ex is AggregateException ae) { - Console.WriteLine(ae); - } else { - Console.WriteLine(ex); - } - } - } - } -} \ No newline at end of file