[#1] Add object Get operation + code quality
Signed-off-by: Ivan Pchelintsev <i.pchelintsev@yadro.com>
This commit is contained in:
parent
9aa93d123d
commit
b307c2c899
17 changed files with 182 additions and 99 deletions
|
@ -2,6 +2,7 @@ using System.Security.Cryptography;
|
||||||
using FrostFS.Container;
|
using FrostFS.Container;
|
||||||
using FrostFS.Netmap;
|
using FrostFS.Netmap;
|
||||||
using FrostFS.Object;
|
using FrostFS.Object;
|
||||||
|
using FrostFS.SDK.ClientV2.Interfaces;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
||||||
using FrostFS.SDK.Cryptography;
|
using FrostFS.SDK.Cryptography;
|
||||||
using FrostFS.SDK.ModelsV2;
|
using FrostFS.SDK.ModelsV2;
|
||||||
|
|
|
@ -5,7 +5,7 @@ using DeleteResponse = FrostFS.Container.DeleteResponse;
|
||||||
using GetResponse = FrostFS.Container.GetResponse;
|
using GetResponse = FrostFS.Container.GetResponse;
|
||||||
using PutResponse = FrostFS.Container.PutResponse;
|
using PutResponse = FrostFS.Container.PutResponse;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.ClientV2.Interfaces;
|
||||||
|
|
||||||
public interface IFrostFSClient
|
public interface IFrostFSClient
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ public interface IFrostFSClient
|
||||||
Task<GetResponse> GetContainerAsync(ContainerID containerId);
|
Task<GetResponse> GetContainerAsync(ContainerID containerId);
|
||||||
Task<DeleteResponse> DeleteContainerAsync(ContainerID containerId);
|
Task<DeleteResponse> DeleteContainerAsync(ContainerID containerId);
|
||||||
Task<HeadResponse> GetObjectHeadAsync(ContainerID containerId, ObjectID objectId);
|
Task<HeadResponse> GetObjectHeadAsync(ContainerID containerId, ObjectID objectId);
|
||||||
Task<Object.PutResponse> PutObjectAsync(Object.Header header, Stream payload);
|
Task<Object.Object> GetObjectAsync(ContainerID containerId, ObjectID objectId);
|
||||||
|
Task<Object.PutResponse> PutObjectAsync(Header header, Stream payload);
|
||||||
Task<Object.DeleteResponse> DeleteObjectAsync(ContainerID containerId, ObjectID objectId);
|
Task<Object.DeleteResponse> DeleteObjectAsync(ContainerID containerId, ObjectID objectId);
|
||||||
}
|
}
|
|
@ -19,14 +19,14 @@ public static class ContainerMapper
|
||||||
|
|
||||||
public static ModelsV2.Container ToModel(this Container.Container container)
|
public static ModelsV2.Container ToModel(this Container.Container container)
|
||||||
{
|
{
|
||||||
var basicAclName = Enum.GetName(typeof(BasicACL), container.BasicAcl);
|
var basicAclName = Enum.GetName(typeof(BasicAcl), container.BasicAcl);
|
||||||
if (basicAclName is null)
|
if (basicAclName is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'.");
|
throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ModelsV2.Container(
|
return new ModelsV2.Container(
|
||||||
Enum.Parse<BasicACL>(basicAclName),
|
Enum.Parse<BasicAcl>(basicAclName),
|
||||||
container.PlacementPolicy.ToModel()
|
container.PlacementPolicy.ToModel()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,12 +24,9 @@ public static class PlacementPolicyMapper
|
||||||
|
|
||||||
public static ModelsV2.Netmap.PlacementPolicy ToModel(this PlacementPolicy placementPolicy)
|
public static ModelsV2.Netmap.PlacementPolicy ToModel(this PlacementPolicy placementPolicy)
|
||||||
{
|
{
|
||||||
var replicas = new List<Replica>();
|
return new ModelsV2.Netmap.PlacementPolicy(
|
||||||
foreach (var replica in placementPolicy.Replicas)
|
placementPolicy.Unique,
|
||||||
{
|
placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray()
|
||||||
replicas.Add(replica.ToModel());
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return new ModelsV2.Netmap.PlacementPolicy(placementPolicy.Unique, replicas.ToArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -52,20 +52,27 @@ public static class ObjectHeadMapper
|
||||||
throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.");
|
throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var attributes = new List<ObjectAttribute>();
|
|
||||||
foreach (var attribute in header.Attributes)
|
|
||||||
{
|
|
||||||
attributes.Add(attribute.ToModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ObjectHeader(
|
return new ObjectHeader(
|
||||||
ContainerId.FromHash(header.ContainerId.Value.ToByteArray()),
|
ContainerId.FromHash(header.ContainerId.Value.ToByteArray()),
|
||||||
Enum.Parse<ModelsV2.Enums.ObjectType>(objTypeName),
|
Enum.Parse<ModelsV2.Enums.ObjectType>(objTypeName),
|
||||||
attributes.ToArray()
|
header.Attributes.Select(attribute => attribute.ToModel()).ToArray()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Size = (long)header.PayloadLength,
|
Size = (long)header.PayloadLength,
|
||||||
Version = header.Version.ToModel()
|
Version = header.Version.ToModel()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ObjectMapper
|
||||||
|
{
|
||||||
|
public static ModelsV2.Object ToModel(this Object.Object obj)
|
||||||
|
{
|
||||||
|
return new ModelsV2.Object
|
||||||
|
{
|
||||||
|
Header = obj.Header.ToModel(),
|
||||||
|
ObjectId = ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
|
||||||
|
Payload = obj.Payload.ToByteArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ using FrostFS.Refs;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
||||||
using FrostFS.SDK.ModelsV2;
|
using FrostFS.SDK.ModelsV2;
|
||||||
using FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
using Version = FrostFS.SDK.ModelsV2.Version;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.ClientV2;
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,14 @@ namespace FrostFS.SDK.ClientV2
|
||||||
public static byte[] SignRFC6979(this ECDsa key, byte[] data)
|
public static byte[] SignRFC6979(this ECDsa key, byte[] data)
|
||||||
{
|
{
|
||||||
var digest = new Sha256Digest();
|
var digest = new Sha256Digest();
|
||||||
var secp256r1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var ec_parameters = new ECDomainParameters(secp256r1.Curve, secp256r1.G, secp256r1.N);
|
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
||||||
var private_key = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ec_parameters);
|
var privateKey = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ecParameters);
|
||||||
var signer = new ECDsaSigner(new HMacDsaKCalculator(digest));
|
var signer = new ECDsaSigner(new HMacDsaKCalculator(digest));
|
||||||
var hash = new byte[digest.GetDigestSize()];
|
var hash = new byte[digest.GetDigestSize()];
|
||||||
digest.BlockUpdate(data, 0, data.Length);
|
digest.BlockUpdate(data, 0, data.Length);
|
||||||
digest.DoFinal(hash, 0);
|
digest.DoFinal(hash, 0);
|
||||||
signer.Init(true, private_key);
|
signer.Init(true, privateKey);
|
||||||
var rs = signer.GenerateSignature(hash);
|
var rs = signer.GenerateSignature(hash);
|
||||||
var signature = new byte[RFC6979SignatureSize];
|
var signature = new byte[RFC6979SignatureSize];
|
||||||
var rbytes = rs[0].ToByteArrayUnsigned();
|
var rbytes = rs[0].ToByteArrayUnsigned();
|
||||||
|
@ -67,11 +67,11 @@ namespace FrostFS.SDK.ClientV2
|
||||||
|
|
||||||
public static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
public static Signature SignMessagePart(this ECDsa key, IMessage? data)
|
||||||
{
|
{
|
||||||
var data2sign = data is null ? Array.Empty<byte>() : data.ToByteArray();
|
var data2Sign = data is null ? Array.Empty<byte>() : data.ToByteArray();
|
||||||
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 = ByteString.CopyFrom(key.SignData(data2Sign)),
|
||||||
};
|
};
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,19 +24,19 @@ namespace FrostFS.SDK.ClientV2 {
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VerifyRFC6979(this byte[] public_key, byte[] data, byte[] sig)
|
public static bool VerifyRFC6979(this byte[] publicKey, byte[] data, byte[] sig)
|
||||||
{
|
{
|
||||||
if (public_key is null || data is null || sig is null) return false;
|
if (publicKey is null || data is null || sig is null) return false;
|
||||||
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));
|
||||||
var secp256r1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var ec_parameters = new ECDomainParameters(secp256r1.Curve, secp256r1.G, secp256r1.N);
|
var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
||||||
var bc_public_key = new ECPublicKeyParameters(secp256r1.Curve.DecodePoint(public_key), ec_parameters);
|
var bcPublicKey = new ECPublicKeyParameters(secp256R1.Curve.DecodePoint(publicKey), ecParameters);
|
||||||
var hash = new byte[digest.GetDigestSize()];
|
var hash = new byte[digest.GetDigestSize()];
|
||||||
digest.BlockUpdate(data, 0, data.Length);
|
digest.BlockUpdate(data, 0, data.Length);
|
||||||
digest.DoFinal(hash, 0);
|
digest.DoFinal(hash, 0);
|
||||||
signer.Init(false, bc_public_key);
|
signer.Init(false, bcPublicKey);
|
||||||
return signer.VerifySignature(hash, rs[0], rs[1]);
|
return signer.VerifySignature(hash, rs[0], rs[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ namespace FrostFS.SDK.ClientV2 {
|
||||||
{
|
{
|
||||||
if (sig is null || sig.Key is null || sig.Sign is null) return false;
|
if (sig is null || sig.Key is null || sig.Sign is null) return false;
|
||||||
using var key = sig.Key.ToByteArray().LoadPublicKey();
|
using var key = sig.Key.ToByteArray().LoadPublicKey();
|
||||||
var data2verify = data is null ? Array.Empty<byte>() : data.ToByteArray();
|
var data2Verify = data is null ? Array.Empty<byte>() : data.ToByteArray();
|
||||||
return key.VerifyData(data2verify, sig.Sign.ToByteArray());
|
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
public static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification)
|
||||||
|
@ -65,8 +65,7 @@ namespace FrostFS.SDK.ClientV2 {
|
||||||
if (!verification.OriginSignature.VerifyMessagePart(origin)) return false;
|
if (!verification.OriginSignature.VerifyMessagePart(origin)) return false;
|
||||||
if (origin is null)
|
if (origin is null)
|
||||||
return verification.BodySignature.VerifyMessagePart(body);
|
return verification.BodySignature.VerifyMessagePart(body);
|
||||||
if (verification.BodySignature is not null) return false;
|
return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin);
|
||||||
return VerifyMatryoskaLevel(body, meta.GetOrigin(), origin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Verify(this IVerificableMessage message)
|
public static bool Verify(this IVerificableMessage message)
|
||||||
|
|
|
@ -65,25 +65,4 @@ public partial class Client
|
||||||
request.Sign(_key);
|
request.Sign(_key);
|
||||||
return await _containerServiceClient.DeleteAsync(request);
|
return await _containerServiceClient.DeleteAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void PrepareContainerSessionToken(RequestMetaHeader meta, SessionToken sessionToken, ContainerID? cid,
|
|
||||||
// ContainerSessionContext.Types.Verb verb)
|
|
||||||
// {
|
|
||||||
// if (meta.SessionToken is not null) return;
|
|
||||||
// meta.SessionToken = sessionToken;
|
|
||||||
// var ctx = new ContainerSessionContext
|
|
||||||
// {
|
|
||||||
// Verb = verb
|
|
||||||
// };
|
|
||||||
// if (cid is null)
|
|
||||||
// {
|
|
||||||
// ctx.Wildcard = true;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// ctx.ContainerId = cid;
|
|
||||||
// }
|
|
||||||
// meta.SessionToken.Body.Container = ctx;
|
|
||||||
// meta.SessionToken.Signature = _key.SignMessagePart(meta.SessionToken.Body);
|
|
||||||
// }
|
|
||||||
}
|
}
|
|
@ -29,11 +29,64 @@ public partial class Client
|
||||||
return await _objectServiceClient.HeadAsync(request);
|
return await _objectServiceClient.HeadAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async Task<GetResponse> GetObjectAsync(ContainerID cid, ObjectID oid)
|
public async Task<Object.Object> GetObjectAsync(ContainerID cid, ObjectID oid)
|
||||||
// {
|
{
|
||||||
//
|
var sessionToken = await CreateSessionAsync(uint.MaxValue);
|
||||||
// }
|
var request = new GetRequest
|
||||||
|
{
|
||||||
|
Body = new GetRequest.Types.Body
|
||||||
|
{
|
||||||
|
Raw = false,
|
||||||
|
Address = new Address
|
||||||
|
{
|
||||||
|
ContainerId = cid,
|
||||||
|
ObjectId = oid
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.AddMetaHeader();
|
||||||
|
request.AddObjectSessionToken(
|
||||||
|
sessionToken,
|
||||||
|
cid,
|
||||||
|
oid,
|
||||||
|
ObjectSessionContext.Types.Verb.Get,
|
||||||
|
_key
|
||||||
|
);
|
||||||
|
request.Sign(_key);
|
||||||
|
|
||||||
|
return await GetObject(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Object.Object> GetObject(GetRequest request)
|
||||||
|
{
|
||||||
|
using var stream = GetObjectInit(request);
|
||||||
|
var obj = await stream.ReadHeader();
|
||||||
|
var payload = new byte[obj.Header.PayloadLength];
|
||||||
|
var offset = 0;
|
||||||
|
var chunk = await stream.ReadChunk();
|
||||||
|
while (chunk is not null)
|
||||||
|
{
|
||||||
|
chunk.CopyTo(payload, offset);
|
||||||
|
offset += chunk.Length;
|
||||||
|
chunk = await stream.ReadChunk();
|
||||||
|
}
|
||||||
|
obj.Payload = ByteString.CopyFrom(payload);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectReader GetObjectInit(GetRequest initRequest)
|
||||||
|
{
|
||||||
|
if (initRequest is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(initRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectReader
|
||||||
|
{
|
||||||
|
Call = _objectServiceClient.Get(initRequest)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PutResponse> PutObjectAsync(Header header, Stream payload)
|
public async Task<PutResponse> PutObjectAsync(Header header, Stream payload)
|
||||||
{
|
{
|
||||||
var sessionToken = await CreateSessionAsync(uint.MaxValue);
|
var sessionToken = await CreateSessionAsync(uint.MaxValue);
|
||||||
|
@ -63,7 +116,7 @@ public partial class Client
|
||||||
);
|
);
|
||||||
request.Sign(_key);
|
request.Sign(_key);
|
||||||
|
|
||||||
using var stream = await InitObject(request);
|
using var stream = await PutObjectInit(request);
|
||||||
var buffer = new byte[Constants.ObjectChunkSize];
|
var buffer = new byte[Constants.ObjectChunkSize];
|
||||||
var bufferLength = payload.Read(buffer, 0, Constants.ObjectChunkSize);
|
var bufferLength = payload.Read(buffer, 0, Constants.ObjectChunkSize);
|
||||||
while (bufferLength > 0)
|
while (bufferLength > 0)
|
||||||
|
@ -81,7 +134,7 @@ public partial class Client
|
||||||
return await stream.Close();
|
return await stream.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ObjectStreamer> InitObject(PutRequest initRequest)
|
private async Task<ObjectStreamer> PutObjectInit(PutRequest initRequest)
|
||||||
{
|
{
|
||||||
if (initRequest is null)
|
if (initRequest is null)
|
||||||
{
|
{
|
||||||
|
@ -112,14 +165,47 @@ public partial class Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ObjectStreamer : IDisposable
|
internal class ObjectReader : IDisposable
|
||||||
{
|
{
|
||||||
public AsyncClientStreamingCall<PutRequest, PutResponse> Call { get; init; }
|
public AsyncServerStreamingCall<GetResponse> Call { get; init; }
|
||||||
|
|
||||||
|
public async Task<Object.Object> ReadHeader()
|
||||||
|
{
|
||||||
|
if (!await Call.ResponseStream.MoveNext())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("unexpect end of stream");
|
||||||
|
}
|
||||||
|
var response = Call.ResponseStream.Current;
|
||||||
|
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init)
|
||||||
|
throw new InvalidOperationException("unexpect message type");
|
||||||
|
return new Object.Object
|
||||||
|
{
|
||||||
|
ObjectId = response.Body.Init.ObjectId,
|
||||||
|
Header = response.Body.Init.Header,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]?> ReadChunk()
|
||||||
|
{
|
||||||
|
if (!await Call.ResponseStream.MoveNext())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var response = Call.ResponseStream.Current;
|
||||||
|
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk)
|
||||||
|
throw new InvalidOperationException("unexpect message type");
|
||||||
|
return response.Body.Chunk.ToByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Call.Dispose();
|
Call.Dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjectStreamer : IDisposable
|
||||||
|
{
|
||||||
|
public AsyncClientStreamingCall<PutRequest, PutResponse> Call { get; init; }
|
||||||
|
|
||||||
public async Task Write(PutRequest request)
|
public async Task Write(PutRequest request)
|
||||||
{
|
{
|
||||||
|
@ -136,4 +222,9 @@ internal class ObjectStreamer : IDisposable
|
||||||
await Call.RequestStream.CompleteAsync();
|
await Call.RequestStream.CompleteAsync();
|
||||||
return await Call.ResponseAsync;
|
return await Call.ResponseAsync;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Call.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,12 +7,11 @@ namespace FrostFS.SDK.Cryptography
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static byte[] Concat(params byte[][] buffers)
|
public static byte[] Concat(params byte[][] buffers)
|
||||||
{
|
{
|
||||||
int length = 0;
|
var length = buffers.Sum(buffer => buffer.Length);
|
||||||
for (int i = 0; i < buffers.Length; i++)
|
|
||||||
length += buffers[i].Length;
|
var dst = new byte[length];
|
||||||
byte[] dst = new byte[length];
|
var p = 0;
|
||||||
int p = 0;
|
foreach (var src in buffers)
|
||||||
foreach (byte[] src in buffers)
|
|
||||||
{
|
{
|
||||||
Buffer.BlockCopy(src, 0, dst, p, src.Length);
|
Buffer.BlockCopy(src, 0, dst, p, src.Length);
|
||||||
p += src.Length;
|
p += src.Length;
|
||||||
|
|
|
@ -22,8 +22,8 @@ public static class KeyExtension
|
||||||
$"{nameof(Compress)} argument isn't uncompressed public key. " +
|
$"{nameof(Compress)} argument isn't uncompressed public key. " +
|
||||||
$"expected length={UncompressedPublicKeyLength}, actual={publicKey.Length}"
|
$"expected length={UncompressedPublicKeyLength}, actual={publicKey.Length}"
|
||||||
);
|
);
|
||||||
var secp256r1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var point = secp256r1.Curve.DecodePoint(publicKey);
|
var point = secp256R1.Curve.DecodePoint(publicKey);
|
||||||
return point.GetEncoded(true);
|
return point.GetEncoded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ public static class KeyExtension
|
||||||
$"{nameof(Decompress)} argument isn't compressed public key. " +
|
$"{nameof(Decompress)} argument isn't compressed public key. " +
|
||||||
$"expected length={CompressedPublicKeyLength}, actual={publicKey.Length}"
|
$"expected length={CompressedPublicKeyLength}, actual={publicKey.Length}"
|
||||||
);
|
);
|
||||||
var secp256r1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var point = secp256r1.Curve.DecodePoint(publicKey);
|
var point = secp256R1.Curve.DecodePoint(publicKey);
|
||||||
return point.GetEncoded(false);
|
return point.GetEncoded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,19 +115,19 @@ public static class KeyExtension
|
||||||
return key.ExportParameters(true).D;
|
return key.ExportParameters(true).D;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ECDsa LoadPrivateKey(this byte[] private_key)
|
public static ECDsa LoadPrivateKey(this byte[] privateKey)
|
||||||
{
|
{
|
||||||
var secp256r1 = SecNamedCurves.GetByName("secp256r1");
|
var secp256R1 = SecNamedCurves.GetByName("secp256r1");
|
||||||
var public_key =
|
var publicKey =
|
||||||
secp256r1.G.Multiply(new Org.BouncyCastle.Math.BigInteger(1, private_key)).GetEncoded(false)[1..];
|
secp256R1.G.Multiply(new Org.BouncyCastle.Math.BigInteger(1, privateKey)).GetEncoded(false)[1..];
|
||||||
var key = ECDsa.Create(new ECParameters
|
var key = ECDsa.Create(new ECParameters
|
||||||
{
|
{
|
||||||
Curve = ECCurve.NamedCurves.nistP256,
|
Curve = ECCurve.NamedCurves.nistP256,
|
||||||
D = private_key,
|
D = privateKey,
|
||||||
Q = new ECPoint
|
Q = new ECPoint
|
||||||
{
|
{
|
||||||
X = public_key[..32],
|
X = publicKey[..32],
|
||||||
Y = public_key[32..]
|
Y = publicKey[32..]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return key;
|
return key;
|
||||||
|
@ -135,20 +135,20 @@ public static class KeyExtension
|
||||||
|
|
||||||
public static ECDsa LoadWif(this string wif)
|
public static ECDsa LoadWif(this string wif)
|
||||||
{
|
{
|
||||||
var private_key = GetPrivateKeyFromWIF(wif);
|
var privateKey = GetPrivateKeyFromWIF(wif);
|
||||||
return LoadPrivateKey(private_key);
|
return LoadPrivateKey(privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ECDsa LoadPublicKey(this byte[] public_key)
|
public static ECDsa LoadPublicKey(this byte[] publicKey)
|
||||||
{
|
{
|
||||||
var public_key_full = public_key.Decompress()[1..];
|
var publicKeyFull = publicKey.Decompress()[1..];
|
||||||
var key = ECDsa.Create(new ECParameters
|
var key = ECDsa.Create(new ECParameters
|
||||||
{
|
{
|
||||||
Curve = ECCurve.NamedCurves.nistP256,
|
Curve = ECCurve.NamedCurves.nistP256,
|
||||||
Q = new ECPoint
|
Q = new ECPoint
|
||||||
{
|
{
|
||||||
X = public_key_full[..32],
|
X = publicKeyFull[..32],
|
||||||
Y = public_key_full[32..]
|
Y = publicKeyFull[32..]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return key;
|
return key;
|
||||||
|
|
|
@ -6,11 +6,11 @@ namespace FrostFS.SDK.ModelsV2;
|
||||||
public class Container
|
public class Container
|
||||||
{
|
{
|
||||||
public Guid Nonce { get; set; }
|
public Guid Nonce { get; set; }
|
||||||
public BasicACL BasicAcl { get; set; }
|
public BasicAcl BasicAcl { get; set; }
|
||||||
public PlacementPolicy PlacementPolicy { get; set; }
|
public PlacementPolicy PlacementPolicy { get; set; }
|
||||||
public Version Version { get; set; }
|
public Version Version { get; set; }
|
||||||
|
|
||||||
public Container(BasicACL basicAcl, PlacementPolicy placementPolicy)
|
public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy)
|
||||||
{
|
{
|
||||||
Nonce = Guid.NewGuid();
|
Nonce = Guid.NewGuid();
|
||||||
BasicAcl = basicAcl;
|
BasicAcl = basicAcl;
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace FrostFS.SDK.ModelsV2;
|
||||||
|
|
||||||
public class ContainerId
|
public class ContainerId
|
||||||
{
|
{
|
||||||
public string Value { get; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
public ContainerId(string id)
|
public ContainerId(string id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ModelsV2.Enums;
|
namespace FrostFS.SDK.ModelsV2.Enums;
|
||||||
|
|
||||||
public enum BasicACL
|
public enum BasicAcl
|
||||||
{
|
{
|
||||||
[Description("Basic ACL for private container")]
|
[Description("Basic ACL for private container")]
|
||||||
Private = 0x1C8C8CCC,
|
Private = 0x1C8C8CCC,
|
|
@ -37,5 +37,6 @@ public class ObjectHeader
|
||||||
public class Object
|
public class Object
|
||||||
{
|
{
|
||||||
public ObjectHeader Header { get; set; }
|
public ObjectHeader Header { get; set; }
|
||||||
public Stream Payload { get; set; }
|
public ObjectId ObjectId { get; set; }
|
||||||
|
public byte[] Payload { get; set; }
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using FrostFS.SDK.ClientV2;
|
using FrostFS.SDK.ClientV2;
|
||||||
|
using FrostFS.SDK.ClientV2.Interfaces;
|
||||||
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
||||||
using FrostFS.SDK.ModelsV2;
|
using FrostFS.SDK.ModelsV2;
|
||||||
|
|
||||||
|
@ -16,14 +17,11 @@ public class FrostFsService
|
||||||
|
|
||||||
public async Task<ContainerId[]> ListContainersAsync()
|
public async Task<ContainerId[]> ListContainersAsync()
|
||||||
{
|
{
|
||||||
var containersIds = new List<ContainerId>();
|
|
||||||
var listContainersResponse = await _client.ListContainersAsync();
|
var listContainersResponse = await _client.ListContainersAsync();
|
||||||
foreach (var cid in listContainersResponse.Body.ContainerIds)
|
|
||||||
{
|
|
||||||
containersIds.Add(ContainerId.FromHash(cid.Value.ToByteArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return containersIds.ToArray();
|
return listContainersResponse.Body.ContainerIds.Select(
|
||||||
|
cid => ContainerId.FromHash(cid.Value.ToByteArray())
|
||||||
|
).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ContainerId> CreateContainerAsync(ModelsV2.Container container)
|
public async Task<ContainerId> CreateContainerAsync(ModelsV2.Container container)
|
||||||
|
@ -52,12 +50,21 @@ public class FrostFsService
|
||||||
return getObjectHeadResponse.Body.Header.Header.ToModel();
|
return getObjectHeadResponse.Body.Header.Header.ToModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId)
|
||||||
|
{
|
||||||
|
var obj = await _client.GetObjectAsync(
|
||||||
|
containerId.ToGrpcMessage(),
|
||||||
|
objectId.ToGrpcMessage()
|
||||||
|
);
|
||||||
|
return obj.ToModel();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, Stream payload)
|
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, Stream payload)
|
||||||
{
|
{
|
||||||
var putObjectResponse = await _client.PutObjectAsync(header.ToGrpcMessage(), payload);
|
var putObjectResponse = await _client.PutObjectAsync(header.ToGrpcMessage(), payload);
|
||||||
return ObjectId.FromHash(putObjectResponse.Body.ObjectId.Value.ToByteArray());
|
return ObjectId.FromHash(putObjectResponse.Body.ObjectId.Value.ToByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, byte[] payload)
|
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, byte[] payload)
|
||||||
{
|
{
|
||||||
var putObjectResponse = await _client.PutObjectAsync(header.ToGrpcMessage(), new MemoryStream(payload));
|
var putObjectResponse = await _client.PutObjectAsync(header.ToGrpcMessage(), new MemoryStream(payload));
|
||||||
|
|
Loading…
Reference in a new issue