span and memory
This commit is contained in:
parent
423b5a35b0
commit
cf1fae8d2c
29 changed files with 193 additions and 165 deletions
|
@ -175,7 +175,7 @@ public class FrostFSClient : IFrostFSClient
|
||||||
}
|
}
|
||||||
|
|
||||||
#region ApeManagerImplementation
|
#region ApeManagerImplementation
|
||||||
public Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
public Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,7 +21,7 @@ public interface IFrostFSClient : IDisposable
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ApeManager
|
#region ApeManager
|
||||||
Task<byte[]> AddChainAsync(PrmApeChainAdd args);
|
Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args);
|
||||||
|
|
||||||
Task RemoveChainAsync(PrmApeChainRemove args);
|
Task RemoveChainAsync(PrmApeChainRemove args);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,6 @@ public static class ContainerIdMapper
|
||||||
throw new ArgumentNullException(nameof(message));
|
throw new ArgumentNullException(nameof(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FrostFsContainerId(Base58.Encode(message.Value.ToByteArray()));
|
return new FrostFsContainerId(Base58.Encode(message.Value.Span));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,7 +39,7 @@ public static class NodeInfoMapper
|
||||||
state: state,
|
state: state,
|
||||||
addresses: [.. nodeInfo.Addresses],
|
addresses: [.. nodeInfo.Addresses],
|
||||||
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
||||||
publicKey: nodeInfo.PublicKey.ToByteArray()
|
publicKey: nodeInfo.PublicKey.Memory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ internal static class ObjectMapper
|
||||||
{
|
{
|
||||||
return new FrostFsObject(obj.Header.ToModel())
|
return new FrostFsObject(obj.Header.ToModel())
|
||||||
{
|
{
|
||||||
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray())
|
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public static class ObjectHeaderMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = new FrostFsObjectHeader(
|
var model = new FrostFsObjectHeader(
|
||||||
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())),
|
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)),
|
||||||
objTypeName,
|
objTypeName,
|
||||||
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
||||||
split,
|
split,
|
||||||
|
|
|
@ -28,6 +28,6 @@ public static class ObjectIdMapper
|
||||||
throw new ArgumentNullException(nameof(objectId));
|
throw new ArgumentNullException(nameof(objectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(objectId.Value.ToByteArray());
|
return FrostFsObjectId.FromHash(objectId.Value.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,7 +44,7 @@ public static class OwnerIdMapper
|
||||||
|
|
||||||
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
||||||
{
|
{
|
||||||
model = new FrostFsOwner(Base58.Encode(message.Value.ToByteArray()));
|
model = new FrostFsOwner(Base58.Encode(message.Value.Span));
|
||||||
|
|
||||||
Caches.Owners.Set(message, model, _oneHourExpiration);
|
Caches.Owners.Set(message, model, _oneHourExpiration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,12 @@ public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatab
|
||||||
return $"{Name}{Type}".GetHashCode(StringComparison.InvariantCulture);
|
return $"{Name}{Type}".GetHashCode(StringComparison.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator == (FrostFsChainTarget left, FrostFsChainTarget right)
|
public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right)
|
||||||
{
|
{
|
||||||
return left.Equals(right);
|
return left.Equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator != (FrostFsChainTarget left, FrostFsChainTarget right)
|
public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right)
|
||||||
{
|
{
|
||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class FrostFsContainerId
|
||||||
|
|
||||||
if (containerID != null)
|
if (containerID != null)
|
||||||
{
|
{
|
||||||
this.modelId = Base58.Encode(containerID.Value.ToByteArray());
|
this.modelId = Base58.Encode(containerID.Value.Span);
|
||||||
return this.modelId;
|
return this.modelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,8 @@ public class FrostFsObjectId(string id)
|
||||||
{
|
{
|
||||||
public string Value { get; } = id;
|
public string Value { get; } = id;
|
||||||
|
|
||||||
public static FrostFsObjectId FromHash(byte[] hash)
|
public static FrostFsObjectId FromHash(ReadOnlySpan<byte> hash)
|
||||||
{
|
{
|
||||||
if (hash is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash.Length != Constants.Sha256HashLength)
|
if (hash.Length != Constants.Sha256HashLength)
|
||||||
throw new FormatException("ObjectID must be a sha256 hash.");
|
throw new FormatException("ObjectID must be a sha256 hash.");
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class ClientWrapper : ClientStatusMonitor
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
client = new(WrapperPrm, sessionCache);
|
client = new(WrapperPrm, sessionCache);
|
||||||
|
|
||||||
//TODO: set additioanl params
|
//TODO: set additioanl params
|
||||||
var error = await client.Dial(ctx).ConfigureAwait(false);
|
var error = await client.Dial(ctx).ConfigureAwait(false);
|
||||||
if (!string.IsNullOrEmpty(error))
|
if (!string.IsNullOrEmpty(error))
|
||||||
|
|
|
@ -560,7 +560,7 @@ public partial class Pool : IFrostFSClient
|
||||||
return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false);
|
return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
public async Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
if (args is null)
|
if (args is null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
_apeManagerServiceClient = apeManagerServiceClient;
|
_apeManagerServiceClient = apeManagerServiceClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<byte[]> AddChainAsync(PrmApeChainAdd args)
|
internal async Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args)
|
||||||
{
|
{
|
||||||
var ctx = args.Context!;
|
var ctx = args.Context!;
|
||||||
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
ctx.Key ??= ClientContext.Key?.ECDsaKey;
|
||||||
|
@ -40,7 +40,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor
|
||||||
|
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return response.Body.ChainId.ToByteArray();
|
return response.Body.ChainId.Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RemoveChainAsync(PrmApeChainRemove args)
|
internal async Task RemoveChainAsync(PrmApeChainRemove args)
|
||||||
|
|
|
@ -68,7 +68,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService
|
||||||
|
|
||||||
foreach (var cid in response.Body.ContainerIds)
|
foreach (var cid in response.Body.ContainerIds)
|
||||||
{
|
{
|
||||||
yield return new FrostFsContainerId(Base58.Encode(cid.Value.ToByteArray()));
|
yield return new FrostFsContainerId(Base58.Encode(cid.Value.Span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
|
@ -107,12 +105,16 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
return response.ToModel();
|
return response.ToModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetBoolValue(byte[] bytes)
|
private static bool GetBoolValue(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
return bytes.Any(b => b != 0);
|
for (int i = bytes.Length - 1; i >= 0; i--)
|
||||||
|
if (bytes[i] != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong GetLongValue(byte[] bytes)
|
private static ulong GetLongValue(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
ulong val = 0;
|
ulong val = 0;
|
||||||
for (var i = bytes.Length - 1; i >= 0; i--)
|
for (var i = bytes.Length - 1; i >= 0; i--)
|
||||||
|
@ -123,24 +125,50 @@ internal sealed class NetmapServiceProvider : ContextAccessor
|
||||||
|
|
||||||
private static void SetNetworksParam(Parameter param, NetworkSettings settings)
|
private static void SetNetworksParam(Parameter param, NetworkSettings settings)
|
||||||
{
|
{
|
||||||
var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
|
var key = param.Key.ToStringUtf8();
|
||||||
|
|
||||||
var valueBytes = param.Value.ToByteArray();
|
var valueBytes = param.Value.Span;
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case "AuditFee": settings.AuditFee = GetLongValue(valueBytes); break;
|
case "AuditFee":
|
||||||
case "BasicIncomeRate": settings.BasicIncomeRate = GetLongValue(valueBytes); break;
|
settings.AuditFee = GetLongValue(valueBytes);
|
||||||
case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "ContainerAliasFee": settings.ContainerAliasFee = GetLongValue(valueBytes); break;
|
case "BasicIncomeRate":
|
||||||
case "EpochDuration": settings.EpochDuration = GetLongValue(valueBytes); break;
|
settings.BasicIncomeRate = GetLongValue(valueBytes);
|
||||||
case "InnerRingCandidateFee": settings.InnerRingCandidateFee = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "MaxECDataCount": settings.MaxECDataCount = GetLongValue(valueBytes); break;
|
case "ContainerFee":
|
||||||
case "MaxECParityCount": settings.MaxECParityCount = GetLongValue(valueBytes); break;
|
settings.ContainerFee = GetLongValue(valueBytes);
|
||||||
case "MaxObjectSize": settings.MaxObjectSize = GetLongValue(valueBytes); break;
|
break;
|
||||||
case "WithdrawFee": settings.WithdrawFee = GetLongValue(valueBytes); break;
|
case "ContainerAliasFee":
|
||||||
case "HomomorphicHashingDisabled": settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); break;
|
settings.ContainerAliasFee = GetLongValue(valueBytes);
|
||||||
case "MaintenanceModeAllowed": settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); break;
|
break;
|
||||||
default: settings.UnnamedSettings.Add(key, valueBytes); break;
|
case "EpochDuration":
|
||||||
|
settings.EpochDuration = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "InnerRingCandidateFee":
|
||||||
|
settings.InnerRingCandidateFee = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxECDataCount":
|
||||||
|
settings.MaxECDataCount = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxECParityCount":
|
||||||
|
settings.MaxECParityCount = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaxObjectSize":
|
||||||
|
settings.MaxObjectSize = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "WithdrawFee":
|
||||||
|
settings.WithdrawFee = GetLongValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "HomomorphicHashingDisabled":
|
||||||
|
settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes);
|
||||||
|
break;
|
||||||
|
case "MaintenanceModeAllowed":
|
||||||
|
settings.MaintenanceModeAllowed = GetBoolValue(valueBytes);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
settings.UnnamedSettings.Add(key, valueBytes.ToArray());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,7 +271,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
|
|
||||||
await foreach (var oid in objectsIds)
|
await foreach (var oid in objectsIds)
|
||||||
{
|
{
|
||||||
yield return FrostFsObjectId.FromHash(oid.Value.ToByteArray());
|
yield return FrostFsObjectId.FromHash(oid.Value.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
|
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray());
|
return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.Span);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
internal async Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args)
|
||||||
|
@ -589,7 +589,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl
|
||||||
var response = await stream.Close().ConfigureAwait(false);
|
var response = await stream.Close().ConfigureAwait(false);
|
||||||
Verifier.CheckResponse(response);
|
Verifier.CheckResponse(response);
|
||||||
|
|
||||||
return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes);
|
return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.Span), sentBytes);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,7 +47,7 @@ internal static class ObjectTools
|
||||||
obj.Signature = new Signature
|
obj.Signature = new Signature
|
||||||
{
|
{
|
||||||
Key = ctx.GetPublicKeyCache(),
|
Key = ctx.GetPublicKeyCache(),
|
||||||
Sign = ByteString.CopyFrom(ctx.Key!.SignData(obj.ObjectId.ToByteArray())),
|
Sign = ctx.Key!.SignData(obj.ObjectId.ToByteArray()),
|
||||||
};
|
};
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -78,7 +78,7 @@ internal static class ObjectTools
|
||||||
grpcHeader.Split.ParentSignature = new Signature
|
grpcHeader.Split.ParentSignature = new Signature
|
||||||
{
|
{
|
||||||
Key = ctx.GetPublicKeyCache(),
|
Key = ctx.GetPublicKeyCache(),
|
||||||
Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())),
|
Sign = ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,18 +20,13 @@ public static class RequestSigner
|
||||||
{
|
{
|
||||||
internal const int RFC6979SignatureSize = 64;
|
internal const int RFC6979SignatureSize = 64;
|
||||||
|
|
||||||
internal static byte[] SignRFC6979(this ECDsa key, byte[] data)
|
internal static ByteString SignRFC6979(this ECDsa key, byte[] data)
|
||||||
{
|
{
|
||||||
if (key is null)
|
if (key is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
var digest = new Sha256Digest();
|
var digest = new Sha256Digest();
|
||||||
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
||||||
|
@ -44,16 +39,18 @@ public static class RequestSigner
|
||||||
signer.Init(true, privateKey);
|
signer.Init(true, privateKey);
|
||||||
|
|
||||||
var rs = signer.GenerateSignature(hash);
|
var rs = signer.GenerateSignature(hash);
|
||||||
var signature = new byte[RFC6979SignatureSize];
|
|
||||||
|
Span<byte> signature = stackalloc byte[RFC6979SignatureSize];
|
||||||
|
|
||||||
var rbytes = rs[0].ToByteArrayUnsigned();
|
var rbytes = rs[0].ToByteArrayUnsigned();
|
||||||
var sbytes = rs[1].ToByteArrayUnsigned();
|
var sbytes = rs[1].ToByteArrayUnsigned();
|
||||||
var index = RFC6979SignatureSize / 2 - rbytes.Length;
|
var index = RFC6979SignatureSize / 2 - rbytes.Length;
|
||||||
|
|
||||||
rbytes.CopyTo(signature, index);
|
rbytes.AsSpan().CopyTo(signature[index..]);
|
||||||
index = RFC6979SignatureSize - sbytes.Length;
|
index = RFC6979SignatureSize - sbytes.Length;
|
||||||
sbytes.CopyTo(signature, index);
|
sbytes.AsSpan().CopyTo(signature[index..]);
|
||||||
|
|
||||||
return signature;
|
return ByteString.CopyFrom(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message)
|
internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message)
|
||||||
|
@ -61,7 +58,7 @@ public static class RequestSigner
|
||||||
return new SignatureRFC6979
|
return new SignatureRFC6979
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignRFC6979(message.ToByteArray())),
|
Sign = key.SignRFC6979(message.ToByteArray())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,23 +67,29 @@ public static class RequestSigner
|
||||||
return new SignatureRFC6979
|
return new SignatureRFC6979
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignRFC6979(data.ToByteArray())),
|
Sign = key.SignRFC6979(data.ToByteArray()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] SignData(this ECDsa key, byte[] data)
|
public static ByteString SignData(this ECDsa key, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
if (key is null)
|
if (key is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = new byte[65];
|
Span<byte> dataHash = stackalloc byte[64];
|
||||||
hash[0] = 0x04;
|
data.Sha512(ref dataHash);
|
||||||
|
|
||||||
key.SignHash(data.Sha512()).CopyTo(hash, 1);
|
Span<byte> hash = stackalloc byte[64];
|
||||||
|
key.TrySignHash(dataHash, hash, out _);
|
||||||
|
|
||||||
return hash;
|
Span<byte> result = stackalloc byte[65];
|
||||||
|
result[0] = 0x04;
|
||||||
|
|
||||||
|
hash.CopyTo(result[1..]);
|
||||||
|
|
||||||
|
return ByteString.CopyFrom(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
internal static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
||||||
|
@ -95,12 +98,11 @@ public static class RequestSigner
|
||||||
var sig = new Signature
|
var sig = new Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(key.PublicKey()),
|
Key = ByteString.CopyFrom(key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(key.SignData(data2Sign)),
|
Sign = key.SignData(data2Sign),
|
||||||
};
|
};
|
||||||
|
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Sign(this IVerifiableMessage message, ECDsa key)
|
internal static void Sign(this IVerifiableMessage message, ECDsa key)
|
||||||
{
|
{
|
||||||
var meta = message.GetMetaHeader();
|
var meta = message.GetMetaHeader();
|
||||||
|
|
|
@ -33,11 +33,14 @@ public static class Verifier
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VerifyRFC6979(this byte[] publicKey, byte[] data, byte[] sig)
|
public static bool VerifyRFC6979(this byte[] publicKey, IMessage message, ByteString signature)
|
||||||
{
|
{
|
||||||
if (publicKey is null || data is null || sig is null)
|
if (publicKey is null || message is null || signature is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
byte[] sig = signature.ToByteArray();
|
||||||
|
byte[] data = message.ToByteArray();
|
||||||
|
|
||||||
var rs = DecodeSignature(sig);
|
var rs = DecodeSignature(sig);
|
||||||
var digest = new Sha256Digest();
|
var digest = new Sha256Digest();
|
||||||
var signer = new ECDsaSigner(new HMacDsaKCalculator(digest));
|
var signer = new ECDsaSigner(new HMacDsaKCalculator(digest));
|
||||||
|
@ -60,21 +63,22 @@ public static class Verifier
|
||||||
throw new ArgumentNullException(nameof(signature));
|
throw new ArgumentNullException(nameof(signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray());
|
return signature.Key.ToByteArray().VerifyRFC6979(message, signature.Sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VerifyData(this ECDsa key, byte[] data, byte[] sig)
|
public static bool VerifyData(this ECDsa key, ReadOnlySpan<byte> data, ByteString sig)
|
||||||
{
|
{
|
||||||
if (key is null)
|
if (key is null)
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
if (data is null)
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
|
||||||
|
|
||||||
if (sig is null)
|
if (sig is null)
|
||||||
throw new ArgumentNullException(nameof(sig));
|
throw new ArgumentNullException(nameof(sig));
|
||||||
|
|
||||||
return key.VerifyHash(data.Sha512(), sig[1..]);
|
Span<byte> hash = stackalloc byte[64];
|
||||||
|
|
||||||
|
data.Sha512(ref hash);
|
||||||
|
|
||||||
|
return key.VerifyHash(hash, sig.Span[1..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VerifyMessagePart(this Signature sig, IMessage data)
|
public static bool VerifyMessagePart(this Signature sig, IMessage data)
|
||||||
|
@ -85,7 +89,7 @@ public static class Verifier
|
||||||
using var key = sig.Key.ToByteArray().LoadPublicKey();
|
using var key = sig.Key.ToByteArray().LoadPublicKey();
|
||||||
var data2Verify = data is null ? [] : data.ToByteArray();
|
var data2Verify = data is null ? [] : data.ToByteArray();
|
||||||
|
|
||||||
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
|
return key.VerifyData(data2Verify, sig.Sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -46,14 +47,14 @@ public static class Extentions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] Sha512(this byte[] value)
|
public static void Sha512(this ReadOnlySpan<byte> value, ref Span<byte> hash)
|
||||||
{
|
{
|
||||||
bool lockTaken = false;
|
bool lockTaken = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_spinlockSha512.Enter(ref lockTaken);
|
_spinlockSha512.Enter(ref lockTaken);
|
||||||
|
|
||||||
return _sha512.ComputeHash(value);
|
_sha512.TryComputeHash(value, hash, out _);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,14 @@
|
||||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DefineConstants>$(DefineConstants);NETSTANDARD2_1_OR_GREATER</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DefineConstants>$(DefineConstants);NETSTANDARD2_1_OR_GREATER</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
|
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.28.3" />
|
<PackageReference Include="Google.Protobuf" Version="3.28.3" />
|
||||||
|
|
|
@ -11,9 +11,7 @@ public static class UUIDExtension
|
||||||
if (id == null)
|
if (id == null)
|
||||||
throw new ArgumentNullException(nameof(id));
|
throw new ArgumentNullException(nameof(id));
|
||||||
|
|
||||||
var bytes = id.ToByteArray();
|
var orderedBytes = GetGuidBytesDirectOrder(id.Span);
|
||||||
|
|
||||||
var orderedBytes = GetGuidBytesDirectOrder(bytes);
|
|
||||||
|
|
||||||
return new Guid(orderedBytes);
|
return new Guid(orderedBytes);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +30,7 @@ public static class UUIDExtension
|
||||||
return orderedBytes;
|
return orderedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] GetGuidBytesDirectOrder(byte[] source)
|
private static byte[] GetGuidBytesDirectOrder(ReadOnlySpan<byte> source)
|
||||||
{
|
{
|
||||||
if (source.Length != 16)
|
if (source.Length != 16)
|
||||||
throw new ArgumentException("Wrong uuid binary format");
|
throw new ArgumentException("Wrong uuid binary format");
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader)
|
||||||
Signature = new Refs.Signature
|
Signature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(header.ToByteArray())),
|
Sign = Key.SignData(header.ToByteArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,19 +46,19 @@ public abstract class ServiceBase(string key)
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray()))
|
Sign = Key.SignData(response.MetaHeader.ToByteArray())
|
||||||
},
|
},
|
||||||
BodySignature = new Refs.Signature
|
BodySignature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray()))
|
Sign = Key.SignData(response.GetBody().ToByteArray())
|
||||||
},
|
},
|
||||||
OriginSignature = new Refs.Signature
|
OriginSignature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
||||||
Sign = ByteString.CopyFrom(Key.SignData([]))
|
Sign = Key.SignData([])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ using FrostFS.Object;
|
||||||
using FrostFS.SDK.Client;
|
using FrostFS.SDK.Client;
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
using FrostFS.SDK.Client.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
using FrostFS.Session;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
|
|
||||||
public GetRangeHashRequest? GetRangeHashRequest { get; set; }
|
public GetRangeHashRequest? GetRangeHashRequest { get; set; }
|
||||||
|
|
||||||
public Collection<ByteString> RangeHashResponses { get; } = [];
|
public Collection<ByteString> RangeHashResponses { get; } = [];
|
||||||
|
|
||||||
public override Mock<ObjectService.ObjectServiceClient> GetMock()
|
public override Mock<ObjectService.ObjectServiceClient> GetMock()
|
||||||
{
|
{
|
||||||
|
@ -94,7 +93,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
headResponse.Body.Header.Signature = new Refs.Signature
|
headResponse.Body.Header.Signature = new Refs.Signature
|
||||||
{
|
{
|
||||||
Key = ByteString.CopyFrom(Key.PublicKey()),
|
Key = ByteString.CopyFrom(Key.PublicKey()),
|
||||||
Sign = ByteString.CopyFrom(Key.SignData(headResponse.Body.Header.ToByteArray())),
|
Sign = Key.SignData(headResponse.Body.Header.ToByteArray()),
|
||||||
};
|
};
|
||||||
|
|
||||||
headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse);
|
headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse);
|
||||||
|
@ -259,7 +258,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
response.Body.HashList.Add(hash);
|
response.Body.HashList.Add(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.VerifyHeader = GetResponseVerificationHeader(response);
|
response.VerifyHeader = GetResponseVerificationHeader(response);
|
||||||
|
|
||||||
return new AsyncUnaryCall<GetRangeHashResponse>(
|
return new AsyncUnaryCall<GetRangeHashResponse>(
|
||||||
|
@ -281,7 +280,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key)
|
||||||
{
|
{
|
||||||
Body = new PatchResponse.Types.Body
|
Body = new PatchResponse.Types.Body
|
||||||
{
|
{
|
||||||
ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1,2,3])) },
|
ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1, 2, 3])) },
|
||||||
},
|
},
|
||||||
MetaHeader = ResponseMetaHeader
|
MetaHeader = ResponseMetaHeader
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,8 +10,6 @@ using FrostFS.SDK.Cryptography;
|
||||||
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
using static FrostFS.Object.ECInfo.Types;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Tests;
|
namespace FrostFS.SDK.Tests;
|
||||||
|
|
||||||
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
|
||||||
|
@ -339,9 +337,9 @@ public class ObjectTest : ObjectTestsBase
|
||||||
|
|
||||||
Assert.Equal(address.ContainerId, body.Address.ContainerId);
|
Assert.Equal(address.ContainerId, body.Address.ContainerId);
|
||||||
Assert.Equal(address.ObjectId, body.Address.ObjectId);
|
Assert.Equal(address.ObjectId, body.Address.ObjectId);
|
||||||
|
|
||||||
Assert.Equal(32, body.Patch.Chunk.Length);
|
Assert.Equal(32, body.Patch.Chunk.Length);
|
||||||
|
|
||||||
Assert.Equal(SHA256.HashData(patch), SHA256.HashData(body.Patch.Chunk.ToArray()));
|
Assert.Equal(SHA256.HashData(patch), SHA256.HashData(body.Patch.Chunk.ToArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,83 +248,83 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
[InlineData(6 * 1024 * 1024 + 100)]
|
[InlineData(6 * 1024 * 1024 + 100)]
|
||||||
public async void SimpleScenarioTest(int objectSize)
|
public async void SimpleScenarioTest(int objectSize)
|
||||||
{
|
{
|
||||||
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
bool callbackInvoked = false;
|
bool callbackInvoked = false;
|
||||||
var ctx = new CallContext
|
var ctx = new CallContext
|
||||||
|
{
|
||||||
|
// Timeout = TimeSpan.FromSeconds(20),
|
||||||
|
Callback = new((CallStatistics cs) =>
|
||||||
{
|
{
|
||||||
// Timeout = TimeSpan.FromSeconds(20),
|
callbackInvoked = true;
|
||||||
Callback = new((CallStatistics cs) =>
|
Assert.True(cs.ElapsedMicroSeconds > 0);
|
||||||
{
|
|
||||||
callbackInvoked = true;
|
|
||||||
Assert.True(cs.ElapsedMicroSeconds > 0);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
|
||||||
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
|
||||||
|
|
||||||
var createdContainer = await client.CreateContainerAsync(createContainerParam);
|
|
||||||
|
|
||||||
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx));
|
|
||||||
Assert.NotNull(container);
|
|
||||||
Assert.True(callbackInvoked);
|
|
||||||
|
|
||||||
var bytes = GetRandomBytes(objectSize);
|
|
||||||
|
|
||||||
var param = new PrmObjectPut(new CallContext
|
|
||||||
{
|
|
||||||
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
|
||||||
})
|
})
|
||||||
{
|
};
|
||||||
Header = new FrostFsObjectHeader(
|
|
||||||
containerId: createdContainer,
|
|
||||||
type: FrostFsObjectType.Regular,
|
|
||||||
[new FrostFsAttributePair("fileName", "test")]),
|
|
||||||
Payload = new MemoryStream(bytes),
|
|
||||||
ClientCut = false
|
|
||||||
};
|
|
||||||
|
|
||||||
var objectId = await client.PutObjectAsync(param);
|
var createContainerParam = new PrmContainerCreate(
|
||||||
|
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
|
||||||
|
|
||||||
var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test");
|
var createdContainer = await client.CreateContainerAsync(createContainerParam);
|
||||||
|
|
||||||
bool hasObject = false;
|
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx));
|
||||||
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] }))
|
Assert.NotNull(container);
|
||||||
{
|
Assert.True(callbackInvoked);
|
||||||
hasObject = true;
|
|
||||||
|
|
||||||
var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId));
|
var bytes = GetRandomBytes(objectSize);
|
||||||
Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength);
|
|
||||||
Assert.NotNull(objHeader.Attributes);
|
|
||||||
Assert.Single(objHeader.Attributes);
|
|
||||||
Assert.Equal("fileName", objHeader.Attributes.First().Key);
|
|
||||||
Assert.Equal("test", objHeader.Attributes.First().Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.True(hasObject);
|
var param = new PrmObjectPut(new CallContext
|
||||||
|
{
|
||||||
|
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Header = new FrostFsObjectHeader(
|
||||||
|
containerId: createdContainer,
|
||||||
|
type: FrostFsObjectType.Regular,
|
||||||
|
[new FrostFsAttributePair("fileName", "test")]),
|
||||||
|
Payload = new MemoryStream(bytes),
|
||||||
|
ClientCut = false
|
||||||
|
};
|
||||||
|
|
||||||
var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId));
|
var objectId = await client.PutObjectAsync(param);
|
||||||
|
|
||||||
var downloadedBytes = new byte[@object.Header.PayloadLength];
|
var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test");
|
||||||
MemoryStream ms = new(downloadedBytes);
|
|
||||||
|
|
||||||
ReadOnlyMemory<byte>? chunk = null;
|
bool hasObject = false;
|
||||||
while ((chunk = await @object.ObjectReader!.ReadChunk()) != null)
|
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] }))
|
||||||
{
|
{
|
||||||
ms.Write(chunk.Value.Span);
|
hasObject = true;
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes));
|
var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId));
|
||||||
|
Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength);
|
||||||
|
Assert.NotNull(objHeader.Attributes);
|
||||||
|
Assert.Single(objHeader.Attributes);
|
||||||
|
Assert.Equal("fileName", objHeader.Attributes.First().Key);
|
||||||
|
Assert.Equal("test", objHeader.Attributes.First().Value);
|
||||||
|
}
|
||||||
|
|
||||||
await Cleanup(client);
|
Assert.True(hasObject);
|
||||||
|
|
||||||
await foreach (var _ in client.ListContainersAsync())
|
var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId));
|
||||||
{
|
|
||||||
Assert.Fail("Containers exist");
|
var downloadedBytes = new byte[@object.Header.PayloadLength];
|
||||||
}
|
MemoryStream ms = new(downloadedBytes);
|
||||||
|
|
||||||
|
ReadOnlyMemory<byte>? chunk = null;
|
||||||
|
while ((chunk = await @object.ObjectReader!.ReadChunk()) != null)
|
||||||
|
{
|
||||||
|
ms.Write(chunk.Value.Span);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes));
|
||||||
|
|
||||||
|
await Cleanup(client);
|
||||||
|
|
||||||
|
await foreach (var _ in client.ListContainersAsync())
|
||||||
|
{
|
||||||
|
Assert.Fail("Containers exist");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -388,7 +388,7 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
ms.Write(chunk.Value.Span);
|
ms.Write(chunk.Value.Span);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < (int)range.Offset; i++)
|
for (int i = 0; i < (int)range.Offset; i++)
|
||||||
Assert.Equal(downloadedBytes[i], bytes[i]);
|
Assert.Equal(downloadedBytes[i], bytes[i]);
|
||||||
|
|
||||||
var rangeEnd = range.Offset + range.Length;
|
var rangeEnd = range.Offset + range.Length;
|
||||||
|
@ -494,15 +494,15 @@ public class SmokeClientTests : SmokeTestsBase
|
||||||
|
|
||||||
var objectId = await client.PutObjectAsync(param);
|
var objectId = await client.PutObjectAsync(param);
|
||||||
|
|
||||||
var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [ new FrostFsRange(100, 64)], bytes);
|
var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [new FrostFsRange(100, 64)], bytes);
|
||||||
|
|
||||||
var hashes = await client.GetRangeHashAsync(rangeParam);
|
var hashes = await client.GetRangeHashAsync(rangeParam);
|
||||||
|
|
||||||
foreach (var hash in hashes)
|
foreach (var hash in hashes)
|
||||||
{
|
{
|
||||||
var x = hash.Slice(0, 32).ToArray();
|
var x = hash[..32].ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
await Cleanup(client);
|
await Cleanup(client);
|
||||||
|
|
||||||
await foreach (var _ in client.ListContainersAsync())
|
await foreach (var _ in client.ListContainersAsync())
|
||||||
|
|
Loading…
Reference in a new issue