diff --git a/InterlockLedger.Rest.Client/Abstractions/Globals.cs b/InterlockLedger.Rest.Client/Abstractions/Globals.cs index d6c2f9c..885d01b 100644 --- a/InterlockLedger.Rest.Client/Abstractions/Globals.cs +++ b/InterlockLedger.Rest.Client/Abstractions/Globals.cs @@ -41,6 +41,7 @@ internal static class Globals PropertyNamingPolicy = JsonNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Skip, WriteIndented = true, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace, }; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs b/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs index aad46ed..4c571bc 100644 --- a/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs +++ b/InterlockLedger.Rest.Client/Abstractions/InterlockingsImplementation.cs @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions; internal sealed class InterlockingsImplementation : IRestInterlockings { public InterlockingsImplementation(RestAbstractChain parent) { - _parent = parent.Required(nameof(parent)); + _parent = parent.Required(); _rest = _parent._rest; _id = _parent.Id; } diff --git a/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs b/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs index 1e975d6..8466297 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RecordsAsJsonImplementation.cs @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions; internal sealed class RecordsAsJsonImplementation : IRestRecordsAsJson { public RecordsAsJsonImplementation(RestAbstractChain parent) { - _parent = parent.Required(nameof(parent)); + _parent = parent.Required(); _rest = _parent._rest; _id = _parent.Id; } @@ -49,11 +49,11 @@ public Task AddAsync(ulong applicationId, ulong payloadTagId, 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> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false) - => _rest.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}"); + 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> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false) - => _rest.GetAsync>($"records@{_id}/asJson?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}"); + 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}"); private readonly string _id; private readonly RestAbstractChain _parent; diff --git a/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs b/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs index 485c887..81b11e1 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RecordsImplementation.cs @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions; internal sealed class RecordsImplementation : IRestRecords { public RecordsImplementation(RestAbstractChain parent) { - _parent = parent.Required(nameof(parent)); + _parent = parent.Required(); _rest = _parent._rest; _id = _parent.Id; } @@ -49,11 +49,11 @@ public Task AddRecordAsync(ulong applicationId, ulong payloadTagId, 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) - => _rest.GetAsync>($"records@{_id}?firstSerial={firstSerial}&page={page}&pageSize={pageSize}"); + 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) - => _rest.GetAsync>($"records@{_id}?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}"); + 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}"); private readonly string _id; private readonly RestAbstractChain _parent; diff --git a/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs b/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs index 7dfe99a..0c52fef 100644 --- a/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs +++ b/InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs @@ -96,45 +96,40 @@ Task IRestNodeInternals.PostStreamAsync(string url, Stream body, string co public override string ToString() => $"Node [{BaseUri}] connected with certificate '{CertificateName}'"; - protected readonly X509Certificate2 _certificate; - - protected static async Task GetResponseAsync(HttpWebRequest req) { - return await ValidatedAsync(await GetInnerResponseAsync(req).ConfigureAwait(false)); - - static async Task ValidatedAsync(HttpWebResponse resp) => await IfOkOrCreatedReturnAsync(resp, () => Task.FromResult(resp)); - static async Task GetInnerResponseAsync(HttpWebRequest req) { - 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); - } + protected internal readonly X509Certificate2 _certificate; + + protected internal static async Task GetResponseAsync(HttpWebRequest req) { + 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); } } - protected static async Task GetStringResponseAsync(HttpWebRequest req) => await ReadAsStringAsync(await GetResponseAsync(req)); + protected internal static async Task GetStringResponseAsync(HttpWebRequest req) => await ReadAsStringAsync(await GetResponseAsync(req)); - protected static async Task ReadAsStringAsync(HttpWebResponse resp) { + protected internal static async Task ReadAsStringAsync(HttpWebResponse resp) { if (resp == null) return string.Empty; using var readStream = new StreamReader(resp.GetResponseStream()); return await readStream.ReadToEndAsync(); } - protected abstract T BuildChain(ChainIdModel c); + protected internal abstract T BuildChain(ChainIdModel c); - protected async Task CallApiAsync(string url, string method, string accept = "application/json") + protected internal async Task CallApiAsync(string url, string method, string accept = "application/json") => await GetStringResponseAsync(PrepareRequest(url, method, accept)); - protected async Task CallApiPlainDocAsync(string url, string method, string accept = "plain/text") + protected internal async Task CallApiPlainDocAsync(string url, string method, string accept = "plain/text") => await GetStringResponseAsync(PrepareRequest(url, method, accept)); - protected 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 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 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)) @@ -144,19 +139,23 @@ protected async Task CallApiRawDocAsync(string url, string met var resp = await GetResponseAsync(PrepareRequest(url, method, accept)); return (ParseFileName(resp), resp.ContentType, resp.GetResponseStream()); } + protected internal async Task<(ulong AppId, ulong PayloadTypeId, 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()); + } - protected 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 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 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))); } - protected HttpWebRequest PrepareRequest(string url, string method, string accept) { + protected internal HttpWebRequest PrepareRequest(string url, string method, string accept) { #pragma warning disable SYSLIB0014 // Type or member is obsolete var req = (HttpWebRequest)WebRequest.Create(new Uri(BaseUri, url)); #pragma warning restore SYSLIB0014 // Type or member is obsolete @@ -170,7 +169,7 @@ protected HttpWebRequest PrepareRequest(string url, string method, string accept return req; } - protected 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); @@ -189,7 +188,15 @@ private static async Task CheckMessageAsync(HttpWebResponse resp) { : throw new InvalidDataException($"API error: {resp.StatusCode}(#{(int)resp.StatusCode}) {resp.StatusDescription}{Environment.NewLine}{content}"); } - private static TR Deserialize(string s) => JsonSerializer.Deserialize(s, Globals.JsonSettings); + 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); + } + } private static X509Certificate2 GetCertFromFile(string certPath, string certPassword) => new(certPath, certPassword, X509KeyStorageFlags.PersistKeySet); diff --git a/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs b/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs index e7ca1e3..0167be4 100644 --- a/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs +++ b/InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs @@ -40,7 +40,7 @@ public interface IRestRecords Task AddRecordAsync(ulong applicationId, ulong payloadTagId, RecordType type, byte[] bytes); - Task> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10); + 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); + 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/IRestRecordsAsJson.cs b/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs index 71cfdca..bfca2a9 100644 --- a/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs +++ b/InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs @@ -40,7 +40,7 @@ public interface IRestRecordsAsJson Task AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload); - Task> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false); + 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); + 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 2fb8517..377ea0e 100644 --- a/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj +++ b/InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj @@ -1,59 +1,64 @@ - + - - net6.0 - 7.0.1 - true - Copyright (c) 2018-2022 InterlockLedger Network - - This library implements a C# wrapper around the InterlockLedger Node REST API. + + net8.0 + 13.6.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 - Drop diagram - Update to square icon - il2.png - InterlockLedger Network - InterlockLedger - LICENSE - Enable - + 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 + il2.png + InterlockLedger Network + InterlockLedger + LICENSE + Enable + - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - True - - - - True - - - + + + True + + + + True + + + + + + + diff --git a/InterlockLedger.Rest.Client/Models/AppPermissions.cs b/InterlockLedger.Rest.Client/Models/AppPermissions.cs index ad3bd33..72afbc1 100644 --- a/InterlockLedger.Rest.Client/Models/AppPermissions.cs +++ b/InterlockLedger.Rest.Client/Models/AppPermissions.cs @@ -33,16 +33,16 @@ namespace InterlockLedger.Rest.Client; [JsonConverter(typeof(Converter))] -public class AppPermissions +public partial class AppPermissions { - public static readonly Regex Mask = new("^#[0-9]+(,[0-9]+)*$"); + public static readonly Regex Mask = AppPermissionsRegex(); public AppPermissions(ulong app, params ulong[] appActions) : this(app, (IEnumerable)appActions) { } public AppPermissions(ulong app, IEnumerable appActions) { AppId = app; - ActionIds = appActions ?? Array.Empty(); + ActionIds = appActions ?? []; } /// @@ -57,10 +57,10 @@ public AppPermissions(ulong app, IEnumerable appActions) { public string TextualRepresentation => $"#{AppId}{_firstComma}{ActionIds.WithCommas(noSpaces: true)}"; - public IEnumerable ToEnumerable() => new AppPermissions[] { this }; + public IEnumerable ToEnumerable() => [this]; public override string ToString() { - var actions = ActionIds?.ToArray() ?? Array.Empty(); + var actions = ActionIds?.ToArray() ?? []; var plural = (actions.Length == 1 ? "" : "s"); return $"App #{AppId} {(actions.Length > 0 ? $"Action{plural} {actions.WithCommas(noSpaces: true)}" : "All Actions")}"; } @@ -94,4 +94,7 @@ internal AppPermissions(string textualRepresentation) { private string _firstComma => ActionIds.Any() ? "," : string.Empty; private static ulong AsUlong(string s) => ulong.TryParse(s?.Trim(), out ulong result) ? result : 0; + + [GeneratedRegex("^#[0-9]+(,[0-9]+)*$")] + private static partial Regex AppPermissionsRegex(); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs b/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs index 3806a7f..bfa359f 100644 --- a/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs +++ b/InterlockLedger.Rest.Client/Models/ChainSummaryModel.cs @@ -32,7 +32,7 @@ namespace InterlockLedger.Rest.Client; -public sealed class ChainSummaryModel : ChainIdModel +public sealed partial class ChainSummaryModel : ChainIdModel { /// /// List of active apps (only the numeric ids) @@ -63,14 +63,14 @@ public sealed class ChainSummaryModel : ChainIdModel public ulong[] LicensedApps { get { - var match = Regex.Match(LicensingStatus.Safe(), @"Apps: \[([\d,]+)\]"); + var match = LicensedAppsRegex().Match(LicensingStatus.Safe()); if (match.Success) if (match.Groups.Count > 1) { string[] values = match.Groups[1].Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); return values.Select(s => ulong.Parse(s)).ToArray(); } - return Array.Empty(); + return []; } } @@ -88,4 +88,7 @@ public ulong[] LicensedApps { /// Size in bytes the chain occupies in storage /// public ulong? SizeInBytes { get; set; } + + [GeneratedRegex(@"Apps: \[([\d,]+)\]")] + private static partial Regex LicensedAppsRegex(); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs b/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs index 9ecc4b1..66f3eb5 100644 --- a/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs +++ b/InterlockLedger.Rest.Client/Models/ForceInterlockModel.cs @@ -36,7 +36,7 @@ public class ForceInterlockModel { public ForceInterlockModel() { } - public ForceInterlockModel(string targetChain) => TargetChain = targetChain.Required(nameof(targetChain)); + public ForceInterlockModel(string targetChain) => TargetChain = targetChain.Required(); /// /// Hash algorithm to use. Default: SHA256 diff --git a/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs b/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs index 1f75f83..3e5147b 100644 --- a/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs +++ b/InterlockLedger.Rest.Client/Models/KeyPermitModel.cs @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client; public class KeyPermitModel { public KeyPermitModel(string id, string name, string publicKey, ulong app, IEnumerable appActions, params KeyPurpose[] purposes) - : this(id, name, publicKey, new AppPermissions[] { new AppPermissions(app, appActions) }, purposes) { + : this(id, name, publicKey, [new(app, appActions)], purposes) { } public KeyPermitModel(string id, string name, string publicKey, IEnumerable permissions, params KeyPurpose[] purposes) { diff --git a/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs b/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs index 4e9ade5..3860a30 100644 --- a/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs +++ b/InterlockLedger.Rest.Client/Models/NodeCommonModel.cs @@ -70,27 +70,35 @@ public class NodeCommonModel /// public string OwnerName { get; set; } + /// + /// Peer address in the cannonical form like 'ilkl-minerva://node.il2:32025' + /// + public string PeerAddress { get; set; } + /// /// List of active roles running in the node /// public IEnumerable Roles { get; set; } + + public virtual string ResolvedPeerAddress => PeerAddress; + /// /// Version of software running the Node /// public Versions SoftwareVersions { get; set; } - public override string ToString() => $@"Node '{Name}' {Id} -Running il2 node#{SoftwareVersions?.Node} using [Message Envelope Wire Format #{SoftwareVersions?.MessageEnvelopeWireFormat}] - with Peer2Peer#{SoftwareVersions?.Peer2peer} and CoreLibs#{SoftwareVersions?.CoreLibs} -Network {Network} -Color {Fancy(Color)} -Owner {OwnerName} #{OwnerId} -Roles: {string.Join(", ", Roles ?? _empty)} -{Extras} -"; - - protected static readonly IEnumerable _empty = Enumerable.Empty(); + public override string ToString() => $""" + Node '{Name}' [{Id}] at {ResolvedPeerAddress} + Running il2 Node#{SoftwareVersions?.NodeVersion} + with Peer2Peer#{SoftwareVersions?.Peer2peer}, Tags#{SoftwareVersions?.Tags} and CoreLibs#{SoftwareVersions?.CoreLibs} + Network: {Network} + Color: {Fancy(Color)} + Owner: {OwnerName} #{OwnerId} + Roles: {Roles.JoinedBy(", ")} + {Extras} + """; + protected static readonly IEnumerable _empty = []; protected virtual string Extras { get; } private static string Fancy(Color color) => color.IsNamedColor ? color.Name : "#" + color.Name.Remove(0, 2).ToUpperInvariant(); diff --git a/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs b/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs index c812634..ec36c0e 100644 --- a/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs +++ b/InterlockLedger.Rest.Client/Models/NodeDetailsModel.cs @@ -38,9 +38,23 @@ namespace InterlockLedger.Rest.Client; public class NodeDetailsModel : NodeCommonModel { /// - /// List of owned records, only the ids + /// List of owned chains, only the ids /// public IEnumerable Chains { get; set; } - protected override string Extras => $"Chains: {string.Join(", ", Chains ?? _empty)}"; + /// + /// Other properties the node may have + /// + public Dictionary Extensions { get; set; } + + private static string ToLine(KeyValuePair x) => $"{x.Key}: {x.Value}"; + + private static readonly string _itemSeparator = $"{Environment.NewLine} - "; + + private static string ToList(string label, IEnumerable items) => items.PrependedBy(label).JoinedBy(_itemSeparator); + + protected override string Extras => $""" + {ToList("Extensions:", Extensions.Select(ToLine))} + {ToList("Chains:", Chains)} + """; } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/PeerModel.cs b/InterlockLedger.Rest.Client/Models/PeerModel.cs index ed37a8c..049b08e 100644 --- a/InterlockLedger.Rest.Client/Models/PeerModel.cs +++ b/InterlockLedger.Rest.Client/Models/PeerModel.cs @@ -51,6 +51,6 @@ public sealed class PeerModel : NodeCommonModel /// Network protocol the peer is listening /// public string Protocol { get; set; } - - protected override string Extras => $"P2P listening at {Address}:{Port}"; + 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/PublishedApp.cs b/InterlockLedger.Rest.Client/Models/PublishedApp.cs index 865fba4..76ef882 100644 --- a/InterlockLedger.Rest.Client/Models/PublishedApp.cs +++ b/InterlockLedger.Rest.Client/Models/PublishedApp.cs @@ -32,7 +32,7 @@ namespace InterlockLedger.Rest.Client; -public class PublishedApp : IComparable, IEquatable +public partial class PublishedApp : IComparable, IEquatable { public ulong? AlternativeId { get; set; } [JsonConverter(typeof(VersionJsonConverter))] @@ -76,5 +76,8 @@ public int CompareTo(PublishedApp other) { public override string ToString() => $" #{Id} {CompositeName} {Environment.NewLine} {Description}"; - private static string Safe(string name) => Regex.Replace(name, @"[\s\\/:""<>|\*\?]+", "_"); + private static string Safe(string name) => UnsafeCharsRegex().Replace(name, "_"); + + [GeneratedRegex(@"[\s\\/:""<>|\*\?]+")] + private static partial Regex UnsafeCharsRegex(); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/RawDocumentModel.cs b/InterlockLedger.Rest.Client/Models/RawDocumentModel.cs index e1d525c..d52a6b1 100644 --- a/InterlockLedger.Rest.Client/Models/RawDocumentModel.cs +++ b/InterlockLedger.Rest.Client/Models/RawDocumentModel.cs @@ -32,17 +32,11 @@ namespace InterlockLedger.Rest.Client; -public class RawDocumentModel +public class RawDocumentModel(string contentType, byte[] content, string name) { - public RawDocumentModel(string contentType, byte[] content, string name) { - ContentType = contentType.Required(); - Content = content.Required(); - Name = name.Required(); - } - - public byte[] Content { get; } - public string ContentType { get; } - public string Name { get; } + public byte[] Content { get; } = content.Required(); + public string ContentType { get; } = contentType.Required(); + public string Name { get; } = name.Required(); public override string ToString() => $"Document '{Name}' [{ContentType}]{Environment.NewLine}{_partialContentAsBase64}"; diff --git a/InterlockLedger.Rest.Client/Models/RecordModel.cs b/InterlockLedger.Rest.Client/Models/RecordModel.cs index 4d95d33..a366465 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModel.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModel.cs @@ -38,4 +38,5 @@ public class RecordModel : RecordModelBase /// The payload's bytes /// 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 bcf9b7c..7ab6196 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModelAsJson.cs @@ -41,4 +41,5 @@ public class RecordModelAsJson : RecordModelBase /// The payload's bytes /// 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 7f05def..0d486df 100644 --- a/InterlockLedger.Rest.Client/Models/RecordModelBase.cs +++ b/InterlockLedger.Rest.Client/Models/RecordModelBase.cs @@ -53,21 +53,27 @@ public abstract class RecordModelBase public DateTimeOffset CreatedAt { get; set; } /// - /// Hash of the full encoded bytes of the record + /// IL2 Network /// - public string Hash { get; set; } + public string Network { get; set; } /// /// The payload's TagId /// public ulong PayloadTagId { get; set; } + /// + /// Record universal reference [Network]:[ChainId]@[Serial] + /// + public string Reference { get; set; } + /// /// Record serial number. /// For the first record this value is zero (0) /// public ulong Serial { get; set; } + /// /// Block type /// Most records are of the type 'Data' @@ -79,5 +85,4 @@ public abstract class RecordModelBase /// public ushort Version { get; set; } - public override string ToString() => JsonSerializer.Serialize(this, Globals.JsonSettings); } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/Types/LimitedRange.cs b/InterlockLedger.Rest.Client/Models/Types/LimitedRange.cs deleted file mode 100644 index 0d40ba0..0000000 --- a/InterlockLedger.Rest.Client/Models/Types/LimitedRange.cs +++ /dev/null @@ -1,142 +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 System.ComponentModel; -using System.ComponentModel.Design.Serialization; -using System.Globalization; - -namespace InterlockLedger.Rest.Client; - -[TypeConverter(typeof(LimitedRangeConverter))] -[JsonConverter(typeof(LimitedRangeJsonConverter))] -public struct LimitedRange : IEquatable -{ - public readonly ulong End; - public readonly ulong Start; - - public LimitedRange(ulong start) : this(start, 1) { - } - - public LimitedRange(ulong start, ushort count) { - if (count == 0) - throw new ArgumentOutOfRangeException(nameof(count)); - Start = start; - checked { - End = start + count - 1; - } - } - - public static bool operator !=(LimitedRange left, LimitedRange right) => !(left == right); - - public static bool operator ==(LimitedRange left, LimitedRange right) => left.Equals(right); - - public static LimitedRange Resolve(string text) { - var parts = text.Trim('[', ']').Split('-'); - var start = ulong.Parse(parts[0].Trim()); - if (parts.Length == 1) - return new LimitedRange(start); - var end = ulong.Parse(parts[1].Trim()); - var range = new LimitedRange(start, end); - return range; - } - - public bool Contains(ulong value) => Start <= value && value <= End; - - public bool Contains(LimitedRange other) => Contains(other.Start) && Contains(other.End); - - public override bool Equals(object obj) => obj is LimitedRange range && Equals(range); - - public bool Equals(LimitedRange other) => End == other.End && Start == other.Start; - - public override int GetHashCode() => HashCode.Combine(End, Start); - - public bool OverlapsWith(LimitedRange other) => Contains(other.Start) || Contains(other.End) || other.Contains(Start); - - public override string ToString() => $"[{Start}{((End != Start) ? ("-" + End) : "")}]"; - - internal ushort Count => (ushort)(End - Start + 1); - - private LimitedRange(ulong start, ulong end) { - if (end < start) - throw new ArgumentOutOfRangeException(nameof(end)); - Start = start; - End = end; - } -} - -public static class LimitedRangeExtensions -{ - public static bool AnyOverlapsWith(this IEnumerable first, IEnumerable second) => first.Any(f => second.Any(s => s.OverlapsWith(f))); - - public static bool Includes(this IEnumerable ranges, ulong value) => ranges.Any(r => r.Contains(value)); - - public static bool IsSupersetOf(this IEnumerable first, IEnumerable second) => second.All(r => first.Any(fr => fr.Contains(r))); -} - -internal class LimitedRangeConverter : TypeConverter -{ - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string); - - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - => destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); - - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - if (value is string text) { - text = text.Trim(); - return LimitedRange.Resolve(text); - } - return base.ConvertFrom(context, culture, value); - } - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - => destinationType == typeof(string) - ? TryConvertToString(value) - : throw new InvalidOperationException("Can only convert to string!!!"); - - private static string TryConvertToString(object value) => value switch { - null => throw new ArgumentNullException(nameof(value)), - LimitedRange lr => lr.ToString(), - _ => throw new InvalidOperationException("Can only convert Range to string!!!") - }; -} - -internal class LimitedRangeJsonConverter : JsonConverter -{ - public override LimitedRange Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => reader.TokenType switch { - JsonTokenType.String => LimitedRange.Resolve(reader.GetString()), - _ => throw new InvalidDataException("Invalid format for a LimitedRange") - }; - - public override void Write(Utf8JsonWriter writer, LimitedRange value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); -} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/Models/Versions.cs b/InterlockLedger.Rest.Client/Models/Versions.cs index b654fb9..7387ed5 100644 --- a/InterlockLedger.Rest.Client/Models/Versions.cs +++ b/InterlockLedger.Rest.Client/Models/Versions.cs @@ -43,14 +43,23 @@ public class Versions public string CoreLibs { get; set; } /// - /// Message envelope wire format version + /// InterlockLedger-Tags library version /// - public string MessageEnvelopeWireFormat { get; set; } + public string Tags { get; set; } /// - /// Interlockledger node daemon version + /// Interlockledger node daemon version [Deprecated] /// public string Node { get; set; } + /// + /// Interlockledger node daemon version + /// + public string Main { get; set; } + + /// + /// Resolved node version, either Main or Node + /// + public string NodeVersion => Main.WithDefault(Node).WithDefault("?"); /// /// Peer2Peer connectivity library version diff --git a/InterlockLedger.Rest.Client/V6_0/RestNode.cs b/InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs similarity index 64% rename from InterlockLedger.Rest.Client/V6_0/RestNode.cs rename to InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs index 0512030..d966c4e 100644 --- a/InterlockLedger.Rest.Client/V6_0/RestNode.cs +++ b/InterlockLedger.Rest.Client/V6_0/DocumentRegistryImplementation.cs @@ -1,4 +1,4 @@ -// ****************************************************************************************************************************** +// ****************************************************************************************************************************** // // Copyright (c) 2018-2022 InterlockLedger Network // All rights reserved. @@ -29,29 +29,22 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // ****************************************************************************************************************************** +#nullable enable namespace InterlockLedger.Rest.Client.V6_0; -public class RestNode : RestAbstractNode, IDocumentRegistry +internal class DocumentRegistryImplementation(RestAbstractNode node) : IDocumentRegistry where ChainType : IRestChain { - public RestNode(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) - : base(x509Certificate, networkId, address) { } - - public RestNode(X509Certificate2 x509Certificate, ushort port, string address) - : base(x509Certificate, port, address) { } - - public RestNode(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) - : base(certFile, certPassword, networkId, address) { } + private readonly RestAbstractNode _node = node.Required(); - public RestNode(string certFile, string certPassword, ushort port, string address) - : base(certFile, certPassword, port, address) { } + Uri IDocumentRegistry.BaseUri => _node.BaseUri; Task IDocumentRegistry.GetDocumentsUploadConfigurationAsync() - => GetAsync("/documents/configuration"); + => _node.GetAsync("/documents/configuration"); public async Task> ListKnownChainsAcceptingTransactionsAsync() { var result = new List(); - foreach (IRestChain chain in (await GetChainsAsync().ConfigureAwait(false)).Safe()) { + foreach (IRestChain chain in (await _node.GetChainsAsync().ConfigureAwait(false)).Safe()) { var summary = await chain.GetSummaryAsync().ConfigureAwait(false); if (summary is not null && !summary.IsClosedForNewTransactions @@ -64,33 +57,31 @@ public async Task> ListKnownChainsAcceptingTransactionsAsync } public async Task> ListKnownChainsAsync() - => (await GetChainsAsync().ConfigureAwait(false)).Safe().Select(ch => ch.Id); + => (await _node.GetChainsAsync().ConfigureAwait(false)).Safe().Select(ch => ch.Id); Task IDocumentRegistry.RetrieveMetadataAsync(string locator) - => GetAsync(FromLocator(locator.Required(nameof(locator)), "metadata")); + => _node.GetAsync(FromLocator(locator.Required(), "metadata")); Task<(string Name, string ContentType, Stream Content)> IDocumentRegistry.RetrieveSingleAsync(string locator, int index) - => GetFileReadStreamAsync(FromLocator(locator.Required(nameof(locator)), index)); + => _node.GetFileReadStreamAsync(FromLocator(locator.Required(), index)); Task<(string Name, string ContentType, Stream Content)> IDocumentRegistry.RetrieveZipAsync(string locator) - => GetFileReadStreamAsync(FromLocator(locator.Required(nameof(locator)), "zip"), accept: "application/zip"); + => _node.GetFileReadStreamAsync(FromLocator(locator.Required(), "zip"), accept: "application/zip"); Task IDocumentRegistry.TransactionAddItemAsync(string transactionId, string path, string name, string comment, string contentType, Stream source) - => PostStreamAsync($"/documents/transaction/{transactionId.Required(nameof(transactionId))}?name={HttpUtility.UrlEncode(name.Required(nameof(name)))}&comment={HttpUtility.UrlEncode(comment)}", source.Required(nameof(source)), contentType.Required(nameof(contentType))); + => _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) { - await ValidateChainAsync(transactionStart.Required(nameof(transactionStart)).Chain); - return await PostAsync("/documents/transaction", transactionStart); + await ValidateChainAsync(transactionStart.Required().Chain); + return await _node.PostAsync("/documents/transaction", transactionStart); } Task IDocumentRegistry.TransactionCommitAsync(string transactionId) - => PostAsync($"/documents/transaction/{transactionId.Required(nameof(transactionId))}/commit", null); + => _node.PostAsync($"/documents/transaction/{transactionId.Required()}/commit", null); Task IDocumentRegistry.TransactionStatusAsync(string transactionId) - => GetAsync($"/documents/transaction/{transactionId.Required(nameof(transactionId))}"); - - protected override RestChain BuildChain(ChainIdModel c) => new(this, c.Required(nameof(c))); - + => _node.GetAsync($"/documents/transaction/{transactionId.Required()}"); + private static string FromLocator(string locator, T selector) => $"/documents/{HttpUtility.UrlEncode(locator)}/{selector}"; private async Task ValidateChainAsync(string chain) { diff --git a/InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs b/InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs new file mode 100644 index 0000000..51d28aa --- /dev/null +++ b/InterlockLedger.Rest.Client/V6_0/Interfaces/INodeWithDocumentRegistry.cs @@ -0,0 +1,38 @@ +// ****************************************************************************************************************************** +// +// 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 INodeWithDocumentRegistry +{ + IDocumentRegistry DocumentRegistry { get; } +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs b/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs index 660c4ec..b121eeb 100644 --- a/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs +++ b/InterlockLedger.Rest.Client/V6_0/JsonStoreImplementation.cs @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.V6_0; internal sealed class JsonStoreImplementation : IJsonStore { public JsonStoreImplementation(RestAbstractChain parent) { - _parent = parent.Required(nameof(parent)); + _parent = parent.Required(); _rest = _parent._rest; _id = _parent.Id; } diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs b/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs index 2822531..7f95d4f 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs +++ b/InterlockLedger.Rest.Client/V6_0/Models/DocumentsBeginTransactionModel.cs @@ -30,6 +30,8 @@ // // ****************************************************************************************************************************** +#nullable enable + namespace InterlockLedger.Rest.Client.V6_0; /// @@ -40,12 +42,12 @@ public sealed class DocumentsBeginTransactionModel /// /// Id of the chain where the set of documents should 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: @@ -56,12 +58,12 @@ public sealed class DocumentsBeginTransactionModel ///
ZSTDCompression of the data using the ZStandard from Facebook (In the future)
/// ///
- public string Compression { get; set; } + public string? Compression { get; set; } /// - /// The encryption descriptor in the &lt;pbe&gt;-&lt;hash&gt;-&lt;cipher&gt;-&lt;level&gt; format + /// The encryption descriptor in the [pbe]-[hash]-[cipher]-[level] format. Ex: "PBKDF2-SHA256-AES256-MID" /// - public string Encryption { get; set; } + public string? Encryption { get; set; } /// /// If the publically viewable PublicDirectory field should be created @@ -76,10 +78,26 @@ public sealed class DocumentsBeginTransactionModel /// /// Password as bytes if Encryption is not null /// - public byte[] Password { get; set; } + public byte[]? Password { get; set; } /// /// Locator for the previous version of this set /// - public string Previous { get; set; } + public string? Previous { get; set; } + + /// + /// Indexes of the documents from the previous version of this document set NOT to be copied into this new set (supported since API Version 13.0.0). + /// If absent/empty all previous documents will be copied + /// + public int[]? PreviousDocumentsNotToCopy { get; set; } + + /// + /// For 'Multi-Document Storage Application Chains' embeds '.to-children' control file if true. + /// + public bool? AllowChildren { get; set; } + + /// + /// If AllowChildren is true the textual comment to be contained in the '.to-children' control file + /// + public string? ToChildrenComment { get; set; } } \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs b/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs index d16bc70..73ca7ca 100644 --- a/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs +++ b/InterlockLedger.Rest.Client/V6_0/Models/DocumentsMetadataModel.cs @@ -30,6 +30,8 @@ // // ****************************************************************************************************************************** +#nullable enable + namespace InterlockLedger.Rest.Client.V6_0; /// @@ -37,10 +39,20 @@ namespace InterlockLedger.Rest.Client.V6_0; /// public class DocumentsMetadataModel { + /// + /// Replicates RecordReference record info to simplify REST API usage + /// + public string? RecordReference { get; } + + /// + /// Replicates CreationTime record info to simplify REST API usage + /// + public DateTimeOffset? CreationTime { get; set; } + /// /// Any additional information about this set of documents /// - public string Comment { get; set; } + public string? Comment { get; set; } /// /// Compression algorithm can be: @@ -51,22 +63,22 @@ public class DocumentsMetadataModel ///
ZSTDCompression of the data using the ZStandard from Facebook (In the future)
/// ///
- public string Compression { get; set; } + public string? Compression { 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; } /// /// Parameters used to do the encryption /// - public Parameters EncryptionParameters { get; set; } + public Parameters? EncryptionParameters { get; set; } /// /// List of stored documents /// - public IEnumerable PublicDirectory { get; set; } + public IEnumerable? PublicDirectory { get; set; } public override string ToString() => $@"{nameof(DocumentsMetadataModel)} {nameof(Comment)} : {Comment} @@ -84,22 +96,32 @@ public class DirectoryEntry /// /// Any provided additional information about the document file /// - public string Comment { get; set; } + public string? Comment { get; set; } /// /// Mime Type for the document content /// - public string MimeType { get; set; } + public required string MimeType { get; set; } /// /// Document (file) name /// - public string Name { get; set; } + public required string Name { get; set; } /// /// Document (file) path (without the name) /// - public string Path { get; set; } + public string? Path { get; set; } + + /// + /// Hash SHA256 of Document (file) content + /// + public byte[]? HashSHA256 { get; } + + /// + /// Size in bytes of Document (file) + /// + public ulong? Size { get; } public override string ToString() => $@"{nameof(DirectoryEntry)} {{{_joiner} {nameof(Comment)} : {Comment}{_joiner} {nameof(MimeType)} : {MimeType}{_joiner} {nameof(Name)} : {Name}{_joiner} {nameof(Path)} : {Path}{_joiner}}} "; @@ -118,9 +140,9 @@ public class Parameters /// /// Salt value to feed the cypher engine /// - public byte[] Salt { get; set; } + public byte[]? Salt { get; set; } - public override string ToString() => $@"{nameof(Parameters)} {{{_joiner} {nameof(Iterations)} : {Iterations}{_joiner} {nameof(Salt)} : '{Convert.ToBase64String(Salt)}'{_joiner}}} + public override string ToString() => $@"{nameof(Parameters)} {{{_joiner} {nameof(Iterations)} : {Iterations}{_joiner} {nameof(Salt)} : '{Convert.ToBase64String(Salt ?? [])}'{_joiner}}} "; } diff --git a/InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs b/InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs new file mode 100644 index 0000000..e8df1f1 --- /dev/null +++ b/InterlockLedger.Rest.Client/V6_0/RestAbstractNodeWithDocumentRegistry.cs @@ -0,0 +1,53 @@ +// ****************************************************************************************************************************** +// +// 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 abstract class RestAbstractNodeWithDocumentRegistry : RestAbstractNode, INodeWithDocumentRegistry where T : IRestChain +{ + public RestAbstractNodeWithDocumentRegistry(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) + : base(x509Certificate, networkId, address) { } + + public RestAbstractNodeWithDocumentRegistry(X509Certificate2 x509Certificate, ushort port, string address) + : base(x509Certificate, port, address) { } + + public RestAbstractNodeWithDocumentRegistry(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) + : base(certFile, certPassword, networkId, address) { } + + public RestAbstractNodeWithDocumentRegistry(string certFile, string certPassword, ushort port, string address) + : base(certFile, certPassword, port, address) { } + public IDocumentRegistry DocumentRegistry => _documentRegistry ??= new DocumentRegistryImplementation(this); + + private IDocumentRegistry? _documentRegistry; + +} diff --git a/InterlockLedger.Rest.Client/V6_0/RestChain.cs b/InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs similarity index 90% rename from InterlockLedger.Rest.Client/V6_0/RestChain.cs rename to InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs index 3f721ef..28f66f0 100644 --- a/InterlockLedger.Rest.Client/V6_0/RestChain.cs +++ b/InterlockLedger.Rest.Client/V6_0/RestChainV6_0.cs @@ -32,11 +32,11 @@ namespace InterlockLedger.Rest.Client.V6_0 { - public class RestChain : RestAbstractChain, IRestChainV6_0 + public class RestChainV6_0 : RestAbstractChain, IRestChainV6_0 { IJsonStore IRestChainV6_0.JsonStore => _jsonStore; - internal RestChain(RestNode rest, ChainIdModel chainId) : base(rest, chainId) => _jsonStore = new JsonStoreImplementation(this); + internal RestChainV6_0(RestNodeV6_0 rest, ChainIdModel chainId) : base(rest, chainId) => _jsonStore = new JsonStoreImplementation(this); private readonly IJsonStore _jsonStore; } diff --git a/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs b/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs new file mode 100644 index 0000000..2307526 --- /dev/null +++ b/InterlockLedger.Rest.Client/V6_0/RestNodeV6_0.cs @@ -0,0 +1,50 @@ +// ****************************************************************************************************************************** +// +// 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/V6_0/X509Certificate2Extensions.cs b/InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs index 38b3352..e66f2f0 100644 --- a/InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs +++ b/InterlockLedger.Rest.Client/V6_0/X509Certificate2Extensions.cs @@ -49,7 +49,7 @@ public static string ToPubKeyHash(this X509Certificate2 certificate) { return HashSha256(pubKeyRSAParametersTag).ToSafeBase64() + "#SHA256"; static byte[] PseudoTag(ulong tagId, params byte[][] parts) - => tagId.ILIntEncode().Concat(((ulong)parts.Sum(b => b.Length)).ILIntEncode()).Concat(parts.SelectMany(b => b)).ToArray(); + => [.. tagId.ILIntEncode(), .. ((ulong)parts.Sum(b => b.Length)).ILIntEncode(), .. parts.SelectMany(b => b)]; static byte[] HashSha256(byte[] data) { using var hasher = SHA256.Create(); diff --git a/InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.cs b/InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.cs new file mode 100644 index 0000000..2013cf7 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/Interfaces/IOpaqueStore.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. +// +// ****************************************************************************************************************************** + + +using InterlockLedger.Rest.Client.V6_0; + +namespace InterlockLedger.Rest.Client.V7_6; + +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); +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs b/InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs new file mode 100644 index 0000000..e7f0167 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/Interfaces/IRestChainV7_6.cs @@ -0,0 +1,38 @@ +// ****************************************************************************************************************************** +// +// 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.V7_6; + +public interface IRestChainV7_6 : V6_0.IRestChainV6_0 +{ + IOpaqueStore OpaqueStore { get; } +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs b/InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs new file mode 100644 index 0000000..88a1495 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/Models/OpaqueRecordModel.cs @@ -0,0 +1,72 @@ +// ****************************************************************************************************************************** +// +// 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.V7_6; + +/// +/// Generic opaque record +/// +public class OpaqueRecordModel +{ + + /// + /// Network the chain that contains this record belongs to + /// + public required string Network { get; set; } + + /// + /// chain id that owns this record + /// + public required string ChainId { get; set; } + + /// + /// Block serial number. + /// For the first record this value is zero (0) + /// + public ulong Serial { get; set; } + + /// + /// Application id this record is associated with + /// + public ulong ApplicationId { get; set; } + + /// + /// The payload's TagId + /// + public ulong PayloadTagId { get; set; } + + /// + /// Time of record creation + /// + public DateTimeOffset CreatedAt { get; set; } +} diff --git a/InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs b/InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs new file mode 100644 index 0000000..904cbdc --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/Models/PageOfOpaqueRecordsModel.cs @@ -0,0 +1,39 @@ +// ****************************************************************************************************************************** +// +// 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.V7_6; + +public class PageOfOpaqueRecordsModel : PageOf +{ + public ulong LastChangedRecordSerial { get; set; } +} \ No newline at end of file diff --git a/InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs b/InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs new file mode 100644 index 0000000..21ee057 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/OpaqueStoreImplementation.cs @@ -0,0 +1,73 @@ +// ****************************************************************************************************************************** +// +// 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 + +using InterlockLedger.Rest.Client.V6_0; + +namespace InterlockLedger.Rest.Client.V7_6; + +internal class OpaqueStoreImplementation : IOpaqueStore +{ + public OpaqueStoreImplementation(RestAbstractNode node, RestChainV7_6 chain) { + _chain = chain.Required(); + _node = node; + _id = _chain.Id; + } + + 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) { + using var body = new MemoryStream(bytes); + return AddRecordAsync(appId, payloadTypeId, lastChangedRecordSerial, body); + } + public Task<(ulong AppId, ulong PayloadTypeId, 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) { + string url = $"/opaque/{_id}/asJson/query?appId={appId}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}"; + if (howMany != null) { + url += $"&howMany={howMany}"; + } + if (payloadTypeIds != null) { + foreach (var payloadTypeId in payloadTypeIds) { + url += $"&payloadTypeIds={payloadTypeId}"; + } + } + return _node.GetAsync(url); + } + + + private readonly string _id; + private readonly RestChainV7_6 _chain; + private readonly RestAbstractNode _node; +} diff --git a/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs b/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs new file mode 100644 index 0000000..452d688 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/RestChainV7_6.cs @@ -0,0 +1,51 @@ +// ****************************************************************************************************************************** +// +// 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/InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs b/InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs new file mode 100644 index 0000000..dd24566 --- /dev/null +++ b/InterlockLedger.Rest.Client/V7_6/RestNodeV7_6.cs @@ -0,0 +1,52 @@ +// ****************************************************************************************************************************** +// +// 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 RestNodeV7_6 : RestAbstractNodeWithDocumentRegistry +{ + public RestNodeV7_6(X509Certificate2 x509Certificate, NetworkPredefinedPorts networkId, string address) + : base(x509Certificate, networkId, address) { } + + public RestNodeV7_6(X509Certificate2 x509Certificate, ushort port, string address) + : base(x509Certificate, port, address) { } + + public RestNodeV7_6(string certFile, string certPassword, NetworkPredefinedPorts networkId, string address) + : base(certFile, certPassword, networkId, address) { } + + 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()); + +} \ No newline at end of file diff --git a/rest_client/AbstractUsing.cs b/rest_client/AbstractUsing.cs index bb58b84..706f8d1 100644 --- a/rest_client/AbstractUsing.cs +++ b/rest_client/AbstractUsing.cs @@ -36,13 +36,10 @@ using InterlockLedger.Rest.Client.V6_0; namespace rest_client; - -public abstract class AbstractUsing where T : IRestChain +public abstract class AbstractUsing(RestAbstractNode node) where T : IRestChain { public static byte[] Content { get; } = Encoding.UTF8.GetBytes("Nothing to see here"); - protected readonly RestAbstractNode _node; - - protected AbstractUsing(RestAbstractNode node) => _node = node.Required(nameof(node)); + protected readonly RestAbstractNode _node = node.Required(); protected abstract string Version { get; } @@ -62,16 +59,16 @@ protected KeyPermitModel BuildKey() KeyPurpose.Action); protected void DisplayOtherNodeInfo() { - if (_node is IDocumentRegistry nodeV4_2) - Console.WriteLine($" {nodeV4_2.GetDocumentsUploadConfigurationAsync().Result}"); + 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 async Task ExerciseAsync(bool write) { - Console.WriteLine($"Client connected to {_node.BaseUri} using certificate {_node.CertificateName} using API version {Version}"); + 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 { @@ -82,7 +79,7 @@ protected async Task ExerciseAsync(bool write) { ManagementKeyPassword = "password", ManagementKeyStrength = KeyStrength.ExtraStrong, KeysAlgorithm = Algorithms.RSA, - AdditionalApps = new List { 4, 8 } + AdditionalApps = [4, 8] }); Console.WriteLine(chain); } catch (InvalidOperationException) { @@ -96,13 +93,15 @@ protected async Task ExerciseAsync(bool write) { DisplayOtherNodeInfo(); var apps = await _node.Network.GetAppsAsync(); Console.WriteLine($"-- Valid apps for network {apps.Network}:"); - foreach (var app in apps.ValidApps.OrderBy(a => a)) + foreach (var app in apps.ValidApps.OrderByDescending(a => (a.Id, a.AppVersion)).DistinctBy(a => a.Id).OrderBy(a => a.Id)) Console.WriteLine(app); Console.WriteLine(); var peers = await _node.GetPeersAsync(); Console.WriteLine($"-- Known peers:"); - foreach (var peer in peers.OrderBy(a => a.Name)) + foreach (var peer in peers.OrderBy(a => a.Name)) { Console.WriteLine(peer); + Console.WriteLine("---------------------------------"); + } Console.WriteLine(); Console.WriteLine("-- Chains:"); foreach (var chain in await _node.GetChainsAsync()) @@ -115,7 +114,7 @@ protected async Task ExerciseAsync(bool write) { if (write) { Console.WriteLine("-- Create Mirror:"); try { - foreach (var chain in await _node.AddMirrorsOfAsync(new string[] { "72_1DyspOtgOpg5XG2ihe7M0xCb2DhrZIQWv3-Bivy4" })) + foreach (var chain in await _node.AddMirrorsOfAsync(_newMirrors)) Console.WriteLine(chain); } catch (Exception e) { Console.WriteLine(e); @@ -133,6 +132,7 @@ protected async Task ExerciseChainAsync(RestAbstractNode node, T chain, bool Console.WriteLine($" Summary.Description: {summary.Description}"); Console.WriteLine($" Summary.IsClosedForNewTransactions: {summary.IsClosedForNewTransactions}"); Console.WriteLine($" Summary.LastRecord: {summary.LastRecord}"); + Console.WriteLine($" Summary.LastUpdate: {summary.LastUpdate}"); Console.WriteLine($" Summary.SizeInBytes: {summary.SizeInBytes}"); Console.WriteLine($" Summary.LicensingStatus: {summary.LicensingStatus}"); Console.WriteLine($" Summary.Licensed: {summary.Licensed}"); @@ -177,7 +177,7 @@ 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, new byte[] { 10, 5, 0, 0, 20, 5, 4, 0, 1, 2, 3 }); + 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); @@ -221,7 +221,7 @@ 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, new byte[] { 5, 0, 0, 20, 5, 4, 0, 1, 2, 3 }); + 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); @@ -295,5 +295,42 @@ protected async Task TryToStoreNiceDocumentsAsync(T chain) { } } + 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); + } + } + private static readonly byte[] _password = Encoding.UTF8.GetBytes("LongEnoughPassword"); + private static readonly string[] _newMirrors = ["72_1DyspOtgOpg5XG2ihe7M0xCb2DhrZIQWv3-Bivy4"]; } \ No newline at end of file diff --git a/rest_client/KnownVersions.cs b/rest_client/KnownVersions.cs new file mode 100644 index 0000000..90fe4de --- /dev/null +++ b/rest_client/KnownVersions.cs @@ -0,0 +1,39 @@ +// ****************************************************************************************************************************** +// +// 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 rest_client; + +public enum KnownVersions +{ + V6_0, + V7_6 +} diff --git a/rest_client/Program.cs b/rest_client/Program.cs index 77fb219..04c26fa 100644 --- a/rest_client/Program.cs +++ b/rest_client/Program.cs @@ -30,17 +30,44 @@ // // ****************************************************************************************************************************** -namespace rest_client; +using Cocona; +using InterlockLedger.Rest.Client.V6_0; +using InterlockLedger.Rest.Client.V7_6; +using rest_client; -public static class Program -{ - public static void Main(string[] args) { - if (args.Length < 4) { - Console.WriteLine("You must provide at least 4 parameters!"); - Console.WriteLine(); - Console.WriteLine("Usage: rest_client path-to-certificate-pfx-file certificate-password api-port rest-url [writeable]"); - } else { - UsingV6_0.DoIt(args); - } - } -} \ No newline at end of file + +CoconaLiteApp.Run(([Argument(Description = "Path to file containing client certificate in .pfx format")] string pathToCertificatePfxFile, + [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) => { + 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; + } + return 0; + } catch (Exception e) { + Console.WriteLine(e); + return 1; + } + }, configureOptions: options => { + options.EnableConvertArgumentNameToLowerCase = false; + options.EnableConvertOptionNameToLowerCase = false; + options.TreatPublicMethodsAsCommands = false; + }); diff --git a/rest_client/Properties/launchSettings.json b/rest_client/Properties/launchSettings.json index e7b427d..1ad5126 100644 --- a/rest_client/Properties/launchSettings.json +++ b/rest_client/Properties/launchSettings.json @@ -1,8 +1,12 @@ { - "profiles": { - "rest_client": { - "commandName": "Project", - "commandLineArgs": "D:\\clients\\minerva-examples\\certificates\\examples.api.pfx restClientPassword 32024 node.il2" - } + "profiles": { + "rest_client": { + "commandName": "Project", + "commandLineArgs": "D:\\vault\\clients\\minerva-debug\\certificates\\rest.api.pfx DebugKey 32024 node.il2" + }, + "rest_client Help": { + "commandName": "Project", + "commandLineArgs": "--help" } + } } \ No newline at end of file diff --git a/rest_client/UsingV6_0.cs b/rest_client/UsingV6_0.cs index 5e8dd3f..7d8e6ce 100644 --- a/rest_client/UsingV6_0.cs +++ b/rest_client/UsingV6_0.cs @@ -30,59 +30,14 @@ // // ****************************************************************************************************************************** -using InterlockLedger.Rest.Client; using InterlockLedger.Rest.Client.Abstractions; using InterlockLedger.Rest.Client.V6_0; namespace rest_client; -public class UsingV6_0 : AbstractUsing +public class UsingV6_0(RestAbstractNode node) : AbstractUsing(node) { - public static void DoIt(string[] args) { - try { - var client = - args.Length > 3 - ? new RestNode(args[0], args[1], ushort.Parse(args[2]), args[3]) - : throw new InvalidOperationException("You must provide at least 4 parameters"); - new UsingV6_0(client).ExerciseAsync(args.Length > 4).Wait(); - } catch (Exception e) { - Console.WriteLine(e); - } - } - - protected UsingV6_0(RestAbstractNode node) : base(node) { - } - protected override string Version => "6.0"; - protected override async Task DoExtraExercisesAsync(RestAbstractNode node, bool write) { - try { - foreach (IRestChainV6_0 chain in await node.GetChainsAsync()) { - if (write) { - // Add something - } - var records = await chain.Records.RecordsFromAsync(0, pageSize: 0); - var filteredRecords = records.Items.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)}"); - } - } - } - } catch (Exception e) { - Console.WriteLine(e); - } - } -} \ No newline at end of file + protected override Task DoExtraExercisesAsync(RestAbstractNode node, bool write) => ExerciseJsonDocumentAsync(node, write); +} diff --git a/rest_client/UsingV7_6.cs b/rest_client/UsingV7_6.cs new file mode 100644 index 0000000..6395835 --- /dev/null +++ b/rest_client/UsingV7_6.cs @@ -0,0 +1,74 @@ +// ****************************************************************************************************************************** +// +// 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 diff --git a/rest_client/rest_client.csproj b/rest_client/rest_client.csproj index 9ca7fa4..e173da1 100644 --- a/rest_client/rest_client.csproj +++ b/rest_client/rest_client.csproj @@ -1,14 +1,14 @@ - + Exe - net6.0 + net8.0 preview Rafael Teixeira InterlockLedger Network InterlockLedger - Copyright (c) 2017-2022 InterlockLedger Network - 6.0.4 + Copyright (c) 2017-2024 InterlockLedger Network + 13.6.0 Enable @@ -23,11 +23,12 @@ - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - +