From 63d3bd41b90c4b7d8ed06f631f13d52fc4d45283 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 1 Jul 2024 16:08:20 +0200 Subject: [PATCH 01/46] enable aot compilation --- src/BeeNet/BeeNet.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BeeNet/BeeNet.csproj b/src/BeeNet/BeeNet.csproj index 4976d56..fd43069 100644 --- a/src/BeeNet/BeeNet.csproj +++ b/src/BeeNet/BeeNet.csproj @@ -10,6 +10,7 @@ 12 enable + true true AllEnabledByDefault From 77aaad1a3f2fa1ccadc07bcd5e1878740ddfd6e7 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 1 Jul 2024 18:34:30 +0200 Subject: [PATCH 02/46] use bouncing castle as hashing library --- src/BeeNet/BeeNet.csproj | 2 +- src/BeeNet/Feeds/EpochFeedIndex.cs | 9 +++-- src/BeeNet/Feeds/FeedIndexBase.cs | 3 +- src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs | 13 ++------ src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs | 5 +-- src/BeeNet/Hasher/HashProvider.cs | 35 ++++++++++++++++++++ src/BeeNet/Hasher/Postage/PostageStamper.cs | 31 ++++++++++++----- src/BeeNet/Manifest/MantarayNode.cs | 6 ++-- src/BeeNet/Models/SwarmFeedChunk.cs | 10 +++--- 9 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 src/BeeNet/Hasher/HashProvider.cs diff --git a/src/BeeNet/BeeNet.csproj b/src/BeeNet/BeeNet.csproj index fd43069..9b660dd 100644 --- a/src/BeeNet/BeeNet.csproj +++ b/src/BeeNet/BeeNet.csproj @@ -28,11 +28,11 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BeeNet/Feeds/EpochFeedIndex.cs b/src/BeeNet/Feeds/EpochFeedIndex.cs index 8ca01a6..1ff5398 100644 --- a/src/BeeNet/Feeds/EpochFeedIndex.cs +++ b/src/BeeNet/Feeds/EpochFeedIndex.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 Epoche; using Etherna.BeeNet.Extensions; +using Etherna.BeeNet.Hasher; using System; -using System.Collections.ObjectModel; namespace Etherna.BeeNet.Feeds { @@ -82,9 +81,9 @@ public EpochFeedIndex Parent public byte Level { get; } /// - /// Index represenentation as keccak256 hash + /// Index representation as keccak256 hash /// - public override ReadOnlyCollection MarshalBinary + public override Memory MarshalBinary { get { @@ -93,7 +92,7 @@ public override ReadOnlyCollection MarshalBinary epochBytes.CopyTo(newArray, 0); newArray[epochBytes.Length] = Level; - return new ReadOnlyCollection(Keccak256.ComputeHash(newArray)); + return new HashProvider().ComputeHash(newArray); } } diff --git a/src/BeeNet/Feeds/FeedIndexBase.cs b/src/BeeNet/Feeds/FeedIndexBase.cs index 95d9d18..675ecac 100644 --- a/src/BeeNet/Feeds/FeedIndexBase.cs +++ b/src/BeeNet/Feeds/FeedIndexBase.cs @@ -13,14 +13,13 @@ // If not, see . using System; -using System.Collections.ObjectModel; namespace Etherna.BeeNet.Feeds { public abstract class FeedIndexBase { // Properties. - public abstract ReadOnlyCollection MarshalBinary { get; } + public abstract Memory MarshalBinary { get; } // Methods. public FeedIndexBase GetNext(DateTimeOffset at) => diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs b/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs index 82a57e1..364e144 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs +++ b/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.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 Epoche; using Etherna.BeeNet.Models; using Nethereum.Merkle; using Nethereum.Merkle.StrategyOptions.PairingConcat; @@ -46,28 +45,20 @@ 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 SegmentSize = SwarmHash.HashSize; // Static fields. private static readonly ChunkBmtByteArrayConvertor byteArrayConvertor = new(); - private static readonly HashProvider hashProvider = new(); // Constructor. - public SwarmChunkBmt() + public SwarmChunkBmt(IHashProvider hashProvider) : 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) => new(byteArrayConvertor.ConvertToByteArray(item)); diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs index 88f42bb..23acbe1 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs +++ b/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs @@ -39,11 +39,12 @@ public static SwarmHash Hash(byte[] span, byte[] data) } // Build the merkle tree. - var bmt = new SwarmChunkBmt(); + var hashProvider = new HashProvider(); + var bmt = new SwarmChunkBmt(hashProvider); bmt.BuildTree(segments); var result = bmt.Root.Hash; - return SwarmChunkBmt.ComputeHash(span.Concat(result).ToArray()); + return hashProvider.ComputeHash(span.Concat(result).ToArray()); } } } \ No newline at end of file diff --git a/src/BeeNet/Hasher/HashProvider.cs b/src/BeeNet/Hasher/HashProvider.cs new file mode 100644 index 0000000..22db43c --- /dev/null +++ b/src/BeeNet/Hasher/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.Hasher +{ + 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/src/BeeNet/Hasher/Postage/PostageStamper.cs b/src/BeeNet/Hasher/Postage/PostageStamper.cs index 142bf7e..3913ffa 100644 --- a/src/BeeNet/Hasher/Postage/PostageStamper.cs +++ b/src/BeeNet/Hasher/Postage/PostageStamper.cs @@ -12,13 +12,12 @@ // 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.Models; +using Org.BouncyCastle.Crypto.Digests; using System; -using System.Linq; namespace Etherna.BeeNet.Hasher.Postage { @@ -28,6 +27,9 @@ internal class PostageStamper( 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/Manifest/MantarayNode.cs b/src/BeeNet/Manifest/MantarayNode.cs index 83663ea..3105a8e 100644 --- a/src/BeeNet/Manifest/MantarayNode.cs +++ b/src/BeeNet/Manifest/MantarayNode.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 Epoche; using Etherna.BeeNet.Extensions; +using Etherna.BeeNet.Hasher; using Etherna.BeeNet.Hasher.Pipeline; using Etherna.BeeNet.Models; using System; @@ -28,8 +28,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 HashProvider().ComputeHash( + "mantaray:0.2"u8.ToArray()).Take(VersionHashSize).ToArray(); public const int VersionHashSize = 31; // Fields. diff --git a/src/BeeNet/Models/SwarmFeedChunk.cs b/src/BeeNet/Models/SwarmFeedChunk.cs index 641d1d8..56ae42a 100644 --- a/src/BeeNet/Models/SwarmFeedChunk.cs +++ b/src/BeeNet/Models/SwarmFeedChunk.cs @@ -12,9 +12,9 @@ // 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.Hasher; using Nethereum.Hex.HexConvertors.Extensions; using Nethereum.Util; using System; @@ -118,8 +118,8 @@ public static SwarmHash BuildHash(byte[] account, byte[] identifier) 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 new HashProvider().ComputeHash(identifier.Concat(account).ToArray()); } public static byte[] BuildIdentifier(byte[] topic, FeedIndexBase index) @@ -132,9 +132,9 @@ public static byte[] BuildIdentifier(byte[] topic, FeedIndexBase index) var newArray = new byte[TopicSize + IndexSize]; topic.CopyTo(newArray, 0); - index.MarshalBinary.CopyTo(newArray, topic.Length); + index.MarshalBinary.CopyTo(newArray.AsMemory()[topic.Length..]); - return Keccak256.ComputeHash(newArray); + return new HashProvider().ComputeHash(newArray); } } } From 5e48ce541b730db3165e7126dc3da1c6405e65b2 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 2 Jul 2024 16:36:46 +0200 Subject: [PATCH 03/46] Remove multiple models and migrate others into a dedicated project --- BeeNet.sln | 7 + src/BeeNet.Core/BeeNet.Core.csproj | 48 ++ .../Extensions/ArrayExtensions.cs | 0 .../Extensions/LongExtensions.cs | 0 .../Models/Account.cs} | 34 +- src/BeeNet.Core/Models/AddressDetail.cs | 33 + .../Models/BzzBalance.cs | 0 src/BeeNet.Core/Models/ChainState.cs | 29 + src/BeeNet.Core/Models/CheckPinsResult.cs | 28 + src/BeeNet.Core/Models/ChequePayment.cs | 27 + .../Models/ChequebookBalance.cs} | 17 +- .../Models/ChequebookCashout.cs} | 25 +- src/BeeNet.Core/Models/ChequebookCheque.cs | 27 + .../Models}/Feeds/EpochFeedIndex.cs | 33 +- .../Models}/Feeds/FeedIndexBase.cs | 5 +- .../Models/FileResponse.cs | 21 +- .../Models/GnosisChain.cs | 0 .../Models/Health.cs} | 21 +- src/BeeNet.Core/Models/InfoBeeMode.cs | 34 + .../Models/LogData.cs} | 17 +- src/BeeNet.Core/Models/Loggers.cs | 29 + .../Models/NetworkAvailability.cs | 0 src/BeeNet.Core/Models/NodeInfo.cs | 27 + .../Models/Peer.cs} | 38 +- src/BeeNet.Core/Models/PeersAggregate.cs | 31 + .../Models/PostageBatch.cs | 49 +- .../Models/PostageBatchId.cs | 0 .../Models/PostageBuckets.cs | 0 src/BeeNet.Core/Models/PostageProof.cs | 31 + .../Models}/PostageStamp.cs | 3 +- .../Models/Reachability.cs | 0 src/BeeNet.Core/Models/RedistributionState.cs | 43 ++ .../Models/RedundancyLevel.cs | 0 .../Models/RedundancyStrategy.cs | 0 .../Models/ReserveCommitment.cs} | 30 +- .../Models/ReserveCommitmentProof.cs | 39 ++ src/BeeNet.Core/Models/ReserveState.cs | 27 + src/BeeNet.Core/Models/ResultChequebook.cs | 27 + src/BeeNet.Core/Models/Settlement.cs | 29 + src/BeeNet.Core/Models/SettlementData.cs | 27 + src/BeeNet.Core/Models/SocProof.cs | 29 + .../Models}/StampBucketIndex.cs | 2 +- src/BeeNet.Core/Models/StampsBuckets.cs | 31 + .../Models/StatusBeeMode.cs | 0 src/BeeNet.Core/Models/StatusNode.cs | 43 ++ .../Models/SwarmAddress.cs | 0 .../Models/SwarmChunk.cs | 4 +- .../Models/SwarmFeedChunk.cs | 30 +- .../Models/SwarmHash.cs | 0 .../Models/TagInfo.cs} | 27 +- src/BeeNet.Core/Models/Topology.cs | 42 ++ src/BeeNet.Core/Models/TxInfo.cs | 41 ++ .../Models/WalletBalances.cs} | 14 +- .../Models/XDaiBalance.cs | 0 src/BeeNet/BeeClient.cs | 660 +++++++++++++++--- src/BeeNet/BeeNet.csproj | 6 +- src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs | 3 +- src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs | 4 +- src/BeeNet/IBeeClient.cs | 59 +- src/BeeNet/Models/Account.cs | 44 -- src/BeeNet/Models/ChequePayment.cs | 69 -- src/BeeNet/Models/ChequebookBalance.cs | 35 - src/BeeNet/Models/ChequebookCashoutGet.cs | 42 -- src/BeeNet/Models/ChequebookChequeGet.cs | 45 -- src/BeeNet/Models/ConnectedPeers.cs | 45 -- src/BeeNet/Models/Health.cs | 73 -- src/BeeNet/Models/LogData.cs | 44 -- src/BeeNet/Models/Loggers.cs | 48 -- src/BeeNet/Models/MessageResponse.cs | 50 -- src/BeeNet/Models/NodeInfo.cs | 45 -- src/BeeNet/Models/PeerBalance.cs | 59 -- src/BeeNet/Models/PeerMetrics.cs | 42 -- src/BeeNet/Models/PeersAggregate.cs | 42 -- src/BeeNet/Models/PostageBatchShort.cs | 49 -- src/BeeNet/Models/PostageProof.cs | 62 -- src/BeeNet/Models/RedistributionState.cs | 50 -- src/BeeNet/Models/ReserveCommitment.cs | 41 -- src/BeeNet/Models/ReserveCommitmentProof.cs | 78 --- src/BeeNet/Models/ReserveState.cs | 37 - src/BeeNet/Models/ResultChequebook.cs | 37 - src/BeeNet/Models/Settlement.cs | 50 -- src/BeeNet/Models/SettlementData.cs | 55 -- src/BeeNet/Models/SocProof.cs | 59 -- src/BeeNet/Models/StampsBuckets.cs | 40 -- src/BeeNet/Models/StatusNode.cs | 97 --- src/BeeNet/Models/TagInfo.cs | 70 -- src/BeeNet/Models/Topology.cs | 66 -- src/BeeNet/Models/TxInfo.cs | 64 -- src/BeeNet/Services/FeedService.cs | 19 +- src/BeeNet/Services/IFeedService.cs | 2 +- .../v1_13_2/GatewayApi/BalanceTest.cs | 7 - .../v1_13_2/GatewayApi/ChequebookTest.cs | 54 +- .../v1_13_2/GatewayApi/PinningTest.cs | 18 +- .../v1_13_2/GatewayApi/PostageStampsTest.cs | 9 +- .../v1_13_2/GatewayApi/SettlementsTest.cs | 6 +- .../v1_13_2/GatewayApi/StatusResultTest.cs | 2 +- .../v1_13_2/GatewayApi/StewardshipTest.cs | 8 +- .../v1_13_2/GatewayApi/WalletTest.cs | 4 +- test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs | 4 +- test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs | 12 +- test/BeeNet.Tests/Services/FeedServiceTest.cs | 51 +- 101 files changed, 1565 insertions(+), 2000 deletions(-) create mode 100644 src/BeeNet.Core/BeeNet.Core.csproj rename src/{BeeNet => BeeNet.Core}/Extensions/ArrayExtensions.cs (100%) rename src/{BeeNet => BeeNet.Core}/Extensions/LongExtensions.cs (100%) rename src/{BeeNet/Models/ChainState.cs => BeeNet.Core/Models/Account.cs} (51%) create mode 100644 src/BeeNet.Core/Models/AddressDetail.cs rename src/{BeeNet => BeeNet.Core}/Models/BzzBalance.cs (100%) create mode 100644 src/BeeNet.Core/Models/ChainState.cs create mode 100644 src/BeeNet.Core/Models/CheckPinsResult.cs create mode 100644 src/BeeNet.Core/Models/ChequePayment.cs rename src/{BeeNet/Models/Auth.cs => BeeNet.Core/Models/ChequebookBalance.cs} (73%) rename src/{BeeNet/Models/Wallet.cs => BeeNet.Core/Models/ChequebookCashout.cs} (60%) create mode 100644 src/BeeNet.Core/Models/ChequebookCheque.cs rename src/{BeeNet => BeeNet.Core/Models}/Feeds/EpochFeedIndex.cs (92%) rename src/{BeeNet => BeeNet.Core/Models}/Feeds/FeedIndexBase.cs (86%) rename src/{BeeNet => BeeNet.Core}/Models/FileResponse.cs (69%) rename src/{BeeNet => BeeNet.Core}/Models/GnosisChain.cs (100%) rename src/{BeeNet/Models/Bucket.cs => BeeNet.Core/Models/Health.cs} (68%) create mode 100644 src/BeeNet.Core/Models/InfoBeeMode.cs rename src/{BeeNet/Models/StewardshipGet.cs => BeeNet.Core/Models/LogData.cs} (70%) create mode 100644 src/BeeNet.Core/Models/Loggers.cs rename src/{BeeNet => BeeNet.Core}/Models/NetworkAvailability.cs (100%) create mode 100644 src/BeeNet.Core/Models/NodeInfo.cs rename src/{BeeNet/Models/AddressDetail.cs => BeeNet.Core/Models/Peer.cs} (50%) create mode 100644 src/BeeNet.Core/Models/PeersAggregate.cs rename src/{BeeNet => BeeNet.Core}/Models/PostageBatch.cs (70%) rename src/{BeeNet => BeeNet.Core}/Models/PostageBatchId.cs (100%) rename src/{BeeNet => BeeNet.Core}/Models/PostageBuckets.cs (100%) create mode 100644 src/BeeNet.Core/Models/PostageProof.cs rename src/{BeeNet/Hasher/Postage => BeeNet.Core/Models}/PostageStamp.cs (96%) rename src/{BeeNet => BeeNet.Core}/Models/Reachability.cs (100%) create mode 100644 src/BeeNet.Core/Models/RedistributionState.cs rename src/{BeeNet => BeeNet.Core}/Models/RedundancyLevel.cs (100%) rename src/{BeeNet => BeeNet.Core}/Models/RedundancyStrategy.cs (100%) rename src/{BeeNet/Models/CheckPinsResult.cs => BeeNet.Core/Models/ReserveCommitment.cs} (58%) create mode 100644 src/BeeNet.Core/Models/ReserveCommitmentProof.cs create mode 100644 src/BeeNet.Core/Models/ReserveState.cs create mode 100644 src/BeeNet.Core/Models/ResultChequebook.cs create mode 100644 src/BeeNet.Core/Models/Settlement.cs create mode 100644 src/BeeNet.Core/Models/SettlementData.cs create mode 100644 src/BeeNet.Core/Models/SocProof.cs rename src/{BeeNet/Hasher/Postage => BeeNet.Core/Models}/StampBucketIndex.cs (97%) create mode 100644 src/BeeNet.Core/Models/StampsBuckets.cs rename src/{BeeNet => BeeNet.Core}/Models/StatusBeeMode.cs (100%) create mode 100644 src/BeeNet.Core/Models/StatusNode.cs rename src/{BeeNet => BeeNet.Core}/Models/SwarmAddress.cs (100%) rename src/{BeeNet => BeeNet.Core}/Models/SwarmChunk.cs (95%) rename src/{BeeNet => BeeNet.Core}/Models/SwarmFeedChunk.cs (84%) rename src/{BeeNet => BeeNet.Core}/Models/SwarmHash.cs (100%) rename src/{BeeNet/Models/DisconnectedPeers.cs => BeeNet.Core/Models/TagInfo.cs} (62%) create mode 100644 src/BeeNet.Core/Models/Topology.cs create mode 100644 src/BeeNet.Core/Models/TxInfo.cs rename src/{BeeNet/Models/InfoBeeMode.cs => BeeNet.Core/Models/WalletBalances.cs} (75%) rename src/{BeeNet => BeeNet.Core}/Models/XDaiBalance.cs (100%) delete mode 100644 src/BeeNet/Models/Account.cs delete mode 100644 src/BeeNet/Models/ChequePayment.cs delete mode 100644 src/BeeNet/Models/ChequebookBalance.cs delete mode 100644 src/BeeNet/Models/ChequebookCashoutGet.cs delete mode 100644 src/BeeNet/Models/ChequebookChequeGet.cs delete mode 100644 src/BeeNet/Models/ConnectedPeers.cs delete mode 100644 src/BeeNet/Models/Health.cs delete mode 100644 src/BeeNet/Models/LogData.cs delete mode 100644 src/BeeNet/Models/Loggers.cs delete mode 100644 src/BeeNet/Models/MessageResponse.cs delete mode 100644 src/BeeNet/Models/NodeInfo.cs delete mode 100644 src/BeeNet/Models/PeerBalance.cs delete mode 100644 src/BeeNet/Models/PeerMetrics.cs delete mode 100644 src/BeeNet/Models/PeersAggregate.cs delete mode 100644 src/BeeNet/Models/PostageBatchShort.cs delete mode 100644 src/BeeNet/Models/PostageProof.cs delete mode 100644 src/BeeNet/Models/RedistributionState.cs delete mode 100644 src/BeeNet/Models/ReserveCommitment.cs delete mode 100644 src/BeeNet/Models/ReserveCommitmentProof.cs delete mode 100644 src/BeeNet/Models/ReserveState.cs delete mode 100644 src/BeeNet/Models/ResultChequebook.cs delete mode 100644 src/BeeNet/Models/Settlement.cs delete mode 100644 src/BeeNet/Models/SettlementData.cs delete mode 100644 src/BeeNet/Models/SocProof.cs delete mode 100644 src/BeeNet/Models/StampsBuckets.cs delete mode 100644 src/BeeNet/Models/StatusNode.cs delete mode 100644 src/BeeNet/Models/TagInfo.cs delete mode 100644 src/BeeNet/Models/Topology.cs delete mode 100644 src/BeeNet/Models/TxInfo.cs diff --git a/BeeNet.sln b/BeeNet.sln index 09c39e5..5e25d10 100644 --- a/BeeNet.sln +++ b/BeeNet.sln @@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.IntegrationTest", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.Tests", "test\BeeNet.Tests\BeeNet.Tests.csproj", "{549BB2F5-D48E-4785-AF14-EF116A79AD00}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Core", "src\BeeNet.Core\BeeNet.Core.csproj", "{0C681EE6-7290-4548-9502-4C9B6B061F12}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -61,6 +63,10 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -72,6 +78,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BC8368EB-8E3B-4FBB-B657-D1C9375AFAFC} diff --git a/src/BeeNet.Core/BeeNet.Core.csproj b/src/BeeNet.Core/BeeNet.Core.csproj new file mode 100644 index 0000000..79d6079 --- /dev/null +++ b/src/BeeNet.Core/BeeNet.Core.csproj @@ -0,0 +1,48 @@ + + + + net6.0;net7.0;net8.0 + true + Etherna.BeeNet + + Etherna SA + A .Net client for Swarm Bee + + 12 + enable + true + true + AllEnabledByDefault + + Bee.Net.Core + 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/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/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 cee96af..68490db 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 0000000..3c24964 --- /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 0000000..b8a4537 --- /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 0000000..4af7a11 --- /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 0000000..6dd15f0 --- /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 7f0800e..2ceb6e0 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 edf2e05..41dca12 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 0000000..edcd472 --- /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 92% rename from src/BeeNet/Feeds/EpochFeedIndex.cs rename to src/BeeNet.Core/Models/Feeds/EpochFeedIndex.cs index 1ff5398..6d4632c 100644 --- a/src/BeeNet/Feeds/EpochFeedIndex.cs +++ b/src/BeeNet.Core/Models/Feeds/EpochFeedIndex.cs @@ -13,10 +13,10 @@ // If not, see . using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Hasher; +using Nethereum.Util.HashProviders; using System; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public sealed class EpochFeedIndex : FeedIndexBase { @@ -80,22 +80,6 @@ public EpochFeedIndex Parent /// public byte Level { get; } - /// - /// Index representation as keccak256 hash - /// - public override Memory MarshalBinary - { - get - { - var epochBytes = Start.UnixDateTimeToByteArray(); - var newArray = new byte[epochBytes.Length + 1]; - epochBytes.CopyTo(newArray, 0); - newArray[epochBytes.Length] = Level; - - return new HashProvider().ComputeHash(newArray); - } - } - /// /// Epoch start in seconds /// @@ -137,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 675ecac..4e21371 100644 --- a/src/BeeNet/Feeds/FeedIndexBase.cs +++ b/src/BeeNet.Core/Models/Feeds/FeedIndexBase.cs @@ -12,14 +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; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public abstract class FeedIndexBase { // Properties. - public abstract Memory 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 a897798..0e13deb 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 dbd817e..d26e532 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/InfoBeeMode.cs b/src/BeeNet.Core/Models/InfoBeeMode.cs new file mode 100644 index 0000000..dae66e1 --- /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 6536a29..6a65706 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 0000000..deac777 --- /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 0000000..9d46c39 --- /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 b8e9963..c893530 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 0000000..3b95dcc --- /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 85da91c..4de6394 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/Models/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs similarity index 100% rename from src/BeeNet/Models/PostageBuckets.cs rename to src/BeeNet.Core/Models/PostageBuckets.cs diff --git a/src/BeeNet.Core/Models/PostageProof.cs b/src/BeeNet.Core/Models/PostageProof.cs new file mode 100644 index 0000000..a1ca83c --- /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 e43d583..a518592 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 0000000..eb072e7 --- /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 bca7b88..da5f260 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 0000000..8b6b610 --- /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 0000000..aca04f7 --- /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 0000000..53dc4de --- /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 0000000..16d1aa1 --- /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 0000000..2f0bf48 --- /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 0000000..2f502cb --- /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 2972a41..c697d15 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 0000000..355e55f --- /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 0000000..ac0633b --- /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 100% rename from src/BeeNet/Models/SwarmAddress.cs rename to src/BeeNet.Core/Models/SwarmAddress.cs 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 e45d1a9..b3ff467 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/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 56ae42a..e524c22 100644 --- a/src/BeeNet/Models/SwarmFeedChunk.cs +++ b/src/BeeNet.Core/Models/SwarmFeedChunk.cs @@ -13,10 +13,10 @@ // If not, see . using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Feeds; -using Etherna.BeeNet.Hasher; +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,23 +95,24 @@ 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) @@ -119,22 +120,23 @@ public static SwarmHash BuildHash(byte[] account, byte[] identifier) if (identifier.Length != IdentifierSize) throw new ArgumentOutOfRangeException(nameof(identifier), "Invalid identifier length"); - return new HashProvider().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.AsMemory()[topic.Length..]); + index.GetMarshalBinaryHash(hashProvider).CopyTo(newArray.AsMemory()[topic.Length..]); - return new HashProvider().ComputeHash(newArray); + return hashProvider.ComputeHash(newArray); } } } diff --git a/src/BeeNet/Models/SwarmHash.cs b/src/BeeNet.Core/Models/SwarmHash.cs similarity index 100% rename from src/BeeNet/Models/SwarmHash.cs rename to src/BeeNet.Core/Models/SwarmHash.cs 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 246f96a..b6801e2 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 0000000..2621ec6 --- /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 0000000..7fecb20 --- /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 a783192..8d281c8 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/BeeClient.cs b/src/BeeNet/BeeClient.cs index 04f7299..5897a86 100644 --- a/src/BeeNet/BeeClient.cs +++ b/src/BeeNet/BeeClient.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -25,6 +26,7 @@ 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; @@ -88,15 +90,24 @@ protected virtual void Dispose(bool disposing) // 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( + (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)); + }).ConfigureAwait(false)).Key; public async Task BuyPostageBatchAsync( BzzBalance amount, @@ -141,15 +152,19 @@ public async Task CheckChunkExistsAsync( } } - 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)); + 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, @@ -165,24 +180,34 @@ public async Task CreateFeedAsync( CancellationToken cancellationToken = default) => (await generatedClient.FeedsPostAsync(owner, topic, type, swarmPin, batchId.ToString(), cancellationToken).ConfigureAwait(false)).Reference; - public async Task CreatePinAsync( + public Task CreatePinAsync( SwarmHash hash, CancellationToken cancellationToken = default) => - new(await generatedClient.PinsPostAsync((string)hash, cancellationToken).ConfigureAwait(false)); + generatedClient.PinsPostAsync((string)hash, cancellationToken); public async Task CreateTagAsync( - CancellationToken cancellationToken = default) => - new(await generatedClient.TagsPostAsync(cancellationToken).ConfigureAwait(false)); + 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 async Task DeletePeerAsync( + public Task DeletePeerAsync( string peerAddress, CancellationToken cancellationToken = default) => - new(await generatedClient.PeersDeleteAsync(peerAddress, cancellationToken).ConfigureAwait(false)); + generatedClient.PeersDeleteAsync(peerAddress, cancellationToken); - public async Task DeletePinAsync( + public Task DeletePinAsync( SwarmHash hash, CancellationToken cancellationToken = default) => - new(await generatedClient.PinsDeleteAsync((string)hash, cancellationToken).ConfigureAwait(false)); + generatedClient.PinsDeleteAsync((string)hash, cancellationToken); public Task DeleteTagAsync( long uid, @@ -198,7 +223,7 @@ public async Task DeleteTransactionAsync( gasPrice?.ToWeiLong(), cancellationToken).ConfigureAwait(false)).TransactionHash; - public async Task DepositIntoChequeBookAsync( + public async Task DepositIntoChequebookAsync( BzzBalance amount, XDaiBalance? gasPrice = null, CancellationToken cancellationToken = default) => @@ -220,17 +245,51 @@ public async Task DilutePostageBatchAsync( gasLimit, cancellationToken).ConfigureAwait(false)).BatchID; - public async Task GetAddressesAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.AddressesAsync(cancellationToken).ConfigureAwait(false)); + 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) => - (await generatedClient.BalancesGetAsync(cancellationToken).ConfigureAwait(false)).Balances.Select(i => new PeerBalance(i)); + 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) => - (await generatedClient.ChequebookChequeGetAsync(cancellationToken).ConfigureAwait(false)).Lastcheques.Select(i => new ChequebookChequeGet(i)); + 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) => - (await generatedClient.ConsumedGetAsync(cancellationToken).ConfigureAwait(false)).Balances.Select(i => new PeerBalance(i)); + 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); @@ -239,19 +298,58 @@ public async Task> GetAllPinsAsync(CancellationToken canc (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 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) => - new(await generatedClient.TimesettlementsAsync(cancellationToken).ConfigureAwait(false)); + 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) => - (await generatedClient.BatchesAsync(cancellationToken).ConfigureAwait(false)).Batches.Select(i => new PostageBatchShort(i)); + 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( + public async Task GetBalanceWithPeerAsync( string peerAddress, CancellationToken cancellationToken = default) => - new(await generatedClient.BalancesGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); + BzzBalance.FromPlurString( + (await generatedClient.BalancesGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Balance); public async Task GetBytesAsync( SwarmHash hash, @@ -274,24 +372,66 @@ public Task GetBytesHeadAsync(SwarmHash hash, CancellationToken cancellationToke 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 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) => + 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 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( + public async Task GetChequebookCashoutForPeerAsync( string peerAddress, - CancellationToken cancellationToken = default) => - new(await generatedClient.ChequebookCashoutGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); + 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( + public async Task GetChequebookChequeForPeerAsync( string peerId, - CancellationToken cancellationToken = default) => - new(await generatedClient.ChequebookChequeGetAsync(peerId, cancellationToken).ConfigureAwait(false)); + 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, @@ -311,10 +451,11 @@ public async Task GetChunkStreamAsync( CancellationToken cancellationToken = default) => (await generatedClient.ChunksGetAsync(hash.ToString(), swarmCache, cancellationToken).ConfigureAwait(false)).Stream; - public async Task GetConsumedBalanceWithPeerAsync( + public async Task GetConsumedBalanceWithPeerAsync( string peerAddress, CancellationToken cancellationToken = default) => - new(await generatedClient.ConsumedGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); + BzzBalance.FromPlurString( + (await generatedClient.ConsumedGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)).Balance); public async Task GetFeedAsync( string owner, @@ -335,37 +476,101 @@ public async Task GetFileAsync( { ArgumentNullException.ThrowIfNull(address, nameof(address)); - return address.RelativePath is null - ? new(await generatedClient.BzzGetAsync( + if (address.RelativePath is null) + { + var response = await generatedClient.BzzGetAsync( address.Hash.ToString(), swarmCache, (SwarmRedundancyStrategy2?)swarmRedundancyStrategy, swarmRedundancyFallbackMode, swarmChunkRetrievalTimeout, - cancellationToken).ConfigureAwait(false)) - : new(await generatedClient.BzzGetAsync( + cancellationToken).ConfigureAwait(false); + return new FileResponse( + response.Headers, + response.Stream); + } + else + { + var response = await generatedClient.BzzGetAsync( address.Hash.ToString(), address.RelativePath.ToString(), (SwarmRedundancyStrategy3?)swarmRedundancyStrategy, swarmRedundancyFallbackMode, swarmChunkRetrievalTimeout, - cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.HealthAsync(cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.NodeAsync(cancellationToken).ConfigureAwait(false)); + 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) => - (await generatedClient.StampsGetAsync(cancellationToken).ConfigureAwait(false)).Stamps.Select(i => new PostageBatch(i)); + 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) => - (await generatedClient.TransactionsGetAsync(cancellationToken).ConfigureAwait(false)).PendingTransactions.Select(i => new TxInfo(i)); + 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, @@ -374,56 +579,267 @@ public async Task GetPinStatusAsync( public async Task GetPostageBatchAsync( PostageBatchId batchId, - CancellationToken cancellationToken = default) => - new(await generatedClient.StampsGetAsync(batchId.ToString(), cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.RchashAsync(depth, anchor1, anchor2, cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.ReservestateAsync(cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.SettlementsGetAsync(peerAddress, cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.StampsBucketsAsync(batchId.ToString(), cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.TopologyAsync(cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.TagsGetAsync(uid, cancellationToken).ConfigureAwait(false)); + 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) => - ((await generatedClient.TagsGetAsync(offset, limit, cancellationToken).ConfigureAwait(false)).Tags ?? Array.Empty()).Select(i => new TagInfo(i)); + 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) => - new(await generatedClient.TransactionsGetAsync(txHash, cancellationToken).ConfigureAwait(false)); + 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) => - new(await generatedClient.WalletAsync(cancellationToken).ConfigureAwait(false)); + 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 LoggersGetAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.LoggersGetAsync(cancellationToken).ConfigureAwait(false)); + public async Task IsContentRetrievableAsync( + SwarmHash hash, + CancellationToken cancellationToken = default) => + (await generatedClient.StewardshipGetAsync((string)hash, cancellationToken).ConfigureAwait(false)) + .IsRetrievable; - public async Task LoggersGetAsync(string exp, CancellationToken cancellationToken = default) => - new(await generatedClient.LoggersGetAsync(exp, cancellationToken).ConfigureAwait(false)); + 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); @@ -433,8 +849,21 @@ public async Task RebroadcastTransactionAsync( 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 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, @@ -494,11 +923,54 @@ await generatedClient.StakePostAsync( gasLimit, cancellationToken).ConfigureAwait(false); - public async Task StatusNodeAsync(CancellationToken cancellationToken = default) => - new(await generatedClient.StatusAsync(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) => - (await generatedClient.StatusPeersAsync(cancellationToken).ConfigureAwait(false)).Stamps.Select(p => new StatusNode(p)); + 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, @@ -668,7 +1140,7 @@ public async Task WalletWithdrawAsync( coin.ToWeiString(), cancellationToken).ConfigureAwait(false)).TransactionHash; - public async Task WithdrawFromChequeBookAsync( + public async Task WithdrawFromChequebookAsync( BzzBalance amount, XDaiBalance? gasPrice = null, CancellationToken cancellationToken = default) => diff --git a/src/BeeNet/BeeNet.csproj b/src/BeeNet/BeeNet.csproj index 9b660dd..bc978ba 100644 --- a/src/BeeNet/BeeNet.csproj +++ b/src/BeeNet/BeeNet.csproj @@ -5,7 +5,7 @@ true Etherna.BeeNet - Etherna Sagl + Etherna SA A .Net client for Swarm Bee 12 @@ -49,4 +49,8 @@ + + + + diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs b/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs index 364e144..f1bdc63 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs +++ b/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs @@ -47,8 +47,7 @@ public byte[] ConvertToByteArray(byte[] data) } // Consts. - public const int MaxDataSize = SegmentsCount * SegmentSize; - public const int SegmentsCount = 128; + public const int SegmentsCount = SwarmChunk.DataSize / SegmentSize; public const int SegmentSize = SwarmHash.HashSize; // Static fields. diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs index 23acbe1..2025c17 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs +++ b/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs @@ -27,8 +27,8 @@ public static SwarmHash Hash(byte[] span, byte[] data) 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(); diff --git a/src/BeeNet/IBeeClient.cs b/src/BeeNet/IBeeClient.cs index 5e3cf65..d0c922e 100644 --- a/src/BeeNet/IBeeClient.cs +++ b/src/BeeNet/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. @@ -809,7 +806,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/Models/Account.cs b/src/BeeNet/Models/Account.cs deleted file mode 100644 index 2a262d5..0000000 --- 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 daf0f1c..0000000 --- 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/ChequebookBalance.cs b/src/BeeNet/Models/ChequebookBalance.cs deleted file mode 100644 index fa25e91..0000000 --- a/src/BeeNet/Models/ChequebookBalance.cs +++ /dev/null @@ -1,35 +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 ChequebookBalance - { - // Constructors. - internal ChequebookBalance(Response27 response) - { - ArgumentNullException.ThrowIfNull(response, nameof(response)); - - TotalBalance = BzzBalance.FromPlurString(response.TotalBalance); - AvailableBalance = BzzBalance.FromPlurString(response.AvailableBalance); - } - - // Properties. - public BzzBalance TotalBalance { get; } - public BzzBalance AvailableBalance { get; } - } -} diff --git a/src/BeeNet/Models/ChequebookCashoutGet.cs b/src/BeeNet/Models/ChequebookCashoutGet.cs deleted file mode 100644 index 623a614..0000000 --- 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 af9a7c5..0000000 --- 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 4164515..0000000 --- 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 c2548d7..0000000 --- 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 d7f33e4..0000000 --- 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 3c25227..0000000 --- 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 e22a3d2..0000000 --- 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 c1b9435..0000000 --- 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 f1f161b..0000000 --- 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 ec72725..0000000 --- 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 4a66baf..0000000 --- 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 d0dee9d..0000000 --- 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/PostageProof.cs b/src/BeeNet/Models/PostageProof.cs deleted file mode 100644 index 33f5302..0000000 --- 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 0372a1c..0000000 --- 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 f5ac8c4..0000000 --- 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 800f6d2..0000000 --- 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 e06d1ed..0000000 --- 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 c1e13a8..0000000 --- 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 e5a8464..0000000 --- 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 6ceca30..0000000 --- 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 34f5883..0000000 --- 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 58260d5..0000000 --- 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 ed432c9..0000000 --- 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 d2fc2f9..0000000 --- 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 199bb58..0000000 --- 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 58de2fe..0000000 --- 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/src/BeeNet/Services/FeedService.cs b/src/BeeNet/Services/FeedService.cs index d18f812..b578f88 100644 --- a/src/BeeNet/Services/FeedService.cs +++ b/src/BeeNet/Services/FeedService.cs @@ -13,8 +13,9 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Feeds; +using Etherna.BeeNet.Hasher; 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 HashProvider()); 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 HashProvider()), index); public async Task TryGetFeedChunkAsync(SwarmHash hash, FeedIndexBase index) { diff --git a/src/BeeNet/Services/IFeedService.cs b/src/BeeNet/Services/IFeedService.cs index 4d9e1bb..0c06a46 100644 --- a/src/BeeNet/Services/IFeedService.cs +++ b/src/BeeNet/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/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs index 186f322..cb9bbe3 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs +++ b/test/BeeNet.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/ChequebookTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs index f42aaca..f575fb2 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs +++ b/test/BeeNet.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/PinningTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs index 506df3a..23ae999 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs +++ b/test/BeeNet.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.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs index e1abb72..8ca7e77 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs +++ b/test/BeeNet.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/SettlementsTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs index e671694..5ac0369 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs +++ b/test/BeeNet.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/StatusResultTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs index 6c28c29..39437f9 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs +++ b/test/BeeNet.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.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs index 543cda1..d028071 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs +++ b/test/BeeNet.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/WalletTest.cs b/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs index 5daa5aa..674d1d8 100644 --- a/test/BeeNet.IntegrationTest/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs +++ b/test/BeeNet.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.Tests/Feeds/EpochFeedIndexTest.cs b/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs index cbb51f3..f9f3706 100644 --- a/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs +++ b/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs @@ -12,6 +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.Hasher; +using Etherna.BeeNet.Models.Feeds; using System; using Xunit; @@ -124,7 +126,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.Tests/Feeds/SwarmFeedChunkTest.cs index 57a7750..1df6ef0 100644 --- a/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs +++ b/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs @@ -13,7 +13,9 @@ // If not, see . using Etherna.BeeNet.Extensions; +using Etherna.BeeNet.Hasher; using Etherna.BeeNet.Models; +using Etherna.BeeNet.Models.Feeds; using System; using System.Collections.Generic; using System.Linq; @@ -139,7 +141,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 +150,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 +164,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 +174,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 +183,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.Tests/Services/FeedServiceTest.cs b/test/BeeNet.Tests/Services/FeedServiceTest.cs index 8846330..51d4542 100644 --- a/test/BeeNet.Tests/Services/FeedServiceTest.cs +++ b/test/BeeNet.Tests/Services/FeedServiceTest.cs @@ -13,8 +13,9 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Feeds; +using Etherna.BeeNet.Hasher; 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); var leftChildEpochIndex = new EpochFeedIndex(4, 0); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); var leftChildEpochIndex = new EpochFeedIndex(4, 1); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new HashProvider()); 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 HashProvider()); 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 HashProvider()); var rightChildEpochIndex = new EpochFeedIndex(6, 1); - var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); var leftEpochIndex = startingEpochIndex.Left; - var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex); + var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex, new HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 HashProvider()); var parentEpochIndex = startingEpochIndex.Parent; - var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex); + var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex, new HashProvider()); 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 HashProvider()); 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 HashProvider()); 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 From 5b8bd4e99143c79e33003219f03f66eed95bafbe Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 2 Jul 2024 17:50:24 +0200 Subject: [PATCH 04/46] create other projects --- BeeNet.sln | 44 +++++++++----- README.md | 14 +++++ src/{BeeNet => BeeNet.Client}/BeeClient.cs | 0 src/BeeNet.Client/BeeNet.Client.csproj | 51 ++++++++++++++++ .../Clients/BeeGeneratedClient.cs | 0 .../Clients/Fixer/PostageBatchDto.cs | 0 src/BeeNet.Core/BeeNet.Core.csproj | 2 +- .../Exceptions/BeeNetApiException.cs | 8 +-- .../JsonConverters/BzzBalanceJsonConverter.cs | 0 .../PostageBatchIdJsonConverter.cs | 0 .../XDaiBalanceJsonConverter.cs | 0 src/BeeNet.Util/BeeNet.Util.csproj | 53 +++++++++++++++++ .../Hasher/Bmt/SwarmChunkBmt.cs | 4 +- .../Hasher/Bmt/SwarmChunkBmtHasher.cs | 0 .../Hasher/HashProvider.cs | 0 .../Pipeline/ChunkAggregatorPipelineStage.cs | 2 +- .../Hasher/Pipeline/ChunkBmtPipelineStage.cs | 0 .../Pipeline/ChunkFeederPipelineStage.cs | 0 .../Pipeline/ChunkStoreWriterPipelineStage.cs | 0 .../Hasher/Pipeline/HasherPipelineBuilder.cs | 0 .../Hasher/Pipeline/HasherPipelineFeedArgs.cs | 2 +- .../Hasher/Pipeline/IHasherPipeline.cs | 0 .../Hasher/Pipeline/IHasherPipelineStage.cs | 0 .../Hasher/Postage/FakePostageStampIssuer.cs | 0 .../Hasher/Postage/FakePostageStamper.cs | 0 .../Hasher/Postage/IPostageStampIssuer.cs | 0 .../Hasher/Postage/IPostageStamper.cs | 0 .../Hasher/Postage/PostageStampIssuer.cs | 0 .../Hasher/Postage/PostageStamper.cs | 2 +- .../Hasher/Signer/FakeSigner.cs | 0 .../Hasher/Signer/ISigner.cs | 0 .../Hasher/Store/ChunkJoiner.cs | 0 .../Hasher/Store/FakeChunkStore.cs | 0 .../Hasher/Store/IChunkStore.cs | 0 .../Hasher/Store/IStampStore.cs | 0 .../Hasher/Store/LocalDirectoryChunkStore.cs | 0 .../Hasher/Store/MemoryStampStore.cs | 0 .../Hasher/Store/StampStoreItem.cs | 1 - src/{BeeNet => BeeNet.Util}/IBeeClient.cs | 0 .../Manifest/IReadOnlyMantarayManifest.cs | 0 .../Manifest/IReadOnlyMantarayNode.cs | 0 .../Manifest/ManifestEntry.cs | 0 .../Manifest/MantarayManifest.cs | 0 .../Manifest/MantarayNode.cs | 0 .../Manifest/MantarayNodeFork.cs | 0 .../Manifest/NodeType.cs | 0 .../Manifest/ReferencedMantarayManifest.cs | 0 .../Manifest/ReferencedMantarayNode.cs | 0 .../Manifest/ReferencedMantarayNodeFork.cs | 0 .../Manifest/XorEncryptKey.cs | 0 .../Properties/AssemblyInfo.cs | 4 +- .../Services/CalculatorService.cs | 0 .../Services/FeedService.cs | 2 +- .../Services/FileContentTypeProvider.cs | 0 .../Services/ICalculatorService.cs | 0 .../Services/IFeedService.cs | 0 .../Services/UploadEvaluationResult.cs | 0 src/BeeNet/BeeNet.csproj | 56 ------------------ .../BeeNet.Client.IntegrationTest.csproj} | 2 +- .../IgnoreOtherVersionFactAttribute.cs | 0 .../v1_13_2/BaseTest_Gateway_v5_0_0.cs | 0 .../v1_13_2/GatewayApi/BalanceTest.cs | 0 .../v1_13_2/GatewayApi/BytesTest.cs | 0 .../BeeVersions/v1_13_2/GatewayApi/BzzTest.cs | 0 .../v1_13_2/GatewayApi/ChequebookTest.cs | 0 .../v1_13_2/GatewayApi/ChunkTest.cs | 0 .../v1_13_2/GatewayApi/ConnectivityTest.cs | 0 .../v1_13_2/GatewayApi/FeedTest.cs | 0 .../v1_13_2/GatewayApi/PinningTest.cs | 0 .../v1_13_2/GatewayApi/PostageStampsTest.cs | 0 .../GatewayApi/PostalServiceSwarmTest.cs | 0 .../v1_13_2/GatewayApi/SettlementsTest.cs | 0 .../GatewayApi/SingleOwnerChunkTest.cs | 0 .../v1_13_2/GatewayApi/StatusResultTest.cs | 0 .../v1_13_2/GatewayApi/StewardshipTest.cs | 0 .../BeeVersions/v1_13_2/GatewayApi/TagTest.cs | 0 .../v1_13_2/GatewayApi/TransactionTest.cs | 0 .../v1_13_2/GatewayApi/WalletTest.cs | 0 .../Data/BzzFIleForUpload.tar | Bin .../Data/TestFileForUpload_Gateway.txt | 0 .../Data/TestFileForUpload_GatewaySecond.txt | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/launchSettings.json | 0 .../BeeNet.Core.UnitTest.csproj | 26 ++++++++ .../BeeNet.Core.UnitTest/HashProvider.cs | 29 ++++++--- .../Models}/Feeds/EpochFeedIndexTest.cs | 4 +- .../Models}/Feeds/SwarmFeedChunkTest.cs | 5 +- .../Models/SwarmAddressTest.cs | 0 .../BeeNet.Util.UnitTest.csproj} | 2 +- .../Services/FeedServiceTest.cs | 0 90 files changed, 210 insertions(+), 103 deletions(-) rename src/{BeeNet => BeeNet.Client}/BeeClient.cs (100%) create mode 100644 src/BeeNet.Client/BeeNet.Client.csproj rename src/{BeeNet => BeeNet.Client}/Clients/BeeGeneratedClient.cs (100%) rename src/{BeeNet => BeeNet.Client}/Clients/Fixer/PostageBatchDto.cs (100%) rename src/{BeeNet => BeeNet.Core}/Exceptions/BeeNetApiException.cs (92%) rename src/{BeeNet => BeeNet.Core}/JsonConverters/BzzBalanceJsonConverter.cs (100%) rename src/{BeeNet => BeeNet.Core}/JsonConverters/PostageBatchIdJsonConverter.cs (100%) rename src/{BeeNet => BeeNet.Core}/JsonConverters/XDaiBalanceJsonConverter.cs (100%) create mode 100644 src/BeeNet.Util/BeeNet.Util.csproj rename src/{BeeNet => BeeNet.Util}/Hasher/Bmt/SwarmChunkBmt.cs (95%) rename src/{BeeNet => BeeNet.Util}/Hasher/Bmt/SwarmChunkBmtHasher.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/HashProvider.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs (98%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/ChunkBmtPipelineStage.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/ChunkFeederPipelineStage.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/HasherPipelineBuilder.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/HasherPipelineFeedArgs.cs (97%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/IHasherPipeline.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Pipeline/IHasherPipelineStage.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/FakePostageStampIssuer.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/FakePostageStamper.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/IPostageStampIssuer.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/IPostageStamper.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/PostageStampIssuer.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Postage/PostageStamper.cs (98%) rename src/{BeeNet => BeeNet.Util}/Hasher/Signer/FakeSigner.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Signer/ISigner.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/ChunkJoiner.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/FakeChunkStore.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/IChunkStore.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/IStampStore.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/LocalDirectoryChunkStore.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/MemoryStampStore.cs (100%) rename src/{BeeNet => BeeNet.Util}/Hasher/Store/StampStoreItem.cs (97%) rename src/{BeeNet => BeeNet.Util}/IBeeClient.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/IReadOnlyMantarayManifest.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/IReadOnlyMantarayNode.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/ManifestEntry.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/MantarayManifest.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/MantarayNode.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/MantarayNodeFork.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/NodeType.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/ReferencedMantarayManifest.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/ReferencedMantarayNode.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/ReferencedMantarayNodeFork.cs (100%) rename src/{BeeNet => BeeNet.Util}/Manifest/XorEncryptKey.cs (100%) rename src/{BeeNet => BeeNet.Util}/Properties/AssemblyInfo.cs (89%) rename src/{BeeNet => BeeNet.Util}/Services/CalculatorService.cs (100%) rename src/{BeeNet => BeeNet.Util}/Services/FeedService.cs (99%) rename src/{BeeNet => BeeNet.Util}/Services/FileContentTypeProvider.cs (100%) rename src/{BeeNet => BeeNet.Util}/Services/ICalculatorService.cs (100%) rename src/{BeeNet => BeeNet.Util}/Services/IFeedService.cs (100%) rename src/{BeeNet => BeeNet.Util}/Services/UploadEvaluationResult.cs (100%) delete mode 100644 src/BeeNet/BeeNet.csproj rename test/{BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj => BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj} (93%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/IgnoreOtherVersionFactAttribute.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/BaseTest_Gateway_v5_0_0.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/BalanceTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/BytesTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/BzzTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/ChequebookTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/ChunkTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/ConnectivityTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/FeedTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/PinningTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/PostageStampsTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/PostalServiceSwarmTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/SettlementsTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/SingleOwnerChunkTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/StatusResultTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/StewardshipTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/TagTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/TransactionTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/BeeVersions/v1_13_2/GatewayApi/WalletTest.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/Data/BzzFIleForUpload.tar (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/Data/TestFileForUpload_Gateway.txt (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/Data/TestFileForUpload_GatewaySecond.txt (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/Properties/AssemblyInfo.cs (100%) rename test/{BeeNet.IntegrationTest => BeeNet.Client.IntegrationTest}/Properties/launchSettings.json (100%) create mode 100644 test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj rename src/BeeNet/GlobalSuppressions.cs => test/BeeNet.Core.UnitTest/HashProvider.cs (55%) rename test/{BeeNet.Tests => BeeNet.Core.UnitTest/Models}/Feeds/EpochFeedIndexTest.cs (99%) rename test/{BeeNet.Tests => BeeNet.Core.UnitTest/Models}/Feeds/SwarmFeedChunkTest.cs (98%) rename test/{BeeNet.Tests => BeeNet.Core.UnitTest}/Models/SwarmAddressTest.cs (100%) rename test/{BeeNet.Tests/BeeNet.Tests.csproj => BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj} (91%) rename test/{BeeNet.Tests => BeeNet.Util.UnitTest}/Services/FeedServiceTest.cs (100%) diff --git a/BeeNet.sln b/BeeNet.sln index 5e25d10..f62e610 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}" @@ -39,46 +37,62 @@ 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}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BeeNet.Tests", "test\BeeNet.Tests\BeeNet.Tests.csproj", "{549BB2F5-D48E-4785-AF14-EF116A79AD00}" +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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU 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 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BC8368EB-8E3B-4FBB-B657-D1C9375AFAFC} diff --git a/README.md b/README.md index 049d26f..7440830 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/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs similarity index 100% rename from src/BeeNet/BeeClient.cs rename to src/BeeNet.Client/BeeClient.cs diff --git a/src/BeeNet.Client/BeeNet.Client.csproj b/src/BeeNet.Client/BeeNet.Client.csproj new file mode 100644 index 0000000..e1692bf --- /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.Core/BeeNet.Core.csproj b/src/BeeNet.Core/BeeNet.Core.csproj index 79d6079..55fdde8 100644 --- a/src/BeeNet.Core/BeeNet.Core.csproj +++ b/src/BeeNet.Core/BeeNet.Core.csproj @@ -6,7 +6,7 @@ Etherna.BeeNet Etherna SA - A .Net client for Swarm Bee + Core models to work with Ethereum Swarm in .Net 12 enable 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 f5b5aa6..92e5a30 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/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.Util/BeeNet.Util.csproj b/src/BeeNet.Util/BeeNet.Util.csproj new file mode 100644 index 0000000..ce0263e --- /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/Hasher/Bmt/SwarmChunkBmt.cs similarity index 95% rename from src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs rename to src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmt.cs index f1bdc63..ed1e2df 100644 --- a/src/BeeNet/Hasher/Bmt/SwarmChunkBmt.cs +++ b/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmt.cs @@ -22,10 +22,10 @@ namespace Etherna.BeeNet.Hasher.Bmt { - internal class SwarmChunkBmt : MerkleTree + internal sealed class SwarmChunkBmt : MerkleTree { // Classes. - private class ChunkBmtByteArrayConvertor : IByteArrayConvertor + private sealed class ChunkBmtByteArrayConvertor : IByteArrayConvertor { /// /// Verify that chunk segment data has right size diff --git a/src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmtHasher.cs similarity index 100% rename from src/BeeNet/Hasher/Bmt/SwarmChunkBmtHasher.cs rename to src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmtHasher.cs diff --git a/src/BeeNet/Hasher/HashProvider.cs b/src/BeeNet.Util/Hasher/HashProvider.cs similarity index 100% rename from src/BeeNet/Hasher/HashProvider.cs rename to src/BeeNet.Util/Hasher/HashProvider.cs diff --git a/src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs similarity index 98% rename from src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs rename to src/BeeNet.Util/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs index cf78ec9..8449b82 100644 --- a/src/BeeNet/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs +++ b/src/BeeNet.Util/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs @@ -27,7 +27,7 @@ namespace Etherna.BeeNet.Hasher.Pipeline internal sealed class ChunkAggregatorPipelineStage : IHasherPipelineStage { // Private classes. - private class ChunkHeader(SwarmHash hash, ReadOnlyMemory span, bool isParityChunk) + private sealed class ChunkHeader(SwarmHash hash, ReadOnlyMemory span, bool isParityChunk) { public SwarmHash Hash { get; } = hash; public ReadOnlyMemory Span { get; } = span; diff --git a/src/BeeNet/Hasher/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hasher/Pipeline/ChunkBmtPipelineStage.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/ChunkBmtPipelineStage.cs rename to src/BeeNet.Util/Hasher/Pipeline/ChunkBmtPipelineStage.cs diff --git a/src/BeeNet/Hasher/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hasher/Pipeline/ChunkFeederPipelineStage.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/ChunkFeederPipelineStage.cs rename to src/BeeNet.Util/Hasher/Pipeline/ChunkFeederPipelineStage.cs diff --git a/src/BeeNet/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs rename to src/BeeNet.Util/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs diff --git a/src/BeeNet/Hasher/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineBuilder.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/HasherPipelineBuilder.cs rename to src/BeeNet.Util/Hasher/Pipeline/HasherPipelineBuilder.cs diff --git a/src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs similarity index 97% rename from src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs rename to src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs index a712e9c..79d76d2 100644 --- a/src/BeeNet/Hasher/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs @@ -17,7 +17,7 @@ namespace Etherna.BeeNet.Hasher.Pipeline { - internal class HasherPipelineFeedArgs + internal sealed class HasherPipelineFeedArgs { // Fields. private readonly byte[] _data; diff --git a/src/BeeNet/Hasher/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hasher/Pipeline/IHasherPipeline.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/IHasherPipeline.cs rename to src/BeeNet.Util/Hasher/Pipeline/IHasherPipeline.cs diff --git a/src/BeeNet/Hasher/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hasher/Pipeline/IHasherPipelineStage.cs similarity index 100% rename from src/BeeNet/Hasher/Pipeline/IHasherPipelineStage.cs rename to src/BeeNet.Util/Hasher/Pipeline/IHasherPipelineStage.cs diff --git a/src/BeeNet/Hasher/Postage/FakePostageStampIssuer.cs b/src/BeeNet.Util/Hasher/Postage/FakePostageStampIssuer.cs similarity index 100% rename from src/BeeNet/Hasher/Postage/FakePostageStampIssuer.cs rename to src/BeeNet.Util/Hasher/Postage/FakePostageStampIssuer.cs diff --git a/src/BeeNet/Hasher/Postage/FakePostageStamper.cs b/src/BeeNet.Util/Hasher/Postage/FakePostageStamper.cs similarity index 100% rename from src/BeeNet/Hasher/Postage/FakePostageStamper.cs rename to src/BeeNet.Util/Hasher/Postage/FakePostageStamper.cs diff --git a/src/BeeNet/Hasher/Postage/IPostageStampIssuer.cs b/src/BeeNet.Util/Hasher/Postage/IPostageStampIssuer.cs similarity index 100% rename from src/BeeNet/Hasher/Postage/IPostageStampIssuer.cs rename to src/BeeNet.Util/Hasher/Postage/IPostageStampIssuer.cs diff --git a/src/BeeNet/Hasher/Postage/IPostageStamper.cs b/src/BeeNet.Util/Hasher/Postage/IPostageStamper.cs similarity index 100% rename from src/BeeNet/Hasher/Postage/IPostageStamper.cs rename to src/BeeNet.Util/Hasher/Postage/IPostageStamper.cs diff --git a/src/BeeNet/Hasher/Postage/PostageStampIssuer.cs b/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs similarity index 100% rename from src/BeeNet/Hasher/Postage/PostageStampIssuer.cs rename to src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs diff --git a/src/BeeNet/Hasher/Postage/PostageStamper.cs b/src/BeeNet.Util/Hasher/Postage/PostageStamper.cs similarity index 98% rename from src/BeeNet/Hasher/Postage/PostageStamper.cs rename to src/BeeNet.Util/Hasher/Postage/PostageStamper.cs index 3913ffa..a6740e9 100644 --- a/src/BeeNet/Hasher/Postage/PostageStamper.cs +++ b/src/BeeNet.Util/Hasher/Postage/PostageStamper.cs @@ -21,7 +21,7 @@ namespace Etherna.BeeNet.Hasher.Postage { - internal class PostageStamper( + internal sealed class PostageStamper( ISigner signer, IPostageStampIssuer stampIssuer, IStampStore stampStore) diff --git a/src/BeeNet/Hasher/Signer/FakeSigner.cs b/src/BeeNet.Util/Hasher/Signer/FakeSigner.cs similarity index 100% rename from src/BeeNet/Hasher/Signer/FakeSigner.cs rename to src/BeeNet.Util/Hasher/Signer/FakeSigner.cs diff --git a/src/BeeNet/Hasher/Signer/ISigner.cs b/src/BeeNet.Util/Hasher/Signer/ISigner.cs similarity index 100% rename from src/BeeNet/Hasher/Signer/ISigner.cs rename to src/BeeNet.Util/Hasher/Signer/ISigner.cs diff --git a/src/BeeNet/Hasher/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs similarity index 100% rename from src/BeeNet/Hasher/Store/ChunkJoiner.cs rename to src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs diff --git a/src/BeeNet/Hasher/Store/FakeChunkStore.cs b/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs similarity index 100% rename from src/BeeNet/Hasher/Store/FakeChunkStore.cs rename to src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs diff --git a/src/BeeNet/Hasher/Store/IChunkStore.cs b/src/BeeNet.Util/Hasher/Store/IChunkStore.cs similarity index 100% rename from src/BeeNet/Hasher/Store/IChunkStore.cs rename to src/BeeNet.Util/Hasher/Store/IChunkStore.cs diff --git a/src/BeeNet/Hasher/Store/IStampStore.cs b/src/BeeNet.Util/Hasher/Store/IStampStore.cs similarity index 100% rename from src/BeeNet/Hasher/Store/IStampStore.cs rename to src/BeeNet.Util/Hasher/Store/IStampStore.cs diff --git a/src/BeeNet/Hasher/Store/LocalDirectoryChunkStore.cs b/src/BeeNet.Util/Hasher/Store/LocalDirectoryChunkStore.cs similarity index 100% rename from src/BeeNet/Hasher/Store/LocalDirectoryChunkStore.cs rename to src/BeeNet.Util/Hasher/Store/LocalDirectoryChunkStore.cs diff --git a/src/BeeNet/Hasher/Store/MemoryStampStore.cs b/src/BeeNet.Util/Hasher/Store/MemoryStampStore.cs similarity index 100% rename from src/BeeNet/Hasher/Store/MemoryStampStore.cs rename to src/BeeNet.Util/Hasher/Store/MemoryStampStore.cs diff --git a/src/BeeNet/Hasher/Store/StampStoreItem.cs b/src/BeeNet.Util/Hasher/Store/StampStoreItem.cs similarity index 97% rename from src/BeeNet/Hasher/Store/StampStoreItem.cs rename to src/BeeNet.Util/Hasher/Store/StampStoreItem.cs index 42d7575..782a885 100644 --- a/src/BeeNet/Hasher/Store/StampStoreItem.cs +++ b/src/BeeNet.Util/Hasher/Store/StampStoreItem.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.Hasher.Postage; using Etherna.BeeNet.Models; using System; diff --git a/src/BeeNet/IBeeClient.cs b/src/BeeNet.Util/IBeeClient.cs similarity index 100% rename from src/BeeNet/IBeeClient.cs rename to src/BeeNet.Util/IBeeClient.cs 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 100% rename from src/BeeNet/Manifest/IReadOnlyMantarayNode.cs rename to src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs diff --git a/src/BeeNet/Manifest/ManifestEntry.cs b/src/BeeNet.Util/Manifest/ManifestEntry.cs similarity index 100% rename from src/BeeNet/Manifest/ManifestEntry.cs rename to src/BeeNet.Util/Manifest/ManifestEntry.cs diff --git a/src/BeeNet/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs similarity index 100% rename from src/BeeNet/Manifest/MantarayManifest.cs rename to src/BeeNet.Util/Manifest/MantarayManifest.cs diff --git a/src/BeeNet/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs similarity index 100% rename from src/BeeNet/Manifest/MantarayNode.cs rename to src/BeeNet.Util/Manifest/MantarayNode.cs 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 100% rename from src/BeeNet/Manifest/ReferencedMantarayManifest.cs rename to src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs diff --git a/src/BeeNet/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs similarity index 100% rename from src/BeeNet/Manifest/ReferencedMantarayNode.cs rename to src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs 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/Manifest/XorEncryptKey.cs b/src/BeeNet.Util/Manifest/XorEncryptKey.cs similarity index 100% rename from src/BeeNet/Manifest/XorEncryptKey.cs rename to src/BeeNet.Util/Manifest/XorEncryptKey.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 8bf4037..ae2d241 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 100% rename from src/BeeNet/Services/CalculatorService.cs rename to src/BeeNet.Util/Services/CalculatorService.cs diff --git a/src/BeeNet/Services/FeedService.cs b/src/BeeNet.Util/Services/FeedService.cs similarity index 99% rename from src/BeeNet/Services/FeedService.cs rename to src/BeeNet.Util/Services/FeedService.cs index b578f88..43db131 100644 --- a/src/BeeNet/Services/FeedService.cs +++ b/src/BeeNet.Util/Services/FeedService.cs @@ -237,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 100% rename from src/BeeNet/Services/ICalculatorService.cs rename to src/BeeNet.Util/Services/ICalculatorService.cs diff --git a/src/BeeNet/Services/IFeedService.cs b/src/BeeNet.Util/Services/IFeedService.cs similarity index 100% rename from src/BeeNet/Services/IFeedService.cs rename to src/BeeNet.Util/Services/IFeedService.cs diff --git a/src/BeeNet/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs similarity index 100% rename from src/BeeNet/Services/UploadEvaluationResult.cs rename to src/BeeNet.Util/Services/UploadEvaluationResult.cs diff --git a/src/BeeNet/BeeNet.csproj b/src/BeeNet/BeeNet.csproj deleted file mode 100644 index bc978ba..0000000 --- a/src/BeeNet/BeeNet.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - - net6.0;net7.0;net8.0 - true - Etherna.BeeNet - - Etherna SA - A .Net client for Swarm Bee - - 12 - enable - true - true - AllEnabledByDefault - - Bee.Net - 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/test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj similarity index 93% rename from test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj rename to test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj index 5dd6bfd..68904aa 100644 --- a/test/BeeNet.IntegrationTest/BeeNet.IntegrationTest.csproj +++ b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj @@ -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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 0000000..7eb4022 --- /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/src/BeeNet/GlobalSuppressions.cs b/test/BeeNet.Core.UnitTest/HashProvider.cs similarity index 55% rename from src/BeeNet/GlobalSuppressions.cs rename to test/BeeNet.Core.UnitTest/HashProvider.cs index 2a923b4..acd878b 100644 --- a/src/BeeNet/GlobalSuppressions.cs +++ b/test/BeeNet.Core.UnitTest/HashProvider.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,11 +12,24 @@ // 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 Etherna.BeeNet.Models; +using Nethereum.Util.HashProviders; +using Org.BouncyCastle.Crypto.Digests; -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")] +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 99% rename from test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs rename to test/BeeNet.Core.UnitTest/Models/Feeds/EpochFeedIndexTest.cs index f9f3706..3fd499b 100644 --- a/test/BeeNet.Tests/Feeds/EpochFeedIndexTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/Feeds/EpochFeedIndexTest.cs @@ -12,12 +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; -using Etherna.BeeNet.Models.Feeds; using System; using Xunit; -namespace Etherna.BeeNet.Feeds +namespace Etherna.BeeNet.Models.Feeds { public class EpochFeedIndexTest { diff --git a/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs b/test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs similarity index 98% rename from test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs rename to test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs index 1df6ef0..619a643 100644 --- a/test/BeeNet.Tests/Feeds/SwarmFeedChunkTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/Feeds/SwarmFeedChunkTest.cs @@ -13,16 +13,13 @@ // If not, see . using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Hasher; -using Etherna.BeeNet.Models; -using Etherna.BeeNet.Models.Feeds; 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 { diff --git a/test/BeeNet.Tests/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs similarity index 100% rename from test/BeeNet.Tests/Models/SwarmAddressTest.cs rename to test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs diff --git a/test/BeeNet.Tests/BeeNet.Tests.csproj b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj similarity index 91% rename from test/BeeNet.Tests/BeeNet.Tests.csproj rename to test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj index 8732169..ce2509f 100644 --- a/test/BeeNet.Tests/BeeNet.Tests.csproj +++ b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj @@ -19,7 +19,7 @@ - + diff --git a/test/BeeNet.Tests/Services/FeedServiceTest.cs b/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs similarity index 100% rename from test/BeeNet.Tests/Services/FeedServiceTest.cs rename to test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs From 4322ed1b46b46c73122bc988b1d4148ee2a4552f Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 2 Jul 2024 18:52:55 +0200 Subject: [PATCH 05/46] Update PostageStampIssuer constructor parameter --- src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs b/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs index b1ec918..4e1e738 100644 --- a/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs +++ b/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs @@ -25,11 +25,11 @@ 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; } From 3e1b22e49e6ab3bb1b01bd6c213e22b234402b97 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Thu, 4 Jul 2024 02:20:01 +0200 Subject: [PATCH 06/46] Remove redundant file links in BeeNet.sln --- BeeNet.sln | 3 --- 1 file changed, 3 deletions(-) diff --git a/BeeNet.sln b/BeeNet.sln index f62e610..e6ffb94 100644 --- a/BeeNet.sln +++ b/BeeNet.sln @@ -11,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}" From d28a4ee459b5d2a5950c6b83f979fa69e21bca53 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Thu, 4 Jul 2024 02:44:18 +0200 Subject: [PATCH 07/46] Add ComputeHash overload methods in HashProvider --- src/BeeNet.Util/Hasher/HashProvider.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/BeeNet.Util/Hasher/HashProvider.cs b/src/BeeNet.Util/Hasher/HashProvider.cs index 22db43c..39b3f72 100644 --- a/src/BeeNet.Util/Hasher/HashProvider.cs +++ b/src/BeeNet.Util/Hasher/HashProvider.cs @@ -15,6 +15,7 @@ using Etherna.BeeNet.Models; using Nethereum.Util.HashProviders; using Org.BouncyCastle.Crypto.Digests; +using System; namespace Etherna.BeeNet.Hasher { @@ -24,12 +25,20 @@ public class HashProvider : IHashProvider 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]; - hasher.BlockUpdate(data); - hasher.DoFinal(result); + ComputeHash(data, result); return result; } + + public byte[] ComputeHash(string data) => + ComputeHash(System.Text.Encoding.UTF8.GetBytes(data)); } } \ No newline at end of file From fb61011498f71478f6b5f0501244a625c0f8cde9 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 6 Jul 2024 14:41:27 +0200 Subject: [PATCH 08/46] update SwarmAddress with path as string instead of Uri --- src/BeeNet.Client/BeeClient.cs | 4 +-- src/BeeNet.Core/Models/SwarmAddress.cs | 26 +++++++---------- .../Manifest/ReferencedMantarayManifest.cs | 4 +-- .../Models/SwarmAddressTest.cs | 28 ++++++------------- 4 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs index 5897a86..8c75050 100644 --- a/src/BeeNet.Client/BeeClient.cs +++ b/src/BeeNet.Client/BeeClient.cs @@ -476,7 +476,7 @@ public async Task GetFileAsync( { ArgumentNullException.ThrowIfNull(address, nameof(address)); - if (address.RelativePath is null) + if (address.Path is null) { var response = await generatedClient.BzzGetAsync( address.Hash.ToString(), @@ -493,7 +493,7 @@ public async Task GetFileAsync( { var response = await generatedClient.BzzGetAsync( address.Hash.ToString(), - address.RelativePath.ToString(), + address.Path.ToString(), (SwarmRedundancyStrategy3?)swarmRedundancyStrategy, swarmRedundancyFallbackMode, swarmChunkRetrievalTimeout, diff --git a/src/BeeNet.Core/Models/SwarmAddress.cs b/src/BeeNet.Core/Models/SwarmAddress.cs index eac9d41..1c4e144 100644 --- a/src/BeeNet.Core/Models/SwarmAddress.cs +++ b/src/BeeNet.Core/Models/SwarmAddress.cs @@ -14,21 +14,16 @@ using System; using System.Collections.Generic; -using System.IO; namespace Etherna.BeeNet.Models { public readonly struct SwarmAddress : IEquatable { // 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 = path; } public SwarmAddress(string address) { @@ -45,30 +40,29 @@ public SwarmAddress(string address) // Set hash and path. Hash = new SwarmHash(root); if (!string.IsNullOrEmpty(path)) - RelativePath = new Uri(path, UriKind.Relative); + Path = 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); + (Path?.GetHashCode(StringComparison.InvariantCulture) ?? 0); public override string ToString() { - if (RelativePath is null) + if (Path is null) return Hash + "/"; - var pathString = RelativePath.ToString(); - if (Path.IsPathRooted(pathString)) - return Hash + pathString; + if (System.IO.Path.IsPathRooted(Path)) + return Hash + Path; - return Hash + "/" + pathString; + return Hash + "/" + Path; } // Static methods. diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs index c8ad660..51c48f8 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs @@ -44,7 +44,7 @@ public async Task> GetResourceMetadataAsync( await _rootNode.DecodeFromChunkAsync().ConfigureAwait(false); return await RootNode.GetResourceMetadataAsync( - address.RelativePath?.ToString() ?? "").ConfigureAwait(false); + address.Path?.ToString() ?? "").ConfigureAwait(false); } public async Task ResolveResourceHashAsync(SwarmAddress address) @@ -53,7 +53,7 @@ public async Task ResolveResourceHashAsync(SwarmAddress address) await _rootNode.DecodeFromChunkAsync().ConfigureAwait(false); return await RootNode.ResolveResourceHashAsync( - address.RelativePath?.ToString() ?? "").ConfigureAwait(false); + address.Path?.ToString() ?? "").ConfigureAwait(false); } } } \ No newline at end of file diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs index 6f27deb..6bdb628 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs @@ -33,11 +33,11 @@ public class AddressToStringTestElement( public class StringToAddressTestElement( string inputString, SwarmHash expectedHash, - Uri? expectedRelativePath) + string? expectedRelativePath) { public string InputString { get; } = inputString; public SwarmHash ExpectedHash { get; } = expectedHash; - public Uri? ExpectedRelativePath { get; } = expectedRelativePath; + public string? ExpectedRelativePath { get; } = expectedRelativePath; } // Data. @@ -54,17 +54,17 @@ public static IEnumerable AddressToStringTests // With path without root. tests.Add(new( - new SwarmAddress(SwarmHash.Zero, new Uri("Im/a/relative/path", UriKind.Relative)), + new SwarmAddress(SwarmHash.Zero, "Im/a/relative/path"), "0000000000000000000000000000000000000000000000000000000000000000/Im/a/relative/path")); // With path with root. tests.Add(new( - new SwarmAddress(SwarmHash.Zero, new Uri("/I/have/a/root", UriKind.Relative)), + new SwarmAddress(SwarmHash.Zero, "/I/have/a/root"), "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)), + new SwarmAddress(SwarmHash.Zero, "I have a % of special\\chars!"), "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!")); return tests.Select(t => new object[] { t }); @@ -105,19 +105,19 @@ public static IEnumerable StringToAddressTests tests.Add(new( "0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", SwarmHash.Zero, - new Uri("Im/a/path", UriKind.Relative))); + "Im/a/path")); // With initial root and path. tests.Add(new( "/0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", SwarmHash.Zero, - new Uri("Im/a/path", UriKind.Relative))); + "Im/a/path")); // 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))); + "I have a % of special\\chars!")); return tests.Select(t => new object[] { t }); } @@ -138,17 +138,7 @@ 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); - }); + Assert.Equal(test.ExpectedRelativePath, result.Path); } } } \ No newline at end of file From b03da123524373507bd2a8090d1258098ac383f3 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 6 Jul 2024 14:53:17 +0200 Subject: [PATCH 09/46] update SwarmAddress and tests --- src/BeeNet.Core/Models/SwarmAddress.cs | 2 +- .../Models/SwarmAddressTest.cs | 50 +++++++++++-------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/BeeNet.Core/Models/SwarmAddress.cs b/src/BeeNet.Core/Models/SwarmAddress.cs index 1c4e144..7a8b998 100644 --- a/src/BeeNet.Core/Models/SwarmAddress.cs +++ b/src/BeeNet.Core/Models/SwarmAddress.cs @@ -30,7 +30,7 @@ public SwarmAddress(string address) ArgumentNullException.ThrowIfNull(address, nameof(address)); // Trim initial and final slash. - address = address.Trim('/'); + address = address.TrimStart('/'); // Extract hash root. var slashIndex = address.IndexOf('/', StringComparison.InvariantCulture); diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs index 6bdb628..4f28d70 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.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 System; using System.Collections.Generic; using System.Linq; using Xunit; @@ -45,27 +44,28 @@ 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, "Im/a/relative/path"), - "0000000000000000000000000000000000000000000000000000000000000000/Im/a/relative/path")); - - // With path with root. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero, "/I/have/a/root"), - "0000000000000000000000000000000000000000000000000000000000000000/I/have/a/root")); - - // With special chars. - tests.Add(new( - new SwarmAddress(SwarmHash.Zero, "I have a % of special\\chars!"), - "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!")); + 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 }); } @@ -113,6 +113,12 @@ public static IEnumerable StringToAddressTests SwarmHash.Zero, "Im/a/path")); + // With final slash. + tests.Add(new( + "0000000000000000000000000000000000000000000000000000000000000000/I/have/final/slash/", + SwarmHash.Zero, + "I/have/final/slash/")); + // With special chars. tests.Add(new( "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!", From 83deea3c967fec2b687356aa14cefd2574bc73f2 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 6 Jul 2024 18:56:14 +0200 Subject: [PATCH 10/46] implement SwarmUri and refactor SwarmAddress --- src/BeeNet.Core/Models/SwarmAddress.cs | 34 ++--- src/BeeNet.Core/Models/SwarmUri.cs | 123 ++++++++++++++++++ .../Models/SwarmAddressTest.cs | 16 +-- 3 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 src/BeeNet.Core/Models/SwarmUri.cs diff --git a/src/BeeNet.Core/Models/SwarmAddress.cs b/src/BeeNet.Core/Models/SwarmAddress.cs index 7a8b998..2816525 100644 --- a/src/BeeNet.Core/Models/SwarmAddress.cs +++ b/src/BeeNet.Core/Models/SwarmAddress.cs @@ -23,29 +23,28 @@ namespace Etherna.BeeNet.Models public SwarmAddress(SwarmHash hash, string? path = null) { Hash = hash; - Path = path; + Path = NormalizePath(path); } public SwarmAddress(string address) { ArgumentNullException.ThrowIfNull(address, nameof(address)); - // Trim initial and final slash. + // Trim initial slash. address = address.TrimStart('/'); // 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 hash = slashIndex > 0 ? address[..slashIndex] : address; + var path = slashIndex > 0 ? address[slashIndex..] : "/"; // Set hash and path. - Hash = new SwarmHash(root); - if (!string.IsNullOrEmpty(path)) - Path = path; + Hash = new SwarmHash(hash); + Path = NormalizePath(path); } // Properties. public SwarmHash Hash { get; } - public string? Path { get; } + public string Path { get; } // Methods. public bool Equals(SwarmAddress other) => @@ -54,30 +53,25 @@ public bool Equals(SwarmAddress other) => public override bool Equals(object? obj) => obj is SwarmAddress other && Equals(other); public override int GetHashCode() => Hash.GetHashCode() ^ (Path?.GetHashCode(StringComparison.InvariantCulture) ?? 0); - public override string ToString() - { - if (Path is null) - return Hash + "/"; - - if (System.IO.Path.IsPathRooted(Path)) - return Hash + Path; - - return Hash + "/" + Path; - } + 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) => + '/' + (path ?? "").TrimStart('/'); } } \ No newline at end of file diff --git a/src/BeeNet.Core/Models/SwarmUri.cs b/src/BeeNet.Core/Models/SwarmUri.cs new file mode 100644 index 0000000..75a4d98 --- /dev/null +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -0,0 +1,123 @@ +// 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, bool isAbsolute) + { + ArgumentNullException.ThrowIfNull(uri, nameof(uri)); + + if (isAbsolute) + { + var address = new SwarmAddress(uri); + Hash = address.Hash; + Path = address.Path; + } + else + { + Path = uri; + } + } + + // Properties. + public SwarmHash? Hash { get; } + public bool IsAbsolute => Hash.HasValue; + public bool IsRooted => IsAbsolute || System.IO.Path.IsPathRooted(Path); + public string Path { get; } + + // 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() => + IsAbsolute ? new SwarmAddress(Hash!.Value, Path).ToString() : Path!; + + public SwarmAddress ToSwarmAddress(SwarmAddress prefix) + { + var combined = Combine(prefix, this); + return new(combined.Hash!.Value, combined.Path); + } + + public bool TryGetRelativeTo(SwarmUri relativeTo, out SwarmUri output) + { + output = default; + + if (relativeTo.Hash != Hash) + return false; + + if (!Path.StartsWith(relativeTo.Path, StringComparison.InvariantCulture)) + return false; + + output = new SwarmUri(null, Path[relativeTo.Path.Length..].TrimStart('/')); + 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.IsAbsolute) + combined = uri; + else if (uri.IsRooted) + combined = new SwarmUri(combined.Hash, uri.Path); + else + combined = new SwarmUri(combined.Hash, string.Concat(combined.Path ?? "", uri.Path!)); + } + + return combined; + } + 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(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/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs index 4f28d70..ce9f3a1 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs @@ -81,49 +81,49 @@ public static IEnumerable StringToAddressTests 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, - "Im/a/path")); + "/Im/a/path")); // With initial root and path. tests.Add(new( "/0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", SwarmHash.Zero, - "Im/a/path")); + "/Im/a/path")); // With final slash. tests.Add(new( "0000000000000000000000000000000000000000000000000000000000000000/I/have/final/slash/", SwarmHash.Zero, - "I/have/final/slash/")); + "/I/have/final/slash/")); // With special chars. tests.Add(new( "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!", SwarmHash.Zero, - "I have a % of special\\chars!")); + "/I have a % of special\\chars!")); return tests.Select(t => new object[] { t }); } From 9e160797d383b79da28479bd790ea5b41cd700c6 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 6 Jul 2024 20:20:21 +0200 Subject: [PATCH 11/46] first SwarmUriTest implementation and some fixes --- src/BeeNet.Core/Models/SwarmHash.cs | 13 +- .../Models/SwarmAddressTest.cs | 95 +++--- .../Models/SwarmUriTest.cs | 276 ++++++++++++++++++ 3 files changed, 330 insertions(+), 54 deletions(-) create mode 100644 test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs diff --git a/src/BeeNet.Core/Models/SwarmHash.cs b/src/BeeNet.Core/Models/SwarmHash.cs index 5c7fee9..d9a0ae8 100644 --- a/src/BeeNet.Core/Models/SwarmHash.cs +++ b/src/BeeNet.Core/Models/SwarmHash.cs @@ -40,11 +40,18 @@ public SwarmHash(byte[] hash) 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) - throw new ArgumentOutOfRangeException(nameof(hash)); + throw new ArgumentException("Invalid hash", nameof(hash)); } // Static properties. diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs index ce9f3a1..ea86aff 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmAddressTest.cs @@ -32,11 +32,11 @@ public class AddressToStringTestElement( public class StringToAddressTestElement( string inputString, SwarmHash expectedHash, - string? expectedRelativePath) + string expectedRelativePath) { public string InputString { get; } = inputString; public SwarmHash ExpectedHash { get; } = expectedHash; - public string? ExpectedRelativePath { get; } = expectedRelativePath; + public string ExpectedRelativePath { get; } = expectedRelativePath; } // Data. @@ -75,55 +75,48 @@ public static IEnumerable StringToAddressTests { get { - var tests = new List(); - - // Only hash without ending slash. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000", - SwarmHash.Zero, - "/")); - - // Only hash with ending slash. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/", - SwarmHash.Zero, - "/")); - - // With initial root. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000", - SwarmHash.Zero, - "/")); - - // With initial root and ending slash. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000/", - SwarmHash.Zero, - "/")); - - // With path. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", - SwarmHash.Zero, - "/Im/a/path")); - - // With initial root and path. - tests.Add(new( - "/0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", - SwarmHash.Zero, - "/Im/a/path")); - - // With final slash. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/I/have/final/slash/", - SwarmHash.Zero, - "/I/have/final/slash/")); - - // With special chars. - tests.Add(new( - "0000000000000000000000000000000000000000000000000000000000000000/I have a % of special\\chars!", - SwarmHash.Zero, - "/I have a % of special\\chars!")); + 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 }); } diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs new file mode 100644 index 0000000..7c9ce58 --- /dev/null +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -0,0 +1,276 @@ +// 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 HashAndPathToUriTestElement( + SwarmHash? inputHash, + string? inputPath, + Type? expectedExceptionType, + SwarmHash? expectedHash, + string expectedPath, + bool expectedIsAbsolute, + 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 ExpectedIsAbsolute { get; } = expectedIsAbsolute; + public bool ExpectedIsRooted { get; } = expectedIsRooted; + } + + public class StringToUriTestElement( + string inputString, + bool inputIsAbsolute, + Type? expectedExceptionType, + SwarmHash? expectedHash, + string expectedPath, + bool expectedIsAbsolute, + bool expectedIsRooted) + { + public string InputString { get; } = inputString; + public bool InputIsAbsolute { get; } = inputIsAbsolute; + public Type? ExpectedExceptionType { get; } = expectedExceptionType; + public SwarmHash? ExpectedHash { get; } = expectedHash; + public string ExpectedPath { get; } = expectedPath; + public bool ExpectedIsAbsolute { get; } = expectedIsAbsolute; + public bool ExpectedIsRooted { get; } = expectedIsRooted; + } + + public class UriToStringTestElement( + SwarmUri uri, + string expectedString) + { + public SwarmUri Uri { get; } = uri; + public string ExpectedString { get; } = expectedString; + } + + // Data. + public static IEnumerable HashAndPathToUriTests + { + get + { + var tests = new List + { + // No hash and no path (throws) + new(null, + null, + typeof(ArgumentException), + null, + "", + false, + false), + + // Only hash. + new(SwarmHash.Zero, + null, + null, + SwarmHash.Zero, + "/", + true, + true), + + // No hash and not rooted path. + new(null, + "not/rooted/path", + null, + null, + "not/rooted/path", + false, + false), + + // No hash and rooted path. + new(null, + "/rooted/path", + null, + null, + "/rooted/path", + false, + true), + + // Hash and not rooted path. + new(SwarmHash.Zero, + "not/rooted/path", + null, + SwarmHash.Zero, + "/not/rooted/path", + true, + true), + + // Hash and rooted path. + new(SwarmHash.Zero, + "/rooted/path", + null, + SwarmHash.Zero, + "/rooted/path", + true, + true), + }; + + return tests.Select(t => new object[] { t }); + } + } + + public static IEnumerable StringToUriTests + { + get + { + var tests = new List + { + // Relative not rooted path. + new("relative/not/rooted/path", + false, + null, + null, + "relative/not/rooted/path", + false, + false), + + // Relative rooted path. + new("/relative/rooted/path", + false, + null, + null, + "/relative/rooted/path", + false, + true), + + // Absolute with only hash (without slashes). + new("0000000000000000000000000000000000000000000000000000000000000000", + true, + null, + SwarmHash.Zero, + "/", + true, + true), + + // Absolute with only hash (with slashes). + new("/0000000000000000000000000000000000000000000000000000000000000000/", + true, + null, + SwarmHash.Zero, + "/", + true, + true), + + // Absolute with hash and path. + new("0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", + true, + null, + SwarmHash.Zero, + "/Im/a/path", + true, + true), + + // Absolute with invalid initial hash (throws). + new("not/An/Hash", + true, + typeof(ArgumentException), + null, + "", + false, + false) + }; + + 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(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.ExpectedIsAbsolute, result.IsAbsolute); + Assert.Equal(test.ExpectedIsRooted, result.IsRooted); + } + } + + [Theory, MemberData(nameof(StringToUriTests))] + public void StringToUri(StringToUriTestElement test) + { + if (test.ExpectedExceptionType is not null) + { + Assert.Throws( + test.ExpectedExceptionType, + () => new SwarmUri(test.InputString, test.InputIsAbsolute)); + } + else + { + var result = new SwarmUri(test.InputString, test.InputIsAbsolute); + + Assert.Equal(test.ExpectedHash, result.Hash); + Assert.Equal(test.ExpectedPath, result.Path); + Assert.Equal(test.ExpectedIsAbsolute, result.IsAbsolute); + 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 From de3e623d4cdad5919478fdda2bcd748d8e6009c9 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 7 Jul 2024 08:12:48 +0200 Subject: [PATCH 12/46] add uri kind automatic recognition --- src/BeeNet.Core/Models/SwarmHash.cs | 24 ++++- src/BeeNet.Core/Models/SwarmUri.cs | 16 +-- .../Models/SwarmUriTest.cs | 101 +++++++++++++----- 3 files changed, 103 insertions(+), 38 deletions(-) diff --git a/src/BeeNet.Core/Models/SwarmHash.cs b/src/BeeNet.Core/Models/SwarmHash.cs index d9a0ae8..38392fa 100644 --- a/src/BeeNet.Core/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; @@ -40,7 +40,7 @@ public SwarmHash(byte[] hash) public SwarmHash(string hash) { ArgumentNullException.ThrowIfNull(hash, nameof(hash)); - + try { byteHash = hash.HexToByteArray(); @@ -50,8 +50,8 @@ public SwarmHash(string hash) throw new ArgumentException("Invalid hash", nameof(hash)); } - if (byteHash.Length != HashSize) - throw new ArgumentException("Invalid hash", nameof(hash)); + if (!IsValidHash(byteHash)) + throw new ArgumentOutOfRangeException(nameof(hash)); } // Static properties. @@ -70,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 index 75a4d98..a5ef72f 100644 --- a/src/BeeNet.Core/Models/SwarmUri.cs +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -31,11 +31,15 @@ public SwarmUri(SwarmHash? hash, string? path) Hash = hash; Path = hash != null ? SwarmAddress.NormalizePath(path) : path!; } - public SwarmUri(string uri, bool isAbsolute) + public SwarmUri(string uri, UriKind uriKind) { ArgumentNullException.ThrowIfNull(uri, nameof(uri)); - if (isAbsolute) + // Determine uri kind. + if (uriKind == UriKind.RelativeOrAbsolute) + uriKind = SwarmHash.IsValidHash(uri.Split('/')[0]) ? UriKind.Absolute : UriKind.Relative; + + if (uriKind == UriKind.Absolute) { var address = new SwarmAddress(uri); Hash = address.Hash; @@ -49,9 +53,9 @@ public SwarmUri(string uri, bool isAbsolute) // Properties. public SwarmHash? Hash { get; } - public bool IsAbsolute => Hash.HasValue; - public bool IsRooted => IsAbsolute || System.IO.Path.IsPathRooted(Path); + 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) => @@ -64,7 +68,7 @@ public override int GetHashCode() => Hash.GetHashCode() ^ (Path?.GetHashCode(StringComparison.InvariantCulture) ?? 0); public override string ToString() => - IsAbsolute ? new SwarmAddress(Hash!.Value, Path).ToString() : Path!; + UriKind == UriKind.Absolute ? new SwarmAddress(Hash!.Value, Path).ToString() : Path!; public SwarmAddress ToSwarmAddress(SwarmAddress prefix) { @@ -96,7 +100,7 @@ public static SwarmUri Combine(params SwarmUri[] uris) var combined = uris[0]; foreach (var uri in uris.Skip(1)) { - if (uri.IsAbsolute) + if (uri.UriKind == UriKind.Absolute) combined = uri; else if (uri.IsRooted) combined = new SwarmUri(combined.Hash, uri.Path); diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs index 7c9ce58..baa6faf 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -28,7 +28,7 @@ public class HashAndPathToUriTestElement( Type? expectedExceptionType, SwarmHash? expectedHash, string expectedPath, - bool expectedIsAbsolute, + UriKind expectedUriKind, bool expectedIsRooted) { public SwarmHash? InputHash { get; } = inputHash; @@ -36,26 +36,26 @@ public class HashAndPathToUriTestElement( public Type? ExpectedExceptionType { get; } = expectedExceptionType; public SwarmHash? ExpectedHash { get; } = expectedHash; public string ExpectedPath { get; } = expectedPath; - public bool ExpectedIsAbsolute { get; } = expectedIsAbsolute; public bool ExpectedIsRooted { get; } = expectedIsRooted; + public UriKind ExpectedUriKind { get; } = expectedUriKind; } public class StringToUriTestElement( string inputString, - bool inputIsAbsolute, + UriKind inputUriKind, Type? expectedExceptionType, SwarmHash? expectedHash, string expectedPath, - bool expectedIsAbsolute, + UriKind expectedUriKind, bool expectedIsRooted) { public string InputString { get; } = inputString; - public bool InputIsAbsolute { get; } = inputIsAbsolute; + public UriKind InputUriKind { get; } = inputUriKind; public Type? ExpectedExceptionType { get; } = expectedExceptionType; public SwarmHash? ExpectedHash { get; } = expectedHash; public string ExpectedPath { get; } = expectedPath; - public bool ExpectedIsAbsolute { get; } = expectedIsAbsolute; public bool ExpectedIsRooted { get; } = expectedIsRooted; + public UriKind ExpectedUriKind { get; } = expectedUriKind; } public class UriToStringTestElement( @@ -79,7 +79,7 @@ public static IEnumerable HashAndPathToUriTests typeof(ArgumentException), null, "", - false, + UriKind.RelativeOrAbsolute, false), // Only hash. @@ -88,7 +88,7 @@ public static IEnumerable HashAndPathToUriTests null, SwarmHash.Zero, "/", - true, + UriKind.Absolute, true), // No hash and not rooted path. @@ -97,7 +97,7 @@ public static IEnumerable HashAndPathToUriTests null, null, "not/rooted/path", - false, + UriKind.Relative, false), // No hash and rooted path. @@ -106,7 +106,7 @@ public static IEnumerable HashAndPathToUriTests null, null, "/rooted/path", - false, + UriKind.Relative, true), // Hash and not rooted path. @@ -115,7 +115,7 @@ public static IEnumerable HashAndPathToUriTests null, SwarmHash.Zero, "/not/rooted/path", - true, + UriKind.Absolute, true), // Hash and rooted path. @@ -124,7 +124,7 @@ public static IEnumerable HashAndPathToUriTests null, SwarmHash.Zero, "/rooted/path", - true, + UriKind.Absolute, true), }; @@ -138,58 +138,103 @@ public static IEnumerable StringToUriTests { 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", - false, + UriKind.Relative, null, null, "relative/not/rooted/path", - false, + UriKind.Relative, false), // Relative rooted path. new("/relative/rooted/path", - false, + UriKind.Relative, null, null, "/relative/rooted/path", - false, + UriKind.Relative, true), // Absolute with only hash (without slashes). new("0000000000000000000000000000000000000000000000000000000000000000", - true, + UriKind.Absolute, null, SwarmHash.Zero, "/", - true, + UriKind.Absolute, true), // Absolute with only hash (with slashes). new("/0000000000000000000000000000000000000000000000000000000000000000/", - true, + UriKind.Absolute, null, SwarmHash.Zero, "/", - true, + UriKind.Absolute, true), // Absolute with hash and path. new("0000000000000000000000000000000000000000000000000000000000000000/Im/a/path", - true, + UriKind.Absolute, null, SwarmHash.Zero, "/Im/a/path", - true, + UriKind.Absolute, true), // Absolute with invalid initial hash (throws). new("not/An/Hash", - true, + UriKind.Absolute, typeof(ArgumentException), null, "", - false, + UriKind.RelativeOrAbsolute, false) }; @@ -240,7 +285,7 @@ public void HashAndPathToUri(HashAndPathToUriTestElement test) Assert.Equal(test.ExpectedHash, result.Hash); Assert.Equal(test.ExpectedPath, result.Path); - Assert.Equal(test.ExpectedIsAbsolute, result.IsAbsolute); + Assert.Equal(test.ExpectedUriKind, result.UriKind); Assert.Equal(test.ExpectedIsRooted, result.IsRooted); } } @@ -252,15 +297,15 @@ public void StringToUri(StringToUriTestElement test) { Assert.Throws( test.ExpectedExceptionType, - () => new SwarmUri(test.InputString, test.InputIsAbsolute)); + () => new SwarmUri(test.InputString, test.InputUriKind)); } else { - var result = new SwarmUri(test.InputString, test.InputIsAbsolute); + var result = new SwarmUri(test.InputString, test.InputUriKind); Assert.Equal(test.ExpectedHash, result.Hash); Assert.Equal(test.ExpectedPath, result.Path); - Assert.Equal(test.ExpectedIsAbsolute, result.IsAbsolute); + Assert.Equal(test.ExpectedUriKind, result.UriKind); Assert.Equal(test.ExpectedIsRooted, result.IsRooted); } } From ce5503998437896dbe12f35c2a09e96cd4f8896a Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 8 Jul 2024 18:16:11 +0200 Subject: [PATCH 13/46] new tests and fixes --- src/BeeNet.Core/Models/SwarmAddress.cs | 11 +- src/BeeNet.Core/Models/SwarmUri.cs | 17 +- src/BeeNet.Util/Manifest/MantarayManifest.cs | 2 +- src/BeeNet.Util/Manifest/MantarayNode.cs | 3 +- .../Manifest/ReferencedMantarayNode.cs | 8 +- src/BeeNet.Util/Services/CalculatorService.cs | 2 +- .../Models/SwarmUriTest.cs | 152 ++++++++++++++++++ 7 files changed, 179 insertions(+), 16 deletions(-) diff --git a/src/BeeNet.Core/Models/SwarmAddress.cs b/src/BeeNet.Core/Models/SwarmAddress.cs index 2816525..f1b12aa 100644 --- a/src/BeeNet.Core/Models/SwarmAddress.cs +++ b/src/BeeNet.Core/Models/SwarmAddress.cs @@ -19,6 +19,9 @@ namespace Etherna.BeeNet.Models { public readonly struct SwarmAddress : IEquatable { + // Consts. + public const char Separator = '/'; + // Constructor. public SwarmAddress(SwarmHash hash, string? path = null) { @@ -30,12 +33,12 @@ public SwarmAddress(string address) ArgumentNullException.ThrowIfNull(address, nameof(address)); // Trim initial slash. - address = address.TrimStart('/'); + address = address.TrimStart(Separator); // Extract hash root. - var slashIndex = address.IndexOf('/', StringComparison.InvariantCulture); + var slashIndex = address.IndexOf(Separator, StringComparison.InvariantCulture); var hash = slashIndex > 0 ? address[..slashIndex] : address; - var path = slashIndex > 0 ? address[slashIndex..] : "/"; + var path = slashIndex > 0 ? address[slashIndex..] : Separator.ToString(); // Set hash and path. Hash = new SwarmHash(hash); @@ -72,6 +75,6 @@ public override int GetHashCode() => Hash.GetHashCode() ^ // Helpers. internal static string NormalizePath(string? path) => - '/' + (path ?? "").TrimStart('/'); + Separator + (path ?? "").TrimStart(Separator); } } \ No newline at end of file diff --git a/src/BeeNet.Core/Models/SwarmUri.cs b/src/BeeNet.Core/Models/SwarmUri.cs index a5ef72f..3300f29 100644 --- a/src/BeeNet.Core/Models/SwarmUri.cs +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -37,7 +37,9 @@ public SwarmUri(string uri, UriKind uriKind) // Determine uri kind. if (uriKind == UriKind.RelativeOrAbsolute) - uriKind = SwarmHash.IsValidHash(uri.Split('/')[0]) ? UriKind.Absolute : UriKind.Relative; + uriKind = SwarmHash.IsValidHash(uri.Split(SwarmAddress.Separator)[0]) + ? UriKind.Absolute + : UriKind.Relative; if (uriKind == UriKind.Absolute) { @@ -83,10 +85,13 @@ public bool TryGetRelativeTo(SwarmUri relativeTo, out SwarmUri output) if (relativeTo.Hash != Hash) return false; - if (!Path.StartsWith(relativeTo.Path, StringComparison.InvariantCulture)) + 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, Path[relativeTo.Path.Length..].TrimStart('/')); + output = new SwarmUri(null, string.Join(SwarmAddress.Separator, dirs[relativeToDirs.Length..])); return true; } @@ -105,11 +110,14 @@ public static SwarmUri Combine(params SwarmUri[] uris) else if (uri.IsRooted) combined = new SwarmUri(combined.Hash, uri.Path); else - combined = new SwarmUri(combined.Hash, string.Concat(combined.Path ?? "", uri.Path!)); + 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); @@ -118,6 +126,7 @@ public static SwarmUri Combine(params SwarmUri[] uris) 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); diff --git a/src/BeeNet.Util/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs index 13b8161..2e8f354 100644 --- a/src/BeeNet.Util/Manifest/MantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/MantarayManifest.cs @@ -22,7 +22,7 @@ namespace Etherna.BeeNet.Manifest public class MantarayManifest : IReadOnlyMantarayManifest { // Consts. - public const string RootPath = "/"; + public static readonly string RootPath = SwarmAddress.Separator.ToString(); // Fields. private readonly Func hasherBuilder; diff --git a/src/BeeNet.Util/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs index 3105a8e..9c38083 100644 --- a/src/BeeNet.Util/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -27,7 +27,6 @@ public class MantarayNode : IReadOnlyMantarayNode { // Consts. public const int ForksIndexSize = 32; - public const char PathSeparator = '/'; public static readonly byte[] Version02Hash = new HashProvider().ComputeHash( "mantaray:0.2"u8.ToArray()).Take(VersionHashSize).ToArray(); public const int VersionHashSize = 31; @@ -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.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index 1b76c02..cdcabfa 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -97,8 +97,8 @@ public async Task> GetResourceMetadataAsync( if (path.Length == 0) { //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)) @@ -136,8 +136,8 @@ public async Task ResolveResourceHashAsync(string path) return EntryHash.Value; //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)) diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 7920a9b..023cf1b 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -38,7 +38,7 @@ public async Task EvaluateDirectoryUploadAsync( 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)); diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs index baa6faf..9ce5afa 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -22,6 +22,14 @@ 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, @@ -40,6 +48,16 @@ public class HashAndPathToUriTestElement( public UriKind ExpectedUriKind { get; } = expectedUriKind; } + 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, @@ -67,6 +85,38 @@ public class UriToStringTestElement( } // 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 @@ -241,6 +291,82 @@ public static IEnumerable StringToUriTests 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 { @@ -270,6 +396,15 @@ public static IEnumerable UriToStringTests } // 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) { @@ -289,6 +424,23 @@ public void HashAndPathToUri(HashAndPathToUriTestElement test) Assert.Equal(test.ExpectedIsRooted, result.IsRooted); } } + + [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) From ce89e9dbe607c55cc81b779bea7a7c9609ed902c Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 8 Jul 2024 18:25:02 +0200 Subject: [PATCH 14/46] add SwarmUri test --- test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs index 9ce5afa..a4f681f 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -425,6 +425,18 @@ public void HashAndPathToUri(HashAndPathToUriTestElement test) } } + [Fact] + public void ToSwarmAddressConversion() + { + var originalUri = new SwarmUri(null, "Im/path"); + var prefixAddress = new SwarmAddress(SwarmHash.Zero, "Im/prefix"); + + var result = originalUri.ToSwarmAddress(prefixAddress); + + Assert.Equal(SwarmHash.Zero, result.Hash); + Assert.Equal("/Im/prefix/Im/path", result.Path); + } + [Theory, MemberData(nameof(TryGetRelativeToUriTests))] public void TryGetRelativeToUri(TryGetRelativeToUriTestElement test) { From 29644ca48e8e51ba489ff5e8a997f655320c684c Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 9 Jul 2024 00:05:34 +0200 Subject: [PATCH 15/46] implement online SwarmAddress resolution --- BeeNet.sln.DotSettings | 3 +- src/BeeNet.Client/BeeClient.cs | 13 +++++++ .../Hasher/Store/BeeClientChunkStore.cs | 39 +++++++++++++++++++ src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs | 12 +----- .../Hasher/Store/FakeChunkStore.cs | 4 -- src/BeeNet.Util/Hasher/Store/IChunkStore.cs | 9 +---- .../Hasher/Store/IReadOnlyChunkStore.cs | 26 +++++++++++++ src/BeeNet.Util/IBeeClient.cs | 2 + .../Manifest/ReferencedMantarayManifest.cs | 2 +- .../Manifest/ReferencedMantarayNode.cs | 4 +- 10 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.cs create mode 100644 src/BeeNet.Util/Hasher/Store/IReadOnlyChunkStore.cs diff --git a/BeeNet.sln.DotSettings b/BeeNet.sln.DotSettings index 9bd7b53..1e2d99e 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/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs index 8c75050..c70ef82 100644 --- a/src/BeeNet.Client/BeeClient.cs +++ b/src/BeeNet.Client/BeeClient.cs @@ -14,6 +14,8 @@ using Etherna.BeeNet.Clients; using Etherna.BeeNet.Exceptions; +using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -877,6 +879,17 @@ public async Task RefreshAuthAsync( }, cancellationToken).ConfigureAwait(false)).Key; + public async Task ResolveResourceHashAsync(SwarmAddress address) + { + var chunkStore = new BeeClientChunkStore(this); + + var rootManifest = new ReferencedMantarayManifest( + chunkStore, + address.Hash); + + return await rootManifest.ResolveResourceHashAsync(address).ConfigureAwait(false); + } + public Task ReuploadContentAsync( SwarmHash hash, CancellationToken cancellationToken = default) => diff --git a/src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.cs b/src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.cs new file mode 100644 index 0000000..f777687 --- /dev/null +++ b/src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.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 Etherna.BeeNet.Exceptions; +using Etherna.BeeNet.Models; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Hasher.Store +{ + public class BeeClientChunkStore(IBeeClient beeClient) + : IReadOnlyChunkStore + { + public Task GetAsync(SwarmHash hash) => + beeClient.GetChunkAsync(hash); + + public async Task TryGetAsync(SwarmHash hash) + { + try + { + return await beeClient.GetChunkAsync(hash).ConfigureAwait(false); + } + catch (BeeNetApiException) + { + return null; + } + } + } +} \ No newline at end of file diff --git a/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs index 0100925..108d42b 100644 --- a/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs +++ b/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs @@ -18,17 +18,9 @@ namespace Etherna.BeeNet.Hasher.Store { - public class ChunkJoiner + public class ChunkJoiner( + IReadOnlyChunkStore chunkStore) { - // Fields. - private readonly IChunkStore chunkStore; - - // Constructor. - public ChunkJoiner(IChunkStore chunkStore) - { - this.chunkStore = chunkStore; - } - // Methods. public async Task> GetJoinedChunkDataAsync(SwarmHash hash) { diff --git a/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs b/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs index c2fc532..3f7de2a 100644 --- a/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs +++ b/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs @@ -13,7 +13,6 @@ // If not, see . using Etherna.BeeNet.Models; -using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -21,9 +20,6 @@ namespace Etherna.BeeNet.Hasher.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.Util/Hasher/Store/IChunkStore.cs b/src/BeeNet.Util/Hasher/Store/IChunkStore.cs index b071c4f..cc2fb2c 100644 --- a/src/BeeNet.Util/Hasher/Store/IChunkStore.cs +++ b/src/BeeNet.Util/Hasher/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 { - 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/Hasher/Store/IReadOnlyChunkStore.cs b/src/BeeNet.Util/Hasher/Store/IReadOnlyChunkStore.cs new file mode 100644 index 0000000..54182eb --- /dev/null +++ b/src/BeeNet.Util/Hasher/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.Hasher.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.Util/IBeeClient.cs b/src/BeeNet.Util/IBeeClient.cs index d0c922e..d98d5ee 100644 --- a/src/BeeNet.Util/IBeeClient.cs +++ b/src/BeeNet.Util/IBeeClient.cs @@ -536,6 +536,8 @@ Task RefreshAuthAsync( int expiry, CancellationToken cancellationToken = default); + Task ResolveResourceHashAsync(SwarmAddress address); + /// Reupload a root hash to the network /// Root hash of content (can be of any type: collection, file, chunk) /// Ok diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs index 51c48f8..caf6e92 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs @@ -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); diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index cdcabfa..6613e6b 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -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) From 14ae282222e81fb52c839c62effb0f82af4f58a6 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 9 Jul 2024 14:18:21 +0200 Subject: [PATCH 16/46] Allow to convert absolute SwarmUri to SwarmAddress without prefix --- src/BeeNet.Core/Models/SwarmUri.cs | 17 ++++- .../Models/SwarmUriTest.cs | 71 ++++++++++++++++--- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/BeeNet.Core/Models/SwarmUri.cs b/src/BeeNet.Core/Models/SwarmUri.cs index 3300f29..3cf5379 100644 --- a/src/BeeNet.Core/Models/SwarmUri.cs +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -72,9 +72,22 @@ public override int GetHashCode() => Hash.GetHashCode() ^ public override string ToString() => UriKind == UriKind.Absolute ? new SwarmAddress(Hash!.Value, Path).ToString() : Path!; - public SwarmAddress ToSwarmAddress(SwarmAddress prefix) + /// + /// 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) { - var combined = Combine(prefix, this); + 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); } diff --git a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs index a4f681f..7416ece 100644 --- a/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs +++ b/test/BeeNet.Core.UnitTest/Models/SwarmUriTest.cs @@ -48,6 +48,18 @@ public class HashAndPathToUriTestElement( 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, @@ -292,6 +304,41 @@ public static IEnumerable StringToUriTests } } + 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 @@ -425,16 +472,22 @@ public void HashAndPathToUri(HashAndPathToUriTestElement test) } } - [Fact] - public void ToSwarmAddressConversion() + [Theory, MemberData(nameof(ToSwarmAddressConversionTests))] + public void ToSwarmAddressConversion(ToSwarmAddressConversionTestElement test) { - var originalUri = new SwarmUri(null, "Im/path"); - var prefixAddress = new SwarmAddress(SwarmHash.Zero, "Im/prefix"); - - var result = originalUri.ToSwarmAddress(prefixAddress); - - Assert.Equal(SwarmHash.Zero, result.Hash); - Assert.Equal("/Im/prefix/Im/path", result.Path); + 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))] From 1729172405b0b452cc5d285997ee32c816c25300 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 9 Jul 2024 15:58:21 +0200 Subject: [PATCH 17/46] minor updates --- src/BeeNet.Client/BeeClient.cs | 2 +- src/BeeNet.Core/Models/SwarmUri.cs | 2 +- src/BeeNet.Util/IBeeClient.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs index c70ef82..c20b933 100644 --- a/src/BeeNet.Client/BeeClient.cs +++ b/src/BeeNet.Client/BeeClient.cs @@ -879,7 +879,7 @@ public async Task RefreshAuthAsync( }, cancellationToken).ConfigureAwait(false)).Key; - public async Task ResolveResourceHashAsync(SwarmAddress address) + public async Task ResolveSwarmAddressToHashAsync(SwarmAddress address) { var chunkStore = new BeeClientChunkStore(this); diff --git a/src/BeeNet.Core/Models/SwarmUri.cs b/src/BeeNet.Core/Models/SwarmUri.cs index 3cf5379..da256a2 100644 --- a/src/BeeNet.Core/Models/SwarmUri.cs +++ b/src/BeeNet.Core/Models/SwarmUri.cs @@ -77,7 +77,7 @@ public override string ToString() => /// /// Optional prefix address /// The absolute URI as an Address - public SwarmAddress ToSwarmAddress(SwarmAddress? prefix) + public SwarmAddress ToSwarmAddress(SwarmAddress? prefix = null) { if (prefix is null) { diff --git a/src/BeeNet.Util/IBeeClient.cs b/src/BeeNet.Util/IBeeClient.cs index d98d5ee..a6be056 100644 --- a/src/BeeNet.Util/IBeeClient.cs +++ b/src/BeeNet.Util/IBeeClient.cs @@ -536,7 +536,7 @@ Task RefreshAuthAsync( int expiry, CancellationToken cancellationToken = default); - Task ResolveResourceHashAsync(SwarmAddress address); + Task ResolveSwarmAddressToHashAsync(SwarmAddress address); /// Reupload a root hash to the network /// Root hash of content (can be of any type: collection, file, chunk) From 8ab6b965394e52d1f6aa989ecfb0065e00c00e7b Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Thu, 11 Jul 2024 15:27:36 +0200 Subject: [PATCH 18/46] Add IHasher interface and refactor hasher naming --- src/BeeNet.Client/BeeClient.cs | 2 +- .../{Hasher => Hashing}/Bmt/SwarmChunkBmt.cs | 14 +++--- .../Bmt/SwarmChunkBmtHasher.cs | 8 +-- .../HashProvider.cs => Hashing/Hasher.cs} | 11 ++-- src/BeeNet.Util/Hashing/IHasher.cs | 25 ++++++++++ .../Pipeline/ChunkAggregatorPipelineStage.cs | 4 +- .../Pipeline/ChunkBmtPipelineStage.cs | 4 +- .../Pipeline/ChunkFeederPipelineStage.cs | 2 +- .../Pipeline/ChunkStoreWriterPipelineStage.cs | 28 +++-------- .../Pipeline/HasherPipelineBuilder.cs | 6 +-- .../Pipeline/HasherPipelineFeedArgs.cs | 2 +- .../Pipeline/IHasherPipeline.cs | 2 +- .../Pipeline/IHasherPipelineStage.cs | 2 +- .../Postage/FakePostageStampIssuer.cs | 2 +- .../Postage/FakePostageStamper.cs | 6 +-- .../Postage/IPostageStampIssuer.cs | 2 +- .../Postage/IPostageStamper.cs | 6 +-- .../Postage/PostageStampIssuer.cs | 2 +- .../Postage/PostageStamper.cs | 6 +-- .../{Hasher => Hashing}/Signer/FakeSigner.cs | 2 +- .../{Hasher => Hashing}/Signer/ISigner.cs | 2 +- .../Store/BeeClientChunkStore.cs | 2 +- .../{Hasher => Hashing}/Store/ChunkJoiner.cs | 2 +- .../Store/FakeChunkStore.cs | 2 +- .../{Hasher => Hashing}/Store/IChunkStore.cs | 2 +- .../Store/IReadOnlyChunkStore.cs | 2 +- .../{Hasher => Hashing}/Store/IStampStore.cs | 2 +- .../Store/LocalDirectoryChunkStore.cs | 2 +- .../Store/MemoryStampStore.cs | 2 +- .../Store/StampStoreItem.cs | 2 +- src/BeeNet.Util/Manifest/MantarayManifest.cs | 2 +- src/BeeNet.Util/Manifest/MantarayNode.cs | 6 +-- .../Manifest/ReferencedMantarayManifest.cs | 2 +- .../Manifest/ReferencedMantarayNode.cs | 2 +- src/BeeNet.Util/Services/CalculatorService.cs | 8 +-- src/BeeNet.Util/Services/FeedService.cs | 6 +-- .../Services/ICalculatorService.cs | 4 +- .../Services/UploadEvaluationResult.cs | 2 +- .../Services/FeedServiceTest.cs | 50 +++++++++---------- 39 files changed, 126 insertions(+), 112 deletions(-) rename src/BeeNet.Util/{Hasher => Hashing}/Bmt/SwarmChunkBmt.cs (89%) rename src/BeeNet.Util/{Hasher => Hashing}/Bmt/SwarmChunkBmtHasher.cs (89%) rename src/BeeNet.Util/{Hasher/HashProvider.cs => Hashing/Hasher.cs} (85%) create mode 100644 src/BeeNet.Util/Hashing/IHasher.cs rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/ChunkAggregatorPipelineStage.cs (98%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/ChunkBmtPipelineStage.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/ChunkFeederPipelineStage.cs (99%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/ChunkStoreWriterPipelineStage.cs (70%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/HasherPipelineBuilder.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/HasherPipelineFeedArgs.cs (98%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/IHasherPipeline.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Pipeline/IHasherPipelineStage.cs (95%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/FakePostageStampIssuer.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/FakePostageStamper.cs (91%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/IPostageStampIssuer.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/IPostageStamper.cs (89%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/PostageStampIssuer.cs (98%) rename src/BeeNet.Util/{Hasher => Hashing}/Postage/PostageStamper.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Signer/FakeSigner.cs (95%) rename src/BeeNet.Util/{Hasher => Hashing}/Signer/ISigner.cs (95%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/BeeClientChunkStore.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/ChunkJoiner.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/FakeChunkStore.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/IChunkStore.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/IReadOnlyChunkStore.cs (96%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/IStampStore.cs (95%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/LocalDirectoryChunkStore.cs (99%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/MemoryStampStore.cs (97%) rename src/BeeNet.Util/{Hasher => Hashing}/Store/StampStoreItem.cs (97%) diff --git a/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs index c20b933..cf6c2a3 100644 --- a/src/BeeNet.Client/BeeClient.cs +++ b/src/BeeNet.Client/BeeClient.cs @@ -14,7 +14,7 @@ using Etherna.BeeNet.Clients; using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Hasher.Store; +using Etherna.BeeNet.Hashing.Store; using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; diff --git a/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmt.cs b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs similarity index 89% rename from src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmt.cs rename to src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs index ed1e2df..7a2f5a7 100644 --- a/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmt.cs +++ b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmt.cs @@ -16,13 +16,16 @@ 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 sealed class SwarmChunkBmt : MerkleTree + internal sealed class SwarmChunkBmt(IHasher hasher) + : MerkleTree( + hasher, + byteArrayConvertor, + PairingConcatType.Normal) { // Classes. private sealed class ChunkBmtByteArrayConvertor : IByteArrayConvertor @@ -53,11 +56,6 @@ public byte[] ConvertToByteArray(byte[] data) // Static fields. private static readonly ChunkBmtByteArrayConvertor byteArrayConvertor = new(); - // Constructor. - public SwarmChunkBmt(IHashProvider hashProvider) - : base(hashProvider, byteArrayConvertor, PairingConcatType.Normal) - { } - // Protected override methods. protected override MerkleTreeNode CreateMerkleTreeNode(byte[] item) => new(byteArrayConvertor.ConvertToByteArray(item)); diff --git a/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs similarity index 89% rename from src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmtHasher.cs rename to src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs index 2025c17..2e472a9 100644 --- a/src/BeeNet.Util/Hasher/Bmt/SwarmChunkBmtHasher.cs +++ b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs @@ -17,7 +17,7 @@ using System.Collections.Generic; using System.Linq; -namespace Etherna.BeeNet.Hasher.Bmt +namespace Etherna.BeeNet.Hashing.Bmt { internal static class SwarmChunkBmtHasher { @@ -39,12 +39,12 @@ public static SwarmHash Hash(byte[] span, byte[] data) } // Build the merkle tree. - var hashProvider = new HashProvider(); - var bmt = new SwarmChunkBmt(hashProvider); + var hasher = new Hasher(); + var bmt = new SwarmChunkBmt(hasher); bmt.BuildTree(segments); var result = bmt.Root.Hash; - return hashProvider.ComputeHash(span.Concat(result).ToArray()); + return hasher.ComputeHash(span.Concat(result).ToArray()); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hasher/HashProvider.cs b/src/BeeNet.Util/Hashing/Hasher.cs similarity index 85% rename from src/BeeNet.Util/Hasher/HashProvider.cs rename to src/BeeNet.Util/Hashing/Hasher.cs index 39b3f72..c3ce9a6 100644 --- a/src/BeeNet.Util/Hasher/HashProvider.cs +++ b/src/BeeNet.Util/Hashing/Hasher.cs @@ -13,13 +13,16 @@ // If not, see . using Etherna.BeeNet.Models; -using Nethereum.Util.HashProviders; using Org.BouncyCastle.Crypto.Digests; using System; +using System.Text; -namespace Etherna.BeeNet.Hasher +namespace Etherna.BeeNet.Hashing { - public class HashProvider : IHashProvider + /// + /// Hasher service, not thread safe + /// + public class Hasher : IHasher { // Fields. private readonly KeccakDigest hasher = new(256); @@ -39,6 +42,6 @@ public byte[] ComputeHash(byte[] data) } public byte[] ComputeHash(string data) => - ComputeHash(System.Text.Encoding.UTF8.GetBytes(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 0000000..02be670 --- /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.Util/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs similarity index 98% rename from src/BeeNet.Util/Hasher/Pipeline/ChunkAggregatorPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs index 8449b82..4d66004 100644 --- a/src/BeeNet.Util/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,7 +20,7 @@ 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); diff --git a/src/BeeNet.Util/Hasher/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Pipeline/ChunkBmtPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 7d81bb9..43b15b3 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.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.Bmt; +using Etherna.BeeNet.Hashing.Bmt; using Etherna.BeeNet.Models; using System; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { /// /// Calculate hash of each chunk diff --git a/src/BeeNet.Util/Hasher/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs similarity index 99% rename from src/BeeNet.Util/Hasher/Pipeline/ChunkFeederPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index 12ddf91..0b24cf6 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -19,7 +19,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { /// /// Produce chunked data with span prefix for successive stages. diff --git a/src/BeeNet.Util/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs similarity index 70% rename from src/BeeNet.Util/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs index 1286da8..e1a5d98 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/ChunkStoreWriterPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs @@ -12,32 +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.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() { diff --git a/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Pipeline/HasherPipelineBuilder.cs rename to src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 6d060ac..f0f0516 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.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.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 diff --git a/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs similarity index 98% rename from src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs rename to src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs index 79d76d2..89ce779 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { internal sealed class HasherPipelineFeedArgs { diff --git a/src/BeeNet.Util/Hasher/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Pipeline/IHasherPipeline.cs rename to src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs index 1da6b28..67b4869 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/IHasherPipeline.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs @@ -17,7 +17,7 @@ using System.IO; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { public interface IHasherPipeline : IDisposable { diff --git a/src/BeeNet.Util/Hasher/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs similarity index 95% rename from src/BeeNet.Util/Hasher/Pipeline/IHasherPipelineStage.cs rename to src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index b03b11c..728b539 100644 --- a/src/BeeNet.Util/Hasher/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs @@ -16,7 +16,7 @@ using System; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Pipeline +namespace Etherna.BeeNet.Hashing.Pipeline { internal interface IHasherPipelineStage : IDisposable { diff --git a/src/BeeNet.Util/Hasher/Postage/FakePostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Postage/FakePostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs index bae9ab9..a1b77ff 100644 --- a/src/BeeNet.Util/Hasher/Postage/FakePostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public class FakePostageStampIssuer : IPostageStampIssuer { diff --git a/src/BeeNet.Util/Hasher/Postage/FakePostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/FakePostageStamper.cs similarity index 91% rename from src/BeeNet.Util/Hasher/Postage/FakePostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/FakePostageStamper.cs index 8529c55..bab0d28 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Postage/IPostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Postage/IPostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs index 4753f62..6432b18 100644 --- a/src/BeeNet.Util/Hasher/Postage/IPostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { public interface IPostageStampIssuer { diff --git a/src/BeeNet.Util/Hasher/Postage/IPostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs similarity index 89% rename from src/BeeNet.Util/Hasher/Postage/IPostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs index 6f2fd45..044899d 100644 --- a/src/BeeNet.Util/Hasher/Postage/IPostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.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 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 { diff --git a/src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs similarity index 98% rename from src/BeeNet.Util/Hasher/Postage/PostageStampIssuer.cs rename to src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs index 4e1e738..ac8bc8a 100644 --- a/src/BeeNet.Util/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 { diff --git a/src/BeeNet.Util/Hasher/Postage/PostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/PostageStamper.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Postage/PostageStamper.cs rename to src/BeeNet.Util/Hashing/Postage/PostageStamper.cs index a6740e9..ec87797 100644 --- a/src/BeeNet.Util/Hasher/Postage/PostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/PostageStamper.cs @@ -13,13 +13,13 @@ // If not, see . 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; -namespace Etherna.BeeNet.Hasher.Postage +namespace Etherna.BeeNet.Hashing.Postage { internal sealed class PostageStamper( ISigner signer, diff --git a/src/BeeNet.Util/Hasher/Signer/FakeSigner.cs b/src/BeeNet.Util/Hashing/Signer/FakeSigner.cs similarity index 95% rename from src/BeeNet.Util/Hasher/Signer/FakeSigner.cs rename to src/BeeNet.Util/Hashing/Signer/FakeSigner.cs index cc6f474..d143a21 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Signer/ISigner.cs b/src/BeeNet.Util/Hashing/Signer/ISigner.cs similarity index 95% rename from src/BeeNet.Util/Hasher/Signer/ISigner.cs rename to src/BeeNet.Util/Hashing/Signer/ISigner.cs index 4c75c28..6c5672b 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Store/BeeClientChunkStore.cs b/src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs index f777687..4b882d4 100644 --- a/src/BeeNet.Util/Hasher/Store/BeeClientChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/BeeClientChunkStore.cs @@ -16,7 +16,7 @@ using Etherna.BeeNet.Models; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class BeeClientChunkStore(IBeeClient beeClient) : IReadOnlyChunkStore diff --git a/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs rename to src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs index 108d42b..01e1232 100644 --- a/src/BeeNet.Util/Hasher/Store/ChunkJoiner.cs +++ b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs @@ -16,7 +16,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class ChunkJoiner( IReadOnlyChunkStore chunkStore) diff --git a/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs b/src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs index 3f7de2a..5ee901c 100644 --- a/src/BeeNet.Util/Hasher/Store/FakeChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/FakeChunkStore.cs @@ -16,7 +16,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public class FakeChunkStore : IChunkStore { diff --git a/src/BeeNet.Util/Hasher/Store/IChunkStore.cs b/src/BeeNet.Util/Hashing/Store/IChunkStore.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Store/IChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/IChunkStore.cs index cc2fb2c..05262da 100644 --- a/src/BeeNet.Util/Hasher/Store/IChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/IChunkStore.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public interface IChunkStore : IReadOnlyChunkStore { diff --git a/src/BeeNet.Util/Hasher/Store/IReadOnlyChunkStore.cs b/src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs similarity index 96% rename from src/BeeNet.Util/Hasher/Store/IReadOnlyChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs index 54182eb..30761fa 100644 --- a/src/BeeNet.Util/Hasher/Store/IReadOnlyChunkStore.cs +++ b/src/BeeNet.Util/Hashing/Store/IReadOnlyChunkStore.cs @@ -15,7 +15,7 @@ using Etherna.BeeNet.Models; using System.Threading.Tasks; -namespace Etherna.BeeNet.Hasher.Store +namespace Etherna.BeeNet.Hashing.Store { public interface IReadOnlyChunkStore { diff --git a/src/BeeNet.Util/Hasher/Store/IStampStore.cs b/src/BeeNet.Util/Hashing/Store/IStampStore.cs similarity index 95% rename from src/BeeNet.Util/Hasher/Store/IStampStore.cs rename to src/BeeNet.Util/Hashing/Store/IStampStore.cs index 14d2ca3..2a515b2 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Store/LocalDirectoryChunkStore.cs b/src/BeeNet.Util/Hashing/Store/LocalDirectoryChunkStore.cs similarity index 99% rename from src/BeeNet.Util/Hasher/Store/LocalDirectoryChunkStore.cs rename to src/BeeNet.Util/Hashing/Store/LocalDirectoryChunkStore.cs index 3b70957..5a1b0cf 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Store/MemoryStampStore.cs b/src/BeeNet.Util/Hashing/Store/MemoryStampStore.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Store/MemoryStampStore.cs rename to src/BeeNet.Util/Hashing/Store/MemoryStampStore.cs index d7fda20..e560fc6 100644 --- a/src/BeeNet.Util/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.Util/Hasher/Store/StampStoreItem.cs b/src/BeeNet.Util/Hashing/Store/StampStoreItem.cs similarity index 97% rename from src/BeeNet.Util/Hasher/Store/StampStoreItem.cs rename to src/BeeNet.Util/Hashing/Store/StampStoreItem.cs index 782a885..e864e6e 100644 --- a/src/BeeNet.Util/Hasher/Store/StampStoreItem.cs +++ b/src/BeeNet.Util/Hashing/Store/StampStoreItem.cs @@ -15,7 +15,7 @@ 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.Util/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs index 2e8f354..a03ce96 100644 --- a/src/BeeNet.Util/Manifest/MantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/MantarayManifest.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.Pipeline; +using Etherna.BeeNet.Hashing.Pipeline; using Etherna.BeeNet.Models; using System; using System.Threading.Tasks; diff --git a/src/BeeNet.Util/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs index 9c38083..346926e 100644 --- a/src/BeeNet.Util/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -13,8 +13,8 @@ // If not, see . using Etherna.BeeNet.Extensions; -using Etherna.BeeNet.Hasher; -using Etherna.BeeNet.Hasher.Pipeline; +using Etherna.BeeNet.Hashing; +using Etherna.BeeNet.Hashing.Pipeline; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -27,7 +27,7 @@ public class MantarayNode : IReadOnlyMantarayNode { // Consts. public const int ForksIndexSize = 32; - public static readonly byte[] Version02Hash = new HashProvider().ComputeHash( + public static readonly byte[] Version02Hash = new Hasher().ComputeHash( "mantaray:0.2"u8.ToArray()).Take(VersionHashSize).ToArray(); public const int VersionHashSize = 31; diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs index caf6e92..b7b5fb8 100644 --- a/src/BeeNet.Util/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; diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index 6613e6b..f9185dc 100644 --- a/src/BeeNet.Util/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; diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 023cf1b..5fbd5bc 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -12,10 +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.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; diff --git a/src/BeeNet.Util/Services/FeedService.cs b/src/BeeNet.Util/Services/FeedService.cs index 43db131..7b365f1 100644 --- a/src/BeeNet.Util/Services/FeedService.cs +++ b/src/BeeNet.Util/Services/FeedService.cs @@ -13,7 +13,7 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Hasher; +using Etherna.BeeNet.Hashing; using Etherna.BeeNet.Models; using Etherna.BeeNet.Models.Feeds; using Nethereum.Hex.HexConvertors.Extensions; @@ -58,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, new HashProvider()); + var chunkHash = SwarmFeedChunk.BuildHash(account, topic, nextEpochIndex, new Hasher()); return new SwarmFeedChunk(nextEpochIndex, chunkPayload, chunkHash); } @@ -153,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, new HashProvider()), index); + TryGetFeedChunkAsync(SwarmFeedChunk.BuildHash(account, topic, index, new Hasher()), index); public async Task TryGetFeedChunkAsync(SwarmHash hash, FeedIndexBase index) { diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index 5f17d73..d2613c7 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.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.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; diff --git a/src/BeeNet.Util/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs index 89cb0d5..e61dd9b 100644 --- a/src/BeeNet.Util/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; diff --git a/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs b/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs index 51d4542..2a8dad4 100644 --- a/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs +++ b/test/BeeNet.Util.UnitTest/Services/FeedServiceTest.cs @@ -13,7 +13,7 @@ // If not, see . using Etherna.BeeNet.Exceptions; -using Etherna.BeeNet.Hasher; +using Etherna.BeeNet.Hashing; using Etherna.BeeNet.Models; using Etherna.BeeNet.Models.Feeds; using Moq; @@ -129,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, new HashProvider()); + 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); @@ -147,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, new HashProvider()); + 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, new HashProvider()); + 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); @@ -172,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, new HashProvider()); + 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, new HashProvider()); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new Hasher()); var leftChildEpochIndex = new EpochFeedIndex(4, 0); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new HashProvider()); + 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); @@ -205,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, new HashProvider()); + 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, new HashProvider()); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); tests.Add(new FindLastEpochChunkBeforeDateTestElement( ChunkAccount, @@ -230,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, new HashProvider()); + 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, new HashProvider()); + var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new Hasher()); var leftChildEpochIndex = new EpochFeedIndex(4, 1); - var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new HashProvider()); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); tests.Add(new FindLastEpochChunkBeforeDateTestElement( ChunkAccount, @@ -260,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, new HashProvider()); + 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, new HashProvider()); + var leftChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftChildEpochIndex, new Hasher()); var rightChildEpochIndex = new EpochFeedIndex(6, 1); - var rightChildChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, rightChildEpochIndex, new HashProvider()); + 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); @@ -346,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, new HashProvider()); + var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); tests.Add(new TryFindStartingEpochChunkOnlineTestElement( ChunkAccount, @@ -377,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, new HashProvider()); + var reference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); tests.Add(new TryFindStartingEpochChunkOnlineTestElement( ChunkAccount, @@ -393,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, new HashProvider()); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var leftEpochIndex = startingEpochIndex.Left; - var leftChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, leftEpochIndex, new HashProvider()); + 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 @@ -419,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, new HashProvider()); + 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, new HashProvider()); + 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 @@ -447,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, new HashProvider()); + var startingChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, startingEpochIndex, new Hasher()); var parentEpochIndex = startingEpochIndex.Parent; - var parentChunkReference = SwarmFeedChunk.BuildHash(ChunkAccount, ChunkTopic, parentEpochIndex, new HashProvider()); + 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 @@ -473,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, new HashProvider()); + 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, new HashProvider()); + 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 From 01faeb6ec74a1eecb61b232ae4c2dbb3c17058fb Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 14 Jul 2024 16:51:08 +0200 Subject: [PATCH 19/46] add simple data chunk production --- src/BeeNet.Util/Services/CalculatorService.cs | 41 ++++++++++++++++++- .../Services/ICalculatorService.cs | 36 +++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 5fbd5bc..fd799e9 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -200,7 +200,7 @@ public async Task EvaluateFileUploadAsync( postageStampIssuer); } - public async Task> GetResourceMetadataFromChunksAsync( + public async Task> GetFileMetadataFromChunksAsync( string chunkStoreDirectory, SwarmAddress address) { @@ -213,7 +213,7 @@ public async Task> GetResourceMetadataFromCh return await rootManifest.GetResourceMetadataAsync(address).ConfigureAwait(false); } - public async Task GetResourceStreamFromChunksAsync( + public async Task GetFileStreamFromChunksAsync( string chunkStoreDirectory, SwarmAddress address) { @@ -233,5 +233,42 @@ public async Task GetResourceStreamFromChunksAsync( return memoryStream; } + + public Task WriteDataChunksAsync( + byte[] data, + string outputDirectory, + bool createDirectory = true, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None) + { + using var stream = new MemoryStream(data); + return WriteDataChunksAsync( + stream, + outputDirectory, + createDirectory, + encrypt, + redundancyLevel); + } + + public async Task WriteDataChunksAsync( + Stream stream, + string outputDirectory, + bool createDirectory = true, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None) + { + var chunkStore = new LocalDirectoryChunkStore(outputDirectory, createDirectory); + + // Create chunks and get file hash. + using var fileHasherPipeline = HasherPipelineBuilder.BuildNewHasherPipeline( + chunkStore, + new FakePostageStamper(), + redundancyLevel, + encrypt); + var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); + + // Return file hash. + return fileHash; + } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index d2613c7..e83595e 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -89,7 +89,7 @@ Task EvaluateFileUploadAsync( /// The chunk directory /// Resource address /// Resource metadata - Task> GetResourceMetadataFromChunksAsync( + Task> GetFileMetadataFromChunksAsync( string chunkStoreDirectory, SwarmAddress address); @@ -99,8 +99,40 @@ Task> GetResourceMetadataFromChunksAsync( /// The chunk directory /// Resource address /// Resource stream - Task GetResourceStreamFromChunksAsync( + Task GetFileStreamFromChunksAsync( string chunkStoreDirectory, SwarmAddress address); + + /// + /// 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 + /// True to encrypt + /// Choose the redundancy level + /// The data root hash + Task WriteDataChunksAsync( + byte[] data, + string outputDirectory, + bool createDirectory = true, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None); + + /// + /// 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 + /// True to encrypt + /// Choose the redundancy level + /// The data root hash + Task WriteDataChunksAsync( + Stream stream, + string outputDirectory, + bool createDirectory = true, + bool encrypt = false, + RedundancyLevel redundancyLevel = RedundancyLevel.None); } } \ No newline at end of file From 975d486486c751d9802b0e810b2d6452a331bebb Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 14 Jul 2024 17:12:28 +0200 Subject: [PATCH 20/46] Make hashing and postage classes public --- src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs | 2 +- src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs | 4 +++- src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs | 2 +- src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index f0f0516..2b4b3d9 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -21,7 +21,7 @@ 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( diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs index 89ce779..e323a5a 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs @@ -17,7 +17,7 @@ namespace Etherna.BeeNet.Hashing.Pipeline { - internal sealed class HasherPipelineFeedArgs + public sealed class HasherPipelineFeedArgs { // Fields. private readonly byte[] _data; @@ -29,6 +29,8 @@ public HasherPipelineFeedArgs( byte[]? span = null, long numberId = 0) { + ArgumentNullException.ThrowIfNull(data, nameof(data)); + if (span is not null) { if (data.Length < SwarmChunk.SpanSize) diff --git a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index 728b539..ea578c8 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs @@ -18,7 +18,7 @@ namespace Etherna.BeeNet.Hashing.Pipeline { - internal interface IHasherPipelineStage : IDisposable + public interface IHasherPipelineStage : IDisposable { Task FeedAsync(HasherPipelineFeedArgs args); Task SumAsync(); diff --git a/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs index 044899d..dffdc20 100644 --- a/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs +++ b/src/BeeNet.Util/Hashing/Postage/IPostageStamper.cs @@ -18,7 +18,7 @@ namespace Etherna.BeeNet.Hashing.Postage { - internal interface IPostageStamper + public interface IPostageStamper { // Properties. ISigner Signer { get; } From 07bc415890bf1a6b1ef7c75224c0efb889ccaa43 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 16 Jul 2024 15:16:33 +0200 Subject: [PATCH 21/46] initial compaction implementation --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 67 +++++++++++++++++-- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 22 ++++-- src/BeeNet.Util/Manifest/ManifestEntry.cs | 2 + src/BeeNet.Util/Services/CalculatorService.cs | 22 ++++-- .../Services/ICalculatorService.cs | 10 +++ 5 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 43b15b3..7820996 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -13,6 +13,7 @@ // If not, see . using Etherna.BeeNet.Hashing.Bmt; +using Etherna.BeeNet.Hashing.Postage; using Etherna.BeeNet.Models; using System; using System.Threading.Tasks; @@ -22,10 +23,33 @@ namespace Etherna.BeeNet.Hashing.Pipeline /// /// Calculate hash of each chunk /// - internal sealed class ChunkBmtPipelineStage( - IHasherPipelineStage nextStage) - : IHasherPipelineStage + internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage { + // Consts. + public const int MinCompactionLevel = 0; + + // Fields. + private readonly int compactionLevel; + private readonly IHasherPipelineStage nextStage; + private readonly IPostageStampIssuer stampIssuer; + + // Constructor. + public ChunkBmtPipelineStage( + int compactionLevel, + IHasherPipelineStage nextStage, + IPostageStampIssuer stampIssuer) + { +#pragma warning disable CA1512 + //disable warning because "ThrowIfLessThan()" is only supported since .Net8 + if (compactionLevel < MinCompactionLevel) + throw new ArgumentOutOfRangeException(nameof(compactionLevel)); +#pragma warning restore CA1512 + + this.compactionLevel = compactionLevel; + this.nextStage = nextStage; + this.stampIssuer = stampIssuer; + } + // Dispose. public void Dispose() { @@ -40,9 +64,44 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) if (args.Data.Length > SwarmChunk.SpanAndDataSize) throw new InvalidOperationException("Data can't be longer than chunk + span size here"); - args.Hash = SwarmChunkBmtHasher.Hash( + var plainChunkHash = SwarmChunkBmtHasher.Hash( args.Data[..SwarmChunk.SpanSize].ToArray(), args.Data[SwarmChunk.SpanSize..].ToArray()); + if (compactionLevel == 0) + { + /* If no chunk compaction is involved, simply calculate the chunk hash and proceed. */ + args.Hash = plainChunkHash; + } + else + { + /* + * If chunk compaction is involved, use optimistic chunk calculation. + * Calculate an encryption key, and try to find an optimal bucket collision. + * Before to proceed with the chunk and its key, wait optimistically until + * the previous chunk has been stored. Then verify if the same bucket has received + * new collisions, or not. + * If not, proceed. + * If yes, 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[..-4] + 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. + * + * If after an optimistic wait we found a collision has happened into the same bucket, + * simply search again the best chunk. At this point we can't do any assumption, and the new first + * best chunk could use and already calculated chunkKey (can't say what), or could be still to + * be calculated. + */ + //TODO + } await nextStage.FeedAsync(args).ConfigureAwait(false); } diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 2b4b3d9..92e6d79 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -28,19 +28,24 @@ public static IHasherPipeline BuildNewHasherPipeline( IPostageStamper postageStamper, RedundancyLevel redundancyLevel, bool isEncrypted, - string? chunkStoreDirectory) => + string? chunkStoreDirectory, + int compactionLevel) => BuildNewHasherPipeline( chunkStoreDirectory is null ? new FakeChunkStore() : new LocalDirectoryChunkStore(chunkStoreDirectory), postageStamper, redundancyLevel, - isEncrypted); + isEncrypted, + compactionLevel); public static IHasherPipeline BuildNewHasherPipeline( IChunkStore chunkStore, IPostageStamper postageStamper, RedundancyLevel redundancyLevel, - bool isEncrypted) + bool isEncrypted, + int compactionLevel) { + ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); + if (redundancyLevel != RedundancyLevel.None) throw new NotImplementedException(); @@ -52,7 +57,7 @@ public static IHasherPipeline BuildNewHasherPipeline( else { //build stages - var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper); + var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper, compactionLevel); var chunkAggregatorStage = new ChunkAggregatorPipelineStage( async (span, data) => @@ -63,7 +68,7 @@ public static IHasherPipeline BuildNewHasherPipeline( } ); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); - bmtStage = new ChunkBmtPipelineStage(storeWriterStage); + bmtStage = new ChunkBmtPipelineStage(compactionLevel, storeWriterStage, postageStamper.StampIssuer); } return new ChunkFeederPipelineStage(bmtStage); @@ -71,10 +76,13 @@ public static IHasherPipeline BuildNewHasherPipeline( public static IHasherPipelineStage BuildNewShortHasherPipeline( IChunkStore chunkStore, - IPostageStamper postageStamper) + IPostageStamper postageStamper, + int compactionLevel) { + ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); + var storeWriter = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(storeWriter); + return new ChunkBmtPipelineStage(compactionLevel, storeWriter, postageStamper.StampIssuer); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Manifest/ManifestEntry.cs b/src/BeeNet.Util/Manifest/ManifestEntry.cs index dfe06ac..694bf9b 100644 --- a/src/BeeNet.Util/Manifest/ManifestEntry.cs +++ b/src/BeeNet.Util/Manifest/ManifestEntry.cs @@ -20,6 +20,8 @@ namespace Etherna.BeeNet.Manifest public class ManifestEntry { // Consts. + public const string ChunkEncryptionKeyKey = "ChunkEncryptKey"; + public const string ChunkRecursivelyEncryptedKey = "ChunkRecursivelyEncrypted"; public const string ContentTypeKey = "Content-Type"; public const string FilenameKey = "Filename"; public const string WebsiteErrorDocPathKey = "website-error-document"; diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index fd799e9..7ccc028 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -32,6 +32,7 @@ public async Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -63,7 +64,8 @@ public async Task EvaluateDirectoryUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt), + encrypt, + compactionLevel), encrypt); // Iterate through the files in the supplied directory. @@ -77,7 +79,8 @@ public async Task EvaluateDirectoryUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt); + encrypt, + compactionLevel); var fileContentType = FileContentTypeProvider.GetContentType(file); var fileName = Path.GetFileName(file); @@ -123,6 +126,7 @@ public async Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -133,6 +137,7 @@ public async Task EvaluateFileUploadAsync( stream, fileContentType, fileName, + compactionLevel, encrypt, redundancyLevel, postageStampIssuer, @@ -143,6 +148,7 @@ public async Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -161,7 +167,8 @@ public async Task EvaluateFileUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt); + encrypt, + compactionLevel); var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); fileName ??= fileHash.ToString(); //if missing, set file name with its address @@ -171,7 +178,8 @@ public async Task EvaluateFileUploadAsync( chunkStore, postageStamper, redundancyLevel, - encrypt), + encrypt, + compactionLevel), encrypt); manifest.Add( @@ -238,6 +246,7 @@ public Task WriteDataChunksAsync( byte[] data, string outputDirectory, bool createDirectory = true, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None) { @@ -246,6 +255,7 @@ public Task WriteDataChunksAsync( stream, outputDirectory, createDirectory, + compactionLevel, encrypt, redundancyLevel); } @@ -254,6 +264,7 @@ public async Task WriteDataChunksAsync( Stream stream, string outputDirectory, bool createDirectory = true, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None) { @@ -264,7 +275,8 @@ public async Task WriteDataChunksAsync( chunkStore, new FakePostageStamper(), redundancyLevel, - encrypt); + encrypt, + compactionLevel); var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); // Return file hash. diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index e83595e..d6d025c 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -29,6 +29,7 @@ public interface ICalculatorService /// The directory to upload /// The index default file /// The error default file + /// Chunk compaction level /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -38,6 +39,7 @@ Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -49,6 +51,7 @@ Task EvaluateDirectoryUploadAsync( /// The file data in byte array /// The file content type /// The file name + /// Chunk compaction level /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -58,6 +61,7 @@ Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -69,6 +73,7 @@ Task EvaluateFileUploadAsync( /// The file stream /// The file content type /// The file name + /// Chunk compaction level /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -78,6 +83,7 @@ Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -109,6 +115,7 @@ Task GetFileStreamFromChunksAsync( /// The data byte array input /// The output directory path /// If true, create if directory doesn't exist + /// Chunk compaction level /// True to encrypt /// Choose the redundancy level /// The data root hash @@ -116,6 +123,7 @@ Task WriteDataChunksAsync( byte[] data, string outputDirectory, bool createDirectory = true, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None); @@ -125,6 +133,7 @@ Task WriteDataChunksAsync( /// The data stream input /// The output directory path /// If true, create if directory doesn't exist + /// Chunk compaction level /// True to encrypt /// Choose the redundancy level /// The data root hash @@ -132,6 +141,7 @@ Task WriteDataChunksAsync( Stream stream, string outputDirectory, bool createDirectory = true, + int compactionLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None); } From 4595a08006f051f0313c59356c95fccfbe59102a Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 16 Jul 2024 19:01:02 +0200 Subject: [PATCH 22/46] Refactor postage buckets for thread safety, and implement reverse index to search buckets by collisions --- .../Models/IReadOnlyPostageBuckets.cs | 48 +++++++ src/BeeNet.Core/Models/PostageBuckets.cs | 126 ++++++++++++------ .../Hashing/Postage/FakePostageStampIssuer.cs | 10 +- .../Hashing/Postage/IPostageStampIssuer.cs | 23 +--- .../Hashing/Postage/PostageStampIssuer.cs | 6 +- .../Services/UploadEvaluationResult.cs | 4 +- 6 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs diff --git a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs new file mode 100644 index 0000000..6ae7d9c --- /dev/null +++ b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs @@ -0,0 +1,48 @@ +// 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; } + + /// + /// Total added chunks in buckets + /// + long TotalChunks { get; } + + // Methods. + /// + /// 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/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs index a3188cf..2391d87 100644 --- a/src/BeeNet.Core/Models/PostageBuckets.cs +++ b/src/BeeNet.Core/Models/PostageBuckets.cs @@ -13,7 +13,8 @@ // If not, see . using System; -using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Etherna.BeeNet.Models @@ -21,13 +22,15 @@ namespace Etherna.BeeNet.Models /// /// A thread safe implementation of postage buckets array /// - public class PostageBuckets + [SuppressMessage("Reliability", "CA2002:Do not lock on objects with weak identity")] + public class PostageBuckets : IReadOnlyPostageBuckets { // Consts. public const int BucketsSize = 1 << PostageBatch.BucketDepth; // Fields. - private readonly ConcurrentDictionary _buckets; // + private readonly uint[] _buckets; //number of collisions. MUST be private to permit locks on it + private readonly Dictionary> bucketsByCollisions; // // Constructor. public PostageBuckets( @@ -38,56 +41,103 @@ public PostageBuckets( throw new ArgumentOutOfRangeException(nameof(initialBuckets), $"Initial buckets must have length {BucketsSize}, or be null"); - _buckets = ArrayToDictionary(initialBuckets ?? []); + //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; } // Properties. - public ReadOnlySpan Buckets => DictionaryToArray(_buckets); - public uint MaxBucketCount { get; private set; } + public uint MaxBucketCollisions { get; private set; } + public uint MinBucketCollisions { get; private set; } public long TotalChunks { get; private set; } // Methods. + public uint[] GetBuckets() + { + lock (_buckets) + { + return _buckets.ToArray(); + } + } + + public IEnumerable GetBucketsByCollisions(uint collisions) + { + lock (_buckets) + { + return bucketsByCollisions.TryGetValue(collisions, out var bucketsSet) + ? bucketsSet + : Array.Empty(); + } + } + public uint GetCollisions(uint bucketId) { - _buckets.TryGetValue(bucketId, out var collisions); - return collisions; + lock (_buckets) + { + return _buckets[bucketId]; + } } 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; - }); + /* + * We need to lock on the full _buckets because we need atomic operations also with bucketsByCollisions. + * ConcurrentDictionary would have better locking on single values, but doesn't support atomic + * operations involving third objects, like counters and "bucketsByCollisions". + * + * By itself, "bucketsByCollisions" would require a full lock to perform atomic buckets moving, + * and this lock would expand also to counters modification, making the full operation inside the + * ConcurrentDictionary blocked by a global lock on "bucketsByCollisions", making the ConcurrentDictionary + * useless. + * + * Because of this, simply lock on "_buckets". + */ + lock (_buckets) + { + // 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++; + } } - 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) + public void ResetBucketCollisions(uint bucketId) { - var outArray = new uint[BucketsSize]; - for (uint i = 0; i < BucketsSize; i++) - if (dictionary.TryGetValue(i, out var value)) - outArray[i] = value; - return outArray; + lock (_buckets) + { + // 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; + } } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/FakePostageStampIssuer.cs index a1b77ff..b50a2ed 100644 --- a/src/BeeNet.Util/Hashing/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.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.Util/Hashing/Postage/IPostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/IPostageStampIssuer.cs index 6432b18..fec1006 100644 --- a/src/BeeNet.Util/Hashing/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.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.Util/Hashing/Postage/PostageStampIssuer.cs b/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs index ac8bc8a..dd55036 100644 --- a/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs +++ b/src/BeeNet.Util/Hashing/Postage/PostageStampIssuer.cs @@ -35,16 +35,12 @@ public PostageStampIssuer( } // 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.Util/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs index e61dd9b..5c9e2a4 100644 --- a/src/BeeNet.Util/Services/UploadEvaluationResult.cs +++ b/src/BeeNet.Util/Services/UploadEvaluationResult.cs @@ -41,7 +41,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; @@ -55,7 +55,7 @@ internal UploadEvaluationResult( /// public int RequiredPostageBatchDepth => Math.Max( - (int)Math.Ceiling(Math.Log2(PostageStampIssuer.MaxBucketCount)) + PostageBatch.BucketDepth, + (int)Math.Ceiling(Math.Log2(PostageStampIssuer.Buckets.MaxBucketCollisions)) + PostageBatch.BucketDepth, PostageBatch.MinDepth); /// From 0eb698b7b76831cc244917ad08433854033287dd Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 16 Jul 2024 19:09:15 +0200 Subject: [PATCH 23/46] Update chunk key encryption logic in BmtPipelineStage --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 7820996..91ca562 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -14,8 +14,10 @@ using Etherna.BeeNet.Hashing.Bmt; using Etherna.BeeNet.Hashing.Postage; +using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; +using System.Buffers.Binary; using System.Threading.Tasks; namespace Etherna.BeeNet.Hashing.Pipeline @@ -86,7 +88,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) * 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[..-4] + attempt) + * chunkKey = Keccack(plainChunkHash[..^4] + attempt) * * The encrypted chunk is calculated encrypting data with the chunk key. * @@ -100,6 +102,40 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) * best chunk could use and already calculated chunkKey (can't say what), or could be still to * be calculated. */ + XorEncryptKey? bestChunkKey = null; + + var encryptedData = new byte[args.Data.Length - SwarmChunk.SpanSize]; + var plainChunkHashArray = plainChunkHash.ToByteArray(); + var spanArray = args.Data[..SwarmChunk.SpanSize].ToArray(); + + // Search best chunk key. + for (int i = 0; i < compactionLevel; i++) + { + // Create key. + BinaryPrimitives.WriteInt32BigEndian(plainChunkHashArray.AsSpan()[..^4], i); + var chunkKey = new XorEncryptKey(plainChunkHashArray); + + // Encrypt data. + args.Data[SwarmChunk.SpanSize..].CopyTo(encryptedData); + chunkKey.EncryptDecrypt(encryptedData); + + // Calculate hash and bucket id. + var encryptedHash = SwarmChunkBmtHasher.Hash(spanArray, encryptedData); + var bucketId = encryptedHash.ToBucketId(); + + // Check key collisions. + var collisions = stampIssuer.Buckets.GetCollisions(bucketId); + if (collisions == stampIssuer.Buckets.MinBucketCollisions) //it's an optimal bucket + { + bestChunkKey = chunkKey; + break; + } + + //TODO + } + + // Perform optimistic waiting. + //TODO } From 6f9cfebd47b3fa481f0041b498b80dde07f43685 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Wed, 17 Jul 2024 03:11:08 +0200 Subject: [PATCH 24/46] continue bmt implementation --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 125 +++++++++++------- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 14 +- src/BeeNet.Util/Services/CalculatorService.cs | 24 ++-- .../Services/ICalculatorService.cs | 20 +-- 4 files changed, 103 insertions(+), 80 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 91ca562..2f16faf 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -18,6 +18,7 @@ using Etherna.BeeNet.Models; using System; using System.Buffers.Binary; +using System.Collections.Generic; using System.Threading.Tasks; namespace Etherna.BeeNet.Hashing.Pipeline @@ -25,33 +26,12 @@ namespace Etherna.BeeNet.Hashing.Pipeline /// /// Calculate hash of each chunk /// - internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage + internal sealed class ChunkBmtPipelineStage( + ushort compactLevel, + IHasherPipelineStage nextStage, + IPostageStampIssuer stampIssuer) + : IHasherPipelineStage { - // Consts. - public const int MinCompactionLevel = 0; - - // Fields. - private readonly int compactionLevel; - private readonly IHasherPipelineStage nextStage; - private readonly IPostageStampIssuer stampIssuer; - - // Constructor. - public ChunkBmtPipelineStage( - int compactionLevel, - IHasherPipelineStage nextStage, - IPostageStampIssuer stampIssuer) - { -#pragma warning disable CA1512 - //disable warning because "ThrowIfLessThan()" is only supported since .Net8 - if (compactionLevel < MinCompactionLevel) - throw new ArgumentOutOfRangeException(nameof(compactionLevel)); -#pragma warning restore CA1512 - - this.compactionLevel = compactionLevel; - this.nextStage = nextStage; - this.stampIssuer = stampIssuer; - } - // Dispose. public void Dispose() { @@ -69,7 +49,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) var plainChunkHash = SwarmChunkBmtHasher.Hash( args.Data[..SwarmChunk.SpanSize].ToArray(), args.Data[SwarmChunk.SpanSize..].ToArray()); - if (compactionLevel == 0) + if (compactLevel == 0) { /* If no chunk compaction is involved, simply calculate the chunk hash and proceed. */ args.Hash = plainChunkHash; @@ -88,7 +68,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) * 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[..^4] + attempt) + * chunkKey = Keccack(plainChunkHash[..^2] + attempt) * * The encrypted chunk is calculated encrypting data with the chunk key. * @@ -101,47 +81,90 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) * simply search again the best chunk. At this point we can't do any assumption, and the new first * best chunk could use and already calculated chunkKey (can't say what), or could be still to * be calculated. + * + * 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. */ - XorEncryptKey? bestChunkKey = null; - - var encryptedData = new byte[args.Data.Length - SwarmChunk.SpanSize]; - var plainChunkHashArray = plainChunkHash.ToByteArray(); - var spanArray = args.Data[..SwarmChunk.SpanSize].ToArray(); // Search best chunk key. - for (int i = 0; i < compactionLevel; i++) + var encryptionCache = new Dictionary(); + var (chunkKey, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); + + // Perform optimistic waiting. + + //TODO + } + + await nextStage.FeedAsync(args).ConfigureAwait(false); + } + + public Task SumAsync() => nextStage.SumAsync(); + + // Helpers. + private (XorEncryptKey ChunkKey, uint ExpectedCollisions) TrySearchFirstBestChunkKey( + HasherPipelineFeedArgs args, + Dictionary encryptionCache, + SwarmHash plainChunkHash) + { + // Init. + XorEncryptKey? bestChunkKey = default; + uint bestCollisions = 0; + + var encryptedData = new byte[args.Data.Length - SwarmChunk.SpanSize]; + var hasher = new Hasher(); + var plainChunkHashArray = plainChunkHash.ToByteArray(); + var spanArray = args.Data[..SwarmChunk.SpanSize].ToArray(); + + // Search best chunk key. + for (ushort i = 0; i < compactLevel; i++) + { + XorEncryptKey chunkKey; + uint collisions; + + if (encryptionCache.TryGetValue(i, out var cachedValues)) + { + chunkKey = cachedValues.ChunkKey; + collisions = stampIssuer.Buckets.GetCollisions(cachedValues.BucketId); + } + else { // Create key. - BinaryPrimitives.WriteInt32BigEndian(plainChunkHashArray.AsSpan()[..^4], i); - var chunkKey = new XorEncryptKey(plainChunkHashArray); + BinaryPrimitives.WriteUInt16BigEndian(plainChunkHashArray.AsSpan()[..^2], i); + chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); // Encrypt data. args.Data[SwarmChunk.SpanSize..].CopyTo(encryptedData); chunkKey.EncryptDecrypt(encryptedData); - // Calculate hash and bucket id. + // Calculate hash, bucket id, and save in cache. var encryptedHash = SwarmChunkBmtHasher.Hash(spanArray, encryptedData); var bucketId = encryptedHash.ToBucketId(); + encryptionCache[i] = (chunkKey, bucketId); // Check key collisions. - var collisions = stampIssuer.Buckets.GetCollisions(bucketId); - if (collisions == stampIssuer.Buckets.MinBucketCollisions) //it's an optimal bucket - { - bestChunkKey = chunkKey; - break; - } - - //TODO + collisions = stampIssuer.Buckets.GetCollisions(bucketId); } - // Perform optimistic waiting. - - //TODO + // First attempt is always the best one. + if (bestChunkKey is null) + { + bestChunkKey = chunkKey; + bestCollisions = collisions; + } + + // Check if collisions are optimal. + if (collisions == stampIssuer.Buckets.MinBucketCollisions) + return (chunkKey, collisions); + + // Else, if this reach better collisions, but not the best. + if (collisions < bestCollisions) + { + bestChunkKey = chunkKey; + bestCollisions = collisions; + } } - await nextStage.FeedAsync(args).ConfigureAwait(false); + return (bestChunkKey!, bestCollisions); } - - public Task SumAsync() => nextStage.SumAsync(); } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 92e6d79..161446a 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -29,20 +29,20 @@ public static IHasherPipeline BuildNewHasherPipeline( RedundancyLevel redundancyLevel, bool isEncrypted, string? chunkStoreDirectory, - int compactionLevel) => + ushort compactLevel) => BuildNewHasherPipeline( chunkStoreDirectory is null ? new FakeChunkStore() : new LocalDirectoryChunkStore(chunkStoreDirectory), postageStamper, redundancyLevel, isEncrypted, - compactionLevel); + compactLevel); public static IHasherPipeline BuildNewHasherPipeline( IChunkStore chunkStore, IPostageStamper postageStamper, RedundancyLevel redundancyLevel, bool isEncrypted, - int compactionLevel) + ushort compactLevel) { ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); @@ -57,7 +57,7 @@ public static IHasherPipeline BuildNewHasherPipeline( else { //build stages - var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper, compactionLevel); + var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper, compactLevel); var chunkAggregatorStage = new ChunkAggregatorPipelineStage( async (span, data) => @@ -68,7 +68,7 @@ public static IHasherPipeline BuildNewHasherPipeline( } ); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); - bmtStage = new ChunkBmtPipelineStage(compactionLevel, storeWriterStage, postageStamper.StampIssuer); + bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } return new ChunkFeederPipelineStage(bmtStage); @@ -77,12 +77,12 @@ public static IHasherPipeline BuildNewHasherPipeline( public static IHasherPipelineStage BuildNewShortHasherPipeline( IChunkStore chunkStore, IPostageStamper postageStamper, - int compactionLevel) + ushort compactLevel) { ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); var storeWriter = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(compactionLevel, storeWriter, postageStamper.StampIssuer); + return new ChunkBmtPipelineStage(compactLevel, storeWriter, postageStamper.StampIssuer); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 7ccc028..4e74dc4 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -32,7 +32,7 @@ public async Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -65,7 +65,7 @@ public async Task EvaluateDirectoryUploadAsync( postageStamper, redundancyLevel, encrypt, - compactionLevel), + compactLevel), encrypt); // Iterate through the files in the supplied directory. @@ -80,7 +80,7 @@ public async Task EvaluateDirectoryUploadAsync( postageStamper, redundancyLevel, encrypt, - compactionLevel); + compactLevel); var fileContentType = FileContentTypeProvider.GetContentType(file); var fileName = Path.GetFileName(file); @@ -126,7 +126,7 @@ public async Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -137,7 +137,7 @@ public async Task EvaluateFileUploadAsync( stream, fileContentType, fileName, - compactionLevel, + compactLevel, encrypt, redundancyLevel, postageStampIssuer, @@ -148,7 +148,7 @@ public async Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -168,7 +168,7 @@ public async Task EvaluateFileUploadAsync( postageStamper, redundancyLevel, encrypt, - compactionLevel); + compactLevel); var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); fileName ??= fileHash.ToString(); //if missing, set file name with its address @@ -179,7 +179,7 @@ public async Task EvaluateFileUploadAsync( postageStamper, redundancyLevel, encrypt, - compactionLevel), + compactLevel), encrypt); manifest.Add( @@ -246,7 +246,7 @@ public Task WriteDataChunksAsync( byte[] data, string outputDirectory, bool createDirectory = true, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None) { @@ -255,7 +255,7 @@ public Task WriteDataChunksAsync( stream, outputDirectory, createDirectory, - compactionLevel, + compactLevel, encrypt, redundancyLevel); } @@ -264,7 +264,7 @@ public async Task WriteDataChunksAsync( Stream stream, string outputDirectory, bool createDirectory = true, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None) { @@ -276,7 +276,7 @@ public async Task WriteDataChunksAsync( new FakePostageStamper(), redundancyLevel, encrypt, - compactionLevel); + compactLevel); var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); // Return file hash. diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index d6d025c..31013f4 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -29,7 +29,7 @@ public interface ICalculatorService /// The directory to upload /// The index default file /// The error default file - /// Chunk compaction level + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -39,7 +39,7 @@ Task EvaluateDirectoryUploadAsync( string directoryPath, string? indexFilename = null, string? errorFilename = null, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -51,7 +51,7 @@ Task EvaluateDirectoryUploadAsync( /// The file data in byte array /// The file content type /// The file name - /// Chunk compaction level + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -61,7 +61,7 @@ Task EvaluateFileUploadAsync( byte[] data, string fileContentType, string? fileName, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -73,7 +73,7 @@ Task EvaluateFileUploadAsync( /// The file stream /// The file content type /// The file name - /// Chunk compaction level + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// Custom postage stamp issuer @@ -83,7 +83,7 @@ Task EvaluateFileUploadAsync( Stream stream, string fileContentType, string? fileName, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, @@ -115,7 +115,7 @@ Task GetFileStreamFromChunksAsync( /// The data byte array input /// The output directory path /// If true, create if directory doesn't exist - /// Chunk compaction level + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// The data root hash @@ -123,7 +123,7 @@ Task WriteDataChunksAsync( byte[] data, string outputDirectory, bool createDirectory = true, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None); @@ -133,7 +133,7 @@ Task WriteDataChunksAsync( /// The data stream input /// The output directory path /// If true, create if directory doesn't exist - /// Chunk compaction level + /// Chunk compact level [0, 65535] /// True to encrypt /// Choose the redundancy level /// The data root hash @@ -141,7 +141,7 @@ Task WriteDataChunksAsync( Stream stream, string outputDirectory, bool createDirectory = true, - int compactionLevel = 0, + ushort compactLevel = 0, bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None); } From 69266ca54a260a324be133b6bac3f30d6091e482 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Wed, 17 Jul 2024 18:24:56 +0200 Subject: [PATCH 25/46] Implement task execution coordination in ChunkBmtPipelineStage --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 2f16faf..c3ec95f 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -18,6 +18,7 @@ using Etherna.BeeNet.Models; using System; using System.Buffers.Binary; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading.Tasks; @@ -32,6 +33,9 @@ internal sealed class ChunkBmtPipelineStage( IPostageStampIssuer stampIssuer) : IHasherPipelineStage { + // Fields. + private readonly ConcurrentDictionary lockObjectsDictionary = new(); // + // Dispose. public void Dispose() { @@ -90,9 +94,39 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) var encryptionCache = new Dictionary(); var (chunkKey, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); - // Perform optimistic waiting. - - //TODO + // Coordinate tasks execution in order. + var lockObj = new object(); + lock (lockObj) + { + //add current lockObj in dictionary, in order can be found by next task + lockObjectsDictionary.TryAdd(args.NumberId, lockObj); + + //if it's not the first task, try to lock on previous chunk's lockObj + if (args.NumberId > 0) + { + //get lockObj from previous chunk, and remove from dictionary to clean up. + //at this point, it must be already locked by prev task, and maybe be released. + object prevLockObj; + while (!lockObjectsDictionary.TryRemove(args.NumberId - 1, out prevLockObj!)) + Task.Delay(1); + + //wait until it is released from prev task + lock (prevLockObj) + { + // Check the optimistic result, and if it has been invalidated, do it again. + + //TODO + } + } + else + { + // Because this is the first chunk, we don't need to wait any previous chunk to complete. + // Moreover, any result with optimistic calculation must be valid, because no previous chunks + // can have invalidated the result. So we can simply proceed. + + //TODO + } + } } await nextStage.FeedAsync(args).ConfigureAwait(false); From 9af166a161792b2e80beadd3deebd19eade33cf9 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Fri, 19 Jul 2024 14:26:17 +0200 Subject: [PATCH 26/46] Update encryption logic and data handling in ChunkBmtPipelineStage --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 73 +++++++++++++------ .../Pipeline/HasherPipelineFeedArgs.cs | 14 +++- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index c3ec95f..2b5e20c 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -41,6 +41,9 @@ public void Dispose() { nextStage.Dispose(); } + + // Properties. + public long OptimisticRetriesCounter { get; private set; } // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) @@ -91,8 +94,8 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) */ // Search best chunk key. - var encryptionCache = new Dictionary(); - var (chunkKey, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); + var encryptionCache = new Dictionary(); + var (bestKeyAttempt, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); // Coordinate tasks execution in order. var lockObj = new object(); @@ -114,8 +117,26 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) lock (prevLockObj) { // Check the optimistic result, and if it has been invalidated, do it again. - - //TODO + + var bestBucketId = encryptionCache[bestKeyAttempt].BucketId; + var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); + + if (actualCollisions == expectedCollisions) + { + //if optimism succeeded + args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; + args.Data = encryptionCache[bestKeyAttempt].EncryptedData; + } + else + { + //if optimism failed, recalculate + OptimisticRetriesCounter++; + + var (newBestKeyAttempt, _) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); + + args.ChunkKey = encryptionCache[newBestKeyAttempt].ChunkKey; + args.Data = encryptionCache[newBestKeyAttempt].EncryptedData; + } } } else @@ -123,8 +144,9 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) // Because this is the first chunk, we don't need to wait any previous chunk to complete. // Moreover, any result with optimistic calculation must be valid, because no previous chunks // can have invalidated the result. So we can simply proceed. - - //TODO + + args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; + args.Data = encryptionCache[bestKeyAttempt].EncryptedData; } } } @@ -135,70 +157,75 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) public Task SumAsync() => nextStage.SumAsync(); // Helpers. - private (XorEncryptKey ChunkKey, uint ExpectedCollisions) TrySearchFirstBestChunkKey( + 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 (ushort BestKeyAttempt, uint ExpectedCollisions) TrySearchFirstBestChunkKey( HasherPipelineFeedArgs args, - Dictionary encryptionCache, + Dictionary encryptionCache, SwarmHash plainChunkHash) { // Init. - XorEncryptKey? bestChunkKey = default; + ushort? bestKeyAttempt = null; uint bestCollisions = 0; - var encryptedData = new byte[args.Data.Length - SwarmChunk.SpanSize]; + var encryptedData = new byte[args.Data.Length]; var hasher = new Hasher(); var plainChunkHashArray = plainChunkHash.ToByteArray(); - var spanArray = args.Data[..SwarmChunk.SpanSize].ToArray(); // Search best chunk key. for (ushort i = 0; i < compactLevel; i++) { - XorEncryptKey chunkKey; uint collisions; if (encryptionCache.TryGetValue(i, out var cachedValues)) { - chunkKey = cachedValues.ChunkKey; collisions = stampIssuer.Buckets.GetCollisions(cachedValues.BucketId); } else { // Create key. BinaryPrimitives.WriteUInt16BigEndian(plainChunkHashArray.AsSpan()[..^2], i); - chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); + var chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); // Encrypt data. - args.Data[SwarmChunk.SpanSize..].CopyTo(encryptedData); - chunkKey.EncryptDecrypt(encryptedData); + args.Data.CopyTo(encryptedData); + EncryptDecryptChunkData(chunkKey, encryptedData); // Calculate hash, bucket id, and save in cache. - var encryptedHash = SwarmChunkBmtHasher.Hash(spanArray, encryptedData); + var encryptedHash = SwarmChunkBmtHasher.Hash( + encryptedData[..SwarmChunk.SpanSize], + encryptedData[SwarmChunk.SpanSize..]); var bucketId = encryptedHash.ToBucketId(); - encryptionCache[i] = (chunkKey, bucketId); + encryptionCache[i] = (chunkKey, encryptedData, bucketId); // Check key collisions. collisions = stampIssuer.Buckets.GetCollisions(bucketId); } // First attempt is always the best one. - if (bestChunkKey is null) + if (bestKeyAttempt is null) { - bestChunkKey = chunkKey; + bestKeyAttempt = i; bestCollisions = collisions; } // Check if collisions are optimal. if (collisions == stampIssuer.Buckets.MinBucketCollisions) - return (chunkKey, collisions); + return (i, collisions); // Else, if this reach better collisions, but not the best. if (collisions < bestCollisions) { - bestChunkKey = chunkKey; + bestKeyAttempt = i; bestCollisions = collisions; } } - return (bestChunkKey!, bestCollisions); + return (bestKeyAttempt!.Value, bestCollisions); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs index e323a5a..0bd30d9 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.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 Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; @@ -20,8 +21,8 @@ namespace Etherna.BeeNet.Hashing.Pipeline public sealed class HasherPipelineFeedArgs { // Fields. - private readonly byte[] _data; private readonly byte[]? _span; + private byte[] _data; // Constructor. public HasherPipelineFeedArgs( @@ -45,10 +46,19 @@ public HasherPipelineFeedArgs( } // 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 From 7af83b64f9d60896b0dc5325bf42eddae46581b6 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Fri, 19 Jul 2024 15:34:38 +0200 Subject: [PATCH 27/46] fixes in bmt hashing --- .../Hashing/Bmt/SwarmChunkBmtHasher.cs | 4 +- ...ChunkBmtPipelineOptimisticAttemptResult.cs | 19 +++++ .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 76 +++++++++++-------- 3 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs diff --git a/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs index 2e472a9..cec1ece 100644 --- a/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs +++ b/src/BeeNet.Util/Hashing/Bmt/SwarmChunkBmtHasher.cs @@ -22,7 +22,7 @@ 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)); @@ -39,7 +39,7 @@ public static SwarmHash Hash(byte[] span, byte[] data) } // Build the merkle tree. - var hasher = new Hasher(); + hasher ??= new Hasher(); var bmt = new SwarmChunkBmt(hasher); bmt.BuildTree(segments); var result = bmt.Root.Hash; diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs new file mode 100644 index 0000000..3d8b6bd --- /dev/null +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs @@ -0,0 +1,19 @@ +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 ChunkBmtPipelineOptimisticAttemptResult( + ushort attemptNumber, + XorEncryptKey chunkKey, + byte[] encryptedData, + SwarmHash hash) + { + public ushort AttemptNumber { get; } = attemptNumber; + 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.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 2b5e20c..5045af5 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -27,15 +27,27 @@ namespace Etherna.BeeNet.Hashing.Pipeline /// /// Calculate hash of each chunk /// - internal sealed class ChunkBmtPipelineStage( - ushort compactLevel, - IHasherPipelineStage nextStage, - IPostageStampIssuer stampIssuer) - : IHasherPipelineStage + internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage { // Fields. private readonly ConcurrentDictionary lockObjectsDictionary = new(); // - + private readonly ushort compactLevel; + private readonly IHasherPipelineStage nextStage; + private readonly IPostageStampIssuer stampIssuer; + + // 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() { @@ -52,10 +64,14 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) 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()); + args.Data[SwarmChunk.SpanSize..].ToArray(), + hasher); if (compactLevel == 0) { /* If no chunk compaction is involved, simply calculate the chunk hash and proceed. */ @@ -75,7 +91,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) * 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) + * chunkKey = Keccack(plainChunkHash[^2..] + attempt) * * The encrypted chunk is calculated encrypting data with the chunk key. * @@ -94,8 +110,8 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) */ // Search best chunk key. - var encryptionCache = new Dictionary(); - var (bestKeyAttempt, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); + var encryptionCache = new Dictionary(); + var (bestKeyAttempt, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); // Coordinate tasks execution in order. var lockObj = new object(); @@ -104,7 +120,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) //add current lockObj in dictionary, in order can be found by next task lockObjectsDictionary.TryAdd(args.NumberId, lockObj); - //if it's not the first task, try to lock on previous chunk's lockObj + //if it's not the first chunk, try to lock on previous chunk's lockObj if (args.NumberId > 0) { //get lockObj from previous chunk, and remove from dictionary to clean up. @@ -118,7 +134,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) { // Check the optimistic result, and if it has been invalidated, do it again. - var bestBucketId = encryptionCache[bestKeyAttempt].BucketId; + var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); if (actualCollisions == expectedCollisions) @@ -126,16 +142,18 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) //if optimism succeeded args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; args.Data = encryptionCache[bestKeyAttempt].EncryptedData; + args.Hash = encryptionCache[bestKeyAttempt].Hash; } else { //if optimism failed, recalculate OptimisticRetriesCounter++; - var (newBestKeyAttempt, _) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash); + var (newBestKeyAttempt, _) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); args.ChunkKey = encryptionCache[newBestKeyAttempt].ChunkKey; args.Data = encryptionCache[newBestKeyAttempt].EncryptedData; + args.Hash = encryptionCache[newBestKeyAttempt].Hash; } } } @@ -147,6 +165,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; args.Data = encryptionCache[bestKeyAttempt].EncryptedData; + args.Hash = encryptionCache[bestKeyAttempt].Hash; } } } @@ -165,15 +184,15 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) private (ushort BestKeyAttempt, uint ExpectedCollisions) TrySearchFirstBestChunkKey( HasherPipelineFeedArgs args, - Dictionary encryptionCache, - SwarmHash plainChunkHash) + Dictionary optimisticCache, + SwarmHash plainChunkHash, + Hasher hasher) { // Init. - ushort? bestKeyAttempt = null; + ushort bestAttempt = 0; uint bestCollisions = 0; var encryptedData = new byte[args.Data.Length]; - var hasher = new Hasher(); var plainChunkHashArray = plainChunkHash.ToByteArray(); // Search best chunk key. @@ -181,14 +200,14 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) { uint collisions; - if (encryptionCache.TryGetValue(i, out var cachedValues)) + if (optimisticCache.TryGetValue(i, out var cachedValues)) { - collisions = stampIssuer.Buckets.GetCollisions(cachedValues.BucketId); + collisions = stampIssuer.Buckets.GetCollisions(cachedValues.Hash.ToBucketId()); } else { // Create key. - BinaryPrimitives.WriteUInt16BigEndian(plainChunkHashArray.AsSpan()[..^2], i); + BinaryPrimitives.WriteUInt16BigEndian(plainChunkHashArray.AsSpan()[^2..], i); var chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); // Encrypt data. @@ -198,20 +217,17 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) // Calculate hash, bucket id, and save in cache. var encryptedHash = SwarmChunkBmtHasher.Hash( encryptedData[..SwarmChunk.SpanSize], - encryptedData[SwarmChunk.SpanSize..]); - var bucketId = encryptedHash.ToBucketId(); - encryptionCache[i] = (chunkKey, encryptedData, bucketId); + encryptedData[SwarmChunk.SpanSize..], + hasher); + optimisticCache[i] = new(i, chunkKey, encryptedData, encryptedHash); // Check key collisions. - collisions = stampIssuer.Buckets.GetCollisions(bucketId); + collisions = stampIssuer.Buckets.GetCollisions(encryptedHash.ToBucketId()); } // First attempt is always the best one. - if (bestKeyAttempt is null) - { - bestKeyAttempt = i; + if (i == 0) bestCollisions = collisions; - } // Check if collisions are optimal. if (collisions == stampIssuer.Buckets.MinBucketCollisions) @@ -220,12 +236,12 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) // Else, if this reach better collisions, but not the best. if (collisions < bestCollisions) { - bestKeyAttempt = i; + bestAttempt = i; bestCollisions = collisions; } } - return (bestKeyAttempt!.Value, bestCollisions); + return (bestAttempt, bestCollisions); } } } \ No newline at end of file From 732d38b1d053168360dd92e54ba4920f41dbab02 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Fri, 19 Jul 2024 18:26:55 +0200 Subject: [PATCH 28/46] compact also intermediate chunks --- .../Pipeline/ChunkAggregatorPipelineStage.cs | 33 +++- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 149 ++++++++++-------- .../Pipeline/ChunkFeederPipelineStage.cs | 4 +- .../Hashing/Pipeline/ChunkHashingResult.cs | 11 ++ .../Pipeline/ChunkStoreWriterPipelineStage.cs | 2 +- ...sult.cs => CompactedChunkAttemptResult.cs} | 4 +- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 12 +- .../Hashing/Pipeline/IHasherPipeline.cs | 5 +- .../Hashing/Pipeline/IHasherPipelineStage.cs | 3 +- src/BeeNet.Util/Manifest/MantarayNode.cs | 3 +- src/BeeNet.Util/Services/CalculatorService.cs | 14 +- 11 files changed, 141 insertions(+), 99 deletions(-) create mode 100644 src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs rename src/BeeNet.Util/Hashing/Pipeline/{ChunkBmtPipelineOptimisticAttemptResult.cs => CompactedChunkAttemptResult.cs} (76%) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs index 4d66004..59baf62 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs @@ -13,6 +13,7 @@ // If not, see . using Etherna.BeeNet.Hashing.Bmt; +using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -22,19 +23,25 @@ namespace Etherna.BeeNet.Hashing.Pipeline { - internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); + internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); internal sealed class ChunkAggregatorPipelineStage : IHasherPipelineStage { // Private classes. - private sealed 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; } // Fields. + private readonly bool compactChunks; private readonly SemaphoreSlim feedChunkMutex = new(1, 1); private readonly Dictionary feedingBuffer = new(); private readonly List> chunkLevels; //[level][chunk] @@ -45,11 +52,15 @@ private sealed class ChunkHeader(SwarmHash hash, ReadOnlyMemory span, bool // Constructor. public ChunkAggregatorPipelineStage( - HashChunkDelegateAsync hashChunkDelegate) + HashChunkDelegateAsync hashChunkDelegate, + bool compactChunks) { chunkLevels = []; this.hashChunkDelegate = hashChunkDelegate; - maxChildrenChunks = SwarmChunkBmt.SegmentsCount; + this.compactChunks = compactChunks; + maxChildrenChunks = (byte)(this.compactChunks + ? SwarmChunkBmt.SegmentsCount / 2 //write chunk key after chunk hash + : SwarmChunkBmt.SegmentsCount); } // Dispose. @@ -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); } // Helpers. @@ -151,16 +163,21 @@ 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 => compactChunks + ? 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 hashChunkDelegate(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(); diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 5045af5..23463f2 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -34,18 +34,22 @@ internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage private readonly ushort compactLevel; private readonly IHasherPipelineStage nextStage; private readonly IPostageStampIssuer stampIssuer; + private readonly bool useWithDataChunks; //can run with parallelization only with data chunks // Constructor. /// /// Calculate hash of each chunk /// - public ChunkBmtPipelineStage(ushort compactLevel, + public ChunkBmtPipelineStage( + ushort compactLevel, IHasherPipelineStage nextStage, - IPostageStampIssuer stampIssuer) + IPostageStampIssuer stampIssuer, + bool useWithDataChunks) { this.compactLevel = compactLevel; this.nextStage = nextStage; this.stampIssuer = stampIssuer; + this.useWithDataChunks = useWithDataChunks; } // Dispose. @@ -80,7 +84,11 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) else { /* - * If chunk compaction is involved, use optimistic chunk calculation. + * If chunk compaction is involved, and we are processing data chunks, use optimistic calculation. + * + * If instead this step is invoked with the short pipeline, and so isn't related to data chunk hashing, + * the optimistic algorithm is not required. In fact, in this case, calculation is already synchronous. + * * Calculate an encryption key, and try to find an optimal bucket collision. * Before to proceed with the chunk and its key, wait optimistically until * the previous chunk has been stored. Then verify if the same bucket has received @@ -110,70 +118,19 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) */ // Search best chunk key. - var encryptionCache = new Dictionary(); - var (bestKeyAttempt, expectedCollisions) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); - - // Coordinate tasks execution in order. - var lockObj = new object(); - lock (lockObj) - { - //add current lockObj in dictionary, in order can be found by next task - lockObjectsDictionary.TryAdd(args.NumberId, lockObj); - - //if it's not the first chunk, try to lock on previous chunk's lockObj - if (args.NumberId > 0) - { - //get lockObj from previous chunk, and remove from dictionary to clean up. - //at this point, it must be already locked by prev task, and maybe be released. - object prevLockObj; - while (!lockObjectsDictionary.TryRemove(args.NumberId - 1, out prevLockObj!)) - Task.Delay(1); - - //wait until it is released from prev task - lock (prevLockObj) - { - // Check the optimistic result, and if it has been invalidated, do it again. - - var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); - var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); - - if (actualCollisions == expectedCollisions) - { - //if optimism succeeded - args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; - args.Data = encryptionCache[bestKeyAttempt].EncryptedData; - args.Hash = encryptionCache[bestKeyAttempt].Hash; - } - else - { - //if optimism failed, recalculate - OptimisticRetriesCounter++; - - var (newBestKeyAttempt, _) = TrySearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); - - args.ChunkKey = encryptionCache[newBestKeyAttempt].ChunkKey; - args.Data = encryptionCache[newBestKeyAttempt].EncryptedData; - args.Hash = encryptionCache[newBestKeyAttempt].Hash; - } - } - } - else - { - // Because this is the first chunk, we don't need to wait any previous chunk to complete. - // Moreover, any result with optimistic calculation must be valid, because no previous chunks - // can have invalidated the result. So we can simply proceed. - - args.ChunkKey = encryptionCache[bestKeyAttempt].ChunkKey; - args.Data = encryptionCache[bestKeyAttempt].EncryptedData; - args.Hash = encryptionCache[bestKeyAttempt].Hash; - } - } + var bestChunkResult = useWithDataChunks + ? GetBestChunkOptimistically(args, plainChunkHash, hasher) + : GetBestChunkSynchronously(args, plainChunkHash, hasher); + + args.ChunkKey = bestChunkResult.ChunkKey; + args.Data = bestChunkResult.EncryptedData; + args.Hash = bestChunkResult.Hash; } await nextStage.FeedAsync(args).ConfigureAwait(false); } - - public Task SumAsync() => nextStage.SumAsync(); + + public Task SumAsync() => nextStage.SumAsync(); // Helpers. private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) @@ -182,9 +139,69 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) chunkKey.EncryptDecrypt(data.AsSpan()[SwarmChunk.SpanSize..]); } - private (ushort BestKeyAttempt, uint ExpectedCollisions) TrySearchFirstBestChunkKey( + private CompactedChunkAttemptResult GetBestChunkOptimistically( + HasherPipelineFeedArgs args, + SwarmHash plainChunkHash, + Hasher hasher) + { + var encryptionCache = new Dictionary(); + var (bestKeyAttempt, expectedCollisions) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + + // Coordinate tasks execution in order. + var lockObj = new object(); + lock (lockObj) + { + //add current lockObj in dictionary, in order can be found by next task + lockObjectsDictionary.TryAdd(args.NumberId, lockObj); + + //if it's the first chunk + if (args.NumberId == 0) + { + // Because this is the first chunk, we don't need to wait any previous chunk to complete. + // Moreover, any result with optimistic calculation must be valid, because no previous chunks + // can have invalidated the result. So we can simply proceed. + return encryptionCache[bestKeyAttempt]; + } + + //if it's not the first chunk, try to lock on previous chunk's lockObj, and remove it from dictionary. + //at this point, it must be already locked by prev task, and maybe be released. + object prevLockObj; + while (!lockObjectsDictionary.TryRemove(args.NumberId - 1, out prevLockObj!)) + Task.Delay(1); + + //wait until it is released from prev task + lock (prevLockObj) + { + // Check the optimistic result, and if it has been invalidated, do it again. + var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); + var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); + + //if optimism succeeded + if (actualCollisions == expectedCollisions) + return encryptionCache[bestKeyAttempt]; + + //if optimism failed, recalculate + OptimisticRetriesCounter++; + + var (newBestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + return encryptionCache[newBestKeyAttempt]; + } + } + } + + private CompactedChunkAttemptResult GetBestChunkSynchronously( + HasherPipelineFeedArgs args, + SwarmHash plainChunkHash, + Hasher hasher) + { + var encryptionCache = new Dictionary(); + var (bestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + return encryptionCache[bestKeyAttempt]; + } + + private (ushort BestKeyAttempt, uint ExpectedCollisions) SearchFirstBestChunkKey( HasherPipelineFeedArgs args, - Dictionary optimisticCache, + Dictionary optimisticCache, SwarmHash plainChunkHash, Hasher hasher) { @@ -219,7 +236,7 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) encryptedData[..SwarmChunk.SpanSize], encryptedData[SwarmChunk.SpanSize..], hasher); - optimisticCache[i] = new(i, chunkKey, encryptedData, encryptedHash); + optimisticCache[i] = new(chunkKey, encryptedData, encryptedHash); // Check key collisions. collisions = stampIssuer.Buckets.GetCollisions(encryptedHash.ToBucketId()); diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index 0b24cf6..7971d95 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -52,7 +52,7 @@ public void Dispose() public bool IsUsable { get; private set; } = true; // Methods. - public async Task HashDataAsync(byte[] data) + public async Task HashDataAsync(byte[] data) { ArgumentNullException.ThrowIfNull(data, nameof(data)); @@ -60,7 +60,7 @@ public async Task HashDataAsync(byte[] data) return await HashDataAsync(memoryStream).ConfigureAwait(false); } - public async Task HashDataAsync(Stream dataStream) + public async Task HashDataAsync(Stream dataStream) { ArgumentNullException.ThrowIfNull(dataStream, nameof(dataStream)); diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs new file mode 100644 index 0000000..3f24d50 --- /dev/null +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs @@ -0,0 +1,11 @@ +using Etherna.BeeNet.Manifest; +using Etherna.BeeNet.Models; + +namespace Etherna.BeeNet.Hashing.Pipeline +{ + public class ChunkHashingResult(SwarmHash hash, XorEncryptKey? encryptionKey) + { + public XorEncryptKey? EncryptionKey { get; } = encryptionKey; + public SwarmHash Hash { get; } = hash; + } +} \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs index e1a5d98..cd056fb 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs @@ -50,7 +50,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/ChunkBmtPipelineOptimisticAttemptResult.cs b/src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs similarity index 76% rename from src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs rename to src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs index 3d8b6bd..df4ed46 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineOptimisticAttemptResult.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/CompactedChunkAttemptResult.cs @@ -5,13 +5,11 @@ namespace Etherna.BeeNet.Hashing.Pipeline { [SuppressMessage("Performance", "CA1819:Properties should not return arrays")] - public class ChunkBmtPipelineOptimisticAttemptResult( - ushort attemptNumber, + public class CompactedChunkAttemptResult( XorEncryptKey chunkKey, byte[] encryptedData, SwarmHash hash) { - public ushort AttemptNumber { get; } = attemptNumber; public XorEncryptKey ChunkKey { get; } = chunkKey; public byte[] EncryptedData { get; } = encryptedData; public SwarmHash Hash { get; } = hash; diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 161446a..a070421 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -64,11 +64,11 @@ public static IHasherPipeline BuildNewHasherPipeline( { var args = new HasherPipelineFeedArgs(span: span, data: data); await shortPipelineStage.FeedAsync(args).ConfigureAwait(false); - return args.Hash!.Value; - } - ); + return new(args.Hash!.Value, args.ChunkKey); + }, + compactLevel > 0); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); - bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); + bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer, true); } return new ChunkFeederPipelineStage(bmtStage); @@ -81,8 +81,8 @@ public static IHasherPipelineStage BuildNewShortHasherPipeline( { ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); - var storeWriter = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(compactLevel, storeWriter, postageStamper.StampIssuer); + var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); + return new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer, false); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs index 67b4869..17f38c6 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.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.Models; using System; using System.IO; using System.Threading.Tasks; @@ -28,13 +27,13 @@ public interface IHasherPipeline : IDisposable /// /// 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.Util/Hashing/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index ea578c8..ef76fc5 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.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.Models; using System; using System.Threading.Tasks; @@ -21,6 +20,6 @@ namespace Etherna.BeeNet.Hashing.Pipeline public interface IHasherPipelineStage : IDisposable { Task FeedAsync(HasherPipelineFeedArgs args); - Task SumAsync(); + Task SumAsync(); } } \ No newline at end of file diff --git a/src/BeeNet.Util/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs index 346926e..13f1ae4 100644 --- a/src/BeeNet.Util/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -156,7 +156,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(); diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 4e74dc4..f2c0743 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -86,12 +86,12 @@ public async Task EvaluateDirectoryUploadAsync( 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); // Add file entry to dir manifest. dirManifest.Add( Path.GetRelativePath(directoryPath, file), - ManifestEntry.NewFile(fileHash, new Dictionary + ManifestEntry.NewFile(fileHashingResult.Hash, new Dictionary { [ManifestEntry.ContentTypeKey] = fileContentType, [ManifestEntry.FilenameKey] = fileName @@ -169,8 +169,8 @@ public async Task EvaluateFileUploadAsync( redundancyLevel, encrypt, compactLevel); - var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); - fileName ??= fileHash.ToString(); //if missing, set file name with its address + 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( @@ -193,7 +193,7 @@ public async Task EvaluateFileUploadAsync( manifest.Add( fileName, ManifestEntry.NewFile( - fileHash, + fileHashingResult.Hash, new Dictionary { [ManifestEntry.ContentTypeKey] = fileContentType, @@ -277,10 +277,10 @@ public async Task WriteDataChunksAsync( redundancyLevel, encrypt, compactLevel); - var fileHash = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); + var fileHashingResult = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); // Return file hash. - return fileHash; + return fileHashingResult.Hash; } } } \ No newline at end of file From 6b24f7ea37e022824d90482e61f882e6b490f08f Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 20 Jul 2024 01:49:17 +0200 Subject: [PATCH 29/46] refactoring and add encryption key in file metadata --- .../Models}/XorEncryptKey.cs | 29 ++++++++++++- src/BeeNet.Util/Manifest/ManifestEntry.cs | 3 +- src/BeeNet.Util/Manifest/MantarayManifest.cs | 12 +++--- src/BeeNet.Util/Manifest/MantarayNode.cs | 3 +- src/BeeNet.Util/Services/CalculatorService.cs | 42 ++++++++++++------- 5 files changed, 63 insertions(+), 26 deletions(-) rename src/{BeeNet.Util/Manifest => BeeNet.Core/Models}/XorEncryptKey.cs (72%) diff --git a/src/BeeNet.Util/Manifest/XorEncryptKey.cs b/src/BeeNet.Core/Models/XorEncryptKey.cs similarity index 72% rename from src/BeeNet.Util/Manifest/XorEncryptKey.cs rename to src/BeeNet.Core/Models/XorEncryptKey.cs index b1c88f0..5ef2099 100644 --- a/src/BeeNet.Util/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/Manifest/ManifestEntry.cs b/src/BeeNet.Util/Manifest/ManifestEntry.cs index 694bf9b..d6427af 100644 --- a/src/BeeNet.Util/Manifest/ManifestEntry.cs +++ b/src/BeeNet.Util/Manifest/ManifestEntry.cs @@ -20,8 +20,7 @@ namespace Etherna.BeeNet.Manifest public class ManifestEntry { // Consts. - public const string ChunkEncryptionKeyKey = "ChunkEncryptKey"; - public const string ChunkRecursivelyEncryptedKey = "ChunkRecursivelyEncrypted"; + public const string ChunkEncryptKeyKey = "ChunkEncryptKey"; public const string ContentTypeKey = "Content-Type"; public const string FilenameKey = "Filename"; public const string WebsiteErrorDocPathKey = "website-error-document"; diff --git a/src/BeeNet.Util/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs index a03ce96..9cd5b6c 100644 --- a/src/BeeNet.Util/Manifest/MantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/MantarayManifest.cs @@ -19,18 +19,20 @@ namespace Etherna.BeeNet.Manifest { + public delegate IHasherPipeline BuildHasherPipeline(); + public class MantarayManifest : IReadOnlyMantarayManifest { // Consts. 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 ChunkHashingResult(_rootNode.Hash, null); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs index 13f1ae4..e472ee9 100644 --- a/src/BeeNet.Util/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -14,7 +14,6 @@ using Etherna.BeeNet.Extensions; using Etherna.BeeNet.Hashing; -using Etherna.BeeNet.Hashing.Pipeline; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -143,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)); diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index f2c0743..dcef861 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -65,7 +65,7 @@ public async Task EvaluateDirectoryUploadAsync( postageStamper, redundancyLevel, encrypt, - compactLevel), + 0), encrypt); // Iterate through the files in the supplied directory. @@ -89,13 +89,19 @@ public async Task EvaluateDirectoryUploadAsync( var fileHashingResult = await fileHasherPipeline.HashDataAsync(fileStream).ConfigureAwait(false); // 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()); + dirManifest.Add( Path.GetRelativePath(directoryPath, file), - ManifestEntry.NewFile(fileHashingResult.Hash, new Dictionary - { - [ManifestEntry.ContentTypeKey] = fileContentType, - [ManifestEntry.FilenameKey] = fileName - })); + ManifestEntry.NewFile( + fileHashingResult.Hash, + fileEntryMetadata)); } // Store website information. @@ -114,11 +120,11 @@ 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, postageStampIssuer); } @@ -179,7 +185,7 @@ public async Task EvaluateFileUploadAsync( postageStamper, redundancyLevel, encrypt, - compactLevel), + 0), encrypt); manifest.Add( @@ -189,22 +195,26 @@ 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()); manifest.Add( fileName, ManifestEntry.NewFile( fileHashingResult.Hash, - new Dictionary - { - [ManifestEntry.ContentTypeKey] = fileContentType, - [ManifestEntry.FilenameKey] = fileName - })); + fileEntryMetadata)); - var manifestHash = await manifest.GetHashAsync().ConfigureAwait(false); + var chunkHashingResult = await manifest.GetHashAsync().ConfigureAwait(false); // Return result. return new UploadEvaluationResult( - manifestHash, + chunkHashingResult.Hash, postageStampIssuer); } From 61bfba8ce5be5d024444eb9861776229c3387e85 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 20 Jul 2024 02:48:01 +0200 Subject: [PATCH 30/46] implementing reading chunks with recursive encryption --- src/BeeNet.Client/BeeClient.cs | 4 +- src/BeeNet.Core/Models/SwarmChunkReference.cs | 9 +++++ .../Pipeline/ChunkAggregatorPipelineStage.cs | 16 ++++---- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 2 +- .../Pipeline/ChunkFeederPipelineStage.cs | 4 +- .../Hashing/Pipeline/ChunkHashingResult.cs | 11 ------ .../Pipeline/ChunkStoreWriterPipelineStage.cs | 2 +- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 7 ++-- .../Hashing/Pipeline/IHasherPipeline.cs | 5 ++- .../Hashing/Pipeline/IHasherPipelineStage.cs | 3 +- src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs | 37 +++++++++++++++---- src/BeeNet.Util/IBeeClient.cs | 2 +- .../Manifest/IReadOnlyMantarayNode.cs | 2 +- src/BeeNet.Util/Manifest/ManifestEntry.cs | 1 + src/BeeNet.Util/Manifest/MantarayManifest.cs | 4 +- src/BeeNet.Util/Manifest/MantarayNode.cs | 2 +- .../Manifest/ReferencedMantarayManifest.cs | 8 ++-- .../Manifest/ReferencedMantarayNode.cs | 4 +- src/BeeNet.Util/Services/CalculatorService.cs | 8 +++- 19 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 src/BeeNet.Core/Models/SwarmChunkReference.cs delete mode 100644 src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs diff --git a/src/BeeNet.Client/BeeClient.cs b/src/BeeNet.Client/BeeClient.cs index cf6c2a3..0cb7b55 100644 --- a/src/BeeNet.Client/BeeClient.cs +++ b/src/BeeNet.Client/BeeClient.cs @@ -879,7 +879,7 @@ public async Task RefreshAuthAsync( }, cancellationToken).ConfigureAwait(false)).Key; - public async Task ResolveSwarmAddressToHashAsync(SwarmAddress address) + public async Task ResolveAddressToChunkReferenceAsync(SwarmAddress address) { var chunkStore = new BeeClientChunkStore(this); @@ -887,7 +887,7 @@ public async Task ResolveSwarmAddressToHashAsync(SwarmAddress address chunkStore, address.Hash); - return await rootManifest.ResolveResourceHashAsync(address).ConfigureAwait(false); + return await rootManifest.ResolveAddressToChunkReferenceAsync(address).ConfigureAwait(false); } public Task ReuploadContentAsync( diff --git a/src/BeeNet.Core/Models/SwarmChunkReference.cs b/src/BeeNet.Core/Models/SwarmChunkReference.cs new file mode 100644 index 0000000..e746b17 --- /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.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs index 59baf62..e3bc070 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs @@ -23,7 +23,7 @@ namespace Etherna.BeeNet.Hashing.Pipeline { - internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); + internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); internal sealed class ChunkAggregatorPipelineStage : IHasherPipelineStage { @@ -41,26 +41,26 @@ private sealed class ChunkHeader( } // Fields. - private readonly bool compactChunks; 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 bool useRecursiveEncryption; private long feededChunkNumberId; // Constructor. public ChunkAggregatorPipelineStage( HashChunkDelegateAsync hashChunkDelegate, - bool compactChunks) + bool useRecursiveEncryption) { chunkLevels = []; this.hashChunkDelegate = hashChunkDelegate; - this.compactChunks = compactChunks; - maxChildrenChunks = (byte)(this.compactChunks + maxChildrenChunks = (byte)(useRecursiveEncryption ? SwarmChunkBmt.SegmentsCount / 2 //write chunk key after chunk hash : SwarmChunkBmt.SegmentsCount); + this.useRecursiveEncryption = useRecursiveEncryption; } // Dispose. @@ -104,7 +104,7 @@ await AddChunkToLevelAsync( } } - public async Task SumAsync() + public async Task SumAsync() { bool rootChunkFound = false; for (int i = 0; !rootChunkFound; i++) @@ -130,7 +130,7 @@ public async Task SumAsync() var rootChunk = chunkLevels.Last()[0]; - return new(rootChunk.Hash, rootChunk.ChunkKey); + return new(rootChunk.Hash, rootChunk.ChunkKey, useRecursiveEncryption); } // Helpers. @@ -165,7 +165,7 @@ private async Task WrapFullLevelAsync(int level) // 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 => compactChunks + levelChunks.SelectMany(c => useRecursiveEncryption ? c.Hash.ToByteArray().Concat(c.ChunkKey!.Bytes.ToArray()) : c.Hash.ToByteArray())) .ToArray(); diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 23463f2..0af72ae 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -130,7 +130,7 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) await nextStage.FeedAsync(args).ConfigureAwait(false); } - public Task SumAsync() => nextStage.SumAsync(); + public Task SumAsync() => nextStage.SumAsync(); // Helpers. private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index 7971d95..19ec6a8 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -52,7 +52,7 @@ public void Dispose() public bool IsUsable { get; private set; } = true; // Methods. - public async Task HashDataAsync(byte[] data) + public async Task HashDataAsync(byte[] data) { ArgumentNullException.ThrowIfNull(data, nameof(data)); @@ -60,7 +60,7 @@ public async Task HashDataAsync(byte[] data) return await HashDataAsync(memoryStream).ConfigureAwait(false); } - public async Task HashDataAsync(Stream dataStream) + public async Task HashDataAsync(Stream dataStream) { ArgumentNullException.ThrowIfNull(dataStream, nameof(dataStream)); diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs deleted file mode 100644 index 3f24d50..0000000 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkHashingResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Etherna.BeeNet.Manifest; -using Etherna.BeeNet.Models; - -namespace Etherna.BeeNet.Hashing.Pipeline -{ - public class ChunkHashingResult(SwarmHash hash, XorEncryptKey? encryptionKey) - { - public XorEncryptKey? EncryptionKey { get; } = encryptionKey; - public SwarmHash Hash { get; } = hash; - } -} \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs index cd056fb..a98d11e 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs @@ -50,7 +50,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/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index a070421..35f00ad 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -58,15 +58,16 @@ public static IHasherPipeline BuildNewHasherPipeline( { //build stages var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper, compactLevel); - + + var useRecursiveEncryption = compactLevel > 0; var chunkAggregatorStage = new ChunkAggregatorPipelineStage( async (span, data) => { var args = new HasherPipelineFeedArgs(span: span, data: data); await shortPipelineStage.FeedAsync(args).ConfigureAwait(false); - return new(args.Hash!.Value, args.ChunkKey); + return new(args.Hash!.Value, args.ChunkKey, useRecursiveEncryption); }, - compactLevel > 0); + useRecursiveEncryption); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer, true); } diff --git a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs index 17f38c6..4ccd0eb 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.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 Etherna.BeeNet.Models; using System; using System.IO; using System.Threading.Tasks; @@ -27,13 +28,13 @@ public interface IHasherPipeline : IDisposable /// /// 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.Util/Hashing/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index ef76fc5..ce3c09c 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.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 Etherna.BeeNet.Models; using System; using System.Threading.Tasks; @@ -20,6 +21,6 @@ namespace Etherna.BeeNet.Hashing.Pipeline public interface IHasherPipelineStage : IDisposable { Task FeedAsync(HasherPipelineFeedArgs args); - Task SumAsync(); + Task SumAsync(); } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs index 01e1232..9c7d387 100644 --- a/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs +++ b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs @@ -13,6 +13,7 @@ // If not, see . using Etherna.BeeNet.Models; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -22,20 +23,40 @@ public class ChunkJoiner( IReadOnlyChunkStore chunkStore) { // Methods. - public async Task> GetJoinedChunkDataAsync(SwarmHash hash) + public async Task> GetJoinedChunkDataAsync(SwarmChunkReference chunkReference) { - var chunk = await chunkStore.GetAsync(hash).ConfigureAwait(false); - var totalDataLength = SwarmChunk.SpanToLength(chunk.Span.Span); + ArgumentNullException.ThrowIfNull(chunkReference, nameof(chunkReference)); + + var chunk = await chunkStore.GetAsync(chunkReference.Hash).ConfigureAwait(false); + var dataArray = chunk.Data.ToArray(); + chunkReference.EncryptionKey?.EncryptDecrypt(dataArray); + + var totalDataLength = SwarmChunk.SpanToLength(chunk.Span.Span); if (totalDataLength <= SwarmChunk.DataSize) - return chunk.Data.ToArray(); + return dataArray; var joinedData = new List(); - - for (int i = 0; i < chunk.Data.Length; i += SwarmHash.HashSize) + for (int i = 0; i < dataArray.Length;) { - var childHash = new SwarmHash(chunk.Data[i..(i + SwarmHash.HashSize)].ToArray()); - joinedData.AddRange(await GetJoinedChunkDataAsync(childHash).ConfigureAwait(false)); + //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 + joinedData.AddRange(await GetJoinedChunkDataAsync( + new SwarmChunkReference( + childHash, + childEncryptionKey, + chunkReference.UseRecursiveEncryption)).ConfigureAwait(false)); } return joinedData; diff --git a/src/BeeNet.Util/IBeeClient.cs b/src/BeeNet.Util/IBeeClient.cs index a6be056..df2fd1b 100644 --- a/src/BeeNet.Util/IBeeClient.cs +++ b/src/BeeNet.Util/IBeeClient.cs @@ -536,7 +536,7 @@ Task RefreshAuthAsync( int expiry, CancellationToken cancellationToken = default); - Task ResolveSwarmAddressToHashAsync(SwarmAddress address); + Task ResolveAddressToChunkReferenceAsync(SwarmAddress address); /// Reupload a root hash to the network /// Root hash of content (can be of any type: collection, file, chunk) diff --git a/src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs b/src/BeeNet.Util/Manifest/IReadOnlyMantarayNode.cs index 9861ed4..0a70e52 100644 --- a/src/BeeNet.Util/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.Util/Manifest/ManifestEntry.cs b/src/BeeNet.Util/Manifest/ManifestEntry.cs index d6427af..e107190 100644 --- a/src/BeeNet.Util/Manifest/ManifestEntry.cs +++ b/src/BeeNet.Util/Manifest/ManifestEntry.cs @@ -25,6 +25,7 @@ public class ManifestEntry 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.Util/Manifest/MantarayManifest.cs b/src/BeeNet.Util/Manifest/MantarayManifest.cs index 9cd5b6c..aa68674 100644 --- a/src/BeeNet.Util/Manifest/MantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/MantarayManifest.cs @@ -60,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 new ChunkHashingResult(_rootNode.Hash, null); + return new SwarmChunkReference(_rootNode.Hash, null, false); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Manifest/MantarayNode.cs b/src/BeeNet.Util/Manifest/MantarayNode.cs index e472ee9..fa62f48 100644 --- a/src/BeeNet.Util/Manifest/MantarayNode.cs +++ b/src/BeeNet.Util/Manifest/MantarayNode.cs @@ -194,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(); diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs index b7b5fb8..97a2fc9 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayManifest.cs @@ -43,17 +43,15 @@ public async Task> GetResourceMetadataAsync( if (!_rootNode.IsDecoded) await _rootNode.DecodeFromChunkAsync().ConfigureAwait(false); - return await RootNode.GetResourceMetadataAsync( - address.Path?.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.Path?.ToString() ?? "").ConfigureAwait(false); + return await RootNode.ResolveChunkReferenceAsync(address.Path).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index f9185dc..48d3606 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -124,7 +124,7 @@ 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)); @@ -154,7 +154,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.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index dcef861..c0aa96c 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -96,6 +96,8 @@ public async Task EvaluateDirectoryUploadAsync( }; 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), @@ -203,6 +205,8 @@ public async Task EvaluateFileUploadAsync( }; if (fileHashingResult.EncryptionKey != null) fileEntryMetadata.Add(ManifestEntry.ChunkEncryptKeyKey, fileHashingResult.EncryptionKey.ToString()); + if (compactLevel > 0) + fileEntryMetadata.Add(ManifestEntry.UseRecursiveEncryptionKey, true.ToString()); manifest.Add( fileName, @@ -242,10 +246,10 @@ public async Task GetFileStreamFromChunksAsync( 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); + var resourceData = await chunkJoiner.GetJoinedChunkDataAsync(chunkReference).ConfigureAwait(false); memoryStream.Write(resourceData.ToArray()); memoryStream.Position = 0; From 84c0d3838c366e62edf62e8295028cf16d6ba076 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 20 Jul 2024 16:36:42 +0200 Subject: [PATCH 31/46] read decoding info from mantaray --- .../Manifest/ReferencedMantarayNode.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index 48d3606..8e5d151 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -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"); @@ -129,21 +137,24 @@ 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(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. From bf9ce7983ba3db09827b4699c14fec05cda794f9 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 20 Jul 2024 16:43:56 +0200 Subject: [PATCH 32/46] fix accessing mantaray metadata --- src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs index 8e5d151..6486ecf 100644 --- a/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs +++ b/src/BeeNet.Util/Manifest/ReferencedMantarayNode.cs @@ -102,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(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. From f77bf8ad7a7ab7ef45b6146e2571b3b02d122862 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sat, 20 Jul 2024 20:23:44 +0200 Subject: [PATCH 33/46] fix bmt async execution --- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 144 ++++++------------ .../Pipeline/ChunkFeederPipelineStage.cs | 61 ++++++-- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 4 +- .../Pipeline/HasherPipelineFeedArgs.cs | 13 +- 4 files changed, 111 insertions(+), 111 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 0af72ae..6681a52 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -14,11 +14,9 @@ using Etherna.BeeNet.Hashing.Bmt; using Etherna.BeeNet.Hashing.Postage; -using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; using System.Buffers.Binary; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading.Tasks; @@ -30,11 +28,9 @@ namespace Etherna.BeeNet.Hashing.Pipeline internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage { // Fields. - private readonly ConcurrentDictionary lockObjectsDictionary = new(); // private readonly ushort compactLevel; private readonly IHasherPipelineStage nextStage; private readonly IPostageStampIssuer stampIssuer; - private readonly bool useWithDataChunks; //can run with parallelization only with data chunks // Constructor. /// @@ -43,13 +39,11 @@ internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage public ChunkBmtPipelineStage( ushort compactLevel, IHasherPipelineStage nextStage, - IPostageStampIssuer stampIssuer, - bool useWithDataChunks) + IPostageStampIssuer stampIssuer) { this.compactLevel = compactLevel; this.nextStage = nextStage; this.stampIssuer = stampIssuer; - this.useWithDataChunks = useWithDataChunks; } // Dispose. @@ -57,9 +51,6 @@ public void Dispose() { nextStage.Dispose(); } - - // Properties. - public long OptimisticRetriesCounter { get; private set; } // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) @@ -83,44 +74,8 @@ public async Task FeedAsync(HasherPipelineFeedArgs args) } else { - /* - * If chunk compaction is involved, and we are processing data chunks, use optimistic calculation. - * - * If instead this step is invoked with the short pipeline, and so isn't related to data chunk hashing, - * the optimistic algorithm is not required. In fact, in this case, calculation is already synchronous. - * - * Calculate an encryption key, and try to find an optimal bucket collision. - * Before to proceed with the chunk and its key, wait optimistically until - * the previous chunk has been stored. Then verify if the same bucket has received - * new collisions, or not. - * If not, proceed. - * If yes, 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. - * - * If after an optimistic wait we found a collision has happened into the same bucket, - * simply search again the best chunk. At this point we can't do any assumption, and the new first - * best chunk could use and already calculated chunkKey (can't say what), or could be still to - * be calculated. - * - * 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. - */ - // Search best chunk key. - var bestChunkResult = useWithDataChunks - ? GetBestChunkOptimistically(args, plainChunkHash, hasher) - : GetBestChunkSynchronously(args, plainChunkHash, hasher); + var bestChunkResult = await GetBestChunkAsync(args, plainChunkHash, hasher).ConfigureAwait(false); args.ChunkKey = bestChunkResult.ChunkKey; args.Data = bestChunkResult.EncryptedData; @@ -139,64 +94,63 @@ private static void EncryptDecryptChunkData(XorEncryptKey chunkKey, byte[] data) chunkKey.EncryptDecrypt(data.AsSpan()[SwarmChunk.SpanSize..]); } - private CompactedChunkAttemptResult GetBestChunkOptimistically( + 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); - // Coordinate tasks execution in order. - var lockObj = new object(); - lock (lockObj) - { - //add current lockObj in dictionary, in order can be found by next task - lockObjectsDictionary.TryAdd(args.NumberId, lockObj); + // If there isn't any prev chunk to wait, proceed with result. + if (args.PrevChunkSemaphore == null) + return encryptionCache[bestKeyAttempt]; - //if it's the first chunk - if (args.NumberId == 0) - { - // Because this is the first chunk, we don't need to wait any previous chunk to complete. - // Moreover, any result with optimistic calculation must be valid, because no previous chunks - // can have invalidated the result. So we can simply proceed. + try + { + // Otherwise wait until prev chunk has been processed. + await args.PrevChunkSemaphore.WaitAsync().ConfigureAwait(false); + + // Check the optimistic result, and if it has been invalidated, do it again. + var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); + var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); + + if (actualCollisions == expectedCollisions) return encryptionCache[bestKeyAttempt]; - } - - //if it's not the first chunk, try to lock on previous chunk's lockObj, and remove it from dictionary. - //at this point, it must be already locked by prev task, and maybe be released. - object prevLockObj; - while (!lockObjectsDictionary.TryRemove(args.NumberId - 1, out prevLockObj!)) - Task.Delay(1); - - //wait until it is released from prev task - lock (prevLockObj) - { - // Check the optimistic result, and if it has been invalidated, do it again. - var bestBucketId = encryptionCache[bestKeyAttempt].Hash.ToBucketId(); - var actualCollisions = stampIssuer.Buckets.GetCollisions(bestBucketId); - - //if optimism succeeded - if (actualCollisions == expectedCollisions) - return encryptionCache[bestKeyAttempt]; - - //if optimism failed, recalculate - OptimisticRetriesCounter++; - - var (newBestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); - return encryptionCache[newBestKeyAttempt]; - } + + var (newBestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); + return encryptionCache[newBestKeyAttempt]; + } + finally + { + //release prev chunk semaphore to reuse it + args.PrevChunkSemaphore.Release(); } - } - - private CompactedChunkAttemptResult GetBestChunkSynchronously( - HasherPipelineFeedArgs args, - SwarmHash plainChunkHash, - Hasher hasher) - { - var encryptionCache = new Dictionary(); - var (bestKeyAttempt, _) = SearchFirstBestChunkKey(args, encryptionCache, plainChunkHash, hasher); - return encryptionCache[bestKeyAttempt]; } private (ushort BestKeyAttempt, uint ExpectedCollisions) SearchFirstBestChunkKey( diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index 19ec6a8..c05686f 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -14,7 +14,9 @@ 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; @@ -25,27 +27,42 @@ 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( - IHasherPipelineStage nextStage, - int chunkConcurrency) - : IHasherPipeline + internal sealed class ChunkFeederPipelineStage : IHasherPipeline { // Fields. + private readonly SemaphoreSlim chunkConcurrencySemaphore; + private readonly ConcurrentQueue chunkSemaphorePool; + private readonly IHasherPipelineStage nextStage; private readonly List nextStageTasks = new(); - private readonly SemaphoreSlim semaphore = new(chunkConcurrency, chunkConcurrency); private long passedBytes; - - // Constructor. + + // Constructors. public ChunkFeederPipelineStage(IHasherPipelineStage nextStage) : this(nextStage, Environment.ProcessorCount) { } + [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] + public ChunkFeederPipelineStage( + IHasherPipelineStage nextStage, + int chunkConcurrency) + { + this.nextStage = nextStage; + chunkConcurrencySemaphore = new(chunkConcurrency, chunkConcurrency); + chunkSemaphorePool = new ConcurrentQueue(); + + //init semaphore pool + for (int i = 0; i < chunkConcurrency + 1; i++) + chunkSemaphorePool.Enqueue(new SemaphoreSlim(1, 1)); + } + // Dispose. public void Dispose() { nextStage.Dispose(); - semaphore.Dispose(); + chunkConcurrencySemaphore.Dispose(); + while (chunkSemaphorePool.TryDequeue(out var semaphore)) + semaphore.Dispose(); } // Properties. @@ -73,6 +90,7 @@ public async Task HashDataAsync(Stream dataStream) // 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? prevChunkHashedSemaphore = null; do { chunkReadSize = await dataStream.ReadAsync(chunkBuffer).ConfigureAwait(false); @@ -90,11 +108,24 @@ public async Task HashDataAsync(Stream dataStream) (ulong)chunkReadSize); // Invoke next stage with parallelism on chunks. - await semaphore.WaitAsync().ConfigureAwait(false); + //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: passedBytes / SwarmChunk.DataSize); + numberId: passedBytes / SwarmChunk.DataSize, + prevChunkSemaphore: prevChunkHashedSemaphore); + + //run task nextStageTasks.Add( Task.Run(async () => { @@ -104,10 +135,18 @@ public async Task HashDataAsync(Stream dataStream) } finally { - semaphore.Release(); + //release and restore chunk hashed semaphore in pool + chunkSemaphore.Release(); + chunkSemaphorePool.Enqueue(chunkSemaphore); + + //release task for next chunk + chunkConcurrencySemaphore.Release(); } })); + //set current chunk hashed semaphore as prev for next chunk + prevChunkHashedSemaphore = chunkSemaphore; + passedBytes += chunkReadSize; } } while (chunkReadSize == SwarmChunk.DataSize); diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 35f00ad..a822f22 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -69,7 +69,7 @@ public static IHasherPipeline BuildNewHasherPipeline( }, useRecursiveEncryption); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); - bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer, true); + bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } return new ChunkFeederPipelineStage(bmtStage); @@ -83,7 +83,7 @@ public static IHasherPipelineStage BuildNewShortHasherPipeline( ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer, false); + return new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs index 0bd30d9..dae12f2 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineFeedArgs.cs @@ -12,9 +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.Manifest; using Etherna.BeeNet.Models; using System; +using System.Threading; namespace Etherna.BeeNet.Hashing.Pipeline { @@ -28,7 +28,8 @@ public sealed class HasherPipelineFeedArgs public HasherPipelineFeedArgs( byte[] data, byte[]? span = null, - long numberId = 0) + long numberId = 0, + SemaphoreSlim? prevChunkSemaphore = null) { ArgumentNullException.ThrowIfNull(data, nameof(data)); @@ -43,6 +44,7 @@ public HasherPipelineFeedArgs( _data = data; _span = span; NumberId = numberId; + PrevChunkSemaphore = prevChunkSemaphore; } // Properties. @@ -71,8 +73,13 @@ public ReadOnlyMemory Data 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 From 6827191b41ac5701d3d80932680332ff1be0d4fc Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 21 Jul 2024 02:54:08 +0200 Subject: [PATCH 34/46] expose chunkConcurrency argument --- .../Pipeline/ChunkFeederPipelineStage.cs | 12 ++++---- .../Hashing/Pipeline/HasherPipelineBuilder.cs | 11 +++++--- src/BeeNet.Util/Services/CalculatorService.cs | 28 +++++++++++++------ .../Services/ICalculatorService.cs | 14 ++++++++-- 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index c05686f..b7589b9 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -38,21 +38,19 @@ internal sealed class ChunkFeederPipelineStage : IHasherPipeline private long passedBytes; // Constructors. - public ChunkFeederPipelineStage(IHasherPipelineStage nextStage) - : this(nextStage, Environment.ProcessorCount) - { } - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")] public ChunkFeederPipelineStage( IHasherPipelineStage nextStage, - int chunkConcurrency) + int? chunkConcurrency = default) { + chunkConcurrency ??= Environment.ProcessorCount; + this.nextStage = nextStage; - chunkConcurrencySemaphore = new(chunkConcurrency, chunkConcurrency); + chunkConcurrencySemaphore = new(chunkConcurrency.Value, chunkConcurrency.Value); chunkSemaphorePool = new ConcurrentQueue(); //init semaphore pool - for (int i = 0; i < chunkConcurrency + 1; i++) + for (int i = 0; i < chunkConcurrency + 1; i++) //add one more to avoid circular dependencies chunkSemaphorePool.Enqueue(new SemaphoreSlim(1, 1)); } diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index a822f22..0b309f6 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -29,20 +29,23 @@ public static IHasherPipeline BuildNewHasherPipeline( RedundancyLevel redundancyLevel, bool isEncrypted, string? chunkStoreDirectory, - ushort compactLevel) => + ushort compactLevel, + int? chunkConcurrency) => BuildNewHasherPipeline( chunkStoreDirectory is null ? new FakeChunkStore() : new LocalDirectoryChunkStore(chunkStoreDirectory), postageStamper, redundancyLevel, isEncrypted, - compactLevel); + compactLevel, + chunkConcurrency); public static IHasherPipeline BuildNewHasherPipeline( IChunkStore chunkStore, IPostageStamper postageStamper, RedundancyLevel redundancyLevel, bool isEncrypted, - ushort compactLevel) + ushort compactLevel, + int? chunkConcurrency) { ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); @@ -72,7 +75,7 @@ public static IHasherPipeline BuildNewHasherPipeline( bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } - return new ChunkFeederPipelineStage(bmtStage); + return new ChunkFeederPipelineStage(bmtStage, chunkConcurrency); } public static IHasherPipelineStage BuildNewShortHasherPipeline( diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index c0aa96c..5b0362d 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -36,6 +36,7 @@ public async Task EvaluateDirectoryUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { // Checks. @@ -65,7 +66,8 @@ public async Task EvaluateDirectoryUploadAsync( postageStamper, redundancyLevel, encrypt, - 0), + 0, + chunkCuncorrency), encrypt); // Iterate through the files in the supplied directory. @@ -80,7 +82,8 @@ public async Task EvaluateDirectoryUploadAsync( postageStamper, redundancyLevel, encrypt, - compactLevel); + compactLevel, + chunkCuncorrency); var fileContentType = FileContentTypeProvider.GetContentType(file); var fileName = Path.GetFileName(file); @@ -138,6 +141,7 @@ public async Task EvaluateFileUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { using var stream = new MemoryStream(data); @@ -149,6 +153,7 @@ public async Task EvaluateFileUploadAsync( encrypt, redundancyLevel, postageStampIssuer, + chunkCuncorrency, chunkStore).ConfigureAwait(false); } @@ -160,6 +165,7 @@ public async Task EvaluateFileUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null) { chunkStore ??= new FakeChunkStore(); @@ -176,7 +182,8 @@ public async Task EvaluateFileUploadAsync( postageStamper, redundancyLevel, encrypt, - compactLevel); + compactLevel, + chunkCuncorrency); var fileHashingResult = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); fileName ??= fileHashingResult.Hash.ToString(); //if missing, set file name with its address @@ -187,7 +194,8 @@ public async Task EvaluateFileUploadAsync( postageStamper, redundancyLevel, encrypt, - 0), + 0, + chunkCuncorrency), encrypt); manifest.Add( @@ -262,7 +270,8 @@ public Task WriteDataChunksAsync( bool createDirectory = true, ushort compactLevel = 0, bool encrypt = false, - RedundancyLevel redundancyLevel = RedundancyLevel.None) + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null) { using var stream = new MemoryStream(data); return WriteDataChunksAsync( @@ -271,7 +280,8 @@ public Task WriteDataChunksAsync( createDirectory, compactLevel, encrypt, - redundancyLevel); + redundancyLevel, + chunkCuncorrency); } public async Task WriteDataChunksAsync( @@ -280,7 +290,8 @@ public async Task WriteDataChunksAsync( bool createDirectory = true, ushort compactLevel = 0, bool encrypt = false, - RedundancyLevel redundancyLevel = RedundancyLevel.None) + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null) { var chunkStore = new LocalDirectoryChunkStore(outputDirectory, createDirectory); @@ -290,7 +301,8 @@ public async Task WriteDataChunksAsync( new FakePostageStamper(), redundancyLevel, encrypt, - compactLevel); + compactLevel, + chunkCuncorrency); var fileHashingResult = await fileHasherPipeline.HashDataAsync(stream).ConfigureAwait(false); // Return file hash. diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index 31013f4..1ccd757 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -33,6 +33,7 @@ public interface ICalculatorService /// 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( @@ -43,6 +44,7 @@ Task EvaluateDirectoryUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -55,6 +57,7 @@ Task EvaluateDirectoryUploadAsync( /// 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( @@ -65,6 +68,7 @@ Task EvaluateFileUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -77,6 +81,7 @@ Task EvaluateFileUploadAsync( /// 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( @@ -87,6 +92,7 @@ Task EvaluateFileUploadAsync( bool encrypt = false, RedundancyLevel redundancyLevel = RedundancyLevel.None, IPostageStampIssuer? postageStampIssuer = null, + int? chunkCuncorrency = null, IChunkStore? chunkStore = null); /// @@ -118,6 +124,7 @@ Task GetFileStreamFromChunksAsync( /// 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, @@ -125,7 +132,8 @@ Task WriteDataChunksAsync( bool createDirectory = true, ushort compactLevel = 0, bool encrypt = false, - RedundancyLevel redundancyLevel = RedundancyLevel.None); + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null); /// /// Write data chunks on a local directory, without any manifest @@ -136,6 +144,7 @@ Task WriteDataChunksAsync( /// 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, @@ -143,6 +152,7 @@ Task WriteDataChunksAsync( bool createDirectory = true, ushort compactLevel = 0, bool encrypt = false, - RedundancyLevel redundancyLevel = RedundancyLevel.None); + RedundancyLevel redundancyLevel = RedundancyLevel.None, + int? chunkCuncorrency = null); } } \ No newline at end of file From 3fe1a41662bed4953c273ffb813beaeb5529cd20 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 21 Jul 2024 05:04:56 +0200 Subject: [PATCH 35/46] fix hashing --- src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs index 6681a52..92eb1f6 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -163,7 +163,6 @@ private async Task GetBestChunkAsync( ushort bestAttempt = 0; uint bestCollisions = 0; - var encryptedData = new byte[args.Data.Length]; var plainChunkHashArray = plainChunkHash.ToByteArray(); // Search best chunk key. @@ -182,7 +181,7 @@ private async Task GetBestChunkAsync( var chunkKey = new XorEncryptKey(hasher.ComputeHash(plainChunkHashArray)); // Encrypt data. - args.Data.CopyTo(encryptedData); + var encryptedData = args.Data.ToArray(); EncryptDecryptChunkData(chunkKey, encryptedData); // Calculate hash, bucket id, and save in cache. From fa8cb6a352c0ef39d71562f57ac2ae4e34e8228b Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 21 Jul 2024 15:20:33 +0200 Subject: [PATCH 36/46] use ReaderWriterLockSlim in PostageBuckets instead of lock --- src/BeeNet.Core/Models/PostageBuckets.cs | 65 +++++++++++++++++++----- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/BeeNet.Core/Models/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs index 2391d87..6d368f7 100644 --- a/src/BeeNet.Core/Models/PostageBuckets.cs +++ b/src/BeeNet.Core/Models/PostageBuckets.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; namespace Etherna.BeeNet.Models { @@ -23,7 +24,7 @@ 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 + public class PostageBuckets : IReadOnlyPostageBuckets, IDisposable { // Consts. public const int BucketsSize = 1 << PostageBatch.BucketDepth; @@ -31,6 +32,8 @@ public class PostageBuckets : IReadOnlyPostageBuckets // Fields. private readonly uint[] _buckets; //number of collisions. MUST be private to permit locks on it private readonly Dictionary> bucketsByCollisions; // + private readonly ReaderWriterLockSlim bucketsLock = new(LockRecursionPolicy.NoRecursion); + private bool disposed; // Constructor. public PostageBuckets( @@ -53,6 +56,24 @@ public PostageBuckets( 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; } @@ -61,28 +82,43 @@ public PostageBuckets( // Methods. public uint[] GetBuckets() { - lock (_buckets) + bucketsLock.EnterReadLock(); + try { return _buckets.ToArray(); } + finally + { + bucketsLock.ExitReadLock(); + } } public IEnumerable GetBucketsByCollisions(uint collisions) { - lock (_buckets) + bucketsLock.EnterReadLock(); + try { return bucketsByCollisions.TryGetValue(collisions, out var bucketsSet) ? bucketsSet : Array.Empty(); } + finally + { + bucketsLock.ExitReadLock(); + } } public uint GetCollisions(uint bucketId) { - lock (_buckets) + bucketsLock.EnterReadLock(); + try { return _buckets[bucketId]; } + finally + { + bucketsLock.ExitReadLock(); + } } public void IncrementCollisions(uint bucketId) @@ -91,15 +127,9 @@ public void IncrementCollisions(uint bucketId) * We need to lock on the full _buckets because we need atomic operations also with bucketsByCollisions. * ConcurrentDictionary would have better locking on single values, but doesn't support atomic * operations involving third objects, like counters and "bucketsByCollisions". - * - * By itself, "bucketsByCollisions" would require a full lock to perform atomic buckets moving, - * and this lock would expand also to counters modification, making the full operation inside the - * ConcurrentDictionary blocked by a global lock on "bucketsByCollisions", making the ConcurrentDictionary - * useless. - * - * Because of this, simply lock on "_buckets". */ - lock (_buckets) + bucketsLock.EnterWriteLock(); + try { // Update collections. _buckets[bucketId]++; @@ -118,11 +148,16 @@ public void IncrementCollisions(uint bucketId) TotalChunks++; } + finally + { + bucketsLock.ExitWriteLock(); + } } public void ResetBucketCollisions(uint bucketId) { - lock (_buckets) + bucketsLock.EnterWriteLock(); + try { // Update collections. var oldCollisions = _buckets[bucketId]; @@ -138,6 +173,10 @@ public void ResetBucketCollisions(uint bucketId) MinBucketCollisions = 0; } + finally + { + bucketsLock.ExitWriteLock(); + } } } } \ No newline at end of file From 89076a2402027c6dc4897859e8ca6bb7ef5afd4b Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Sun, 21 Jul 2024 17:58:31 +0200 Subject: [PATCH 37/46] fix chunk concurrency issue --- .../Hashing/Pipeline/ChunkFeederPipelineStage.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index b7589b9..3fd0041 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -50,7 +50,15 @@ public ChunkFeederPipelineStage( chunkSemaphorePool = new ConcurrentQueue(); //init semaphore pool - for (int i = 0; i < chunkConcurrency + 1; i++) //add one more to avoid circular dependencies + /* + * Duplicate number of semaphore respect to level of concurrency. + * This avoids the possible race condition where a chunk starts to wait a prev chunk, + * after that the prev chunk's semaphore is already been assigned to a new next chunk. + * + * In this way, because semaphores are distributed with a queue, the queue will never reassign + * a semaphore before the chunk windows has shifted, and prev chunk linking it has completed. + */ + for (int i = 0; i < chunkConcurrency * 2; i++) chunkSemaphorePool.Enqueue(new SemaphoreSlim(1, 1)); } From 9e26703fe759e901cf341caf3fb6986a1a8f4db5 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 22 Jul 2024 00:58:03 +0200 Subject: [PATCH 38/46] minor --- .../Hashing/Pipeline/ChunkFeederPipelineStage.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index 3fd0041..d0b5427 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -96,7 +96,7 @@ public async Task HashDataAsync(Stream dataStream) // 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? prevChunkHashedSemaphore = null; + SemaphoreSlim? prevChunkSemaphore = null; do { chunkReadSize = await dataStream.ReadAsync(chunkBuffer).ConfigureAwait(false); @@ -129,7 +129,7 @@ public async Task HashDataAsync(Stream dataStream) span: chunkData[..SwarmChunk.SpanSize], data: chunkData, numberId: passedBytes / SwarmChunk.DataSize, - prevChunkSemaphore: prevChunkHashedSemaphore); + prevChunkSemaphore: prevChunkSemaphore); //run task nextStageTasks.Add( @@ -141,7 +141,7 @@ public async Task HashDataAsync(Stream dataStream) } finally { - //release and restore chunk hashed semaphore in pool + //release and restore chunk semaphore in pool chunkSemaphore.Release(); chunkSemaphorePool.Enqueue(chunkSemaphore); @@ -150,8 +150,8 @@ public async Task HashDataAsync(Stream dataStream) } })); - //set current chunk hashed semaphore as prev for next chunk - prevChunkHashedSemaphore = chunkSemaphore; + //set current chunk semaphore as prev for next chunk + prevChunkSemaphore = chunkSemaphore; passedBytes += chunkReadSize; } From b6f080bb51ac8ce9deb40809bd2f406a270cfadc Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 22 Jul 2024 01:57:30 +0200 Subject: [PATCH 39/46] output more buckets metrics --- src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs | 6 ++++++ src/BeeNet.Core/Models/PostageBuckets.cs | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs index 6ae7d9c..3c608ca 100644 --- a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs +++ b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs @@ -35,6 +35,12 @@ public interface IReadOnlyPostageBuckets 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 /// diff --git a/src/BeeNet.Core/Models/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs index 6d368f7..c8ed451 100644 --- a/src/BeeNet.Core/Models/PostageBuckets.cs +++ b/src/BeeNet.Core/Models/PostageBuckets.cs @@ -80,6 +80,19 @@ protected virtual void Dispose(bool disposing) 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(); From ef1f89c732b91dada0e933595cb3cb9401554b31 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 22 Jul 2024 03:01:37 +0200 Subject: [PATCH 40/46] expose missedOptimisticHashing metric and pipeline refactoring --- .../Pipeline/ChunkAggregatorPipelineStage.cs | 22 ++++++++++----- .../Hashing/Pipeline/ChunkBmtPipelineStage.cs | 14 ++++++++-- .../Pipeline/ChunkFeederPipelineStage.cs | 2 ++ .../Pipeline/ChunkStoreWriterPipelineStage.cs | 3 ++ .../Hashing/Pipeline/HasherPipelineBuilder.cs | 28 +++++-------------- .../Hashing/Pipeline/IHasherPipeline.cs | 4 +++ .../Hashing/Pipeline/IHasherPipelineStage.cs | 4 +++ src/BeeNet.Util/Services/CalculatorService.cs | 5 ++++ .../Services/UploadEvaluationResult.cs | 4 +++ 9 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs index e3bc070..9f73724 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkAggregatorPipelineStage.cs @@ -13,7 +13,6 @@ // If not, see . using Etherna.BeeNet.Hashing.Bmt; -using Etherna.BeeNet.Manifest; using Etherna.BeeNet.Models; using System; using System.Collections.Generic; @@ -23,8 +22,6 @@ namespace Etherna.BeeNet.Hashing.Pipeline { - internal delegate Task HashChunkDelegateAsync(byte[] span, byte[] data); - internal sealed class ChunkAggregatorPipelineStage : IHasherPipelineStage { // Private classes. @@ -44,22 +41,22 @@ private sealed class ChunkHeader( 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 = (byte)(useRecursiveEncryption ? SwarmChunkBmt.SegmentsCount / 2 //write chunk key after chunk hash : SwarmChunkBmt.SegmentsCount); + this.shortBmtPipelineStage = shortBmtPipelineStage; this.useRecursiveEncryption = useRecursiveEncryption; } @@ -69,6 +66,9 @@ public void Dispose() feedChunkMutex.Dispose(); } + // Properties. + public long MissedOptimisticHashing => shortBmtPipelineStage.MissedOptimisticHashing; + // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) { @@ -171,7 +171,7 @@ private async Task WrapFullLevelAsync(int level) .ToArray(); // Run hashing on the new chunk, and add it to next level. - var hashingResult = await hashChunkDelegate(totalSpan, totalData).ConfigureAwait(false); + var hashingResult = await HashIntermediateChunkAsync(totalSpan, totalData).ConfigureAwait(false); await AddChunkToLevelAsync( level + 1, new ChunkHeader( @@ -182,5 +182,13 @@ await AddChunkToLevelAsync( 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 index 92eb1f6..f85897b 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkBmtPipelineStage.cs @@ -32,6 +32,8 @@ internal sealed class ChunkBmtPipelineStage : IHasherPipelineStage private readonly IHasherPipelineStage nextStage; private readonly IPostageStampIssuer stampIssuer; + private long _missedOptimisticHashing; + // Constructor. /// /// Calculate hash of each chunk @@ -51,6 +53,10 @@ public void Dispose() { nextStage.Dispose(); } + + // Properties. + public long MissedOptimisticHashing => + _missedOptimisticHashing + nextStage.MissedOptimisticHashing; // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) @@ -135,14 +141,18 @@ private async Task GetBestChunkAsync( { // 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 if it has been invalidated, do it again. + // 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]; } diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index d0b5427..618609f 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -74,6 +74,8 @@ public void Dispose() // Properties. public bool IsUsable { get; private set; } = true; + public long MissedOptimisticHashing => nextStage.MissedOptimisticHashing; + // Methods. public async Task HashDataAsync(byte[] data) { diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs index a98d11e..edeadb8 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkStoreWriterPipelineStage.cs @@ -31,6 +31,9 @@ public void Dispose() { nextStage?.Dispose(); } + + // Properties. + public long MissedOptimisticHashing => nextStage?.MissedOptimisticHashing ?? 0; // Methods. public async Task FeedAsync(HasherPipelineFeedArgs args) diff --git a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs index 0b309f6..73e4a49 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/HasherPipelineBuilder.cs @@ -60,33 +60,19 @@ public static IHasherPipeline BuildNewHasherPipeline( else { //build stages - var shortPipelineStage = BuildNewShortHasherPipeline(chunkStore, postageStamper, compactLevel); - - var useRecursiveEncryption = compactLevel > 0; var chunkAggregatorStage = new ChunkAggregatorPipelineStage( - async (span, data) => - { - var args = new HasherPipelineFeedArgs(span: span, data: data); - await shortPipelineStage.FeedAsync(args).ConfigureAwait(false); - return new(args.Hash!.Value, args.ChunkKey, useRecursiveEncryption); - }, - useRecursiveEncryption); + new ChunkBmtPipelineStage( + compactLevel, + new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null), + postageStamper.StampIssuer), + compactLevel > 0); + var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, chunkAggregatorStage); + bmtStage = new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); } return new ChunkFeederPipelineStage(bmtStage, chunkConcurrency); } - - public static IHasherPipelineStage BuildNewShortHasherPipeline( - IChunkStore chunkStore, - IPostageStamper postageStamper, - ushort compactLevel) - { - ArgumentNullException.ThrowIfNull(postageStamper, nameof(postageStamper)); - - var storeWriterStage = new ChunkStoreWriterPipelineStage(chunkStore, postageStamper, null); - return new ChunkBmtPipelineStage(compactLevel, storeWriterStage, postageStamper.StampIssuer); - } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs index 4ccd0eb..40cf2a6 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipeline.cs @@ -21,8 +21,12 @@ 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 /// diff --git a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs index ce3c09c..d25e038 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/IHasherPipelineStage.cs @@ -20,6 +20,10 @@ namespace Etherna.BeeNet.Hashing.Pipeline { public interface IHasherPipelineStage : IDisposable { + // Properties. + long MissedOptimisticHashing { get; } + + // Methods. Task FeedAsync(HasherPipelineFeedArgs args); Task SumAsync(); } diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index 5b0362d..cf734b6 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -53,6 +53,8 @@ public async Task EvaluateDirectoryUploadAsync( new FakeSigner(), postageStampIssuer, new MemoryStampStore()); + + long totalMissedOptimisticHashing = 0; // Try set index document. if (indexFilename is null && @@ -90,6 +92,7 @@ public async Task EvaluateDirectoryUploadAsync( using var fileStream = File.OpenRead(file); var fileHashingResult = await fileHasherPipeline.HashDataAsync(fileStream).ConfigureAwait(false); + totalMissedOptimisticHashing += fileHasherPipeline.MissedOptimisticHashing; // Add file entry to dir manifest. var fileEntryMetadata = new Dictionary @@ -130,6 +133,7 @@ public async Task EvaluateDirectoryUploadAsync( // Return result. return new UploadEvaluationResult( chunkHashingResult.Hash, + totalMissedOptimisticHashing, postageStampIssuer); } @@ -227,6 +231,7 @@ public async Task EvaluateFileUploadAsync( // Return result. return new UploadEvaluationResult( chunkHashingResult.Hash, + fileHasherPipeline.MissedOptimisticHashing, postageStampIssuer); } diff --git a/src/BeeNet.Util/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs index 5c9e2a4..4dfc587 100644 --- a/src/BeeNet.Util/Services/UploadEvaluationResult.cs +++ b/src/BeeNet.Util/Services/UploadEvaluationResult.cs @@ -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; } From 02b889e18dd0acd5b1d831e5e5c5a1fe95b07f5c Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Mon, 22 Jul 2024 18:55:31 +0200 Subject: [PATCH 41/46] expose RequiredPostageBatchDepth from PostageBuckets --- .../Models/IReadOnlyPostageBuckets.cs | 5 +++++ src/BeeNet.Core/Models/PostageBuckets.cs | 21 +++++++++++++++++++ .../Services/UploadEvaluationResult.cs | 10 +-------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs index 3c608ca..054f667 100644 --- a/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs +++ b/src/BeeNet.Core/Models/IReadOnlyPostageBuckets.cs @@ -29,6 +29,11 @@ public interface IReadOnlyPostageBuckets /// uint MinBucketCollisions { get; } + /// + /// Minimum required postage batch depth + /// + int RequiredPostageBatchDepth { get; } + /// /// Total added chunks in buckets /// diff --git a/src/BeeNet.Core/Models/PostageBuckets.cs b/src/BeeNet.Core/Models/PostageBuckets.cs index c8ed451..5e5f3d3 100644 --- a/src/BeeNet.Core/Models/PostageBuckets.cs +++ b/src/BeeNet.Core/Models/PostageBuckets.cs @@ -77,6 +77,7 @@ protected virtual void Dispose(bool disposing) // Properties. public uint MaxBucketCollisions { get; private set; } public uint MinBucketCollisions { get; private set; } + public int RequiredPostageBatchDepth => RequiredPostageBatchDepthFromCollisions(MaxBucketCollisions); public long TotalChunks { get; private set; } // Methods. @@ -191,5 +192,25 @@ public void ResetBucketCollisions(uint bucketId) bucketsLock.ExitWriteLock(); } } + + // Static methods. + 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); + } + + public static int RequiredPostageBatchDepthFromCollisions(uint maxBucketCollisions) + { + if (maxBucketCollisions == 0) + return PostageBatch.MinDepth; + return Math.Max( + (int)Math.Ceiling(Math.Log2(maxBucketCollisions)) + PostageBatch.BucketDepth, + PostageBatch.MinDepth); + } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Services/UploadEvaluationResult.cs b/src/BeeNet.Util/Services/UploadEvaluationResult.cs index 4dfc587..715f41c 100644 --- a/src/BeeNet.Util/Services/UploadEvaluationResult.cs +++ b/src/BeeNet.Util/Services/UploadEvaluationResult.cs @@ -53,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.Buckets.MaxBucketCollisions)) + 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 From 5556bbfd9d81538b42f1244897db3394dee6465e Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Wed, 24 Jul 2024 17:24:28 +0200 Subject: [PATCH 42/46] better explanation on choose of semaphores amount --- .../Hashing/Pipeline/ChunkFeederPipelineStage.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs index d26342b..c498ab7 100644 --- a/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs +++ b/src/BeeNet.Util/Hashing/Pipeline/ChunkFeederPipelineStage.cs @@ -54,6 +54,18 @@ public ChunkFeederPipelineStage( * 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)); @@ -103,6 +115,8 @@ public async Task HashDataAsync(Stream dataStream) 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)); @@ -127,7 +141,7 @@ public async Task HashDataAsync(Stream dataStream) var feedArgs = new HasherPipelineFeedArgs( span: chunkData[..SwarmChunk.SpanSize], data: chunkData, - numberId: passedBytes / SwarmChunk.DataSize, + numberId: chunkNumberId, prevChunkSemaphore: prevChunkSemaphore); //run task From ee2485567ab870ef64192779022b96d14ae85bf4 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Wed, 24 Jul 2024 17:38:59 +0200 Subject: [PATCH 43/46] update dependencies --- src/BeeNet.Core/BeeNet.Core.csproj | 2 +- src/BeeNet.Util/BeeNet.Util.csproj | 2 +- .../BeeNet.Client.IntegrationTest.csproj | 4 ++-- test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj | 4 ++-- test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BeeNet.Core/BeeNet.Core.csproj b/src/BeeNet.Core/BeeNet.Core.csproj index 55fdde8..00be88a 100644 --- a/src/BeeNet.Core/BeeNet.Core.csproj +++ b/src/BeeNet.Core/BeeNet.Core.csproj @@ -36,7 +36,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/BeeNet.Util/BeeNet.Util.csproj b/src/BeeNet.Util/BeeNet.Util.csproj index ce0263e..32a92dc 100644 --- a/src/BeeNet.Util/BeeNet.Util.csproj +++ b/src/BeeNet.Util/BeeNet.Util.csproj @@ -37,7 +37,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj index 68904aa..c8e8581 100644 --- a/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj +++ b/test/BeeNet.Client.IntegrationTest/BeeNet.Client.IntegrationTest.csproj @@ -8,8 +8,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj b/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj index 7eb4022..f343249 100644 --- a/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj +++ b/test/BeeNet.Core.UnitTest/BeeNet.Core.UnitTest.csproj @@ -12,8 +12,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj index ce2509f..bd9f0ab 100644 --- a/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj +++ b/test/BeeNet.Util.UnitTest/BeeNet.Util.UnitTest.csproj @@ -11,8 +11,8 @@ - - + + all runtime; build; native; contentfiles; analyzers From 18594a73193dc92083a2070255a6c4c620df094b Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Wed, 24 Jul 2024 19:09:35 +0200 Subject: [PATCH 44/46] fix big file reading --- src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs | 75 ++++++++++++++++--- src/BeeNet.Util/Services/CalculatorService.cs | 16 ++-- .../Services/ICalculatorService.cs | 7 +- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs index 9c7d387..82ddcfb 100644 --- a/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs +++ b/src/BeeNet.Util/Hashing/Store/ChunkJoiner.cs @@ -14,7 +14,8 @@ using Etherna.BeeNet.Models; using System; -using System.Collections.Generic; +using System.IO; +using System.Threading; using System.Threading.Tasks; namespace Etherna.BeeNet.Hashing.Store @@ -23,20 +24,70 @@ public class ChunkJoiner( IReadOnlyChunkStore chunkStore) { // Methods. - public async Task> GetJoinedChunkDataAsync(SwarmChunkReference chunkReference) + /// + /// 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(chunkReference, nameof(chunkReference)); - + 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) - return dataArray; + { + await dataStream.WriteAsync(dataArray, cancellationToken).ConfigureAwait(false); + return; + } - var joinedData = new List(); + //else, is intermediate chunk for (int i = 0; i < dataArray.Length;) { //read hash @@ -50,16 +101,16 @@ public async Task> GetJoinedChunkDataAsync(SwarmChunkReference childEncryptionKey = new XorEncryptKey(dataArray[i..(i + XorEncryptKey.KeySize)]); i += XorEncryptKey.KeySize; } - + //add joined data recursively - joinedData.AddRange(await GetJoinedChunkDataAsync( + await GetJoinedChunkDataHelperAsync( new SwarmChunkReference( childHash, childEncryptionKey, - chunkReference.UseRecursiveEncryption)).ConfigureAwait(false)); + chunkReference.UseRecursiveEncryption), + dataStream, + cancellationToken).ConfigureAwait(false); } - - return joinedData; } } } \ No newline at end of file diff --git a/src/BeeNet.Util/Services/CalculatorService.cs b/src/BeeNet.Util/Services/CalculatorService.cs index cf734b6..9d4f6e8 100644 --- a/src/BeeNet.Util/Services/CalculatorService.cs +++ b/src/BeeNet.Util/Services/CalculatorService.cs @@ -21,7 +21,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Etherna.BeeNet.Services @@ -250,7 +250,9 @@ public async Task> GetFileMetadataFromChunks 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); @@ -261,12 +263,10 @@ public async Task GetFileStreamFromChunksAsync( var chunkReference = await rootManifest.ResolveAddressToChunkReferenceAsync(address).ConfigureAwait(false); - var memoryStream = new MemoryStream(); - var resourceData = await chunkJoiner.GetJoinedChunkDataAsync(chunkReference).ConfigureAwait(false); - memoryStream.Write(resourceData.ToArray()); - memoryStream.Position = 0; - - return memoryStream; + return await chunkJoiner.GetJoinedChunkDataAsync( + chunkReference, + fileCachePath, + cancellationToken).ConfigureAwait(false); } public Task WriteDataChunksAsync( diff --git a/src/BeeNet.Util/Services/ICalculatorService.cs b/src/BeeNet.Util/Services/ICalculatorService.cs index 1ccd757..17216da 100644 --- a/src/BeeNet.Util/Services/ICalculatorService.cs +++ b/src/BeeNet.Util/Services/ICalculatorService.cs @@ -17,6 +17,7 @@ using Etherna.BeeNet.Models; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; namespace Etherna.BeeNet.Services @@ -110,10 +111,14 @@ Task> GetFileMetadataFromChunksAsync( /// /// The chunk directory /// Resource address + /// Optional file where store read data. Necessary if data is >2GB + /// Optional cancellation token /// Resource stream Task GetFileStreamFromChunksAsync( string chunkStoreDirectory, - SwarmAddress address); + SwarmAddress address, + string? fileCachePath = null, + CancellationToken? cancellationToken = default); /// /// Write data chunks on a local directory, without any manifest From 1b2efa3b21a31d99b96acf01a2370e184aba2791 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Fri, 26 Jul 2024 18:01:15 +0200 Subject: [PATCH 45/46] first experimental wasm project --- BeeNet.sln | 7 ++++ src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj | 30 +++++++++++++++++ src/BeeNet.Util.Wasm/Program.cs | 29 ++++++++++++++++ .../Properties/AssemblyInfo.cs | 4 +++ .../Properties/launchSettings.json | 13 ++++++++ .../runtimeconfig.template.json | 10 ++++++ src/BeeNet.Util.Wasm/wwwroot/index.html | 17 ++++++++++ src/BeeNet.Util.Wasm/wwwroot/main.js | 33 +++++++++++++++++++ 8 files changed, 143 insertions(+) create mode 100644 src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj create mode 100644 src/BeeNet.Util.Wasm/Program.cs create mode 100644 src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs create mode 100644 src/BeeNet.Util.Wasm/Properties/launchSettings.json create mode 100644 src/BeeNet.Util.Wasm/runtimeconfig.template.json create mode 100644 src/BeeNet.Util.Wasm/wwwroot/index.html create mode 100644 src/BeeNet.Util.Wasm/wwwroot/main.js diff --git a/BeeNet.sln b/BeeNet.sln index e6ffb94..ac75d7b 100644 --- a/BeeNet.sln +++ b/BeeNet.sln @@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeeNet.Core.UnitTest", "tes 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("{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 Debug|Any CPU = Debug|Any CPU @@ -76,6 +78,10 @@ Global {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 @@ -90,6 +96,7 @@ Global {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/src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj b/src/BeeNet.Util.Wasm/BeeNet.Util.Wasm.csproj new file mode 100644 index 0000000..68146d5 --- /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 0000000..70d3c3b --- /dev/null +++ b/src/BeeNet.Util.Wasm/Program.cs @@ -0,0 +1,29 @@ +using Etherna.BeeNet.Services; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; +using System.Threading.Tasks; + +namespace Etherna.BeeNet.Wasm +{ + [SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable")] + [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] + public partial class BeeNetWasmUtil + { + public static void Main() { } + + [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(); + } + } +} \ No newline at end of file diff --git a/src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs b/src/BeeNet.Util.Wasm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2b51b75 --- /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 0000000..bed8bdc --- /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 0000000..b96a943 --- /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 0000000..04ac67c --- /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 0000000..e45548a --- /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 From 07c86087aa7d996240e638d6ecd6c3ee8d8252e4 Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Fri, 26 Jul 2024 18:22:45 +0200 Subject: [PATCH 46/46] fix --- src/BeeNet.Util.Wasm/Program.cs | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/BeeNet.Util.Wasm/Program.cs b/src/BeeNet.Util.Wasm/Program.cs index 70d3c3b..a1c6435 100644 --- a/src/BeeNet.Util.Wasm/Program.cs +++ b/src/BeeNet.Util.Wasm/Program.cs @@ -1,29 +1,29 @@ using Etherna.BeeNet.Services; +using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -namespace Etherna.BeeNet.Wasm +#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 { - [SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable")] - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] - public partial class BeeNetWasmUtil + [JSExport] + internal static async Task GetHashStringAsync( + byte[] data, + string fileContentType, + string fileName) { - public static void Main() { } - - [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(); - } + var calculatorService = new CalculatorService(); + var result = await calculatorService.EvaluateFileUploadAsync( + data, + fileContentType, + fileName).ConfigureAwait(false); + return result.Hash.ToString(); } -} \ No newline at end of file +}