diff --git a/BeeNet.sln b/BeeNet.sln index 09c39e51..ac75d7bb 100644 --- a/BeeNet.sln +++ b/BeeNet.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32811.315 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet", "src\BeeNet\BeeNet.csproj", "{653BEB35-743E-4612-BEF4-7B26C6FB3D8F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7932D52A-3D44-4723-9E77-1711F7486417}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6EAC22F4-C9F3-4C2E-A0AC-922D4F92B8F6}" @@ -13,9 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md - COPYING = COPYING - README.md = README.md - COPYING-LESSER = COPYING-LESSER EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{C162BCCF-B5C8-466A-AAB7-6D85CC171434}" @@ -39,9 +34,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{990C5C57-6E00-4E76-A23A-519F19290ACB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.IntegrationTest", "test\BeeNet.IntegrationTest\BeeNet.IntegrationTest.csproj", "{F0C776C9-4856-4031-B4CE-18E53FEF9E55}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.Client.IntegrationTest", "test\BeeNet.Client.IntegrationTest\BeeNet.Client.IntegrationTest.csproj", "{F0C776C9-4856-4031-B4CE-18E53FEF9E55}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Core", "src\BeeNet.Core\BeeNet.Core.csproj", "{0C681EE6-7290-4548-9502-4C9B6B061F12}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Util", "src\BeeNet.Util\BeeNet.Util.csproj", "{FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Client", "src\BeeNet.Client\BeeNet.Client.csproj", "{EC667802-776E-4306-922C-AA23C7D5835B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Core.UnitTest", "test\BeeNet.Core.UnitTest\BeeNet.Core.UnitTest.csproj", "{63368E76-475A-45BE-B6DB-86402C041C39}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Util.UnitTest", "test\BeeNet.Util.UnitTest\BeeNet.Util.UnitTest.csproj", "{BBC17EDC-660C-4006-A14C-377693290D5E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.Tests", "test\BeeNet.Tests\BeeNet.Tests.csproj", "{549BB2F5-D48E-4785-AF14-EF116A79AD00}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Util.Wasm", "src\BeeNet.Util.Wasm\BeeNet.Util.Wasm.csproj", "{7257332C-5291-4814-B00B-3732BE90FB2E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -49,29 +54,49 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {653BEB35-743E-4612-BEF4-7B26C6FB3D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {653BEB35-743E-4612-BEF4-7B26C6FB3D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {653BEB35-743E-4612-BEF4-7B26C6FB3D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {653BEB35-743E-4612-BEF4-7B26C6FB3D8F}.Release|Any CPU.Build.0 = Release|Any CPU {F0C776C9-4856-4031-B4CE-18E53FEF9E55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F0C776C9-4856-4031-B4CE-18E53FEF9E55}.Debug|Any CPU.Build.0 = Debug|Any CPU {F0C776C9-4856-4031-B4CE-18E53FEF9E55}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0C776C9-4856-4031-B4CE-18E53FEF9E55}.Release|Any CPU.Build.0 = Release|Any CPU - {549BB2F5-D48E-4785-AF14-EF116A79AD00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {549BB2F5-D48E-4785-AF14-EF116A79AD00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {549BB2F5-D48E-4785-AF14-EF116A79AD00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {549BB2F5-D48E-4785-AF14-EF116A79AD00}.Release|Any CPU.Build.0 = Release|Any CPU + {0C681EE6-7290-4548-9502-4C9B6B061F12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C681EE6-7290-4548-9502-4C9B6B061F12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C681EE6-7290-4548-9502-4C9B6B061F12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C681EE6-7290-4548-9502-4C9B6B061F12}.Release|Any CPU.Build.0 = Release|Any CPU + {FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2}.Release|Any CPU.Build.0 = Release|Any CPU + {EC667802-776E-4306-922C-AA23C7D5835B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC667802-776E-4306-922C-AA23C7D5835B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC667802-776E-4306-922C-AA23C7D5835B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC667802-776E-4306-922C-AA23C7D5835B}.Release|Any CPU.Build.0 = Release|Any CPU + {63368E76-475A-45BE-B6DB-86402C041C39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63368E76-475A-45BE-B6DB-86402C041C39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63368E76-475A-45BE-B6DB-86402C041C39}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63368E76-475A-45BE-B6DB-86402C041C39}.Release|Any CPU.Build.0 = Release|Any CPU + {BBC17EDC-660C-4006-A14C-377693290D5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBC17EDC-660C-4006-A14C-377693290D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBC17EDC-660C-4006-A14C-377693290D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBC17EDC-660C-4006-A14C-377693290D5E}.Release|Any CPU.Build.0 = Release|Any CPU + {7257332C-5291-4814-B00B-3732BE90FB2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7257332C-5291-4814-B00B-3732BE90FB2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7257332C-5291-4814-B00B-3732BE90FB2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7257332C-5291-4814-B00B-3732BE90FB2E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {653BEB35-743E-4612-BEF4-7B26C6FB3D8F} = {7932D52A-3D44-4723-9E77-1711F7486417} {C162BCCF-B5C8-466A-AAB7-6D85CC171434} = {6EAC22F4-C9F3-4C2E-A0AC-922D4F92B8F6} {1956F8A3-25CE-42BB-8AE5-77ACC56B9B92} = {C162BCCF-B5C8-466A-AAB7-6D85CC171434} {F6289FE6-5CC0-4225-98BC-2DCFE85C767F} = {6EAC22F4-C9F3-4C2E-A0AC-922D4F92B8F6} {F0C776C9-4856-4031-B4CE-18E53FEF9E55} = {990C5C57-6E00-4E76-A23A-519F19290ACB} - {549BB2F5-D48E-4785-AF14-EF116A79AD00} = {990C5C57-6E00-4E76-A23A-519F19290ACB} + {0C681EE6-7290-4548-9502-4C9B6B061F12} = {7932D52A-3D44-4723-9E77-1711F7486417} + {FDB74F14-61A0-42D9-89C1-8F8DA8A8A4C2} = {7932D52A-3D44-4723-9E77-1711F7486417} + {EC667802-776E-4306-922C-AA23C7D5835B} = {7932D52A-3D44-4723-9E77-1711F7486417} + {63368E76-475A-45BE-B6DB-86402C041C39} = {990C5C57-6E00-4E76-A23A-519F19290ACB} + {BBC17EDC-660C-4006-A14C-377693290D5E} = {990C5C57-6E00-4E76-A23A-519F19290ACB} + {7257332C-5291-4814-B00B-3732BE90FB2E} = {7932D52A-3D44-4723-9E77-1711F7486417} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BC8368EB-8E3B-4FBB-B657-D1C9375AFAFC} diff --git a/BeeNet.sln.DotSettings b/BeeNet.sln.DotSettings index 9bd7b53e..1e2d99e2 100644 --- a/BeeNet.sln.DotSettings +++ b/BeeNet.sln.DotSettings @@ -15,4 +15,5 @@ If not, see <https://www.gnu.org/licenses/>. True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/README.md b/README.md index 049d26f6..74408306 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,20 @@ With this client you can consume public and debug api of any Bee node exposing t This includes also some useful to calculate data chunking, and postage batch usage. +## Packages + +There are several NuGet packages available for Bee.Net. Here's a brief description of each: + +1. **Bee.Net.Client**: This package contains a client that is designed to connect to and work with Bee Swarm. It + simplifies the interaction with Bee nodes by providing high-level methods and classes. + +2. **Bee.Net.Core**: This package contains the base models for working with Swarm. These models represent the core + entities in the Swarm world and provide a foundation for interacting with the Bee Swarm. + +3. **Bee.Net.Util**: This package contains a series of useful tools. These tools include utilities for data chunking, + calculating postage batch usage, and other helpful operations that enhance your experience when working with + Swarm. + ## Package repositories You can get latest public releases from Nuget.org feed. Here you can see our [published packages](https://www.nuget.org/profiles/etherna). diff --git a/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs new file mode 100644 index 00000000..0cb7b550 --- /dev/null +++ b/src/BeeNet.Client/BeeClient.cs @@ -0,0 +1,1189 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Clients; +using Etherna.BeeNet.Exceptions; +using Etherna.BeeNet.Hashing.Store; +using Etherna.BeeNet.Manifest; +using Etherna.BeeNet.Models; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using FileResponse = Etherna.BeeNet.Models.FileResponse; +using Loggers = Etherna.BeeNet.Models.Loggers; + +#if NET7_0_OR_GREATER +using System.Formats.Tar; +#endif + +namespace Etherna.BeeNet +{ + [SuppressMessage("Design", "CA1054:URI-like parameters should not be strings")] + public class BeeClient : IBeeClient, IDisposable + { + // Consts. + public const int DefaultPort = 1633; + public readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(10); + + // Fields. + private readonly BeeGeneratedClient generatedClient; + private readonly HttpClient httpClient; + + private bool disposed; + + // Constructors. + public BeeClient( + string baseUrl = "http://localhost/", + int port = DefaultPort, + HttpClient? httpClient = null) + : this(BuildBaseUrl(baseUrl, port), httpClient) + { } + + public BeeClient( + Uri baseUrl, + HttpClient? httpClient = null) + { + this.httpClient = httpClient ?? new HttpClient { Timeout = DefaultTimeout }; + + BaseUrl = baseUrl; + generatedClient = new BeeGeneratedClient(this.httpClient) { BaseUrl = BaseUrl.ToString() }; + } + + // Dispose. + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) return; + + // Dispose managed resources. + if (disposing) + httpClient.Dispose(); + + disposed = true; + } + + + // Properties. + public Uri BaseUrl { get; } + + // Methods. + public async Task> AccountingAsync( + CancellationToken cancellationToken = default) => + (await generatedClient.AccountingAsync(cancellationToken).ConfigureAwait(false)).PeerData.ToDictionary( + i => i.Key, + i => new Account( + balance: BzzBalance.FromPlurString(i.Value.Balance), + thresholdReceived: BzzBalance.FromPlurString(i.Value.ThresholdReceived), + thresholdGiven: BzzBalance.FromPlurString(i.Value.ThresholdGiven), + surplusBalance: BzzBalance.FromPlurString(i.Value.SurplusBalance), + reservedBalance: BzzBalance.FromPlurString(i.Value.ReservedBalance), + shadowReservedBalance: BzzBalance.FromPlurString(i.Value.ShadowReservedBalance), + ghostBalance: BzzBalance.FromPlurString(i.Value.GhostBalance))); + + public async Task AuthenticateAsync(string role, int expiry) => + (await generatedClient.AuthAsync( + new Body + { + Role = role, + Expiry = expiry + }).ConfigureAwait(false)).Key; + + public async Task BuyPostageBatchAsync( + BzzBalance amount, + int depth, + string? label = null, + bool? immutable = null, + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + (await generatedClient.StampsPostAsync( + amount.ToPlurString(), + depth, + label, + immutable, + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false)).BatchID; + + public async Task CashoutChequeForPeerAsync( + string peerId, + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + (await generatedClient.ChequebookCashoutPostAsync( + peerId, + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false)).TransactionHash; + + public async Task CheckChunkExistsAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) + { + try + { + await generatedClient.ChunksHeadAsync(hash.ToString(), cancellationToken).ConfigureAwait(false); + return true; + } + catch (BeeNetApiException) + { + return false; + } + } + + public async Task CheckPinsAsync( + SwarmHash? hash, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.PinsCheckAsync( + hash?.ToString(), + cancellationToken).ConfigureAwait(false); + return new CheckPinsResult( + hash: response.Reference, + invalid: response.Invalid, + missing: response.Missing, + total: response.Total); + } + + public async Task ConnectToPeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) => + (await generatedClient.ConnectAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Address; + + public async Task CreateFeedAsync( + string owner, + string topic, + PostageBatchId batchId, + string? type = null, + bool? swarmPin = null, + CancellationToken cancellationToken = default) => + (await generatedClient.FeedsPostAsync(owner, topic, type, swarmPin, batchId.ToString(), cancellationToken).ConfigureAwait(false)).Reference; + + public Task CreatePinAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + generatedClient.PinsPostAsync((string)hash, cancellationToken); + + public async Task CreateTagAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.TagsPostAsync(cancellationToken).ConfigureAwait(false); + return new TagInfo( + uid: response.Uid, + startedAt: response.StartedAt, + split: response.Split, + seen: response.Seen, + stored: response.Stored, + sent: response.Sent, + synced: response.Synced); + } + + public Task DeletePeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) => + generatedClient.PeersDeleteAsync(peerAddress, cancellationToken); + + public Task DeletePinAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + generatedClient.PinsDeleteAsync((string)hash, cancellationToken); + + public Task DeleteTagAsync( + long uid, + CancellationToken cancellationToken = default) => + generatedClient.TagsDeleteAsync(uid, cancellationToken); + + public async Task DeleteTransactionAsync( + string txHash, + XDaiBalance? gasPrice = null, + CancellationToken cancellationToken = default) => + (await generatedClient.TransactionsDeleteAsync( + txHash, + gasPrice?.ToWeiLong(), + cancellationToken).ConfigureAwait(false)).TransactionHash; + + public async Task DepositIntoChequebookAsync( + BzzBalance amount, + XDaiBalance? gasPrice = null, + CancellationToken cancellationToken = default) => + (await generatedClient.ChequebookDepositAsync( + amount.ToPlurLong(), + gasPrice?.ToWeiLong(), + cancellationToken).ConfigureAwait(false)).TransactionHash; + + public async Task DilutePostageBatchAsync( + PostageBatchId batchId, + int depth, + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + (await generatedClient.StampsDiluteAsync( + batchId.ToString(), + depth, + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false)).BatchID; + + public async Task GetAddressesAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.AddressesAsync(cancellationToken).ConfigureAwait(false); + return new AddressDetail( + underlay: response.Underlay.Where(i => !string.IsNullOrWhiteSpace(i)), + overlay: response.Overlay, + ethereum: response.Ethereum, + publicKey: response.PublicKey, + pssPublicKey: response.PssPublicKey); + } + + public async Task> GetAllBalancesAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.BalancesGetAsync(cancellationToken).ConfigureAwait(false); + return response.Balances.ToDictionary( + b => b.Peer, + b => BzzBalance.FromPlurString(b.Balance)); + } + + public async Task> GetAllChequebookChequesAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.ChequebookChequeGetAsync(cancellationToken).ConfigureAwait(false); + return response.Lastcheques.Select(c => + new ChequebookCheque( + peer: c.Peer, + lastReceived: c.Lastreceived is not null ? new ChequePayment( + beneficiary: c.Lastreceived.Beneficiary, + chequebook: c.Lastreceived.Chequebook, + payout: BzzBalance.FromPlurString(c.Lastreceived.Payout)) : null, + lastSent: c.Lastsent is not null ? new ChequePayment( + beneficiary: c.Lastsent.Beneficiary, + chequebook: c.Lastsent.Chequebook, + payout: BzzBalance.FromPlurString(c.Lastsent.Payout)) : null)); + } + + public async Task> GetAllConsumedBalancesAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.ConsumedGetAsync(cancellationToken).ConfigureAwait(false); + return response.Balances.ToDictionary( + b => b.Peer, + b => BzzBalance.FromPlurString(b.Balance)); + } + + public async Task> GetAllPeerAddressesAsync(CancellationToken cancellationToken = default) => + (await generatedClient.PeersGetAsync(cancellationToken).ConfigureAwait(false)).Peers.Select(i => i.Address); + + public async Task> GetAllPinsAsync(CancellationToken cancellationToken = default) => + (await generatedClient.PinsGetAsync(cancellationToken).ConfigureAwait(false)).Reference + .Select(h => new SwarmHash(h)); + + public async Task GetAllSettlementsAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.SettlementsGetAsync(cancellationToken).ConfigureAwait(false); + return new Settlement( + totalReceived: BzzBalance.FromPlurString(response.TotalReceived), + totalSent: BzzBalance.FromPlurString(response.TotalSent), + settlements: response.Settlements + .Select(s => new SettlementData( + peer: s.Peer, + received: BzzBalance.FromPlurString(s.Received), + sent: BzzBalance.FromPlurString(s.Sent)))); + } + + public async Task GetAllTimeSettlementsAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.TimesettlementsAsync(cancellationToken).ConfigureAwait(false); + return new Settlement( + totalReceived: BzzBalance.FromPlurString(response.TotalReceived), + totalSent: BzzBalance.FromPlurString(response.TotalSent), + settlements: response.Settlements + .Select(s => new SettlementData( + peer: s.Peer, + received: BzzBalance.FromPlurString(s.Received), + sent: BzzBalance.FromPlurString(s.Sent)))); + } + + public async Task>> GetAllValidPostageBatchesFromAllNodesAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.BatchesAsync(cancellationToken).ConfigureAwait(false); + return response.Batches.GroupBy(b => b.Owner) + .ToDictionary( + g => g.Key, + g => g.Select(batch => new PostageBatch( + id: batch.BatchID, + amount: BzzBalance.FromPlurString(batch.Value), + blockNumber: batch.Start, + depth: batch.Depth, + exists: true, + isImmutable: batch.ImmutableFlag, + isUsable: true, + label: null, + storageRadius: batch.StorageRadius, + ttl: TimeSpan.FromSeconds(batch.BatchTTL), + utilization: null))); + } + + public async Task GetBalanceWithPeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) => + BzzBalance.FromPlurString( + (await generatedClient.BalancesGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Balance); + + public async Task GetBytesAsync( + SwarmHash hash, + bool? swarmCache = null, + RedundancyStrategy? swarmRedundancyStrategy = null, + bool? swarmRedundancyFallbackMode = null, + string? swarmChunkRetrievalTimeout = null, + CancellationToken cancellationToken = default) => + (await generatedClient.BytesGetAsync( + (string)hash, + swarmCache, + (SwarmRedundancyStrategy?)swarmRedundancyStrategy, + swarmRedundancyFallbackMode, + swarmChunkRetrievalTimeout, + cancellationToken).ConfigureAwait(false)).Stream; + + public Task GetBytesHeadAsync(SwarmHash hash, CancellationToken cancellationToken = default) => + generatedClient.BytesHeadAsync(hash.ToString(), cancellationToken); + + public async Task> GetBlocklistedPeerAddressesAsync(CancellationToken cancellationToken = default) => + (await generatedClient.BlocklistAsync(cancellationToken).ConfigureAwait(false)).Select(i => i.Address.Address1); + + public async Task GetChainStateAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.ChainstateAsync(cancellationToken).ConfigureAwait(false); + return new ChainState( + block: response.Block, + chainTip: response.ChainTip, + currentPrice: BzzBalance.FromPlurString(response.CurrentPrice), + totalAmount: BzzBalance.FromPlurString(response.TotalAmount)); + } + + public async Task GetChequebookAddressAsync(CancellationToken cancellationToken = default) => + (await generatedClient.ChequebookAddressAsync(cancellationToken).ConfigureAwait(false)).ChequebookAddress; + + public async Task GetChequebookBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.ChequebookBalanceAsync(cancellationToken).ConfigureAwait(false); + return new ChequebookBalance( + totalBalance: BzzBalance.FromPlurString(response.TotalBalance), + availableBalance: BzzBalance.FromPlurString(response.AvailableBalance)); + } + + public async Task GetChequebookCashoutForPeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.ChequebookCashoutGetAsync( + peerAddress, + cancellationToken).ConfigureAwait(false); + return new ChequebookCashout( + peer: response.Peer, + lastCashedCheque: response.LastCashedCheque is not null + ? new ChequePayment( + response.LastCashedCheque.Beneficiary, + response.LastCashedCheque.Chequebook, + BzzBalance.FromPlurString(response.LastCashedCheque.Payout)) + : null, + transactionHash: response.TransactionHash, + result: new ResultChequebook( + recipient: response.Result.Recipient, + lastPayout: BzzBalance.FromPlurString(response.Result.LastPayout), + bounced: response.Result.Bounced), + uncashedAmount: BzzBalance.FromPlurString(response.UncashedAmount)); + } + + public async Task GetChequebookChequeForPeerAsync( + string peerId, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.ChequebookChequeGetAsync(peerId, cancellationToken).ConfigureAwait(false); + return new ChequebookCheque( + peer: response.Peer, + lastReceived: response.Lastreceived is not null ? new ChequePayment( + beneficiary: response.Lastreceived.Beneficiary, + chequebook: response.Lastreceived.Chequebook, + payout: BzzBalance.FromPlurString(response.Lastreceived.Payout)) : null, + lastSent: response.Lastsent is not null ? new ChequePayment( + beneficiary: response.Lastsent.Beneficiary, + chequebook: response.Lastsent.Chequebook, + payout: BzzBalance.FromPlurString(response.Lastsent.Payout)) : null); + } + + public async Task GetChunkAsync( + SwarmHash hash, + bool? swarmCache = null, + CancellationToken cancellationToken = default) + { + var chunkDto = await generatedClient.ChunksGetAsync((string)hash, swarmCache, cancellationToken).ConfigureAwait(false); + using var memoryStream = new MemoryStream(); + await chunkDto.Stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); + var data = memoryStream.ToArray(); + return new SwarmChunk(hash, data); + } + + public async Task GetChunkStreamAsync( + SwarmHash hash, + bool? swarmCache = null, + CancellationToken cancellationToken = default) => + (await generatedClient.ChunksGetAsync(hash.ToString(), swarmCache, cancellationToken).ConfigureAwait(false)).Stream; + + public async Task GetConsumedBalanceWithPeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) => + BzzBalance.FromPlurString( + (await generatedClient.ConsumedGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Balance); + + public async Task GetFeedAsync( + string owner, + string topic, + int? at = null, + int? after = null, + string? type = null, + CancellationToken cancellationToken = default) => + (await generatedClient.FeedsGetAsync(owner, topic, at, after, type, cancellationToken).ConfigureAwait(false)).Reference; + + public async Task GetFileAsync( + SwarmAddress address, + bool? swarmCache = null, + RedundancyStrategy? swarmRedundancyStrategy = null, + bool? swarmRedundancyFallbackMode = null, + string? swarmChunkRetrievalTimeout = null, + CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(address, nameof(address)); + + if (address.Path is null) + { + var response = await generatedClient.BzzGetAsync( + address.Hash.ToString(), + swarmCache, + (SwarmRedundancyStrategy2?)swarmRedundancyStrategy, + swarmRedundancyFallbackMode, + swarmChunkRetrievalTimeout, + cancellationToken).ConfigureAwait(false); + return new FileResponse( + response.Headers, + response.Stream); + } + else + { + var response = await generatedClient.BzzGetAsync( + address.Hash.ToString(), + address.Path.ToString(), + (SwarmRedundancyStrategy3?)swarmRedundancyStrategy, + swarmRedundancyFallbackMode, + swarmChunkRetrievalTimeout, + cancellationToken).ConfigureAwait(false); + return new FileResponse( + response.Headers, + response.Stream); + } + } + + public Task GetFileHeadAsync(SwarmHash hash, CancellationToken cancellationToken = default) => + generatedClient.BzzHeadAsync((string)hash, cancellationToken); + + public async Task GetHealthAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.HealthAsync(cancellationToken).ConfigureAwait(false); + return new( + isStatusOk: response.Status switch + { + Response21Status.Ok => true, + Response21Status.Nok => false, + _ => throw new InvalidOperationException() + }, + version: response.Version, + apiVersion: response.ApiVersion, + debugApiVersion: response.DebugApiVersion); + } + + public async Task GetNodeInfoAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.NodeAsync(cancellationToken).ConfigureAwait(false); + return new( + beeMode: response.BeeMode switch + { + Response31BeeMode.Dev => InfoBeeMode.Dev, + Response31BeeMode.Full => InfoBeeMode.Full, + Response31BeeMode.Light => InfoBeeMode.Light, + _ => throw new InvalidOperationException() + }, + chequebookEnabled: response.ChequebookEnabled, + swapEnabled: response.SwapEnabled); + } + + public async Task> GetOwnedPostageBatchesByNodeAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.StampsGetAsync(cancellationToken).ConfigureAwait(false); + return response.Stamps.Select(b => + new PostageBatch( + amount: BzzBalance.FromPlurString(b.Amount), + depth: b.Depth, + blockNumber: b.BlockNumber, + exists: b.Exists, + id: b.BatchID, + isImmutable: b.ImmutableFlag, + label: b.Label, + ttl: TimeSpan.FromSeconds(b.BatchTTL), + isUsable: b.Usable, + utilization: b.Utilization, + storageRadius: null)); + } + + public async Task> GetPendingTransactionsAsync( + CancellationToken cancellationToken = default) + { + var response = await generatedClient.TransactionsGetAsync(cancellationToken).ConfigureAwait(false); + return response.PendingTransactions.Select(tx => new TxInfo( + transactionHash: tx.TransactionHash, + to: tx.To, + nonce: tx.Nonce, + gasPrice: XDaiBalance.FromWeiString(tx.GasPrice), + gasLimit: tx.GasLimit, + data: tx.Data, + created: tx.Created, + description: tx.Description, + value: XDaiBalance.FromWeiString(tx.Value))); + } + + public async Task GetPinStatusAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + (await generatedClient.PinsGetAsync((string)hash, cancellationToken).ConfigureAwait(false)).Reference; + + public async Task GetPostageBatchAsync( + PostageBatchId batchId, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.StampsGetAsync( + batchId.ToString(), + cancellationToken).ConfigureAwait(false); + return new PostageBatch( + amount: BzzBalance.FromPlurString(response.Amount), + depth: response.Depth, + blockNumber: response.BlockNumber, + exists: response.Exists, + id: response.BatchID, + isImmutable: response.ImmutableFlag, + label: response.Label, + ttl: TimeSpan.FromSeconds(response.BatchTTL), + isUsable: response.Usable, + utilization: response.Utilization, + storageRadius: null); + } + + public async Task GetReserveCommitmentAsync(int depth, string anchor1, string anchor2, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.RchashAsync(depth, anchor1, anchor2, cancellationToken).ConfigureAwait(false); + return new( + duration: response.Duration, + hash: response.Hash, + proof1: new ReserveCommitmentProof( + chunkSpan: response.Proofs.Proof1.ChunkSpan, + postageProof: new Models.PostageProof( + index: response.Proofs.Proof1.PostageProof.Index, + postageId: response.Proofs.Proof1.PostageProof.PostageId, + signature: response.Proofs.Proof1.PostageProof.Signature, + timeStamp: DateTimeOffset.FromUnixTimeSeconds( + long.Parse(response.Proofs.Proof1.PostageProof.TimeStamp, CultureInfo.InvariantCulture))), + proofSegments: response.Proofs.Proof1.ProofSegments ?? Array.Empty(), + proofSegments2: response.Proofs.Proof1.ProofSegments2 ?? Array.Empty(), + proofSegments3: response.Proofs.Proof1.ProofSegments3 ?? Array.Empty(), + proveSegment: response.Proofs.Proof1.ProveSegment, + proveSegment2: response.Proofs.Proof1.ProveSegment2, + socProof: (response.Proofs.Proof1.SocProof ?? Array.Empty()).Select( + p => new Models.SocProof( + chunkHash: p.ChunkAddr, + identifier: p.Identifier, + signature: p.Signature, + signer: p.Signer))), + proof2: new ReserveCommitmentProof( + chunkSpan: response.Proofs.Proof2.ChunkSpan, + postageProof: new Models.PostageProof( + index: response.Proofs.Proof2.PostageProof.Index, + postageId: response.Proofs.Proof2.PostageProof.PostageId, + signature: response.Proofs.Proof2.PostageProof.Signature, + timeStamp: DateTimeOffset.FromUnixTimeSeconds( + long.Parse(response.Proofs.Proof2.PostageProof.TimeStamp, CultureInfo.InvariantCulture))), + proofSegments: response.Proofs.Proof2.ProofSegments ?? Array.Empty(), + proofSegments2: response.Proofs.Proof2.ProofSegments2 ?? Array.Empty(), + proofSegments3: response.Proofs.Proof2.ProofSegments3 ?? Array.Empty(), + proveSegment: response.Proofs.Proof2.ProveSegment, + proveSegment2: response.Proofs.Proof2.ProveSegment2, + socProof: (response.Proofs.Proof2.SocProof ?? Array.Empty()).Select( + p => new Models.SocProof( + chunkHash: p.ChunkAddr, + identifier: p.Identifier, + signature: p.Signature, + signer: p.Signer))), + proofLast: new ReserveCommitmentProof( + chunkSpan: response.Proofs.ProofLast.ChunkSpan, + postageProof: new Models.PostageProof( + index: response.Proofs.ProofLast.PostageProof.Index, + postageId: response.Proofs.ProofLast.PostageProof.PostageId, + signature: response.Proofs.ProofLast.PostageProof.Signature, + timeStamp: DateTimeOffset.FromUnixTimeSeconds( + long.Parse(response.Proofs.ProofLast.PostageProof.TimeStamp, CultureInfo.InvariantCulture))), + proofSegments: response.Proofs.ProofLast.ProofSegments ?? Array.Empty(), + proofSegments2: response.Proofs.ProofLast.ProofSegments2 ?? Array.Empty(), + proofSegments3: response.Proofs.ProofLast.ProofSegments3 ?? Array.Empty(), + proveSegment: response.Proofs.ProofLast.ProveSegment, + proveSegment2: response.Proofs.ProofLast.ProveSegment2, + socProof: (response.Proofs.ProofLast.SocProof ?? Array.Empty()).Select( + p => new Models.SocProof( + chunkHash: p.ChunkAddr, + identifier: p.Identifier, + signature: p.Signature, + signer: p.Signer)))); + } + + public async Task GetReserveStateAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.ReservestateAsync(cancellationToken).ConfigureAwait(false); + return new( + commitment: response.Commitment, + radius: response.Radius, + storageRadius: response.StorageRadius); + } + + public async Task GetSettlementsWithPeerAsync( + string peerAddress, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.SettlementsGetAsync(peerAddress, cancellationToken).ConfigureAwait(false); + return new SettlementData( + peer: response.Peer, + received: BzzBalance.FromPlurString(response.Received), + sent: BzzBalance.FromPlurString(response.Sent)); + } + + public async Task GetStampsBucketsForBatchAsync( + PostageBatchId batchId, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.StampsBucketsAsync( + batchId.ToString(), + cancellationToken).ConfigureAwait(false); + return new( + depth: response.Depth, + bucketDepth: response.BucketDepth, + bucketUpperBound: response.BucketUpperBound, + collisions: response.Buckets.Select(b => b.Collisions)); + } + + public async Task GetSwarmTopologyAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.TopologyAsync(cancellationToken).ConfigureAwait(false); + return new( + baseAddr: response.BaseAddr, + bins: response.Bins.ToDictionary( + i => i.Key, + i => new PeersAggregate( + population: i.Value.Population, + connected: i.Value.Connected, + disconnectedPeers: i.Value.DisconnectedPeers.Select( + peer => new Peer( + address: peer.Address, + lastSeenTimestamp: peer.Metrics.LastSeenTimestamp, + sessionConnectionRetry: peer.Metrics.SessionConnectionRetry, + connectionTotalDuration: peer.Metrics.ConnectionTotalDuration, + sessionConnectionDuration: peer.Metrics.SessionConnectionDuration, + sessionConnectionDirection: peer.Metrics.SessionConnectionDirection, + latencyEwma: peer.Metrics.LatencyEWMA)), + connectedPeers: i.Value.ConnectedPeers.Select( + peer => new Peer( + address: peer.Address, + lastSeenTimestamp: peer.Metrics.LastSeenTimestamp, + sessionConnectionRetry: peer.Metrics.SessionConnectionRetry, + connectionTotalDuration: peer.Metrics.ConnectionTotalDuration, + sessionConnectionDuration: peer.Metrics.SessionConnectionDuration, + sessionConnectionDirection: peer.Metrics.SessionConnectionDirection, + latencyEwma: peer.Metrics.LatencyEWMA)))), + connected: response.Connected, + depth: response.Depth, + networkAvailability: response.NetworkAvailability switch + { + Response38NetworkAvailability.Unknown => NetworkAvailability.Unknown, + Response38NetworkAvailability.Available => NetworkAvailability.Available, + Response38NetworkAvailability.Unavailable => NetworkAvailability.Unavailable, + _ => throw new InvalidOperationException(), + }, + nnLowWatermark: response.NnLowWatermark, + population: response.Population, + reachability: response.Reachability switch + { + Response38Reachability.Unknown => Reachability.Unknown, + Response38Reachability.Public => Reachability.Public, + Response38Reachability.Private => Reachability.Private, + _ => throw new InvalidOperationException(), + }, + timestamp: DateTimeOffset.FromUnixTimeSeconds( + long.Parse(response.Timestamp, CultureInfo.InvariantCulture))); + } + + public async Task GetTagInfoAsync( + long uid, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.TagsGetAsync(uid, cancellationToken).ConfigureAwait(false); + return new TagInfo( + uid: response.Uid, + startedAt: response.StartedAt, + split: response.Split, + seen: response.Seen, + stored: response.Stored, + sent: response.Sent, + synced: response.Synced); + } + + public async Task> GetTagsListAsync( + int? offset = null, + int? limit = null, + CancellationToken cancellationToken = default) + { + var tags = + (await generatedClient.TagsGetAsync(offset, limit, cancellationToken).ConfigureAwait(false)).Tags ?? + Array.Empty(); + return tags.Select(t => new TagInfo( + uid: t.Uid, + startedAt: t.StartedAt, + split: t.Split, + seen: t.Seen, + stored: t.Stored, + sent: t.Sent, + synced: t.Synced)); + } + + public async Task GetTransactionInfoAsync( + string txHash, + CancellationToken cancellationToken = default) + { + var response = await generatedClient.TransactionsGetAsync(txHash, cancellationToken).ConfigureAwait(false); + return new TxInfo( + transactionHash: response.TransactionHash, + to: response.To, + nonce: response.Nonce, + gasPrice: XDaiBalance.FromWeiString(response.GasPrice), + gasLimit: response.GasLimit, + data: response.Data, + created: response.Created, + description: response.Description, + value: XDaiBalance.FromWeiString(response.Value)); + } + + public async Task GetWalletBalance(CancellationToken cancellationToken = default) + { + var response = await generatedClient.WalletAsync(cancellationToken).ConfigureAwait(false); + return new( + bzzBalance: BzzBalance.FromPlurString(response.BzzBalance), + xDaiBalance: XDaiBalance.FromWeiString(response.NativeTokenBalance)); + } + + public async Task GetWelcomeMessageAsync(CancellationToken cancellationToken = default) => + (await generatedClient.WelcomeMessageGetAsync(cancellationToken).ConfigureAwait(false)).WelcomeMessage; + + public async Task IsContentRetrievableAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + (await generatedClient.StewardshipGetAsync((string)hash, cancellationToken).ConfigureAwait(false)) + .IsRetrievable; + + public async Task LoggersGetAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.LoggersGetAsync(cancellationToken).ConfigureAwait(false); + return new( + loggers: response.Loggers.Select( + i => new Loggers( + id: i.Id, + logger: i.Logger, + subsystem: i.Subsystem, + verbosity: i.Verbosity)).ToList(), + tree: response.Tree.ToDictionary(i => i.Key, i => i.Value?.Plus.ToList() ?? new List())); + } + + public async Task LoggersGetAsync(string exp, CancellationToken cancellationToken = default) + { + var response = await generatedClient.LoggersGetAsync(exp, cancellationToken).ConfigureAwait(false); + return new( + loggers: response.Loggers.Select( + i => new Loggers( + id: i.Id, + logger: i.Logger, + subsystem: i.Subsystem, + verbosity: i.Verbosity)).ToList(), + tree: response.Tree.ToDictionary(i => i.Key, i => i.Value?.Plus.ToList() ?? new List())); + } + + public async Task LoggersPutAsync(string exp, CancellationToken cancellationToken = default) => + await generatedClient.LoggersPutAsync(exp, cancellationToken).ConfigureAwait(false); + + public async Task RebroadcastTransactionAsync( + string txHash, + CancellationToken cancellationToken = default) => + (await generatedClient.TransactionsPostAsync(txHash, cancellationToken).ConfigureAwait(false)).TransactionHash; + + public async Task RedistributionStateAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.RedistributionstateAsync(cancellationToken).ConfigureAwait(false); + return new( + isFrozen: response.IsFrozen, + isFullySynced: response.IsFullySynced, + isHealthy: response.IsHealthy, + round: response.Round, + lastWonRound: response.LastWonRound, + lastPlayedRound: response.LastPlayedRound, + lastFrozenRound: response.LastFrozenRound, + block: response.Block, + reward: BzzBalance.FromPlurString(response.Reward), + fees: XDaiBalance.FromWeiString(response.Fees)); + } + + public async Task RefreshAuthAsync( + string role, + int expiry, + CancellationToken cancellationToken = default) => + (await generatedClient.RefreshAsync( + new Body2 + { + Role = role, + Expiry = expiry + }, + cancellationToken).ConfigureAwait(false)).Key; + + public async Task ResolveAddressToChunkReferenceAsync(SwarmAddress address) + { + var chunkStore = new BeeClientChunkStore(this); + + var rootManifest = new ReferencedMantarayManifest( + chunkStore, + address.Hash); + + return await rootManifest.ResolveAddressToChunkReferenceAsync(address).ConfigureAwait(false); + } + + public Task ReuploadContentAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + generatedClient.StewardshipPutAsync((string)hash, cancellationToken: cancellationToken); + + public Task SendPssAsync( + string topic, + string targets, + PostageBatchId batchId, + string? recipient = null, + CancellationToken cancellationToken = default) => + generatedClient.PssSendAsync(topic, targets, batchId.ToString(), recipient, cancellationToken); + + public Task SetWelcomeMessageAsync( + string welcomeMessage, + CancellationToken cancellationToken = default) => + generatedClient.WelcomeMessagePostAsync( + new Body4 + { + WelcomeMessage = welcomeMessage + }, + cancellationToken); + + public async Task StakeDeleteAsync( + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + await generatedClient.StakeDeleteAsync( + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false); + + public async Task StakeGetAsync(CancellationToken cancellationToken = default) => + await generatedClient.StakeGetAsync(cancellationToken).ConfigureAwait(false); + + public async Task StakePostAsync( + BzzBalance amount, + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + await generatedClient.StakePostAsync( + amount.ToPlurString(), + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false); + + public async Task StatusNodeAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.StatusAsync(cancellationToken).ConfigureAwait(false); + return new( + beeMode: response.BeeMode switch + { + Response65BeeMode.Light => StatusBeeMode.Light, + Response65BeeMode.Full => StatusBeeMode.Full, + Response65BeeMode.UltraLight => StatusBeeMode.UltraLight, + Response65BeeMode.Unknown => StatusBeeMode.Unknown, + _ => throw new InvalidOperationException() + }, + batchCommitment: response.BatchCommitment, + connectedPeers: response.ConnectedPeers, + neighborhoodSize: response.NeighborhoodSize, + peer: response.Peer, + proximity: response.Proximity, + pullsyncRate: response.PullsyncRate, + reserveSize: response.ReserveSize, + reserveSizeWithinRadius: (int)response.ReserveSizeWithinRadius, + requestFailed: response.RequestFailed, + storageRadius: response.StorageRadius); + } + + public async Task> StatusPeersAsync(CancellationToken cancellationToken = default) + { + var response = await generatedClient.StatusPeersAsync(cancellationToken).ConfigureAwait(false); + return response.Stamps.Select( + s => new StatusNode( + beeMode: s.BeeMode switch + { + StampsBeeMode.Light => StatusBeeMode.Light, + StampsBeeMode.Full => StatusBeeMode.Full, + StampsBeeMode.UltraLight => StatusBeeMode.UltraLight, + StampsBeeMode.Unknown => StatusBeeMode.Unknown, + _ => throw new InvalidOperationException() + }, + batchCommitment: s.BatchCommitment, + connectedPeers: s.ConnectedPeers, + neighborhoodSize: s.NeighborhoodSize, + peer: s.Peer, + proximity: s.Proximity, + pullsyncRate: s.PullsyncRate, + reserveSize: s.ReserveSize, + reserveSizeWithinRadius: (int)s.ReserveSizeWithinRadius, + requestFailed: s.RequestFailed, + storageRadius: s.StorageRadius)); + } + + public Task SubscribeToPssAsync( + string topic, + CancellationToken cancellationToken = default) => + generatedClient.PssSubscribeAsync(topic, cancellationToken); + + public async Task TopUpPostageBatchAsync( + PostageBatchId batchId, + BzzBalance amount, + XDaiBalance? gasPrice = null, + long? gasLimit = null, + CancellationToken cancellationToken = default) => + (await generatedClient.StampsTopupAsync( + batchId.ToString(), + amount.ToPlurLong(), + gasPrice?.ToWeiLong(), + gasLimit, + cancellationToken).ConfigureAwait(false)).BatchID; + + public async Task TryConnectToPeerAsync( + string peerId, + CancellationToken cancellationToken = default) => + (await generatedClient.PingpongAsync(peerId, cancellationToken).ConfigureAwait(false)).Rtt; + + public Task UpdateTagAsync( + long uid, + SwarmHash? hash = null, + CancellationToken cancellationToken = default) => + generatedClient.TagsPatchAsync( + uid, + hash.HasValue ? + new Body3 { Address = hash.Value.ToString() } : + null, + cancellationToken); + + public async Task UploadChunkAsync(PostageBatchId batchId, + Stream chunkData, + bool swarmPin = false, + bool swarmDeferredUpload = true, + long? swarmTag = null, + CancellationToken cancellationToken = default) => + (await generatedClient.ChunksPostAsync( + swarmTag, + batchId.ToString(), + chunkData, + cancellationToken).ConfigureAwait(false)).Reference; + + public Task UploadChunksStreamAsync( + PostageBatchId batchId, + int? swarmTag = null, + bool? swarmPin = null, + CancellationToken cancellationToken = default) => + generatedClient.ChunksStreamAsync(swarmTag, batchId.ToString(), cancellationToken); + + public async Task UploadBytesAsync( + PostageBatchId batchId, + Stream body, + int? swarmTag = null, + bool? swarmPin = null, + bool? swarmEncrypt = null, + bool? swarmDeferredUpload = null, + RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, + CancellationToken cancellationToken = default) => + (await generatedClient.BytesPostAsync( + swarm_postage_batch_id: batchId.ToString(), + swarm_tag: swarmTag, + swarm_pin: swarmPin, + swarm_deferred_upload: swarmDeferredUpload, + swarm_encrypt: swarmEncrypt, + swarm_redundancy_level: (int)swarmRedundancyLevel, + body: body, + cancellationToken).ConfigureAwait(false)).Reference; + +#if NET7_0_OR_GREATER + public async Task UploadDirectoryAsync( + PostageBatchId batchId, + string directoryPath, + int? swarmTag = null, + bool? swarmPin = null, + bool? swarmEncrypt = null, + string? swarmIndexDocument = null, + string? swarmErrorDocument = null, + bool? swarmDeferredUpload = null, + RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, + CancellationToken cancellationToken = default) + { + // Create tar file. + using var memoryStream = new MemoryStream(); + await TarFile.CreateFromDirectoryAsync(directoryPath, memoryStream, false, cancellationToken).ConfigureAwait(false); + memoryStream.Position = 0; + + // Try set index document. + if (swarmIndexDocument is null && + File.Exists(Path.Combine(directoryPath, "index.html"))) + swarmIndexDocument = "index.html"; + + // Upload directory. + return (await generatedClient.BzzPostAsync( + new FileParameter(memoryStream, null, "application/x-tar"), + swarm_tag: swarmTag, + swarm_pin: swarmPin, + swarm_encrypt: swarmEncrypt, + swarm_collection: true, + swarm_index_document: swarmIndexDocument, + swarm_error_document: swarmErrorDocument, + swarm_postage_batch_id: batchId.ToString(), + swarm_deferred_upload: swarmDeferredUpload, + swarm_redundancy_level: (SwarmRedundancyLevel)swarmRedundancyLevel, + cancellationToken: cancellationToken).ConfigureAwait(false)).Reference; + } +#endif + + public async Task UploadFileAsync( + PostageBatchId batchId, + Stream content, + string? name = null, + string? contentType = null, + bool isFileCollection = false, + int? swarmTag = null, + bool? swarmPin = null, + bool? swarmEncrypt = null, + string? swarmIndexDocument = null, + string? swarmErrorDocument = null, + bool? swarmDeferredUpload = null, + RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, + CancellationToken cancellationToken = default) + { + return (await generatedClient.BzzPostAsync( + new FileParameter(content, name, contentType), + swarm_tag: swarmTag, + swarm_pin: swarmPin, + swarm_encrypt: swarmEncrypt, + swarm_collection: isFileCollection, + swarm_index_document: swarmIndexDocument, + swarm_error_document: swarmErrorDocument, + swarm_postage_batch_id: batchId.ToString(), + swarm_deferred_upload: swarmDeferredUpload, + swarm_redundancy_level: (SwarmRedundancyLevel)swarmRedundancyLevel, + cancellationToken: cancellationToken).ConfigureAwait(false)).Reference; + } + + public async Task UploadSocAsync( + string owner, + string id, + string sig, + PostageBatchId batchId, + Stream body, + bool? swarmPin = null, + CancellationToken cancellationToken = default) => + (await generatedClient.SocAsync( + owner, + id, + sig, + batchId.ToString(), + body, + swarmPin, + cancellationToken).ConfigureAwait(false)).Reference; + + public async Task WalletWithdrawAsync( + BzzBalance amount, + string address, + XDaiBalance coin, + CancellationToken cancellationToken = default) => + (await generatedClient.WalletWithdrawAsync( + amount.ToPlurString(), + address, + coin.ToWeiString(), + cancellationToken).ConfigureAwait(false)).TransactionHash; + + public async Task WithdrawFromChequebookAsync( + BzzBalance amount, + XDaiBalance? gasPrice = null, + CancellationToken cancellationToken = default) => + (await generatedClient.ChequebookWithdrawAsync( + amount.ToPlurLong(), + gasPrice?.ToWeiLong(), + cancellationToken).ConfigureAwait(false)).TransactionHash; + + // Helpers. + private static Uri BuildBaseUrl(string url, int port) + { + var normalizedUrl = url; + if (normalizedUrl.Last() != '/') + normalizedUrl += '/'; + + var baseUrl = ""; + + var urlRegex = new Regex(@"^((?\w+)://)?(?[^/:]+)", + RegexOptions.None, TimeSpan.FromMilliseconds(150)); + var urlMatch = urlRegex.Match(normalizedUrl); + + if (!urlMatch.Success) + throw new ArgumentException("Url is not valid", nameof(url)); + + if (!string.IsNullOrEmpty(urlMatch.Groups["proto"].Value)) + baseUrl += urlMatch.Groups["proto"].Value + "://"; + + baseUrl += $"{urlMatch.Groups["host"].Value}:{port}"; + + return new Uri(baseUrl); + } + } +} \ No newline at end of file diff --git a/src/BeeNet.Client/BeeNet.Client.csproj b/src/BeeNet.Client/BeeNet.Client.csproj new file mode 100644 index 00000000..e1692bf9 --- /dev/null +++ b/src/BeeNet.Client/BeeNet.Client.csproj @@ -0,0 +1,51 @@ + + + + net6.0;net7.0;net8.0 + true + Etherna.BeeNet + + Etherna SA + A .Net client to connect with Ethereum Swarm Bee + + 12 + enable + true + true + AllEnabledByDefault + + Bee.Net.Client + https://github.com/Etherna/bee-net + git + true + true + snupkg + COPYING + COPYING-LESSER + README.md + true + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/src/BeeNet/Clients/BeeGeneratedClient.cs b/src/BeeNet.Client/Clients/BeeGeneratedClient.cs similarity index 100% rename from src/BeeNet/Clients/BeeGeneratedClient.cs rename to src/BeeNet.Client/Clients/BeeGeneratedClient.cs diff --git a/src/BeeNet/Clients/Fixer/PostageBatchDto.cs b/src/BeeNet.Client/Clients/Fixer/PostageBatchDto.cs similarity index 100% rename from src/BeeNet/Clients/Fixer/PostageBatchDto.cs rename to src/BeeNet.Client/Clients/Fixer/PostageBatchDto.cs diff --git a/src/BeeNet/BeeNet.csproj b/src/BeeNet.Core/BeeNet.Core.csproj similarity index 77% rename from src/BeeNet/BeeNet.csproj rename to src/BeeNet.Core/BeeNet.Core.csproj index 4976d563..00be88a7 100644 --- a/src/BeeNet/BeeNet.csproj +++ b/src/BeeNet.Core/BeeNet.Core.csproj @@ -4,16 +4,17 @@ net6.0;net7.0;net8.0 true Etherna.BeeNet - - Etherna Sagl - A .Net client for Swarm Bee - + + Etherna SA + Core models to work with Ethereum Swarm in .Net + 12 enable + true true AllEnabledByDefault - Bee.Net + Bee.Net.Core https://github.com/Etherna/bee-net git true @@ -31,15 +32,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + diff --git a/src/BeeNet/Exceptions/BeeNetApiException.cs b/src/BeeNet.Core/Exceptions/BeeNetApiException.cs similarity index 92% rename from src/BeeNet/Exceptions/BeeNetApiException.cs rename to src/BeeNet.Core/Exceptions/BeeNetApiException.cs index f5b5aa6c..92e5a303 100644 --- a/src/BeeNet/Exceptions/BeeNetApiException.cs +++ b/src/BeeNet.Core/Exceptions/BeeNetApiException.cs @@ -20,10 +20,10 @@ namespace Etherna.BeeNet.Exceptions { [SuppressMessage("Design", "CA1032:Implement standard exception constructors")] - public partial class BeeNetApiException : Exception + public class BeeNetApiException : Exception { // Constructor. - internal BeeNetApiException( + public BeeNetApiException( string message, int statusCode, string? response, @@ -55,10 +55,10 @@ public override string ToString() } [SuppressMessage("Design", "CA1032:Implement standard exception constructors")] - public partial class BeeNetApiException : BeeNetApiException + public class BeeNetApiException : BeeNetApiException { // Constructor. - internal BeeNetApiException( + public BeeNetApiException( string message, int statusCode, string? response, diff --git a/src/BeeNet/Extensions/ArrayExtensions.cs b/src/BeeNet.Core/Extensions/ArrayExtensions.cs similarity index 100% rename from src/BeeNet/Extensions/ArrayExtensions.cs rename to src/BeeNet.Core/Extensions/ArrayExtensions.cs diff --git a/src/BeeNet/Extensions/LongExtensions.cs b/src/BeeNet.Core/Extensions/LongExtensions.cs similarity index 100% rename from src/BeeNet/Extensions/LongExtensions.cs rename to src/BeeNet.Core/Extensions/LongExtensions.cs diff --git a/src/BeeNet/JsonConverters/BzzBalanceJsonConverter.cs b/src/BeeNet.Core/JsonConverters/BzzBalanceJsonConverter.cs similarity index 100% rename from src/BeeNet/JsonConverters/BzzBalanceJsonConverter.cs rename to src/BeeNet.Core/JsonConverters/BzzBalanceJsonConverter.cs diff --git a/src/BeeNet/JsonConverters/PostageBatchIdJsonConverter.cs b/src/BeeNet.Core/JsonConverters/PostageBatchIdJsonConverter.cs similarity index 100% rename from src/BeeNet/JsonConverters/PostageBatchIdJsonConverter.cs rename to src/BeeNet.Core/JsonConverters/PostageBatchIdJsonConverter.cs diff --git a/src/BeeNet/JsonConverters/XDaiBalanceJsonConverter.cs b/src/BeeNet.Core/JsonConverters/XDaiBalanceJsonConverter.cs similarity index 100% rename from src/BeeNet/JsonConverters/XDaiBalanceJsonConverter.cs rename to src/BeeNet.Core/JsonConverters/XDaiBalanceJsonConverter.cs diff --git a/src/BeeNet/Models/ChainState.cs b/src/BeeNet.Core/Models/Account.cs similarity index 51% rename from src/BeeNet/Models/ChainState.cs rename to src/BeeNet.Core/Models/Account.cs index cee96af4..68490db0 100644 --- a/src/BeeNet/Models/ChainState.cs +++ b/src/BeeNet.Core/Models/Account.cs @@ -12,28 +12,24 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; -using System.Globalization; - namespace Etherna.BeeNet.Models { - public sealed class ChainState + public sealed class Account( + BzzBalance balance, + BzzBalance thresholdReceived, + BzzBalance thresholdGiven, + BzzBalance surplusBalance, + BzzBalance reservedBalance, + BzzBalance shadowReservedBalance, + BzzBalance ghostBalance) { - // Constructors. - internal ChainState(Clients.Response30 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Block = response.Block; - ChainTip = response.ChainTip; - CurrentPrice = BzzBalance.FromPlurString(response.CurrentPrice); - TotalAmount = BzzBalance.FromPlurString(response.TotalAmount); - } - // Properties. - public long Block { get; } - public int ChainTip { get; } - public BzzBalance CurrentPrice { get; } - public BzzBalance TotalAmount { get; } + public BzzBalance Balance { get; } = balance; + public BzzBalance ThresholdReceived { get; } = thresholdReceived; + public BzzBalance ThresholdGiven { get; } = thresholdGiven; + public BzzBalance SurplusBalance { get; } = surplusBalance; + public BzzBalance ReservedBalance { get; } = reservedBalance; + public BzzBalance ShadowReservedBalance { get; } = shadowReservedBalance; + public BzzBalance GhostBalance { get; } = ghostBalance; } } diff --git a/src/BeeNet.Core/Models/AddressDetail.cs b/src/BeeNet.Core/Models/AddressDetail.cs new file mode 100644 index 00000000..3c24964f --- /dev/null +++ b/src/BeeNet.Core/Models/AddressDetail.cs @@ -0,0 +1,33 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class AddressDetail( + string overlay, + IEnumerable underlay, + string ethereum, + string publicKey, + string pssPublicKey) + { + // Properties. + public string Overlay { get; } = overlay; + public IEnumerable Underlay { get; } = underlay; + public string Ethereum { get; } = ethereum; + public string PublicKey { get; } = publicKey; + public string PssPublicKey { get; } = pssPublicKey; + } +} diff --git a/src/BeeNet/Models/BzzBalance.cs b/src/BeeNet.Core/Models/BzzBalance.cs similarity index 100% rename from src/BeeNet/Models/BzzBalance.cs rename to src/BeeNet.Core/Models/BzzBalance.cs diff --git a/src/BeeNet.Core/Models/ChainState.cs b/src/BeeNet.Core/Models/ChainState.cs new file mode 100644 index 00000000..b8a45372 --- /dev/null +++ b/src/BeeNet.Core/Models/ChainState.cs @@ -0,0 +1,29 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class ChainState( + long block, + int chainTip, + BzzBalance currentPrice, + BzzBalance totalAmount) + { + // Properties. + public long Block { get; } = block; + public int ChainTip { get; } = chainTip; + public BzzBalance CurrentPrice { get; } = currentPrice; + public BzzBalance TotalAmount { get; } = totalAmount; + } +} diff --git a/src/BeeNet.Core/Models/CheckPinsResult.cs b/src/BeeNet.Core/Models/CheckPinsResult.cs new file mode 100644 index 00000000..4af7a112 --- /dev/null +++ b/src/BeeNet.Core/Models/CheckPinsResult.cs @@ -0,0 +1,28 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class CheckPinsResult( + SwarmHash hash, + int invalid, + int missing, + int total) + { + public SwarmHash Hash { get; } = hash; + public int Invalid { get; } = invalid; + public int Missing { get; } = missing; + public int Total { get; } = total; + } +} \ No newline at end of file diff --git a/src/BeeNet.Core/Models/ChequePayment.cs b/src/BeeNet.Core/Models/ChequePayment.cs new file mode 100644 index 00000000..6dd15f09 --- /dev/null +++ b/src/BeeNet.Core/Models/ChequePayment.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class ChequePayment( + string beneficiary, + string chequebook, + BzzBalance payout) + { + // Properties. + public string Beneficiary { get; } = beneficiary; + public string Chequebook { get; } = chequebook; + public BzzBalance Payout { get; } = payout; + } +} diff --git a/src/BeeNet/Models/Auth.cs b/src/BeeNet.Core/Models/ChequebookBalance.cs similarity index 73% rename from src/BeeNet/Models/Auth.cs rename to src/BeeNet.Core/Models/ChequebookBalance.cs index 7f0800e7..2ceb6e00 100644 --- a/src/BeeNet/Models/Auth.cs +++ b/src/BeeNet.Core/Models/ChequebookBalance.cs @@ -12,21 +12,14 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; - namespace Etherna.BeeNet.Models { - public sealed class Auth + public sealed class ChequebookBalance( + BzzBalance availableBalance, + BzzBalance totalBalance) { - // Constructors. - internal Auth(Clients.Response response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Key = response.Key; - } - // Properties. - public string Key { get; } + public BzzBalance AvailableBalance { get; } = availableBalance; + public BzzBalance TotalBalance { get; } = totalBalance; } } diff --git a/src/BeeNet/Models/Wallet.cs b/src/BeeNet.Core/Models/ChequebookCashout.cs similarity index 60% rename from src/BeeNet/Models/Wallet.cs rename to src/BeeNet.Core/Models/ChequebookCashout.cs index edf2e05c..41dca128 100644 --- a/src/BeeNet/Models/Wallet.cs +++ b/src/BeeNet.Core/Models/ChequebookCashout.cs @@ -12,23 +12,20 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; - namespace Etherna.BeeNet.Models { - public sealed class Wallet + public sealed class ChequebookCashout( + string peer, + ChequePayment? lastCashedCheque, + string transactionHash, + ResultChequebook result, + BzzBalance uncashedAmount) { - // Constructors. - internal Wallet(Clients.Response61 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Bzz = BzzBalance.FromPlurString(response.BzzBalance); - NativeTokenBalance = XDaiBalance.FromWeiString(response.NativeTokenBalance); - } - // Properties. - public BzzBalance Bzz { get; } - public XDaiBalance NativeTokenBalance { get; } + public string Peer { get; } = peer; + public ChequePayment? LastCashedCheque { get; } = lastCashedCheque; + public string TransactionHash { get; } = transactionHash; + public ResultChequebook Result { get; } = result; + public BzzBalance UncashedAmount { get; } = uncashedAmount; } } diff --git a/src/BeeNet.Core/Models/ChequebookCheque.cs b/src/BeeNet.Core/Models/ChequebookCheque.cs new file mode 100644 index 00000000..edcd472b --- /dev/null +++ b/src/BeeNet.Core/Models/ChequebookCheque.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class ChequebookCheque( + string peer, + ChequePayment? lastReceived, + ChequePayment? lastSent) + { + // Properties. + public string Peer { get; } = peer; + public ChequePayment? LastReceived { get; } = lastReceived; + public ChequePayment? LastSent { get; } = lastSent; + } +} diff --git a/src/BeeNet/Feeds/EpochFeedIndex.cs b/src/BeeNet.Core/Models/Feeds/EpochFeedIndex.cs similarity index 90% rename from src/BeeNet/Feeds/EpochFeedIndex.cs rename to src/BeeNet.Core/Models/Feeds/EpochFeedIndex.cs index 8ca01a62..6d4632c9 100644 --- a/src/BeeNet/Feeds/EpochFeedIndex.cs +++ b/src/BeeNet.Core/Models/Feeds/EpochFeedIndex.cs @@ -12,12 +12,11 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Epoche; using Etherna.BeeNet.Extensions; +using Nethereum.Util.HashProviders; using System; -using System.Collections.ObjectModel; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public sealed class EpochFeedIndex : FeedIndexBase { @@ -81,22 +80,6 @@ public EpochFeedIndex Parent /// public byte Level { get; } - /// - /// Index represenentation as keccak256 hash - /// - public override ReadOnlyCollection MarshalBinary - { - get - { - var epochBytes = Start.UnixDateTimeToByteArray(); - var newArray = new byte[epochBytes.Length + 1]; - epochBytes.CopyTo(newArray, 0); - newArray[epochBytes.Length] = Level; - - return new ReadOnlyCollection(Keccak256.ComputeHash(newArray)); - } - } - /// /// Epoch start in seconds /// @@ -138,6 +121,19 @@ public EpochFeedIndex GetChildAt(ulong at) public override int GetHashCode() => Level.GetHashCode() ^ Start.GetHashCode(); + /// + /// Index representation as keccak256 hash + /// + public override Memory GetMarshalBinaryHash(IHashProvider hashProvider) + { + var epochBytes = Start.UnixDateTimeToByteArray(); + var newArray = new byte[epochBytes.Length + 1]; + epochBytes.CopyTo(newArray, 0); + newArray[epochBytes.Length] = Level; + + return hashProvider.ComputeHash(newArray); + } + public override FeedIndexBase GetNext(ulong at) { #if NET8_0_OR_GREATER diff --git a/src/BeeNet/Feeds/FeedIndexBase.cs b/src/BeeNet.Core/Models/Feeds/FeedIndexBase.cs similarity index 86% rename from src/BeeNet/Feeds/FeedIndexBase.cs rename to src/BeeNet.Core/Models/Feeds/FeedIndexBase.cs index 95d9d18f..4e213714 100644 --- a/src/BeeNet/Feeds/FeedIndexBase.cs +++ b/src/BeeNet.Core/Models/Feeds/FeedIndexBase.cs @@ -12,15 +12,15 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . +using Nethereum.Util.HashProviders; using System; -using System.Collections.ObjectModel; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public abstract class FeedIndexBase { // Properties. - public abstract ReadOnlyCollection MarshalBinary { get; } + public abstract Memory GetMarshalBinaryHash(IHashProvider hashProvider); // Methods. public FeedIndexBase GetNext(DateTimeOffset at) => diff --git a/src/BeeNet/Models/FileResponse.cs b/src/BeeNet.Core/Models/FileResponse.cs similarity index 69% rename from src/BeeNet/Models/FileResponse.cs rename to src/BeeNet.Core/Models/FileResponse.cs index a8977985..0e13deba 100644 --- a/src/BeeNet/Models/FileResponse.cs +++ b/src/BeeNet.Core/Models/FileResponse.cs @@ -13,29 +13,24 @@ // If not, see . using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Etherna.BeeNet.Models { - public sealed class FileResponse : IDisposable, IAsyncDisposable + public sealed class FileResponse( + IReadOnlyDictionary> headers, + Stream stream) + : IDisposable, IAsyncDisposable { - // Constructors. - internal FileResponse(Clients.FileResponse response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Stream = response.Stream; - IsFeed = response.Headers.ContainsKey("Swarm-Feed-Index"); - } - // Dispose. public void Dispose() => Stream.Dispose(); public ValueTask DisposeAsync() => Stream.DisposeAsync(); // Properties. - public bool IsFeed { get; } - public Stream Stream { get; } - + public bool IsFeed => Headers.ContainsKey("Swarm-Feed-Index"); + public IReadOnlyDictionary> Headers { get; } = headers; + public Stream Stream { get; } = stream; } } diff --git a/src/BeeNet/Models/GnosisChain.cs b/src/BeeNet.Core/Models/GnosisChain.cs similarity index 100% rename from src/BeeNet/Models/GnosisChain.cs rename to src/BeeNet.Core/Models/GnosisChain.cs diff --git a/src/BeeNet/Models/Bucket.cs b/src/BeeNet.Core/Models/Health.cs similarity index 68% rename from src/BeeNet/Models/Bucket.cs rename to src/BeeNet.Core/Models/Health.cs index dbd817e9..d26e532b 100644 --- a/src/BeeNet/Models/Bucket.cs +++ b/src/BeeNet.Core/Models/Health.cs @@ -16,19 +16,16 @@ namespace Etherna.BeeNet.Models { - public sealed class Bucket + public sealed class Health( + bool isStatusOk, + string version, + string apiVersion, + string debugApiVersion) { - // Constructors. - internal Bucket(Clients.Buckets bucket) - { - ArgumentNullException.ThrowIfNull(bucket, nameof(bucket)); - - BucketId = bucket.BucketID; - Collisions = bucket.Collisions; - } - // Properties. - public int BucketId { get; } - public int Collisions { get; } + public bool IsStatusOk { get; } = isStatusOk; + public string Version { get; } = version; + public string ApiVersion { get; } = apiVersion; + public string DebugApiVersion { get; } = debugApiVersion; } } diff --git a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs new file mode 100644 index 00000000..054f6670 --- /dev/null +++ b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs @@ -0,0 +1,59 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public interface IReadOnlyPostageBuckets + { + // Properties. + /// + /// The higher level of collisions for a bucket + /// + uint MaxBucketCollisions { get; } + + /// + /// The lower level of collisions for a bucket + /// + uint MinBucketCollisions { get; } + + /// + /// Minimum required postage batch depth + /// + int RequiredPostageBatchDepth { get; } + + /// + /// Total added chunks in buckets + /// + long TotalChunks { get; } + + // Methods. + /// + /// Amount of buckets, grouped by collisions + /// + /// Array with collisions as index, and buckets amount as values + int[] CountBucketsByCollisions(); + + /// + /// Get a copy of all buckets + /// + /// All the buckets + uint[] GetBuckets(); + + IEnumerable GetBucketsByCollisions(uint collisions); + + uint GetCollisions(uint bucketId); + } +} \ No newline at end of file diff --git a/src/BeeNet.Core/Models/InfoBeeMode.cs b/src/BeeNet.Core/Models/InfoBeeMode.cs new file mode 100644 index 00000000..dae66e1a --- /dev/null +++ b/src/BeeNet.Core/Models/InfoBeeMode.cs @@ -0,0 +1,34 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public enum InfoBeeMode + { + /// + /// Light node; does not participate in forwarding or storing chunks + /// + Light, + + /// + /// Full node + /// + Full, + + /// + /// Development mode; Bee client for development purposes, blockchain operations are mocked + /// + Dev, + } +} diff --git a/src/BeeNet/Models/StewardshipGet.cs b/src/BeeNet.Core/Models/LogData.cs similarity index 70% rename from src/BeeNet/Models/StewardshipGet.cs rename to src/BeeNet.Core/Models/LogData.cs index 6536a297..6a657061 100644 --- a/src/BeeNet/Models/StewardshipGet.cs +++ b/src/BeeNet.Core/Models/LogData.cs @@ -12,21 +12,16 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; +using System.Collections.Generic; namespace Etherna.BeeNet.Models { - public sealed class StewardshipGet + public sealed class LogData( + ICollection loggers, + IDictionary> tree) { - // Constructors. - internal StewardshipGet(Clients.Response19 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - IsRetrievable = response.IsRetrievable; - } - // Properties. - public bool IsRetrievable { get; } + public ICollection Loggers { get; } = loggers; + public IDictionary> Tree { get; } = tree; } } diff --git a/src/BeeNet.Core/Models/Loggers.cs b/src/BeeNet.Core/Models/Loggers.cs new file mode 100644 index 00000000..deac777e --- /dev/null +++ b/src/BeeNet.Core/Models/Loggers.cs @@ -0,0 +1,29 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class Loggers( + string id, + string logger, + string subsystem, + string verbosity) + { + // Properties. + public string Id { get; } = id; + public string Logger { get; } = logger; + public string Subsystem { get; } = subsystem; + public string Verbosity { get; } = verbosity; + } +} diff --git a/src/BeeNet/Models/NetworkAvailability.cs b/src/BeeNet.Core/Models/NetworkAvailability.cs similarity index 100% rename from src/BeeNet/Models/NetworkAvailability.cs rename to src/BeeNet.Core/Models/NetworkAvailability.cs diff --git a/src/BeeNet.Core/Models/NodeInfo.cs b/src/BeeNet.Core/Models/NodeInfo.cs new file mode 100644 index 00000000..9d46c39e --- /dev/null +++ b/src/BeeNet.Core/Models/NodeInfo.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class NodeInfo( + InfoBeeMode beeMode, + bool chequebookEnabled, + bool swapEnabled) + { + // Properties. + public InfoBeeMode BeeMode { get; } = beeMode; + public bool ChequebookEnabled { get; } = chequebookEnabled; + public bool SwapEnabled { get; } = swapEnabled; + } +} diff --git a/src/BeeNet/Models/AddressDetail.cs b/src/BeeNet.Core/Models/Peer.cs similarity index 50% rename from src/BeeNet/Models/AddressDetail.cs rename to src/BeeNet.Core/Models/Peer.cs index b8e9963f..c8935300 100644 --- a/src/BeeNet/Models/AddressDetail.cs +++ b/src/BeeNet.Core/Models/Peer.cs @@ -12,31 +12,25 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; -using System.Collections.Generic; -using System.Linq; - namespace Etherna.BeeNet.Models { - public sealed class AddressDetail + public sealed class Peer( + string address, + int lastSeenTimestamp, + int sessionConnectionRetry, + double connectionTotalDuration, + double sessionConnectionDuration, + string sessionConnectionDirection, + int latencyEwma) { - // Constructors. - internal AddressDetail(Clients.Response20 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Underlay = response.Underlay.Where(i => !string.IsNullOrWhiteSpace(i)); - Overlay = response.Overlay; - Ethereum = response.Ethereum; - PublicKey = response.PublicKey; - PssPublicKey = response.PssPublicKey; - } - // Properties. - public string Overlay { get; } - public IEnumerable Underlay { get; } - public string Ethereum { get; } - public string PublicKey { get; } - public string PssPublicKey { get; } + public string Address { get; } = address; + public int LastSeenTimestamp { get; } = lastSeenTimestamp; + public int SessionConnectionRetry { get; } = sessionConnectionRetry; + public double ConnectionTotalDuration { get; } = connectionTotalDuration; + public double SessionConnectionDuration { get; } = sessionConnectionDuration; + public string SessionConnectionDirection { get; } = sessionConnectionDirection; + public int LatencyEwma { get; } = latencyEwma; } + } diff --git a/src/BeeNet.Core/Models/PeersAggregate.cs b/src/BeeNet.Core/Models/PeersAggregate.cs new file mode 100644 index 00000000..3b95dccd --- /dev/null +++ b/src/BeeNet.Core/Models/PeersAggregate.cs @@ -0,0 +1,31 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class PeersAggregate( + int population, + int connected, + IEnumerable disconnectedPeers, + IEnumerable connectedPeers) + { + // Properties. + public int Population { get; } = population; + public int Connected { get; } = connected; + public IEnumerable DisconnectedPeers { get; } = disconnectedPeers; + public IEnumerable ConnectedPeers { get; } = connectedPeers; + } +} diff --git a/src/BeeNet/Models/PostageBatch.cs b/src/BeeNet.Core/Models/PostageBatch.cs similarity index 70% rename from src/BeeNet/Models/PostageBatch.cs rename to src/BeeNet.Core/Models/PostageBatch.cs index 85da91c8..4de63942 100644 --- a/src/BeeNet/Models/PostageBatch.cs +++ b/src/BeeNet.Core/Models/PostageBatch.cs @@ -12,7 +12,6 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Clients; using System; namespace Etherna.BeeNet.Models @@ -34,8 +33,9 @@ public PostageBatch( bool isImmutable, bool isUsable, string? label, + int? storageRadius, TimeSpan ttl, - uint utilization) + uint? utilization) { if (depth is < MinDepth or > MaxDepth) throw new ArgumentOutOfRangeException(nameof(depth), "Batch depth out of range"); @@ -48,47 +48,11 @@ public PostageBatch( IsImmutable = isImmutable; IsUsable = isUsable; Label = label; + StorageRadius = storageRadius; Ttl = ttl; Utilization = utilization; } - // Internal constructors. - internal PostageBatch(Stamps batch) - { - ArgumentNullException.ThrowIfNull(batch, nameof(batch)); - if (batch.Depth is < MinDepth or > MaxDepth) - throw new ArgumentOutOfRangeException(nameof(batch), "Batch depth out of range"); - - Amount = BzzBalance.FromPlurString(batch.Amount); - Depth = batch.Depth; - BlockNumber = batch.BlockNumber; - Exists = batch.Exists; - Id = batch.BatchID; - IsImmutable = batch.ImmutableFlag; - Label = batch.Label; - Ttl = TimeSpan.FromSeconds(batch.BatchTTL); - IsUsable = batch.Usable; - Utilization = batch.Utilization; - } - - internal PostageBatch(Response52 batch) - { - ArgumentNullException.ThrowIfNull(batch, nameof(batch)); - if (batch.Depth is < MinDepth or > MaxDepth) - throw new ArgumentOutOfRangeException(nameof(batch), "Batch depth out of range"); - - Amount = BzzBalance.FromPlurString(batch.Amount); - Depth = batch.Depth; - BlockNumber = batch.BlockNumber; - Exists = batch.Exists; - Id = batch.BatchID; - IsImmutable = batch.ImmutableFlag; - Label = batch.Label; - Ttl = TimeSpan.FromSeconds(batch.BatchTTL); - IsUsable = batch.Usable; - Utilization = batch.Utilization; - } - // Static properties. public static PostageBatch MaxDepthInstance { get; } = new( PostageBatchId.Zero, @@ -99,6 +63,7 @@ internal PostageBatch(Response52 batch) false, true, null, + null, TimeSpan.FromDays(3650), 0); @@ -136,7 +101,9 @@ internal PostageBatch(Response52 batch) /// Label to identify the batch /// public string? Label { get; } - + + public int? StorageRadius { get; } + /// /// Time to live before expiration /// @@ -145,7 +112,7 @@ internal PostageBatch(Response52 batch) /// /// The count of the fullest bucket /// - public uint Utilization { get; } + public uint? Utilization { get; } // Static methods. public static BzzBalance CalculateAmount(BzzBalance chainPrice, TimeSpan ttl) => diff --git a/src/BeeNet/Models/PostageBatchId.cs b/src/BeeNet.Core/Models/PostageBatchId.cs similarity index 100% rename from src/BeeNet/Models/PostageBatchId.cs rename to src/BeeNet.Core/Models/PostageBatchId.cs diff --git a/src/BeeNet.Core/Models/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs new file mode 100644 index 00000000..19b29dab --- /dev/null +++ b/src/BeeNet.Core/Models/PostageBuckets.cs @@ -0,0 +1,216 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; + +namespace Etherna.BeeNet.Models +{ + /// + /// A thread safe implementation of postage buckets array + /// + [SuppressMessage("Reliability", "CA2002:Do not lock on objects with weak identity")] + public class PostageBuckets : IReadOnlyPostageBuckets, IDisposable + { + // Consts. + public const int BucketsSize = 1 << PostageBatch.BucketDepth; + + // Fields. + private readonly uint[] _buckets; + private readonly Dictionary> bucketsByCollisions; // + private readonly ReaderWriterLockSlim bucketsLock = new(LockRecursionPolicy.NoRecursion); + private bool disposed; + + // Constructor. + public PostageBuckets( + uint[]? initialBuckets = null) + { + if (initialBuckets is not null && + initialBuckets.Length != BucketsSize) + throw new ArgumentOutOfRangeException(nameof(initialBuckets), + $"Initial buckets must have length {BucketsSize}, or be null"); + + //init "buckets" and reverse index "bucketsByCollisions" + _buckets = initialBuckets ?? new uint[BucketsSize]; + bucketsByCollisions = new Dictionary> { [0] = [] }; + for (uint i = 0; i < BucketsSize; i++) + bucketsByCollisions[0].Add(i); + + //init counters + MaxBucketCollisions = 0; + MinBucketCollisions = 0; + TotalChunks = 0; + } + + // Dispose. + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) return; + + // Dispose managed resources. + if (disposing) + bucketsLock.Dispose(); + + disposed = true; + } + + // Properties. + public uint MaxBucketCollisions { get; private set; } + public uint MinBucketCollisions { get; private set; } + public int RequiredPostageBatchDepth => CollisionsToRequiredPostageBatchDepth(MaxBucketCollisions); + public long TotalChunks { get; private set; } + + // Methods. + public int[] CountBucketsByCollisions() + { + bucketsLock.EnterReadLock(); + try + { + return bucketsByCollisions.Select(pair => pair.Value.Count).ToArray(); + } + finally + { + bucketsLock.ExitReadLock(); + } + } + + public uint[] GetBuckets() + { + bucketsLock.EnterReadLock(); + try + { + return _buckets.ToArray(); + } + finally + { + bucketsLock.ExitReadLock(); + } + } + + public IEnumerable GetBucketsByCollisions(uint collisions) + { + bucketsLock.EnterReadLock(); + try + { + return bucketsByCollisions.TryGetValue(collisions, out var bucketsSet) + ? bucketsSet + : Array.Empty(); + } + finally + { + bucketsLock.ExitReadLock(); + } + } + + public uint GetCollisions(uint bucketId) + { + bucketsLock.EnterReadLock(); + try + { + return _buckets[bucketId]; + } + finally + { + bucketsLock.ExitReadLock(); + } + } + + public void IncrementCollisions(uint bucketId) + { + /* + * We have to lock on _buckets because we need atomic operations also with bucketsByCollisions + * and counters. ConcurrentDictionary would have better locking on single values, but doesn't + * support atomic operations involving third objects, like counters and "bucketsByCollisions". + */ + bucketsLock.EnterWriteLock(); + try + { + // Update collections. + _buckets[bucketId]++; + + bucketsByCollisions.TryAdd(_buckets[bucketId], []); + bucketsByCollisions[_buckets[bucketId] - 1].Remove(bucketId); + bucketsByCollisions[_buckets[bucketId]].Add(bucketId); + + // Update counters. + if (_buckets[bucketId] > MaxBucketCollisions) + MaxBucketCollisions = _buckets[bucketId]; + + MinBucketCollisions = bucketsByCollisions.OrderBy(p => p.Key) + .First(p => p.Value.Count > 0) + .Key; + + TotalChunks++; + } + finally + { + bucketsLock.ExitWriteLock(); + } + } + + public void ResetBucketCollisions(uint bucketId) + { + bucketsLock.EnterWriteLock(); + try + { + // Update collections. + var oldCollisions = _buckets[bucketId]; + _buckets[bucketId] = 0; + + bucketsByCollisions[oldCollisions].Remove(bucketId); + bucketsByCollisions[0].Add(bucketId); + + // Update counters. + MaxBucketCollisions = bucketsByCollisions.OrderByDescending(p => p.Key) + .First(p => p.Value.Count > 0) + .Key; + + MinBucketCollisions = 0; + } + finally + { + bucketsLock.ExitWriteLock(); + } + } + + // Static methods. + public static int CollisionsToRequiredPostageBatchDepth(uint collisions) + { + if (collisions == 0) + return PostageBatch.MinDepth; + return Math.Max( + (int)Math.Ceiling(Math.Log2(collisions)) + PostageBatch.BucketDepth, + PostageBatch.MinDepth); + } + + public static uint PostageBatchDepthToMaxCollisions(int postageBatchDepth) + { +#pragma warning disable CA1512 //only supported from .net 8 + if (postageBatchDepth < PostageBatch.MinDepth) + throw new ArgumentOutOfRangeException(nameof(postageBatchDepth)); +#pragma warning restore CA1512 + + return (uint)Math.Pow(2, postageBatchDepth - PostageBatch.BucketDepth); + } + } +} \ No newline at end of file diff --git a/src/BeeNet.Core/Models/PostageProof.cs b/src/BeeNet.Core/Models/PostageProof.cs new file mode 100644 index 00000000..a1ca83cd --- /dev/null +++ b/src/BeeNet.Core/Models/PostageProof.cs @@ -0,0 +1,31 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; + +namespace Etherna.BeeNet.Models +{ + public sealed class PostageProof( + string index, + PostageBatchId postageId, + string signature, + DateTimeOffset timeStamp) + { + // Properties. + public string Index { get; } = index; + public PostageBatchId PostageId { get; } = postageId; + public string Signature { get; } = signature; + public DateTimeOffset TimeStamp { get; } = timeStamp; + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Postage/PostageStamp.cs b/src/BeeNet.Core/Models/PostageStamp.cs similarity index 96% rename from src/BeeNet/Hasher/Postage/PostageStamp.cs rename to src/BeeNet.Core/Models/PostageStamp.cs index e43d5832..a5185929 100644 --- a/src/BeeNet/Hasher/Postage/PostageStamp.cs +++ b/src/BeeNet.Core/Models/PostageStamp.cs @@ -12,10 +12,9 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Models { public class PostageStamp { diff --git a/src/BeeNet/Models/Reachability.cs b/src/BeeNet.Core/Models/Reachability.cs similarity index 100% rename from src/BeeNet/Models/Reachability.cs rename to src/BeeNet.Core/Models/Reachability.cs diff --git a/src/BeeNet.Core/Models/RedistributionState.cs b/src/BeeNet.Core/Models/RedistributionState.cs new file mode 100644 index 00000000..eb072e73 --- /dev/null +++ b/src/BeeNet.Core/Models/RedistributionState.cs @@ -0,0 +1,43 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; + +namespace Etherna.BeeNet.Models +{ + public sealed class RedistributionState( + bool isFrozen, + bool isFullySynced, + bool isHealthy, + int round, + int lastWonRound, + int lastPlayedRound, + int lastFrozenRound, + int block, + BzzBalance reward, + XDaiBalance fees) + { + // Properties. + public bool IsFrozen { get; } = isFrozen; + public bool IsFullySynced { get; } = isFullySynced; + public bool IsHealthy { get; } = isHealthy; + public int Round { get; } = round; + public int LastWonRound { get; } = lastWonRound; + public int LastPlayedRound { get; } = lastPlayedRound; + public int LastFrozenRound { get; } = lastFrozenRound; + public int Block { get; } = block; + public BzzBalance Reward { get; } = reward; + public XDaiBalance Fees { get; } = fees; + } +} diff --git a/src/BeeNet/Models/RedundancyLevel.cs b/src/BeeNet.Core/Models/RedundancyLevel.cs similarity index 100% rename from src/BeeNet/Models/RedundancyLevel.cs rename to src/BeeNet.Core/Models/RedundancyLevel.cs diff --git a/src/BeeNet/Models/RedundancyStrategy.cs b/src/BeeNet.Core/Models/RedundancyStrategy.cs similarity index 100% rename from src/BeeNet/Models/RedundancyStrategy.cs rename to src/BeeNet.Core/Models/RedundancyStrategy.cs diff --git a/src/BeeNet/Models/CheckPinsResult.cs b/src/BeeNet.Core/Models/ReserveCommitment.cs similarity index 58% rename from src/BeeNet/Models/CheckPinsResult.cs rename to src/BeeNet.Core/Models/ReserveCommitment.cs index bca7b88d..da5f2605 100644 --- a/src/BeeNet/Models/CheckPinsResult.cs +++ b/src/BeeNet.Core/Models/ReserveCommitment.cs @@ -12,26 +12,20 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Clients; -using System; - namespace Etherna.BeeNet.Models { - public sealed class CheckPinsResult + public sealed class ReserveCommitment( + int duration, + SwarmHash hash, + ReserveCommitmentProof proof1, + ReserveCommitmentProof proof2, + ReserveCommitmentProof proofLast) { - internal CheckPinsResult(Response15 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Hash = response.Reference; - Invalid = response.Invalid; - Missing = response.Missing; - Total = response.Total; - } - - public SwarmHash Hash { get; } - public int Invalid { get; } - public int Missing { get; } - public int Total { get; } + // Properties. + public int Duration { get; } = duration; + public SwarmHash Hash { get; } = hash; + public ReserveCommitmentProof Proof1 { get; } = proof1; + public ReserveCommitmentProof Proof2 { get; } = proof2; + public ReserveCommitmentProof ProofLast { get; } = proofLast; } } \ No newline at end of file diff --git a/src/BeeNet.Core/Models/ReserveCommitmentProof.cs b/src/BeeNet.Core/Models/ReserveCommitmentProof.cs new file mode 100644 index 00000000..8b6b6100 --- /dev/null +++ b/src/BeeNet.Core/Models/ReserveCommitmentProof.cs @@ -0,0 +1,39 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class ReserveCommitmentProof( + int chunkSpan, + PostageProof postageProof, + IEnumerable proofSegments, + IEnumerable proofSegments2, + IEnumerable proofSegments3, + string proveSegment, + string proveSegment2, + IEnumerable socProof) + { + // Properties. + public int ChunkSpan { get; } = chunkSpan; + public PostageProof PostageProof { get; } = postageProof; + public IEnumerable ProofSegments { get; } = proofSegments; + public IEnumerable ProofSegments2 { get; } = proofSegments2; + public IEnumerable ProofSegments3 { get; } = proofSegments3; + public string ProveSegment { get; } = proveSegment; + public string ProveSegment2 { get; } = proveSegment2; + public IEnumerable SocProof { get; } = socProof; + } +} \ No newline at end of file diff --git a/src/BeeNet.Core/Models/ReserveState.cs b/src/BeeNet.Core/Models/ReserveState.cs new file mode 100644 index 00000000..aca04f7f --- /dev/null +++ b/src/BeeNet.Core/Models/ReserveState.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class ReserveState( + long commitment, + int radius, + int storageRadius) + { + // Properties. + public long Commitment { get; } = commitment; + public int Radius { get; } = radius; + public int StorageRadius { get; } = storageRadius; + } +} diff --git a/src/BeeNet.Core/Models/ResultChequebook.cs b/src/BeeNet.Core/Models/ResultChequebook.cs new file mode 100644 index 00000000..53dc4de5 --- /dev/null +++ b/src/BeeNet.Core/Models/ResultChequebook.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class ResultChequebook( + bool bounced, + BzzBalance lastPayout, + string recipient) + { + // Properties. + public bool Bounced { get; } = bounced; + public BzzBalance LastPayout { get; } = lastPayout; + public string Recipient { get; } = recipient; + } +} diff --git a/src/BeeNet.Core/Models/Settlement.cs b/src/BeeNet.Core/Models/Settlement.cs new file mode 100644 index 00000000..16d1aa1e --- /dev/null +++ b/src/BeeNet.Core/Models/Settlement.cs @@ -0,0 +1,29 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class Settlement( + BzzBalance totalReceived, + BzzBalance totalSent, + IEnumerable settlements) + { + // Properties. + public BzzBalance TotalReceived { get; } = totalReceived; + public BzzBalance TotalSent { get; } = totalSent; + public IEnumerable Settlements { get; } = settlements; + } +} diff --git a/src/BeeNet.Core/Models/SettlementData.cs b/src/BeeNet.Core/Models/SettlementData.cs new file mode 100644 index 00000000..2f0bf487 --- /dev/null +++ b/src/BeeNet.Core/Models/SettlementData.cs @@ -0,0 +1,27 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class SettlementData( + string peer, + BzzBalance received, + BzzBalance sent) + { + // Properties. + public string Peer { get; } = peer; + public BzzBalance Received { get; } = received; + public BzzBalance Sent { get; } = sent; + } +} diff --git a/src/BeeNet.Core/Models/SocProof.cs b/src/BeeNet.Core/Models/SocProof.cs new file mode 100644 index 00000000..2f502cba --- /dev/null +++ b/src/BeeNet.Core/Models/SocProof.cs @@ -0,0 +1,29 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class SocProof( + SwarmHash chunkHash, + string identifier, + string signature, + string signer) + { + // Properties. + public SwarmHash ChunkHash { get; } = chunkHash; + public string Identifier { get; } = identifier; + public string Signature { get; } = signature; + public string Signer { get; } = signer; + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Postage/StampBucketIndex.cs b/src/BeeNet.Core/Models/StampBucketIndex.cs similarity index 97% rename from src/BeeNet/Hasher/Postage/StampBucketIndex.cs rename to src/BeeNet.Core/Models/StampBucketIndex.cs index 2972a419..c697d154 100644 --- a/src/BeeNet/Hasher/Postage/StampBucketIndex.cs +++ b/src/BeeNet.Core/Models/StampBucketIndex.cs @@ -15,7 +15,7 @@ using System; using System.Buffers.Binary; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Models { public class StampBucketIndex(uint bucketId, uint bucketCounter) { diff --git a/src/BeeNet.Core/Models/StampsBuckets.cs b/src/BeeNet.Core/Models/StampsBuckets.cs new file mode 100644 index 00000000..355e55fa --- /dev/null +++ b/src/BeeNet.Core/Models/StampsBuckets.cs @@ -0,0 +1,31 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class StampsBuckets( + int depth, + int bucketDepth, + int bucketUpperBound, + IEnumerable collisions) + { + // Properties. + public int Depth { get; } = depth; + public int BucketDepth { get; } = bucketDepth; + public int BucketUpperBound { get; } = bucketUpperBound; + public IEnumerable Collisions { get; } = collisions; + } +} diff --git a/src/BeeNet/Models/StatusBeeMode.cs b/src/BeeNet.Core/Models/StatusBeeMode.cs similarity index 100% rename from src/BeeNet/Models/StatusBeeMode.cs rename to src/BeeNet.Core/Models/StatusBeeMode.cs diff --git a/src/BeeNet.Core/Models/StatusNode.cs b/src/BeeNet.Core/Models/StatusNode.cs new file mode 100644 index 00000000..ac0633bb --- /dev/null +++ b/src/BeeNet.Core/Models/StatusNode.cs @@ -0,0 +1,43 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +namespace Etherna.BeeNet.Models +{ + public sealed class StatusNode( + StatusBeeMode beeMode, + int batchCommitment, + int connectedPeers, + int neighborhoodSize, + string peer, + int proximity, + double pullsyncRate, + int reserveSize, + int reserveSizeWithinRadius, + bool? requestFailed, + int storageRadius) + { + // Properties. + public StatusBeeMode BeeMode { get; } = beeMode; + public int BatchCommitment { get; } = batchCommitment; + public int ConnectedPeers { get; } = connectedPeers; + public int NeighborhoodSize { get; } = neighborhoodSize; + public string Peer { get; } = peer; + public int Proximity { get; } = proximity; + public double PullsyncRate { get; } = pullsyncRate; + public int ReserveSize { get; } = reserveSize; + public int ReserveSizeWithinRadius { get; } = reserveSizeWithinRadius; + public bool? RequestFailed { get; } = requestFailed; + public int StorageRadius { get; } = storageRadius; + } +} diff --git a/src/BeeNet/Models/SwarmAddress.cs b/src/BeeNet.Core/Models/SwarmAddress.cs similarity index 63% rename from src/BeeNet/Models/SwarmAddress.cs rename to src/BeeNet.Core/Models/SwarmAddress.cs index eac9d419..f1b12aae 100644 --- a/src/BeeNet/Models/SwarmAddress.cs +++ b/src/BeeNet.Core/Models/SwarmAddress.cs @@ -14,76 +14,67 @@ using System; using System.Collections.Generic; -using System.IO; namespace Etherna.BeeNet.Models { public readonly struct SwarmAddress : IEquatable { + // Consts. + public const char Separator = '/'; + // Constructor. - public SwarmAddress(SwarmHash hash, Uri? relativePath = null) + public SwarmAddress(SwarmHash hash, string? path = null) { - if (relativePath is not null && - relativePath.IsAbsoluteUri) - throw new ArgumentException("Path needs to be relative", nameof(relativePath)); - Hash = hash; - RelativePath = relativePath; + Path = NormalizePath(path); } public SwarmAddress(string address) { ArgumentNullException.ThrowIfNull(address, nameof(address)); - // Trim initial and final slash. - address = address.Trim('/'); + // Trim initial slash. + address = address.TrimStart(Separator); // Extract hash root. - var slashIndex = address.IndexOf('/', StringComparison.InvariantCulture); - var root = slashIndex > 0 ? address[..slashIndex] : address; - var path = slashIndex > 0 ? address[(slashIndex + 1)..] : null; + var slashIndex = address.IndexOf(Separator, StringComparison.InvariantCulture); + var hash = slashIndex > 0 ? address[..slashIndex] : address; + var path = slashIndex > 0 ? address[slashIndex..] : Separator.ToString(); // Set hash and path. - Hash = new SwarmHash(root); - if (!string.IsNullOrEmpty(path)) - RelativePath = new Uri(path, UriKind.Relative); + Hash = new SwarmHash(hash); + Path = NormalizePath(path); } // Properties. public SwarmHash Hash { get; } - public Uri? RelativePath { get; } + public string Path { get; } // Methods. public bool Equals(SwarmAddress other) => Hash.Equals(other.Hash) && - EqualityComparer.Default.Equals(RelativePath, other.RelativePath); + EqualityComparer.Default.Equals(Path, other.Path); public override bool Equals(object? obj) => obj is SwarmAddress other && Equals(other); public override int GetHashCode() => Hash.GetHashCode() ^ - (RelativePath?.GetHashCode() ?? 0); - public override string ToString() - { - if (RelativePath is null) - return Hash + "/"; - - var pathString = RelativePath.ToString(); - if (Path.IsPathRooted(pathString)) - return Hash + pathString; - - return Hash + "/" + pathString; - } + (Path?.GetHashCode(StringComparison.InvariantCulture) ?? 0); + public override string ToString() => Hash + Path; // Static methods. - public static SwarmAddress FromSwarmHash(SwarmHash value) => new(value); public static SwarmAddress FromString(string value) => new(value); + public static SwarmAddress FromSwarmHash(SwarmHash value) => new(value); // Operator methods. public static bool operator ==(SwarmAddress left, SwarmAddress right) => left.Equals(right); public static bool operator !=(SwarmAddress left, SwarmAddress right) => !(left == right); // Implicit conversion operator methods. - public static implicit operator SwarmAddress(SwarmHash value) => new(value); public static implicit operator SwarmAddress(string value) => new(value); + public static implicit operator SwarmAddress(SwarmHash value) => new(value); // Explicit conversion operator methods. public static explicit operator string(SwarmAddress value) => value.ToString(); + + // Helpers. + internal static string NormalizePath(string? path) => + Separator + (path ?? "").TrimStart(Separator); } } \ No newline at end of file diff --git a/src/BeeNet/Models/SwarmChunk.cs b/src/BeeNet.Core/Models/SwarmChunk.cs similarity index 95% rename from src/BeeNet/Models/SwarmChunk.cs rename to src/BeeNet.Core/Models/SwarmChunk.cs index e45d1a96..b3ff467a 100644 --- a/src/BeeNet/Models/SwarmChunk.cs +++ b/src/BeeNet.Core/Models/SwarmChunk.cs @@ -12,8 +12,6 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Bmt; -using Etherna.BeeNet.Hasher.Postage; using System; using System.Buffers.Binary; @@ -26,7 +24,7 @@ public class SwarmChunk protected readonly byte[] _span; // Consts. - public const int DataSize = SwarmChunkBmt.SegmentSize * SwarmChunkBmt.SegmentsCount; + public const int DataSize = 4096; public const int SpanAndDataSize = SpanSize + DataSize; public const int SpanSize = 8; diff --git a/src/BeeNet.Core/Models/SwarmChunkReference.cs b/src/BeeNet.Core/Models/SwarmChunkReference.cs new file mode 100644 index 00000000..e746b175 --- /dev/null +++ b/src/BeeNet.Core/Models/SwarmChunkReference.cs @@ -0,0 +1,9 @@ +namespace Etherna.BeeNet.Models +{ + public class SwarmChunkReference(SwarmHash hash, XorEncryptKey? encryptionKey, bool useRecursiveEncryption) + { + public XorEncryptKey? EncryptionKey { get; } = encryptionKey; + public SwarmHash Hash { get; } = hash; + public bool UseRecursiveEncryption { get; } = useRecursiveEncryption; + } +} \ No newline at end of file diff --git a/src/BeeNet/Models/SwarmFeedChunk.cs b/src/BeeNet.Core/Models/SwarmFeedChunk.cs similarity index 84% rename from src/BeeNet/Models/SwarmFeedChunk.cs rename to src/BeeNet.Core/Models/SwarmFeedChunk.cs index 641d1d86..e524c22e 100644 --- a/src/BeeNet/Models/SwarmFeedChunk.cs +++ b/src/BeeNet.Core/Models/SwarmFeedChunk.cs @@ -12,11 +12,11 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Epoche; using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Feeds; +using Etherna.BeeNet.Models.Feeds; using Nethereum.Hex.HexConvertors.Extensions; using Nethereum.Util; +using Nethereum.Util.HashProviders; using System; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -95,46 +95,48 @@ public static byte[] BuildChunkPayload(byte[] payload, ulong? timestamp = null) return chunkData; } - public static SwarmHash BuildHash(string account, byte[] topic, FeedIndexBase index) => - BuildHash(account, BuildIdentifier(topic, index)); + public static SwarmHash BuildHash(string account, byte[] topic, FeedIndexBase index, IHashProvider hashProvider) => + BuildHash(account, BuildIdentifier(topic, index, hashProvider), hashProvider); - public static SwarmHash BuildHash(byte[] account, byte[] topic, FeedIndexBase index) => - BuildHash(account, BuildIdentifier(topic, index)); + public static SwarmHash BuildHash(byte[] account, byte[] topic, FeedIndexBase index, IHashProvider hashProvider) => + BuildHash(account, BuildIdentifier(topic, index, hashProvider), hashProvider); - public static SwarmHash BuildHash(string account, byte[] identifier) + public static SwarmHash BuildHash(string account, byte[] identifier, IHashProvider hashProvider) { if (!account.IsValidEthereumAddressHexFormat()) throw new ArgumentException("Value is not a valid ethereum account", nameof(account)); - return BuildHash(account.HexToByteArray(), identifier); + return BuildHash(account.HexToByteArray(), identifier, hashProvider); } - public static SwarmHash BuildHash(byte[] account, byte[] identifier) + public static SwarmHash BuildHash(byte[] account, byte[] identifier, IHashProvider hashProvider) { ArgumentNullException.ThrowIfNull(account, nameof(account)); + ArgumentNullException.ThrowIfNull(hashProvider, nameof(hashProvider)); ArgumentNullException.ThrowIfNull(identifier, nameof(identifier)); if (account.Length != AccountSize) throw new ArgumentOutOfRangeException(nameof(account), "Invalid account length"); if (identifier.Length != IdentifierSize) throw new ArgumentOutOfRangeException(nameof(identifier), "Invalid identifier length"); - - return Keccak256.ComputeHash(identifier.Concat(account).ToArray()); + + return hashProvider.ComputeHash(identifier.Concat(account).ToArray()); } - public static byte[] BuildIdentifier(byte[] topic, FeedIndexBase index) + public static byte[] BuildIdentifier(byte[] topic, FeedIndexBase index, IHashProvider hashProvider) { - ArgumentNullException.ThrowIfNull(topic, nameof(topic)); + ArgumentNullException.ThrowIfNull(hashProvider, nameof(hashProvider)); ArgumentNullException.ThrowIfNull(index, nameof(index)); + ArgumentNullException.ThrowIfNull(topic, nameof(topic)); if (topic.Length != TopicSize) throw new ArgumentOutOfRangeException(nameof(topic), "Invalid topic length"); var newArray = new byte[TopicSize + IndexSize]; topic.CopyTo(newArray, 0); - index.MarshalBinary.CopyTo(newArray, topic.Length); + index.GetMarshalBinaryHash(hashProvider).CopyTo(newArray.AsMemory()[topic.Length..]); - return Keccak256.ComputeHash(newArray); + return hashProvider.ComputeHash(newArray); } } } diff --git a/src/BeeNet/Models/SwarmHash.cs b/src/BeeNet.Core/Models/SwarmHash.cs similarity index 81% rename from src/BeeNet/Models/SwarmHash.cs rename to src/BeeNet.Core/Models/SwarmHash.cs index 5c7fee99..38392fae 100644 --- a/src/BeeNet/Models/SwarmHash.cs +++ b/src/BeeNet.Core/Models/SwarmHash.cs @@ -31,7 +31,7 @@ namespace Etherna.BeeNet.Models public SwarmHash(byte[] hash) { ArgumentNullException.ThrowIfNull(hash, nameof(hash)); - if (hash.Length != HashSize) + if (!IsValidHash(hash)) throw new ArgumentOutOfRangeException(nameof(hash)); byteHash = hash; @@ -41,9 +41,16 @@ public SwarmHash(string hash) { ArgumentNullException.ThrowIfNull(hash, nameof(hash)); - byteHash = hash.HexToByteArray(); + try + { + byteHash = hash.HexToByteArray(); + } + catch (FormatException) + { + throw new ArgumentException("Invalid hash", nameof(hash)); + } - if (byteHash.Length != HashSize) + if (!IsValidHash(byteHash)) throw new ArgumentOutOfRangeException(nameof(hash)); } @@ -63,6 +70,22 @@ public uint ToBucketId() => // Static methods. public static SwarmHash FromByteArray(byte[] value) => new(value); public static SwarmHash FromString(string value) => new(value); + public static bool IsValidHash(byte[] value) + { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + return value.Length == HashSize; + } + public static bool IsValidHash(string value) + { + try + { + return IsValidHash(value.HexToByteArray()); + } + catch (FormatException) + { + return false; + } + } // Operator methods. public static bool operator ==(SwarmHash left, SwarmHash right) => left.Equals(right); diff --git a/src/BeeNet.Core/Models/SwarmUri.cs b/src/BeeNet.Core/Models/SwarmUri.cs new file mode 100644 index 00000000..da256a2f --- /dev/null +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -0,0 +1,149 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Etherna.BeeNet.Models +{ + [SuppressMessage("Design", "CA1054:URI-like parameters should not be strings")] + public readonly struct SwarmUri : IEquatable + { + // Constructor. + public SwarmUri(SwarmHash? hash, string? path) + { + if (hash is null && path is null) + throw new ArgumentException("Hash and path can't be both null"); + + Hash = hash; + Path = hash != null ? SwarmAddress.NormalizePath(path) : path!; + } + public SwarmUri(string uri, UriKind uriKind) + { + ArgumentNullException.ThrowIfNull(uri, nameof(uri)); + + // Determine uri kind. + if (uriKind == UriKind.RelativeOrAbsolute) + uriKind = SwarmHash.IsValidHash(uri.Split(SwarmAddress.Separator)[0]) + ? UriKind.Absolute + : UriKind.Relative; + + if (uriKind == UriKind.Absolute) + { + var address = new SwarmAddress(uri); + Hash = address.Hash; + Path = address.Path; + } + else + { + Path = uri; + } + } + + // Properties. + public SwarmHash? Hash { get; } + public bool IsRooted => UriKind == UriKind.Absolute || System.IO.Path.IsPathRooted(Path); + public string Path { get; } + public UriKind UriKind => Hash.HasValue ? UriKind.Absolute : UriKind.Relative; + + // Methods. + public bool Equals(SwarmUri other) => + Hash.Equals(other.Hash) && + EqualityComparer.Default.Equals(Path, other.Path); + + public override bool Equals(object? obj) => obj is SwarmUri other && Equals(other); + + public override int GetHashCode() => Hash.GetHashCode() ^ + (Path?.GetHashCode(StringComparison.InvariantCulture) ?? 0); + + public override string ToString() => + UriKind == UriKind.Absolute ? new SwarmAddress(Hash!.Value, Path).ToString() : Path!; + + /// + /// Convert a URI to an Address. If URI is relative, a prefix Address must be provided + /// + /// Optional prefix address + /// The absolute URI as an Address + public SwarmAddress ToSwarmAddress(SwarmAddress? prefix = null) + { + if (prefix is null) + { + if (UriKind != UriKind.Absolute) + throw new InvalidOperationException("Url is not absolute, and a prefix address is required"); + + return new SwarmAddress(Hash!.Value, Path); + } + + var combined = Combine(prefix.Value, this); + return new(combined.Hash!.Value, combined.Path); + } + + public bool TryGetRelativeTo(SwarmUri relativeTo, out SwarmUri output) + { + output = default; + + if (relativeTo.Hash != Hash) + return false; + + var dirs = Path.Split(SwarmAddress.Separator); + var relativeToDirs = relativeTo.Path.TrimEnd(SwarmAddress.Separator).Split(SwarmAddress.Separator); + if (dirs.Length < relativeToDirs.Length || + !dirs[..relativeToDirs.Length].SequenceEqual(relativeToDirs)) + return false; + + output = new SwarmUri(null, string.Join(SwarmAddress.Separator, dirs[relativeToDirs.Length..])); + return true; + } + + // Static methods. + public static SwarmUri Combine(params SwarmUri[] uris) + { + ArgumentNullException.ThrowIfNull(uris, nameof(uris)); + if (uris.Length == 0) + throw new ArgumentOutOfRangeException(nameof(uris), "Empty uri list"); + + var combined = uris[0]; + foreach (var uri in uris.Skip(1)) + { + if (uri.UriKind == UriKind.Absolute) + combined = uri; + else if (uri.IsRooted) + combined = new SwarmUri(combined.Hash, uri.Path); + else + combined = new SwarmUri( + combined.Hash, + (combined.Path ?? "").TrimEnd(SwarmAddress.Separator) + SwarmAddress.Separator + uri.Path); + } + + return combined; + } + public static SwarmUri FromString(string value) => new(value, UriKind.RelativeOrAbsolute); + public static SwarmUri FromSwarmAddress(SwarmAddress value) => new(value.Hash, value.Path); + public static SwarmUri FromSwarmHash(SwarmHash value) => new(value, null); + + // Operator methods. + public static bool operator ==(SwarmUri left, SwarmUri right) => left.Equals(right); + public static bool operator !=(SwarmUri left, SwarmUri right) => !(left == right); + + // Implicit conversion operator methods. + public static implicit operator SwarmUri(string value) => new(value, UriKind.RelativeOrAbsolute); + public static implicit operator SwarmUri(SwarmAddress value) => new(value.Hash, value.Path); + public static implicit operator SwarmUri(SwarmHash value) => new(value, null); + + // Explicit conversion operator methods. + public static explicit operator string(SwarmUri value) => value.ToString(); + } +} \ No newline at end of file diff --git a/src/BeeNet/Models/DisconnectedPeers.cs b/src/BeeNet.Core/Models/TagInfo.cs similarity index 62% rename from src/BeeNet/Models/DisconnectedPeers.cs rename to src/BeeNet.Core/Models/TagInfo.cs index 246f96ae..b6801e20 100644 --- a/src/BeeNet/Models/DisconnectedPeers.cs +++ b/src/BeeNet.Core/Models/TagInfo.cs @@ -16,19 +16,22 @@ namespace Etherna.BeeNet.Models { - public sealed class DisconnectedPeers + public sealed class TagInfo( + long uid, + DateTimeOffset startedAt, + int split, + int seen, + int stored, + int sent, + int synced) { - // Constructors. - internal DisconnectedPeers(Clients.DisconnectedPeers disconnectedPeers) - { - ArgumentNullException.ThrowIfNull(disconnectedPeers, nameof(disconnectedPeers)); - - Address = disconnectedPeers.Address; - Metrics = new PeerMetrics(disconnectedPeers.Metrics); - } - // Properties. - public string Address { get; } - public PeerMetrics Metrics { get; } + public long Uid { get; } = uid; + public DateTimeOffset StartedAt { get; } = startedAt; + public int Split { get; } = split; + public int Seen { get; } = seen; + public int Stored { get; } = stored; + public int Sent { get; } = sent; + public int Synced { get; } = synced; } } diff --git a/src/BeeNet.Core/Models/Topology.cs b/src/BeeNet.Core/Models/Topology.cs new file mode 100644 index 00000000..2621ec6a --- /dev/null +++ b/src/BeeNet.Core/Models/Topology.cs @@ -0,0 +1,42 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; +using System.Collections.Generic; + +namespace Etherna.BeeNet.Models +{ + public sealed class Topology( + string baseAddr, + IDictionary bins, + int connected, + int depth, + NetworkAvailability networkAvailability, + int nnLowWatermark, + int population, + Reachability reachability, + DateTimeOffset timestamp) + { + // Properties. + public string BaseAddr { get; } = baseAddr; + public IDictionary Bins { get; } = bins; + public int Connected { get; } = connected; + public int Depth { get; } = depth; + public NetworkAvailability NetworkAvailability { get; } = networkAvailability; + public int NnLowWatermark { get; } = nnLowWatermark; + public int Population { get; } = population; + public Reachability Reachability { get; } = reachability; + public DateTimeOffset Timestamp { get; } = timestamp; + } +} diff --git a/src/BeeNet.Core/Models/TxInfo.cs b/src/BeeNet.Core/Models/TxInfo.cs new file mode 100644 index 00000000..7fecb204 --- /dev/null +++ b/src/BeeNet.Core/Models/TxInfo.cs @@ -0,0 +1,41 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; + +namespace Etherna.BeeNet.Models +{ + public sealed class TxInfo( + string transactionHash, + string to, + int nonce, + XDaiBalance gasPrice, + long gasLimit, + string data, + DateTimeOffset created, + string description, + XDaiBalance value) + { + // Properties. + public string TransactionHash { get; } = transactionHash; + public string To { get; } = to; + public int Nonce { get; } = nonce; + public XDaiBalance GasPrice { get; } = gasPrice; + public long GasLimit { get; } = gasLimit; + public string Data { get; } = data; + public DateTimeOffset Created { get; } = created; + public string Description { get; } = description; + public XDaiBalance Value { get; } = value; + } +} diff --git a/src/BeeNet/Models/InfoBeeMode.cs b/src/BeeNet.Core/Models/WalletBalances.cs similarity index 75% rename from src/BeeNet/Models/InfoBeeMode.cs rename to src/BeeNet.Core/Models/WalletBalances.cs index a7831929..8d281c85 100644 --- a/src/BeeNet/Models/InfoBeeMode.cs +++ b/src/BeeNet.Core/Models/WalletBalances.cs @@ -12,16 +12,14 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; -using System.Collections.Generic; -using System.Text; - namespace Etherna.BeeNet.Models { - public enum InfoBeeMode + public sealed class WalletBalances( + BzzBalance bzzBalance, + XDaiBalance xDaiBalance) { - Light, - Full, - Dev, + // Properties. + public BzzBalance BzzBalance { get; } = bzzBalance; + public XDaiBalance XDaiBalance { get; } = xDaiBalance; } } diff --git a/src/BeeNet/Models/XDaiBalance.cs b/src/BeeNet.Core/Models/XDaiBalance.cs similarity index 100% rename from src/BeeNet/Models/XDaiBalance.cs rename to src/BeeNet.Core/Models/XDaiBalance.cs diff --git a/src/BeeNet/Manifest/XorEncryptKey.cs b/src/BeeNet.Core/Models/XorEncryptKey.cs similarity index 72% rename from src/BeeNet/Manifest/XorEncryptKey.cs rename to src/BeeNet.Core/Models/XorEncryptKey.cs index b1c88f0a..5ef20990 100644 --- a/src/BeeNet/Manifest/XorEncryptKey.cs +++ b/src/BeeNet.Core/Models/XorEncryptKey.cs @@ -12,10 +12,11 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . +using Nethereum.Hex.HexConvertors.Extensions; using System; using System.Security.Cryptography; -namespace Etherna.BeeNet.Manifest +namespace Etherna.BeeNet.Models { public class XorEncryptKey { @@ -34,6 +35,23 @@ public XorEncryptKey(byte[] bytes) this.bytes = bytes; } + + public XorEncryptKey(string key) + { + ArgumentNullException.ThrowIfNull(key, nameof(key)); + + try + { + bytes = key.HexToByteArray(); + } + catch (FormatException) + { + throw new ArgumentException("Invalid hash", nameof(key)); + } + + if (!IsValidKey(bytes)) + throw new ArgumentOutOfRangeException(nameof(key)); + } // Builders. public static XorEncryptKey BuildNewRandom() @@ -61,5 +79,14 @@ public void EncryptDecrypt(Span data) for (var i = 0; i < data.Length; i++) data[i] = (byte)(data[i] ^ bytes[i % bytes.Length]); } + + public override string ToString() => bytes.ToHex(); + + // Static methods. + public static bool IsValidKey(byte[] value) + { + ArgumentNullException.ThrowIfNull(value, nameof(value)); + return value.Length == KeySize; + } } } \ No newline at end of file diff --git a/src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj b/src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj new file mode 100644 index 00000000..68146d5b --- /dev/null +++ b/src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj @@ -0,0 +1,30 @@ + + + net8.0 + true + Etherna.BeeNet.Wasm + + Etherna SA + Utility services to work with Ethereum Swarm with WASM + + false + true + AllEnabledByDefault + + https://github.com/Etherna/bee-net + git + COPYING + COPYING-LESSER + README.md + + + + + + + + + + + + diff --git a/src/BeeNet.Util.Wasm/Program.cs b/src/BeeNet.Util.Wasm/Program.cs new file mode 100644 index 00000000..a1c6435d --- /dev/null +++ b/src/BeeNet.Util.Wasm/Program.cs @@ -0,0 +1,29 @@ +using Etherna.BeeNet.Services; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; +using System.Threading.Tasks; + +#pragma warning disable CA1303 +Console.WriteLine("Main, don't remove!"); +#pragma warning restore CA1303 + +[SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable")] +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +[SuppressMessage("Design", "CA1050:Declare types in namespaces")] +public partial class BeeNetWasmUtil +{ + [JSExport] + internal static async Task GetHashStringAsync( + byte[] data, + string fileContentType, + string fileName) + { + var calculatorService = new CalculatorService(); + var result = await calculatorService.EvaluateFileUploadAsync( + data, + fileContentType, + fileName).ConfigureAwait(false); + return result.Hash.ToString(); + } +} diff --git a/src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs b/src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2b51b753 --- /dev/null +++ b/src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +[assembly: System.Runtime.Versioning.SupportedOSPlatform("browser")] \ No newline at end of file diff --git a/src/BeeNet.Util.Wasm/Properties/launchSettings.json b/src/BeeNet.Util.Wasm/Properties/launchSettings.json new file mode 100644 index 00000000..bed8bdce --- /dev/null +++ b/src/BeeNet.Util.Wasm/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "BeeNet.Util.Wasm": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:7080;http://localhost:5094", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + } + } +} diff --git a/src/BeeNet.Util.Wasm/runtimeconfig.template.json b/src/BeeNet.Util.Wasm/runtimeconfig.template.json new file mode 100644 index 00000000..b96a9432 --- /dev/null +++ b/src/BeeNet.Util.Wasm/runtimeconfig.template.json @@ -0,0 +1,10 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "browser", + "host": "browser" + } + ] + } +} \ No newline at end of file diff --git a/src/BeeNet.Util.Wasm/wwwroot/index.html b/src/BeeNet.Util.Wasm/wwwroot/index.html new file mode 100644 index 00000000..04ac67cb --- /dev/null +++ b/src/BeeNet.Util.Wasm/wwwroot/index.html @@ -0,0 +1,17 @@ + + + + + + + BeeNet.Util.Wasm + + + + + + + + + + diff --git a/src/BeeNet.Util.Wasm/wwwroot/main.js b/src/BeeNet.Util.Wasm/wwwroot/main.js new file mode 100644 index 00000000..e45548a0 --- /dev/null +++ b/src/BeeNet.Util.Wasm/wwwroot/main.js @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { dotnet } from './_framework/dotnet.js' + +const { setModuleImports, getAssemblyExports, getConfig } = await dotnet + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +setModuleImports('main.js', { + window: { + location: { + href: () => globalThis.window.location.href + } + } +}); + +const config = getConfig(); +const exports = await getAssemblyExports(config.mainAssemblyName); + +let utf8Encode = new TextEncoder(); +let array = utf8Encode.encode("Bzz over the world!"); + +const hash = await exports.BeeNetWasmUtil.GetHashStringAsync( + array, + "text/plain", + "hello.txt" +); +console.log(hash); + +document.getElementById('out').innerHTML = hash; +await dotnet.run(); \ No newline at end of file diff --git a/src/BeeNet.Util/BeeNet.Util.csproj b/src/BeeNet.Util/BeeNet.Util.csproj new file mode 100644 index 00000000..32a92dcb --- /dev/null +++ b/src/BeeNet.Util/BeeNet.Util.csproj @@ -0,0 +1,53 @@ + + + + net6.0;net7.0;net8.0 + true + Etherna.BeeNet + + Etherna SA + Utility services to work with Ethereum Swarm in .Net + + 12 + enable + true + true + AllEnabledByDefault + + Bee.Net.Util + https://github.com/Etherna/bee-net + git + true + true + snupkg + COPYING + COPYING-LESSER + README.md + true + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs similarity index 73% rename from src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs rename to src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs index 82a57e1e..7a2f5a7c 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs +++ b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs @@ -12,21 +12,23 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Epoche; using Etherna.BeeNet.Models; using Nethereum.Merkle; using Nethereum.Merkle.StrategyOptions.PairingConcat; using Nethereum.Util.ByteArrayConvertors; -using Nethereum.Util.HashProviders; using System; using System.Collections.Generic; -namespace Etherna.BeeNet.Hasher.Bmt +namespace Etherna.BeeNet.Hashing.Bmt { - internal class SwarmChunkBmt : MerkleTree + internal sealed class SwarmChunkBmt(IHasher hasher) + : MerkleTree( + hasher, + byteArrayConvertor, + PairingConcatType.Normal) { // Classes. - private class ChunkBmtByteArrayConvertor : IByteArrayConvertor + private sealed class ChunkBmtByteArrayConvertor : IByteArrayConvertor { /// /// Verify that chunk segment data has right size @@ -46,27 +48,13 @@ public byte[] ConvertToByteArray(byte[] data) return data; } } - private class HashProvider : IHashProvider - { - public byte[] ComputeHash(byte[] data) => Keccak256.ComputeHash(data); - } // Consts. - public const int MaxDataSize = SegmentsCount * SegmentSize; - public const int SegmentsCount = 128; - public const int SegmentSize = 32; //Keccak hash size + public const int SegmentsCount = SwarmChunk.DataSize / SegmentSize; + public const int SegmentSize = SwarmHash.HashSize; // Static fields. private static readonly ChunkBmtByteArrayConvertor byteArrayConvertor = new(); - private static readonly HashProvider hashProvider = new(); - - // Constructor. - public SwarmChunkBmt() - : base(hashProvider, byteArrayConvertor, PairingConcatType.Normal) - { } - - // Static methods. - public static SwarmHash ComputeHash(byte[] data) => hashProvider.ComputeHash(data); // Protected override methods. protected override MerkleTreeNode CreateMerkleTreeNode(byte[] item) => diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs similarity index 80% rename from src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs rename to src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs index 88f42bb0..cec1ece2 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs +++ b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs @@ -17,18 +17,18 @@ using System.Collections.Generic; using System.Linq; -namespace Etherna.BeeNet.Hasher.Bmt +namespace Etherna.BeeNet.Hashing.Bmt { internal static class SwarmChunkBmtHasher { // Static methods. - public static SwarmHash Hash(byte[] span, byte[] data) + public static SwarmHash Hash(byte[] span, byte[] data, IHasher? hasher = null) { ArgumentNullException.ThrowIfNull(span, nameof(span)); ArgumentNullException.ThrowIfNull(data, nameof(data)); - if (data.Length > SwarmChunkBmt.MaxDataSize) - throw new ArgumentOutOfRangeException(nameof(data), $"Max writable data is {SwarmChunkBmt.MaxDataSize} bytes"); + if (data.Length > SwarmChunk.DataSize) + throw new ArgumentOutOfRangeException(nameof(data), $"Max writable data is {SwarmChunk.DataSize} bytes"); // Split input data into leaf segments. var segments = new List(); @@ -39,11 +39,12 @@ public static SwarmHash Hash(byte[] span, byte[] data) } // Build the merkle tree. - var bmt = new SwarmChunkBmt(); + hasher ??= new Hasher(); + var bmt = new SwarmChunkBmt(hasher); bmt.BuildTree(segments); var result = bmt.Root.Hash; - return SwarmChunkBmt.ComputeHash(span.Concat(result).ToArray()); + return hasher.ComputeHash(span.Concat(result).ToArray()); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Hasher.cs b/src/BeeNet.Util/Hashing/Hasher.cs new file mode 100644 index 00000000..c3ce9a65 --- /dev/null +++ b/src/BeeNet.Util/Hashing/Hasher.cs @@ -0,0 +1,47 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Models; +using Org.BouncyCastle.Crypto.Digests; +using System; +using System.Text; + +namespace Etherna.BeeNet.Hashing +{ + /// + /// Hasher service, not thread safe + /// + public class Hasher : IHasher + { + // Fields. + private readonly KeccakDigest hasher = new(256); + + // Methods. + public void ComputeHash(byte[] data, Span output) + { + hasher.BlockUpdate(data); + hasher.DoFinal(output); + } + + public byte[] ComputeHash(byte[] data) + { + var result = new byte[SwarmHash.HashSize]; + ComputeHash(data, result); + return result; + } + + public byte[] ComputeHash(string data) => + ComputeHash(Encoding.UTF8.GetBytes(data)); + } +} \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/IHasher.cs b/src/BeeNet.Util/Hashing/IHasher.cs new file mode 100644 index 00000000..02be670c --- /dev/null +++ b/src/BeeNet.Util/Hashing/IHasher.cs @@ -0,0 +1,25 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; + +namespace Etherna.BeeNet.Hashing +{ + public interface IHasher : + Nethereum.Util.HashProviders.IHashProvider + { + void ComputeHash(byte[] data, Span output); + byte[] ComputeHash(string data); + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs similarity index 73% rename from src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs index cf78ec97..9f73724d 100644 --- a/src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Bmt; +using Etherna.BeeNet.Hashing.Bmt; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -20,15 +20,18 @@ using System.Threading; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { - internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); - internal sealed class ChunkAggregatorPipelineStage : IHasherPipelineStage { // Private classes. - private class ChunkHeader(SwarmHash hash, ReadOnlyMemory span, bool isParityChunk) + private sealed class ChunkHeader( + SwarmHash hash, + ReadOnlyMemory span, + XorEncryptKey? chunkKey, + bool isParityChunk) { + public XorEncryptKey? ChunkKey { get; } = chunkKey; public SwarmHash Hash { get; } = hash; public ReadOnlyMemory Span { get; } = span; public bool IsParityChunk { get; } = isParityChunk; @@ -38,18 +41,23 @@ private class ChunkHeader(SwarmHash hash, ReadOnlyMemory span, bool isPari private readonly SemaphoreSlim feedChunkMutex = new(1, 1); private readonly Dictionary feedingBuffer = new(); private readonly List> chunkLevels; //[level][chunk] - private readonly HashChunkDelegateAsync hashChunkDelegate; private readonly byte maxChildrenChunks; + private readonly ChunkBmtPipelineStage shortBmtPipelineStage; + private readonly bool useRecursiveEncryption; private long feededChunkNumberId; // Constructor. public ChunkAggregatorPipelineStage( - HashChunkDelegateAsync hashChunkDelegate) + ChunkBmtPipelineStage shortBmtPipelineStage, + bool useRecursiveEncryption) { chunkLevels = []; - this.hashChunkDelegate = hashChunkDelegate; - maxChildrenChunks = SwarmChunkBmt.SegmentsCount; + maxChildrenChunks = (byte)(useRecursiveEncryption + ? SwarmChunkBmt.SegmentsCount / 2 //write chunk key after chunk hash + : SwarmChunkBmt.SegmentsCount); + this.shortBmtPipelineStage = shortBmtPipelineStage; + this.useRecursiveEncryption = useRecursiveEncryption; } // Dispose. @@ -58,6 +66,9 @@ public void Dispose() feedChunkMutex.Dispose(); } + // Properties. + public long MissedOptimisticHashing => shortBmtPipelineStage.MissedOptimisticHashing; + // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) { @@ -83,6 +94,7 @@ await AddChunkToLevelAsync( new ChunkHeader( processingChunk.Hash!.Value, processingChunk.Span, + processingChunk.ChunkKey, false)).ConfigureAwait(false); } } @@ -92,7 +104,7 @@ await AddChunkToLevelAsync( } } - public async Task SumAsync() + public async Task SumAsync() { bool rootChunkFound = false; for (int i = 0; !rootChunkFound; i++) @@ -118,7 +130,7 @@ public async Task SumAsync() var rootChunk = chunkLevels.Last()[0]; - return rootChunk.Hash; + return new(rootChunk.Hash, rootChunk.ChunkKey, useRecursiveEncryption); } // Helpers. @@ -151,19 +163,32 @@ private async Task WrapFullLevelAsync(int level) .Aggregate((a,c) => a + c)); //sum of ulongs. Linq doesn't have it // Build total data from total span, and all the hashes in level. + // If chunks are compacted, append the encryption key after the chunk hash. var totalData = totalSpan.Concat( - levelChunks.SelectMany(c => c.Hash.ToByteArray())) + levelChunks.SelectMany(c => useRecursiveEncryption + ? c.Hash.ToByteArray().Concat(c.ChunkKey!.Bytes.ToArray()) + : c.Hash.ToByteArray())) .ToArray(); // Run hashing on the new chunk, and add it to next level. + var hashingResult = await HashIntermediateChunkAsync(totalSpan, totalData).ConfigureAwait(false); await AddChunkToLevelAsync( level + 1, new ChunkHeader( - await hashChunkDelegate(totalSpan, totalData).ConfigureAwait(false), + hashingResult.Hash, totalSpan, + hashingResult.EncryptionKey, false)).ConfigureAwait(false); levelChunks.Clear(); } + + // Helpers. + private async Task HashIntermediateChunkAsync(byte[] span, byte[] data) + { + var args = new HasherPipelineFeedArgs(span: span, data: data); + await shortBmtPipelineStage.FeedAsync(args).ConfigureAwait(false); + return new(args.Hash!.Value, args.ChunkKey, useRecursiveEncryption); + } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs new file mode 100644 index 00000000..f85897be --- /dev/null +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -0,0 +1,227 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Hashing.Bmt; +using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Models; +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Hashing.Pipeline +{ + /// + /// Calculate hash of each chunk + /// + internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage + { + // Fields. + private readonly ushort compactLevel; + private readonly IHasherPipelineStage nextStage; + private readonly IPostageStampIssuer stampIssuer; + + private long _missedOptimisticHashing; + + // Constructor. + /// + /// Calculate hash of each chunk + /// + public ChunkBmtPipelineStage( + ushort compactLevel, + IHasherPipelineStage nextStage, + IPostageStampIssuer stampIssuer) + { + this.compactLevel = compactLevel; + this.nextStage = nextStage; + this.stampIssuer = stampIssuer; + } + + // Dispose. + public void Dispose() + { + nextStage.Dispose(); + } + + // Properties. + public long MissedOptimisticHashing => + _missedOptimisticHashing + nextStage.MissedOptimisticHashing; + + // Methods. + public async Task FeedAsync(HasherPipelineFeedArgs args) + { + if (args.Data.Length < SwarmChunk.SpanSize) + throw new InvalidOperationException("Data can't be shorter than span size here"); + if (args.Data.Length > SwarmChunk.SpanAndDataSize) + throw new InvalidOperationException("Data can't be longer than chunk + span size here"); + + // Create an instance for this specific task. Hasher is not thread safe. + var hasher = new Hasher(); + + var plainChunkHash = SwarmChunkBmtHasher.Hash( + args.Data[..SwarmChunk.SpanSize].ToArray(), + args.Data[SwarmChunk.SpanSize..].ToArray(), + hasher); + if (compactLevel == 0) + { + /* If no chunk compaction is involved, simply calculate the chunk hash and proceed. */ + args.Hash = plainChunkHash; + } + else + { + // Search best chunk key. + var bestChunkResult = await GetBestChunkAsync(args, plainChunkHash, hasher).ConfigureAwait(false); + + args.ChunkKey = bestChunkResult.ChunkKey; + args.Data = bestChunkResult.EncryptedData; + args.Hash = bestChunkResult.Hash; + } + + await nextStage.FeedAsync(args).ConfigureAwait(false); + } + + public Task SumAsync() => nextStage.SumAsync(); + + // Helpers. + private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) + { + // Don't encrypt span, otherwise knowing the chunk length, we could reconstruct the key. + chunkKey.EncryptDecrypt(data.AsSpan()[SwarmChunk.SpanSize..]); + } + + private async Task GetBestChunkAsync( + HasherPipelineFeedArgs args, + SwarmHash plainChunkHash, + Hasher hasher) + { + /* + * If chunk compaction is involved, use optimistic calculation. + * + * Calculate an encryption key, and try to find a bucket with optimal collisions. + * Before to proceed with the chunk and its key, wait optimistically until + * the previous chunk has been completed. Then verify if the same bucket has received + * new collisions, or not. If not, proceed, otherwise try again to search the best chunk key. + * + * The chunk key is calculate from the plain chunk hash, replacing the last 4 bytes + * with the attempt counter (int), and then hashing again. + * + * chunkKey = Keccack(plainChunkHash[^2..] + attempt) + * + * The encrypted chunk is calculated encrypting data with the chunk key. + * + * The optimistic algorithm will search the first best chunk available, trying a max of + * incremental attempts with max at the "compactionLevel" parameter. + * + * Best chunk is a chunk that fits in a bucket with the lowest possible number of collisions. + * + * Use a cache on generated chunkKey and relative bucketId for each attempt. This permits to not + * repeat the hash calculation in case that we need to repeat the search. + */ + + // Run optimistically before prev chunk completion. + var encryptionCache = new Dictionary(); + var (bestKeyAttempt, expectedCollisions) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + + // If there isn't any prev chunk to wait, proceed with result. + if (args.PrevChunkSemaphore == null) + return encryptionCache[bestKeyAttempt]; + + try + { + // Otherwise wait until prev chunk has been processed. + await args.PrevChunkSemaphore.WaitAsync().ConfigureAwait(false); + + // ** Here chunks can enter only once per time, and in order. ** + + // Check the optimistic result, and keep if valid. + var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); + var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); + + if (actualCollisions == expectedCollisions) + return encryptionCache[bestKeyAttempt]; + + // If it has been invalidated, do it again. + _missedOptimisticHashing++; + var (newBestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + return encryptionCache[newBestKeyAttempt]; + } + finally + { + //release prev chunk semaphore to reuse it + args.PrevChunkSemaphore.Release(); + } + } + + private (ushort BestKeyAttempt, uint ExpectedCollisions) SearchFirstBestChunkKey( + HasherPipelineFeedArgs args, + Dictionary optimisticCache, + SwarmHash plainChunkHash, + Hasher hasher) + { + // Init. + ushort bestAttempt = 0; + uint bestCollisions = 0; + + var plainChunkHashArray = plainChunkHash.ToByteArray(); + + // Search best chunk key. + for (ushort i = 0; i < compactLevel; i++) + { + uint collisions; + + if (optimisticCache.TryGetValue(i, out var cachedValues)) + { + collisions = stampIssuer.Buckets.GetCollisions(cachedValues.Hash.ToBucketId()); + } + else + { + // Create key. + BinaryPrimitives.WriteUInt16BigEndian(plainChunkHashArray.AsSpan()[^2..], i); + var chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); + + // Encrypt data. + var encryptedData = args.Data.ToArray(); + EncryptDecryptChunkData(chunkKey, encryptedData); + + // Calculate hash, bucket id, and save in cache. + var encryptedHash = SwarmChunkBmtHasher.Hash( + encryptedData[..SwarmChunk.SpanSize], + encryptedData[SwarmChunk.SpanSize..], + hasher); + optimisticCache[i] = new(chunkKey, encryptedData, encryptedHash); + + // Check key collisions. + collisions = stampIssuer.Buckets.GetCollisions(encryptedHash.ToBucketId()); + } + + // First attempt is always the best one. + if (i == 0) + bestCollisions = collisions; + + // Check if collisions are optimal. + if (collisions == stampIssuer.Buckets.MinBucketCollisions) + return (i, collisions); + + // Else, if this reach better collisions, but not the best. + if (collisions < bestCollisions) + { + bestAttempt = i; + bestCollisions = collisions; + } + } + + return (bestAttempt, bestCollisions); + } + } +} \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs new file mode 100644 index 00000000..c498ab7e --- /dev/null +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -0,0 +1,179 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Models; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Hashing.Pipeline +{ + /// + /// Produce chunked data with span prefix for successive stages. + /// Also controls the parallelism on chunk elaboration + /// + internal sealed class ChunkFeederPipelineStage : IHasherPipeline + { + // Fields. + private readonly SemaphoreSlim chunkConcurrencySemaphore; + private readonly ConcurrentQueue chunkSemaphorePool; + private readonly IHasherPipelineStage nextStage; + private readonly List nextStageTasks = new(); + + private long passedBytes; + + // Constructors. + [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] + public ChunkFeederPipelineStage( + IHasherPipelineStage nextStage, + int? chunkConcurrency = default) + { + chunkConcurrency ??= Environment.ProcessorCount; + + this.nextStage = nextStage; + chunkConcurrencySemaphore = new(chunkConcurrency.Value, chunkConcurrency.Value); + chunkSemaphorePool = new ConcurrentQueue(); + + //init semaphore pool + /* + * Double semaphores compared to current chunk concurrency. + * This avoids the race condition when: a chunk complete its hashing, it's semaphore is assigned and + * locked by another one, and only after this the direct child of the first one tries to wait its parent. + * + * This is possible because the returning order of semaphores in queue is not guaranteed. + * + * Explanation: + * While the task of chunk A is still waiting to lock on its prev A-1, all the prev chunks have ended tasks. + * Anyway, prevs didn't end in order, and for some reason semaphore that was of chunk A-1 comes in order + * before than next "Concurrency -1" (only active task is with A). Because of this, it can be allocated + * with any next task from A+1. If this happens before A locks on semaphore of A-1, we are in deadlock. + * + * Instead, doubling semaphores we guarantee that queue never goes under level of concurrency + * with contained elements, so a prev chunk's semaphore can't be reused until it's direct next + * has completed and released concurrency. + */ + for (int i = 0; i < chunkConcurrency * 2; i++) + chunkSemaphorePool.Enqueue(new SemaphoreSlim(1, 1)); + } + + // Dispose. + public void Dispose() + { + nextStage.Dispose(); + chunkConcurrencySemaphore.Dispose(); + while (chunkSemaphorePool.TryDequeue(out var semaphore)) + semaphore.Dispose(); + } + + // Properties. + public bool IsUsable { get; private set; } = true; + + public long MissedOptimisticHashing => nextStage.MissedOptimisticHashing; + + // Methods. + public async Task HashDataAsync(byte[] data) + { + ArgumentNullException.ThrowIfNull(data, nameof(data)); + + using var memoryStream = new MemoryStream(data); + return await HashDataAsync(memoryStream).ConfigureAwait(false); + } + + public async Task HashDataAsync(Stream dataStream) + { + ArgumentNullException.ThrowIfNull(dataStream, nameof(dataStream)); + + if (!IsUsable) + throw new InvalidOperationException("Pipeline has already been used"); + + // Make it no more usable. + IsUsable = false; + + // Slicing the stream permits to avoid to load all the stream in memory at the same time. + var chunkBuffer = new byte[SwarmChunk.DataSize]; + int chunkReadSize; + SemaphoreSlim? prevChunkSemaphore = null; + do + { + chunkReadSize = await dataStream.ReadAsync(chunkBuffer).ConfigureAwait(false); + + if (chunkReadSize > 0 || //write only chunks with data + passedBytes == 0) //or if the first and only one is empty + { + var chunkNumberId = passedBytes / SwarmChunk.DataSize; + + // Copy read data from buffer to a new chunk data byte[]. Include also span + var chunkData = new byte[SwarmChunk.SpanSize + chunkReadSize]; + chunkBuffer.AsSpan(0, chunkReadSize).CopyTo(chunkData.AsSpan(SwarmChunk.SpanSize)); + + // Write chunk span. + SwarmChunk.WriteSpan( + chunkData.AsSpan(0, SwarmChunk.SpanSize), + (ulong)chunkReadSize); + + // Invoke next stage with parallelism on chunks. + //control concurrency + await chunkConcurrencySemaphore.WaitAsync().ConfigureAwait(false); + + //initialize chunk semaphore, receiving from semaphore pool +#pragma warning disable CA2000 + if (!chunkSemaphorePool.TryDequeue(out var chunkSemaphore)) + throw new InvalidOperationException("Semaphore pool exhausted"); +#pragma warning restore CA2000 + await chunkSemaphore.WaitAsync().ConfigureAwait(false); + + //build args + var feedArgs = new HasherPipelineFeedArgs( + span: chunkData[..SwarmChunk.SpanSize], + data: chunkData, + numberId: chunkNumberId, + prevChunkSemaphore: prevChunkSemaphore); + + //run task + nextStageTasks.Add( + Task.Run(async () => + { + try + { + await nextStage.FeedAsync(feedArgs).ConfigureAwait(false); + } + finally + { + //release and restore chunk semaphore in pool + chunkSemaphore.Release(); + chunkSemaphorePool.Enqueue(chunkSemaphore); + + //release task for next chunk + chunkConcurrencySemaphore.Release(); + } + })); + + //set current chunk semaphore as prev for next chunk + prevChunkSemaphore = chunkSemaphore; + + passedBytes += chunkReadSize; + } + } while (chunkReadSize == SwarmChunk.DataSize); + + // Wait the end of all chunk computation. + await Task.WhenAll(nextStageTasks).ConfigureAwait(false); + + return await nextStage.SumAsync().ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs similarity index 69% rename from src/BeeNet/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs index 1286da82..edeadb8c 100644 --- a/src/BeeNet/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs @@ -12,37 +12,28 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Postage; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using System; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { - internal sealed class ChunkStoreWriterPipelineStage : IHasherPipelineStage + internal sealed class ChunkStoreWriterPipelineStage( + IChunkStore chunkStore, + IPostageStamper postageStamper, + IHasherPipelineStage? nextStage) + : IHasherPipelineStage { - // Fields. - private readonly IChunkStore chunkStore; - private readonly IHasherPipelineStage? nextStage; - private readonly IPostageStamper postageStamper; - - // Constructor. - public ChunkStoreWriterPipelineStage( - IChunkStore chunkStore, - IPostageStamper postageStamper, - IHasherPipelineStage? nextStage) - { - this.chunkStore = chunkStore; - this.nextStage = nextStage; - this.postageStamper = postageStamper; - } - // Dispose. public void Dispose() { nextStage?.Dispose(); } + + // Properties. + public long MissedOptimisticHashing => nextStage?.MissedOptimisticHashing ?? 0; // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) @@ -62,7 +53,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) await nextStage.FeedAsync(args).ConfigureAwait(false); } - public Task SumAsync() => + public Task SumAsync() => nextStage?.SumAsync() ?? throw new InvalidOperationException(); } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs b/src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs new file mode 100644 index 00000000..df4ed468 --- /dev/null +++ b/src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs @@ -0,0 +1,17 @@ +using Etherna.BeeNet.Manifest; +using Etherna.BeeNet.Models; +using System.Diagnostics.CodeAnalysis; + +namespace Etherna.BeeNet.Hashing.Pipeline +{ + [SuppressMessage("Performance", "CA1819:Properties should not return arrays")] + public class CompactedChunkAttemptResult( + XorEncryptKey chunkKey, + byte[] encryptedData, + SwarmHash hash) + { + public XorEncryptKey ChunkKey { get; } = chunkKey; + public byte[] EncryptedData { get; } = encryptedData; + public SwarmHash Hash { get; } = hash; + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs similarity index 64% rename from src/BeeNet/Hasher/Pipeline/HasherPipelineBuilder.cs rename to src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 6d060ac2..73e4a490 100644 --- a/src/BeeNet/Hasher/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -12,35 +12,43 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Postage; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using System; using System.Diagnostics.CodeAnalysis; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] - internal static class HasherPipelineBuilder + public static class HasherPipelineBuilder { // Static builders. public static IHasherPipeline BuildNewHasherPipeline( IPostageStamper postageStamper, RedundancyLevel redundancyLevel, bool isEncrypted, - string? chunkStoreDirectory) => + string? chunkStoreDirectory, + ushort compactLevel, + int? chunkConcurrency) => BuildNewHasherPipeline( chunkStoreDirectory is null ? new FakeChunkStore() : new LocalDirectoryChunkStore(chunkStoreDirectory), postageStamper, redundancyLevel, - isEncrypted); + isEncrypted, + compactLevel, + chunkConcurrency); public static IHasherPipeline BuildNewHasherPipeline( IChunkStore chunkStore, IPostageStamper postageStamper, RedundancyLevel redundancyLevel, - bool isEncrypted) + bool isEncrypted, + ushort compactLevel, + int? chunkConcurrency) { + ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); + if (redundancyLevel != RedundancyLevel.None) throw new NotImplementedException(); @@ -52,29 +60,19 @@ public static IHasherPipeline BuildNewHasherPipeline( else { //build stages - var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper); - var chunkAggregatorStage = new ChunkAggregatorPipelineStage( - async (span, data) => - { - var args = new HasherPipelineFeedArgs(span: span, data: data); - await shortPipelineStage.FeedAsync(args).ConfigureAwait(false); - return args.Hash!.Value; - } - ); + new ChunkBmtPipelineStage( + compactLevel, + new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null), + postageStamper.StampIssuer), + compactLevel > 0); + var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); - bmtStage = new ChunkBmtPipelineStage(storeWriterStage); + + bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } - return new ChunkFeederPipelineStage(bmtStage); - } - - public static IHasherPipelineStage BuildNewShortHasherPipeline( - IChunkStore chunkStore, - IPostageStamper postageStamper) - { - var storeWriter = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(storeWriter); + return new ChunkFeederPipelineStage(bmtStage, chunkConcurrency); } } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs similarity index 69% rename from src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs rename to src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs index a712e9cd..dae12f22 100644 --- a/src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs @@ -14,21 +14,25 @@ using Etherna.BeeNet.Models; using System; +using System.Threading; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { - internal class HasherPipelineFeedArgs + public sealed class HasherPipelineFeedArgs { // Fields. - private readonly byte[] _data; private readonly byte[]? _span; + private byte[] _data; // Constructor. public HasherPipelineFeedArgs( byte[] data, byte[]? span = null, - long numberId = 0) + long numberId = 0, + SemaphoreSlim? prevChunkSemaphore = null) { + ArgumentNullException.ThrowIfNull(data, nameof(data)); + if (span is not null) { if (data.Length < SwarmChunk.SpanSize) @@ -40,13 +44,23 @@ public HasherPipelineFeedArgs( _data = data; _span = span; NumberId = numberId; + PrevChunkSemaphore = prevChunkSemaphore; } // Properties. + /// + /// The optional chunk encryption key + /// + public XorEncryptKey? ChunkKey { get; internal set; } + /// /// Data can include the span too, but it may be encrypted if the pipeline is encrypted /// - public ReadOnlyMemory Data => _data; + public ReadOnlyMemory Data + { + get => _data; + internal set => _data = value.ToArray(); + } /// /// Hash generated by BMT @@ -59,8 +73,13 @@ public HasherPipelineFeedArgs( public ReadOnlyMemory Span => _span; /// - /// Ordered Id, from 0 to n with the last chunk + /// Ordered id, from 0 to n with the last chunk /// public long NumberId { get; } + + /// + /// Previous chunk semaphore. Occuped resource until chunk is processing. + /// + public SemaphoreSlim? PrevChunkSemaphore { get; } } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs similarity index 83% rename from src/BeeNet/Hasher/Pipeline/IHasherPipeline.cs rename to src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs index 1da6b28a..40cf2a6e 100644 --- a/src/BeeNet/Hasher/Pipeline/IHasherPipeline.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs @@ -17,24 +17,28 @@ using System.IO; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { public interface IHasherPipeline : IDisposable { + // Properties. bool IsUsable { get; } + long MissedOptimisticHashing { get; } + + // Methods. /// /// Consume a byte array and returns a Swarm hash as result /// /// Input data /// Resulting swarm hash - Task HashDataAsync(byte[] data); + Task HashDataAsync(byte[] data); /// /// Consume a stream slicing it in chunk size parts, and returns a Swarm hash as result /// /// Input data stream /// Resulting swarm hash - Task HashDataAsync(Stream dataStream); + Task HashDataAsync(Stream dataStream); } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs similarity index 78% rename from src/BeeNet/Hasher/Pipeline/IHasherPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index b03b11ca..d25e0385 100644 --- a/src/BeeNet/Hasher/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs @@ -16,11 +16,15 @@ using System; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { - internal interface IHasherPipelineStage : IDisposable + public interface IHasherPipelineStage : IDisposable { + // Properties. + long MissedOptimisticHashing { get; } + + // Methods. Task FeedAsync(HasherPipelineFeedArgs args); - Task SumAsync(); + Task SumAsync(); } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Postage/FakePostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs similarity index 80% rename from src/BeeNet/Hasher/Postage/FakePostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs index bae9ab98..b50a2ed1 100644 --- a/src/BeeNet/Hasher/Postage/FakePostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs @@ -13,22 +13,18 @@ // If not, see . using Etherna.BeeNet.Models; -using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public class FakePostageStampIssuer : IPostageStampIssuer { - public ReadOnlySpan Buckets => Array.Empty(); + public IReadOnlyPostageBuckets Buckets { get; } = new PostageBuckets(); public uint BucketUpperBound { get; } public bool HasSaturated { get; } public PostageBatch PostageBatch => PostageBatch.MaxDepthInstance; - public uint MaxBucketCount { get; } + public uint MaxBucketCollisions { get; } public long TotalChunks { get; } - public StampBucketIndex IncrementBucketCount(SwarmHash hash) => - new StampBucketIndex(0, 0); - - public ulong GetCollisions(uint bucketId) => 0; + public StampBucketIndex IncrementBucketCount(SwarmHash hash) => new(0, 0); } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Postage/FakePostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/FakePostageStamper.cs similarity index 91% rename from src/BeeNet/Hasher/Postage/FakePostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/FakePostageStamper.cs index 8529c55f..bab0d284 100644 --- a/src/BeeNet/Hasher/Postage/FakePostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/FakePostageStamper.cs @@ -12,12 +12,12 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Signer; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Signer; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public class FakePostageStamper : IPostageStamper { diff --git a/src/BeeNet/Hasher/Postage/IPostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs similarity index 66% rename from src/BeeNet/Hasher/Postage/IPostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs index 4753f62b..fec10061 100644 --- a/src/BeeNet/Hasher/Postage/IPostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs @@ -13,40 +13,27 @@ // If not, see . using Etherna.BeeNet.Models; -using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public interface IPostageStampIssuer { // Properties. - /// - /// Collision Buckets: counts per neighbourhoods - /// - public ReadOnlySpan Buckets { get; } - - public uint BucketUpperBound { get; } + IReadOnlyPostageBuckets Buckets { get; } + + uint BucketUpperBound { get; } /// /// True if batch is mutable and BucketUpperBound has been it /// - public bool HasSaturated { get; } + bool HasSaturated { get; } /// /// The batch stamps are issued from /// - public PostageBatch PostageBatch { get; } - - /// - /// The count of the fullest bucket - /// - public uint MaxBucketCount { get; } - - long TotalChunks { get; } + PostageBatch PostageBatch { get; } // Methods. StampBucketIndex IncrementBucketCount(SwarmHash hash); - - ulong GetCollisions(uint bucketId); } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Postage/IPostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs similarity index 86% rename from src/BeeNet/Hasher/Postage/IPostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs index 6f2fd454..dffdc200 100644 --- a/src/BeeNet/Hasher/Postage/IPostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs @@ -12,13 +12,13 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Signer; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Signer; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { - internal interface IPostageStamper + public interface IPostageStamper { // Properties. ISigner Signer { get; } diff --git a/src/BeeNet/Hasher/Postage/PostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs similarity index 83% rename from src/BeeNet/Hasher/Postage/PostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs index b1ec918e..dd55036e 100644 --- a/src/BeeNet/Hasher/Postage/PostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public class PostageStampIssuer : IPostageStampIssuer { @@ -25,26 +25,22 @@ public class PostageStampIssuer : IPostageStampIssuer // Constructor. public PostageStampIssuer( PostageBatch postageBatch, - uint[]? initialBuckets = null) + PostageBuckets? buckets = null) { ArgumentNullException.ThrowIfNull(postageBatch, nameof(postageBatch)); - _buckets = new PostageBuckets(initialBuckets); + _buckets = buckets ?? new PostageBuckets(); BucketUpperBound = (uint)1 << (postageBatch.Depth - PostageBatch.BucketDepth); PostageBatch = postageBatch; } // Properties. - public ReadOnlySpan Buckets => _buckets.Buckets; + public IReadOnlyPostageBuckets Buckets => _buckets; public uint BucketUpperBound { get; } public bool HasSaturated { get; private set; } public PostageBatch PostageBatch { get; } - public uint MaxBucketCount => _buckets.MaxBucketCount; - public long TotalChunks => _buckets.TotalChunks; // Methods. - public ulong GetCollisions(uint bucketId) => _buckets.GetCollisions(bucketId); - public StampBucketIndex IncrementBucketCount(SwarmHash hash) { var bucketId = hash.ToBucketId(); diff --git a/src/BeeNet/Hasher/Postage/PostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/PostageStamper.cs similarity index 73% rename from src/BeeNet/Hasher/Postage/PostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/PostageStamper.cs index 142bf7ed..ec877977 100644 --- a/src/BeeNet/Hasher/Postage/PostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/PostageStamper.cs @@ -12,22 +12,24 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Epoche; using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Hasher.Signer; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Signer; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; +using Org.BouncyCastle.Crypto.Digests; using System; -using System.Linq; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { - internal class PostageStamper( + internal sealed class PostageStamper( ISigner signer, IPostageStampIssuer stampIssuer, IStampStore stampStore) : IPostageStamper { + // Fields. + private readonly KeccakDigest hasher = new(256); + // Properties. public ISigner Signer { get; } = signer; public IPostageStampIssuer StampIssuer { get; } = stampIssuer; @@ -76,11 +78,24 @@ public PostageStamp Stamp(SwarmHash hash) /// /// /// - private static byte[] ToSignDigest(SwarmHash hash, PostageBatchId batchId, StampBucketIndex stampBucketIndex, DateTimeOffset timeStamp) => - Keccak256.ComputeHash( - hash.ToByteArray() - .Concat(batchId.ToByteArray()) - .Concat(stampBucketIndex.ToByteArray()) - .Concat(timeStamp.ToUnixTimeMilliseconds().UnixDateTimeToByteArray()).ToArray()); + private byte[] ToSignDigest( + SwarmHash hash, + PostageBatchId batchId, + StampBucketIndex stampBucketIndex, + DateTimeOffset timeStamp) + { + var result = new byte[SwarmHash.HashSize]; + + lock (hasher) + { + hasher.BlockUpdate(hash.ToByteArray()); + hasher.BlockUpdate(batchId.ToByteArray()); + hasher.BlockUpdate(stampBucketIndex.ToByteArray()); + hasher.BlockUpdate(timeStamp.ToUnixTimeMilliseconds().UnixDateTimeToByteArray()); + hasher.DoFinal(result); + } + + return result; + } } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/Signer/FakeSigner.cs b/src/BeeNet.Util/Hashing/Signer/FakeSigner.cs similarity index 95% rename from src/BeeNet/Hasher/Signer/FakeSigner.cs rename to src/BeeNet.Util/Hashing/Signer/FakeSigner.cs index cc6f4746..d143a215 100644 --- a/src/BeeNet/Hasher/Signer/FakeSigner.cs +++ b/src/BeeNet.Util/Hashing/Signer/FakeSigner.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -namespace Etherna.BeeNet.Hasher.Signer +namespace Etherna.BeeNet.Hashing.Signer { public class FakeSigner : ISigner { diff --git a/src/BeeNet/Hasher/Signer/ISigner.cs b/src/BeeNet.Util/Hashing/Signer/ISigner.cs similarity index 95% rename from src/BeeNet/Hasher/Signer/ISigner.cs rename to src/BeeNet.Util/Hashing/Signer/ISigner.cs index 4c75c283..6c5672b4 100644 --- a/src/BeeNet/Hasher/Signer/ISigner.cs +++ b/src/BeeNet.Util/Hashing/Signer/ISigner.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -namespace Etherna.BeeNet.Hasher.Signer +namespace Etherna.BeeNet.Hashing.Signer { public interface ISigner { diff --git a/src/BeeNet/Models/ChequebookBalance.cs b/src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs similarity index 52% rename from src/BeeNet/Models/ChequebookBalance.cs rename to src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs index fa25e916..4b882d49 100644 --- a/src/BeeNet/Models/ChequebookBalance.cs +++ b/src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs @@ -1,4 +1,4 @@ -// Copyright 2021-present Etherna SA +// Copyright 2021-present Etherna SA // This file is part of Bee.Net. // // Bee.Net is free software: you can redistribute it and/or modify it under the terms of the @@ -12,24 +12,28 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Clients; -using System; +using Etherna.BeeNet.Exceptions; +using Etherna.BeeNet.Models; +using System.Threading.Tasks; -namespace Etherna.BeeNet.Models +namespace Etherna.BeeNet.Hashing.Store { - public sealed class ChequebookBalance + public class BeeClientChunkStore(IBeeClient beeClient) + : IReadOnlyChunkStore { - // Constructors. - internal ChequebookBalance(Response27 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); + public Task GetAsync(SwarmHash hash) => + beeClient.GetChunkAsync(hash); - TotalBalance = BzzBalance.FromPlurString(response.TotalBalance); - AvailableBalance = BzzBalance.FromPlurString(response.AvailableBalance); + public async Task TryGetAsync(SwarmHash hash) + { + try + { + return await beeClient.GetChunkAsync(hash).ConfigureAwait(false); + } + catch (BeeNetApiException) + { + return null; + } } - - // Properties. - public BzzBalance TotalBalance { get; } - public BzzBalance AvailableBalance { get; } } -} +} \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs new file mode 100644 index 00000000..82ddcfba --- /dev/null +++ b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs @@ -0,0 +1,116 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Models; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Hashing.Store +{ + public class ChunkJoiner( + IReadOnlyChunkStore chunkStore) + { + // Methods. + /// + /// Get data stream from chunks + /// + /// The root chunk reference + /// Optional file where store read data. Necessary if data is >2GB + /// Optional cancellation token + /// The data stream + public async Task GetJoinedChunkDataAsync( + SwarmChunkReference rootChunkReference, + string? fileCachePath = null, + CancellationToken? cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(rootChunkReference, nameof(rootChunkReference)); + + //in memory + if (fileCachePath is null) + { + var dataStream = new MemoryStream(); + + await GetJoinedChunkDataHelperAsync( + rootChunkReference, + dataStream, + cancellationToken ?? CancellationToken.None).ConfigureAwait(false); + + dataStream.Position = 0; + return dataStream; + } + + //file cached + using (var writeDataStream = File.OpenWrite(fileCachePath)) + { + await GetJoinedChunkDataHelperAsync( + rootChunkReference, + writeDataStream, + cancellationToken ?? CancellationToken.None).ConfigureAwait(false); + + await writeDataStream.FlushAsync().ConfigureAwait(false); + } + + return File.OpenRead(fileCachePath); + } + + // Helpers. + private async Task GetJoinedChunkDataHelperAsync( + SwarmChunkReference chunkReference, + Stream dataStream, + CancellationToken cancellationToken) + { + // Read and decrypt chunk data. + var chunk = await chunkStore.GetAsync(chunkReference.Hash).ConfigureAwait(false); + var dataArray = chunk.Data.ToArray(); + chunkReference.EncryptionKey?.EncryptDecrypt(dataArray); + + // Determine if is a data chunk, or an intermediate chunk. + var totalDataLength = SwarmChunk.SpanToLength(chunk.Span.Span); + + //if is data chunk + if (totalDataLength <= SwarmChunk.DataSize) + { + await dataStream.WriteAsync(dataArray, cancellationToken).ConfigureAwait(false); + return; + } + + //else, is intermediate chunk + for (int i = 0; i < dataArray.Length;) + { + //read hash + var childHash = new SwarmHash(dataArray[i..(i + SwarmHash.HashSize)]); + i += SwarmHash.HashSize; + + //read encryption key + XorEncryptKey? childEncryptionKey = null; + if (chunkReference.UseRecursiveEncryption) + { + childEncryptionKey = new XorEncryptKey(dataArray[i..(i + XorEncryptKey.KeySize)]); + i += XorEncryptKey.KeySize; + } + + //add joined data recursively + await GetJoinedChunkDataHelperAsync( + new SwarmChunkReference( + childHash, + childEncryptionKey, + chunkReference.UseRecursiveEncryption), + dataStream, + cancellationToken).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Store/FakeChunkStore.cs b/src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs similarity index 85% rename from src/BeeNet/Hasher/Store/FakeChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs index c2fc5325..5ee901c1 100644 --- a/src/BeeNet/Hasher/Store/FakeChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs @@ -13,17 +13,13 @@ // If not, see . using Etherna.BeeNet.Models; -using System; using System.Collections.Generic; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class FakeChunkStore : IChunkStore { - public Task> GetAllHashesAsync() => - Task.FromResult>(Array.Empty()); - public Task GetAsync(SwarmHash hash) => throw new KeyNotFoundException("Chunk get on a fake chunk store"); diff --git a/src/BeeNet/Hasher/Store/IChunkStore.cs b/src/BeeNet.Util/Hashing/Store/IChunkStore.cs similarity index 77% rename from src/BeeNet/Hasher/Store/IChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/IChunkStore.cs index b071c4f0..05262da2 100644 --- a/src/BeeNet/Hasher/Store/IChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/IChunkStore.cs @@ -13,19 +13,12 @@ // If not, see . using Etherna.BeeNet.Models; -using System.Collections.Generic; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { - public interface IChunkStore + public interface IChunkStore : IReadOnlyChunkStore { - public Task> GetAllHashesAsync(); - - public Task GetAsync(SwarmHash hash); - - public Task TryGetAsync(SwarmHash hash); - /// /// Add a chunk in the store /// diff --git a/src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs b/src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs new file mode 100644 index 00000000..30761fa2 --- /dev/null +++ b/src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs @@ -0,0 +1,26 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Models; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Hashing.Store +{ + public interface IReadOnlyChunkStore + { + public Task GetAsync(SwarmHash hash); + + public Task TryGetAsync(SwarmHash hash); + } +} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Store/IStampStore.cs b/src/BeeNet.Util/Hashing/Store/IStampStore.cs similarity index 95% rename from src/BeeNet/Hasher/Store/IStampStore.cs rename to src/BeeNet.Util/Hashing/Store/IStampStore.cs index 14d2ca32..2a515b27 100644 --- a/src/BeeNet/Hasher/Store/IStampStore.cs +++ b/src/BeeNet.Util/Hashing/Store/IStampStore.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public interface IStampStore { diff --git a/src/BeeNet/Hasher/Store/LocalDirectoryChunkStore.cs b/src/BeeNet.Util/Hashing/Store/LocalDirectoryChunkStore.cs similarity index 99% rename from src/BeeNet/Hasher/Store/LocalDirectoryChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/LocalDirectoryChunkStore.cs index 3b709571..5a1b0cfb 100644 --- a/src/BeeNet/Hasher/Store/LocalDirectoryChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/LocalDirectoryChunkStore.cs @@ -19,7 +19,7 @@ using System.IO; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { /// /// Store chunks in a local directory diff --git a/src/BeeNet/Hasher/Store/MemoryStampStore.cs b/src/BeeNet.Util/Hashing/Store/MemoryStampStore.cs similarity index 97% rename from src/BeeNet/Hasher/Store/MemoryStampStore.cs rename to src/BeeNet.Util/Hashing/Store/MemoryStampStore.cs index d7fda209..e560fc66 100644 --- a/src/BeeNet/Hasher/Store/MemoryStampStore.cs +++ b/src/BeeNet.Util/Hashing/Store/MemoryStampStore.cs @@ -15,7 +15,7 @@ using System; using System.Collections.Concurrent; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class MemoryStampStore : IStampStore { diff --git a/src/BeeNet/Hasher/Store/StampStoreItem.cs b/src/BeeNet.Util/Hashing/Store/StampStoreItem.cs similarity index 94% rename from src/BeeNet/Hasher/Store/StampStoreItem.cs rename to src/BeeNet.Util/Hashing/Store/StampStoreItem.cs index 42d7575a..e864e6eb 100644 --- a/src/BeeNet/Hasher/Store/StampStoreItem.cs +++ b/src/BeeNet.Util/Hashing/Store/StampStoreItem.cs @@ -12,11 +12,10 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Postage; using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class StampStoreItem( PostageBatchId batchId, diff --git a/src/BeeNet/IBeeClient.cs b/src/BeeNet.Util/IBeeClient.cs similarity index 96% rename from src/BeeNet/IBeeClient.cs rename to src/BeeNet.Util/IBeeClient.cs index 5e3cf65a..df2fd1b3 100644 --- a/src/BeeNet/IBeeClient.cs +++ b/src/BeeNet.Util/IBeeClient.cs @@ -36,9 +36,9 @@ public interface IBeeClient Task> AccountingAsync(CancellationToken cancellationToken = default); /// Authenticate - This endpoint is experimental - /// Ok + /// Auth key /// A server side error occurred. - Task AuthenticateAsync(string role, int expiry); + Task AuthenticateAsync(string role, int expiry); /// Buy a new postage batch. /// Amount of BZZ added that the postage batch will have. @@ -79,14 +79,6 @@ Task CheckChunkExistsAsync( SwarmHash hash, CancellationToken cancellationToken = default); - /// Check if content is available - /// Root hash of content (can be of any type: collection, file, chunk) - /// Returns if the content is retrievable - /// A server side error occurred. - Task CheckIsContentAvailableAsync( - SwarmHash hash, - CancellationToken cancellationToken = default); - /// /// Validate pinned chunks integerity /// @@ -123,9 +115,8 @@ Task CreateFeedAsync( /// Pin the root hash with the given reference /// Swarm reference of the root hash - /// Pin already exists, so no operation /// A server side error occurred. - Task CreatePinAsync( + Task CreatePinAsync( SwarmHash hash, CancellationToken cancellationToken = default); @@ -137,17 +128,15 @@ Task CreateTagAsync( /// Remove peer /// Swarm address of peer - /// Disconnected peer /// A server side error occurred. - Task DeletePeerAsync( + Task DeletePeerAsync( string peerAddress, CancellationToken cancellationToken = default); /// Unpin the root hash with the given reference /// Swarm reference of the root hash - /// Unpinning root hash with reference /// A server side error occurred. - Task DeletePinAsync( + Task DeletePinAsync( SwarmHash hash, CancellationToken cancellationToken = default); @@ -174,7 +163,7 @@ Task DeleteTransactionAsync( /// Gas price for transaction /// Transaction hash of the deposit transaction /// A server side error occurred. - Task DepositIntoChequeBookAsync( + Task DepositIntoChequebookAsync( BzzBalance amount, XDaiBalance? gasPrice = null, CancellationToken cancellationToken = default); @@ -199,17 +188,17 @@ Task DilutePostageBatchAsync( /// Get the balances with all known peers including prepaid services /// Own balances with all known peers /// A server side error occurred. - Task> GetAllBalancesAsync(CancellationToken cancellationToken = default); + Task> GetAllBalancesAsync(CancellationToken cancellationToken = default); /// Get last cheques for all peers /// Last cheques /// A server side error occurred. - Task> GetAllChequeBookChequesAsync(CancellationToken cancellationToken = default); + Task> GetAllChequebookChequesAsync(CancellationToken cancellationToken = default); /// Get the past due consumption balances with all known peers /// Own past due consumption balances with all known peers /// A server side error occurred. - Task> GetAllConsumedBalancesAsync(CancellationToken cancellationToken = default); + Task> GetAllConsumedBalancesAsync(CancellationToken cancellationToken = default); /// Get a list of peers /// Returns overlay addresses of connected peers @@ -235,15 +224,15 @@ Task DilutePostageBatchAsync( /// Get all globally available batches that were purchased by all nodes. /// /// - /// Returns an array of all available and currently valid postage batches. + /// Returns a dictionary with owner as keys, and enumerable of currently valid owned postage batches as values. /// A server side error occurred. - Task> GetAllValidPostageBatchesFromAllNodesAsync(CancellationToken cancellationToken = default); + Task>> GetAllValidPostageBatchesFromAllNodesAsync(CancellationToken cancellationToken = default); /// Get the balances with a specific peer including prepaid services /// Swarm address of peer /// Balance with the specific peer /// A server side error occurred. - Task GetBalanceWithPeerAsync( + Task GetBalanceWithPeerAsync( string peerAddress, CancellationToken cancellationToken = default); @@ -284,18 +273,18 @@ Task GetBytesAsync( /// Get the address of the chequebook contract used /// Ethereum address of chequebook contract /// A server side error occurred. - Task GetChequeBookAddressAsync(CancellationToken cancellationToken = default); + Task GetChequebookAddressAsync(CancellationToken cancellationToken = default); /// Get the balance of the chequebook /// Balance of the chequebook /// A server side error occurred. - Task GetChequeBookBalanceAsync(CancellationToken cancellationToken = default); + Task GetChequebookBalanceAsync(CancellationToken cancellationToken = default); /// Get last cashout action for the peer /// Swarm address of peer /// Cashout status /// A server side error occurred. - Task GetChequeBookCashoutForPeerAsync( + Task GetChequebookCashoutForPeerAsync( string peerAddress, CancellationToken cancellationToken = default); @@ -303,7 +292,7 @@ Task GetChequeBookCashoutForPeerAsync( /// Swarm address of peer /// Last cheques /// A server side error occurred. - Task GetChequeBookChequeForPeerAsync( + Task GetChequebookChequeForPeerAsync( string peerId, CancellationToken cancellationToken = default); @@ -329,7 +318,7 @@ Task GetChunkStreamAsync( /// Swarm address of peer /// Past-due consumption balance with the specific peer /// A server side error occurred. - Task GetConsumedBalanceWithPeerAsync( + Task GetConsumedBalanceWithPeerAsync( string peerAddress, CancellationToken cancellationToken = default); @@ -357,7 +346,7 @@ Task GetFeedAsync( /// Specify the timeout for chunk retrieval. The default is 30 seconds. /// Ok /// A server side error occurred. - Task GetFileAsync( + Task GetFileAsync( SwarmAddress address, bool? swarmCache = null, RedundancyStrategy? swarmRedundancyStrategy = null, @@ -482,13 +471,21 @@ Task GetTransactionInfoAsync( /// Get wallet balance for BZZ and xDai /// /// Wallet balance info - Task GetWalletBalance(CancellationToken cancellationToken = default); + Task GetWalletBalance(CancellationToken cancellationToken = default); /// Get configured P2P welcome message /// Welcome message /// A server side error occurred. Task GetWelcomeMessageAsync(CancellationToken cancellationToken = default); + /// Check if content is retrievable + /// Root hash of content (can be of any type: collection, file, chunk) + /// Returns if the content is retrievable + /// A server side error occurred. + Task IsContentRetrievableAsync( + SwarmHash hash, + CancellationToken cancellationToken = default); + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Get all available loggers. @@ -539,6 +536,8 @@ Task RefreshAuthAsync( int expiry, CancellationToken cancellationToken = default); + Task ResolveAddressToChunkReferenceAsync(SwarmAddress address); + /// Reupload a root hash to the network /// Root hash of content (can be of any type: collection, file, chunk) /// Ok @@ -809,7 +808,7 @@ Task WalletWithdrawAsync( /// Gas price for transaction /// Transaction hash of the withdraw transaction /// A server side error occurred. - Task WithdrawFromChequeBookAsync( + Task WithdrawFromChequebookAsync( BzzBalance amount, XDaiBalance? gasPrice = null, CancellationToken cancellationToken = default); diff --git a/src/BeeNet/Manifest/IReadOnlyMantarayManifest.cs b/src/BeeNet.Util/Manifest/IReadOnlyMantarayManifest.cs similarity index 100% rename from src/BeeNet/Manifest/IReadOnlyMantarayManifest.cs rename to src/BeeNet.Util/Manifest/IReadOnlyMantarayManifest.cs diff --git a/src/BeeNet/Manifest/IReadOnlyMantarayNode.cs b/src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs similarity index 94% rename from src/BeeNet/Manifest/IReadOnlyMantarayNode.cs rename to src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs index 9861ed48..0a70e52f 100644 --- a/src/BeeNet/Manifest/IReadOnlyMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs @@ -29,6 +29,6 @@ public interface IReadOnlyMantarayNode // Methods. Task> GetResourceMetadataAsync(string path); - Task ResolveResourceHashAsync(string path); + Task ResolveChunkReferenceAsync(string path); } } \ No newline at end of file diff --git a/src/BeeNet/Manifest/ManifestEntry.cs b/src/BeeNet.Util/Manifest/ManifestEntry.cs similarity index 93% rename from src/BeeNet/Manifest/ManifestEntry.cs rename to src/BeeNet.Util/Manifest/ManifestEntry.cs index dfe06acb..e1071908 100644 --- a/src/BeeNet/Manifest/ManifestEntry.cs +++ b/src/BeeNet.Util/Manifest/ManifestEntry.cs @@ -20,10 +20,12 @@ namespace Etherna.BeeNet.Manifest public class ManifestEntry { // Consts. + public const string ChunkEncryptKeyKey = "ChunkEncryptKey"; public const string ContentTypeKey = "Content-Type"; public const string FilenameKey = "Filename"; public const string WebsiteErrorDocPathKey = "website-error-document"; public const string WebsiteIndexDocPathKey = "website-index-document"; + public const string UseRecursiveEncryptionKey = "recursiveEncrypt"; // Constructor. private ManifestEntry( diff --git a/src/BeeNet/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs similarity index 80% rename from src/BeeNet/Manifest/MantarayManifest.cs rename to src/BeeNet.Util/Manifest/MantarayManifest.cs index 13b8161e..aa68674a 100644 --- a/src/BeeNet/Manifest/MantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/MantarayManifest.cs @@ -12,25 +12,27 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Pipeline; +using Etherna.BeeNet.Hashing.Pipeline; using Etherna.BeeNet.Models; using System; using System.Threading.Tasks; namespace Etherna.BeeNet.Manifest { + public delegate IHasherPipeline BuildHasherPipeline(); + public class MantarayManifest : IReadOnlyMantarayManifest { // Consts. - public const string RootPath = "/"; + public static readonly string RootPath = SwarmAddress.Separator.ToString(); // Fields. - private readonly Func hasherBuilder; + private readonly BuildHasherPipeline hasherBuilder; private readonly MantarayNode _rootNode; // Constructors. public MantarayManifest( - Func hasherBuilder, + BuildHasherPipeline hasherBuilder, bool isEncrypted) : this(hasherBuilder, new MantarayNode(isEncrypted @@ -39,7 +41,7 @@ public MantarayManifest( { } public MantarayManifest( - Func hasherBuilder, + BuildHasherPipeline hasherBuilder, MantarayNode rootNode) { this.hasherBuilder = hasherBuilder; @@ -58,10 +60,10 @@ public void Add(string path, ManifestEntry entry) _rootNode.Add(path, entry); } - public async Task GetHashAsync() + public async Task GetHashAsync() { await _rootNode.ComputeHashAsync(hasherBuilder).ConfigureAwait(false); - return _rootNode.Hash; + return new SwarmChunkReference(_rootNode.Hash, null, false); } } } \ No newline at end of file diff --git a/src/BeeNet/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs similarity index 94% rename from src/BeeNet/Manifest/MantarayNode.cs rename to src/BeeNet.Util/Manifest/MantarayNode.cs index 83663ea7..fa62f486 100644 --- a/src/BeeNet/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -12,9 +12,8 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Epoche; using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Hasher.Pipeline; +using Etherna.BeeNet.Hashing; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -27,9 +26,8 @@ public class MantarayNode : IReadOnlyMantarayNode { // Consts. public const int ForksIndexSize = 32; - public const char PathSeparator = '/'; - public static readonly byte[] Version02Hash = - Keccak256.ComputeHash("mantaray:0.2").Take(VersionHashSize).ToArray(); + public static readonly byte[] Version02Hash = new Hasher().ComputeHash( + "mantaray:0.2"u8.ToArray()).Take(VersionHashSize).ToArray(); public const int VersionHashSize = 31; // Fields. @@ -144,7 +142,7 @@ public void Add(string path, ManifestEntry entry) } } - public async Task ComputeHashAsync(Func hasherPipelineBuilder) + public async Task ComputeHashAsync(BuildHasherPipeline hasherPipelineBuilder) { ArgumentNullException.ThrowIfNull(hasherPipelineBuilder, nameof(hasherPipelineBuilder)); @@ -157,7 +155,8 @@ public async Task ComputeHashAsync(Func hasherPipelineBuilder) // Marshal current node, and set its hash. using var hasherPipeline = hasherPipelineBuilder(); - _hash = await hasherPipeline.HashDataAsync(ToByteArray()).ConfigureAwait(false); + var hashingResult = await hasherPipeline.HashDataAsync(ToByteArray()).ConfigureAwait(false); + _hash = hashingResult.Hash; // Clean forks. _forks.Clear(); @@ -195,7 +194,7 @@ public Task> GetResourceMetadataAsync(string throw new NotImplementedException(); } - public Task ResolveResourceHashAsync(string path) + public Task ResolveChunkReferenceAsync(string path) { //this will be implemented probably into a base class throw new NotImplementedException(); @@ -231,7 +230,7 @@ private byte[] ToByteArray() private void UpdateFlagIsWithPathSeparator(string path) { - if (path.IndexOf(PathSeparator, StringComparison.InvariantCulture) > 0) + if (path.IndexOf(SwarmAddress.Separator, StringComparison.InvariantCulture) > 0) SetNodeTypeFlag(NodeType.WithPathSeparator); else RemoveNodeTypeFlag(NodeType.WithPathSeparator); diff --git a/src/BeeNet/Manifest/MantarayNodeFork.cs b/src/BeeNet.Util/Manifest/MantarayNodeFork.cs similarity index 100% rename from src/BeeNet/Manifest/MantarayNodeFork.cs rename to src/BeeNet.Util/Manifest/MantarayNodeFork.cs diff --git a/src/BeeNet/Manifest/NodeType.cs b/src/BeeNet.Util/Manifest/NodeType.cs similarity index 100% rename from src/BeeNet/Manifest/NodeType.cs rename to src/BeeNet.Util/Manifest/NodeType.cs diff --git a/src/BeeNet/Manifest/ReferencedMantarayManifest.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs similarity index 80% rename from src/BeeNet/Manifest/ReferencedMantarayManifest.cs rename to src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs index c8ad660a..97a2fc90 100644 --- a/src/BeeNet/Manifest/ReferencedMantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using System.Collections.Generic; using System.Threading.Tasks; @@ -26,7 +26,7 @@ public class ReferencedMantarayManifest : IReadOnlyMantarayManifest // Constructors. public ReferencedMantarayManifest( - IChunkStore chunkStore, + IReadOnlyChunkStore chunkStore, SwarmHash rootHash) { _rootNode = new ReferencedMantarayNode(chunkStore, rootHash, null, NodeType.Edge); @@ -43,17 +43,15 @@ public async Task> GetResourceMetadataAsync( if (!_rootNode.IsDecoded) await _rootNode.DecodeFromChunkAsync().ConfigureAwait(false); - return await RootNode.GetResourceMetadataAsync( - address.RelativePath?.ToString() ?? "").ConfigureAwait(false); + return await RootNode.GetResourceMetadataAsync(address.Path).ConfigureAwait(false); } - public async Task ResolveResourceHashAsync(SwarmAddress address) + public async Task ResolveAddressToChunkReferenceAsync(SwarmAddress address) { if (!_rootNode.IsDecoded) await _rootNode.DecodeFromChunkAsync().ConfigureAwait(false); - return await RootNode.ResolveResourceHashAsync( - address.RelativePath?.ToString() ?? "").ConfigureAwait(false); + return await RootNode.ResolveChunkReferenceAsync(address.Path).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/BeeNet/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs similarity index 84% rename from src/BeeNet/Manifest/ReferencedMantarayNode.cs rename to src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index 1b76c02b..6486ecfa 100644 --- a/src/BeeNet/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using Newtonsoft.Json; using System; @@ -26,7 +26,7 @@ namespace Etherna.BeeNet.Manifest public class ReferencedMantarayNode : IReadOnlyMantarayNode { // Fields. - private readonly IChunkStore chunkStore; + private readonly IReadOnlyChunkStore chunkStore; private SwarmHash? _entryHash; private readonly Dictionary _forks = new(); private readonly Dictionary _metadata; @@ -34,7 +34,7 @@ public class ReferencedMantarayNode : IReadOnlyMantarayNode // Constructor. public ReferencedMantarayNode( - IChunkStore chunkStore, + IReadOnlyChunkStore chunkStore, SwarmHash chunkHash, Dictionary? metadata, NodeType nodeTypeFlags) @@ -43,12 +43,20 @@ public ReferencedMantarayNode( Hash = chunkHash; _metadata = metadata ?? new Dictionary(); NodeTypeFlags = nodeTypeFlags; + + // Read metadata. + if (_metadata.TryGetValue(ManifestEntry.ChunkEncryptKeyKey, out var encryptKeyStr)) + EntryEncryptionKey = new XorEncryptKey(encryptKeyStr); + if (_metadata.TryGetValue(ManifestEntry.UseRecursiveEncryptionKey, out var useRecursiveEncrypStr)) + EntryUseRecursiveEncryption = bool.Parse(useRecursiveEncrypStr); } // Properties. + public XorEncryptKey? EntryEncryptionKey { get; } public SwarmHash? EntryHash => IsDecoded ? _entryHash : throw new InvalidOperationException("Node is not decoded from chunk"); + public bool EntryUseRecursiveEncryption { get; } public IReadOnlyDictionary Forks => IsDecoded ? _forks : throw new InvalidOperationException("Node is not decoded from chunk"); @@ -94,17 +102,17 @@ public async Task> GetResourceMetadataAsync( ArgumentNullException.ThrowIfNull(path, nameof(path)); // If the path is empty - if (path.Length == 0) + if (path.Length == 0 || path == SwarmAddress.Separator.ToString()) { //try to lookup for index document suffix - if (!_forks.TryGetValue('/', out var rootFork) || - rootFork.Prefix != "/") + if (!_forks.TryGetValue(SwarmAddress.Separator, out var rootFork) || + rootFork.Prefix != SwarmAddress.Separator.ToString()) throw new KeyNotFoundException($"Final path {path} can't be found"); - if (!rootFork.Node.Metadata.TryGetValue(ManifestEntry.WebsiteIndexDocPathKey, out var suffix)) + if (!rootFork.Node.Metadata.TryGetValue(ManifestEntry.WebsiteIndexDocPathKey, out var indexDocPat)) throw new KeyNotFoundException($"Index document can't be found"); - path += suffix; + path = indexDocPat; } // Find the child fork. @@ -124,26 +132,29 @@ public async Task> GetResourceMetadataAsync( return await fork.Node.GetResourceMetadataAsync(childSubPath).ConfigureAwait(false); } - public async Task ResolveResourceHashAsync(string path) + public async Task ResolveChunkReferenceAsync(string path) { ArgumentNullException.ThrowIfNull(path, nameof(path)); // If the path is empty - if (path.Length == 0) + if (path.Length == 0 || path == SwarmAddress.Separator.ToString()) { //if entry is not null, return it if (EntryHash.HasValue && EntryHash != SwarmHash.Zero) - return EntryHash.Value; + return new SwarmChunkReference( + EntryHash.Value, + EntryEncryptionKey, + EntryUseRecursiveEncryption); //try to lookup for index document suffix - if (!_forks.TryGetValue('/', out var rootFork) || - rootFork.Prefix != "/") + if (!_forks.TryGetValue(SwarmAddress.Separator, out var rootFork) || + rootFork.Prefix != SwarmAddress.Separator.ToString()) throw new KeyNotFoundException($"Final path {path} can't be found"); - if (!rootFork.Node.Metadata.TryGetValue(ManifestEntry.WebsiteIndexDocPathKey, out var suffix)) + if (!rootFork.Node.Metadata.TryGetValue(ManifestEntry.WebsiteIndexDocPathKey, out var indexDocPat)) throw new KeyNotFoundException($"Index document can't be found"); - path += suffix; + path = indexDocPat; } // Find the child fork. @@ -154,7 +165,7 @@ public async Task ResolveResourceHashAsync(string path) if (!fork.Node.IsDecoded) await fork.Node.DecodeFromChunkAsync().ConfigureAwait(false); - return await fork.Node.ResolveResourceHashAsync(path[fork.Prefix.Length..]).ConfigureAwait(false); + return await fork.Node.ResolveChunkReferenceAsync(path[fork.Prefix.Length..]).ConfigureAwait(false); } // Helpers. diff --git a/src/BeeNet/Manifest/ReferencedMantarayNodeFork.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNodeFork.cs similarity index 100% rename from src/BeeNet/Manifest/ReferencedMantarayNodeFork.cs rename to src/BeeNet.Util/Manifest/ReferencedMantarayNodeFork.cs diff --git a/src/BeeNet/Properties/AssemblyInfo.cs b/src/BeeNet.Util/Properties/AssemblyInfo.cs similarity index 89% rename from src/BeeNet/Properties/AssemblyInfo.cs rename to src/BeeNet.Util/Properties/AssemblyInfo.cs index 8bf40376..ae2d241d 100644 --- a/src/BeeNet/Properties/AssemblyInfo.cs +++ b/src/BeeNet.Util/Properties/AssemblyInfo.cs @@ -12,8 +12,6 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using System; using System.Runtime.CompilerServices; -[assembly: CLSCompliant(false)] -[assembly: InternalsVisibleTo("BeeNet.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("BeeNet.Util.UnitTest")] \ No newline at end of file diff --git a/src/BeeNet/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs similarity index 58% rename from src/BeeNet/Services/CalculatorService.cs rename to src/BeeNet.Util/Services/CalculatorService.cs index 7920a9b7..9d4f6e8a 100644 --- a/src/BeeNet/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -12,16 +12,16 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Pipeline; -using Etherna.BeeNet.Hasher.Postage; -using Etherna.BeeNet.Hasher.Signer; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Pipeline; +using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Hashing.Signer; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; using System.IO; -using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Etherna.BeeNet.Services @@ -32,13 +32,15 @@ public async Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { // Checks. - if (indexFilename?.Contains('/', StringComparison.InvariantCulture) == true) + if (indexFilename?.Contains(SwarmAddress.Separator, StringComparison.InvariantCulture) == true) throw new ArgumentException( "Index document suffix must not include slash character", nameof(indexFilename)); @@ -51,6 +53,8 @@ public async Task EvaluateDirectoryUploadAsync( new FakeSigner(), postageStampIssuer, new MemoryStampStore()); + + long totalMissedOptimisticHashing = 0; // Try set index document. if (indexFilename is null && @@ -63,7 +67,9 @@ public async Task EvaluateDirectoryUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt), + encrypt, + 0, + chunkCuncorrency), encrypt); // Iterate through the files in the supplied directory. @@ -77,22 +83,33 @@ public async Task EvaluateDirectoryUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt); + encrypt, + compactLevel, + chunkCuncorrency); var fileContentType = FileContentTypeProvider.GetContentType(file); var fileName = Path.GetFileName(file); using var fileStream = File.OpenRead(file); - var fileHash = await fileHasherPipeline.HashDataAsync(fileStream).ConfigureAwait(false); + var fileHashingResult = await fileHasherPipeline.HashDataAsync(fileStream).ConfigureAwait(false); + totalMissedOptimisticHashing += fileHasherPipeline.MissedOptimisticHashing; // Add file entry to dir manifest. + var fileEntryMetadata = new Dictionary + { + [ManifestEntry.ContentTypeKey] = fileContentType, + [ManifestEntry.FilenameKey] = fileName + }; + if (fileHashingResult.EncryptionKey != null) + fileEntryMetadata.Add(ManifestEntry.ChunkEncryptKeyKey, fileHashingResult.EncryptionKey.ToString()); + if (compactLevel > 0) + fileEntryMetadata.Add(ManifestEntry.UseRecursiveEncryptionKey, true.ToString()); + dirManifest.Add( Path.GetRelativePath(directoryPath, file), - ManifestEntry.NewFile(fileHash, new Dictionary - { - [ManifestEntry.ContentTypeKey] = fileContentType, - [ManifestEntry.FilenameKey] = fileName - })); + ManifestEntry.NewFile( + fileHashingResult.Hash, + fileEntryMetadata)); } // Store website information. @@ -111,11 +128,12 @@ public async Task EvaluateDirectoryUploadAsync( } // Get manifest hash. - var manifestHash = await dirManifest.GetHashAsync().ConfigureAwait(false); + var chunkHashingResult = await dirManifest.GetHashAsync().ConfigureAwait(false); // Return result. return new UploadEvaluationResult( - manifestHash, + chunkHashingResult.Hash, + totalMissedOptimisticHashing, postageStampIssuer); } @@ -123,9 +141,11 @@ public async Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { using var stream = new MemoryStream(data); @@ -133,9 +153,11 @@ public async Task EvaluateFileUploadAsync( stream, fileContentType, fileName, + compactLevel, encrypt, redundancyLevel, postageStampIssuer, + chunkCuncorrency, chunkStore).ConfigureAwait(false); } @@ -143,9 +165,11 @@ public async Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { chunkStore ??= new FakeChunkStore(); @@ -161,9 +185,11 @@ public async Task EvaluateFileUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt); - var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); - fileName ??= fileHash.ToString(); //if missing, set file name with its address + encrypt, + compactLevel, + chunkCuncorrency); + var fileHashingResult = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); + fileName ??= fileHashingResult.Hash.ToString(); //if missing, set file name with its address // Create manifest. var manifest = new MantarayManifest( @@ -171,7 +197,9 @@ public async Task EvaluateFileUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt), + encrypt, + 0, + chunkCuncorrency), encrypt); manifest.Add( @@ -181,26 +209,33 @@ public async Task EvaluateFileUploadAsync( { [ManifestEntry.WebsiteIndexDocPathKey] = fileName, })); + + var fileEntryMetadata = new Dictionary + { + [ManifestEntry.ContentTypeKey] = fileContentType, + [ManifestEntry.FilenameKey] = fileName + }; + if (fileHashingResult.EncryptionKey != null) + fileEntryMetadata.Add(ManifestEntry.ChunkEncryptKeyKey, fileHashingResult.EncryptionKey.ToString()); + if (compactLevel > 0) + fileEntryMetadata.Add(ManifestEntry.UseRecursiveEncryptionKey, true.ToString()); manifest.Add( fileName, ManifestEntry.NewFile( - fileHash, - new Dictionary - { - [ManifestEntry.ContentTypeKey] = fileContentType, - [ManifestEntry.FilenameKey] = fileName - })); + fileHashingResult.Hash, + fileEntryMetadata)); - var manifestHash = await manifest.GetHashAsync().ConfigureAwait(false); + var chunkHashingResult = await manifest.GetHashAsync().ConfigureAwait(false); // Return result. return new UploadEvaluationResult( - manifestHash, + chunkHashingResult.Hash, + fileHasherPipeline.MissedOptimisticHashing, postageStampIssuer); } - public async Task> GetResourceMetadataFromChunksAsync( + public async Task> GetFileMetadataFromChunksAsync( string chunkStoreDirectory, SwarmAddress address) { @@ -213,9 +248,11 @@ public async Task> GetResourceMetadataFromCh return await rootManifest.GetResourceMetadataAsync(address).ConfigureAwait(false); } - public async Task GetResourceStreamFromChunksAsync( + public async Task GetFileStreamFromChunksAsync( string chunkStoreDirectory, - SwarmAddress address) + SwarmAddress address, + string? fileCachePath = null, + CancellationToken? cancellationToken = default) { var chunkStore = new LocalDirectoryChunkStore(chunkStoreDirectory); var chunkJoiner = new ChunkJoiner(chunkStore); @@ -224,14 +261,57 @@ public async Task GetResourceStreamFromChunksAsync( chunkStore, address.Hash); - var resourceHash = await rootManifest.ResolveResourceHashAsync(address).ConfigureAwait(false); + var chunkReference = await rootManifest.ResolveAddressToChunkReferenceAsync(address).ConfigureAwait(false); - var memoryStream = new MemoryStream(); - var resourceData = await chunkJoiner.GetJoinedChunkDataAsync(resourceHash).ConfigureAwait(false); - memoryStream.Write(resourceData.ToArray()); - memoryStream.Position = 0; + return await chunkJoiner.GetJoinedChunkDataAsync( + chunkReference, + fileCachePath, + cancellationToken).ConfigureAwait(false); + } + + public Task WriteDataChunksAsync( + byte[] data, + string outputDirectory, + bool createDirectory = true, + ushort compactLevel = 0, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null) + { + using var stream = new MemoryStream(data); + return WriteDataChunksAsync( + stream, + outputDirectory, + createDirectory, + compactLevel, + encrypt, + redundancyLevel, + chunkCuncorrency); + } + + public async Task WriteDataChunksAsync( + Stream stream, + string outputDirectory, + bool createDirectory = true, + ushort compactLevel = 0, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null) + { + var chunkStore = new LocalDirectoryChunkStore(outputDirectory, createDirectory); + + // Create chunks and get file hash. + using var fileHasherPipeline = HasherPipelineBuilder.BuildNewHasherPipeline( + chunkStore, + new FakePostageStamper(), + redundancyLevel, + encrypt, + compactLevel, + chunkCuncorrency); + var fileHashingResult = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); - return memoryStream; + // Return file hash. + return fileHashingResult.Hash; } } } \ No newline at end of file diff --git a/src/BeeNet/Services/FeedService.cs b/src/BeeNet.Util/Services/FeedService.cs similarity index 96% rename from src/BeeNet/Services/FeedService.cs rename to src/BeeNet.Util/Services/FeedService.cs index d18f8126..7b365f11 100644 --- a/src/BeeNet/Services/FeedService.cs +++ b/src/BeeNet.Util/Services/FeedService.cs @@ -13,8 +13,9 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Feeds; +using Etherna.BeeNet.Hashing; using Etherna.BeeNet.Models; +using Etherna.BeeNet.Models.Feeds; using Nethereum.Hex.HexConvertors.Extensions; using System; using System.IO; @@ -22,17 +23,9 @@ namespace Etherna.BeeNet.Services { - public class FeedService : IFeedService + public class FeedService(IBeeClient gatewayClient) + : IFeedService { - // Fields. - private readonly IBeeClient gatewayClient; - - // Constructor. - public FeedService(IBeeClient gatewayClient) - { - this.gatewayClient = gatewayClient; - } - // Methods. public Task CreateNextEpochFeedChunkAsync( string account, @@ -65,7 +58,7 @@ public async Task CreateNextEpochFeedChunkAsync( // Create new chunk. var chunkPayload = SwarmFeedChunk.BuildChunkPayload(contentPayload, (ulong)at.ToUnixTimeSeconds()); - var chunkHash = SwarmFeedChunk.BuildHash(account, topic, nextEpochIndex); + var chunkHash = SwarmFeedChunk.BuildHash(account, topic, nextEpochIndex, new Hasher()); return new SwarmFeedChunk(nextEpochIndex, chunkPayload, chunkHash); } @@ -160,7 +153,7 @@ public async Task CreateNextEpochFeedChunkAsync( TryGetFeedChunkAsync(account.HexToByteArray(), topic, index); public Task TryGetFeedChunkAsync(byte[] account, byte[] topic, FeedIndexBase index) => - TryGetFeedChunkAsync(SwarmFeedChunk.BuildHash(account, topic, index), index); + TryGetFeedChunkAsync(SwarmFeedChunk.BuildHash(account, topic, index, new Hasher()), index); public async Task TryGetFeedChunkAsync(SwarmHash hash, FeedIndexBase index) { @@ -244,7 +237,7 @@ internal static EpochFeedIndex FindStartingEpochOffline(EpochFeedIndex? knownNea /// A tuple with found chunk (if any) and updated "at" date internal async Task TryFindStartingEpochChunkOnlineAsync(byte[] account, byte[] topic, ulong at, EpochFeedIndex epochIndex) { - // Try get chunk payload on network. + // Try to get chunk payload on network. var chunk = await TryGetFeedChunkAsync(account, topic, epochIndex).ConfigureAwait(false); // If chunk exists and date is prior. diff --git a/src/BeeNet/Services/FileContentTypeProvider.cs b/src/BeeNet.Util/Services/FileContentTypeProvider.cs similarity index 100% rename from src/BeeNet/Services/FileContentTypeProvider.cs rename to src/BeeNet.Util/Services/FileContentTypeProvider.cs diff --git a/src/BeeNet/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs similarity index 57% rename from src/BeeNet/Services/ICalculatorService.cs rename to src/BeeNet.Util/Services/ICalculatorService.cs index 5f17d73f..17216da8 100644 --- a/src/BeeNet/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -12,11 +12,12 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Postage; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Models; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; namespace Etherna.BeeNet.Services @@ -29,18 +30,22 @@ public interface ICalculatorService /// The directory to upload /// The index default file /// The error default file + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer + /// Amount of concurrent chunk hashing tasks. Null to default /// Optional custom chunk store /// The upload evaluation result Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -49,18 +54,22 @@ Task EvaluateDirectoryUploadAsync( /// The file data in byte array /// The file content type /// The file name + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer + /// Amount of concurrent chunk hashing tasks. Null to default /// Optional custom chunk store /// The upload evaluation result Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -69,18 +78,22 @@ Task EvaluateFileUploadAsync( /// The file stream /// The file content type /// The file name + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer + /// Amount of concurrent chunk hashing tasks. Null to default /// Optional custom chunk store /// The upload evaluation result Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -89,7 +102,7 @@ Task EvaluateFileUploadAsync( /// The chunk directory /// Resource address /// Resource metadata - Task> GetResourceMetadataFromChunksAsync( + Task> GetFileMetadataFromChunksAsync( string chunkStoreDirectory, SwarmAddress address); @@ -98,9 +111,53 @@ Task> GetResourceMetadataFromChunksAsync( /// /// The chunk directory /// Resource address + /// Optional file where store read data. Necessary if data is >2GB + /// Optional cancellation token /// Resource stream - Task GetResourceStreamFromChunksAsync( + Task GetFileStreamFromChunksAsync( string chunkStoreDirectory, - SwarmAddress address); + SwarmAddress address, + string? fileCachePath = null, + CancellationToken? cancellationToken = default); + + /// + /// Write data chunks on a local directory, without any manifest + /// + /// The data byte array input + /// The output directory path + /// If true, create if directory doesn't exist + /// Chunk compact level [0, 65535] + /// True to encrypt + /// Choose the redundancy level + /// Amount of concurrent chunk hashing tasks. Null to default + /// The data root hash + Task WriteDataChunksAsync( + byte[] data, + string outputDirectory, + bool createDirectory = true, + ushort compactLevel = 0, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null); + + /// + /// Write data chunks on a local directory, without any manifest + /// + /// The data stream input + /// The output directory path + /// If true, create if directory doesn't exist + /// Chunk compact level [0, 65535] + /// True to encrypt + /// Choose the redundancy level + /// Amount of concurrent chunk hashing tasks. Null to default + /// The data root hash + Task WriteDataChunksAsync( + Stream stream, + string outputDirectory, + bool createDirectory = true, + ushort compactLevel = 0, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null); } } \ No newline at end of file diff --git a/src/BeeNet/Services/IFeedService.cs b/src/BeeNet.Util/Services/IFeedService.cs similarity index 98% rename from src/BeeNet/Services/IFeedService.cs rename to src/BeeNet.Util/Services/IFeedService.cs index 4d9e1bba..0c06a468 100644 --- a/src/BeeNet/Services/IFeedService.cs +++ b/src/BeeNet.Util/Services/IFeedService.cs @@ -12,8 +12,8 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Feeds; using Etherna.BeeNet.Models; +using Etherna.BeeNet.Models.Feeds; using System; using System.Threading.Tasks; diff --git a/src/BeeNet/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs similarity index 79% rename from src/BeeNet/Services/UploadEvaluationResult.cs rename to src/BeeNet.Util/Services/UploadEvaluationResult.cs index 89cb0d5a..715f41ce 100644 --- a/src/BeeNet/Services/UploadEvaluationResult.cs +++ b/src/BeeNet.Util/Services/UploadEvaluationResult.cs @@ -12,7 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . -using Etherna.BeeNet.Hasher.Postage; +using Etherna.BeeNet.Hashing.Postage; using Etherna.BeeNet.Models; using System; @@ -23,9 +23,11 @@ public class UploadEvaluationResult // Constructor. internal UploadEvaluationResult( SwarmHash hash, + long missedOptimisticHashing, IPostageStampIssuer postageStampIssuer) { Hash = hash; + MissedOptimisticHashing = missedOptimisticHashing; PostageStampIssuer = postageStampIssuer; } @@ -34,6 +36,8 @@ internal UploadEvaluationResult( /// The upload resulting hash /// public SwarmHash Hash { get; } + + public long MissedOptimisticHashing { get; } public IPostageStampIssuer PostageStampIssuer { get; } @@ -41,7 +45,7 @@ internal UploadEvaluationResult( /// Total batch space consumed in bytes /// public long ConsumedSize => - PostageStampIssuer.MaxBucketCount * + PostageStampIssuer.Buckets.MaxBucketCollisions * (long)Math.Pow(2, PostageBatch.BucketDepth) * SwarmChunk.DataSize; @@ -49,19 +53,11 @@ internal UploadEvaluationResult( /// Available postage batch space after the upload, with minimum batch depth /// public long RemainingPostageBatchSize => RequiredPostageBatchByteSize - ConsumedSize; - - /// - /// Minimum required postage batch depth to handle the upload - /// - public int RequiredPostageBatchDepth => - Math.Max( - (int)Math.Ceiling(Math.Log2(PostageStampIssuer.MaxBucketCount)) + PostageBatch.BucketDepth, - PostageBatch.MinDepth); /// /// Minimum required postage batch byte size /// public long RequiredPostageBatchByteSize => - (long)(Math.Pow(2, RequiredPostageBatchDepth) * SwarmChunk.DataSize); + (long)(Math.Pow(2, PostageStampIssuer.Buckets.RequiredPostageBatchDepth) * SwarmChunk.DataSize); } } \ No newline at end of file diff --git a/src/BeeNet/BeeClient.cs b/src/BeeNet/BeeClient.cs deleted file mode 100644 index 04f72991..00000000 --- a/src/BeeNet/BeeClient.cs +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Models; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using FileResponse = Etherna.BeeNet.Models.FileResponse; - -#if NET7_0_OR_GREATER -using System.Formats.Tar; -#endif - -namespace Etherna.BeeNet -{ - [SuppressMessage("Design", "CA1054:URI-like parameters should not be strings")] - public class BeeClient : IBeeClient, IDisposable - { - // Consts. - public const int DefaultPort = 1633; - public readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(10); - - // Fields. - private readonly BeeGeneratedClient generatedClient; - private readonly HttpClient httpClient; - - private bool disposed; - - // Constructors. - public BeeClient( - string baseUrl = "http://localhost/", - int port = DefaultPort, - HttpClient? httpClient = null) - : this(BuildBaseUrl(baseUrl, port), httpClient) - { } - - public BeeClient( - Uri baseUrl, - HttpClient? httpClient = null) - { - this.httpClient = httpClient ?? new HttpClient { Timeout = DefaultTimeout }; - - BaseUrl = baseUrl; - generatedClient = new BeeGeneratedClient(this.httpClient) { BaseUrl = BaseUrl.ToString() }; - } - - // Dispose. - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposed) return; - - // Dispose managed resources. - if (disposing) - httpClient.Dispose(); - - disposed = true; - } - - - // Properties. - public Uri BaseUrl { get; } - - // Methods. - public async Task> AccountingAsync( - CancellationToken cancellationToken = default) => - (await generatedClient.AccountingAsync(cancellationToken).ConfigureAwait(false)).PeerData.ToDictionary(i => i.Key, i => new Account(i.Value)); - - public async Task AuthenticateAsync(string role, int expiry) => - new(await generatedClient.AuthAsync( - new Body - { - Role = role, - Expiry = expiry - }).ConfigureAwait(false)); - - public async Task BuyPostageBatchAsync( - BzzBalance amount, - int depth, - string? label = null, - bool? immutable = null, - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - (await generatedClient.StampsPostAsync( - amount.ToPlurString(), - depth, - label, - immutable, - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false)).BatchID; - - public async Task CashoutChequeForPeerAsync( - string peerId, - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - (await generatedClient.ChequebookCashoutPostAsync( - peerId, - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false)).TransactionHash; - - public async Task CheckChunkExistsAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) - { - try - { - await generatedClient.ChunksHeadAsync(hash.ToString(), cancellationToken).ConfigureAwait(false); - return true; - } - catch (BeeNetApiException) - { - return false; - } - } - - public async Task CheckIsContentAvailableAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) => - new(await generatedClient.StewardshipGetAsync((string)hash, cancellationToken).ConfigureAwait(false)); - - public async Task CheckPinsAsync( - SwarmHash? hash, - CancellationToken cancellationToken = default) => - new(await generatedClient.PinsCheckAsync(hash?.ToString(), cancellationToken).ConfigureAwait(false)); - - public async Task ConnectToPeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - (await generatedClient.ConnectAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Address; - - public async Task CreateFeedAsync( - string owner, - string topic, - PostageBatchId batchId, - string? type = null, - bool? swarmPin = null, - CancellationToken cancellationToken = default) => - (await generatedClient.FeedsPostAsync(owner, topic, type, swarmPin, batchId.ToString(), cancellationToken).ConfigureAwait(false)).Reference; - - public async Task CreatePinAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) => - new(await generatedClient.PinsPostAsync((string)hash, cancellationToken).ConfigureAwait(false)); - - public async Task CreateTagAsync( - CancellationToken cancellationToken = default) => - new(await generatedClient.TagsPostAsync(cancellationToken).ConfigureAwait(false)); - - public async Task DeletePeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.PeersDeleteAsync(peerAddress, cancellationToken).ConfigureAwait(false)); - - public async Task DeletePinAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) => - new(await generatedClient.PinsDeleteAsync((string)hash, cancellationToken).ConfigureAwait(false)); - - public Task DeleteTagAsync( - long uid, - CancellationToken cancellationToken = default) => - generatedClient.TagsDeleteAsync(uid, cancellationToken); - - public async Task DeleteTransactionAsync( - string txHash, - XDaiBalance? gasPrice = null, - CancellationToken cancellationToken = default) => - (await generatedClient.TransactionsDeleteAsync( - txHash, - gasPrice?.ToWeiLong(), - cancellationToken).ConfigureAwait(false)).TransactionHash; - - public async Task DepositIntoChequeBookAsync( - BzzBalance amount, - XDaiBalance? gasPrice = null, - CancellationToken cancellationToken = default) => - (await generatedClient.ChequebookDepositAsync( - amount.ToPlurLong(), - gasPrice?.ToWeiLong(), - cancellationToken).ConfigureAwait(false)).TransactionHash; - - public async Task DilutePostageBatchAsync( - PostageBatchId batchId, - int depth, - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - (await generatedClient.StampsDiluteAsync( - batchId.ToString(), - depth, - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false)).BatchID; - - public async Task GetAddressesAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.AddressesAsync(cancellationToken).ConfigureAwait(false)); - - public async Task> GetAllBalancesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.BalancesGetAsync(cancellationToken).ConfigureAwait(false)).Balances.Select(i => new PeerBalance(i)); - - public async Task> GetAllChequeBookChequesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.ChequebookChequeGetAsync(cancellationToken).ConfigureAwait(false)).Lastcheques.Select(i => new ChequebookChequeGet(i)); - - public async Task> GetAllConsumedBalancesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.ConsumedGetAsync(cancellationToken).ConfigureAwait(false)).Balances.Select(i => new PeerBalance(i)); - - public async Task> GetAllPeerAddressesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.PeersGetAsync(cancellationToken).ConfigureAwait(false)).Peers.Select(i => i.Address); - - public async Task> GetAllPinsAsync(CancellationToken cancellationToken = default) => - (await generatedClient.PinsGetAsync(cancellationToken).ConfigureAwait(false)).Reference - .Select(h => new SwarmHash(h)); - - public async Task GetAllSettlementsAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.SettlementsGetAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetAllTimeSettlementsAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.TimesettlementsAsync(cancellationToken).ConfigureAwait(false)); - - public async Task> GetAllValidPostageBatchesFromAllNodesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.BatchesAsync(cancellationToken).ConfigureAwait(false)).Batches.Select(i => new PostageBatchShort(i)); - - public async Task GetBalanceWithPeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.BalancesGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); - - public async Task GetBytesAsync( - SwarmHash hash, - bool? swarmCache = null, - RedundancyStrategy? swarmRedundancyStrategy = null, - bool? swarmRedundancyFallbackMode = null, - string? swarmChunkRetrievalTimeout = null, - CancellationToken cancellationToken = default) => - (await generatedClient.BytesGetAsync( - (string)hash, - swarmCache, - (SwarmRedundancyStrategy?)swarmRedundancyStrategy, - swarmRedundancyFallbackMode, - swarmChunkRetrievalTimeout, - cancellationToken).ConfigureAwait(false)).Stream; - - public Task GetBytesHeadAsync(SwarmHash hash, CancellationToken cancellationToken = default) => - generatedClient.BytesHeadAsync(hash.ToString(), cancellationToken); - - public async Task> GetBlocklistedPeerAddressesAsync(CancellationToken cancellationToken = default) => - (await generatedClient.BlocklistAsync(cancellationToken).ConfigureAwait(false)).Select(i => i.Address.Address1); - - public async Task GetChainStateAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.ChainstateAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetChequeBookAddressAsync(CancellationToken cancellationToken = default) => - (await generatedClient.ChequebookAddressAsync(cancellationToken).ConfigureAwait(false)).ChequebookAddress; - - public async Task GetChequeBookBalanceAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.ChequebookBalanceAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetChequeBookCashoutForPeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.ChequebookCashoutGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); - - public async Task GetChequeBookChequeForPeerAsync( - string peerId, - CancellationToken cancellationToken = default) => - new(await generatedClient.ChequebookChequeGetAsync(peerId, cancellationToken).ConfigureAwait(false)); - - public async Task GetChunkAsync( - SwarmHash hash, - bool? swarmCache = null, - CancellationToken cancellationToken = default) - { - var chunkDto = await generatedClient.ChunksGetAsync((string)hash, swarmCache, cancellationToken).ConfigureAwait(false); - using var memoryStream = new MemoryStream(); - await chunkDto.Stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); - var data = memoryStream.ToArray(); - return new SwarmChunk(hash, data); - } - - public async Task GetChunkStreamAsync( - SwarmHash hash, - bool? swarmCache = null, - CancellationToken cancellationToken = default) => - (await generatedClient.ChunksGetAsync(hash.ToString(), swarmCache, cancellationToken).ConfigureAwait(false)).Stream; - - public async Task GetConsumedBalanceWithPeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.ConsumedGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); - - public async Task GetFeedAsync( - string owner, - string topic, - int? at = null, - int? after = null, - string? type = null, - CancellationToken cancellationToken = default) => - (await generatedClient.FeedsGetAsync(owner, topic, at, after, type, cancellationToken).ConfigureAwait(false)).Reference; - - public async Task GetFileAsync( - SwarmAddress address, - bool? swarmCache = null, - RedundancyStrategy? swarmRedundancyStrategy = null, - bool? swarmRedundancyFallbackMode = null, - string? swarmChunkRetrievalTimeout = null, - CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - - return address.RelativePath is null - ? new(await generatedClient.BzzGetAsync( - address.Hash.ToString(), - swarmCache, - (SwarmRedundancyStrategy2?)swarmRedundancyStrategy, - swarmRedundancyFallbackMode, - swarmChunkRetrievalTimeout, - cancellationToken).ConfigureAwait(false)) - : new(await generatedClient.BzzGetAsync( - address.Hash.ToString(), - address.RelativePath.ToString(), - (SwarmRedundancyStrategy3?)swarmRedundancyStrategy, - swarmRedundancyFallbackMode, - swarmChunkRetrievalTimeout, - cancellationToken).ConfigureAwait(false)); - } - - public Task GetFileHeadAsync(SwarmHash hash, CancellationToken cancellationToken = default) => - generatedClient.BzzHeadAsync((string)hash, cancellationToken); - - public async Task GetHealthAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.HealthAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetNodeInfoAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.NodeAsync(cancellationToken).ConfigureAwait(false)); - - public async Task> GetOwnedPostageBatchesByNodeAsync(CancellationToken cancellationToken = default) => - (await generatedClient.StampsGetAsync(cancellationToken).ConfigureAwait(false)).Stamps.Select(i => new PostageBatch(i)); - - public async Task> GetPendingTransactionsAsync(CancellationToken cancellationToken = default) => - (await generatedClient.TransactionsGetAsync(cancellationToken).ConfigureAwait(false)).PendingTransactions.Select(i => new TxInfo(i)); - - public async Task GetPinStatusAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) => - (await generatedClient.PinsGetAsync((string)hash, cancellationToken).ConfigureAwait(false)).Reference; - - public async Task GetPostageBatchAsync( - PostageBatchId batchId, - CancellationToken cancellationToken = default) => - new(await generatedClient.StampsGetAsync(batchId.ToString(), cancellationToken).ConfigureAwait(false)); - - public async Task GetReserveCommitmentAsync(int depth, string anchor1, string anchor2, - CancellationToken cancellationToken = default) => - new(await generatedClient.RchashAsync(depth, anchor1, anchor2, cancellationToken).ConfigureAwait(false)); - - public async Task GetReserveStateAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.ReservestateAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetSettlementsWithPeerAsync( - string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.SettlementsGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); - - public async Task GetStampsBucketsForBatchAsync( - PostageBatchId batchId, - CancellationToken cancellationToken = default) => - new(await generatedClient.StampsBucketsAsync(batchId.ToString(), cancellationToken).ConfigureAwait(false)); - - public async Task GetSwarmTopologyAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.TopologyAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetTagInfoAsync( - long uid, - CancellationToken cancellationToken = default) => - new(await generatedClient.TagsGetAsync(uid, cancellationToken).ConfigureAwait(false)); - - public async Task> GetTagsListAsync( - int? offset = null, - int? limit = null, - CancellationToken cancellationToken = default) => - ((await generatedClient.TagsGetAsync(offset, limit, cancellationToken).ConfigureAwait(false)).Tags ?? Array.Empty()).Select(i => new TagInfo(i)); - - public async Task GetTransactionInfoAsync( - string txHash, - CancellationToken cancellationToken = default) => - new(await generatedClient.TransactionsGetAsync(txHash, cancellationToken).ConfigureAwait(false)); - - public async Task GetWalletBalance(CancellationToken cancellationToken = default) => - new(await generatedClient.WalletAsync(cancellationToken).ConfigureAwait(false)); - - public async Task GetWelcomeMessageAsync(CancellationToken cancellationToken = default) => - (await generatedClient.WelcomeMessageGetAsync(cancellationToken).ConfigureAwait(false)).WelcomeMessage; - - public async Task LoggersGetAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.LoggersGetAsync(cancellationToken).ConfigureAwait(false)); - - public async Task LoggersGetAsync(string exp, CancellationToken cancellationToken = default) => - new(await generatedClient.LoggersGetAsync(exp, cancellationToken).ConfigureAwait(false)); - - public async Task LoggersPutAsync(string exp, CancellationToken cancellationToken = default) => - await generatedClient.LoggersPutAsync(exp, cancellationToken).ConfigureAwait(false); - - public async Task RebroadcastTransactionAsync( - string txHash, - CancellationToken cancellationToken = default) => - (await generatedClient.TransactionsPostAsync(txHash, cancellationToken).ConfigureAwait(false)).TransactionHash; - - public async Task RedistributionStateAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.RedistributionstateAsync(cancellationToken).ConfigureAwait(false)); - - public async Task RefreshAuthAsync( - string role, - int expiry, - CancellationToken cancellationToken = default) => - (await generatedClient.RefreshAsync( - new Body2 - { - Role = role, - Expiry = expiry - }, - cancellationToken).ConfigureAwait(false)).Key; - - public Task ReuploadContentAsync( - SwarmHash hash, - CancellationToken cancellationToken = default) => - generatedClient.StewardshipPutAsync((string)hash, cancellationToken: cancellationToken); - - public Task SendPssAsync( - string topic, - string targets, - PostageBatchId batchId, - string? recipient = null, - CancellationToken cancellationToken = default) => - generatedClient.PssSendAsync(topic, targets, batchId.ToString(), recipient, cancellationToken); - - public Task SetWelcomeMessageAsync( - string welcomeMessage, - CancellationToken cancellationToken = default) => - generatedClient.WelcomeMessagePostAsync( - new Body4 - { - WelcomeMessage = welcomeMessage - }, - cancellationToken); - - public async Task StakeDeleteAsync( - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - await generatedClient.StakeDeleteAsync( - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false); - - public async Task StakeGetAsync(CancellationToken cancellationToken = default) => - await generatedClient.StakeGetAsync(cancellationToken).ConfigureAwait(false); - - public async Task StakePostAsync( - BzzBalance amount, - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - await generatedClient.StakePostAsync( - amount.ToPlurString(), - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false); - - public async Task StatusNodeAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.StatusAsync(cancellationToken).ConfigureAwait(false)); - - public async Task> StatusPeersAsync(CancellationToken cancellationToken = default) => - (await generatedClient.StatusPeersAsync(cancellationToken).ConfigureAwait(false)).Stamps.Select(p => new StatusNode(p)); - - public Task SubscribeToPssAsync( - string topic, - CancellationToken cancellationToken = default) => - generatedClient.PssSubscribeAsync(topic, cancellationToken); - - public async Task TopUpPostageBatchAsync( - PostageBatchId batchId, - BzzBalance amount, - XDaiBalance? gasPrice = null, - long? gasLimit = null, - CancellationToken cancellationToken = default) => - (await generatedClient.StampsTopupAsync( - batchId.ToString(), - amount.ToPlurLong(), - gasPrice?.ToWeiLong(), - gasLimit, - cancellationToken).ConfigureAwait(false)).BatchID; - - public async Task TryConnectToPeerAsync( - string peerId, - CancellationToken cancellationToken = default) => - (await generatedClient.PingpongAsync(peerId, cancellationToken).ConfigureAwait(false)).Rtt; - - public Task UpdateTagAsync( - long uid, - SwarmHash? hash = null, - CancellationToken cancellationToken = default) => - generatedClient.TagsPatchAsync( - uid, - hash.HasValue ? - new Body3 { Address = hash.Value.ToString() } : - null, - cancellationToken); - - public async Task UploadChunkAsync(PostageBatchId batchId, - Stream chunkData, - bool swarmPin = false, - bool swarmDeferredUpload = true, - long? swarmTag = null, - CancellationToken cancellationToken = default) => - (await generatedClient.ChunksPostAsync( - swarmTag, - batchId.ToString(), - chunkData, - cancellationToken).ConfigureAwait(false)).Reference; - - public Task UploadChunksStreamAsync( - PostageBatchId batchId, - int? swarmTag = null, - bool? swarmPin = null, - CancellationToken cancellationToken = default) => - generatedClient.ChunksStreamAsync(swarmTag, batchId.ToString(), cancellationToken); - - public async Task UploadBytesAsync( - PostageBatchId batchId, - Stream body, - int? swarmTag = null, - bool? swarmPin = null, - bool? swarmEncrypt = null, - bool? swarmDeferredUpload = null, - RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, - CancellationToken cancellationToken = default) => - (await generatedClient.BytesPostAsync( - swarm_postage_batch_id: batchId.ToString(), - swarm_tag: swarmTag, - swarm_pin: swarmPin, - swarm_deferred_upload: swarmDeferredUpload, - swarm_encrypt: swarmEncrypt, - swarm_redundancy_level: (int)swarmRedundancyLevel, - body: body, - cancellationToken).ConfigureAwait(false)).Reference; - -#if NET7_0_OR_GREATER - public async Task UploadDirectoryAsync( - PostageBatchId batchId, - string directoryPath, - int? swarmTag = null, - bool? swarmPin = null, - bool? swarmEncrypt = null, - string? swarmIndexDocument = null, - string? swarmErrorDocument = null, - bool? swarmDeferredUpload = null, - RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, - CancellationToken cancellationToken = default) - { - // Create tar file. - using var memoryStream = new MemoryStream(); - await TarFile.CreateFromDirectoryAsync(directoryPath, memoryStream, false, cancellationToken).ConfigureAwait(false); - memoryStream.Position = 0; - - // Try set index document. - if (swarmIndexDocument is null && - File.Exists(Path.Combine(directoryPath, "index.html"))) - swarmIndexDocument = "index.html"; - - // Upload directory. - return (await generatedClient.BzzPostAsync( - new FileParameter(memoryStream, null, "application/x-tar"), - swarm_tag: swarmTag, - swarm_pin: swarmPin, - swarm_encrypt: swarmEncrypt, - swarm_collection: true, - swarm_index_document: swarmIndexDocument, - swarm_error_document: swarmErrorDocument, - swarm_postage_batch_id: batchId.ToString(), - swarm_deferred_upload: swarmDeferredUpload, - swarm_redundancy_level: (SwarmRedundancyLevel)swarmRedundancyLevel, - cancellationToken: cancellationToken).ConfigureAwait(false)).Reference; - } -#endif - - public async Task UploadFileAsync( - PostageBatchId batchId, - Stream content, - string? name = null, - string? contentType = null, - bool isFileCollection = false, - int? swarmTag = null, - bool? swarmPin = null, - bool? swarmEncrypt = null, - string? swarmIndexDocument = null, - string? swarmErrorDocument = null, - bool? swarmDeferredUpload = null, - RedundancyLevel swarmRedundancyLevel = RedundancyLevel.None, - CancellationToken cancellationToken = default) - { - return (await generatedClient.BzzPostAsync( - new FileParameter(content, name, contentType), - swarm_tag: swarmTag, - swarm_pin: swarmPin, - swarm_encrypt: swarmEncrypt, - swarm_collection: isFileCollection, - swarm_index_document: swarmIndexDocument, - swarm_error_document: swarmErrorDocument, - swarm_postage_batch_id: batchId.ToString(), - swarm_deferred_upload: swarmDeferredUpload, - swarm_redundancy_level: (SwarmRedundancyLevel)swarmRedundancyLevel, - cancellationToken: cancellationToken).ConfigureAwait(false)).Reference; - } - - public async Task UploadSocAsync( - string owner, - string id, - string sig, - PostageBatchId batchId, - Stream body, - bool? swarmPin = null, - CancellationToken cancellationToken = default) => - (await generatedClient.SocAsync( - owner, - id, - sig, - batchId.ToString(), - body, - swarmPin, - cancellationToken).ConfigureAwait(false)).Reference; - - public async Task WalletWithdrawAsync( - BzzBalance amount, - string address, - XDaiBalance coin, - CancellationToken cancellationToken = default) => - (await generatedClient.WalletWithdrawAsync( - amount.ToPlurString(), - address, - coin.ToWeiString(), - cancellationToken).ConfigureAwait(false)).TransactionHash; - - public async Task WithdrawFromChequeBookAsync( - BzzBalance amount, - XDaiBalance? gasPrice = null, - CancellationToken cancellationToken = default) => - (await generatedClient.ChequebookWithdrawAsync( - amount.ToPlurLong(), - gasPrice?.ToWeiLong(), - cancellationToken).ConfigureAwait(false)).TransactionHash; - - // Helpers. - private static Uri BuildBaseUrl(string url, int port) - { - var normalizedUrl = url; - if (normalizedUrl.Last() != '/') - normalizedUrl += '/'; - - var baseUrl = ""; - - var urlRegex = new Regex(@"^((?\w+)://)?(?[^/:]+)", - RegexOptions.None, TimeSpan.FromMilliseconds(150)); - var urlMatch = urlRegex.Match(normalizedUrl); - - if (!urlMatch.Success) - throw new ArgumentException("Url is not valid", nameof(url)); - - if (!string.IsNullOrEmpty(urlMatch.Groups["proto"].Value)) - baseUrl += urlMatch.Groups["proto"].Value + "://"; - - baseUrl += $"{urlMatch.Groups["host"].Value}:{port}"; - - return new Uri(baseUrl); - } - } -} \ No newline at end of file diff --git a/src/BeeNet/GlobalSuppressions.cs b/src/BeeNet/GlobalSuppressions.cs deleted file mode 100644 index 2a923b49..00000000 --- a/src/BeeNet/GlobalSuppressions.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Usage", "CA1707:Remove the underscores from namespace name", Justification = "Version number should containt underscores", Scope = "namespaceanddescendants", Target = "~N:Etherna.BeeNet.Clients")] diff --git a/src/BeeNet/Hasher/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet/Hasher/Pipeline/ChunkBmtPipelineStage.cs deleted file mode 100644 index 7d81bb9d..00000000 --- a/src/BeeNet/Hasher/Pipeline/ChunkBmtPipelineStage.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Hasher.Bmt; -using Etherna.BeeNet.Models; -using System; -using System.Threading.Tasks; - -namespace Etherna.BeeNet.Hasher.Pipeline -{ - /// - /// Calculate hash of each chunk - /// - internal sealed class ChunkBmtPipelineStage( - IHasherPipelineStage nextStage) - : IHasherPipelineStage - { - // Dispose. - public void Dispose() - { - nextStage.Dispose(); - } - - // Methods. - public async Task FeedAsync(HasherPipelineFeedArgs args) - { - if (args.Data.Length < SwarmChunk.SpanSize) - throw new InvalidOperationException("Data can't be shorter than span size here"); - if (args.Data.Length > SwarmChunk.SpanAndDataSize) - throw new InvalidOperationException("Data can't be longer than chunk + span size here"); - - args.Hash = SwarmChunkBmtHasher.Hash( - args.Data[..SwarmChunk.SpanSize].ToArray(), - args.Data[SwarmChunk.SpanSize..].ToArray()); - - await nextStage.FeedAsync(args).ConfigureAwait(false); - } - - public Task SumAsync() => nextStage.SumAsync(); - } -} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet/Hasher/Pipeline/ChunkFeederPipelineStage.cs deleted file mode 100644 index 12ddf915..00000000 --- a/src/BeeNet/Hasher/Pipeline/ChunkFeederPipelineStage.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Models; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Etherna.BeeNet.Hasher.Pipeline -{ - /// - /// Produce chunked data with span prefix for successive stages. - /// Also controls the parallelism on chunk elaboration - /// - internal sealed class ChunkFeederPipelineStage( - IHasherPipelineStage nextStage, - int chunkConcurrency) - : IHasherPipeline - { - // Fields. - private readonly List nextStageTasks = new(); - private readonly SemaphoreSlim semaphore = new(chunkConcurrency, chunkConcurrency); - - private long passedBytes; - - // Constructor. - public ChunkFeederPipelineStage(IHasherPipelineStage nextStage) - : this(nextStage, Environment.ProcessorCount) - { } - - // Dispose. - public void Dispose() - { - nextStage.Dispose(); - semaphore.Dispose(); - } - - // Properties. - public bool IsUsable { get; private set; } = true; - - // Methods. - public async Task HashDataAsync(byte[] data) - { - ArgumentNullException.ThrowIfNull(data, nameof(data)); - - using var memoryStream = new MemoryStream(data); - return await HashDataAsync(memoryStream).ConfigureAwait(false); - } - - public async Task HashDataAsync(Stream dataStream) - { - ArgumentNullException.ThrowIfNull(dataStream, nameof(dataStream)); - - if (!IsUsable) - throw new InvalidOperationException("Pipeline has already been used"); - - // Make it no more usable. - IsUsable = false; - - // Slicing the stream permits to avoid to load all the stream in memory at the same time. - var chunkBuffer = new byte[SwarmChunk.DataSize]; - int chunkReadSize; - do - { - chunkReadSize = await dataStream.ReadAsync(chunkBuffer).ConfigureAwait(false); - - if (chunkReadSize > 0 || //write only chunks with data - passedBytes == 0) //or if the first and only one is empty - { - // Copy read data from buffer to a new chunk data byte[]. Include also span - var chunkData = new byte[SwarmChunk.SpanSize + chunkReadSize]; - chunkBuffer.AsSpan(0, chunkReadSize).CopyTo(chunkData.AsSpan(SwarmChunk.SpanSize)); - - // Write chunk span. - SwarmChunk.WriteSpan( - chunkData.AsSpan(0, SwarmChunk.SpanSize), - (ulong)chunkReadSize); - - // Invoke next stage with parallelism on chunks. - await semaphore.WaitAsync().ConfigureAwait(false); - var feedArgs = new HasherPipelineFeedArgs( - span: chunkData[..SwarmChunk.SpanSize], - data: chunkData, - numberId: passedBytes / SwarmChunk.DataSize); - nextStageTasks.Add( - Task.Run(async () => - { - try - { - await nextStage.FeedAsync(feedArgs).ConfigureAwait(false); - } - finally - { - semaphore.Release(); - } - })); - - passedBytes += chunkReadSize; - } - } while (chunkReadSize == SwarmChunk.DataSize); - - // Wait the end of all chunk computation. - await Task.WhenAll(nextStageTasks).ConfigureAwait(false); - - return await nextStage.SumAsync().ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/src/BeeNet/Hasher/Store/ChunkJoiner.cs b/src/BeeNet/Hasher/Store/ChunkJoiner.cs deleted file mode 100644 index 01009250..00000000 --- a/src/BeeNet/Hasher/Store/ChunkJoiner.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Models; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Etherna.BeeNet.Hasher.Store -{ - public class ChunkJoiner - { - // Fields. - private readonly IChunkStore chunkStore; - - // Constructor. - public ChunkJoiner(IChunkStore chunkStore) - { - this.chunkStore = chunkStore; - } - - // Methods. - public async Task> GetJoinedChunkDataAsync(SwarmHash hash) - { - var chunk = await chunkStore.GetAsync(hash).ConfigureAwait(false); - var totalDataLength = SwarmChunk.SpanToLength(chunk.Span.Span); - - if (totalDataLength <= SwarmChunk.DataSize) - return chunk.Data.ToArray(); - - var joinedData = new List(); - - for (int i = 0; i < chunk.Data.Length; i += SwarmHash.HashSize) - { - var childHash = new SwarmHash(chunk.Data[i..(i + SwarmHash.HashSize)].ToArray()); - joinedData.AddRange(await GetJoinedChunkDataAsync(childHash).ConfigureAwait(false)); - } - - return joinedData; - } - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/Account.cs b/src/BeeNet/Models/Account.cs deleted file mode 100644 index 2a262d5d..00000000 --- a/src/BeeNet/Models/Account.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class Account - { - // Constructors. - internal Account(Clients.Anonymous3 value) - { - ArgumentNullException.ThrowIfNull(value, nameof(value)); - - Balance = BzzBalance.FromPlurString(value.Balance); - ThresholdReceived = BzzBalance.FromPlurString(value.ThresholdReceived); - ThresholdGiven = BzzBalance.FromPlurString(value.ThresholdGiven); - SurplusBalance = BzzBalance.FromPlurString(value.SurplusBalance); - ReservedBalance = BzzBalance.FromPlurString(value.ReservedBalance); - ShadowReservedBalance = BzzBalance.FromPlurString(value.ShadowReservedBalance); - GhostBalance = BzzBalance.FromPlurString(value.GhostBalance); - } - - // Properties. - public BzzBalance Balance { get; set; } - public BzzBalance ThresholdReceived { get; set; } - public BzzBalance ThresholdGiven { get; set; } - public BzzBalance SurplusBalance { get; set; } - public BzzBalance ReservedBalance { get; set; } - public BzzBalance ShadowReservedBalance { get; set; } - public BzzBalance GhostBalance { get; set; } - } -} diff --git a/src/BeeNet/Models/ChequePayment.cs b/src/BeeNet/Models/ChequePayment.cs deleted file mode 100644 index daf0f1c0..00000000 --- a/src/BeeNet/Models/ChequePayment.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class ChequePayment - { - // Constructors. - internal ChequePayment(LastCashedCheque lastCashedCheque) - { - ArgumentNullException.ThrowIfNull(lastCashedCheque, nameof(lastCashedCheque)); - - Beneficiary = lastCashedCheque.Beneficiary; - Chequebook = lastCashedCheque.Chequebook; - Payout = BzzBalance.FromPlurString(lastCashedCheque.Payout); - } - internal ChequePayment(Lastreceived lastReceived) - { - ArgumentNullException.ThrowIfNull(lastReceived, nameof(lastReceived)); - - Beneficiary = lastReceived.Beneficiary; - Chequebook = lastReceived.Chequebook; - Payout = BzzBalance.FromPlurString(lastReceived.Payout); - } - internal ChequePayment(Lastreceived2 lastReceived) - { - ArgumentNullException.ThrowIfNull(lastReceived, nameof(lastReceived)); - - Beneficiary = lastReceived.Beneficiary; - Chequebook = lastReceived.Chequebook; - Payout = BzzBalance.FromPlurString(lastReceived.Payout); - } - internal ChequePayment(Lastsent lastsent) - { - ArgumentNullException.ThrowIfNull(lastsent, nameof(lastsent)); - - Beneficiary = lastsent.Beneficiary; - Chequebook = lastsent.Chequebook; - Payout = BzzBalance.FromPlurString(lastsent.Payout); - } - internal ChequePayment(Lastsent2 lastsent) - { - ArgumentNullException.ThrowIfNull(lastsent, nameof(lastsent)); - - Beneficiary = lastsent.Beneficiary; - Chequebook = lastsent.Chequebook; - Payout = BzzBalance.FromPlurString(lastsent.Payout); - } - - // Properties. - public string Beneficiary { get; } - public string Chequebook { get; } - public BzzBalance Payout { get; } - } -} diff --git a/src/BeeNet/Models/ChequebookCashoutGet.cs b/src/BeeNet/Models/ChequebookCashoutGet.cs deleted file mode 100644 index 623a6149..00000000 --- a/src/BeeNet/Models/ChequebookCashoutGet.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class ChequebookCashoutGet - { - // Constructors. - internal ChequebookCashoutGet(Clients.Response41 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Peer = response.Peer; - LastCashedCheque = response.LastCashedCheque is not null ? new ChequePayment(response.LastCashedCheque) : null; - TransactionHash = response.TransactionHash; - Result = new ResultChequebook(response.Result); - UncashedAmount = BzzBalance.FromPlurString(response.UncashedAmount); - } - - // Properties. - public string Peer { get; } - public ChequePayment? LastCashedCheque { get; } - public string TransactionHash { get; } - public ResultChequebook Result { get; } - public BzzBalance UncashedAmount { get; } - } - -} diff --git a/src/BeeNet/Models/ChequebookChequeGet.cs b/src/BeeNet/Models/ChequebookChequeGet.cs deleted file mode 100644 index af9a7c5a..00000000 --- a/src/BeeNet/Models/ChequebookChequeGet.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class ChequebookChequeGet - { - // Constructors - internal ChequebookChequeGet(Clients.Lastcheques response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Peer = response.Peer; - LastReceived = response.Lastreceived is not null ? new ChequePayment(response.Lastreceived) : null; - LastSent = response.Lastsent is not null ? new ChequePayment(response.Lastsent) : null; - } - - internal ChequebookChequeGet(Clients.Response43 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Peer = response.Peer; - LastReceived = response.Lastreceived is not null ? new ChequePayment(response.Lastreceived) : null; - LastSent = response.Lastsent is not null ? new ChequePayment(response.Lastsent) : null; - } - - // Properties. - public string Peer { get; } - public ChequePayment? LastReceived { get; } - public ChequePayment? LastSent { get; } - } -} diff --git a/src/BeeNet/Models/ConnectedPeers.cs b/src/BeeNet/Models/ConnectedPeers.cs deleted file mode 100644 index 41645152..00000000 --- a/src/BeeNet/Models/ConnectedPeers.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class ConnectedPeers - { - // Constructors. - internal ConnectedPeers(Clients.ConnectedPeers connectedPeers) - { - ArgumentNullException.ThrowIfNull(connectedPeers, nameof(connectedPeers)); - - Address = connectedPeers.Address; - LastSeenTimestamp = connectedPeers.Metrics.LastSeenTimestamp; - SessionConnectionRetry = connectedPeers.Metrics.SessionConnectionRetry; - ConnectionTotalDuration = connectedPeers.Metrics.ConnectionTotalDuration; - SessionConnectionDuration = connectedPeers.Metrics.SessionConnectionDuration; - SessionConnectionDirection = connectedPeers.Metrics.SessionConnectionDirection; - LatencyEWMA = connectedPeers.Metrics.LatencyEWMA; - } - - // Properties. - public string Address { get; } - public int LastSeenTimestamp { get; } - public int SessionConnectionRetry { get; } - public double ConnectionTotalDuration { get; } - public double SessionConnectionDuration { get; } - public string SessionConnectionDirection { get; } - public int LatencyEWMA { get; } - } - -} diff --git a/src/BeeNet/Models/Health.cs b/src/BeeNet/Models/Health.cs deleted file mode 100644 index c2548d73..00000000 --- a/src/BeeNet/Models/Health.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class Health - { - // Constructors. - internal Health(Clients.Response9 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - StatusIsOk = response.Status switch - { - Clients.Response9Status.Ok => true, - Clients.Response9Status.Nok => false, - _ => throw new InvalidOperationException() - }; - Version = response.Version; - ApiVersion = response.ApiVersion; - DebugApiVersion = response.DebugApiVersion; - } - - internal Health(Clients.Response21 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - StatusIsOk = response.Status switch - { - Clients.Response21Status.Ok => true, - Clients.Response21Status.Nok => false, - _ => throw new InvalidOperationException() - }; - Version = response.Version; - ApiVersion = response.ApiVersion; - DebugApiVersion = response.DebugApiVersion; - } - - internal Health(Clients.Response40 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - StatusIsOk = response.Status switch - { - Clients.Response40Status.Ok => true, - Clients.Response40Status.Nok => false, - _ => throw new InvalidOperationException() - }; - Version = response.Version; - ApiVersion = response.ApiVersion; - DebugApiVersion = response.DebugApiVersion; - } - - // Properties. - public bool StatusIsOk { get; } - public string Version { get; } - public string ApiVersion { get; } - public string DebugApiVersion { get; } - } -} diff --git a/src/BeeNet/Models/LogData.cs b/src/BeeNet/Models/LogData.cs deleted file mode 100644 index d7f33e45..00000000 --- a/src/BeeNet/Models/LogData.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class LogData - { - // Constructors. - internal LogData(Clients.Response63 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Tree = response.Tree.ToDictionary(i => i.Key, i => i.Value?.Plus?.ToList() ?? new List()); - Loggers = response.Loggers.Select(i => new Loggers(i)).ToList(); - } - - internal LogData(Clients.Response64 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Tree = response.Tree.ToDictionary(i => i.Key, i => i.Value?.Plus?.ToList() ?? new List()); - Loggers = response.Loggers.Select(i => new Loggers(i)).ToList(); - } - - // Properties. - public IDictionary> Tree { get; } - public ICollection Loggers { get; } - } -} diff --git a/src/BeeNet/Models/Loggers.cs b/src/BeeNet/Models/Loggers.cs deleted file mode 100644 index 3c25227e..00000000 --- a/src/BeeNet/Models/Loggers.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class Loggers - { - // Constructors. - internal Loggers(Clients.Loggers loggers) - { - ArgumentNullException.ThrowIfNull(loggers, nameof(loggers)); - - Id = loggers.Id; - Logger = loggers.Logger; - Subsystem = loggers.Subsystem; - Verbosity = loggers.Verbosity; - } - - internal Loggers(Clients.Loggers2 loggers) - { - ArgumentNullException.ThrowIfNull(loggers, nameof(loggers)); - - Id = loggers.Id; - Logger = loggers.Logger; - Subsystem = loggers.Subsystem; - Verbosity = loggers.Verbosity; - } - - // Properties. - public string Id { get; set; } - public string Logger { get; set; } - public string Subsystem { get; set; } - public string Verbosity { get; set; } - } -} diff --git a/src/BeeNet/Models/MessageResponse.cs b/src/BeeNet/Models/MessageResponse.cs deleted file mode 100644 index e22a3d2c..00000000 --- a/src/BeeNet/Models/MessageResponse.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class MessageResponse - { - // Constructors. - internal MessageResponse(Clients.Response10 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Message = response.Message; - Code = response.Code; - } - - internal MessageResponse(Clients.Response12 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Message = response.Message; - Code = response.Code; - } - - internal MessageResponse(Clients.Response33 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Message = response.Message; - Code = response.Code; - } - - // Properties. - public string Message { get; } - public int Code { get; } - } -} diff --git a/src/BeeNet/Models/NodeInfo.cs b/src/BeeNet/Models/NodeInfo.cs deleted file mode 100644 index c1b94354..00000000 --- a/src/BeeNet/Models/NodeInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class NodeInfo - { - // Constructors. - internal NodeInfo(Clients.Response31 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - BeeMode = response.BeeMode switch - { - Clients.Response31BeeMode.Dev => InfoBeeMode.Dev, - Clients.Response31BeeMode.Full => InfoBeeMode.Full, - Clients.Response31BeeMode.Light => InfoBeeMode.Light, - _ => throw new InvalidOperationException() - }; - ChequebookEnabled = response.ChequebookEnabled; - SwapEnabled = response.SwapEnabled; - } - - // Methods. - /// - /// Gives back in what mode the Bee client has been started. The modes are mutually exclusive * `light` - light node; does not participate in forwarding or storing chunks * `full` - full node * `dev` - development mode; Bee client for development purposes, blockchain operations are mocked - /// - public InfoBeeMode BeeMode { get; } - public bool ChequebookEnabled { get; } - public bool SwapEnabled { get; } - } -} diff --git a/src/BeeNet/Models/PeerBalance.cs b/src/BeeNet/Models/PeerBalance.cs deleted file mode 100644 index f1f161bc..00000000 --- a/src/BeeNet/Models/PeerBalance.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class PeerBalance - { - // Constructors. - internal PeerBalance(Clients.Balances balance) - { - ArgumentNullException.ThrowIfNull(balance, nameof(balance)); - - Peer = balance.Peer; - Balance = BzzBalance.FromPlurString(balance.Balance); - } - - internal PeerBalance(Clients.Balances2 balance) - { - ArgumentNullException.ThrowIfNull(balance, nameof(balance)); - - Peer = balance.Peer; - Balance = BzzBalance.FromPlurString(balance.Balance); - } - - internal PeerBalance(Clients.Response23 balance) - { - ArgumentNullException.ThrowIfNull(balance, nameof(balance)); - - Peer = balance.Peer; - Balance = BzzBalance.FromPlurString(balance.Balance); - } - - internal PeerBalance(Clients.Response25 balance) - { - ArgumentNullException.ThrowIfNull(balance, nameof(balance)); - - Peer = balance.Peer; - Balance = BzzBalance.FromPlurString(balance.Balance); - } - - // Properties. - public string Peer { get; } - public BzzBalance Balance { get; } - } -} diff --git a/src/BeeNet/Models/PeerMetrics.cs b/src/BeeNet/Models/PeerMetrics.cs deleted file mode 100644 index ec727259..00000000 --- a/src/BeeNet/Models/PeerMetrics.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class PeerMetrics - { - // Constructors - internal PeerMetrics(Clients.Metrics metrics) - { - ArgumentNullException.ThrowIfNull(metrics, nameof(metrics)); - - LastSeenTimestamp = metrics.LastSeenTimestamp; - SessionConnectionRetry = metrics.SessionConnectionRetry; - ConnectionTotalDuration = metrics.ConnectionTotalDuration; - SessionConnectionDuration = metrics.SessionConnectionDuration; - SessionConnectionDirection = metrics.SessionConnectionDirection; - LatencyEWMA = metrics.LatencyEWMA; - } - - // Properties. - public int LastSeenTimestamp { get; } - public int SessionConnectionRetry { get; } - public double ConnectionTotalDuration { get; } - public double SessionConnectionDuration { get; } - public string SessionConnectionDirection { get; } - public int LatencyEWMA { get; } - } -} diff --git a/src/BeeNet/Models/PeersAggregate.cs b/src/BeeNet/Models/PeersAggregate.cs deleted file mode 100644 index 4a66baf1..00000000 --- a/src/BeeNet/Models/PeersAggregate.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class PeersAggregate - { - // Constructors. - internal PeersAggregate(Clients.Anonymous2 anonymous) - { - ArgumentNullException.ThrowIfNull(anonymous, nameof(anonymous)); - - Population = anonymous.Population; - Connected = anonymous.Connected; - DisconnectedPeers = anonymous.DisconnectedPeers - ?.Select(k => new DisconnectedPeers(k)) ?? new List(); - ConnectedPeers = anonymous.ConnectedPeers - ?.Select(k => new ConnectedPeers(k)) ?? new List(); - } - - // Properties. - public int Population { get; } - public int Connected { get; } - public IEnumerable DisconnectedPeers { get; } - public IEnumerable ConnectedPeers { get; } - } -} diff --git a/src/BeeNet/Models/PostageBatchShort.cs b/src/BeeNet/Models/PostageBatchShort.cs deleted file mode 100644 index d0dee9dc..00000000 --- a/src/BeeNet/Models/PostageBatchShort.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class PostageBatchShort - { - // Constructors. - internal PostageBatchShort(Clients.Batches batch) - { - ArgumentNullException.ThrowIfNull(batch, nameof(batch)); - - BatchId = batch.BatchID; - BucketDepth = batch.BucketDepth; - Depth = batch.Depth; - ImmutableFlag = batch.ImmutableFlag; - Owner = batch.Owner; - StartBlockNumber = batch.Start; - StorageRadius = batch.StorageRadius; - Ttl = TimeSpan.FromSeconds(batch.BatchTTL); - Value = batch.Value; - } - - // Properties. - public PostageBatchId BatchId { get; } - public int BucketDepth { get; } - public int Depth { get; } - public bool ImmutableFlag { get; } - public string Owner { get; } - public int StartBlockNumber { get; } - public int StorageRadius { get; } - public TimeSpan Ttl { get; } - public string Value { get; } - } -} diff --git a/src/BeeNet/Models/PostageBuckets.cs b/src/BeeNet/Models/PostageBuckets.cs deleted file mode 100644 index a3188cfd..00000000 --- a/src/BeeNet/Models/PostageBuckets.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Concurrent; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - /// - /// A thread safe implementation of postage buckets array - /// - public class PostageBuckets - { - // Consts. - public const int BucketsSize = 1 << PostageBatch.BucketDepth; - - // Fields. - private readonly ConcurrentDictionary _buckets; // - - // Constructor. - public PostageBuckets( - uint[]? initialBuckets = null) - { - if (initialBuckets is not null && - initialBuckets.Length != BucketsSize) - throw new ArgumentOutOfRangeException(nameof(initialBuckets), - $"Initial buckets must have length {BucketsSize}, or be null"); - - _buckets = ArrayToDictionary(initialBuckets ?? []); - } - - // Properties. - public ReadOnlySpan Buckets => DictionaryToArray(_buckets); - public uint MaxBucketCount { get; private set; } - public long TotalChunks { get; private set; } - - // Methods. - public uint GetCollisions(uint bucketId) - { - _buckets.TryGetValue(bucketId, out var collisions); - return collisions; - } - - public void IncrementCollisions(uint bucketId) - { - _buckets.AddOrUpdate( - bucketId, - _ => - { - TotalChunks++; - if (1 > MaxBucketCount) - MaxBucketCount = 1; - return 1; - }, - (_, c) => - { - TotalChunks++; - if (c + 1 > MaxBucketCount) - MaxBucketCount = c + 1; - return c + 1; - }); - } - - public void ResetBucketCollisions(uint bucketId) => - _buckets.AddOrUpdate(bucketId, _ => 0, (_, _) => 0); - - // Helpers. - private static ConcurrentDictionary ArrayToDictionary(uint[] buckets) => - new(buckets.Select((c, i) => (c, (uint)i)) - .ToDictionary<(uint value, uint index), uint, uint>(pair => pair.index, pair => pair.value)); - - private static uint[] DictionaryToArray(ConcurrentDictionary dictionary) - { - var outArray = new uint[BucketsSize]; - for (uint i = 0; i < BucketsSize; i++) - if (dictionary.TryGetValue(i, out var value)) - outArray[i] = value; - return outArray; - } - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/PostageProof.cs b/src/BeeNet/Models/PostageProof.cs deleted file mode 100644 index 33f53027..00000000 --- a/src/BeeNet/Models/PostageProof.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class PostageProof - { - internal PostageProof(Clients.PostageProof postageProof) - { - ArgumentNullException.ThrowIfNull(postageProof, nameof(postageProof)); - - Index = postageProof.Index; - PostageId = postageProof.PostageId; - Signature = postageProof.Signature; - TimeStamp = DateTimeOffset.FromUnixTimeSeconds( - long.Parse(postageProof.TimeStamp, CultureInfo.InvariantCulture)); - } - - internal PostageProof(PostageProof2 postageProof) - { - ArgumentNullException.ThrowIfNull(postageProof, nameof(postageProof)); - - Index = postageProof.Index; - PostageId = postageProof.PostageId; - Signature = postageProof.Signature; - TimeStamp = DateTimeOffset.FromUnixTimeSeconds( - long.Parse(postageProof.TimeStamp, CultureInfo.InvariantCulture)); - } - - internal PostageProof(PostageProof3 postageProof) - { - ArgumentNullException.ThrowIfNull(postageProof, nameof(postageProof)); - - Index = postageProof.Index; - PostageId = postageProof.PostageId; - Signature = postageProof.Signature; - TimeStamp = DateTimeOffset.FromUnixTimeSeconds( - long.Parse(postageProof.TimeStamp, CultureInfo.InvariantCulture)); - } - - // Properties. - public string Index { get; } - public PostageBatchId PostageId { get; } - public string Signature { get; } - public DateTimeOffset TimeStamp { get; } - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/RedistributionState.cs b/src/BeeNet/Models/RedistributionState.cs deleted file mode 100644 index 0372a1c5..00000000 --- a/src/BeeNet/Models/RedistributionState.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class RedistributionState - { - // Constructors. - internal RedistributionState(Clients.Response60 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - IsFrozen = response.IsFrozen; - IsFullySynced = response.IsFullySynced; - IsHealthy = response.IsHealthy; - Round = response.Round; - LastWonRound = response.LastWonRound; - LastPlayedRound = response.LastPlayedRound; - LastFrozenRound = response.LastFrozenRound; - Block = response.Block; - Reward = BzzBalance.FromPlurString(response.Reward); - Fees = XDaiBalance.FromWeiString(response.Fees); - } - - // Properties. - public bool IsFrozen { get; set; } - public bool IsFullySynced { get; set; } - public bool IsHealthy { get; set; } - public int Round { get; set; } - public int LastWonRound { get; set; } - public int LastPlayedRound { get; set; } - public int LastFrozenRound { get; set; } - public int Block { get; set; } - public BzzBalance Reward { get; set; } - public XDaiBalance Fees { get; set; } - } -} diff --git a/src/BeeNet/Models/ReserveCommitment.cs b/src/BeeNet/Models/ReserveCommitment.cs deleted file mode 100644 index f5ac8c4e..00000000 --- a/src/BeeNet/Models/ReserveCommitment.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class ReserveCommitment - { - // Constructors. - internal ReserveCommitment(Response58 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Duration = response.Duration; - Hash = response.Hash; - Proof1 = new ReserveCommitmentProof(response.Proofs.Proof1); - Proof2 = new ReserveCommitmentProof(response.Proofs.Proof2); - ProofLast = new ReserveCommitmentProof(response.Proofs.ProofLast); - } - - // Properties. - public int Duration { get; set; } - public SwarmHash Hash { get; set; } - public ReserveCommitmentProof Proof1 { get; set; } - public ReserveCommitmentProof Proof2 { get; set; } - public ReserveCommitmentProof ProofLast { get; set; } - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/ReserveCommitmentProof.cs b/src/BeeNet/Models/ReserveCommitmentProof.cs deleted file mode 100644 index 800f6d23..00000000 --- a/src/BeeNet/Models/ReserveCommitmentProof.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class ReserveCommitmentProof - { - // Constructors. - internal ReserveCommitmentProof(Proof1 proof) - { - ArgumentNullException.ThrowIfNull(proof, nameof(proof)); - - ChunkSpan = proof.ChunkSpan; - PostageProof = new PostageProof(proof.PostageProof); - ProofSegments = proof.ProofSegments ?? Array.Empty(); - ProofSegments2 = proof.ProofSegments2 ?? Array.Empty(); - ProofSegments3 = proof.ProofSegments3 ?? Array.Empty(); - ProveSegment = proof.ProveSegment; - ProveSegment2 = proof.ProveSegment2; - SocProof = (proof.SocProof ?? Array.Empty()).Select(p => new SocProof(p)); - } - - internal ReserveCommitmentProof(Proof2 proof) - { - ArgumentNullException.ThrowIfNull(proof, nameof(proof)); - - ChunkSpan = proof.ChunkSpan; - PostageProof = new PostageProof(proof.PostageProof); - ProofSegments = proof.ProofSegments ?? Array.Empty(); - ProofSegments2 = proof.ProofSegments2 ?? Array.Empty(); - ProofSegments3 = proof.ProofSegments3 ?? Array.Empty(); - ProveSegment = proof.ProveSegment; - ProveSegment2 = proof.ProveSegment2; - SocProof = (proof.SocProof ?? Array.Empty()).Select(p => new SocProof(p)); - } - - internal ReserveCommitmentProof(ProofLast proof) - { - ArgumentNullException.ThrowIfNull(proof, nameof(proof)); - - ChunkSpan = proof.ChunkSpan; - PostageProof = new PostageProof(proof.PostageProof); - ProofSegments = proof.ProofSegments ?? Array.Empty(); - ProofSegments2 = proof.ProofSegments2 ?? Array.Empty(); - ProofSegments3 = proof.ProofSegments3 ?? Array.Empty(); - ProveSegment = proof.ProveSegment; - ProveSegment2 = proof.ProveSegment2; - SocProof = (proof.SocProof ?? Array.Empty()).Select(p => new SocProof(p)); - } - - // Properties. - public int ChunkSpan { get; } - public PostageProof PostageProof { get; } - public IEnumerable ProofSegments { get; } - public IEnumerable ProofSegments2 { get; } - public IEnumerable ProofSegments3 { get; } - public string ProveSegment { get; } - public string ProveSegment2 { get; } - public IEnumerable SocProof { get; } - - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/ReserveState.cs b/src/BeeNet/Models/ReserveState.cs deleted file mode 100644 index e06d1ed8..00000000 --- a/src/BeeNet/Models/ReserveState.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class ReserveState - { - // Constructors. - internal ReserveState(Clients.Response29 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Commitment = response.Commitment; - Radius = response.Radius; - StorageRadius = response.StorageRadius; - } - - // Properties. - public long Commitment { get; } - public int Radius { get; } - public int StorageRadius { get; } - } -} diff --git a/src/BeeNet/Models/ResultChequebook.cs b/src/BeeNet/Models/ResultChequebook.cs deleted file mode 100644 index c1e13a8f..00000000 --- a/src/BeeNet/Models/ResultChequebook.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class ResultChequebook - { - // Constructors. - internal ResultChequebook(Clients.Result result) - { - ArgumentNullException.ThrowIfNull(result, nameof(result)); - - Recipient = result.Recipient; - LastPayout = long.Parse(result.LastPayout, CultureInfo.InvariantCulture); - Bounced = result.Bounced; - } - - // Properties. - public bool Bounced { get; } - public long LastPayout { get; } - public string Recipient { get; } - } -} diff --git a/src/BeeNet/Models/Settlement.cs b/src/BeeNet/Models/Settlement.cs deleted file mode 100644 index e5a8464c..00000000 --- a/src/BeeNet/Models/Settlement.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class Settlement - { - // Constructors. - internal Settlement(Clients.Response36 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - TotalReceived = BzzBalance.FromPlurString(response.TotalReceived); - TotalSent = BzzBalance.FromPlurString(response.TotalSent); - Settlements = response.Settlements - .Select(i => new SettlementData(i)); - } - - internal Settlement(Clients.Response37 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - TotalReceived = BzzBalance.FromPlurString(response.TotalReceived); - TotalSent = BzzBalance.FromPlurString(response.TotalSent); - Settlements = response.Settlements - .Select(i => new SettlementData(i)); - } - - // Properties. - public BzzBalance TotalReceived { get; } - public BzzBalance TotalSent { get; } - public IEnumerable Settlements { get; } - } -} diff --git a/src/BeeNet/Models/SettlementData.cs b/src/BeeNet/Models/SettlementData.cs deleted file mode 100644 index 6ceca300..00000000 --- a/src/BeeNet/Models/SettlementData.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class SettlementData - { - // Constructors. - internal SettlementData(Clients.Settlements settlement) - { - ArgumentNullException.ThrowIfNull(settlement, nameof(settlement)); - - Peer = settlement.Peer; - Received = BzzBalance.FromPlurString(settlement.Received); - Sent = BzzBalance.FromPlurString(settlement.Sent); - } - - internal SettlementData(Clients.Settlements2 settlements) - { - ArgumentNullException.ThrowIfNull(settlements, nameof(settlements)); - - Peer = settlements.Peer; - Received = BzzBalance.FromPlurString(settlements.Received); - Sent = BzzBalance.FromPlurString(settlements.Sent); - } - - internal SettlementData(Clients.Response35 settlement) - { - ArgumentNullException.ThrowIfNull(settlement, nameof(settlement)); - - Peer = settlement.Peer; - Received = BzzBalance.FromPlurString(settlement.Received); - Sent = BzzBalance.FromPlurString(settlement.Sent); - } - - // Properties. - public string Peer { get; } - public BzzBalance Received { get; } - public BzzBalance Sent { get; } - } -} diff --git a/src/BeeNet/Models/SocProof.cs b/src/BeeNet/Models/SocProof.cs deleted file mode 100644 index 34f5883a..00000000 --- a/src/BeeNet/Models/SocProof.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using Etherna.BeeNet.Clients; -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class SocProof - { - // Constructors. - internal SocProof(Clients.SocProof socProof) - { - ArgumentNullException.ThrowIfNull(socProof, nameof(socProof)); - - ChunkHash = socProof.ChunkAddr; - Identifier = socProof.Identifier; - Signature = socProof.Signature; - Signer = socProof.Signer; - } - - internal SocProof(SocProof2 socProof) - { - ArgumentNullException.ThrowIfNull(socProof, nameof(socProof)); - - ChunkHash = socProof.ChunkAddr; - Identifier = socProof.Identifier; - Signature = socProof.Signature; - Signer = socProof.Signer; - } - - internal SocProof(SocProof3 socProof) - { - ArgumentNullException.ThrowIfNull(socProof, nameof(socProof)); - - ChunkHash = socProof.ChunkAddr; - Identifier = socProof.Identifier; - Signature = socProof.Signature; - Signer = socProof.Signer; - } - - // Properties. - public SwarmHash ChunkHash { get; set; } - public string Identifier { get; set; } - public string Signature { get; set; } - public string Signer { get; set; } - } -} \ No newline at end of file diff --git a/src/BeeNet/Models/StampsBuckets.cs b/src/BeeNet/Models/StampsBuckets.cs deleted file mode 100644 index 58260d5a..00000000 --- a/src/BeeNet/Models/StampsBuckets.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class StampsBuckets - { - // Constructors. - internal StampsBuckets(Clients.Response53 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Depth = response.Depth; - BucketDepth = response.BucketDepth; - BucketUpperBound = response.BucketUpperBound; - Buckets = response.Buckets.Select(i => new Bucket(i)); - } - - // Properties. - public int Depth { get; } - public int BucketDepth { get; } - public int BucketUpperBound { get; } - public IEnumerable Buckets { get; } - } -} diff --git a/src/BeeNet/Models/StatusNode.cs b/src/BeeNet/Models/StatusNode.cs deleted file mode 100644 index ed432c93..00000000 --- a/src/BeeNet/Models/StatusNode.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class StatusNode - { - // Constructors. - internal StatusNode(Clients.Response65 status) - { - ArgumentNullException.ThrowIfNull(status, nameof(status)); - - switch (status.BeeMode) - { - case Clients.Response65BeeMode.Light: - BeeMode = StatusBeeMode.Light; - break; - case Clients.Response65BeeMode.Full: - BeeMode = StatusBeeMode.Full; - break; - case Clients.Response65BeeMode.UltraLight: - BeeMode = StatusBeeMode.UltraLight; - break; - case Clients.Response65BeeMode.Unknown: - BeeMode = StatusBeeMode.Unknown; - break; - } - BatchCommitment = status.BatchCommitment; - ConnectedPeers = status.ConnectedPeers; - NeighborhoodSize = status.NeighborhoodSize; - Peer = status.Peer; - Proximity = status.Proximity; - PullsyncRate = status.PullsyncRate; - ReserveSize = status.ReserveSize; - ReserveSizeWithinRadius = (int)status.ReserveSizeWithinRadius; - RequestFailed = status.RequestFailed; - StorageRadius = status.StorageRadius; - } - - internal StatusNode(Clients.Stamps2 status) - { - ArgumentNullException.ThrowIfNull(status, nameof(status)); - - switch (status.BeeMode) - { - case Clients.StampsBeeMode.Light: - BeeMode = StatusBeeMode.Light; - break; - case Clients.StampsBeeMode.Full: - BeeMode = StatusBeeMode.Full; - break; - case Clients.StampsBeeMode.UltraLight: - BeeMode = StatusBeeMode.UltraLight; - break; - case Clients.StampsBeeMode.Unknown: - BeeMode = StatusBeeMode.Unknown; - break; - } - BatchCommitment = status.BatchCommitment; - ConnectedPeers = status.ConnectedPeers; - NeighborhoodSize = status.NeighborhoodSize; - Peer = status.Peer; - Proximity = status.Proximity; - PullsyncRate = status.PullsyncRate; - ReserveSize = status.ReserveSize; - ReserveSizeWithinRadius = (int)status.ReserveSizeWithinRadius; - RequestFailed = status.RequestFailed; - StorageRadius = status.StorageRadius; - } - - // Properties. - public StatusBeeMode BeeMode { get; } - public int BatchCommitment { get; } - public int ConnectedPeers { get; } - public int NeighborhoodSize { get; } - public string Peer { get; } - public int Proximity { get; } - public double PullsyncRate { get; } - public int ReserveSize { get; } - public int ReserveSizeWithinRadius { get; } - public bool? RequestFailed { get; } - public int StorageRadius { get; } - } -} diff --git a/src/BeeNet/Models/TagInfo.cs b/src/BeeNet/Models/TagInfo.cs deleted file mode 100644 index d2fc2f9a..00000000 --- a/src/BeeNet/Models/TagInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; - -namespace Etherna.BeeNet.Models -{ - public sealed class TagInfo - { - // Constructors. - internal TagInfo(Clients.Response7 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Uid = response.Uid; - StartedAt = response.StartedAt; - Split = response.Split; - Seen = response.Seen; - Stored = response.Stored; - Sent = response.Sent; - Synced = response.Synced; - } - - internal TagInfo(Clients.Response8 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - Uid = response.Uid; - StartedAt = response.StartedAt; - Split = response.Split; - Seen = response.Seen; - Stored = response.Stored; - Sent = response.Sent; - Synced = response.Synced; - } - - internal TagInfo(Clients.Tags tags) - { - ArgumentNullException.ThrowIfNull(tags, nameof(tags)); - - Uid = tags.Uid; - StartedAt = tags.StartedAt; - Split = tags.Split; - Seen = tags.Seen; - Stored = tags.Stored; - Sent = tags.Sent; - Synced = tags.Synced; - } - - // Properties. - public long Uid { get; } - public DateTimeOffset StartedAt { get; } - public int Split { get; } - public int Seen { get; } - public int Stored { get; } - public int Sent { get; } - public int Synced { get; } - } -} diff --git a/src/BeeNet/Models/Topology.cs b/src/BeeNet/Models/Topology.cs deleted file mode 100644 index 199bb58f..00000000 --- a/src/BeeNet/Models/Topology.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace Etherna.BeeNet.Models -{ - public sealed class Topology - { - // Constructors. - internal Topology(Clients.Response38 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - BaseAddr = response.BaseAddr; - Bins = response.Bins.ToDictionary( - i => i.Key, - i => new PeersAggregate(i.Value)); - Connected = response.Connected; - Depth = response.Depth; - NetworkAvailability = response.NetworkAvailability switch - { - Clients.Response38NetworkAvailability.Unknown => Models.NetworkAvailability.Unknown, - Clients.Response38NetworkAvailability.Available => Models.NetworkAvailability.Available, - Clients.Response38NetworkAvailability.Unavailable => Models.NetworkAvailability.Unavailable, - _ => throw new InvalidOperationException(), - }; - NnLowWatermark = response.NnLowWatermark; - Population = response.Population; - Reachability = response.Reachability switch - { - Clients.Response38Reachability.Unknown => Models.Reachability.Unknown, - Clients.Response38Reachability.Public => Models.Reachability.Public, - Clients.Response38Reachability.Private => Models.Reachability.Private, - _ => throw new InvalidOperationException(), - }; - Timestamp = DateTimeOffset.FromUnixTimeSeconds( - long.Parse(response.Timestamp, CultureInfo.InvariantCulture)); - } - - // Properties. - public string BaseAddr { get; } - public IDictionary Bins { get; } - public int Connected { get; } - public int Depth { get; } - public NetworkAvailability NetworkAvailability { get; } - public int NnLowWatermark { get; } - public int Population { get; } - public Reachability Reachability { get; } - public DateTimeOffset Timestamp { get; } - } -} diff --git a/src/BeeNet/Models/TxInfo.cs b/src/BeeNet/Models/TxInfo.cs deleted file mode 100644 index 58de2fef..00000000 --- a/src/BeeNet/Models/TxInfo.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Globalization; - -namespace Etherna.BeeNet.Models -{ - public sealed class TxInfo - { - // Constructors. - internal TxInfo(Clients.PendingTransactions tx) - { - ArgumentNullException.ThrowIfNull(tx, nameof(tx)); - - TransactionHash = tx.TransactionHash; - To = tx.To; - Nonce = tx.Nonce; - GasPrice = XDaiBalance.FromWeiString(tx.GasPrice); - GasLimit = tx.GasLimit; - Data = tx.Data; - Created = tx.Created; - Description = tx.Description; - Value = XDaiBalance.FromWeiString(tx.Value); - } - - internal TxInfo(Clients.Response48 tx) - { - ArgumentNullException.ThrowIfNull(tx, nameof(tx)); - - TransactionHash = tx.TransactionHash; - To = tx.To; - Nonce = tx.Nonce; - GasPrice = XDaiBalance.FromWeiString(tx.GasPrice); - GasLimit = tx.GasLimit; - Data = tx.Data; - Created = tx.Created; - Description = tx.Description; - Value = XDaiBalance.FromWeiString(tx.Value); - } - - // Properties. - public string TransactionHash { get; } - public string To { get; } - public int Nonce { get; } - public XDaiBalance GasPrice { get; } - public long GasLimit { get; } - public string Data { get; } - public DateTimeOffset Created { get; } - public string Description { get; } - public XDaiBalance Value { get; } - } -} diff --git a/test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj similarity index 88% rename from test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj rename to test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj index 5dd6bfdf..c8e85811 100644 --- a/test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj +++ b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj @@ -8,8 +8,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,7 +20,7 @@ - + diff --git a/test/BeeNet.IntegrationTest/BeeVersions/IgnoreOtherVersionFactAttribute.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/IgnoreOtherVersionFactAttribute.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/IgnoreOtherVersionFactAttribute.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/IgnoreOtherVersionFactAttribute.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/BaseTest_Gateway_v5_0_0.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/BaseTest_Gateway_v5_0_0.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/BaseTest_Gateway_v5_0_0.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/BaseTest_Gateway_v5_0_0.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs similarity index 93% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs index 186f3220..cb9bbe33 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs @@ -43,9 +43,6 @@ public async Task GetBalanceWithPeerAsync() // Act. var balance = await beeNodeClient.GetBalanceWithPeerAsync(peerId); - - // Assert. - Assert.Equal(peerId, balance.Peer); } @@ -59,10 +56,6 @@ public async Task GetConsumedBalanceWithPeerAsync() // Act var balance = await beeNodeClient.GetConsumedBalanceWithPeerAsync(peerId); - - - // Assert - Assert.Equal(peerId, balance.Peer); } } } diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BytesTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BytesTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BytesTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BytesTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BzzTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BzzTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BzzTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BzzTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs similarity index 61% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs index f42aacaf..f575fb20 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs @@ -24,7 +24,7 @@ public class ChequebookTest : BaseTest_Gateway_v5_0_0 public async Task CashoutChequeForPeerAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; // Act @@ -38,35 +38,35 @@ public async Task CashoutChequeForPeerAsync() } [Fact] - public async Task DepositIntoChequeBookAsync() + public async Task DepositIntoChequebookAsync() { // Arrange - var originalChequeBookBalance = await beeNodeClient.GetChequeBookBalanceAsync(); + var originalChequebookBalance = await beeNodeClient.GetChequebookBalanceAsync(); var amount = 123; // Act - var result = await beeNodeClient.DepositIntoChequeBookAsync(amount); + var result = await beeNodeClient.DepositIntoChequebookAsync(amount); await Task.Delay(180000); // Assert Assert.StartsWith("0x", result); - var actualChequeBookBalance = await beeNodeClient.GetChequeBookBalanceAsync(); - Assert.Equal(originalChequeBookBalance.AvailableBalance + amount, actualChequeBookBalance.AvailableBalance); - Assert.Equal(originalChequeBookBalance.TotalBalance + amount, actualChequeBookBalance.TotalBalance); + var actualChequebookBalance = await beeNodeClient.GetChequebookBalanceAsync(); + Assert.Equal(originalChequebookBalance.AvailableBalance + amount, actualChequebookBalance.AvailableBalance); + Assert.Equal(originalChequebookBalance.TotalBalance + amount, actualChequebookBalance.TotalBalance); } [Fact] - public async Task GetAllChequeBookChequesAsync() + public async Task GetAllChequebookChequesAsync() { // Arrange - var allCheques = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheques = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheques.ToList().First().Peer; // Act - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); //TODO this call return only one peer + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); //TODO this call return only one peer // Assert @@ -74,13 +74,13 @@ public async Task GetAllChequeBookChequesAsync() } [Fact] - public async Task GetChequeBookAddressAsync() + public async Task GetChequebookAddressAsync() { // Arrange // Act - var cheque = await beeNodeClient.GetChequeBookAddressAsync(); + var cheque = await beeNodeClient.GetChequebookAddressAsync(); // Assert @@ -88,24 +88,24 @@ public async Task GetChequeBookAddressAsync() } [Fact] - public async Task GetChequeBookBalanceAsync() + public async Task GetChequebookBalanceAsync() { // Arrange // Act - await beeNodeClient.GetChequeBookBalanceAsync(); + await beeNodeClient.GetChequebookBalanceAsync(); } [Fact] - public async Task GetChequeBookCashoutForPeerAsync() + public async Task GetChequebookCashoutForPeerAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; // Act - var chequeBookBalance = await beeNodeClient.GetChequeBookCashoutForPeerAsync(peerId); + var chequeBookBalance = await beeNodeClient.GetChequebookCashoutForPeerAsync(peerId); // Assert @@ -113,15 +113,15 @@ public async Task GetChequeBookCashoutForPeerAsync() } [Fact] - public async Task GetChequeBookChequeForPeerAsync() + public async Task GetChequebookChequeForPeerAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; // Act - var chequeBookBalance = await beeNodeClient.GetChequeBookChequeForPeerAsync(peerId); + var chequeBookBalance = await beeNodeClient.GetChequebookChequeForPeerAsync(peerId); // Assert @@ -130,25 +130,25 @@ public async Task GetChequeBookChequeForPeerAsync() } [Fact] - public async Task WithdrawFromChequeBookAsync() + public async Task WithdrawFromChequebookAsync() { // Arrange var amount = 123; - await beeNodeClient.DepositIntoChequeBookAsync(amount + 10); + await beeNodeClient.DepositIntoChequebookAsync(amount + 10); await Task.Delay(180000); - var originalChequeBookBalance = await beeNodeClient.GetChequeBookBalanceAsync(); + var originalChequebookBalance = await beeNodeClient.GetChequebookBalanceAsync(); // Act - var result = await beeNodeClient.WithdrawFromChequeBookAsync(amount); + var result = await beeNodeClient.WithdrawFromChequebookAsync(amount); await Task.Delay(180000); // Assert Assert.StartsWith("0x", result); - var actualChequeBookBalance = await beeNodeClient.GetChequeBookBalanceAsync(); - Assert.Equal(originalChequeBookBalance.AvailableBalance - amount, actualChequeBookBalance.AvailableBalance); - Assert.Equal(originalChequeBookBalance.TotalBalance - amount, actualChequeBookBalance.TotalBalance); + var actualChequebookBalance = await beeNodeClient.GetChequebookBalanceAsync(); + Assert.Equal(originalChequebookBalance.AvailableBalance - amount, actualChequebookBalance.AvailableBalance); + Assert.Equal(originalChequebookBalance.TotalBalance - amount, actualChequebookBalance.TotalBalance); } } } diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChunkTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChunkTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChunkTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChunkTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ConnectivityTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ConnectivityTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ConnectivityTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ConnectivityTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/FeedTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/FeedTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/FeedTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/FeedTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs similarity index 87% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs index 506df3a6..23ae9994 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs @@ -26,14 +26,9 @@ public async Task CreatePinAsync() { // Arrange var reference = await UploadBZZFileAndGetReferenceAsync(); - - + // Act - var result = await beeNodeClient.CreatePinAsync(reference); - - - // Assert - Assert.True(result.Code == 200 || result.Code == 201); + await beeNodeClient.CreatePinAsync(reference); } [Fact] @@ -42,14 +37,9 @@ public async Task DeletePinAsync() // Arrange var reference = await UploadBZZFileAndGetReferenceAsync(); await beeNodeClient.CreatePinAsync(reference); - - + // Act - var result = await beeNodeClient.DeletePinAsync(reference); - - - // Assert - Assert.Equal(200, result.Code); + await beeNodeClient.DeletePinAsync(reference); } [Fact] diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs similarity index 95% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs index e1abb72f..8ca7e77a 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs @@ -12,6 +12,7 @@ // You should have received a copy of the GNU Lesser General Public License along with Bee.Net. // If not, see . +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -114,16 +115,14 @@ public async Task DilutePostageBatchAsync() public async Task GetAllValidPostageBatchesFromAllNodesAsync() { // Arrange - var batch = await beeNodeClient.BuyPostageBatchAsync(500, 32); + var batchId = await beeNodeClient.BuyPostageBatchAsync(500, 32); await Task.Delay(180000); - - + // Act var results = await beeNodeClient.GetAllValidPostageBatchesFromAllNodesAsync(); - // Assert - Assert.Contains(results, i => i.BatchId == batch); + Assert.Contains(results, i => i.Value.Any(b => b.Id == batchId)); } } } diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostalServiceSwarmTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostalServiceSwarmTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostalServiceSwarmTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostalServiceSwarmTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs similarity index 91% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs index e671694b..5ac03698 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs @@ -24,7 +24,7 @@ public class SettlementsTest : BaseTest_Gateway_v5_0_0 public async Task GetAllSettlementsAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; // Act @@ -39,7 +39,7 @@ public async Task GetAllSettlementsAsync() public async Task GetAllTimeSettlementsAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; @@ -55,7 +55,7 @@ public async Task GetAllTimeSettlementsAsync() public async Task GetSettlementsWithPeerAsync() { // Arrange - var allCheque = await beeNodeClient.GetAllChequeBookChequesAsync(); + var allCheque = await beeNodeClient.GetAllChequebookChequesAsync(); var peerId = allCheque.ToList().First().Peer; diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SingleOwnerChunkTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SingleOwnerChunkTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SingleOwnerChunkTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SingleOwnerChunkTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs similarity index 97% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs index 6c28c29e..39437f9d 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs @@ -73,7 +73,7 @@ public async Task GetHealthAsync() // Assert Assert.Equal("5.0.0", healthAsync.ApiVersion); Assert.Equal("5.0.0", healthAsync.DebugApiVersion); - Assert.True(healthAsync.StatusIsOk); + Assert.True(healthAsync.IsStatusOk); Assert.StartsWith("1.13.2-", healthAsync.Version); } } diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs similarity index 90% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs index 543cda1a..d0280711 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs @@ -20,7 +20,6 @@ namespace BeeNet.IntegrationTest.BeeVersions.v1_13_2.GatewayApi { public class StewardshipTest : BaseTest_Gateway_v5_0_0 { - [Fact] public async Task CheckIsContentAvailableAsync() { @@ -29,11 +28,10 @@ public async Task CheckIsContentAvailableAsync() await Task.Delay(180000); // Act - var result = await beeNodeClient.CheckIsContentAvailableAsync(reference); - + var result = await beeNodeClient.IsContentRetrievableAsync(reference); // Assert - Assert.True(result.IsRetrievable); + Assert.True(result); } [Fact] @@ -42,10 +40,8 @@ public async Task ReuploadContentAsync() // Arrange var reference = await UploadBZZFileAndGetReferenceAsync(); - // Act await beeNodeClient.ReuploadContentAsync(reference); } - } } diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TagTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TagTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TagTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TagTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TransactionTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TransactionTest.cs similarity index 100% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TransactionTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/TransactionTest.cs diff --git a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs similarity index 89% rename from test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs rename to test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs index 5daa5aac..674d1d8f 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs +++ b/test/BeeNet.Client.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs @@ -28,8 +28,8 @@ public async Task GetWalletBalance() var wallet = await beeNodeClient.GetWalletBalance(); // Assert. - Assert.NotEqual(new BzzBalance(0), wallet.Bzz); - Assert.NotEqual(new XDaiBalance(0), wallet.NativeTokenBalance); + Assert.NotEqual(new BzzBalance(0), wallet.BzzBalance); + Assert.NotEqual(new XDaiBalance(0), wallet.XDaiBalance); } } diff --git a/test/BeeNet.IntegrationTest/Data/BzzFIleForUpload.tar b/test/BeeNet.Client.IntegrationTest/Data/BzzFIleForUpload.tar similarity index 100% rename from test/BeeNet.IntegrationTest/Data/BzzFIleForUpload.tar rename to test/BeeNet.Client.IntegrationTest/Data/BzzFIleForUpload.tar diff --git a/test/BeeNet.IntegrationTest/Data/TestFileForUpload_Gateway.txt b/test/BeeNet.Client.IntegrationTest/Data/TestFileForUpload_Gateway.txt similarity index 100% rename from test/BeeNet.IntegrationTest/Data/TestFileForUpload_Gateway.txt rename to test/BeeNet.Client.IntegrationTest/Data/TestFileForUpload_Gateway.txt diff --git a/test/BeeNet.IntegrationTest/Data/TestFileForUpload_GatewaySecond.txt b/test/BeeNet.Client.IntegrationTest/Data/TestFileForUpload_GatewaySecond.txt similarity index 100% rename from test/BeeNet.IntegrationTest/Data/TestFileForUpload_GatewaySecond.txt rename to test/BeeNet.Client.IntegrationTest/Data/TestFileForUpload_GatewaySecond.txt diff --git a/test/BeeNet.IntegrationTest/Properties/AssemblyInfo.cs b/test/BeeNet.Client.IntegrationTest/Properties/AssemblyInfo.cs similarity index 100% rename from test/BeeNet.IntegrationTest/Properties/AssemblyInfo.cs rename to test/BeeNet.Client.IntegrationTest/Properties/AssemblyInfo.cs diff --git a/test/BeeNet.IntegrationTest/Properties/launchSettings.json b/test/BeeNet.Client.IntegrationTest/Properties/launchSettings.json similarity index 100% rename from test/BeeNet.IntegrationTest/Properties/launchSettings.json rename to test/BeeNet.Client.IntegrationTest/Properties/launchSettings.json diff --git a/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj b/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj new file mode 100644 index 00000000..f3432496 --- /dev/null +++ b/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + true + Etherna.BeeNet + false + enable + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/test/BeeNet.Core.UnitTest/HashProvider.cs b/test/BeeNet.Core.UnitTest/HashProvider.cs new file mode 100644 index 00000000..acd878ba --- /dev/null +++ b/test/BeeNet.Core.UnitTest/HashProvider.cs @@ -0,0 +1,35 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using Etherna.BeeNet.Models; +using Nethereum.Util.HashProviders; +using Org.BouncyCastle.Crypto.Digests; + +namespace Etherna.BeeNet +{ + public class HashProvider : IHashProvider + { + // Fields. + private readonly KeccakDigest hasher = new(256); + + // Methods. + public byte[] ComputeHash(byte[] data) + { + var result = new byte[SwarmHash.HashSize]; + hasher.BlockUpdate(data); + hasher.DoFinal(result); + return result; + } + } +} \ No newline at end of file diff --git a/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs b/test/BeeNet.Core.UnitTest/Models/Feeds/EpochFeedIndexTest.cs similarity index 98% rename from test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs rename to test/BeeNet.Core.UnitTest/Models/Feeds/EpochFeedIndexTest.cs index cbb51f36..3fd499b8 100644 --- a/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/Feeds/EpochFeedIndexTest.cs @@ -15,7 +15,7 @@ using System; using Xunit; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public class EpochFeedIndexTest { @@ -124,7 +124,7 @@ public void Length(ulong start, byte level, ulong expected) public void MarshalBinary(ulong start, byte level, byte[] expected) { var index = new EpochFeedIndex(start, level); - Assert.Equal(expected, index.MarshalBinary); + Assert.Equal(expected, index.GetMarshalBinaryHash(new HashProvider())); } [Theory] diff --git a/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs b/test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs similarity index 95% rename from test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs rename to test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs index 57a77504..619a6434 100644 --- a/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs @@ -13,14 +13,13 @@ // If not, see . using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Xunit; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public class SwarmFeedChunkTest { @@ -139,7 +138,7 @@ public void BuildIdentifierVerifyTopic() var index = new EpochFeedIndex(0, 0); Assert.Throws(() => - SwarmFeedChunk.BuildIdentifier(topic, index)); + SwarmFeedChunk.BuildIdentifier(topic, index, new HashProvider())); } [Fact] @@ -148,7 +147,7 @@ public void BuildIdentifier() var topic = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; var index = new EpochFeedIndex(2, 1); - var result = SwarmFeedChunk.BuildIdentifier(topic, index); + var result = SwarmFeedChunk.BuildIdentifier(topic, index, new HashProvider()); Assert.Equal( new byte[] { 229, 116, 252, 141, 32, 73, 147, 48, 181, 92, 124, 96, 74, 217, 20, 163, 90, 16, 124, 66, 174, 221, 76, 184, 135, 58, 193, 210, 235, 104, 138, 215 }, @@ -162,7 +161,7 @@ public void BuildHashVerifyAccount() var identifier = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; Assert.Throws(() => - SwarmFeedChunk.BuildHash(account, identifier)); + SwarmFeedChunk.BuildHash(account, identifier, new HashProvider())); } [Fact] @@ -172,7 +171,7 @@ public void BuildHashVerifyIdentifier() var identifier = new byte[] { 0, 1, 2, 3 }; Assert.Throws(() => - SwarmFeedChunk.BuildHash(account, identifier)); + SwarmFeedChunk.BuildHash(account, identifier, new HashProvider())); } [Fact] @@ -181,7 +180,7 @@ public void BuildHash() var account = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; var identifier = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; - var result = SwarmFeedChunk.BuildHash(account, identifier); + var result = SwarmFeedChunk.BuildHash(account, identifier, new HashProvider()); Assert.Equal( "854f1dd0c708a544e282b25b9f9c1d353dca28e352656993ab3c2c17b384a86f", diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs new file mode 100644 index 00000000..ea86aff4 --- /dev/null +++ b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs @@ -0,0 +1,143 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Etherna.BeeNet.Models +{ + public class SwarmAddressTest + { + // Internal classes. + public class AddressToStringTestElement( + SwarmAddress address, + string expectedString) + { + public SwarmAddress Address { get; } = address; + public string ExpectedString { get; } = expectedString; + } + + public class StringToAddressTestElement( + string inputString, + SwarmHash expectedHash, + string expectedRelativePath) + { + public string InputString { get; } = inputString; + public SwarmHash ExpectedHash { get; } = expectedHash; + public string ExpectedRelativePath { get; } = expectedRelativePath; + } + + // Data. + public static IEnumerable AddressToStringTests + { + get + { + var tests = new List + { + // Only hash. + new(new SwarmAddress(SwarmHash.Zero), + "0000000000000000000000000000000000000000000000000000000000000000/"), + + // With path without root. + new(new SwarmAddress(SwarmHash.Zero, "Im/a/relative/path"), + "0000000000000000000000000000000000000000000000000000000000000000/Im/a/relative/path"), + + // With path with root. + new(new SwarmAddress(SwarmHash.Zero, "/I/have/a/root"), + "0000000000000000000000000000000000000000000000000000000000000000/I/have/a/root"), + + // With path with root. + new(new SwarmAddress(SwarmHash.Zero, "I/have/final/slash/"), + "0000000000000000000000000000000000000000000000000000000000000000/I/have/final/slash/"), + + // With special chars. + new(new SwarmAddress(SwarmHash.Zero, "I have a % of special\\chars!"), + "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!") + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable StringToAddressTests + { + get + { + var tests = new List + { + // Only hash without ending slash. + new("0000000000000000000000000000000000000000000000000000000000000000", + SwarmHash.Zero, + "/"), + + // Only hash with ending slash. + new("0000000000000000000000000000000000000000000000000000000000000000/", + SwarmHash.Zero, + "/"), + + // With initial root. + new("/0000000000000000000000000000000000000000000000000000000000000000", + SwarmHash.Zero, + "/"), + + // With initial root and ending slash. + new("/0000000000000000000000000000000000000000000000000000000000000000/", + SwarmHash.Zero, + "/"), + + // With path. + new("0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", + SwarmHash.Zero, + "/Im/a/path"), + + // With initial root and path. + new("/0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", + SwarmHash.Zero, + "/Im/a/path"), + + // With final slash. + new("0000000000000000000000000000000000000000000000000000000000000000/I/have/final/slash/", + SwarmHash.Zero, + "/I/have/final/slash/"), + + // With special chars. + new("0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!", + SwarmHash.Zero, + "/I have a % of special\\chars!") + }; + + return tests.Select(t => new object[] { t }); + } + } + + // Tests. + [Theory, MemberData(nameof(AddressToStringTests))] + public void AddressToString(AddressToStringTestElement test) + { + var result = test.Address.ToString(); + + Assert.Equal(test.ExpectedString, result); + } + + [Theory, MemberData(nameof(StringToAddressTests))] + public void StringToAddress(StringToAddressTestElement test) + { + var result = new SwarmAddress(test.InputString); + + Assert.Equal(test.ExpectedHash, result.Hash); + Assert.Equal(test.ExpectedRelativePath, result.Path); + } + } +} \ No newline at end of file diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs new file mode 100644 index 00000000..7416ece4 --- /dev/null +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -0,0 +1,538 @@ +// Copyright 2021-present Etherna SA +// This file is part of Bee.Net. +// +// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. +// If not, see . + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Etherna.BeeNet.Models +{ + public class SwarmUriTest + { + // Internal classes. + public class CombineSwarmUrisTestElement( + SwarmUri[] inputUris, + SwarmUri expectedUri) + { + public SwarmUri[] InputUris { get; } = inputUris; + public SwarmUri ExpectedUri { get; } = expectedUri; + } + + public class HashAndPathToUriTestElement( + SwarmHash? inputHash, + string? inputPath, + Type? expectedExceptionType, + SwarmHash? expectedHash, + string expectedPath, + UriKind expectedUriKind, + bool expectedIsRooted) + { + public SwarmHash? InputHash { get; } = inputHash; + public string? InputPath { get; } = inputPath; + public Type? ExpectedExceptionType { get; } = expectedExceptionType; + public SwarmHash? ExpectedHash { get; } = expectedHash; + public string ExpectedPath { get; } = expectedPath; + public bool ExpectedIsRooted { get; } = expectedIsRooted; + public UriKind ExpectedUriKind { get; } = expectedUriKind; + } + + public class ToSwarmAddressConversionTestElement( + SwarmUri originUri, + SwarmAddress? prefixAddress, + SwarmAddress? expectedAddress, + Type? expectedExceptionType) + { + public SwarmAddress? ExpectedAddress { get; } = expectedAddress; + public Type? ExpectedExceptionType { get; } = expectedExceptionType; + public SwarmUri OriginUri { get; } = originUri; + public SwarmAddress? PrefixAddress { get; } = prefixAddress; + } + + public class TryGetRelativeToUriTestElement( + SwarmUri originUri, + SwarmUri relativeToUri, + SwarmUri? expectedUri) + { + public SwarmUri OriginUri { get; } = originUri; + public SwarmUri RelativeToUri { get; } = relativeToUri; + public SwarmUri? ExpectedUri { get; } = expectedUri; + } + + public class StringToUriTestElement( + string inputString, + UriKind inputUriKind, + Type? expectedExceptionType, + SwarmHash? expectedHash, + string expectedPath, + UriKind expectedUriKind, + bool expectedIsRooted) + { + public string InputString { get; } = inputString; + public UriKind InputUriKind { get; } = inputUriKind; + public Type? ExpectedExceptionType { get; } = expectedExceptionType; + public SwarmHash? ExpectedHash { get; } = expectedHash; + public string ExpectedPath { get; } = expectedPath; + public bool ExpectedIsRooted { get; } = expectedIsRooted; + public UriKind ExpectedUriKind { get; } = expectedUriKind; + } + + public class UriToStringTestElement( + SwarmUri uri, + string expectedString) + { + public SwarmUri Uri { get; } = uri; + public string ExpectedString { get; } = expectedString; + } + + // Data. + public static IEnumerable CombineSwarmUrisTests + { + get + { + var tests = new List + { + // Only relative not rooted paths. + new(["Im", "a/simple/", "path"], + new SwarmUri("Im/a/simple/path", UriKind.Relative)), + + // Relative with rooted paths. + new(["Im", "a", "/rooted", "path"], + new SwarmUri("/rooted/path", UriKind.Relative)), + + // Relative and absolute paths. + new(["Im", "a", "relative", new SwarmUri(SwarmHash.Zero, "absolute"), "path"], + new SwarmUri("0000000000000000000000000000000000000000000000000000000000000000/absolute/path", UriKind.Absolute)), + + // Multi absolute paths. + new([ + new SwarmUri(new SwarmHash("0000000000000000000000000000000000000000000000000000000000000000"), null), + new SwarmUri(null, "zeros"), + new SwarmUri(new SwarmHash("1111111111111111111111111111111111111111111111111111111111111111"), null), + new SwarmUri(null, "ones") + ], + new SwarmUri("1111111111111111111111111111111111111111111111111111111111111111/ones", UriKind.Absolute)), + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable HashAndPathToUriTests + { + get + { + var tests = new List + { + // No hash and no path (throws) + new(null, + null, + typeof(ArgumentException), + null, + "", + UriKind.RelativeOrAbsolute, + false), + + // Only hash. + new(SwarmHash.Zero, + null, + null, + SwarmHash.Zero, + "/", + UriKind.Absolute, + true), + + // No hash and not rooted path. + new(null, + "not/rooted/path", + null, + null, + "not/rooted/path", + UriKind.Relative, + false), + + // No hash and rooted path. + new(null, + "/rooted/path", + null, + null, + "/rooted/path", + UriKind.Relative, + true), + + // Hash and not rooted path. + new(SwarmHash.Zero, + "not/rooted/path", + null, + SwarmHash.Zero, + "/not/rooted/path", + UriKind.Absolute, + true), + + // Hash and rooted path. + new(SwarmHash.Zero, + "/rooted/path", + null, + SwarmHash.Zero, + "/rooted/path", + UriKind.Absolute, + true), + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable StringToUriTests + { + get + { + var tests = new List + { + // RelativeOrAbsolute, not rooted, not starting with hash. + new("not/rooted/path", + UriKind.RelativeOrAbsolute, + null, + null, + "not/rooted/path", + UriKind.Relative, + false), + + // RelativeOrAbsolute, rooted, not starting with hash. + new("/rooted/path", + UriKind.RelativeOrAbsolute, + null, + null, + "/rooted/path", + UriKind.Relative, + true), + + // RelativeOrAbsolute, only hash. + new("0000000000000000000000000000000000000000000000000000000000000000", + UriKind.RelativeOrAbsolute, + null, + SwarmHash.Zero, + "/", + UriKind.Absolute, + true), + + // RelativeOrAbsolute, not rooted, starting with hash. + new("0000000000000000000000000000000000000000000000000000000000000000/not/rooted/path", + UriKind.RelativeOrAbsolute, + null, + SwarmHash.Zero, + "/not/rooted/path", + UriKind.Absolute, + true), + + // RelativeOrAbsolute, rooted, starting with hash. + new("/0000000000000000000000000000000000000000000000000000000000000000/rooted/path", + UriKind.RelativeOrAbsolute, + null, + null, + "/0000000000000000000000000000000000000000000000000000000000000000/rooted/path", + UriKind.Relative, + true), + + // Relative not rooted path. + new("relative/not/rooted/path", + UriKind.Relative, + null, + null, + "relative/not/rooted/path", + UriKind.Relative, + false), + + // Relative rooted path. + new("/relative/rooted/path", + UriKind.Relative, + null, + null, + "/relative/rooted/path", + UriKind.Relative, + true), + + // Absolute with only hash (without slashes). + new("0000000000000000000000000000000000000000000000000000000000000000", + UriKind.Absolute, + null, + SwarmHash.Zero, + "/", + UriKind.Absolute, + true), + + // Absolute with only hash (with slashes). + new("/0000000000000000000000000000000000000000000000000000000000000000/", + UriKind.Absolute, + null, + SwarmHash.Zero, + "/", + UriKind.Absolute, + true), + + // Absolute with hash and path. + new("0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", + UriKind.Absolute, + null, + SwarmHash.Zero, + "/Im/a/path", + UriKind.Absolute, + true), + + // Absolute with invalid initial hash (throws). + new("not/An/Hash", + UriKind.Absolute, + typeof(ArgumentException), + null, + "", + UriKind.RelativeOrAbsolute, + false) + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable ToSwarmAddressConversionTests + { + get + { + var tests = new List + { + // Relative uri with prefix address. + new(new SwarmUri(null, "Im/path"), + new SwarmAddress(SwarmHash.Zero, "Im/prefix"), + new SwarmAddress(SwarmHash.Zero, "/Im/prefix/Im/path"), + null), + + // relative uri without prefix address. + new(new SwarmUri(null, "Im/path"), + null, + null, + typeof(InvalidOperationException)), + + // Absolute uri with prefix address. + new(new SwarmUri("1111111111111111111111111111111111111111111111111111111111111111", "Im/path"), + new SwarmAddress(SwarmHash.Zero, "Im/prefix"), + new SwarmAddress("1111111111111111111111111111111111111111111111111111111111111111", "Im/path"), + null), + + // Absolute uri without prefix address. + new(new SwarmUri("1111111111111111111111111111111111111111111111111111111111111111", "Im/path"), + null, + new SwarmAddress("1111111111111111111111111111111111111111111111111111111111111111", "Im/path"), + null), + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable TryGetRelativeToUriTests + { + get + { + var tests = new List + { + // Hash and not hash. + new (SwarmHash.Zero, + "not/an/hash", + null), + + // Different hashes. + new (SwarmHash.Zero, + new SwarmHash("1111111111111111111111111111111111111111111111111111111111111111"), + null), + + // Different paths + new ("we/are", + "different", + null), + + // Different paths with equal root + new ("we/start/equally", + "we/continue/differently", + null), + + // Origin contains relativeTo path, but different dirs count + new ("we/arent/equal", + "we/are", + null), + + // Different dir names + new ("we/arent/equal", + "we/are/similar", + null), + + // One is rooted, the other no + new ("we/are/similar", + "/we/are/similar", + null), + + // One is rooted, the other no + new ("/we/are/similar", + "we/are/similar", + null), + + // RelativeTo contains origin path + new ("Im/very/similar", + "Im", + "very/similar"), + + // RelativeTo contains origin path (relativeTo slash ended) + new ("Im/very/similar", + "Im/", + "very/similar"), + + // Paths are equal + new ("Im/very/equal", + "Im/very/equal", + ""), + + // Paths are equal (relativeTo slash ended) + new ("Im/very/equal", + "Im/very/equal/", + ""), + + // Paths are equal (origin slash ended) + new ("Im/very/equal/", + "Im/very/equal", + ""), + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable UriToStringTests + { + get + { + var tests = new List + { + // Relative with not rooted path. + new(new SwarmUri(null, "relative/not/rooted/path"), + "relative/not/rooted/path"), + + // Relative with rooted path. + new(new SwarmUri(null, "/relative/rooted/path"), + "/relative/rooted/path"), + + // Absolute with not rooted path. + new(new SwarmUri(SwarmHash.Zero, "relative/not/rooted/path"), + "0000000000000000000000000000000000000000000000000000000000000000/relative/not/rooted/path"), + + // Absolute with rooted path. + new(new SwarmUri(SwarmHash.Zero, "/relative/rooted/path"), + "0000000000000000000000000000000000000000000000000000000000000000/relative/rooted/path"), + }; + + return tests.Select(t => new object[] { t }); + } + } + + // Tests. + [Theory, MemberData(nameof(CombineSwarmUrisTests))] + public void CombineSwarmUris(CombineSwarmUrisTestElement test) + { + var result = SwarmUri.Combine(test.InputUris); + + Assert.Equal(test.ExpectedUri.Hash, result.Hash); + Assert.Equal(test.ExpectedUri.Path, result.Path); + } + + [Theory, MemberData(nameof(HashAndPathToUriTests))] + public void HashAndPathToUri(HashAndPathToUriTestElement test) + { + if (test.ExpectedExceptionType is not null) + { + Assert.Throws( + test.ExpectedExceptionType, + () => new SwarmUri(test.InputHash, test.InputPath)); + } + else + { + var result = new SwarmUri(test.InputHash, test.InputPath); + + Assert.Equal(test.ExpectedHash, result.Hash); + Assert.Equal(test.ExpectedPath, result.Path); + Assert.Equal(test.ExpectedUriKind, result.UriKind); + Assert.Equal(test.ExpectedIsRooted, result.IsRooted); + } + } + + [Theory, MemberData(nameof(ToSwarmAddressConversionTests))] + public void ToSwarmAddressConversion(ToSwarmAddressConversionTestElement test) + { + if (test.ExpectedExceptionType is not null) + { + Assert.Throws( + test.ExpectedExceptionType, + () => test.OriginUri.ToSwarmAddress(test.PrefixAddress)); + } + else + { + var result = test.OriginUri.ToSwarmAddress(test.PrefixAddress); + + Assert.Equal(test.ExpectedAddress!.Value.Hash, result.Hash); + Assert.Equal(test.ExpectedAddress!.Value.Path, result.Path); + } + } + + [Theory, MemberData(nameof(TryGetRelativeToUriTests))] + public void TryGetRelativeToUri(TryGetRelativeToUriTestElement test) + { + var success = test.OriginUri.TryGetRelativeTo( + test.RelativeToUri, + out var result); + + if (test.ExpectedUri is null) + Assert.False(success); + else + { + Assert.True(success); + Assert.Equal(test.ExpectedUri.Value.Hash, result.Hash); + Assert.Equal(test.ExpectedUri.Value.Path, result.Path); + } + } + + [Theory, MemberData(nameof(StringToUriTests))] + public void StringToUri(StringToUriTestElement test) + { + if (test.ExpectedExceptionType is not null) + { + Assert.Throws( + test.ExpectedExceptionType, + () => new SwarmUri(test.InputString, test.InputUriKind)); + } + else + { + var result = new SwarmUri(test.InputString, test.InputUriKind); + + Assert.Equal(test.ExpectedHash, result.Hash); + Assert.Equal(test.ExpectedPath, result.Path); + Assert.Equal(test.ExpectedUriKind, result.UriKind); + Assert.Equal(test.ExpectedIsRooted, result.IsRooted); + } + } + + [Theory, MemberData(nameof(UriToStringTests))] + public void UriToString(UriToStringTestElement test) + { + var result = test.Uri.ToString(); + + Assert.Equal(test.ExpectedString, result); + } + } +} \ No newline at end of file diff --git a/test/BeeNet.Tests/Models/SwarmAddressTest.cs b/test/BeeNet.Tests/Models/SwarmAddressTest.cs deleted file mode 100644 index 6f27debe..00000000 --- a/test/BeeNet.Tests/Models/SwarmAddressTest.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2021-present Etherna SA -// This file is part of Bee.Net. -// -// Bee.Net is free software: you can redistribute it and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Bee.Net is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along with Bee.Net. -// If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; - -namespace Etherna.BeeNet.Models -{ - public class SwarmAddressTest - { - // Internal classes. - public class AddressToStringTestElement( - SwarmAddress address, - string expectedString) - { - public SwarmAddress Address { get; } = address; - public string ExpectedString { get; } = expectedString; - } - - public class StringToAddressTestElement( - string inputString, - SwarmHash expectedHash, - Uri? expectedRelativePath) - { - public string InputString { get; } = inputString; - public SwarmHash ExpectedHash { get; } = expectedHash; - public Uri? ExpectedRelativePath { get; } = expectedRelativePath; - } - - // Data. - public static IEnumerable AddressToStringTests - { - get - { - var tests = new List(); - - // Only hash. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero), - "0000000000000000000000000000000000000000000000000000000000000000/")); - - // With path without root. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero, new Uri("Im/a/relative/path", UriKind.Relative)), - "0000000000000000000000000000000000000000000000000000000000000000/Im/a/relative/path")); - - // With path with root. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero, new Uri("/I/have/a/root", UriKind.Relative)), - "0000000000000000000000000000000000000000000000000000000000000000/I/have/a/root")); - - // With special chars. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero, new Uri("I have a % of special\\chars!", UriKind.Relative)), - "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!")); - - return tests.Select(t => new object[] { t }); - } - } - - public static IEnumerable StringToAddressTests - { - get - { - var tests = new List(); - - // Only hash without ending slash. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000", - SwarmHash.Zero, - null)); - - // Only hash with ending slash. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/", - SwarmHash.Zero, - null)); - - // With initial root. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000", - SwarmHash.Zero, - null)); - - // With initial root and ending slash. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000/", - SwarmHash.Zero, - null)); - - // With path. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", - SwarmHash.Zero, - new Uri("Im/a/path", UriKind.Relative))); - - // With initial root and path. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", - SwarmHash.Zero, - new Uri("Im/a/path", UriKind.Relative))); - - // With special chars. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!", - SwarmHash.Zero, - new Uri("I have a % of special\\chars!", UriKind.Relative))); - - return tests.Select(t => new object[] { t }); - } - } - - // Tests. - [Theory, MemberData(nameof(AddressToStringTests))] - public void AddressToString(AddressToStringTestElement test) - { - var result = test.Address.ToString(); - - Assert.Equal(test.ExpectedString, result); - } - - [Theory, MemberData(nameof(StringToAddressTests))] - public void StringToAddress(StringToAddressTestElement test) - { - var result = new SwarmAddress(test.InputString); - - Assert.Equal(test.ExpectedHash, result.Hash); - Assert.Equal(test.ExpectedRelativePath, result.RelativePath); - } - - [Fact] - public void ExceptWithAbsoluteUri() - { - Assert.Throws(() => - { - var absolutePath = new Uri("https://etherna.io/", UriKind.Absolute); - return new SwarmAddress(SwarmHash.Zero, absolutePath); - }); - } - } -} \ No newline at end of file diff --git a/test/BeeNet.Tests/BeeNet.Tests.csproj b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj similarity index 83% rename from test/BeeNet.Tests/BeeNet.Tests.csproj rename to test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj index 87321694..bd9f0abb 100644 --- a/test/BeeNet.Tests/BeeNet.Tests.csproj +++ b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj @@ -11,15 +11,15 @@ - - + + all runtime; build; native; contentfiles; analyzers - + diff --git a/test/BeeNet.Tests/Services/FeedServiceTest.cs b/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs similarity index 94% rename from test/BeeNet.Tests/Services/FeedServiceTest.cs rename to test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs index 88463303..2a8dad4d 100644 --- a/test/BeeNet.Tests/Services/FeedServiceTest.cs +++ b/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs @@ -13,8 +13,9 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Feeds; +using Etherna.BeeNet.Hashing; using Etherna.BeeNet.Models; +using Etherna.BeeNet.Models.Feeds; using Moq; using System; using System.Collections.Generic; @@ -128,7 +129,7 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Starting chunk epoch index is at max resolution (level 0). { var startingEpochIndex = new EpochFeedIndex(4, 0); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); @@ -146,13 +147,13 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Chunk with child epoch at date is valid and at max resolution. { var startingEpochIndex = new EpochFeedIndex(4, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); var childEpochIndex = new EpochFeedIndex(5, 0); - var childChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, childEpochIndex); + var childChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, childEpochIndex, new Hasher()); var childChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var childChunkPayload = childChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var childChunk = new SwarmFeedChunk(childEpochIndex, childChunkPayload, childChunkReference); @@ -171,16 +172,16 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Chunk with left brother of child epoch at date is valid and at max resolution. { var startingEpochIndex = new EpochFeedIndex(4, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); var rightChildEpochIndex = new EpochFeedIndex(5, 0); - var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new Hasher()); var leftChildEpochIndex = new EpochFeedIndex(4, 0); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); var leftChildChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var leftChildChunkPayload = leftChildChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var leftChildChunk = new SwarmFeedChunk(leftChildEpochIndex, leftChildChunkPayload, leftChildChunkReference); @@ -204,13 +205,13 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Chunk on child at date epoch is left and doesn't exist. { var startingEpochIndex = new EpochFeedIndex(4, 2); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); var leftChildEpochIndex = new EpochFeedIndex(4, 1); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); tests.Add(new FindLastEpochChunkBeforeDateTestElement( ChunkAccount, @@ -229,16 +230,16 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Chunks on child at date and its left brother epochs don't exist. { var startingEpochIndex = new EpochFeedIndex(4, 2); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); var rightChildEpochIndex = new EpochFeedIndex(6, 1); - var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new Hasher()); var leftChildEpochIndex = new EpochFeedIndex(4, 1); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); tests.Add(new FindLastEpochChunkBeforeDateTestElement( ChunkAccount, @@ -259,16 +260,16 @@ public static IEnumerable FindLastEpochChunkBeforeDateTests // Chunk on child at date (right) is successive to date, and chunk on its left brother epoch doesn't exist. { var startingEpochIndex = new EpochFeedIndex(4, 2); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 4 }; //1970-01-01 00:00:04 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var startingChunk = new SwarmFeedChunk(startingEpochIndex, startingChunkPayload, startingChunkReference); var leftChildEpochIndex = new EpochFeedIndex(4, 1); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); var rightChildEpochIndex = new EpochFeedIndex(6, 1); - var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new Hasher()); var rightChildChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 7 }; //1970-01-01 00:00:07 var rightChildChunkPayload = rightChildChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var rightChildChunk = new SwarmFeedChunk(rightChildEpochIndex, rightChildChunkPayload, rightChildChunkReference); @@ -345,7 +346,7 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests var startingEpochIndex = new EpochFeedIndex(4, 2); var chunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var payload = chunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload - var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); tests.Add(new TryFindStartingEpochChunkOnlineTestElement( ChunkAccount, @@ -376,7 +377,7 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests var startingEpochIndex = new EpochFeedIndex(0, EpochFeedIndex.MaxLevel); var chunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 7 }; //1970-01-01 00:00:07 var payload = chunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload - var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); tests.Add(new TryFindStartingEpochChunkOnlineTestElement( ChunkAccount, @@ -392,10 +393,10 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests // Valid chunk is found at left, starting chunk is not found. { var startingEpochIndex = new EpochFeedIndex(6, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var leftEpochIndex = startingEpochIndex.Left; - var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex); + var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex, new Hasher()); var leftChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var leftChunkPayload = leftChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload @@ -418,12 +419,12 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests // Valid chunk is found at left, starting chunk is successive to date. { var startingEpochIndex = new EpochFeedIndex(6, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 7 }; //1970-01-01 00:00:07 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var leftEpochIndex = startingEpochIndex.Left; - var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex); + var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex, new Hasher()); var leftChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var leftChunkPayload = leftChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload @@ -446,10 +447,10 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests // Chunk valid is found at parent, starting chunk is not found. { var startingEpochIndex = new EpochFeedIndex(4, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var parentEpochIndex = startingEpochIndex.Parent; - var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex); + var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex, new Hasher()); var parentChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var parentChunkPayload = parentChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload @@ -472,12 +473,12 @@ public static IEnumerable TryFindStartingEpochChunkOnlineTests // Chunk valid is found at parent, starting chunk is successive to date. { var startingEpochIndex = new EpochFeedIndex(4, 1); - var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var startingChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 7 }; //1970-01-01 00:00:07 var startingChunkPayload = startingChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload var parentEpochIndex = startingEpochIndex.Parent; - var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex); + var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex, new Hasher()); var parentChunkTimestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 5 }; //1970-01-01 00:00:05 var parentChunkPayload = parentChunkTimestamp.Concat(new byte[] { 1, 2, 3 }).ToArray(); //arbitrary content payload