diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 482baac..0000000 --- a/.editorconfig +++ /dev/null @@ -1,901 +0,0 @@ -[*.cs] - -# CA1001: Types that own disposable fields should be disposable -dotnet_diagnostic.CA1001.severity = warning - -# CA1000: Do not declare static members on generic types -dotnet_diagnostic.CA1000.severity = warning - -# CA1002: Do not expose generic lists -dotnet_diagnostic.CA1002.severity = warning - -# CA1003: Use generic event handler instances -dotnet_diagnostic.CA1003.severity = warning - -# CA1005: Avoid excessive parameters on generic types -dotnet_diagnostic.CA1005.severity = warning - -# CA1008: Enums should have zero value -dotnet_diagnostic.CA1008.severity = warning - -# CA1010: Generic interface should also be implemented -dotnet_diagnostic.CA1010.severity = warning - -# CA1012: Abstract types should not have public constructors -dotnet_diagnostic.CA1012.severity = warning - -# CA1014: Mark assemblies with CLSCompliant -dotnet_diagnostic.CA1014.severity = warning - -# CA1016: Mark assemblies with assembly version -dotnet_diagnostic.CA1016.severity = warning - -# CA1017: Mark assemblies with ComVisible -dotnet_diagnostic.CA1017.severity = warning - -# CA1018: Mark attributes with AttributeUsageAttribute -dotnet_diagnostic.CA1018.severity = warning - -# CA1019: Define accessors for attribute arguments -dotnet_diagnostic.CA1019.severity = warning - -# CA1021: Avoid out parameters -dotnet_diagnostic.CA1021.severity = warning - -# CA1024: Use properties where appropriate -dotnet_diagnostic.CA1024.severity = warning - -# CA1027: Mark enums with FlagsAttribute -dotnet_diagnostic.CA1027.severity = warning - -# CA1028: Enum Storage should be Int32 -dotnet_diagnostic.CA1028.severity = warning - -# CA1030: Use events where appropriate -dotnet_diagnostic.CA1030.severity = warning - -# CA1031: Do not catch general exception types -dotnet_diagnostic.CA1031.severity = warning - -# CA1033: Interface methods should be callable by child types -dotnet_diagnostic.CA1033.severity = warning - -# CA1034: Nested types should not be visible -dotnet_diagnostic.CA1034.severity = warning - -# CA1036: Override methods on comparable types -dotnet_diagnostic.CA1036.severity = warning - -# CA1040: Avoid empty interfaces -dotnet_diagnostic.CA1040.severity = warning - -# CA1041: Provide ObsoleteAttribute message -dotnet_diagnostic.CA1041.severity = warning - -# CA1043: Use Integral Or String Argument For Indexers -dotnet_diagnostic.CA1043.severity = warning - -# CA1044: Properties should not be write only -dotnet_diagnostic.CA1044.severity = warning - -# CA1045: Do not pass types by reference -dotnet_diagnostic.CA1045.severity = warning - -# CA1046: Do not overload equality operator on reference types -dotnet_diagnostic.CA1046.severity = warning - -# CA1050: Declare types in namespaces -dotnet_diagnostic.CA1050.severity = warning - -# CA1051: Do not declare visible instance fields -dotnet_diagnostic.CA1051.severity = warning - -# CA1052: Static holder types should be Static or NotInheritable -dotnet_diagnostic.CA1052.severity = warning - -# CA1054: URI-like parameters should not be strings -dotnet_diagnostic.CA1054.severity = warning - -# CA1055: URI-like return values should not be strings -dotnet_diagnostic.CA1055.severity = warning - -# CA1056: URI-like properties should not be strings -dotnet_diagnostic.CA1056.severity = warning - -# CA1058: Types should not extend certain base types -dotnet_diagnostic.CA1058.severity = warning - -# CA1060: Move pinvokes to native methods class -dotnet_diagnostic.CA1060.severity = warning - -# CA1061: Do not hide base class methods -dotnet_diagnostic.CA1061.severity = warning - -# CA1062: Validate arguments of public methods -dotnet_diagnostic.CA1062.severity = warning - -# CA1063: Implement IDisposable Correctly -dotnet_diagnostic.CA1063.severity = warning - -# CA1064: Exceptions should be public -dotnet_diagnostic.CA1064.severity = warning - -# CA1065: Do not raise exceptions in unexpected locations -dotnet_diagnostic.CA1065.severity = warning - -# CA1066: Implement IEquatable when overriding Object.Equals -dotnet_diagnostic.CA1066.severity = warning - -# CA1067: Override Object.Equals(object) when implementing IEquatable -dotnet_diagnostic.CA1067.severity = warning - -# CA1068: CancellationToken parameters must come last -dotnet_diagnostic.CA1068.severity = warning - -# CA1069: Enums values should not be duplicated -dotnet_diagnostic.CA1069.severity = warning - -# CA1070: Do not declare event fields as virtual -dotnet_diagnostic.CA1070.severity = warning - -# CA1303: Do not pass literals as localized parameters -dotnet_diagnostic.CA1303.severity = warning - -# CA1304: Specify CultureInfo -dotnet_diagnostic.CA1304.severity = warning - -# CA1305: Specify IFormatProvider -dotnet_diagnostic.CA1305.severity = warning - -# CA1307: Specify StringComparison for clarity -dotnet_diagnostic.CA1307.severity = warning - -# CA1308: Normalize strings to uppercase -dotnet_diagnostic.CA1308.severity = warning - -# CA1310: Specify StringComparison for correctness -dotnet_diagnostic.CA1310.severity = warning - -# CA1401: P/Invokes should not be visible -dotnet_diagnostic.CA1401.severity = warning - -# CA1416: Validate platform compatibility -dotnet_diagnostic.CA1416.severity = warning - -# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes -dotnet_diagnostic.CA1417.severity = warning - -# CA1418: Use valid platform string -dotnet_diagnostic.CA1418.severity = warning - -# CA1419: Provide a parameterless constructor that is as visible as the containing type for concrete types derived from 'System.Runtime.InteropServices.SafeHandle' -dotnet_diagnostic.CA1419.severity = warning - -# CA1420: Property, type, or attribute requires runtime marshalling -dotnet_diagnostic.CA1420.severity = warning - -# CA1421: This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied -dotnet_diagnostic.CA1421.severity = warning - -# CA1422: Validate platform compatibility -dotnet_diagnostic.CA1422.severity = warning - -# CA1501: Avoid excessive inheritance -dotnet_diagnostic.CA1501.severity = warning - -# CA1502: Avoid excessive complexity -dotnet_diagnostic.CA1502.severity = warning - -# CA1505: Avoid unmaintainable code -dotnet_diagnostic.CA1505.severity = warning - -# CA1506: Avoid excessive class coupling -# dotnet_diagnostic.CA1506.severity = warning - -# CA1509: Invalid entry in code metrics rule specification file -dotnet_diagnostic.CA1509.severity = warning - -# CA1510: Use ArgumentNullException throw helper -dotnet_diagnostic.CA1510.severity = warning - -# CA1511: Use ArgumentException throw helper -dotnet_diagnostic.CA1511.severity = warning - -# CA1512: Use ArgumentOutOfRangeException throw helper -dotnet_diagnostic.CA1512.severity = warning - -# CA1513: Use ObjectDisposedException throw helper -dotnet_diagnostic.CA1513.severity = warning - -# CA1700: Do not name enum values 'Reserved' -dotnet_diagnostic.CA1700.severity = warning - -# CA1707: Identifiers should not contain underscores -dotnet_diagnostic.CA1707.severity = warning - -# CA1708: Identifiers should differ by more than case -dotnet_diagnostic.CA1708.severity = none - -# CA1710: Identifiers should have correct suffix -dotnet_diagnostic.CA1710.severity = warning - -# CA1711: Identifiers should not have incorrect suffix -dotnet_diagnostic.CA1711.severity = warning - -# CA1712: Do not prefix enum values with type name -dotnet_diagnostic.CA1712.severity = warning - -# CA1713: Events should not have 'Before' or 'After' prefix -dotnet_diagnostic.CA1713.severity = warning - -# CA1715: Identifiers should have correct prefix -dotnet_diagnostic.CA1715.severity = warning - -# CA1716: Identifiers should not match keywords -dotnet_diagnostic.CA1716.severity = none - -# CA1720: Identifier contains type name -dotnet_diagnostic.CA1720.severity = warning - -# CA1721: Property names should not match get methods -dotnet_diagnostic.CA1721.severity = warning - -# CA1724: Type names should not match namespaces -dotnet_diagnostic.CA1724.severity = warning - -# CA1725: Parameter names should match base declaration -dotnet_diagnostic.CA1725.severity = warning - -# CA1727: Use PascalCase for named placeholders -dotnet_diagnostic.CA1727.severity = warning - -# CA1806: Do not ignore method results -dotnet_diagnostic.CA1806.severity = warning - -# CA1810: Initialize reference type static fields inline -dotnet_diagnostic.CA1810.severity = warning - -# CA1813: Avoid unsealed attributes -dotnet_diagnostic.CA1813.severity = warning - -# CA1814: Prefer jagged arrays over multidimensional -dotnet_diagnostic.CA1814.severity = warning - -# CA1815: Override equals and operator equals on value types -dotnet_diagnostic.CA1815.severity = warning - -# CA1816: Dispose methods should call SuppressFinalize -dotnet_diagnostic.CA1816.severity = warning - -# CA1819: Properties should not return arrays -# dotnet_diagnostic.CA1819.severity = warning - -# CA1820: Test for empty strings using string length -dotnet_diagnostic.CA1820.severity = warning - -# CA1821: Remove empty Finalizers -dotnet_diagnostic.CA1821.severity = warning - -# CA1822: Mark members as static -dotnet_diagnostic.CA1822.severity = warning - -# CA1823: Avoid unused private fields -dotnet_diagnostic.CA1823.severity = warning - -# CA1826: Do not use Enumerable methods on indexable collections -dotnet_diagnostic.CA1826.severity = warning - -# CA1827: Do not use Count() or LongCount() when Any() can be used -dotnet_diagnostic.CA1827.severity = warning - -# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used -dotnet_diagnostic.CA1828.severity = warning - -# CA1829: Use Length/Count property instead of Count() when available -dotnet_diagnostic.CA1829.severity = warning - -# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder -dotnet_diagnostic.CA1830.severity = warning - -# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate -dotnet_diagnostic.CA1831.severity = warning - -# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate -dotnet_diagnostic.CA1832.severity = warning - -# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate -dotnet_diagnostic.CA1833.severity = warning - -# CA1834: Consider using 'StringBuilder.Append(char)' when applicable -dotnet_diagnostic.CA1834.severity = warning - -# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' -dotnet_diagnostic.CA1835.severity = warning - -# CA1836: Prefer IsEmpty over Count -dotnet_diagnostic.CA1836.severity = warning - -# CA1837: Use 'Environment.ProcessId' -dotnet_diagnostic.CA1837.severity = warning - -# CA1838: Avoid 'StringBuilder' parameters for P/Invokes -dotnet_diagnostic.CA1838.severity = warning - -# CA1839: Use 'Environment.ProcessPath' -dotnet_diagnostic.CA1839.severity = warning - -# CA1840: Use 'Environment.CurrentManagedThreadId' -dotnet_diagnostic.CA1840.severity = warning - -# CA1842: Do not use 'WhenAll' with a single task -dotnet_diagnostic.CA1842.severity = warning - -# CA1843: Do not use 'WaitAll' with a single task -dotnet_diagnostic.CA1843.severity = warning - -# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream' -dotnet_diagnostic.CA1844.severity = warning - -# CA1846: Prefer 'AsSpan' over 'Substring' -dotnet_diagnostic.CA1846.severity = warning - -# CA1847: Use char literal for a single character lookup -dotnet_diagnostic.CA1847.severity = warning - -# CA1848: Use the LoggerMessage delegates -dotnet_diagnostic.CA1848.severity = warning - -# CA1849: Call async methods when in an async method -dotnet_diagnostic.CA1849.severity = warning - -# CA1850: Prefer static 'HashData' method over 'ComputeHash' -dotnet_diagnostic.CA1850.severity = warning - -# CA1852: Seal internal types -dotnet_diagnostic.CA1852.severity = warning - -# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)' -dotnet_diagnostic.CA1853.severity = warning - -# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method -dotnet_diagnostic.CA1854.severity = warning - -# CA1858: Use 'StartsWith' instead of 'IndexOf' -dotnet_diagnostic.CA1858.severity = warning - -# CA1859: Use concrete types when possible for improved performance -dotnet_diagnostic.CA1859.severity = warning - -# CA1860: Avoid using 'Enumerable.Any()' extension method -dotnet_diagnostic.CA1860.severity = warning - -# CA1861: Avoid constant arrays as arguments -dotnet_diagnostic.CA1861.severity = warning - -# CA1862: Use the 'StringComparison' method overloads to perform case-insensitive string comparisons -dotnet_diagnostic.CA1862.severity = warning - -# CA1863: Use 'CompositeFormat' -dotnet_diagnostic.CA1863.severity = warning - -# CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method -dotnet_diagnostic.CA1864.severity = warning - -# CA1868: Unnecessary call to 'Contains(item)' -dotnet_diagnostic.CA1868.severity = warning - -# CA1869: Cache and reuse 'JsonSerializerOptions' instances -dotnet_diagnostic.CA1869.severity = warning - -# CA2000: Dispose objects before losing scope -dotnet_diagnostic.CA2000.severity = warning - -# CA2002: Do not lock on objects with weak identity -dotnet_diagnostic.CA2002.severity = warning - -# CA2007: Consider calling ConfigureAwait on the awaited task -dotnet_diagnostic.CA2007.severity = warning - -# CA2008: Do not create tasks without passing a TaskScheduler -dotnet_diagnostic.CA2008.severity = warning - -# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value -dotnet_diagnostic.CA2009.severity = warning - -# CA2011: Avoid infinite recursion -dotnet_diagnostic.CA2011.severity = warning - -# CA2012: Use ValueTasks correctly -dotnet_diagnostic.CA2012.severity = warning - -# CA2013: Do not use ReferenceEquals with value types -dotnet_diagnostic.CA2013.severity = warning - -# CA2015: Do not define finalizers for types derived from MemoryManager -dotnet_diagnostic.CA2015.severity = warning - -# CA2017: Parameter count mismatch -dotnet_diagnostic.CA2017.severity = warning - -# CA2018: 'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument -dotnet_diagnostic.CA2018.severity = warning - -# CA2019: Improper 'ThreadStatic' field initialization -dotnet_diagnostic.CA2019.severity = warning - -# CA2021: Do not call Enumerable.Cast or Enumerable.OfType with incompatible types -dotnet_diagnostic.CA2021.severity = warning - -# CA2100: Review SQL queries for security vulnerabilities -dotnet_diagnostic.CA2100.severity = warning - -# CA2101: Specify marshaling for P/Invoke string arguments -dotnet_diagnostic.CA2101.severity = warning - -# CA2119: Seal methods that satisfy private interfaces -dotnet_diagnostic.CA2119.severity = warning - -# CA2153: Do Not Catch Corrupted State Exceptions -dotnet_diagnostic.CA2153.severity = warning - -# CA2200: Rethrow to preserve stack details -dotnet_diagnostic.CA2200.severity = warning - -# CA2201: Do not raise reserved exception types -dotnet_diagnostic.CA2201.severity = warning - -# CA2207: Initialize value type static fields inline -dotnet_diagnostic.CA2207.severity = warning - -# CA2208: Instantiate argument exceptions correctly -dotnet_diagnostic.CA2208.severity = warning - -# CA2211: Non-constant fields should not be visible -dotnet_diagnostic.CA2211.severity = warning - -# CA2213: Disposable fields should be disposed -dotnet_diagnostic.CA2213.severity = warning - -# CA2214: Do not call overridable methods in constructors -dotnet_diagnostic.CA2214.severity = warning - -# CA2215: Dispose methods should call base class dispose -dotnet_diagnostic.CA2215.severity = warning - -# CA2216: Disposable types should declare finalizer -dotnet_diagnostic.CA2216.severity = warning - -# CA2217: Do not mark enums with FlagsAttribute -dotnet_diagnostic.CA2217.severity = warning - -# CA2219: Do not raise exceptions in finally clauses -dotnet_diagnostic.CA2219.severity = warning - -# CA2225: Operator overloads have named alternates -dotnet_diagnostic.CA2225.severity = warning - -# CA2226: Operators should have symmetrical overloads -dotnet_diagnostic.CA2226.severity = warning - -# CA2227: Collection properties should be read only -dotnet_diagnostic.CA2227.severity = warning - -# CA2231: Overload operator equals on overriding value type Equals -dotnet_diagnostic.CA2231.severity = warning - -# CA2235: Mark all non-serializable fields -dotnet_diagnostic.CA2235.severity = warning - -# CA2237: Mark ISerializable types with serializable -dotnet_diagnostic.CA2237.severity = warning - -# CA2241: Provide correct arguments to formatting methods -dotnet_diagnostic.CA2241.severity = warning - -# CA2242: Test for NaN correctly -dotnet_diagnostic.CA2242.severity = warning - -# CA2243: Attribute string literals should parse correctly -dotnet_diagnostic.CA2243.severity = warning - -# CA2244: Do not duplicate indexed element initializations -dotnet_diagnostic.CA2244.severity = warning - -# CA2245: Do not assign a property to itself -dotnet_diagnostic.CA2245.severity = warning - -# CA2246: Assigning symbol and its member in the same statement -dotnet_diagnostic.CA2246.severity = warning - -# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum -dotnet_diagnostic.CA2247.severity = warning - -# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag' -dotnet_diagnostic.CA2248.severity = warning - -# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf' -dotnet_diagnostic.CA2249.severity = warning - -# CA2250: Use 'ThrowIfCancellationRequested' -dotnet_diagnostic.CA2250.severity = warning - -# CA2251: Use 'string.Equals' -dotnet_diagnostic.CA2251.severity = warning - -# CA2253: Named placeholders should not be numeric values -dotnet_diagnostic.CA2253.severity = warning - -# CA2254: Template should be a static expression -dotnet_diagnostic.CA2254.severity = warning - -# CA2255: The 'ModuleInitializer' attribute should not be used in libraries -dotnet_diagnostic.CA2255.severity = warning - -# CA2256: All members declared in parent interfaces must have an implementation in a DynamicInterfaceCastableImplementation-attributed interface -dotnet_diagnostic.CA2256.severity = warning - -# CA2257: Members defined on an interface with the 'DynamicInterfaceCastableImplementationAttribute' should be 'static' -dotnet_diagnostic.CA2257.severity = warning - -# CA2258: Providing a 'DynamicInterfaceCastableImplementation' interface in Visual Basic is unsupported -dotnet_diagnostic.CA2258.severity = warning - -# CA2259: 'ThreadStatic' only affects static fields -dotnet_diagnostic.CA2259.severity = warning - -# CA2261: Do not use ConfigureAwaitOptions.SuppressThrowing with Task -dotnet_diagnostic.CA2261.severity = warning - -# CA2300: Do not use insecure deserializer BinaryFormatter -dotnet_diagnostic.CA2300.severity = warning - -# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder -dotnet_diagnostic.CA2301.severity = warning - -# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize -dotnet_diagnostic.CA2302.severity = warning - -# CA2305: Do not use insecure deserializer LosFormatter -dotnet_diagnostic.CA2305.severity = warning - -# CA2310: Do not use insecure deserializer NetDataContractSerializer -dotnet_diagnostic.CA2310.severity = warning - -# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder -dotnet_diagnostic.CA2311.severity = warning - -# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing -dotnet_diagnostic.CA2312.severity = warning - -# CA2315: Do not use insecure deserializer ObjectStateFormatter -dotnet_diagnostic.CA2315.severity = warning - -# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver -dotnet_diagnostic.CA2321.severity = warning - -# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing -dotnet_diagnostic.CA2322.severity = warning - -# CA2326: Do not use TypeNameHandling values other than None -dotnet_diagnostic.CA2326.severity = warning - -# CA2327: Do not use insecure JsonSerializerSettings -dotnet_diagnostic.CA2327.severity = warning - -# CA2328: Ensure that JsonSerializerSettings are secure -dotnet_diagnostic.CA2328.severity = warning - -# CA2329: Do not deserialize with JsonSerializer using an insecure configuration -dotnet_diagnostic.CA2329.severity = warning - -# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing -dotnet_diagnostic.CA2330.severity = warning - -# CA2350: Do not use DataTable.ReadXml() with untrusted data -dotnet_diagnostic.CA2350.severity = warning - -# CA2351: Do not use DataSet.ReadXml() with untrusted data -dotnet_diagnostic.CA2351.severity = warning - -# CA2361: Ensure auto-generated class containing DataSet.ReadXml() is not used with untrusted data -dotnet_diagnostic.CA2361.severity = warning - -# CA3001: Review code for SQL injection vulnerabilities -dotnet_diagnostic.CA3001.severity = warning - -# CA3002: Review code for XSS vulnerabilities -dotnet_diagnostic.CA3002.severity = warning - -# CA3003: Review code for file path injection vulnerabilities -dotnet_diagnostic.CA3003.severity = warning - -# CA3004: Review code for information disclosure vulnerabilities -dotnet_diagnostic.CA3004.severity = warning - -# CA3005: Review code for LDAP injection vulnerabilities -dotnet_diagnostic.CA3005.severity = warning - -# CA3006: Review code for process command injection vulnerabilities -dotnet_diagnostic.CA3006.severity = warning - -# CA3007: Review code for open redirect vulnerabilities -dotnet_diagnostic.CA3007.severity = warning - -# CA3008: Review code for XPath injection vulnerabilities -dotnet_diagnostic.CA3008.severity = warning - -# CA3009: Review code for XML injection vulnerabilities -dotnet_diagnostic.CA3009.severity = warning - -# CA3010: Review code for XAML injection vulnerabilities -dotnet_diagnostic.CA3010.severity = warning - -# CA3011: Review code for DLL injection vulnerabilities -dotnet_diagnostic.CA3011.severity = warning - -# CA3012: Review code for regex injection vulnerabilities -dotnet_diagnostic.CA3012.severity = warning - -# CA3061: Do Not Add Schema By URL -dotnet_diagnostic.CA3061.severity = warning - -# CA3075: Insecure DTD processing in XML -dotnet_diagnostic.CA3075.severity = warning - -# CA3076: Insecure XSLT script processing -dotnet_diagnostic.CA3076.severity = warning - -# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader -dotnet_diagnostic.CA3077.severity = warning - -# CA3147: Mark Verb Handlers With Validate Antiforgery Token -dotnet_diagnostic.CA3147.severity = warning - -# CA5350: Do Not Use Weak Cryptographic Algorithms -dotnet_diagnostic.CA5350.severity = warning - -# CA5351: Do Not Use Broken Cryptographic Algorithms -dotnet_diagnostic.CA5351.severity = warning - -# CA5358: Review cipher mode usage with cryptography experts -dotnet_diagnostic.CA5358.severity = warning - -# CA5359: Do Not Disable Certificate Validation -dotnet_diagnostic.CA5359.severity = warning - -# CA5360: Do Not Call Dangerous Methods In Deserialization -dotnet_diagnostic.CA5360.severity = warning - -# CA5361: Do Not Disable SChannel Use of Strong Crypto -dotnet_diagnostic.CA5361.severity = warning - -# CA5362: Potential reference cycle in deserialized object graph -dotnet_diagnostic.CA5362.severity = warning - -# CA5363: Do Not Disable Request Validation -dotnet_diagnostic.CA5363.severity = warning - -# CA5364: Do Not Use Deprecated Security Protocols -dotnet_diagnostic.CA5364.severity = warning - -# CA5365: Do Not Disable HTTP Header Checking -dotnet_diagnostic.CA5365.severity = warning - -# CA5366: Use XmlReader for 'DataSet.ReadXml()' -dotnet_diagnostic.CA5366.severity = warning - -# CA5367: Do Not Serialize Types With Pointer Fields -dotnet_diagnostic.CA5367.severity = warning - -# CA5368: Set ViewStateUserKey For Classes Derived From Page -dotnet_diagnostic.CA5368.severity = warning - -# CA5369: Use XmlReader for 'XmlSerializer.Deserialize()' -dotnet_diagnostic.CA5369.severity = warning - -# CA5370: Use XmlReader for XmlValidatingReader constructor -dotnet_diagnostic.CA5370.severity = warning - -# CA5371: Use XmlReader for 'XmlSchema.Read()' -dotnet_diagnostic.CA5371.severity = warning - -# CA5372: Use XmlReader for XPathDocument constructor -dotnet_diagnostic.CA5372.severity = warning - -# CA5373: Do not use obsolete key derivation function -dotnet_diagnostic.CA5373.severity = warning - -# CA5374: Do Not Use XslTransform -dotnet_diagnostic.CA5374.severity = warning - -# CA5375: Do Not Use Account Shared Access Signature -dotnet_diagnostic.CA5375.severity = warning - -# CA5376: Use SharedAccessProtocol HttpsOnly -dotnet_diagnostic.CA5376.severity = warning - -# CA5377: Use Container Level Access Policy -dotnet_diagnostic.CA5377.severity = warning - -# CA5378: Do not disable ServicePointManagerSecurityProtocols -dotnet_diagnostic.CA5378.severity = warning - -# CA5379: Ensure Key Derivation Function algorithm is sufficiently strong -dotnet_diagnostic.CA5379.severity = warning - -# CA5380: Do Not Add Certificates To Root Store -dotnet_diagnostic.CA5380.severity = warning - -# CA5381: Ensure Certificates Are Not Added To Root Store -dotnet_diagnostic.CA5381.severity = warning - -# CA5382: Use Secure Cookies In ASP.NET Core -dotnet_diagnostic.CA5382.severity = warning - -# CA5383: Ensure Use Secure Cookies In ASP.NET Core -dotnet_diagnostic.CA5383.severity = warning - -# CA5384: Do Not Use Digital Signature Algorithm (DSA) -dotnet_diagnostic.CA5384.severity = warning - -# CA5385: Use Rivest-Shamir-Adleman (RSA) Algorithm With Sufficient Key Size -dotnet_diagnostic.CA5385.severity = warning - -# CA5386: Avoid hardcoding SecurityProtocolType value -dotnet_diagnostic.CA5386.severity = warning - -# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count -dotnet_diagnostic.CA5387.severity = warning - -# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function -dotnet_diagnostic.CA5388.severity = warning - -# CA5389: Do Not Add Archive Item's Path To The Target File System Path -dotnet_diagnostic.CA5389.severity = warning - -# CA5390: Do not hard-code encryption key -dotnet_diagnostic.CA5390.severity = warning - -# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers -dotnet_diagnostic.CA5391.severity = warning - -# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes -dotnet_diagnostic.CA5392.severity = warning - -# CA5393: Do not use unsafe DllImportSearchPath value -dotnet_diagnostic.CA5393.severity = warning - -# CA5394: Do not use insecure randomness -dotnet_diagnostic.CA5394.severity = warning - -# CA5395: Miss HttpVerb attribute for action methods -dotnet_diagnostic.CA5395.severity = warning - -# CA5396: Set HttpOnly to true for HttpCookie -dotnet_diagnostic.CA5396.severity = warning - -# CA5397: Do not use deprecated SslProtocols values -dotnet_diagnostic.CA5397.severity = warning - -# CA5398: Avoid hardcoded SslProtocols values -dotnet_diagnostic.CA5398.severity = warning - -# CA5399: HttpClients should enable certificate revocation list checks -dotnet_diagnostic.CA5399.severity = warning - -# CA5400: Ensure HttpClient certificate revocation list check is not disabled -dotnet_diagnostic.CA5400.severity = warning - -# CA5401: Do not use CreateEncryptor with non-default IV -dotnet_diagnostic.CA5401.severity = warning - -# CA5402: Use CreateEncryptor with the default IV -dotnet_diagnostic.CA5402.severity = warning - -# CA5403: Do not hard-code certificate -dotnet_diagnostic.CA5403.severity = warning - -# CA5404: Do not disable token validation checks -dotnet_diagnostic.CA5404.severity = warning - -# CA5405: Do not always skip token validation in delegates -dotnet_diagnostic.CA5405.severity = warning - -# CA1032: Implement standard exception constructors -dotnet_diagnostic.CA1032.severity = warning - -# CA1200: Avoid using cref tags with a prefix -dotnet_diagnostic.CA1200.severity = warning - -# CA1309: Use ordinal string comparison -dotnet_diagnostic.CA1309.severity = warning - -# CA1311: Specify a culture or use an invariant version -dotnet_diagnostic.CA1311.severity = warning - -# CA1507: Use nameof to express symbol names -dotnet_diagnostic.CA1507.severity = warning - -# CA1508: Avoid dead conditional code -dotnet_diagnostic.CA1508.severity = warning - -# CA1802: Use literals where appropriate -dotnet_diagnostic.CA1802.severity = warning - -# CA1805: Do not initialize unnecessarily -dotnet_diagnostic.CA1805.severity = warning - -# CA1812: Avoid uninstantiated internal classes -dotnet_diagnostic.CA1812.severity = warning - -# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute -dotnet_diagnostic.CA1824.severity = warning - -# CA1825: Avoid zero-length array allocations -dotnet_diagnostic.CA1825.severity = warning - -# CA1841: Prefer Dictionary.Contains methods -dotnet_diagnostic.CA1841.severity = warning - -# CA1845: Use span-based 'string.Concat' -dotnet_diagnostic.CA1845.severity = warning - -# CA1851: Possible multiple enumerations of 'IEnumerable' collection -dotnet_diagnostic.CA1851.severity = warning - -# CA1855: Prefer 'Clear' over 'Fill' -dotnet_diagnostic.CA1855.severity = warning - -# CA1856: Incorrect usage of ConstantExpected attribute -dotnet_diagnostic.CA1856.severity = warning - -# CA1857: A constant is expected for the parameter -dotnet_diagnostic.CA1857.severity = warning - -# CA1865: Use char overload -dotnet_diagnostic.CA1865.severity = warning - -# CA1866: Use char overload -dotnet_diagnostic.CA1866.severity = warning - -# CA1867: Use char overload -dotnet_diagnostic.CA1867.severity = warning - -# CA1870: Use a cached 'SearchValues' instance -dotnet_diagnostic.CA1870.severity = warning - -# CA2014: Do not use stackalloc in loops -dotnet_diagnostic.CA2014.severity = warning - -# CA2016: Forward the 'CancellationToken' parameter to methods -dotnet_diagnostic.CA2016.severity = warning - -# CA2020: Prevent behavioral change -dotnet_diagnostic.CA2020.severity = warning - -# CA2234: Pass system uri objects instead of strings -dotnet_diagnostic.CA2234.severity = warning - -# CA2252: This API requires opting into preview features -dotnet_diagnostic.CA2252.severity = warning - -# CA2260: Use correct type parameter -dotnet_diagnostic.CA2260.severity = warning - -# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks -dotnet_diagnostic.CA2352.severity = warning - -# CA2353: Unsafe DataSet or DataTable in serializable type -dotnet_diagnostic.CA2353.severity = warning - -# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks -dotnet_diagnostic.CA2354.severity = warning - -# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph -dotnet_diagnostic.CA2355.severity = warning - -# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph -dotnet_diagnostic.CA2356.severity = warning - -# CA2362: Unsafe DataSet or DataTable in auto-generated serializable type can be vulnerable to remote code execution attacks -dotnet_diagnostic.CA2362.severity = warning diff --git a/.forgejo/workflows/lint-build.yml b/.forgejo/workflows/lint-build.yml deleted file mode 100644 index ce8b798..0000000 --- a/.forgejo/workflows/lint-build.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: lint-build -on: - push: - pull_request: - -jobs: - lint-build: - name: dotnet${{ matrix.dotnet }} - runs-on: docker - container: git.frostfs.info/truecloudlab/env:dotnet-${{ matrix.dotnet }} - strategy: - matrix: - dotnet: - - '8.0' - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - # Dotnet runs code analyzers on build (if configured): - # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview?tabs=net-8#enable-on-build - - run: dotnet build diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml deleted file mode 100644 index fd79900..0000000 --- a/.forgejo/workflows/publish.yml +++ /dev/null @@ -1,35 +0,0 @@ -on: - workflow_dispatch: - -jobs: - image: - name: Publish NuGet packages - runs-on: docker - container: git.frostfs.info/truecloudlab/env:dotnet-8.0 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Build NuGet packages - # `dotnet build` implies and replaces `dotnet pack` thanks to `GeneratePackageOnBuild` - run: dotnet build - - - name: Publish unsigned NuGet packages - run: |- - dotnet nuget add source \ - --name "$NUGET_REGISTRY" \ - --username "$NUGET_REGISTRY_USER" \ - --password "$NUGET_REGISTRY_PASSWORD" \ - --store-password-in-clear-text \ - "$NUGET_REGISTRY_URL" - find -iname '*.nupkg' | grep . | xargs -d'\n' -t -n1 \ - dotnet nuget push --source "$NUGET_REGISTRY" - env: - NUGET_REGISTRY: TrueCloudLab - NUGET_REGISTRY_URL: https://git.frostfs.info/api/packages/TrueCloudLab/nuget/index.json - NUGET_REGISTRY_USER: ${{secrets.NUGET_REGISTRY_USER}} - NUGET_REGISTRY_PASSWORD: ${{secrets.NUGET_REGISTRY_PASSWORD}} - if: >- - startsWith(github.ref, 'refs/tags/v') && - (github.event_name == 'workflow_dispatch' || github.event_name == 'push') diff --git a/.gitignore b/.gitignore index 449284e..cf6e05a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ vendor/ # IDE .idea .vscode -.vs # coverage coverage.txt @@ -32,8 +31,5 @@ antlr-*.jar # binary bin/ +release/ obj/ - -# Repository signing keys -release/maintainer.* -release/ca.* diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 03bb5e4..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1,3 +0,0 @@ -.* @PavelGrossSpb -.forgejo/.* @potyarkin -Makefile @potyarkin diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln index b96bda2..d7d789e 100644 --- a/FrostFS.SDK.sln +++ b/FrostFS.SDK.sln @@ -1,20 +1,11 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Client", "src\FrostFS.SDK.Client\FrostFS.SDK.Client.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}" +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Protos", "src\FrostFS.SDK.Protos\FrostFS.SDK.Protos.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ModelsV2", "src\FrostFS.SDK.ModelsV2\FrostFS.SDK.ModelsV2.csproj", "{14DC8AC7-E310-40C2-ACDF-5BE78FC0E1B2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F030ACD-F87C-4E83-9A68-4CC5DF03AD90}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -30,16 +21,17 @@ Global {3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Release|Any CPU.Build.0 = Release|Any CPU + {14DC8AC7-E310-40C2-ACDF-5BE78FC0E1B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14DC8AC7-E310-40C2-ACDF-5BE78FC0E1B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14DC8AC7-E310-40C2-ACDF-5BE78FC0E1B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14DC8AC7-E310-40C2-ACDF-5BE78FC0E1B2}.Release|Any CPU.Build.0 = Release|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU - {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + {B738F3E1-654D-41A3-B068-58ED122BB688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B738F3E1-654D-41A3-B068-58ED122BB688}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B738F3E1-654D-41A3-B068-58ED122BB688}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B738F3E1-654D-41A3-B068-58ED122BB688}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Makefile b/Makefile deleted file mode 100644 index f02478e..0000000 --- a/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -DOTNET?=dotnet -DOCKER?=docker - -NUGET_REGISTRY?=TrueCloudLab -NUGET_REGISTRY_URL?=https://git.frostfs.info/api/packages/TrueCloudLab/nuget/index.json -NUGET_REGISTRY_USER?= -NUGET_REGISTRY_PASSWORD?= - -NUPKG=find -iname '*.nupkg' | grep . | xargs -d'\n' -t -r -n1 -RFC3161_TSA?=http://timestamp.digicert.com - - -.PHONY: build -build: - $(DOTNET) build - - -.PHONY: sign -sign: export NUGET_CERT_REVOCATION_MODE=offline -sign: release/maintainer.pfx - $(NUPKG) $(DOTNET) nuget sign --overwrite --certificate-path $< --timestamper "$(RFC3161_TSA)" - @rm -v "$<" # maintainer.pfx is not password protected and must be ephemeral - $(NUPKG) $(DOTNET) nuget verify - - -.PHONY: publish -publish: - $(NUPKG) $(DOTNET) nuget verify - $(NUPKG) $(DOTNET) nuget push --source "$(NUGET_REGISTRY)" - - -.PHONY: nuget-registry -nuget-registry: -ifeq (,$(NUGET_REGISTRY_USER)) - $(error NUGET_REGISTRY_USER not set) -endif -ifeq (,$(NUGET_REGISTRY_PASSWORD)) - $(error NUGET_REGISTRY_PASSWORD not set) -endif - $(DOTNET) nuget add source \ - --name "$(NUGET_REGISTRY)" \ - --username "$(NUGET_REGISTRY_USER)" \ - --password "$(NUGET_REGISTRY_PASSWORD)" \ - --store-password-in-clear-text \ - "$(NUGET_REGISTRY_URL)" - - -.PHONY: clean -clean: - -$(NUPKG) rm -v - - -.PHONY: container -container: - $(DOCKER) run --pull=always --rm -it -v "$$PWD:/src" -w /src git.frostfs.info/truecloudlab/env:dotnet-8.0 - - -include release/codesign.mk diff --git a/README.md b/README.md index b7738b7..832b42f 100644 --- a/README.md +++ b/README.md @@ -21,62 +21,60 @@ neo-go wallet export -w -d ### Container ```csharp -using FrostFS.SDK; using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; -using Microsoft.Extensions.Options; +var fsClient = new Client(, ); -var Key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; -var Host = "http://172.22.33.44:8080"; +// List containers +var containersIds = await fsClient.ListContainersAsync(); -var options = Options.Create(new SingleOwnerClientSettings -{ - Key = Key, - Host = Host -}); +// Create container +var placementPolicy = new PlacementPolicy(true, new Replica(1)); +var containerId = await fsClient.CreateContainerAsync( + new Container( + BasicAcl.PublicRW, + placementPolicy + ) +); -using var client = Client.GetSingleOwnerInstance(options); +// Get container +var container = await fsClient.GetContainerAsync(cId); -await foreach (var cid in client.ListContainersAsync()) -{ - await client.DeleteContainerAsync(new PrmContainerDelete(cid)); -} - -var placementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)); - -var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))); - -var containerId = await client.PutContainerAsync(createContainerParam); - -using var fileStream = File.OpenRead(@"cat.jpeg"); - -var param = new PrmObjectPut -{ - Header = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttribute("fileName", "test")]), - Payload = fileStream -}; - -FrostFsObjectId objectId = await client.PutObjectAsync(param); - -var filter = new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test"); - -await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter] })) -{ - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objId)); -} - -var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId)); - -var downloadedBytes = new byte[@object.Header.PayloadLength]; -MemoryStream ms = new(downloadedBytes); - -ReadOnlyMemory? chunk = null; -while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) -{ - ms.Write(chunk.Value.Span); -} +// Delete container +await fsClient.DeleteContainerAsync(containerId); ``` + +### Object + +```csharp +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; + +var fsClient = new Client(, ); + +// Search regular objects +var objectsIds = await fsClient.SearchObjectAsync( + cId, + ObjectFilter.RootFilter() +); + +// Put object +var f = File.OpenRead("cat.jpg"); +var cat = new ObjectHeader( + containerId: cId, + type: ObjectType.Regular, + new ObjectAttribute("Filename", "cat.jpg") +); +var oId = await fsClient.PutObjectAsync(cat, f); + +// Get object header +var objHeader = await fsClient.GetObjectHeadAsync(cId, oId); + +// Get object +var obj = await fsClient.GetObjectAsync(cId, oId); +``` \ No newline at end of file diff --git a/keyfile.snk b/keyfile.snk deleted file mode 100644 index a14537b..0000000 Binary files a/keyfile.snk and /dev/null differ diff --git a/release/README.md b/release/README.md deleted file mode 100644 index 96f8c25..0000000 --- a/release/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Release process - -## Preparing release - -_TBD_ - -## Trusting TrueCloudLab code signing CA certificate - -Verifying signatures (and signing) TrueCloudLab packages requires adding -[TrueCloudLab Code Signing CA](ca.cert) to the list of trusted roots. - -On Linux this can be done by appending [release/ca.cert](ca.cert) to one of: - -- `/etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem`: compatible with - [update-ca-trust] and originally proposed in [.NET design docs] -- `…/dotnet/sdk/X.Y.ZZZ/trustedroots/codesignctl.pem`: [fallback] codesigning certificate trust list for .NET - -[update-ca-trust]: https://www.linux.org/docs/man8/update-ca-trust.html -[.NET design docs]: https://github.com/dotnet/designs/blob/main/accepted/2021/signed-package-verification/re-enable-signed-package-verification-technical.md#linux -[fallback]: https://github.com/dotnet/sdk/blob/11150c0ec9020625308edeec555a8b78dbfb2aa5/src/Layout/redist/trustedroots/README.md - -## Signing Nuget packages - -Repository maintainer places `maintainer.cert` and `maintainer.key` (see below -regarding obtaining these files) into `release/` directory and then -executes: - -```console -$ make build sign -``` - -## Uploading packages to Nuget registry - -**IMPORTANT: the following steps upload all `*.nupkg` files located under -`src/`. Maintainer MUST make sure that no unnecessary package versions will be -uploaded to the registry.** - -Configure registry credentials (once per machine): - -```console -$ make nuget-registry NUGET_REGISTRY_USER=username NUGET_REGISTRY_PASSWORD=token -``` - -Publish all locally built packages (implicitly clear existing `*.nupkg` and -rebuild current version only): - -```console -$ make clean build sign publish -``` - - -## Obtaining release signing certificate - -Repository maintainer owns and keeps safe the release signing key -(`maintainer.key`). Private key should never leave maintainer's machine and -should be considered a highly sensitive secret. - -- Generating new maintainer key and the corresponding CSR: - - ```console - $ make maintainer.csr - ...lines skipped... - Enter PEM pass phrase: - Verifying - Enter PEM pass phrase: - ----- - IMPORTANT: Keep maintainer.key private! - - Certificate signing request is ready. - Send maintainer.csr to CA administrator to obtain the certificate. - ``` - - Resulting CSR (`maintainer.csr`) does not contain any sensitive - cryptographic material and may be passed to CA administrator through regular - communication channels. - -- CA administrator then issues the certificate (`make maintainer.cert`) and - sends it back to the maintainer to be used in combination with - `maintainer.key` - -This procedure should be repeated once per machine per `maintainer.cert` -lifetime (1 year) - typically just once per year since we expect the -maintainer to use only a single computer to sign releases. diff --git a/release/ca.cert b/release/ca.cert deleted file mode 100644 index 4f4c2f4..0000000 --- a/release/ca.cert +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2zCCA8OgAwIBAgIUa9xC/RgvFtUG/xeR016nn0B4K0YwDQYJKoZIhvcNAQEL -BQAwdTELMAkGA1UEBhMCUlUxFTATBgNVBAoMDFRydWVDbG91ZExhYjEVMBMGA1UE -CwwMVHJ1ZUNsb3VkTGFiMTgwNgYDVQQDDC9UcnVlQ2xvdWRMYWIgQ29kZSBTaWdu -aW5nIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yNTA0MTAxNTI2MTFaFw0zNTA0 -MDgxNTI2MTFaMHUxCzAJBgNVBAYTAlJVMRUwEwYDVQQKDAxUcnVlQ2xvdWRMYWIx -FTATBgNVBAsMDFRydWVDbG91ZExhYjE4MDYGA1UEAwwvVHJ1ZUNsb3VkTGFiIENv -ZGUgU2lnbmluZyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCyANB4cjf+ZEAFx9RiUYXCAOPMV+jyqgcVbhzh2YKc -9SlvGKRlc00Ar1RlFcrycdkIrTKmhhobiWsFp7UgphwLqRTCb5NB6qfUoWhnfiD9 -m0OBgeVX5wivVaibRI9PSTbFDcIhYUiNvwFJ6GduH/9zOxf1BvuL7LMaoyhIDcg/ -XVLuekE2lnX83zsedv0v/2jyyMY9Ct6N2BXzyHSAzSdYYg0F9Qu9fIMAPjoKhWPc -PnotqaACjb1DScLUr3E/o2W1FfprTT2Pip/0AXxO4wixl4QWh9HeOKV22KRcCHo6 -3wNdg5q1ZVGTNBW0+yoB4jsSG8/JM+2Ujhc1ZnYH10armvGq/0Oc2YQE00960Wy8 -t0drCFWJUO1XHNeBxkkupmj7N1TAPbixtfiGZJhECOWOJwyMpcKixlt5P0cNH4N/ -p3vjyrGQxGLBIkgV/QgjfGkpTHKT1/H40YK6DliWJc01KfNTqn0K+/TIyF0n26kD -BWYVlvDh5P1+V9DGuD2zeXB3PstoifD/Pd7D8wuqpm17noFE19MLp94xv03q9nEa -jRMEd2J2buTLvMh5BBVH0Sm38QAHpSIZ9O3dSLvvjlALbVtwmcsNE9fgxiue3vTB -iXNW8wWs+/DMYwbWyBoCwORxVOdOyc1JLn7qAAEUBweilPVXpWuzMLdUsifPiqrV -dQIDAQABo2MwYTAdBgNVHQ4EFgQUEz4y/RvQMmbUFvf5JbGe/0PZR90wHwYDVR0j -BBgwFoAUEz4y/RvQMmbUFvf5JbGe/0PZR90wDwYDVR0TAQH/BAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADggIBAF79W9hMGnrKUWLDOcoeXDEn -+gZxd5rjeF0tNEQGbWKGTJAGQdprOkzWQ47PewpFeS+ePpjOglBAt7cV2ZnOugAT -Brx31vYpNAvYnUCYn+/IUqD8S/U4uErVS9zG9QjirZWo56eJP62vnScKuApCQCbA -pp0zrIyJ+2lQKzlMOENRqzYYA/UTOqYTtnW6x2D8goVqCmDXpgpxEp5XliFjJSr6 -dOjiopNWMvaV3R/Bnd4i41taM7M6HpIV+gbXmEKEFS0ejVfzT8z1pTigN7GBqbxf -nXD03eLUIsbMDv4ZQPrheN7nKnjRUn8kxz0SSK1m2YDrXW51m8fOs6aTvwC/cNe+ -FJMqQMF32i4IXVfUbyUJi+JMvawqm2wEY46vrh7siprY6rXsAzCKJo07i6jvUwnT -TXMldSCPgTEqzT2JBzzr0tRfuPKsv0/NqflHvwfuMRCpcZ7jJZ700iN92xXkiQHP -DmCZOILXcNclAth3nAnyY4XE5a8myv8bwYaPdJdIFlV+BoU/8mClDeA8ck4rDy12 -T5YChKew2oiL4j4B6v9/yrDjD1IT0gv4BWyPhb/n390BCEXt8g9auNcT0s6O8kEc -VUDVc1519ocMCuWVuqUK9o2w0zu50/pBn4hVLfT3QyW8sqtlRKghOWtqZzigvCWF -VjATeO5F/Z7OSDebHUGv ------END CERTIFICATE----- diff --git a/release/codesign.mk b/release/codesign.mk deleted file mode 100644 index 1c39361..0000000 --- a/release/codesign.mk +++ /dev/null @@ -1,74 +0,0 @@ -PKI_ROLE?=maintainer -PKI_DIR?=release - -# Note: Only RSA signatures are supported (NU3013) -# https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3013) - - -ifeq ($(PKI_ROLE),maintainer) -.PHONY: maintainer.csr -maintainer.csr: $(PKI_DIR)/maintainer.csr -$(PKI_DIR)/maintainer.csr: KEY=$(patsubst %.csr,%.key,$@) -$(PKI_DIR)/maintainer.csr: - openssl req \ - -new \ - -newkey rsa:4096 \ - -keyout $(KEY) \ - -out $@ \ - -sha256 \ - -addext keyUsage=critical,digitalSignature \ - -addext extendedKeyUsage=critical,codeSigning,msCodeCom \ - -subj "/C=RU/O=TrueCloudLab/OU=TrueCloudLab/CN=frostfs-sdk-csharp Release Team" - @echo "IMPORTANT: Keep $(KEY) private!\n" - @echo "Certificate signing request is ready.\nSend $@ to CA administrator to obtain the certificate." - -$(PKI_DIR)/maintainer.pfx: $(PKI_DIR)/maintainer.cert $(PKI_DIR)/maintainer.key $(PKI_DIR)/ca.cert - openssl verify \ - -CAfile $(PKI_DIR)/ca.cert \ - $(PKI_DIR)/maintainer.cert - openssl pkcs12 \ - -export \ - -out $@ \ - -inkey $(PKI_DIR)/maintainer.key \ - -in $(PKI_DIR)/maintainer.cert \ - -CAfile $(PKI_DIR)/ca.cert \ - -chain \ - -passout pass: -endif - - -ifeq ($(PKI_ROLE),ca) -.PHONY: maintainer.cert -maintainer.cert: $(PKI_DIR)/maintainer.cert -$(PKI_DIR)/maintainer.cert: CSR=$(patsubst %.cert,%.csr,$@) -$(PKI_DIR)/maintainer.cert: $(PKI_DIR)/ca.key $(PKI_DIR)/ca.cert - openssl req -noout -text -in $(CSR) - @read -p "Review the CSR above. Press Enter to continue, Ctrl+C to cancel " -r null - openssl x509 \ - -req \ - -days 365 \ - -in $(CSR) \ - -copy_extensions copy \ - -ext keyUsage,extendedKeyUsage \ - -CA $(PKI_DIR)/ca.cert \ - -CAkey $(PKI_DIR)/ca.key \ - -CAcreateserial \ - -out $@ - echo >> $@ - cat $(PKI_DIR)/ca.cert >> $@ - openssl x509 -noout -text -in $@ -fingerprint -sha256 - @echo "Certificate is ready.\nSend $@ back to maintainer." - -$(PKI_DIR)/ca.key: CERT=$(patsubst %.key,%.cert,$@) -$(PKI_DIR)/ca.key: - openssl req \ - -x509 \ - -newkey rsa:4096 \ - -keyout $@ \ - -out $(CERT) \ - -sha256 \ - -days 3650 \ - -addext keyUsage=critical,keyCertSign \ - -subj "/C=RU/O=TrueCloudLab/OU=TrueCloudLab/CN=TrueCloudLab Code Signing Certificate Authority" - @echo "IMPORTANT: Keep $@ private!\n" -endif diff --git a/src/FrostFS.SDK.Client/ApeRules/Actions.cs b/src/FrostFS.SDK.Client/ApeRules/Actions.cs deleted file mode 100644 index 8bae303..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Actions.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FrostFS.SDK.Client; - -public struct Actions(bool inverted, string[] names) : System.IEquatable -{ - public bool Inverted { get; set; } = inverted; - - public string[] Names { get; set; } = names; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not Actions) - return false; - - return Equals((Actions)obj); - } - - public override readonly int GetHashCode() - { - return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode(); - } - - public static bool operator ==(Actions left, Actions right) - { - return left.Equals(right); - } - - public static bool operator !=(Actions left, Actions right) - { - return !(left == right); - } - - public readonly bool Equals(Actions other) - { - return this.GetHashCode().Equals(other.GetHashCode()); - } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/ChainTarget.cs b/src/FrostFS.SDK.Client/ApeRules/ChainTarget.cs deleted file mode 100644 index 89b0d8a..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/ChainTarget.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; - -using Frostfs.V2.Ape; - -namespace FrostFS.SDK.Client; - -public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatable -{ - private ChainTarget? chainTarget; - - public FrostFsTargetType Type { get; } = type; - - public string Name { get; } = name; - - internal ChainTarget GetChainTarget() - { - return chainTarget ??= new ChainTarget - { - Type = GetTargetType(Type), - Name = Name - }; - } - - private static TargetType GetTargetType(FrostFsTargetType type) - { - return type switch - { - FrostFsTargetType.Undefined => TargetType.Undefined, - FrostFsTargetType.Namespace => TargetType.Namespace, - FrostFsTargetType.Container => TargetType.Container, - FrostFsTargetType.User => TargetType.User, - FrostFsTargetType.Group => TargetType.Group, - _ => throw new ArgumentException("Unexpected value for TargetType", nameof(type)), - }; - } - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not FrostFsChainTarget) - return false; - - return Equals((FrostFsChainTarget)obj); - } - public readonly bool Equals(FrostFsChainTarget other) - { - return Type == other.Type && Name.Equals(other.Name, StringComparison.Ordinal); - } - - public override readonly int GetHashCode() - { - return Name.GetHashCode() ^ (int)Type; - } - - public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right) - { - return left.Equals(right); - } - - public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Condition.cs b/src/FrostFS.SDK.Client/ApeRules/Condition.cs deleted file mode 100644 index 8b11b2b..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Condition.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace FrostFS.SDK.Client; - -public struct Condition : System.IEquatable -{ - public ConditionType Op { get; set; } - - public ConditionKindType Kind { get; set; } - - public string? Key { get; set; } - - public string? Value { get; set; } - - public override bool Equals(object obj) - { - if (obj == null || obj is not Condition) - return false; - - return Equals((Condition)obj); - } - - public override readonly int GetHashCode() - { - return Op.GetHashCode() - ^ Kind.GetHashCode() - ^ (Key != null ? Key.GetHashCode() : 0) - ^ (Value != null ? Value.GetHashCode() : 0); - } - - public static bool operator ==(Condition left, Condition right) - { - return left.Equals(right); - } - - public static bool operator !=(Condition left, Condition right) - { - return !(left == right); - } - - public readonly bool Equals(Condition other) - { - return this.GetHashCode().Equals(other.GetHashCode()); - } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs deleted file mode 100644 index fd13893..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionKindType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FrostFS.SDK.Client; - -public enum ConditionKindType -{ - Resource, - Request -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs deleted file mode 100644 index a5f6ab4..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Enums/ConditionType.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FrostFS.SDK.Client; - -public enum ConditionType -{ - CondStringEquals, - - CondStringNotEquals, - CondStringEqualsIgnoreCase, - - CondStringNotEqualsIgnoreCase, - CondStringLike, - - CondStringNotLike, - CondStringLessThan, - - CondStringLessThanEquals, - CondStringGreaterThan, - - CondStringGreaterThanEquals, - - // Numeric condition operators. - CondNumericEquals, - - CondNumericNotEquals, - CondNumericLessThan, - - CondNumericLessThanEquals, - CondNumericGreaterThan, - - CondNumericGreaterThanEquals, - - CondSliceContains, - - CondIPAddress, - CondNotIPAddress, -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs deleted file mode 100644 index 178ca63..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Enums/FrostFsTargetType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FrostFS.SDK.Client; - -public enum FrostFsTargetType -{ - Undefined, - Namespace, - Container, - User, - Group -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs b/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs deleted file mode 100644 index 547449b..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Enums/Status.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FrostFS.SDK.Client; - -public enum RuleStatus -{ - Allow, - NoRuleFound, - AccessDenied, - QuotaLimitReached -} diff --git a/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs b/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs deleted file mode 100644 index c79bd7d..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/FrostFsChain.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FrostFS.SDK.Client; - -public class FrostFsChain -{ - public byte[] ID { get; set; } = []; - - public FrostFsRule[] Rules { get; set; } = []; - - public RuleMatchType MatchType { get; set; } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs b/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs deleted file mode 100644 index 367a989..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/FrostFsRule.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace FrostFS.SDK.Client; - -public class FrostFsRule -{ - public RuleStatus Status { get; set; } - - // Actions the operation is applied to. - public Actions Actions { get; set; } - - // List of the resources the operation is applied to. - public Resources Resources { get; set; } - - // True iff individual conditions must be combined with the logical OR. - // By default AND is used, so _each_ condition must pass. - public bool Any { get; set; } - - public Condition[]? Conditions { get; set; } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/MatchType.cs b/src/FrostFS.SDK.Client/ApeRules/MatchType.cs deleted file mode 100644 index fe3305e..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/MatchType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FrostFS.SDK.Client; - -public enum RuleMatchType -{ - // DenyPriority rejects the request if any `Deny` is specified. - DenyPriority, - - // FirstMatch returns the first rule action matched to the request. - FirstMatch -} diff --git a/src/FrostFS.SDK.Client/ApeRules/Resources.cs b/src/FrostFS.SDK.Client/ApeRules/Resources.cs deleted file mode 100644 index 47ff3e1..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/Resources.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FrostFS.SDK.Client; - -public struct Resources(bool inverted, string[] names) : System.IEquatable -{ - public bool Inverted { get; set; } = inverted; - - public string[] Names { get; set; } = names; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not Resources) - return false; - - return Equals((Resources)obj); - } - - public override readonly int GetHashCode() - { - return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode(); - } - - public static bool operator ==(Resources left, Resources right) - { - return left.Equals(right); - } - - public static bool operator !=(Resources left, Resources right) - { - return !(left == right); - } - - public readonly bool Equals(Resources other) - { - return this.GetHashCode().Equals(other.GetHashCode()); - } -} diff --git a/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs b/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs deleted file mode 100644 index 24f784e..0000000 --- a/src/FrostFS.SDK.Client/ApeRules/RuleSerializer.cs +++ /dev/null @@ -1,502 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -internal static class RuleSerializer -{ - const byte Version = 0; // increase if breaking change - const int ByteSize = 1; - const int UInt8Size = ByteSize; - const int BoolSize = ByteSize; - const long NullSlice = -1; - const int NullSliceSize = 1; - const byte ByteTrue = 1; - const byte ByteFalse = 0; - - /// - /// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77 - /// - const int MaxSliceLen = 0x1000000; - - const int ChainMarshalVersion = 0; - - internal static byte[] Serialize(FrostFsChain chain) - { - int s = UInt8Size // Marshaller version - + UInt8Size // Chain version - + SliceSize(chain.ID, b => ByteSize) - + SliceSize(chain.Rules, RuleSize) - + UInt8Size; // MatchType - - byte[] buf = new byte[s]; - - int offset = UInt8Marshal(buf, 0, Version); - offset = UInt8Marshal(buf, offset, ChainMarshalVersion); - offset = SliceMarshal(buf, offset, chain.ID, ByteMarshal); - offset = SliceMarshal(buf, offset, chain.Rules, MarshalRule); - offset = UInt8Marshal(buf, offset, (byte)chain.MatchType); - - VerifyMarshal(buf, offset); - - return buf; - } - - internal static FrostFsChain Deserialize(byte[] data) - { - if (data is null) - { - throw new ArgumentNullException(nameof(data)); - } - - FrostFsChain chain = new(); - - var (offset, version) = UInt8Unmarshal(data, 0); - - if (version != Version) - { - throw new FrostFsException($"unsupported marshaller version {version}"); - } - - (offset, byte chainVersion) = UInt8Unmarshal(data, offset); - - if (chainVersion != ChainMarshalVersion) - { - throw new FrostFsException($"unsupported chain version {chainVersion}"); - } - - (offset, chain.ID) = SliceUnmarshal(data, offset, UInt8Unmarshal); - - (offset, chain.Rules) = SliceUnmarshal(data, offset, UnmarshalRule); - - (offset, var matchTypeV) = UInt8Unmarshal(data, offset); - - chain.MatchType = (RuleMatchType)matchTypeV; - - VerifyUnmarshal(data, offset); - - return chain; - } - - private static int Int64Size(long value) - { - // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c - // and - // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c - ulong ux = (ulong)value << 1; - if (value < 0) - { - ux = ~ux; - } - - int size = 0; - while (ux >= 0x80) - { - size++; - ux >>= 7; - } - - return size + 1; - } - - private static int SliceSize(T[] slice, Func sizeOf) - { - if (slice == null) - { - return NullSliceSize; - } - - // Assuming Int64Size is the size of the slice - var size = Int64Size(slice.Length); - foreach (var v in slice) - { - size += sizeOf(v); - } - - return size; - } - - private static int StringSize(string? s) - { - var len = s != null ? s.Length : 0; - return Int64Size(len) + len; - } - - private static int ActionsSize(Actions action) - { - return BoolSize // Inverted - + SliceSize(action.Names, StringSize); - } - - private static int ResourcesSize(Resources resource) - { - return BoolSize // Inverted - + SliceSize(resource.Names, StringSize); - } - - private static int ConditionSize(Condition condition) - { - return ByteSize // Op - + ByteSize // Object - + StringSize(condition.Key) - + StringSize(condition.Value); - } - - private static int RuleSize(FrostFsRule rule) - { - if (rule is null) - { - throw new ArgumentNullException(nameof(rule)); - } - - return ByteSize // Status - + ActionsSize(rule.Actions) - + ResourcesSize(rule.Resources) - + BoolSize // Any - + SliceSize(rule.Conditions!, ConditionSize); - } - - private static int UInt8Marshal(byte[] buf, int offset, byte value) - { - if (buf.Length - offset < 1) - { - throw new FrostFsException("Not enough bytes left to serialize value of type byte"); - } - - buf[offset] = value; - - return offset + 1; - } - - private static int ByteMarshal(byte[] buf, int offset, byte value) - { - return UInt8Marshal(buf, offset, value); - } - - // PutVarint encodes an int64 into buf and returns the number of bytes written. - // If the buffer is too small, PutVarint will panic. - private static int PutVarint(byte[] buf, int offset, long x) - { - var ux = (ulong)x << 1; - - if (x < 0) - { - ux = ~ux; - } - - return PutUvarint(buf, offset, ux); - } - - private static int PutUvarint(byte[] buf, int offset, ulong x) - { - while (x >= 0x80) - { - buf[offset] = (byte)(x | 0x80); - x >>= 7; - offset++; - } - - buf[offset] = (byte)x; - - return offset + 1; - } - - private static int Int64Marshal(byte[] buf, int offset, long v) - { - if (buf.Length - offset < Int64Size(v)) - { - throw new FrostFsException("Not enough bytes left to serialize value of type long"); - } - - return PutVarint(buf, offset, v); - } - - private static int SliceMarshal(byte[] buf, int offset, T[] slice, Func marshalT) - { - if (slice == null) - { - return Int64Marshal(buf, offset, NullSlice); - } - - if (slice.Length > MaxSliceLen) - { - throw new FrostFsException($"slice size if too big: {slice.Length}"); - } - - offset = Int64Marshal(buf, offset, slice.Length); - - foreach (var v in slice) - { - offset = marshalT(buf, offset, v); - } - - return offset; - } - - private static int BoolMarshal(byte[] buf, int offset, bool value) - { - return UInt8Marshal(buf, offset, value ? ByteTrue : ByteFalse); - } - - private static int StringMarshal(byte[] buf, int offset, string value) - { - if (value == null) - { - throw new FrostFsException($"string value is null"); - } - - if (value.Length > MaxSliceLen) - { - throw new FrostFsException($"string is too long: {value.Length}"); - } - - if (buf.Length - offset < Int64Size(value.Length) + value.Length) - { - throw new FrostFsException($"Not enough bytes left to serialize value of type string with length {value.Length}"); - } - - offset = Int64Marshal(buf, offset, value.Length); - - if (string.IsNullOrEmpty(value)) - { - return offset; - } - - Buffer.BlockCopy(System.Text.Encoding.UTF8.GetBytes(value), 0, buf, offset, value.Length); - - return offset + value.Length; - } - - private static int MarshalActions(byte[] buf, int offset, Actions action) - { - offset = BoolMarshal(buf, offset, action.Inverted); - - return SliceMarshal(buf, offset, action.Names, StringMarshal); - } - - private static int MarshalCondition(byte[] buf, int offset, Condition condition) - { - offset = ByteMarshal(buf, offset, (byte)condition.Op); - - offset = ByteMarshal(buf, offset, (byte)condition.Kind); - - offset = StringMarshal(buf, offset, condition.Key!); - - return StringMarshal(buf, offset, condition.Value!); - } - - private static int MarshalRule(byte[] buf, int offset, FrostFsRule rule) - { - if (rule is null) - { - throw new ArgumentNullException(nameof(rule)); - } - - offset = ByteMarshal(buf, offset, (byte)rule.Status); - - offset = MarshalActions(buf, offset, rule.Actions); - - offset = MarshalResources(buf, offset, rule.Resources); - - offset = BoolMarshal(buf, offset, rule.Any); - - return SliceMarshal(buf, offset, rule.Conditions!, MarshalCondition); - } - - private static int MarshalResources(byte[] buf, int offset, Resources resources) - { - offset = BoolMarshal(buf, offset, resources.Inverted); - - return SliceMarshal(buf, offset, resources.Names, StringMarshal); - } - - private static void VerifyMarshal(byte[] buf, int lastOffset) - { - if (buf.Length != lastOffset) - { - throw new FrostFsException("actual data size differs from expected"); - } - } - - private static (int, bool) BoolUnmarshal(byte[] buf, int offset) - { - (offset, byte val) = UInt8Unmarshal(buf, offset); - return (offset, val == ByteTrue); - } - - private static (int, string) StringUnmarshal(byte[] buf, int offset) - { - (offset, long size) = Int64Unmarshal(buf, offset); - - if (size == 0) - { - return (offset, string.Empty); - } - - if (size > MaxSliceLen) - { - throw new FrostFsException($"string is too long: '{size}'"); - } - if (size < 0) - { - throw new FrostFsException($"invalid string size: '{size}'"); - } - - if (buf.Length - offset < size) - { - throw new FrostFsException($"not enough bytes left to string value"); - } - - return (offset + (int)size, System.Text.Encoding.UTF8.GetString(buf, offset, (int)size)); - } - - private static (int, Actions) UnmarshalActions(byte[] buf, int offset) - { - Actions action = new(); - (offset, action.Inverted) = BoolUnmarshal(buf, offset); - - (offset, action.Names) = SliceUnmarshal(buf, offset, StringUnmarshal); - - return (offset, action); - } - - private static (int, Resources) UnmarshalResources(byte[] buf, int offset) - { - Resources res = new(); - - (offset, res.Inverted) = BoolUnmarshal(buf, offset); - (offset, res.Names) = SliceUnmarshal(buf, offset, StringUnmarshal); - - return (offset, res); - } - - private static (int, Condition) UnmarshalCondition(byte[] buf, int offset) - { - Condition cond = new(); - (offset, var op) = UInt8Unmarshal(buf, offset); - - cond.Op = (ConditionType)op; - - (offset, var kind) = UInt8Unmarshal(buf, offset); - - cond.Kind = (ConditionKindType)kind; - - (offset, cond.Key) = StringUnmarshal(buf, offset); - - (offset, cond.Value) = StringUnmarshal(buf, offset); - - return (offset, cond); - } - - private static (int, FrostFsRule) UnmarshalRule(byte[] buf, int offset) - { - FrostFsRule rule = new(); - - (offset, byte statusV) = UInt8Unmarshal(buf, offset); - rule.Status = (RuleStatus)statusV; - - (offset, rule.Actions) = UnmarshalActions(buf, offset); - - (offset, rule.Resources) = UnmarshalResources(buf, offset); - - (offset, rule.Any) = BoolUnmarshal(buf, offset); - - (offset, rule.Conditions) = SliceUnmarshal(buf, offset, UnmarshalCondition); - - return (offset, rule); - } - - private static (int, byte) UInt8Unmarshal(byte[] buf, int offset) - { - if (buf.Length - offset < 1) - { - throw new FrostFsException($"not enough bytes left to read a value of type 'byte' from offset {offset}"); - } - - return (offset + 1, buf[offset]); - } - - private static (int, long) Int64Unmarshal(byte[] buf, int offset) - { - if (buf.Length - offset < sizeof(long)) - { - throw new FrostFsException($"not enough bytes left to read a value of type 'long' from offset {offset}"); - } - - return Varint(buf, offset); - } - - private static (int, T[]) SliceUnmarshal(byte[] buf, int offset, Func unmarshalT) - { - var (newOffset, size) = Varint(buf, offset); - - if (size == NullSlice) - { - return (newOffset, []); - } - - if (size > MaxSliceLen) - { - throw new FrostFsException($"slice size is too big: '{size}'"); - } - - if (size < 0) - { - throw new FrostFsException($"invalid slice size: '{size}'"); - } - - var result = new T[size]; - for (int i = 0; i < result.Length; i++) - { - (newOffset, result[i]) = unmarshalT(buf, newOffset); - } - - return (newOffset, result); - } - - private static void VerifyUnmarshal(byte[] buf, int offset) - { - if (buf.Length != offset) - { - throw new FrostFsException("unmarshalled bytes left"); - } - } - - private static int MaxVarIntLen64 = 10; - - public static (int, long) Varint(byte[] buf, int offset) - { - var (ux, n) = Uvarint(buf, offset); // ok to continue in presence of error - long x = (long)ux >> 1; - if ((ux & 1) != 0) - { - x = ~x; - } - return (n, x); - } - - public static (ulong, int) Uvarint(byte[] buf, int offset) - { - ulong x = 0; - int s = 0; - - for (int i = offset; i < buf.Length; i++) - { - byte b = buf[i]; - if (i == MaxVarIntLen64) - { - return (0, -(i + 1)); // overflow - } - if (b < 0x80) - { - if (i == MaxVarIntLen64 - 1 && b > 1) - { - return (0, -(i + 1)); // overflow - } - return (x | ((ulong)b << s), i + 1); - } - x |= (ulong)(b & 0x7f) << s; - s += 7; - } - return (0, 0); - } -} diff --git a/src/FrostFS.SDK.Client/AssemblyInfo.cs b/src/FrostFS.SDK.Client/AssemblyInfo.cs deleted file mode 100644 index 4fa6cd9..0000000 --- a/src/FrostFS.SDK.Client/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyCompany("TrueCloudLab")] -[assembly: InternalsVisibleTo("FrostFS.SDK.Tests, PublicKey=" + - "002400000480000094000000060200000024000052534131000400000100010089b992fb14ebcf"+ - "4b85b4b1a3af1897290c52ff85a106035c47dc5604cbaa58ae3180f5c9b8523fee5dd1bb9ea9cf"+ - "e15ab287e6239c98d5dfa91615bd77485d523a3a3f65a4e5028454cedd5ac4d9eca6da18b81985"+ - "ac6905d33cc64b5a2587050c16f67b71ef8889dbd3c90ef7cc0b06bbbe09886601d195f5db179a"+ - "3c2a25b1")] -[assembly: AssemblyFileVersion("1.0.7.0")] -[assembly: AssemblyProduct("FrostFS.SDK.Client")] -[assembly: AssemblyTitle("FrostFS.SDK.Client")] -[assembly: AssemblyVersion("1.0.7")] diff --git a/src/FrostFS.SDK.Client/Caches.cs b/src/FrostFS.SDK.Client/Caches.cs deleted file mode 100644 index e9d9b16..0000000 --- a/src/FrostFS.SDK.Client/Caches.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; - -namespace FrostFS.SDK.Client; - -internal static class Caches -{ - private static readonly IMemoryCache _ownersCache = new MemoryCache(new MemoryCacheOptions - { - SizeLimit = 256 - }); - - internal static IMemoryCache Owners => _ownersCache; -} diff --git a/src/FrostFS.SDK.Client/CllientKey.cs b/src/FrostFS.SDK.Client/CllientKey.cs deleted file mode 100644 index 7a7943b..0000000 --- a/src/FrostFS.SDK.Client/CllientKey.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Security.Cryptography; - -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client; - -public class ClientKey(ECDsa key) -{ - internal ECDsa ECDsaKey { get; } = key; - - internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey()); - - internal string PublicKey { get; } = Base58.Encode(key.PublicKey()); - - internal FrostFsOwner Owner { get; } = new FrostFsOwner(key.PublicKey().PublicKeyToAddress()); -} diff --git a/src/FrostFS.SDK.Client/Exceptions/FrostFsException.cs b/src/FrostFS.SDK.Client/Exceptions/FrostFsException.cs deleted file mode 100644 index 9f3f36c..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/FrostFsException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class FrostFsException : Exception -{ - public FrostFsException() - { - } - - public FrostFsException(string message) : base(message) - { - } - - public FrostFsException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Exceptions/FrostFsInvalidObjectException.cs b/src/FrostFS.SDK.Client/Exceptions/FrostFsInvalidObjectException.cs deleted file mode 100644 index de1a88d..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/FrostFsInvalidObjectException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class FrostFsInvalidObjectException : FrostFsException -{ - public FrostFsInvalidObjectException() - { - } - - public FrostFsInvalidObjectException(string message) : base(message) - { - } - - public FrostFsInvalidObjectException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs b/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs deleted file mode 100644 index 87799e2..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/FrostFsResponseException.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class FrostFsResponseException : FrostFsException -{ - public FrostFsResponseStatus? Status { get; private set; } - - public FrostFsResponseException() - { - } - - public FrostFsResponseException(FrostFsResponseStatus status) - : base(status != null ? status.Message != null ? "" : "" : "") - { - Status = status; - } - - public FrostFsResponseException(string message) : base(message) - { - } - - public FrostFsResponseException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Exceptions/FrostFsStreamException.cs b/src/FrostFS.SDK.Client/Exceptions/FrostFsStreamException.cs deleted file mode 100644 index 0bd40d5..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/FrostFsStreamException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class FrostFsStreamException : FrostFsException -{ - public FrostFsStreamException() - { - } - - public FrostFsStreamException(string message) : base(message) - { - } - - public FrostFsStreamException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Exceptions/SessionExpiredException.cs b/src/FrostFS.SDK.Client/Exceptions/SessionExpiredException.cs deleted file mode 100644 index 0fa6a82..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/SessionExpiredException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class SessionExpiredException : FrostFsException -{ - public SessionExpiredException() - { - } - - public SessionExpiredException(string message) : base(message) - { - } - - public SessionExpiredException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Exceptions/SessionNotFoundException.cs b/src/FrostFS.SDK.Client/Exceptions/SessionNotFoundException.cs deleted file mode 100644 index c5be848..0000000 --- a/src/FrostFS.SDK.Client/Exceptions/SessionNotFoundException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class SessionNotFoundException : FrostFsException -{ - public SessionNotFoundException() - { - } - - public SessionNotFoundException(string message) : base(message) - { - } - - public SessionNotFoundException(string message, Exception innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Extensions/FrostFsExtensions.cs b/src/FrostFS.SDK.Client/Extensions/FrostFsExtensions.cs deleted file mode 100644 index 87728d5..0000000 --- a/src/FrostFS.SDK.Client/Extensions/FrostFsExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Security.Cryptography; -using Google.Protobuf; - -namespace FrostFS.SDK.Cryptography; - -public static class FrostFsExtensions -{ - public static byte[] Sha256(this IMessage data) - { - using var sha256 = SHA256.Create(); - using HashStream stream = new(sha256); - data.WriteTo(stream); - return stream.Hash(); - } - - public static Guid ToUuid(this ByteString id) - { - if (id == null) - throw new ArgumentNullException(nameof(id)); - - return new Guid( - (id[0] << 24) + (id[1] << 16) + (id[2] << 8) + id[3], - (short)((id[4] << 8) + id[5]), - (short)((id[6] << 8) + id[7]), - id[8], - id[9], - id[10], - id[11], - id[12], - id[13], - id[14], - id[15]); - } - - /// - /// Serializes Guid to binary representation in direct order bytes format - /// - /// - /// - public unsafe static void ToBytes(this Guid id, Span span) - { - var pGuid = (byte*)&id; - - span[0] = pGuid[3]; - span[1] = pGuid[2]; - span[2] = pGuid[1]; - span[3] = pGuid[0]; - span[4] = pGuid[5]; - span[5] = pGuid[4]; - span[6] = pGuid[7]; - span[7] = pGuid[6]; - span[8] = pGuid[8]; - span[9] = pGuid[9]; - span[10] = pGuid[10]; - span[11] = pGuid[11]; - span[12] = pGuid[12]; - span[13] = pGuid[13]; - span[14] = pGuid[14]; - span[15] = pGuid[15]; - } -} diff --git a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj b/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj deleted file mode 100644 index 88e20c6..0000000 --- a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj +++ /dev/null @@ -1,58 +0,0 @@ - - - - netstandard2.0 - 12.0 - enable - AllEnabledByDefault - FrostFS.SDK.Client - 1.0.7 - - C# SDK for FrostFS gRPC native protocol - - true - - - - true - - - - true - - - - <_SkipUpgradeNetAnalyzersNuGetWarning>true - - - - true - - - - false - True - True - .\\..\\..\\keyfile.snk - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - diff --git a/src/FrostFS.SDK.Client/FrostFSClient.cs b/src/FrostFS.SDK.Client/FrostFSClient.cs deleted file mode 100644 index bce9104..0000000 --- a/src/FrostFS.SDK.Client/FrostFSClient.cs +++ /dev/null @@ -1,434 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Client.Services; -using FrostFS.SDK.Cryptography; -using FrostFS.Session; - -using Grpc.Core; -using Grpc.Core.Interceptors; - -using Microsoft.Extensions.Options; - -using static Frostfs.V2.Apemanager.APEManagerService; -using static FrostFS.Accounting.AccountingService; -using static FrostFS.Container.ContainerService; -using static FrostFS.Netmap.NetmapService; -using static FrostFS.Object.ObjectService; -using static FrostFS.Session.SessionService; - -namespace FrostFS.SDK.Client; - -public class FrostFSClient : IFrostFSClient -{ - internal ContainerServiceClient? ContainerServiceClient { get; set; } - internal ContainerServiceProvider? ContainerServiceProvider { get; set; } - - internal NetmapServiceClient? NetmapServiceClient { get; set; } - internal NetmapServiceProvider? NetmapServiceProvider { get; set; } - - internal APEManagerServiceClient? ApeManagerServiceClient { get; set; } - internal ApeManagerServiceProvider? ApeManagerServiceProvider { get; set; } - - internal SessionServiceClient? SessionServiceClient { get; set; } - internal SessionServiceProvider? SessionServiceProvider { get; set; } - - internal ObjectServiceClient? ObjectServiceClient { get; set; } - internal ObjectServiceProvider? ObjectServiceProvider { get; set; } - - internal AccountingServiceClient? AccountingServiceClient { get; set; } - internal AccountingServiceProvider? AccountingServiceProvider { get; set; } - - internal ClientContext ClientCtx { get; set; } - - public static IFrostFSClient GetInstance(IOptions clientOptions, Func grpcChannelFactory) - { - if (clientOptions is null) - { - throw new ArgumentNullException(nameof(clientOptions)); - } - - if (grpcChannelFactory is null) - { - throw new ArgumentNullException(nameof(grpcChannelFactory)); - } - - return new FrostFSClient(clientOptions, grpcChannelFactory); - } - - /// - /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client. - /// - /// Global setting for client - /// Setting for gRPC channel - /// ContainerService.ContainerServiceClient implementation - /// Netmap.NetmapService.NetmapServiceClient implementation - /// Session.SessionService.SessionServiceClient implementation - /// Object.ObjectService.ObjectServiceClient implementation - /// - public static IFrostFSClient GetTestInstance( - IOptions settings, - Func grpcChannelFactory, - NetmapServiceClient netmapService, - SessionServiceClient sessionService, - ContainerServiceClient containerService, - ObjectServiceClient objectService) - { - if (settings is null) - { - throw new ArgumentNullException(nameof(settings)); - } - - if (grpcChannelFactory is null) - { - throw new ArgumentNullException(nameof(grpcChannelFactory)); - } - - if (netmapService is null) - { - throw new ArgumentNullException(nameof(netmapService)); - } - - if (sessionService is null) - { - throw new ArgumentNullException(nameof(sessionService)); - } - - if (containerService is null) - { - throw new ArgumentNullException(nameof(containerService)); - } - - if (objectService is null) - { - throw new ArgumentNullException(nameof(objectService)); - } - - return new FrostFSClient( - settings, channel: grpcChannelFactory(settings.Value.Host), containerService, netmapService, sessionService, objectService); - } - - private FrostFSClient( - IOptions settings, - ChannelBase channel, - ContainerServiceClient containerService, - NetmapServiceClient netmapService, - SessionServiceClient sessionService, - ObjectServiceClient objectService) - { - if (settings is null) - { - throw new ArgumentNullException(nameof(settings)); - } - - var ecdsaKey = settings.Value.Key.LoadWif(); - - ClientCtx = new ClientContext( - client: this, - key: new ClientKey(ecdsaKey), - owner: FrostFsOwner.FromKey(ecdsaKey), - channel: channel, - version: new FrostFsVersion(2, 13)) - { - SessionCache = new SessionCache(0), - Callback = settings.Value.Callback, - Interceptors = settings.Value.Interceptors - }; - - ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService)); - NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService)); - SessionServiceClient = sessionService ?? throw new ArgumentNullException(nameof(sessionService)); - ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService)); - } - - private FrostFSClient(IOptions settings, Func grpcChannelFactory) - { - var clientSettings = (settings?.Value) ?? throw new ArgumentNullException(nameof(settings), "Options value must be initialized"); - - clientSettings.Validate(); - - var ecdsaKey = clientSettings.Key.LoadWif(); - - ClientCtx = new ClientContext( - this, - key: new ClientKey(ecdsaKey), - owner: FrostFsOwner.FromKey(ecdsaKey), - channel: grpcChannelFactory(settings.Value.Host), - version: new FrostFsVersion(2, 13)) - { - SessionCache = new SessionCache(0), - Callback = settings.Value.Callback, - Interceptors = settings.Value.Interceptors - }; - } - - #region ApeManagerImplementation - public Task> AddChainAsync(PrmApeChainAdd args, CallContext ctx) - { - return GetApeManagerService().AddChainAsync(args, ctx); - } - - public Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx) - { - return GetApeManagerService().RemoveChainAsync(args, ctx); - } - - public Task ListChainAsync(PrmApeChainList args, CallContext ctx) - { - return GetApeManagerService().ListChainAsync(args, ctx); - } - #endregion - - #region ContainerImplementation - public Task GetContainerAsync(PrmContainerGet args, CallContext ctx) - { - return GetContainerService().GetContainerAsync(args, ctx); - } - - public IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args, CallContext ctx) - { - return GetContainerService().ListContainersAsync(args, ctx); - } - - [Obsolete("Use PutContainerAsync method")] - public Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx) - { - return GetContainerService().PutContainerAsync(args, ctx); - } - - public Task PutContainerAsync(PrmContainerCreate args, CallContext ctx) - { - return GetContainerService().PutContainerAsync(args, ctx); - } - - public Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx) - { - return GetContainerService().DeleteContainerAsync(args, ctx); - } - #endregion - - #region NetworkImplementation - public Task GetNetmapSnapshotAsync(CallContext ctx) - { - return GetNetmapService().GetNetmapSnapshotAsync(ctx); - } - - public Task GetNodeInfoAsync(CallContext ctx) - { - return GetNetmapService().GetLocalNodeInfoAsync(ctx); - } - - public Task GetNetworkSettingsAsync(CallContext ctx) - { - return GetNetmapService().GetNetworkSettingsAsync(ctx); - } - #endregion - - #region ObjectImplementation - public Task GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx) - { - return GetObjectService().GetObjectHeadAsync(args, ctx); - } - - public Task GetObjectAsync(PrmObjectGet args, CallContext ctx) - { - return GetObjectService().GetObjectAsync(args, ctx); - } - - public Task GetRangeAsync(PrmRangeGet args, CallContext ctx) - { - return GetObjectService().GetRangeAsync(args, ctx); - } - - public Task[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx) - { - return GetObjectService().GetRangeHashAsync(args, ctx); - } - - public Task PutObjectAsync(PrmObjectPut args, CallContext ctx) - { - return GetObjectService().PutStreamObjectAsync(args, ctx); - } - - public Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx) - { - return GetObjectService().PutClientCutSingleObjectAsync(args, ctx); - // return GetObjectService().PutClientCutObjectAsync(args, ctx); - } - - public Task PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx) - { - return GetObjectService().PutSingleObjectAsync(args, ctx); - } - - public Task PatchObjectAsync(PrmObjectPatch args, CallContext ctx) - { - return GetObjectService().PatchObjectAsync(args, ctx); - } - - public Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx) - { - return GetObjectService().DeleteObjectAsync(args, ctx); - } - - public IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args, CallContext ctx) - { - return GetObjectService().SearchObjectsAsync(args, ctx); - } - #endregion - - #region Session Implementation - public async Task CreateSessionAsync(PrmSessionCreate args, CallContext ctx) - { - var token = await CreateSessionInternalAsync(args, ctx).ConfigureAwait(false); - - return new FrostFsSessionToken(token); - } - - internal Task CreateSessionInternalAsync(PrmSessionCreate args, CallContext ctx) - { - var service = GetSessionService(); - return service.CreateSessionAsync(args, ctx); - } - #endregion - - #region Accounting Implementation - public async Task GetBalanceAsync(CallContext ctx) - { - return await GetAccouningService().GetBallance(ctx).ConfigureAwait(false); - } - #endregion - - private CallInvoker? CreateInvoker() - { - CallInvoker? callInvoker = null; - - if (ClientCtx.Interceptors != null) - { - foreach (var interceptor in ClientCtx.Interceptors) - callInvoker = AddInvoker(callInvoker, interceptor); - } - - if (ClientCtx.Callback != null) - callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ClientCtx.Callback)); - - if (ClientCtx.PoolErrorHandler != null) - callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ClientCtx.PoolErrorHandler)); - - return callInvoker; - - CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor) - { - if (callInvoker == null) - callInvoker = ClientCtx.Channel.Intercept(interceptor); - else - callInvoker = callInvoker.Intercept(interceptor); - - return callInvoker; - } - } - - private NetmapServiceProvider GetNetmapService() - { - if (NetmapServiceProvider == null) - { - var invoker = CreateInvoker(); - - NetmapServiceClient ??= ( - invoker != null - ? new NetmapServiceClient(invoker) - : new NetmapServiceClient(ClientCtx.Channel)); - - NetmapServiceProvider = new NetmapServiceProvider(NetmapServiceClient, ClientCtx); - } - - return NetmapServiceProvider; - } - - private SessionServiceProvider GetSessionService() - { - if (SessionServiceProvider == null) - { - var invoker = CreateInvoker(); - - SessionServiceClient ??= ( - invoker != null - ? new SessionServiceClient(invoker) - : new SessionServiceClient(ClientCtx.Channel)); - - SessionServiceProvider = new SessionServiceProvider(SessionServiceClient, ClientCtx); - } - - return SessionServiceProvider; - } - - private ApeManagerServiceProvider GetApeManagerService() - { - if (ApeManagerServiceProvider == null) - { - var invoker = CreateInvoker(); - - ApeManagerServiceClient ??= ( - invoker != null - ? new APEManagerServiceClient(invoker) - : new APEManagerServiceClient(ClientCtx.Channel)); - - ApeManagerServiceProvider = new ApeManagerServiceProvider(ApeManagerServiceClient, ClientCtx); - } - - return ApeManagerServiceProvider; - } - - private AccountingServiceProvider GetAccouningService() - { - if (this.AccountingServiceProvider == null) - { - var invoker = CreateInvoker(); - - AccountingServiceClient ??= ( - invoker != null - ? new AccountingServiceClient(invoker) - : new AccountingServiceClient(ClientCtx.Channel)); - - AccountingServiceProvider = new AccountingServiceProvider(AccountingServiceClient, ClientCtx); - } - - return AccountingServiceProvider; - } - - private ContainerServiceProvider GetContainerService() - { - if (this.ContainerServiceProvider == null) - { - var invoker = CreateInvoker(); - - ContainerServiceClient ??= ( - invoker != null - ? new ContainerServiceClient(invoker) - : new ContainerServiceClient(ClientCtx.Channel)); - - ContainerServiceProvider = new ContainerServiceProvider(ContainerServiceClient, ClientCtx); - } - - return ContainerServiceProvider; - } - - private ObjectServiceProvider GetObjectService() - { - if (this.ObjectServiceProvider == null) - { - var invoker = CreateInvoker(); - - ObjectServiceClient ??= ( - invoker != null - ? new ObjectServiceClient(invoker) - : new ObjectServiceClient(ClientCtx.Channel)); - - ObjectServiceProvider = new ObjectServiceProvider(ObjectServiceClient, ClientCtx); - } - - return ObjectServiceProvider; - } -} diff --git a/src/FrostFS.SDK.Client/GlobalSuppressions.cs b/src/FrostFS.SDK.Client/GlobalSuppressions.cs deleted file mode 100644 index 29a5b5c..0000000 --- a/src/FrostFS.SDK.Client/GlobalSuppressions.cs +++ /dev/null @@ -1,8 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "", Scope = "member", Target = "~M:FrostFS.SDK.Client.Sampler.Next~System.Int32")] diff --git a/src/FrostFS.SDK.Client/Interceptors/ErrorInterceptor.cs b/src/FrostFS.SDK.Client/Interceptors/ErrorInterceptor.cs deleted file mode 100644 index 771a0f2..0000000 --- a/src/FrostFS.SDK.Client/Interceptors/ErrorInterceptor.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Threading.Tasks; - -using Grpc.Core; -using Grpc.Core.Interceptors; - -namespace FrostFS.SDK.Client; - -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", - Justification = "parameters are provided by GRPC infrastructure")] -public class ErrorInterceptor(Action handler) : Interceptor -{ - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - var call = continuation(request, context); - - return new AsyncUnaryCall( - HandleUnaryResponse(call), - call.ResponseHeadersAsync, - call.GetStatus, - call.GetTrailers, - call.Dispose); - } - - public override AsyncClientStreamingCall AsyncClientStreamingCall( - ClientInterceptorContext context, - AsyncClientStreamingCallContinuation continuation) - { - var call = continuation(context); - - return new AsyncClientStreamingCall( - call.RequestStream, - HandleStreamResponse(call), - call.ResponseHeadersAsync, - call.GetStatus, - call.GetTrailers, - call.Dispose); - } - - private async Task HandleUnaryResponse(AsyncUnaryCall call) - { - try - { - return await call; - } - catch (Exception ex) - { - handler(ex); - throw; - } - } - - private async Task HandleStreamResponse(AsyncClientStreamingCall call) - { - try - { - return await call; - } - catch (Exception ex) - { - handler(ex); - throw; - } - } -} diff --git a/src/FrostFS.SDK.Client/Interceptors/MetricsInterceptor.cs b/src/FrostFS.SDK.Client/Interceptors/MetricsInterceptor.cs deleted file mode 100644 index 414551e..0000000 --- a/src/FrostFS.SDK.Client/Interceptors/MetricsInterceptor.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; - -using Grpc.Core; -using Grpc.Core.Interceptors; - -namespace FrostFS.SDK.Client; - -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", - Justification = "parameters are provided by GRPC infrastructure")] -public class MetricsInterceptor(Action callback) : Interceptor -{ - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - var call = continuation(request, context); - - return new AsyncUnaryCall( - HandleUnaryResponse(call), - call.ResponseHeadersAsync, - call.GetStatus, - call.GetTrailers, - call.Dispose); - } - - public override AsyncClientStreamingCall AsyncClientStreamingCall( - ClientInterceptorContext context, - AsyncClientStreamingCallContinuation continuation) - { - var call = continuation(context); - - return new AsyncClientStreamingCall( - call.RequestStream, - HandleStreamResponse(call), - call.ResponseHeadersAsync, - call.GetStatus, - call.GetTrailers, - call.Dispose); - } - - private async Task HandleUnaryResponse(AsyncUnaryCall call) - { - var watch = new Stopwatch(); - watch.Start(); - - var response = await call; - - watch.Stop(); - - var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency; - - callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed }); - - return response; - } - - private async Task HandleStreamResponse(AsyncClientStreamingCall call) - { - var watch = new Stopwatch(); - watch.Start(); - - var response = await call; - - watch.Stop(); - - var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency; - - callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed }); - - return response; - } -} diff --git a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs deleted file mode 100644 index 43dc3ac..0000000 --- a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace FrostFS.SDK.Client.Interfaces; - -public interface IFrostFSClient -{ - #region Network - Task GetNetmapSnapshotAsync(CallContext ctx); - - Task GetNodeInfoAsync(CallContext ctx); - - Task GetNetworkSettingsAsync(CallContext ctx); - #endregion - - #region Session - Task CreateSessionAsync(PrmSessionCreate args, CallContext ctx); - #endregion - - #region ApeManager - Task> AddChainAsync(PrmApeChainAdd args, CallContext ctx); - - Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx); - - Task ListChainAsync(PrmApeChainList args, CallContext ctx); - #endregion - - #region Container - Task GetContainerAsync(PrmContainerGet args, CallContext ctx); - - IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args, CallContext ctx); - - [Obsolete("Use PutContainerAsync method")] - Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx); - - Task PutContainerAsync(PrmContainerCreate args, CallContext ctx); - - Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx); - #endregion - - #region Object - Task GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx); - - Task GetObjectAsync(PrmObjectGet args, CallContext ctx); - - Task GetRangeAsync(PrmRangeGet args, CallContext ctx); - - Task[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx); - - Task PutObjectAsync(PrmObjectPut args, CallContext ctx); - - Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx); - - Task PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx); - - Task PatchObjectAsync(PrmObjectPatch args, CallContext ctx); - - Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx); - - IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args, CallContext ctx); - #endregion - - #region Account - Task GetBalanceAsync(CallContext ctx); - #endregion -} diff --git a/src/FrostFS.SDK.Client/Interfaces/IObjectWriter.cs b/src/FrostFS.SDK.Client/Interfaces/IObjectWriter.cs deleted file mode 100644 index 2d1abda..0000000 --- a/src/FrostFS.SDK.Client/Interfaces/IObjectWriter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace FrostFS.SDK.Client.Interfaces -{ - public interface IObjectWriter : IDisposable - { - Task WriteAsync(ReadOnlyMemory memory); - - Task CompleteAsync(); - } -} diff --git a/src/FrostFS.SDK.Client/Logging/FrostFsMessages.cs b/src/FrostFS.SDK.Client/Logging/FrostFsMessages.cs deleted file mode 100644 index b5e78ab..0000000 --- a/src/FrostFS.SDK.Client/Logging/FrostFsMessages.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace FrostFS.SDK.Client; - -internal static partial class FrostFsMessages -{ - [LoggerMessage(100, - LogLevel.Warning, - "Failed to create frostfs session token for client. Address {address}, {error}", - EventName = nameof(SessionCreationError))] - internal static partial void SessionCreationError(ILogger logger, string address, string error); - - [LoggerMessage(101, - LogLevel.Warning, - "Error threshold reached. Address {address}, threshold {threshold}", - EventName = nameof(ErrorЕhresholdReached))] - internal static partial void ErrorЕhresholdReached(ILogger logger, string address, uint threshold); - - [LoggerMessage(102, - LogLevel.Warning, - "Health has changed: {address} healthy {healthy}, reason {error}", - EventName = nameof(HealthChanged))] - internal static partial void HealthChanged(ILogger logger, string address, bool healthy, string error); -} diff --git a/src/FrostFS.SDK.Client/Mappers/Container.cs b/src/FrostFS.SDK.Client/Mappers/Container.cs deleted file mode 100644 index 7355c4a..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Container.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Linq; - -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ContainerMapper -{ - public static FrostFsContainerInfo ToModel(this Container.Container container) - { - if (container == null) - throw new ArgumentNullException(nameof(container)); - - return new FrostFsContainerInfo( - container.PlacementPolicy.ToModel(), - container.Attributes?.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToArray(), - container.Version?.ToModel(), - container.OwnerId?.ToModel(), - container.Nonce?.ToUuid()); - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs b/src/FrostFS.SDK.Client/Mappers/ContainerId.cs deleted file mode 100644 index 55b6179..0000000 --- a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -using FrostFS.Refs; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ContainerIdMapper -{ - public static ContainerID ToMessage(this FrostFsContainerId model) - { - if (model is null) - { - throw new ArgumentNullException(nameof(model)); - } - - var containerId = model.GetValue() ?? throw new ArgumentNullException(nameof(model)); - - var message = new ContainerID - { - Value = UnsafeByteOperations.UnsafeWrap(Base58.Decode(containerId)) - }; - - return message!; - } - - public static FrostFsContainerId ToModel(this ContainerID message) - { - if (message is null) - { - throw new ArgumentNullException(nameof(message)); - } - - return new FrostFsContainerId(Base58.Encode(message.Value.Span)); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs b/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs deleted file mode 100644 index b3fa5e3..0000000 --- a/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -using FrostFS.Session; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class MetaHeaderMapper -{ - public static RequestMetaHeader ToMessage(this MetaHeader metaHeader) - { - if (metaHeader is null) - { - throw new ArgumentNullException(nameof(metaHeader)); - } - - return new RequestMetaHeader - { - Version = metaHeader.Version.ToMessage(), - Epoch = metaHeader.Epoch, - Ttl = metaHeader.Ttl - }; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/Netmap.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/Netmap.cs deleted file mode 100644 index be378fc..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/Netmap.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq; - -using FrostFS.Netmap; - -namespace FrostFS.SDK.Client; - -public static class NetmapMapper -{ - public static FrostFsNetmapSnapshot ToModel(this NetmapSnapshotResponse netmap) - { - if (netmap is null) - { - throw new ArgumentNullException(nameof(netmap)); - } - - return new FrostFsNetmapSnapshot( - netmap.Body.Netmap.Epoch, - netmap.Body.Netmap.Nodes - .Select(n => n.ToModel(netmap.MetaHeader.Version)) - .ToArray()); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs deleted file mode 100644 index d7340eb..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Linq; - -using FrostFS.Netmap; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK.Client; - -public static class NodeInfoMapper -{ - public static FrostFsNodeInfo ToModel(this LocalNodeInfoResponse.Types.Body node) - { - if (node is null) - { - throw new ArgumentNullException(nameof(node)); - } - - return node.NodeInfo.ToModel(node.Version); - } - - public static FrostFsNodeInfo ToModel(this NodeInfo nodeInfo, Refs.Version version) - { - if (nodeInfo is null) - { - throw new ArgumentNullException(nameof(nodeInfo)); - } - - NodeState state = nodeInfo.State switch - { - NodeInfo.Types.State.Unspecified => NodeState.Unspecified, - NodeInfo.Types.State.Online => NodeState.Online, - NodeInfo.Types.State.Offline => NodeState.Offline, - NodeInfo.Types.State.Maintenance => NodeState.Maintenance, - _ => throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.State}'.") - }; - - return new FrostFsNodeInfo( - version: version.ToModel(), - state: state, - addresses: [.. nodeInfo.Addresses], - attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value), - publicKey: nodeInfo.PublicKey.Memory - ); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs deleted file mode 100644 index ee1c8a4..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; - -using FrostFS.Netmap; - -namespace FrostFS.SDK.Client; - -public static class PlacementPolicyMapper -{ - public static FrostFsPlacementPolicy ToModel(this PlacementPolicy placementPolicy) - { - if (placementPolicy is null) - { - throw new ArgumentNullException(nameof(placementPolicy)); - } - - return new FrostFsPlacementPolicy( - placementPolicy.Unique, - placementPolicy.ContainerBackupFactor, - [.. placementPolicy.Selectors.Select(s => s.ToModel())], - [.. placementPolicy.Filters.Select(f => f.ToModel())], - [.. placementPolicy.Replicas.Select(r => r.ToModel())] - ); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs deleted file mode 100644 index 4fa2edd..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Linq; - -using FrostFS.Netmap; - -namespace FrostFS.SDK.Client; - -public static class PolicyMapper -{ - public static Replica ToMessage(this FrostFsReplica replica) - { - return new Replica - { - Count = replica.Count, - Selector = replica.Selector, - EcDataCount = replica.EcDataCount, - EcParityCount = replica.EcParityCount - }; - } - - public static FrostFsReplica ToModel(this Replica replica) - { - if (replica is null) - { - throw new ArgumentNullException(nameof(replica)); - } - - return new FrostFsReplica(replica.Count, replica.Selector) - { - EcDataCount = replica.EcDataCount, - EcParityCount = replica.EcParityCount - }; - } - - public static Selector ToMessage(this FrostFsSelector selector) - { - if (selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } - - return new Selector - { - Name = selector.Name, - Count = selector.Count, - Clause = (Clause)selector.Clause, - Attribute = selector.Attribute, - Filter = selector.Filter - }; - } - - public static FrostFsSelector ToModel(this Selector selector) - { - if (selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } - - var model = new FrostFsSelector(selector.Name) - { - Count = selector.Count, - Clause = (int)selector.Clause, - Attribute = selector.Attribute, - Filter = selector.Filter - }; - - return model; - } - - public static Filter ToMessage(this FrostFsFilter filter) - { - if (filter is null) - { - throw new ArgumentNullException(nameof(filter)); - } - - var message = new Filter - { - Name = filter.Name, - Key = filter.Key, - Op = (Operation)filter.Operation, - Value = filter.Value, - }; - - message.Filters.AddRange(filter.Filters.Select(f => f.ToMessage())); - - return message; - } - - public static FrostFsFilter ToModel(this Filter filter) - { - if (filter is null) - { - throw new ArgumentNullException(nameof(filter)); - } - - var model = new FrostFsFilter( - filter.Name, - filter.Key, - (int)filter.Op, - filter.Value, - [.. filter.Filters.Select(f => f.ToModel())]); - - return model; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs b/src/FrostFS.SDK.Client/Mappers/Object/Object.cs deleted file mode 100644 index 5f85fc5..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FrostFS.SDK.Client.Mappers.GRPC; - -internal static class ObjectMapper -{ - internal static FrostFsObject ToModel(this Object.Object obj) - { - return new FrostFsObject(obj.Header.ToModel()) - { - ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span) - }; - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs deleted file mode 100644 index a28100e..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -using FrostFS.Object; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ObjectAttributeMapper -{ - public static Header.Types.Attribute ToMessage(this FrostFsAttributePair attribute) - { - return new Header.Types.Attribute - { - Key = attribute.Key, - Value = attribute.Value - }; - } - - public static FrostFsAttributePair ToModel(this Header.Types.Attribute attribute) - { - if (attribute is null) - { - throw new ArgumentNullException(nameof(attribute)); - } - - return new FrostFsAttributePair(attribute.Key, attribute.Value); - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectFilterMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectFilterMapper.cs deleted file mode 100644 index 249045d..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectFilterMapper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - -using FrostFS.Object; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ObjectFilterMapper -{ - public static SearchRequest.Types.Body.Types.Filter ToMessage(this IObjectFilter filter) - { - if (filter is null) - { - throw new ArgumentNullException(nameof(filter)); - } - - var objMatchTypeName = filter.MatchType switch - { - FrostFsMatchType.Unspecified => MatchType.Unspecified, - FrostFsMatchType.Equals => MatchType.StringEqual, - FrostFsMatchType.NotEquals => MatchType.StringNotEqual, - FrostFsMatchType.KeyAbsent => MatchType.NotPresent, - FrostFsMatchType.StartsWith => MatchType.CommonPrefix, - - _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.") - }; - - return new SearchRequest.Types.Body.Types.Filter - { - MatchType = objMatchTypeName, - Key = filter.Key, - Value = filter.GetSerializedValue() - }; - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs deleted file mode 100644 index fde13ff..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; - -using FrostFS.Object; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ObjectHeaderMapper -{ - public static FrostFsObjectHeader ToModel(this Header header) - { - if (header is null) - { - throw new ArgumentNullException(nameof(header)); - } - - var objTypeName = header.ObjectType switch - { - ObjectType.Regular => FrostFsObjectType.Regular, - ObjectType.Lock => FrostFsObjectType.Lock, - ObjectType.Tombstone => FrostFsObjectType.Tombstone, - _ => throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.") - }; - - FrostFsSplit? split = header!.Split != null - ? header.Split.ToModel() - : null; - - var model = new FrostFsObjectHeader( - new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)), - objTypeName, - [.. header.Attributes.Select(attribute => attribute.ToModel())], - split, - header.OwnerId.ToModel(), - header.Version.ToModel()) - { - PayloadLength = header.PayloadLength, - }; - - return model; - } - - public static FrostFsSplit ToModel(this Header.Types.Split split) - { - if (split is null) - { - throw new ArgumentNullException(nameof(split)); - } - - var children = split!.Children.Count != 0 - ? new ReadOnlyCollection([.. split.Children.Select(x => x.ToModel())]) - : null; - - return new FrostFsSplit(new SplitId(split.SplitId.ToUuid()), - split.Previous?.ToModel(), - split.Parent?.ToModel(), - split.ParentHeader?.ToModel(), - null, - children); - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs deleted file mode 100644 index 5562be5..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -using FrostFS.Refs; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class ObjectIdMapper -{ - public static ObjectID ToMessage(this FrostFsObjectId objectId) - { - if (objectId is null) - { - throw new ArgumentNullException(nameof(objectId)); - } - - return new ObjectID - { - Value = UnsafeByteOperations.UnsafeWrap(objectId.ToHash()) - }; - } - - public static FrostFsObjectId ToModel(this ObjectID objectId) - { - if (objectId is null) - { - throw new ArgumentNullException(nameof(objectId)); - } - - return FrostFsObjectId.FromHash(objectId.Value.Span); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs b/src/FrostFS.SDK.Client/Mappers/OwnerId.cs deleted file mode 100644 index 54f7a21..0000000 --- a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -using FrostFS.Refs; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -using Microsoft.Extensions.Caching.Memory; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class OwnerIdMapper -{ - private static readonly MemoryCacheEntryOptions _oneHourExpiration = new MemoryCacheEntryOptions() - .SetSlidingExpiration(TimeSpan.FromHours(1)) - .SetSize(1); - - public static OwnerID ToMessage(this FrostFsOwner model) - { - if (model is null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (!Caches.Owners.TryGetValue(model, out OwnerID? message)) - { - message = new OwnerID - { - Value = UnsafeByteOperations.UnsafeWrap(model.ToHash()) - }; - - Caches.Owners.Set(model, message, _oneHourExpiration); - } - - return message!; - } - - public static FrostFsOwner ToModel(this OwnerID message) - { - if (message is null) - { - throw new ArgumentNullException(nameof(message)); - } - - if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model)) - { - model = new FrostFsOwner(Base58.Encode(message.Value.Span)); - - Caches.Owners.Set(message, model, _oneHourExpiration); - } - - return model!; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/SignatureMapper.cs b/src/FrostFS.SDK.Client/Mappers/SignatureMapper.cs deleted file mode 100644 index ffae746..0000000 --- a/src/FrostFS.SDK.Client/Mappers/SignatureMapper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class SignatureMapper -{ - public static Refs.Signature ToMessage(this FrostFsSignature signature) - { - if (signature is null) - { - throw new ArgumentNullException(nameof(signature)); - } - - var scheme = signature.Scheme switch - { - SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256, - SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect, - SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512, - _ => throw new ArgumentException(nameof(signature.Scheme), $"Unexpected enum value: {signature.Scheme}") - }; - - return new Refs.Signature - { - Key = UnsafeByteOperations.UnsafeWrap(signature.Key), - Scheme = scheme, - Sign = UnsafeByteOperations.UnsafeWrap(signature.Sign) - }; - } -} diff --git a/src/FrostFS.SDK.Client/Mappers/Status.cs b/src/FrostFS.SDK.Client/Mappers/Status.cs deleted file mode 100644 index f3da090..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Status.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Linq; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class StatusMapper -{ - public static FrostFsResponseStatus ToModel(this Status.Status status) - { - if (status is null) - return new FrostFsResponseStatus(FrostFsStatusCode.Success); - - var codeName = Enum.GetName(typeof(FrostFsStatusCode), status.Code); - - return codeName is null - ? throw new ArgumentException($"Unknown StatusCode. Value: '{status.Code}'.") - : new FrostFsResponseStatus( - (FrostFsStatusCode)status.Code, - status.Message, - string.Join(", ", status.Details.Select(d => System.Text.Encoding.UTF8.GetString([.. d.Value])))); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Version.cs b/src/FrostFS.SDK.Client/Mappers/Version.cs deleted file mode 100644 index af8738d..0000000 --- a/src/FrostFS.SDK.Client/Mappers/Version.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections; -using System.Threading; - -using FrostFS.Refs; - -namespace FrostFS.SDK.Client.Mappers.GRPC; - -public static class VersionMapper -{ - private static readonly Hashtable _cacheMessages = []; - private static readonly Hashtable _cacheModels = []; - private static SpinLock _spinlock; - - public static Version ToMessage(this FrostFsVersion model) - { - if (model is null) - { - throw new System.ArgumentNullException(nameof(model)); - } - - var key = (int)model.Major << 16 + (int)model.Minor; - - if (!_cacheMessages.ContainsKey(key)) - { - bool lockTaken = false; - try - { - _spinlock.Enter(ref lockTaken); - var message = new Version - { - Major = model.Major, - Minor = model.Minor - }; - - _cacheMessages.Add(key, message); - return message; - } - catch (System.ArgumentException) - { - // ignore attempt to add duplicate - } - finally - { - if (lockTaken) - _spinlock.Exit(false); - } - } - - return (Version)_cacheMessages[key]; - } - - public static FrostFsVersion ToModel(this Version message) - { - if (message is null) - { - throw new System.ArgumentNullException(nameof(message)); - } - - var key = (int)message.Major << 16 + (int)message.Minor; - - if (!_cacheModels.ContainsKey(key)) - { - bool lockTaken = false; - try - { - _spinlock.Enter(ref lockTaken); - var model = new FrostFsVersion(message.Major, message.Minor); - - _cacheModels.Add(key, model); - return model; - } - catch (System.ArgumentException) - { - // ignore attempt to add duplicate - } - finally - { - if (lockTaken) - _spinlock.Exit(false); - } - } - - return (FrostFsVersion)_cacheModels[key]; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs b/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs deleted file mode 100644 index 4aa00f9..0000000 --- a/src/FrostFS.SDK.Client/Models/Client/ClientSettings.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Text; - -using Grpc.Core.Interceptors; - -namespace FrostFS.SDK; - -public class ClientSettings -{ - protected static readonly string errorTemplate = "{0} is required parameter"; - - public string Host { get; set; } = string.Empty; - - public string Key { get; set; } = string.Empty; - - public Action? Callback { get; set; } - - public Collection Interceptors { get; } = []; - - public void Validate() - { - StringBuilder? errors = null; - - if (string.IsNullOrWhiteSpace(Host)) - (errors = new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host))); - - if (string.IsNullOrWhiteSpace(Key)) - (errors ??= new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Key))); - - if (errors != null) - { - throw new ArgumentException(errors.ToString()); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs deleted file mode 100644 index e91e1b8..0000000 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FrostFS.Refs; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK; - -public class FrostFsContainerId -{ - private string? modelId; - private ContainerID? containerID; - - public FrostFsContainerId(string id) - { - this.modelId = id; - } - - internal FrostFsContainerId(ContainerID id) - { - this.containerID = id; - } - - public string GetValue() - { - if (this.modelId != null) - return this.modelId; - - if (containerID != null) - { - this.modelId = Base58.Encode(containerID.Value.Span); - return this.modelId; - } - - throw new FrostFsInvalidObjectException(); - } - - public ContainerID GetContainerID() - { - if (this.containerID != null) - return this.containerID; - - if (modelId != null) - { - this.containerID = this.ToMessage(); - return this.containerID; - } - - throw new FrostFsInvalidObjectException(); - } - - public override string ToString() - { - return GetValue(); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs deleted file mode 100644 index c7dc782..0000000 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerInfo.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; - -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK; - -public class FrostFsContainerInfo -{ - private Container.Container.Types.Attribute[]? grpsAttributes; - private ReadOnlyCollection? attributes; - private FrostFsPlacementPolicy? placementPolicy; - private Guid? nonce; - - private Container.Container? container; - - public FrostFsContainerInfo( - FrostFsPlacementPolicy placementPolicy, - FrostFsAttributePair[]? attributes = null, - FrostFsVersion? version = null, - FrostFsOwner? owner = null, - Guid? nonce = null) - { - this.placementPolicy = placementPolicy; - Version = version; - Owner = owner; - this.nonce = nonce; - - if (attributes != null) - this.attributes = new ReadOnlyCollection(attributes); - } - - internal FrostFsContainerInfo(Container.Container container) - { - this.container = container; - } - - public Guid Nonce - { - get - { - nonce ??= container?.Nonce != null ? container.Nonce.ToUuid() : Guid.NewGuid(); - return nonce.Value; - } - } - - public FrostFsPlacementPolicy? PlacementPolicy - { - get - { - placementPolicy ??= container?.PlacementPolicy?.ToModel(); - return placementPolicy; - } - } - - public ReadOnlyCollection? Attributes - { - get - { - if (attributes == null && grpsAttributes != null) - attributes = new ReadOnlyCollection(grpsAttributes.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToList()); - - return attributes; - } - } - - public FrostFsVersion? Version { get; private set; } - - public FrostFsOwner? Owner { get; private set; } - - internal Container.Container.Types.Attribute[]? GetGrpsAttributes() - { - grpsAttributes ??= Attributes? - .Select(a => new Container.Container.Types.Attribute { Key = a.Key, Value = a.Value }) - .ToArray(); - - return grpsAttributes; - } - - internal Container.Container GetContainer() - { - if (this.container == null) - { - if (PlacementPolicy == null) - { - throw new ArgumentNullException("PlacementPolicy is null"); - } - - Span nonce = stackalloc byte[16]; - Nonce.ToBytes(nonce); - - this.container = new Container.Container() - { - PlacementPolicy = PlacementPolicy.Value.GetPolicy(), - Nonce = ByteString.CopyFrom(nonce), - OwnerId = Owner?.OwnerID, - Version = Version?.VersionID - }; - - var attribs = GetGrpsAttributes(); - if (attribs != null) - this.container.Attributes.AddRange(attribs); - } - - return this.container; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Enums/FrostFsObjectType.cs b/src/FrostFS.SDK.Client/Models/Enums/FrostFsObjectType.cs deleted file mode 100644 index 793fe2e..0000000 --- a/src/FrostFS.SDK.Client/Models/Enums/FrostFsObjectType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK; - -public enum FrostFsObjectType -{ - Regular = 0, - Tombstone = 1, - Lock = 3 -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Enums/SignatureScheme.cs b/src/FrostFS.SDK.Client/Models/Enums/SignatureScheme.cs deleted file mode 100644 index 4b5634c..0000000 --- a/src/FrostFS.SDK.Client/Models/Enums/SignatureScheme.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK; - -public enum SignatureScheme -{ - EcdsaSha512, - EcdsaRfc6979Sha256, - EcdsaRfc6979Sha256WalletConnect -} diff --git a/src/FrostFS.SDK.Client/Models/Misc/CallStatistics.cs b/src/FrostFS.SDK.Client/Models/Misc/CallStatistics.cs deleted file mode 100644 index 706b3f2..0000000 --- a/src/FrostFS.SDK.Client/Models/Misc/CallStatistics.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FrostFS.SDK; - -public class CallStatistics -{ - public string? MethodName { get; set; } - public long ElapsedMicroSeconds { get; set; } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Misc/CheckSum.cs b/src/FrostFS.SDK.Client/Models/Misc/CheckSum.cs deleted file mode 100644 index 2241227..0000000 --- a/src/FrostFS.SDK.Client/Models/Misc/CheckSum.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK; - -public class CheckSum -{ - private byte[]? hash; - private string? text; - - public static CheckSum CreateCheckSum(byte[] content) - { - return new CheckSum { hash = DataHasher.Sha256(content.AsSpan()) }; - } - - public override string ToString() - { - return text ??= BitConverter.ToString(hash).Replace("-", ""); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Misc/Constants.cs b/src/FrostFS.SDK.Client/Models/Misc/Constants.cs deleted file mode 100644 index 28c8cf0..0000000 --- a/src/FrostFS.SDK.Client/Models/Misc/Constants.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace FrostFS.SDK; - -public static class Constants -{ - public const int ObjectChunkSize = 3 * (1 << 20); - public const int Sha256HashLength = 32; - - // HeaderPrefix is a prefix of key to object header value or property. - public const string HeaderPrefix = "$Object:"; - - // FilterHeaderVersion is a filter key to "version" field of the object header. - public const string FilterHeaderVersion = HeaderPrefix + "version"; - - // FilterHeaderObjectID is a filter key to "object_id" field of the object. - public const string FilterHeaderObjectID = HeaderPrefix + "objectID"; - - // FilterHeaderContainerID is a filter key to "container_id" field of the object header. - public const string FilterHeaderContainerID = HeaderPrefix + "containerID"; - - // FilterHeaderOwnerID is a filter key to "owner_id" field of the object header. - public const string FilterHeaderOwnerID = HeaderPrefix + "ownerID"; - - // FilterHeaderCreationEpoch is a filter key to "creation_epoch" field of the object header. - public const string FilterHeaderCreationEpoch = HeaderPrefix + "creationEpoch"; - - // FilterHeaderPayloadLength is a filter key to "payload_length" field of the object header. - public const string FilterHeaderPayloadLength = HeaderPrefix + "payloadLength"; - - // FilterHeaderPayloadHash is a filter key to "payload_hash" field of the object header. - public const string FilterHeaderPayloadHash = HeaderPrefix + "payloadHash"; - - // FilterHeaderObjectType is a filter key to "object_type" field of the object header. - public const string FilterHeaderObjectType = HeaderPrefix + "objectType"; - - // FilterHeaderHomomorphicHash is a filter key to "homomorphic_hash" field of the object header. - public const string FilterHeaderHomomorphicHash = HeaderPrefix + "homomorphicHash"; - - // FilterHeaderParent is a filter key to "split.parent" field of the object header. - public const string FilterHeaderParent = HeaderPrefix + "split.parent"; - - // FilterHeaderSplitID is a filter key to "split.splitID" field of the object header. - public const string FilterHeaderSplitID = HeaderPrefix + "split.splitID"; - - // FilterHeaderECParent is a filter key to "ec.parent" field of the object header. - public const string FilterHeaderECParent = HeaderPrefix + "ec.parent"; - - // FilterPropertyRoot is a filter key to check if regular object is on top of split hierarchy. - public const string FilterHeaderRoot = HeaderPrefix + "ROOT"; - - // FilterPropertyPhy is a filter key to check if an object physically stored on a node. - public const string FilterHeaderPhy = HeaderPrefix + "PHY"; -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs deleted file mode 100644 index 11ee079..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using FrostFS.Netmap; - -namespace FrostFS.SDK; - -public class FrostFsFilter(string name, string key, int operation, string value, FrostFsFilter[] filters) : IFrostFsFilter -{ - public string Name { get; } = name; - public string Key { get; } = key; - public int Operation { get; } = operation; - public string Value { get; } = value; - public FrostFsFilter[] Filters { get; } = filters; - - internal Filter GetMessage() - { - var filter = new Filter() - { - Name = Name, - Key = Key, - Op = (Operation)Operation, - Value = Value, - }; - - filter.Filters.AddRange(Filters.Select(f => f.GetMessage())); - - return filter; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNetmapSnapshot.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNetmapSnapshot.cs deleted file mode 100644 index e15c2a8..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNetmapSnapshot.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Models.Netmap.Placement; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK; - -public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList nodeInfoCollection) -{ - public ulong Epoch { get; private set; } = epoch; - - public IReadOnlyList NodeInfoCollection { get; private set; } = nodeInfoCollection; - - internal static INormalizer NewReverseMinNorm(double minV) - { - return new ReverseMinNorm { min = minV }; - } - - // newSigmoidNorm returns a normalizer which - // normalize values in range of 0.0 to 1.0 to a scaled sigmoid. - internal static INormalizer NewSigmoidNorm(double scale) - { - return new SigmoidNorm(scale); - } - - // PlacementVectors sorts container nodes returned by ContainerNodes method - // and returns placement vectors for the entity identified by the given pivot. - // For example, in order to build node list to store the object, binary-encoded - // object identifier can be used as pivot. Result is deterministic for - // the fixed NetMap and parameters. - public FrostFsNodeInfo[][] PlacementVectors(FrostFsNodeInfo[][] vectors, byte[] pivot) - { - if (vectors is null) - { - throw new ArgumentNullException(nameof(vectors)); - } - - using var murmur3 = new Murmur3(0); - var hash = murmur3.GetCheckSum64(pivot); - - var wf = Tools.DefaultWeightFunc(NodeInfoCollection.ToArray()); - - var result = new FrostFsNodeInfo[vectors.Length][]; - var maxSize = vectors.Max(x => x.Length); - - var spanWeigths = new double[maxSize]; - - for (int i = 0; i < vectors.Length; i++) - { - result[i] = new FrostFsNodeInfo[vectors[i].Length]; - - for (int j = 0; j < vectors[i].Length; j++) - { - result[i][j] = vectors[i][j]; - } - - Tools.AppendWeightsTo(result[i], wf, ref spanWeigths); - - result[i] = Tools.SortHasherSliceByWeightValue(result[i].ToList(), spanWeigths, hash).ToArray(); - } - - return result; - } - - // SelectFilterNodes returns a two-dimensional list of nodes as a result of applying the - // given SelectFilterExpr to the NetMap. - // If the SelectFilterExpr contains only filters, the result contains a single row with the - // result of the last filter application. - // If the SelectFilterExpr contains only selectors, the result contains the selection rows - // of the last select application. - List> SelectFilterNodes(SelectFilterExpr expr) - { - var policy = new FrostFsPlacementPolicy(false, expr.Cbf, [expr.Selector], expr.Filters); - - var ctx = new Context(this) - { - Cbf = expr.Cbf - }; - - ctx.ProcessFilters(policy); - ctx.ProcessSelectors(policy); - - var ret = new List>(); - - if (expr.Selector == null) - { - var lastFilter = expr.Filters.Last(); - - var subCollestion = new List(); - ret.Add(subCollestion); - - foreach (var nodeInfo in NodeInfoCollection) - { - if (ctx.Match(ctx.ProcessedFilters[lastFilter.Name], nodeInfo)) - { - subCollestion.Add(nodeInfo); - } - } - } - else if (expr.Selector.Name != null) - { - var sel = ctx.GetSelection(ctx.ProcessedSelectors[expr.Selector.Name]); - - foreach (var ns in sel) - { - var subCollestion = new List(); - ret.Add(subCollestion); - foreach (var n in ns) - { - subCollestion.Add(n); - } - } - } - - return ret; - } - - internal static Func NewWeightFunc(INormalizer capNorm, INormalizer priceNorm) - { - return new Func((FrostFsNodeInfo nodeInfo) => - { - return capNorm.Normalize(nodeInfo.GetCapacity()) * priceNorm.Normalize(nodeInfo.Price); - }); - } - - private static FrostFsNodeInfo[] FlattenNodes(List> nodes) - { - int sz = 0; - foreach (var ns in nodes) - { - sz += ns.Count; - } - - var result = new FrostFsNodeInfo[sz]; - - int i = 0; - foreach (var ns in nodes) - { - foreach (var n in ns) - { - result[i++] = n; - } - } - - return result; - } - - // ContainerNodes returns two-dimensional list of nodes as a result of applying - // given PlacementPolicy to the NetMap. Each line of the list corresponds to a - // replica descriptor. Line order corresponds to order of ReplicaDescriptor list - // in the policy. Nodes are pre-filtered according to the Filter list from - // the policy, and then selected by Selector list. Result is deterministic for - // the fixed NetMap and parameters. - // - // Result can be used in PlacementVectors. - public FrostFsNodeInfo[][] ContainerNodes(FrostFsPlacementPolicy p, byte[]? pivot) - { - var c = new Context(this) - { - Cbf = p.BackupFactor == 0 ? 3 : p.BackupFactor - }; - - if (pivot != null && pivot.Length > 0) - { - c.HrwSeed = pivot; - - using var murmur = new Murmur3(0); - c.HrwSeedHash = murmur.GetCheckSum64(pivot); - } - - c.ProcessFilters(p); - c.ProcessSelectors(p); - - var unique = p.IsUnique(); - - var result = new List>(p.Replicas.Length); - for (int i = 0; i < p.Replicas.Length; i++) - { - result.Add([]); - } - - // Note that the cached selectors are not used when the policy contains the UNIQUE flag. - // This is necessary because each selection vector affects potentially the subsequent vectors - // and thus we call getSelection in such case, in order to take into account nodes previously - // marked as used by earlier replicas. - for (int i = 0; i < p.Replicas.Length; i++) - { - var sName = p.Replicas[i].Selector; - - if (string.IsNullOrEmpty(sName) && !(p.Replicas.Length == 1 && p.Selectors.Count == 1)) - { - var s = new FrostFsSelector(string.Empty) - { - Count = p.Replicas[i].CountNodes(), - Filter = Context.mainFilterName - }; - - var nodes = c.GetSelection(s); - result[i].AddRange(FlattenNodes(nodes)); - - if (unique) - { - foreach (var n in result[i]) - { - c.UsedNodes[n.Hash()] = true; - } - } - - continue; - } - - if (unique) - { - if (!c.ProcessedSelectors.TryGetValue(sName, out var s) || s == null) - { - throw new FrostFsException($"selector not found: {sName}"); - } - - var nodes = c.GetSelection(c.ProcessedSelectors[sName]); - - result[i].AddRange(FlattenNodes(nodes)); - - foreach (var n in result[i]) - { - c.UsedNodes[n.Hash()] = true; - } - } - else - { - var nodes = c.Selections[sName]; - result[i].AddRange(FlattenNodes(nodes)); - } - } - - var collection = new FrostFsNodeInfo[result.Count][]; - for (int i = 0; i < result.Count; i++) - { - collection[i] = [.. result[i]]; - } - - return collection; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNodeInfo.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNodeInfo.cs deleted file mode 100644 index ea7d7c5..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsNodeInfo.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; - -using FrostFS.SDK.Client.Models.Netmap.Placement; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK; - -public class FrostFsNodeInfo( - FrostFsVersion version, - NodeState state, - IReadOnlyCollection addresses, - IReadOnlyDictionary attributes, - ReadOnlyMemory publicKey) : IHasher -{ - private ulong _hash; - - // attrPrice is a key to the node attribute that indicates the - // price in GAS tokens for storing one GB of data during one Epoch. - internal const string AttrPrice = "Price"; - - // attrCapacity is a key to the node attribute that indicates the - // total available disk space in Gigabytes. - internal const string AttrCapacity = "Capacity"; - - // attrExternalAddr is a key for the attribute storing node external addresses. - internal const string AttrExternalAddr = "ExternalAddr"; - - // sepExternalAddr is a separator for multi-value ExternalAddr attribute. - internal const string SepExternalAddr = ","; - - private ulong price = ulong.MaxValue; - - public NodeState State { get; } = state; - - public FrostFsVersion Version { get; } = version; - - public IReadOnlyCollection Addresses { get; } = addresses; - - public IReadOnlyDictionary Attributes { get; } = attributes; - - public ReadOnlyMemory PublicKey { get; } = publicKey; - - public ulong Hash() - { - if (_hash == 0) - { - using var murmur3 = new Murmur3(0); - murmur3.Initialize(); - _hash = murmur3.GetCheckSum64(PublicKey.ToArray()); - } - - return _hash; - } - - internal ulong GetCapacity() - { - if (!Attributes.TryGetValue(AttrCapacity, out var val)) - return 0; - - return ulong.Parse(val, CultureInfo.InvariantCulture); - } - - internal ulong Price - { - get - { - if (price == ulong.MaxValue) - { - if (!Attributes.TryGetValue(AttrPrice, out var val)) - price = 0; - else - price = uint.Parse(val, CultureInfo.InvariantCulture); - } - - return price; - } - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs deleted file mode 100644 index 61bdb82..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; - -using FrostFS.Netmap; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK; - -public struct FrostFsPlacementPolicy(bool unique, - uint backupFactor, - Collection selectors, - Collection filters, - params FrostFsReplica[] replicas) - : IEquatable -{ - private PlacementPolicy? policy; - - public FrostFsReplica[] Replicas { get; } = replicas; - - public Collection Selectors { get; } = selectors; - - public Collection Filters { get; } = filters; - - public bool Unique { get; } = unique; - - public uint BackupFactor { get; } = backupFactor; - - public override readonly bool Equals(object obj) - { - if (obj is null) - { - return false; - } - - var other = (FrostFsPlacementPolicy)obj; - - return Equals(other); - } - - public PlacementPolicy GetPolicy() - { - if (policy == null) - { - policy = new PlacementPolicy - { - Filters = { }, - Selectors = { }, - Replicas = { }, - Unique = Unique, - ContainerBackupFactor = BackupFactor - }; - - if (Selectors != null && Selectors.Count > 0) - { - policy.Selectors.AddRange(Selectors.Select(s => s.GetMessage())); - } - - if (Filters != null && Filters.Count > 0) - { - policy.Filters.AddRange(Filters.Select(s => s.ToMessage())); - } - - foreach (var replica in Replicas) - { - policy.Replicas.Add(replica.ToMessage()); - } - } - - return policy; - } - - internal readonly bool IsUnique() - { - return Unique || Replicas.Any(r => r.EcDataCount != 0 || r.EcParityCount != 0); - } - - public override readonly int GetHashCode() - { - return Unique ? 17 : 0 + Replicas.GetHashCode(); - } - - public static bool operator ==(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right) - { - return left.Equals(right); - } - - public static bool operator !=(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right) - { - return !(left == right); - } - - public readonly bool Equals(FrostFsPlacementPolicy other) - { - var notEqual = Unique != other.Unique - || Replicas.Length != other.Replicas.Length; - - if (notEqual) - return false; - - foreach (var replica in Replicas) - { - if (!other.Replicas.Any(r => r.Equals(replica))) - return false; - } - - return true; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs deleted file mode 100644 index 82e2240..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; - -namespace FrostFS.SDK; - -public struct FrostFsReplica : IEquatable -{ - public uint Count { get; set; } - public string Selector { get; set; } - public uint EcDataCount { get; set; } - public uint EcParityCount { get; set; } - - public FrostFsReplica(uint count, string? selector = null) - { - selector ??= string.Empty; - - Count = count; - Selector = selector; - } - - public override readonly bool Equals(object obj) - { - if (obj is null) - { - return false; - } - - var other = (FrostFsReplica)obj; - - return Count == other.Count && Selector == other.Selector; - } - - public readonly uint CountNodes() - { - return Count != 0 ? Count : EcDataCount + EcParityCount; - } - - public override readonly int GetHashCode() - { - return Count.GetHashCode() ^ Selector.GetHashCode() ^ (int)EcDataCount ^ (int)EcParityCount; - } - - public static bool operator ==(FrostFsReplica left, FrostFsReplica right) - { - return left.Equals(right); - } - - public static bool operator !=(FrostFsReplica left, FrostFsReplica right) - { - return !(left == right); - } - - public readonly bool Equals(FrostFsReplica other) - { - return Count == other.Count - && Selector == other.Selector - && EcDataCount == other.EcDataCount - && EcParityCount == other.EcParityCount; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs deleted file mode 100644 index c058967..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs +++ /dev/null @@ -1,24 +0,0 @@ -using FrostFS.Netmap; - -namespace FrostFS.SDK; - -public class FrostFsSelector(string name) -{ - public string Name { get; } = name; - public uint Count { get; set; } - public int Clause { get; set; } - public string? Attribute { get; set; } - public string? Filter { get; set; } - - internal Selector GetMessage() - { - return new Selector() - { - Name = Name, - Clause = (Clause)Clause, - Count = Count, - Filter = Filter ?? string.Empty, - Attribute = Attribute ?? string.Empty, - }; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs deleted file mode 100644 index 9ed9522..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs +++ /dev/null @@ -1,36 +0,0 @@ -using FrostFS.Refs; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK; - -public class FrostFsVersion(uint major, uint minor) -{ - private Version? version; - - public uint Major { get; set; } = major; - public uint Minor { get; set; } = minor; - - internal Version VersionID - { - get - { - this.version ??= this.ToMessage(); - return this.version; - } - } - - public bool IsSupported(FrostFsVersion version) - { - if (version is null) - { - throw new System.ArgumentNullException(nameof(version)); - } - - return Major == version.Major; - } - - public override string ToString() - { - return $"v{Major}.{Minor}"; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Netmap/IFrostFsFilter.cs b/src/FrostFS.SDK.Client/Models/Netmap/IFrostFsFilter.cs deleted file mode 100644 index e13875a..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/IFrostFsFilter.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace FrostFS.SDK -{ - public interface IFrostFsFilter - { - FrostFsFilter[] Filters { get; } - string Key { get; } - string Name { get; } - int Operation { get; } - string Value { get; } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Netmap/NodeAttrPair.cs b/src/FrostFS.SDK.Client/Models/Netmap/NodeAttrPair.cs deleted file mode 100644 index 3c014fc..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/NodeAttrPair.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FrostFS.SDK; - -struct NodeAttrPair -{ - internal string attr; - internal FrostFsNodeInfo[] nodes; -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Clause.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/Clause.cs deleted file mode 100644 index b982027..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Clause.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -public enum FrostFsClause -{ - Unspecified = 0, - Same, - Distinct -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Context.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/Context.cs deleted file mode 100644 index aedfd35..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Context.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct Context -{ - private const string errInvalidFilterName = "filter name is invalid"; - private const string errInvalidFilterOp = "invalid filter operation"; - private const string errFilterNotFound = "filter not found"; - private const string errNonEmptyFilters = "simple filter contains sub-filters"; - private const string errNotEnoughNodes = "not enough nodes to SELECT from"; - private const string errUnnamedTopFilter = "unnamed top-level filter"; - - internal const string mainFilterName = "*"; - internal const string likeWildcard = "*"; - - // network map to operate on - internal FrostFsNetmapSnapshot NetMap { get; } - - // cache of processed filters - internal Dictionary ProcessedFilters { get; } = []; - - // cache of processed selectors - internal Dictionary ProcessedSelectors { get; } = []; - - // stores results of selector processing - internal Dictionary>> Selections { get; } = []; - - // cache of parsed numeric values - internal Dictionary NumCache { get; } = []; - - internal byte[]? HrwSeed { get; set; } - - // hrw.Hash of hrwSeed - internal ulong HrwSeedHash { get; set; } - - // container backup factor - internal uint Cbf { get; set; } - - // nodes already used in previous selections, which is needed when the placement - // policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent - // base selections. - internal Dictionary UsedNodes { get; } = []; - - // If true, returns an error when netmap does not contain enough nodes for selection. - // By default best effort is taken. - internal bool Strict { get; set; } - - // weightFunc is a weighting function for determining node priority - // which combines low price and high performance - private readonly Func weightFunc; - - public Context(FrostFsNetmapSnapshot netMap) - { - NetMap = netMap; - weightFunc = Tools.DefaultWeightFunc(NetMap.NodeInfoCollection); - } - - internal void ProcessFilters(FrostFsPlacementPolicy policy) - { - foreach (var filter in policy.Filters) - { - ProcessFilter(filter, true); - } - } - - readonly void ProcessFilter(FrostFsFilter filter, bool top) - { - var filterName = filter.Name; - if (filterName == mainFilterName) - { - throw new FrostFsException($"{errInvalidFilterName}: '{errInvalidFilterName}' is reserved"); - } - - if (top && string.IsNullOrEmpty(filterName)) - { - throw new FrostFsException(errUnnamedTopFilter); - } - - if (!top && !string.IsNullOrEmpty(filterName) && !ProcessedFilters.ContainsKey(filterName)) - { - throw new FrostFsException(errFilterNotFound); - } - - if (filter.Operation == (int)Operation.AND || - filter.Operation == (int)Operation.OR || - filter.Operation == (int)Operation.NOT) - { - foreach (var f in filter.Filters) - ProcessFilter(f, false); - } - else - { - if (filter.Filters.Length != 0) - { - throw new FrostFsException(errNonEmptyFilters); - } - else if (!top && !string.IsNullOrEmpty(filterName)) - { - // named reference - return; - } - - switch (filter.Operation) - { - case (int)Operation.EQ: - case (int)Operation.NE: - case (int)Operation.LIKE: - break; - case (int)Operation.GT: - case (int)Operation.GE: - case (int)Operation.LT: - case (int)Operation.LE: - { - var n = uint.Parse(filter.Value, CultureInfo.InvariantCulture); - NumCache[filter.Value] = n; - break; - } - default: - throw new FrostFsException($"{errInvalidFilterOp}: {filter.Operation}"); - } - } - - if (top) - { - ProcessedFilters[filterName] = filter; - } - } - - // processSelectors processes selectors and returns error is any of them is invalid. - internal void ProcessSelectors(FrostFsPlacementPolicy policy) - { - foreach (var selector in policy.Selectors) - { - var filterName = selector.Filter; - if (filterName != mainFilterName) - { - if (selector.Filter == null || !ProcessedFilters.ContainsKey(selector.Filter)) - { - throw new FrostFsException($"{errFilterNotFound}: SELECT FROM '{filterName}'"); - } - } - - ProcessedSelectors[selector.Name] = selector; - - var selection = GetSelection(selector); - - Selections[selector.Name] = selection; - } - } - - // calcNodesCount returns number of buckets and minimum number of nodes in every bucket - // for the given selector. - static (int bucketCount, int nodesInBucket) CalcNodesCount(FrostFsSelector selector) - { - return selector.Clause == (int)FrostFsClause.Same - ? (1, (int)selector.Count) - : ((int)selector.Count, 1); - } - - // getSelectionBase returns nodes grouped by selector attribute. - // It it guaranteed that each pair will contain at least one node. - internal NodeAttrPair[] GetSelectionBase(FrostFsSelector selector) - { - var fName = selector.Filter ?? throw new FrostFsException("Filter name for selector is empty"); - - _ = ProcessedFilters.TryGetValue(fName, out var f); - - var isMain = fName == mainFilterName; - var result = new List(); - - var nodeMap = new Dictionary>(); - var attr = selector.Attribute; - - foreach (var node in NetMap.NodeInfoCollection) - { - if (UsedNodes.ContainsKey(node.Hash())) - { - continue; - } - - if (isMain || Match(f, node)) - { - if (attr == null) - { - // Default attribute is transparent identifier which is different for every node. - result.Add(new NodeAttrPair { attr = "", nodes = [node] }); - } - else - { - var v = node.Attributes[attr]; - if (!nodeMap.TryGetValue(v, out var nodes) || nodes == null) - { - nodeMap[v] = []; - } - - nodeMap[v].Add(node); - } - } - } - - if (!string.IsNullOrEmpty(attr)) - { - foreach (var v in nodeMap) - { - result.Add(new NodeAttrPair() { attr = v.Key, nodes = [.. v.Value] }); - } - } - - if (HrwSeed != null && HrwSeed.Length != 0) - { - double[] ws = []; - - var sortedNodes = new NodeAttrPair[result.Count]; - - for (int i = 0; i < result.Count; i++) - { - var res = result[i]; - Tools.AppendWeightsTo(res.nodes, weightFunc, ref ws); - sortedNodes[i].nodes = Tools.SortHasherSliceByWeightValue(res.nodes.ToList(), ws, HrwSeedHash).ToArray(); - sortedNodes[i].attr = result[i].attr; - } - - return sortedNodes; - } - return [.. result]; - } - - static double CalcBucketWeight(List ns, MeanIQRAgg a, Func wf) - { - foreach (var node in ns) - { - a.Add(wf(node)); - } - - return a.Compute(); - } - - // getSelection returns nodes grouped by s.attribute. - // Last argument specifies if more buckets can be used to fulfill CBF. - internal List> GetSelection(FrostFsSelector s) - { - var (bucketCount, nodesInBucket) = CalcNodesCount(s); - - var buckets = GetSelectionBase(s); - - if (Strict && buckets.Length < bucketCount) - throw new FrostFsException($"errNotEnoughNodes: '{s.Name}'"); - - // We need deterministic output in case there is no pivot. - // If pivot is set, buckets are sorted by HRW. - // However, because initial order influences HRW order for buckets with equal weights, - // we also need to have deterministic input to HRW sorting routine. - if (HrwSeed == null || HrwSeed.Length == 0) - { - buckets = string.IsNullOrEmpty(s.Attribute) - ? [.. buckets.OrderBy(b => b.nodes[0].Hash())] - : [.. buckets.OrderBy(b => b.attr)]; - } - - var maxNodesInBucket = nodesInBucket * (int)Cbf; - - var res = new List>(buckets.Length); - var fallback = new List>(buckets.Length); - - for (int i = 0; i < buckets.Length; i++) - { - var ns = buckets[i].nodes; - if (ns.Length >= maxNodesInBucket) - { - res.Add(ns.Take(maxNodesInBucket).ToList()); - } - else if (ns.Length >= nodesInBucket) - { - fallback.Add(new List(ns)); - } - } - - if (res.Count < bucketCount) - { - // Fallback to using minimum allowed backup factor (1). - res.AddRange(fallback); - - if (Strict && res.Count < bucketCount) - { - throw new FrostFsException($"{errNotEnoughNodes}: {s}"); - } - } - - if (HrwSeed != null && HrwSeed.Length != 0) - { - var weights = new double[res.Count]; - var a = new MeanIQRAgg(); - - for (int i = 0; i < res.Count; i++) - { - a.Clear(); - weights[i] = CalcBucketWeight(res[i], a, weightFunc); - } - - var hashers = res.Select(r => new HasherList(r)).ToList(); - hashers = Tools.SortHasherSliceByWeightValue(hashers, weights, HrwSeedHash); - - for (int i = 0; i < res.Count; i++) - { - res[i] = hashers[i].Nodes; - } - } - - if (res.Count < bucketCount) - { - if (Strict && res.Count == 0) - { - throw new FrostFsException(errNotEnoughNodes); - } - - bucketCount = res.Count; - } - - if (string.IsNullOrEmpty(s.Attribute)) - { - fallback = res.Skip(bucketCount).ToList(); - res = res.Take(bucketCount).ToList(); - - for (int i = 0; i < fallback.Count; i++) - { - var index = i % bucketCount; - if (res[index].Count >= maxNodesInBucket) - { - break; - } - - res[index].AddRange(fallback[i]); - } - } - - return res.Take(bucketCount).ToList(); - } - - internal bool MatchKeyValue(FrostFsFilter f, FrostFsNodeInfo nodeInfo) - { - switch (f.Operation) - { - case (int)Operation.EQ: - return nodeInfo.Attributes.TryGetValue(f.Key, out var val) && val == f.Value; - case (int)Operation.LIKE: - { - var hasPrefix = f.Value.StartsWith(likeWildcard, StringComparison.Ordinal); - var hasSuffix = f.Value.EndsWith(likeWildcard, StringComparison.Ordinal); - - var start = hasPrefix ? likeWildcard.Length : 0; - var end = hasSuffix ? f.Value.Length - likeWildcard.Length : f.Value.Length; - var str = f.Value.Substring(start, end - start); - - if (hasPrefix && hasSuffix) - return nodeInfo.Attributes[f.Key].Contains(str); - - if (hasPrefix && !hasSuffix) - return nodeInfo.Attributes[f.Key].EndsWith(str, StringComparison.Ordinal); - - if (!hasPrefix && hasSuffix) - return nodeInfo.Attributes[f.Key].StartsWith(str, StringComparison.Ordinal); - - - return nodeInfo.Attributes[f.Key] == f.Value; - } - case (int)Operation.NE: - return nodeInfo.Attributes[f.Key] != f.Value; - default: - { - ulong attr; - switch (f.Key) - { - case FrostFsNodeInfo.AttrPrice: - attr = nodeInfo.Price; - break; - - case FrostFsNodeInfo.AttrCapacity: - attr = nodeInfo.GetCapacity(); - break; - default: - if (!ulong.TryParse(nodeInfo.Attributes[f.Key], NumberStyles.Integer, CultureInfo.InvariantCulture, out attr)) - return false; - break; - } - - switch (f.Operation) - { - case (int)Operation.GT: - return attr > NumCache[f.Value]; - case (int)Operation.GE: - return attr >= NumCache[f.Value]; - case (int)Operation.LT: - return attr < NumCache[f.Value]; - case (int)Operation.LE: - return attr <= NumCache[f.Value]; - default: - // do nothing and return false - break; - } - } - break; - } - - // will not happen if context was created from f (maybe panic?) - return false; - } - - // match matches f against b. It returns no errors because - // filter should have been parsed during context creation - // and missing node properties are considered as a regular fail. - internal bool Match(FrostFsFilter f, FrostFsNodeInfo nodeInfo) - { - switch (f.Operation) - { - case (int)Operation.NOT: - { - var inner = f.Filters; - var fSub = inner[0]; - - if (!string.IsNullOrEmpty(inner[0].Name)) - { - fSub = ProcessedFilters[inner[0].Name]; - } - return !Match(fSub, nodeInfo); - } - case (int)Operation.AND: - case (int)Operation.OR: - { - for (int i = 0; i < f.Filters.Length; i++) - { - var fSub = f.Filters[i]; - - if (!string.IsNullOrEmpty(f.Filters[i].Name)) - { - fSub = ProcessedFilters[f.Filters[i].Name]; - } - - var ok = Match(fSub, nodeInfo); - - if (ok == (f.Operation == (int)Operation.OR)) - { - return ok; - } - } - - return f.Operation == (int)Operation.AND; - } - default: - return MatchKeyValue(f, nodeInfo); - } - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/HasherList.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/HasherList.cs deleted file mode 100644 index 17fabfe..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/HasherList.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; - -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal sealed class HasherList : IHasher -{ - private readonly List _nodes; - - internal HasherList(List nodes) - { - _nodes = nodes; - } - - internal List Nodes - { - get - { - return _nodes; - } - } - - public ulong Hash() - { - return _nodes.Count > 0 ? _nodes[0].Hash() : 0; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/IAggregator.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/IAggregator.cs deleted file mode 100644 index 774dbc2..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/IAggregator.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal interface IAggregator -{ - void Add(double d); - - double Compute(); -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/IHasher.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/IHasher.cs deleted file mode 100644 index 6a0f536..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/IHasher.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal interface IHasher -{ - ulong Hash(); -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/INormalizer.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/INormalizer.cs deleted file mode 100644 index ddf4169..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/INormalizer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -interface INormalizer -{ - double Normalize(double w); -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanAgg.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanAgg.cs deleted file mode 100644 index 98187b3..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanAgg.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct MeanAgg -{ - private double mean; - private int count; - - internal void Add(double n) - { - int c = count + 1; - mean = mean * count / c + n / c; - - count++; - } - - internal readonly double Compute() - { - return mean; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanIQRAgg.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanIQRAgg.cs deleted file mode 100644 index e7f6fca..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MeanIQRAgg.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.ObjectModel; -using System.Linq; - -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct MeanIQRAgg : IAggregator -{ - private const int minLn = 4; - internal Collection arr = []; - - public MeanIQRAgg() - { - } - - public readonly void Add(double d) - { - arr.Add(d); - } - - public readonly double Compute() - { - var length = arr.Count; - if (length == 0) - { - return 0; - } - - var sorted = arr.OrderBy(p => p).ToArray(); - - double minV, maxV; - - if (arr.Count < minLn) - { - minV = sorted[0]; - maxV = sorted[length - 1]; - } - else - { - var start = length / minLn; - var end = length * 3 / minLn - 1; - - minV = sorted[start]; - maxV = sorted[end]; - } - - var count = 0; - double sum = 0; - - foreach (var e in sorted) - { - if (e >= minV && e <= maxV) - { - sum += e; - count++; - } - } - - return sum / count; - } - - internal readonly void Clear() - { - arr.Clear(); - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MinAgg.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/MinAgg.cs deleted file mode 100644 index 7b68fc0..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/MinAgg.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct MinAgg -{ - private double min; - private bool minFound; - - internal void Add(double n) - { - if (!minFound) - { - min = n; - minFound = true; - return; - } - - if (n < min) - { - min = n; - } - } - - internal readonly double Compute() - { - return min; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Operation.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/Operation.cs deleted file mode 100644 index 705827e..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Operation.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -public enum Operation -{ - Unspecified = 0, - EQ, - NE, - GT, - GE, - LT, - LE, - OR, - AND, - NOT, - LIKE -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/ReverseMinNorm.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/ReverseMinNorm.cs deleted file mode 100644 index d3ffe5c..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/ReverseMinNorm.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct ReverseMinNorm : INormalizer -{ - internal double min; - - public readonly double Normalize(double w) => (min + 1) / (w + 1); -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/SelectFilterExpr.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/SelectFilterExpr.cs deleted file mode 100644 index 22cb961..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/SelectFilterExpr.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.ObjectModel; - -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal struct SelectFilterExpr(uint cbf, FrostFsSelector selector, Collection filters) -{ - internal uint Cbf { get; } = cbf; - internal FrostFsSelector Selector { get; } = selector; - internal Collection Filters { get; } = filters; -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/SigmoidNorm.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/SigmoidNorm.cs deleted file mode 100644 index e4cd416..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/SigmoidNorm.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -internal readonly struct SigmoidNorm : INormalizer -{ - private readonly double _scale; - - internal SigmoidNorm(double scale) - { - _scale = scale; - } - - public readonly double Normalize(double w) - { - if (_scale == 0) - { - return 0; - } - - var x = w / _scale; - - return x / (1 + x); - } -} diff --git a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Tools.cs b/src/FrostFS.SDK.Client/Models/Netmap/Placement/Tools.cs deleted file mode 100644 index e87994a..0000000 --- a/src/FrostFS.SDK.Client/Models/Netmap/Placement/Tools.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using static FrostFS.SDK.FrostFsNetmapSnapshot; - -namespace FrostFS.SDK.Client.Models.Netmap.Placement; - -public static class Tools -{ - internal static ulong Distance(ulong x, ulong y) - { - var acc = x ^ y; - acc ^= acc >> 33; - acc *= 0xff51afd7ed558ccd; - acc ^= acc >> 33; - acc *= 0xc4ceb9fe1a85ec53; - acc ^= acc >> 33; - - return acc; - } - - internal static double ReverceNormalize(double r, double w) - { - return (r + 1) / (w + 1); - } - - internal static double Normalize(double r, double w) - { - if (r == 0) - { - return 0; - } - - var x = w / r; - return x / (1 + x); - } - - internal static void AppendWeightsTo(FrostFsNodeInfo[] nodes, Func wf, ref double[] weights) - { - if (weights.Length < nodes.Length) - { - weights = new double[nodes.Length]; - } - - for (int i = 0; i < nodes.Length; i++) - { - weights[i] = wf(nodes[i]); - } - } - - internal static List SortHasherSliceByWeightValue(List nodes, Span weights, ulong hash) where T : IHasher - { - if (nodes.Count == 0) - { - return nodes; - } - - var allEquals = true; - - if (weights.Length > 1) - { - for (int i = 1; i < weights.Length; i++) - { - if (weights[i] != weights[0]) - { - allEquals = false; - break; - } - } - } - - var dist = new double[nodes.Count]; - - if (allEquals) - { - for (int i = 0; i < dist.Length; i++) - { - var x = nodes[i].Hash(); - dist[i] = Distance(x, hash); - } - - return SortHasherByDistance(nodes, dist, true); - } - - for (int i = 0; i < dist.Length; i++) - { - var d = Distance(nodes[i].Hash(), hash); - dist[i] = (ulong.MaxValue - d) * weights[i]; - } - - return SortHasherByDistance(nodes, dist, false); - } - - internal static List SortHasherByDistance(List nodes, N[] dist, bool asc) - { - IndexedValue[] indexes = new IndexedValue[nodes.Count]; - for (int i = 0; i < dist.Length; i++) - { - indexes[i] = new IndexedValue() { nodeInfo = nodes[i], dist = dist[i] }; - } - - if (asc) - { - return new List(indexes - .OrderBy(x => x.dist) - .Select(x => x.nodeInfo).ToArray()); - } - else - { - return new List(indexes - .OrderByDescending(x => x.dist) - .Select(x => x.nodeInfo)); - } - } - - internal static Func DefaultWeightFunc(IReadOnlyList nodes) - { - MeanAgg mean = new(); - MinAgg minV = new(); - - foreach (var node in nodes) - { - mean.Add(node.GetCapacity()); - minV.Add(node.Price); - } - - return NewWeightFunc( - NewSigmoidNorm(mean.Compute()), - NewReverseMinNorm(minV.Compute())); - } - - private struct IndexedValue - { - internal T nodeInfo; - internal N dist; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsAddress.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsAddress.cs deleted file mode 100644 index bf17f18..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsAddress.cs +++ /dev/null @@ -1,48 +0,0 @@ -using FrostFS.Refs; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK; - -public class FrostFsAddress -{ - private FrostFsObjectId? frostFsObjectId; - private FrostFsContainerId? frostFsContainerId; - private ObjectID? objectId; - private ContainerID? containerId; - - public FrostFsAddress(FrostFsContainerId frostFsContainerId, FrostFsObjectId frostFsObjectId) - { - FrostFsObjectId = frostFsObjectId ?? throw new System.ArgumentNullException(nameof(frostFsObjectId)); - FrostFsContainerId = frostFsContainerId ?? throw new System.ArgumentNullException(nameof(frostFsContainerId)); - } - - internal FrostFsAddress(ObjectID objectId, ContainerID containerId) - { - ObjectId = objectId ?? throw new System.ArgumentNullException(nameof(objectId)); - ContainerId = containerId ?? throw new System.ArgumentNullException(nameof(containerId)); - } - - public FrostFsObjectId FrostFsObjectId - { - get => frostFsObjectId ??= objectId!.ToModel(); - set => frostFsObjectId = value; - } - - public FrostFsContainerId FrostFsContainerId - { - get => frostFsContainerId ??= containerId!.ToModel(); - set => frostFsContainerId = value; - } - - public ObjectID ObjectId - { - get => objectId ??= frostFsObjectId!.ToMessage(); - set => objectId = value; - } - - public ContainerID ContainerId - { - get => containerId ??= frostFsContainerId!.ToMessage(); - set => containerId = value; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs deleted file mode 100644 index 9db7898..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FrostFS.SDK; - -public struct FrostFsAttributePair(string key, string value) : System.IEquatable -{ - public string Key { get; set; } = key; - - public string Value { get; set; } = value; - - public override bool Equals(object obj) - { - if (obj == null || obj is not FrostFsAttributePair) - return false; - - return Equals((FrostFsAttributePair)obj); - } - - public override int GetHashCode() - { - return Key.GetHashCode() ^ Value.GetHashCode(); - } - - public static bool operator ==(FrostFsAttributePair left, FrostFsAttributePair right) - { - return left.Equals(right); - } - - public static bool operator !=(FrostFsAttributePair left, FrostFsAttributePair right) - { - return !(left == right); - } - - public bool Equals(FrostFsAttributePair other) - { - return GetHashCode().Equals(other.GetHashCode()); - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsHeaderResult.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsHeaderResult.cs deleted file mode 100644 index 43ea299..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsHeaderResult.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrostFS.SDK; - -public class FrostFsHeaderResult -{ - public FrostFsObjectHeader? HeaderInfo { get; internal set; } - - public FrostFsSplitInfo? SplitInfo { get; internal set; } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsLargeObject.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsLargeObject.cs deleted file mode 100644 index 12a95c6..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsLargeObject.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FrostFS.SDK; - -public class FrostFsLargeObject(FrostFsContainerId container) : FrostFsObject(container) -{ - public ulong PayloadLength - { - get { return Header!.PayloadLength; } - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsLinkObject.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsLinkObject.cs deleted file mode 100644 index f43c6d0..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsLinkObject.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace FrostFS.SDK; - -public class FrostFsLinkObject : FrostFsObject -{ - public FrostFsLinkObject(FrostFsContainerId containerId, - SplitId splitId, - FrostFsObjectHeader largeObjectHeader, - IList children) - : base(containerId) - { - Header!.Split = new FrostFsSplit(splitId, - null, - null, - largeObjectHeader, - null, - new ReadOnlyCollection(children)); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObject.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsObject.cs deleted file mode 100644 index e22016a..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObject.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -namespace FrostFS.SDK; - -public class FrostFsObject -{ - /// - /// Creates new instance from ObjectHeader - /// - /// - public FrostFsObject(FrostFsObjectHeader header) - { - Header = header; - } - - /// - /// Creates new instance with specified parameters - /// - /// - /// - public FrostFsObject(FrostFsContainerId container, FrostFsObjectType objectType = FrostFsObjectType.Regular) - { - Header = new FrostFsObjectHeader(containerId: container, type: objectType); - } - - /// - /// Header contains metadata for the object - /// - /// - public FrostFsObjectHeader Header { get; set; } - - /// - /// The value is calculated internally as a hash of ObjectHeader. Do not use pre-calculated value is the object has been changed. - /// - public FrostFsObjectId? ObjectId - { - get; set; - } - - /// - /// A payload is obtained via stream reader - /// - /// Reader for received data - public IObjectReader? ObjectReader { get; set; } - - public ReadOnlyMemory SingleObjectPayload { get; set; } - - /// - /// Provide SHA256 hash of the payload. If null, the hash is calculated by internal logic - /// - public byte[]? PayloadHash { get; set; } - - /// - /// Applied only for the last Object in chain in case of manual multipart uploading - /// - /// Parent for multipart object - public void SetParent(FrostFsObjectHeader largeObjectHeader) - { - if (Header?.Split == null) - throw new ArgumentNullException(nameof(largeObjectHeader), "Split value must not be null"); - - Header.Split.ParentHeader = largeObjectHeader; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectFilter.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectFilter.cs deleted file mode 100644 index f2f21d7..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectFilter.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace FrostFS.SDK; - -public interface IObjectFilter -{ - public FrostFsMatchType MatchType { get; set; } - public string Key { get; set; } - - string? GetSerializedValue(); -} - -public abstract class FrostFsObjectFilter(FrostFsMatchType matchType, string key, T value) : IObjectFilter -{ - public FrostFsMatchType MatchType { get; set; } = matchType; - public string Key { get; set; } = key; - - public T Value { get; set; } = value; - - public string? GetSerializedValue() - { - return Value?.ToString(); - } -} - -/// -/// Creates filter to search by Attribute -/// -/// Match type -/// Attribute key -/// Attribute value -public class FilterByAttributePair(FrostFsMatchType matchType, string key, string value) : FrostFsObjectFilter(matchType, key, value) { } - -/// -/// Creates filter to search by ObjectId -/// -/// Match type -/// ObjectId -public class FilterByObjectId(FrostFsMatchType matchType, FrostFsObjectId objectId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderObjectID, objectId) { } - -/// -/// Creates filter to search by OwnerId -/// -/// Match type -/// ObjectId -public class FilterByOwnerId(FrostFsMatchType matchType, FrostFsOwner ownerId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderOwnerID, ownerId) { } - -/// -/// Creates filter to search by Version -/// -/// Match type -/// Version -public class FilterByVersion(FrostFsMatchType matchType, FrostFsVersion version) : FrostFsObjectFilter(matchType, Constants.FilterHeaderVersion, version) { } - -/// -/// Creates filter to search by ContainerId -/// -/// Match type -/// ContainerId -public class FilterByContainerId(FrostFsMatchType matchType, FrostFsContainerId containerId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderContainerID, containerId) { } - -/// -/// Creates filter to search by creation Epoch -/// -/// Match type -/// Creation Epoch -public class FilterByEpoch(FrostFsMatchType matchType, ulong epoch) : FrostFsObjectFilter(matchType, Constants.FilterHeaderCreationEpoch, epoch) { } - -/// -/// Creates filter to search by Payload Length -/// -/// Match type -/// Payload Length -public class FilterByPayloadLength(FrostFsMatchType matchType, ulong payloadLength) : FrostFsObjectFilter(matchType, Constants.FilterHeaderPayloadLength, payloadLength) { } - -/// -/// Creates filter to search by Payload Hash -/// -/// Match type -/// Payload Hash -public class FilterByPayloadHash(FrostFsMatchType matchType, CheckSum payloadHash) : FrostFsObjectFilter(matchType, Constants.FilterHeaderPayloadHash, payloadHash) { } - -/// -/// Creates filter to search by Parent -/// -/// Match type -/// Parent -public class FilterByParent(FrostFsMatchType matchType, FrostFsObjectId parentId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderParent, parentId) { } - -/// -/// Creates filter to search by SplitId -/// -/// Match type -/// SplitId -public class FilterBySplitId(FrostFsMatchType matchType, SplitId splitId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderSplitID, splitId) { } - -/// -/// Creates filter to search by Payload Hash -/// -/// Match type -/// Payload Hash -public class FilterByECParent(FrostFsMatchType matchType, FrostFsObjectId ecParentId) : FrostFsObjectFilter(matchType, Constants.FilterHeaderECParent, ecParentId) { } - -/// -/// Creates filter to search Root objects -/// -public class FilterByRootObject() : FrostFsObjectFilter(FrostFsMatchType.Unspecified, Constants.FilterHeaderRoot, string.Empty) { } - -/// -/// Creates filter to search objects that are physically stored on the server -/// (FrostFsMatchType.Unspecified, Constants.FilterHeaderPhy, string.Empty) { } - diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectHeader.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectHeader.cs deleted file mode 100644 index 4f1c5d0..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectHeader.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; - -using FrostFS.Object; - -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK; - -public class FrostFsObjectHeader( - FrostFsContainerId containerId, - FrostFsObjectType type = FrostFsObjectType.Regular, - FrostFsAttributePair[]? attributes = null, - FrostFsSplit? split = null, - FrostFsOwner? owner = null, - FrostFsVersion? version = null) -{ - private Header? header; - private Container.Container.Types.Attribute[]? grpsAttributes; - - public ReadOnlyCollection? Attributes { get; internal set; } = - attributes == null ? null : - new ReadOnlyCollection(attributes); - - public FrostFsContainerId ContainerId { get; } = containerId; - - public ulong PayloadLength { get; set; } - - public byte[]? PayloadCheckSum { get; set; } - - public FrostFsObjectType ObjectType { get; } = type; - - public FrostFsOwner? OwnerId { get; internal set; } = owner; - - public FrostFsVersion? Version { get; internal set; } = version; - - public FrostFsSplit? Split { get; internal set; } = split; - - internal Container.Container.Types.Attribute[]? GetGrpsAttributes() - { - grpsAttributes ??= Attributes? - .Select(a => new Container.Container.Types.Attribute { Key = a.Key, Value = a.Value }) - .ToArray(); - - return grpsAttributes; - } - - public Header GetHeader() - { - if (header == null) - { - var objTypeName = ObjectType switch - { - FrostFsObjectType.Regular => Object.ObjectType.Regular, - FrostFsObjectType.Lock => Object.ObjectType.Lock, - FrostFsObjectType.Tombstone => Object.ObjectType.Tombstone, - _ => throw new ArgumentException($"Unknown ObjectType. Value: '{ObjectType}'.") - }; - - this.header = new Header - { - OwnerId = OwnerId?.ToMessage(), - Version = Version?.ToMessage(), - ContainerId = ContainerId.ToMessage(), - ObjectType = objTypeName, - PayloadLength = PayloadLength - }; - - if (Attributes != null) - { - foreach (var attribute in Attributes) - { - this.header.Attributes.Add(attribute.ToMessage()); - } - } - - var split = Split; - if (split != null) - { - this.header.Split = new Header.Types.Split - { - SplitId = split!.SplitId != null ? split.SplitId.GetSplitId() : null - }; - } - } - - return this.header; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs deleted file mode 100644 index 228edf2..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsOwner.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Security.Cryptography; - -using FrostFS.Refs; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; - - -namespace FrostFS.SDK; - -public class FrostFsOwner(string id) -{ - private OwnerID? ownerID; - - public string Value { get; } = id; - - public static FrostFsOwner FromKey(ECDsa key) - { - return new FrostFsOwner(key.PublicKey().PublicKeyToAddress()); - } - - internal OwnerID OwnerID - { - get - { - ownerID ??= this.ToMessage(); - return ownerID; - } - } - - public byte[] ToHash() - { - return Base58.Decode(Value); - } - - public override string ToString() - { - return Value; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsRange.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsRange.cs deleted file mode 100644 index b50568f..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsRange.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace FrostFS.SDK; - -public readonly struct FrostFsRange(ulong offset, ulong length) : System.IEquatable -{ - public ulong Offset { get; } = offset; - - public ulong Length { get; } = length; - - public override readonly bool Equals(object obj) => this == (FrostFsRange)obj; - - public override readonly int GetHashCode() => $"{Offset}{Length}".GetHashCode(); - - public static bool operator ==(FrostFsRange left, FrostFsRange right) - { - return left.Equals(right); - } - - public static bool operator !=(FrostFsRange left, FrostFsRange right) - { - return !(left == right); - } - - public readonly bool Equals(FrostFsRange other) - { - return this == other; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsSplit.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsSplit.cs deleted file mode 100644 index 5afd46b..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsSplit.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.ObjectModel; -using System.Linq; -using FrostFS.Object; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK; - -public class FrostFsSplit(SplitId splitId, - FrostFsObjectId? previous = null, - FrostFsObjectId? parent = null, - FrostFsObjectHeader? parentHeader = null, - FrostFsSignature? parentSignature = null, - ReadOnlyCollection? children = null) -{ - private Header.Types.Split? _split; - - public FrostFsSplit() : this(new SplitId()) - { - } - - public SplitId SplitId { get; private set; } = splitId; - - public FrostFsObjectId? Previous { get; } = previous; - - public FrostFsObjectId? Parent { get; } = parent; - - public FrostFsSignature? ParentSignature { get; } = parentSignature; - - public FrostFsObjectHeader? ParentHeader { get; set; } = parentHeader; - - public ReadOnlyCollection? Children { get; } = children; - - public Header.Types.Split GetSplit() - { - if (_split == null) - { - _split = new Header.Types.Split - { - SplitId = SplitId?.GetSplitId(), - Parent = Parent?.ToMessage(), - ParentHeader = ParentHeader?.GetHeader(), - ParentSignature = ParentSignature?.ToMessage() - }; - - if (Children != null) - { - _split.Children.AddRange(Children.Select(x => x.ToMessage())); - } - } - - return _split; - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsSplitInfo.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsSplitInfo.cs deleted file mode 100644 index d8f8783..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsSplitInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FrostFS.Object; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK; - -public class FrostFsSplitInfo -{ - private readonly SplitInfo _splitInfo; - - private SplitId? _splitId; - - private FrostFsObjectId? _link; - - private FrostFsObjectId? _lastPart; - - internal FrostFsSplitInfo(SplitInfo splitInfo) - { - _splitInfo = splitInfo; - } - - public SplitId SplitId => _splitId ??= new SplitId(_splitInfo.SplitId.ToUuid()); - - public FrostFsObjectId? Link => _link ??= _splitInfo.Link == null - ? null : FrostFsObjectId.FromHash(_splitInfo.Link.Value.Span); - - public FrostFsObjectId? LastPart => _lastPart ??= _splitInfo.LastPart == null - ? null : FrostFsObjectId.FromHash(_splitInfo.LastPart.Value.Span); -} diff --git a/src/FrostFS.SDK.Client/Models/Object/IObjectReader.cs b/src/FrostFS.SDK.Client/Models/Object/IObjectReader.cs deleted file mode 100644 index 8b08e6b..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/IObjectReader.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace FrostFS.SDK; - -public interface IObjectReader : IDisposable -{ - ValueTask?> ReadChunk(CancellationToken cancellationToken = default); -} diff --git a/src/FrostFS.SDK.Client/Models/Object/PartUploadedEventArgs.cs b/src/FrostFS.SDK.Client/Models/Object/PartUploadedEventArgs.cs deleted file mode 100644 index 16ad326..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/PartUploadedEventArgs.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public class PartUploadedEventArgs(ObjectPartInfo part) : EventArgs -{ - public ObjectPartInfo Part { get; } = part; -} diff --git a/src/FrostFS.SDK.Client/Models/Object/SplitId.cs b/src/FrostFS.SDK.Client/Models/Object/SplitId.cs deleted file mode 100644 index a113361..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/SplitId.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; - -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK; - -public class SplitId -{ - private readonly Guid id; - - private ByteString? message; - - public SplitId() - { - this.id = Guid.NewGuid(); - } - - public SplitId(Guid id) - { - this.id = id; - } - - private SplitId(byte[] binary) - { - this.id = new Guid(binary); - } - - private SplitId(string str) - { - this.id = new Guid(str); - } - - public static SplitId CreateFromBinary(byte[] binaryData) - { - return new SplitId(binaryData); - } - - public static SplitId CreateFromString(string stringData) - { - return new SplitId(stringData); - } - - public override string ToString() - { - return this.id.ToString(); - } - - public ByteString? GetSplitId() - { - Span span = stackalloc byte[16]; - id.ToBytes(span); - - return this.message ??= ByteString.CopyFrom(span); - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs b/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs deleted file mode 100644 index ca96c7d..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct ObjectPartInfo(long offset, int length, FrostFsObjectId objectId) : System.IEquatable -{ - public long Offset { get; } = offset; - public int Length { get; } = length; - public FrostFsObjectId ObjectId { get; } = objectId; - - public override bool Equals(object obj) - { - if (obj == null || obj is not ObjectPartInfo) - return false; - - return Equals((ObjectPartInfo)obj); - } - - public override int GetHashCode() - { - return ((int)(Offset >> 32)) ^ (int)Offset ^ Length ^ ObjectId.Value.GetHashCode(); - } - - public static bool operator ==(ObjectPartInfo left, ObjectPartInfo right) - { - return left.Equals(right); - } - - public static bool operator !=(ObjectPartInfo left, ObjectPartInfo right) - { - return !(left == right); - } - - public bool Equals(ObjectPartInfo other) - { - return GetHashCode() == other.GetHashCode(); - } -} diff --git a/src/FrostFS.SDK.Client/Models/Object/UploadProgressInfo.cs b/src/FrostFS.SDK.Client/Models/Object/UploadProgressInfo.cs deleted file mode 100644 index a6fa905..0000000 --- a/src/FrostFS.SDK.Client/Models/Object/UploadProgressInfo.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; - -namespace FrostFS.SDK.Client; - -public class UploadProgressInfo -{ - private List _parts; - - public UploadProgressInfo(Guid splitId, int capacity = 8) - { - _parts = new List(capacity); - SplitId = new SplitId(splitId); - } - - public UploadProgressInfo(Guid splitId, Collection parts) - { - _parts = [.. parts]; - SplitId = new SplitId(splitId); - } - - public event EventHandler? Notify; - - public SplitId SplitId { get; } - - internal void AddPart(ObjectPartInfo part) - { - _parts.Add(part); - Notify?.Invoke(this, new PartUploadedEventArgs(part)); - } - - public ObjectPartInfo GetPart(int index) - { - return _parts[index]; - } - - public ObjectPartInfo GetLast() - { - return _parts.LastOrDefault(); - } - - public ReadOnlyCollection GetParts() - { - return new ReadOnlyCollection(_parts); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs b/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs deleted file mode 100644 index 7b003e7..0000000 --- a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace FrostFS.SDK; - -public class FrostFsResponseStatus(FrostFsStatusCode code, string? message = null, string? details = null) -{ - public FrostFsStatusCode Code { get; set; } = code; - - public string Message { get; set; } = message ?? string.Empty; - - public string Details { get; set; } = details ?? string.Empty; - - public bool IsSuccess => Code == FrostFsStatusCode.Success; - - public override string ToString() - { - return $"Response status: {Code}. Message: {Message}. Details: {Details}"; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Response/FrostFsSignature.cs b/src/FrostFS.SDK.Client/Models/Response/FrostFsSignature.cs deleted file mode 100644 index 995b161..0000000 --- a/src/FrostFS.SDK.Client/Models/Response/FrostFsSignature.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FrostFS.SDK; - -public class FrostFsSignature() -{ - public byte[]? Key { get; set; } - - public byte[]? Sign { get; set; } - - public SignatureScheme Scheme { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs b/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs deleted file mode 100644 index 547b2b5..0000000 --- a/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FrostFS.SDK; - -public class MetaHeader(FrostFsVersion version, ulong epoch, uint ttl) -{ - public FrostFsVersion Version { get; set; } = version; - public ulong Epoch { get; set; } = epoch; - public uint Ttl { get; set; } = ttl; - - public static MetaHeader Default() - { - return new MetaHeader( - new FrostFsVersion( - major: 2, - minor: 13 - ), - epoch: 0, - ttl: 2 - ); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs b/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs deleted file mode 100644 index 808e91b..0000000 --- a/src/FrostFS.SDK.Client/Models/Session/FrostFsSessionToken.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using FrostFS.Refs; - -using FrostFS.SDK.Client; -using FrostFS.SDK.Cryptography; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.SDK; - -public class FrostFsSessionToken -{ - private Guid _id; - private ReadOnlyMemory _sessionKey; - private readonly SessionToken.Types.Body _body; - - private FrostFsSessionToken() - { - ProtoId = ByteString.Empty; - ProtoSessionKey = ByteString.Empty; - _body = new SessionToken.Types.Body(); - } - - internal FrostFsSessionToken(SessionToken token) - { - ProtoId = token.Body.Id; - ProtoSessionKey = token.Body.SessionKey; - - _body = token.Body; - } - - public Guid Id - { - get - { - if (_id == Guid.Empty) - { - _id = ProtoId.ToUuid(); - } - - return _id; - } - } - - public ReadOnlyMemory SessionKey - { - get - { - if (_sessionKey.IsEmpty) - { - _sessionKey = ProtoSessionKey.Memory; - } - - return _sessionKey; - } - } - - internal ByteString ProtoId { get; } - - internal ByteString ProtoSessionKey { get; } - - public SessionToken CreateContainerToken(ContainerID? containerId, ContainerSessionContext.Types.Verb verb, ClientKey key) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - SessionToken sessionToken = new() { Body = _body.Clone() }; - - sessionToken.Body.Container = new() { Verb = verb }; - - if (containerId != null) - { - sessionToken.Body.Container.ContainerId = containerId; - } - else - { - sessionToken.Body.Container.Wildcard = true; - } - - sessionToken.Body.SessionKey = key.PublicKeyProto; - sessionToken.Signature = key.SignMessagePart(sessionToken.Body); - - return sessionToken; - } - - public SessionToken CreateObjectTokenContext(Address address, ObjectSessionContext.Types.Verb verb, ClientKey key) - { - if (address is null) - { - throw new ArgumentNullException(nameof(address)); - } - - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - SessionToken sessionToken = new() - { - Body = _body.Clone() - }; - - ObjectSessionContext.Types.Target target = new() { Container = address.ContainerId }; - - if (address.ObjectId != null) - { - target.Objects.Add(address.ObjectId); - } - - sessionToken.Body.Object = new() - { - Target = target, - Verb = verb - }; - - sessionToken.Signature = key.SignMessagePart(sessionToken.Body); - - return sessionToken; - } -} diff --git a/src/FrostFS.SDK.Client/ObjectWriter.cs b/src/FrostFS.SDK.Client/ObjectWriter.cs deleted file mode 100644 index a1859cc..0000000 --- a/src/FrostFS.SDK.Client/ObjectWriter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Threading.Tasks; - -using FrostFS.Object; -using FrostFS.SDK.Client.Interfaces; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client -{ - internal sealed class ObjectWriter : IObjectWriter - { - private readonly ClientContext ctx; - private readonly PrmObjectPutBase args; - private readonly ObjectStreamer streamer; - private bool disposedValue; - - internal ObjectWriter(ClientContext ctx, PrmObjectPutBase args, ObjectStreamer streamer) - { - this.ctx = ctx; - this.args = args; - this.streamer = streamer; - } - - public async Task WriteAsync(ReadOnlyMemory memory) - { - var chunkRequest = new PutRequest - { - Body = new PutRequest.Types.Body - { - Chunk = UnsafeByteOperations.UnsafeWrap(memory) - } - }; - - chunkRequest.AddMetaHeader(args.XHeaders); - - chunkRequest.Sign(this.ctx.Key); - - await streamer.Write(chunkRequest).ConfigureAwait(false); - } - - public async Task CompleteAsync() - { - var response = await streamer.Close().ConfigureAwait(false); - - Verifier.CheckResponse(response); - - return FrostFsObjectId.FromHash(response.Body.ObjectId.Value.Span); - } - - private void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - streamer.Dispose(); - } - - disposedValue = true; - } - } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/CallContext.cs b/src/FrostFS.SDK.Client/Parameters/CallContext.cs deleted file mode 100644 index 018b7fc..0000000 --- a/src/FrostFS.SDK.Client/Parameters/CallContext.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Threading; - -namespace FrostFS.SDK.Client; - -public readonly struct CallContext(TimeSpan timeout, CancellationToken cancellationToken = default) : IEquatable -{ - public CancellationToken CancellationToken { get; } = cancellationToken; - - public TimeSpan Timeout { get; } = timeout; - - internal readonly DateTime? GetDeadline() - { - return Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; - } - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not CallContext) - return false; - - return Equals((CallContext)obj); - } - - public bool Equals(CallContext other) - { - return Timeout == other.Timeout && CancellationToken.Equals(other.CancellationToken); - } - - public override int GetHashCode() - { - return CancellationToken.GetHashCode() ^ Timeout.GetHashCode(); - } - - public static bool operator ==(CallContext left, CallContext right) - { - return left.Equals(right); - } - - public static bool operator !=(CallContext left, CallContext right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/ISessionToken.cs b/src/FrostFS.SDK.Client/Parameters/ISessionToken.cs deleted file mode 100644 index de1a376..0000000 --- a/src/FrostFS.SDK.Client/Parameters/ISessionToken.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FrostFS.SDK.Client; - -public interface ISessionToken -{ - /// - /// Object represents token of the FrostFS Object session. A session is opened between any two sides of the - /// system, and implements a mechanism for transferring the power of attorney of actions to another network - /// member. The session has a limited validity period, and applies to a strictly defined set of operations. - /// - /// Instance of the session obtained from the server - FrostFsSessionToken? SessionToken { get; } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs deleted file mode 100644 index adcc1b9..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainAdd.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmApeChainAdd(FrostFsChainTarget target, FrostFsChain chain, string[]? xheaders = null) : System.IEquatable -{ - public FrostFsChainTarget Target { get; } = target; - - public FrostFsChain Chain { get; } = chain; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmApeChainAdd) - return false; - - return Equals((PrmApeChainAdd)obj); - } - - public readonly bool Equals(PrmApeChainAdd other) - { - return Target == other.Target - && Chain == other.Chain - && XHeaders == other.XHeaders; - } - - public override readonly int GetHashCode() - { - return Chain.GetHashCode() ^ Target.GetHashCode() ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmApeChainAdd left, PrmApeChainAdd right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmApeChainAdd left, PrmApeChainAdd right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs deleted file mode 100644 index a946840..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainList.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmApeChainList(FrostFsChainTarget target, string[]? xheaders = null) : System.IEquatable -{ - public FrostFsChainTarget Target { get; } = target; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmApeChainList) - return false; - - return Equals((PrmApeChainList)obj); - } - - public override readonly int GetHashCode() - { - return Target.GetHashCode() ^ XHeaders.GetHashCode(); - } - - public readonly bool Equals(PrmApeChainList other) - { - return Target == other.Target - && XHeaders == other.XHeaders; - } - - public static bool operator ==(PrmApeChainList left, PrmApeChainList right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmApeChainList left, PrmApeChainList right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs b/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs deleted file mode 100644 index 2303776..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmApeChainRemove.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmApeChainRemove( - FrostFsChainTarget target, - byte[] chainId, - string[]? xheaders = null) : System.IEquatable -{ - public FrostFsChainTarget Target { get; } = target; - - public byte[] ChainId { get; } = chainId; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmApeChainRemove) - return false; - - return Equals((PrmApeChainRemove)obj); - } - - public readonly bool Equals(PrmApeChainRemove other) - { - return Target == other.Target - && ChainId.Equals(other.ChainId) - && XHeaders == other.XHeaders; - } - - public override readonly int GetHashCode() - { - return ChainId.GetHashCode() ^ Target.GetHashCode() ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmApeChainRemove left, PrmApeChainRemove right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmApeChainRemove left, PrmApeChainRemove right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmContainerCreate.cs b/src/FrostFS.SDK.Client/Parameters/PrmContainerCreate.cs deleted file mode 100644 index d5c6ca0..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmContainerCreate.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmContainerCreate( - FrostFsContainerInfo container, - PrmWait waitParams, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerInfo Container { get; } = container; - - /// - /// Since the container becomes available with some delay, it needs to poll the container status - /// - /// Rules for polling the result - public PrmWait WaitParams { get; } = waitParams; - - /// - /// Blank session token - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmContainerCreate) - return false; - - return Equals((PrmContainerCreate)obj); - } - - public readonly bool Equals(PrmContainerCreate other) - { - return Container == other.Container - && WaitParams == other.WaitParams - && SessionToken == other.SessionToken - && XHeaders == other.XHeaders; - } - - public override readonly int GetHashCode() - { - return Container.GetHashCode() - ^ WaitParams.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()) - ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmContainerCreate left, PrmContainerCreate right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmContainerCreate left, PrmContainerCreate right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmContainerDelete.cs b/src/FrostFS.SDK.Client/Parameters/PrmContainerDelete.cs deleted file mode 100644 index 1ad1b33..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmContainerDelete.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmContainerDelete( - FrostFsContainerId containerId, - PrmWait waitParams, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - /// - /// Since the container is removed with some delay, it needs to poll the container status - /// - /// Rules for polling the result - public PrmWait WaitParams { get; } = waitParams; - - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmContainerDelete) - return false; - - return Equals((PrmContainerDelete)obj); - } - - public readonly bool Equals(PrmContainerDelete other) - { - return ContainerId == other.ContainerId - && WaitParams.Equals(other.WaitParams); - } - - public override int GetHashCode() - { - return ContainerId.GetHashCode() ^ WaitParams.GetHashCode(); - } - - public static bool operator ==(PrmContainerDelete left, PrmContainerDelete right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmContainerDelete left, PrmContainerDelete right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmContainerGet.cs b/src/FrostFS.SDK.Client/Parameters/PrmContainerGet.cs deleted file mode 100644 index a2e34ae..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmContainerGet.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmContainerGet(FrostFsContainerId container, string[]? xheaders = null) : System.IEquatable -{ - public FrostFsContainerId Container { get; } = container; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmContainerGet) - return false; - - return Equals((PrmContainerGet)obj); - } - - public readonly bool Equals(PrmContainerGet other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return Container.GetHashCode() - ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmContainerGet left, PrmContainerGet right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmContainerGet left, PrmContainerGet right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmContainerGetAll.cs b/src/FrostFS.SDK.Client/Parameters/PrmContainerGetAll.cs deleted file mode 100644 index 50b408e..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmContainerGetAll.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public readonly struct PrmContainerGetAll(string[]? xheaders = null) : IEquatable -{ - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmContainerGetAll) - return false; - - return Equals((PrmContainerGetAll)obj); - } - - public readonly bool Equals(PrmContainerGetAll other) - { - return XHeaders == other.XHeaders; - } - - public override readonly int GetHashCode() - { - return XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmContainerGetAll left, PrmContainerGetAll right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmContainerGetAll left, PrmContainerGetAll right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectClientCutPut.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectClientCutPut.cs deleted file mode 100644 index 46f25e0..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectClientCutPut.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.IO; - -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectClientCutPut( - FrostFsObjectHeader? header, - Stream? payload, - int bufferMaxSize = 0, - FrostFsSessionToken? sessionToken = null, - byte[]? customBuffer = null, - string[]? xheaders = null, - UploadProgressInfo? progress = null) : PrmObjectPutBase, System.IEquatable -{ - /// - /// Need to provide values like ContainerId and ObjectType to create and object. - /// Optional parameters ike Attributes can be provided as well. - /// - /// Header with required parameters to create an object - public FrostFsObjectHeader? Header { get; } = header; - - /// - /// A stream with source data - /// - public Stream? Payload { get; } = payload; - - /// - /// Overrides default size of the buffer for stream transferring. - /// - /// Size of the buffer - public int BufferMaxSize { get; } = bufferMaxSize; - - /// - /// Allows to define a buffer for chunks to manage by the memory allocation and releasing. - /// - public byte[]? CustomBuffer { get; } = customBuffer; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public UploadProgressInfo? Progress { get; } = progress; - - internal PutObjectContext PutObjectContext { get; } = new(); - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectClientCutPut) - return false; - - return Equals((PrmObjectClientCutPut)obj); - } - - public readonly bool Equals(PrmObjectClientCutPut other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return BufferMaxSize - ^ (Header == null ? 0 : Header.GetHashCode()) - ^ (Payload == null ? 0 : Payload.GetHashCode()) - ^ (CustomBuffer == null ? 0 : CustomBuffer.GetHashCode()) - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()) - ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmObjectClientCutPut left, PrmObjectClientCutPut right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectClientCutPut left, PrmObjectClientCutPut right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectDelete.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectDelete.cs deleted file mode 100644 index fa12755..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectDelete.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectDelete( - FrostFsContainerId containerId, - FrostFsObjectId objectId, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - public FrostFsObjectId ObjectId { get; } = objectId; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectDelete) - return false; - - return Equals((PrmObjectDelete)obj); - } - - public readonly bool Equals(PrmObjectDelete other) - { - return ContainerId == other.ContainerId - && ObjectId == other.ObjectId - && SessionToken == other.SessionToken - && XHeaders == other.XHeaders; - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() ^ ObjectId.GetHashCode() ^ (SessionToken != null ? SessionToken.GetHashCode() : 1); - } - - public static bool operator ==(PrmObjectDelete left, PrmObjectDelete right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectDelete left, PrmObjectDelete right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectGet.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectGet.cs deleted file mode 100644 index b5ed7f8..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectGet.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectGet( - FrostFsContainerId containerId, - FrostFsObjectId objectId, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - public FrostFsObjectId ObjectId { get; } = objectId; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectGet) - return false; - - return Equals((PrmObjectGet)obj); - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() - ^ ObjectId.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()); - } - - public readonly bool Equals(PrmObjectGet other) - { - return ContainerId == other.ContainerId - && ObjectId == other.ObjectId - && SessionToken == other.SessionToken; - } - - public static bool operator ==(PrmObjectGet left, PrmObjectGet right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectGet left, PrmObjectGet right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectHeadGet.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectHeadGet.cs deleted file mode 100644 index 15e2a25..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectHeadGet.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectHeadGet( - FrostFsContainerId containerId, - FrostFsObjectId objectId, - bool raw = false, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) - : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - public FrostFsObjectId ObjectId { get; } = objectId; - - public bool Raw { get; } = raw; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectHeadGet) - return false; - - return Equals((PrmObjectHeadGet)obj); - } - - public readonly bool Equals(PrmObjectHeadGet other) - { - return ContainerId == other.ContainerId - && ObjectId == other.ObjectId - && SessionToken == other.SessionToken; - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() - ^ ObjectId.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()); - } - - public static bool operator ==(PrmObjectHeadGet left, PrmObjectHeadGet right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectHeadGet left, PrmObjectHeadGet right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectPatch.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectPatch.cs deleted file mode 100644 index 7b26f5b..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectPatch.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.IO; - -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectPatch( - FrostFsAddress address, - FrostFsRange range, - Stream? payload, - int maxChunkLength, - FrostFsSessionToken? sessionToken = null, - bool replaceAttributes = false, - FrostFsAttributePair[]? newAttributes = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsAddress Address { get; } = address; - - public FrostFsRange Range { get; } = range; - - /// - /// A stream with source data - /// - public Stream? Payload { get; } = payload; - - public FrostFsAttributePair[]? NewAttributes { get; } = newAttributes; - - public bool ReplaceAttributes { get; } = replaceAttributes; - - public int MaxChunkLength { get; } = maxChunkLength; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectPatch) - return false; - - return Equals((PrmObjectPatch)obj); - } - - public readonly bool Equals(PrmObjectPatch other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return Address.GetHashCode() - ^ Range.GetHashCode() - ^ (Payload == null ? 0 : Payload.GetHashCode()) - ^ (NewAttributes == null ? 0 : NewAttributes.GetHashCode()) - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()) - ^ (ReplaceAttributes ? 1 : 0) - ^ MaxChunkLength; - } - - public static bool operator ==(PrmObjectPatch left, PrmObjectPatch right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectPatch left, PrmObjectPatch right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectPut.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectPut.cs deleted file mode 100644 index 78eaf1e..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectPut.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace FrostFS.SDK.Client; - -internal interface PrmObjectPutBase : ISessionToken -{ - FrostFsObjectHeader? Header { get; } - - string[] XHeaders { get; } -} - - -public readonly struct PrmObjectPut( - FrostFsObjectHeader? header, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : PrmObjectPutBase, System.IEquatable -{ - /// - /// Need to provide values like ContainerId and ObjectType to create and object. - /// Optional parameters ike Attributes can be provided as well. - /// - /// Header with required parameters to create an object - public FrostFsObjectHeader? Header { get; } = header; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - internal PutObjectContext PutObjectContext { get; } = new(); - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectPut) - return false; - - return Equals((PrmObjectPut)obj); - } - - public readonly bool Equals(PrmObjectPut other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return (Header == null ? 0 : Header.GetHashCode()) - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()) - ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmObjectPut left, PrmObjectPut right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectPut left, PrmObjectPut right) - { - return !(left == right); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Parameters/PrmObjectSearch.cs b/src/FrostFS.SDK.Client/Parameters/PrmObjectSearch.cs deleted file mode 100644 index 508fbd0..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmObjectSearch.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmObjectSearch( - FrostFsContainerId containerId, - FrostFsSessionToken? token, - string[]? xheaders = null, - params IObjectFilter[] filters) : ISessionToken, System.IEquatable -{ - /// - /// Defines container for the search - /// - /// - public FrostFsContainerId ContainerId { get; } = containerId; - - /// - /// Defines the search criteria - /// - /// Collection of filters - public IObjectFilter[] Filters { get; } = filters; - - /// - public FrostFsSessionToken? SessionToken { get; } = token; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmObjectSearch) - return false; - - return Equals((PrmObjectSearch)obj); - } - - public readonly bool Equals(PrmObjectSearch other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() - ^ Filters.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()) - ^ XHeaders.GetHashCode(); - } - - public static bool operator ==(PrmObjectSearch left, PrmObjectSearch right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmObjectSearch left, PrmObjectSearch right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmRangeGet.cs b/src/FrostFS.SDK.Client/Parameters/PrmRangeGet.cs deleted file mode 100644 index ecb53ed..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmRangeGet.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmRangeGet( - FrostFsContainerId containerId, - FrostFsObjectId objectId, - FrostFsRange range, - bool raw = false, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - public FrostFsObjectId ObjectId { get; } = objectId; - - public FrostFsRange Range { get; } = range; - - public bool Raw { get; } = raw; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmRangeGet) - return false; - - return Equals((PrmRangeGet)obj); - } - - public readonly bool Equals(PrmRangeGet other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() - ^ ObjectId.GetHashCode() - ^ Range.GetHashCode() - ^ Raw.GetHashCode(); - } - - public static bool operator ==(PrmRangeGet left, PrmRangeGet right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmRangeGet left, PrmRangeGet right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmRangeHashGet.cs b/src/FrostFS.SDK.Client/Parameters/PrmRangeHashGet.cs deleted file mode 100644 index 0d74008..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmRangeHashGet.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmRangeHashGet( - FrostFsContainerId containerId, - FrostFsObjectId objectId, - FrostFsRange[] ranges, - byte[] salt, - FrostFsSessionToken? sessionToken = null, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsContainerId ContainerId { get; } = containerId; - - public FrostFsObjectId ObjectId { get; } = objectId; - - public FrostFsRange[] Ranges { get; } = ranges; - - public byte[] Salt { get; } = salt; - - /// - public FrostFsSessionToken? SessionToken { get; } = sessionToken; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmRangeHashGet) - return false; - - return Equals((PrmRangeHashGet)obj); - } - - public readonly bool Equals(PrmRangeHashGet other) - { - return GetHashCode() == other.GetHashCode(); - } - - public override readonly int GetHashCode() - { - return ContainerId.GetHashCode() - ^ ObjectId.GetHashCode() - ^ Ranges.GetHashCode() - ^ Salt.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()); - } - - public static bool operator ==(PrmRangeHashGet left, PrmRangeHashGet right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmRangeHashGet left, PrmRangeHashGet right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmSessionCreate.cs b/src/FrostFS.SDK.Client/Parameters/PrmSessionCreate.cs deleted file mode 100644 index 5a89ebf..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmSessionCreate.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace FrostFS.SDK.Client; - -public readonly struct PrmSessionCreate(ulong expiration, string[]? xheaders = null) : System.IEquatable -{ - public ulong Expiration { get; } = expiration; - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmSessionCreate) - return false; - - return Equals((PrmSessionCreate)obj); - } - - public override readonly int GetHashCode() - { - return Expiration.GetHashCode() ^ XHeaders.GetHashCode(); - } - - public readonly bool Equals(PrmSessionCreate other) - { - return Expiration == other.Expiration - && XHeaders == other.XHeaders; - } - - public static bool operator ==(PrmSessionCreate left, PrmSessionCreate right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmSessionCreate left, PrmSessionCreate right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmSingleObjectPut.cs b/src/FrostFS.SDK.Client/Parameters/PrmSingleObjectPut.cs deleted file mode 100644 index 6ca73df..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmSingleObjectPut.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace FrostFS.SDK.Client; - -public struct PrmSingleObjectPut( - FrostFsObject frostFsObject, - string[]? xheaders = null) : ISessionToken, System.IEquatable -{ - public FrostFsObject FrostFsObject { get; set; } = frostFsObject; - - /// - public FrostFsSessionToken? SessionToken { get; set; } - - /// - /// FrostFS request X-Headers - /// - public string[] XHeaders { get; } = xheaders ?? []; - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmSingleObjectPut) - return false; - - return Equals((PrmSingleObjectPut)obj); - } - - public override readonly int GetHashCode() - { - return FrostFsObject.GetHashCode() - ^ (SessionToken == null ? 0 : SessionToken.GetHashCode()); - } - - public readonly bool Equals(PrmSingleObjectPut other) - { - return FrostFsObject == other.FrostFsObject - && SessionToken == other.SessionToken; - } - - public static bool operator ==(PrmSingleObjectPut left, PrmSingleObjectPut right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmSingleObjectPut left, PrmSingleObjectPut right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PrmWait.cs b/src/FrostFS.SDK.Client/Parameters/PrmWait.cs deleted file mode 100644 index 2090ef3..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PrmWait.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; - -namespace FrostFS.SDK.Client; - -public readonly struct PrmWait(TimeSpan timeout, TimeSpan pollInterval) : IEquatable -{ - private static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(120); - private static TimeSpan DefaultPollInterval = TimeSpan.FromSeconds(5); - - public PrmWait(int timeout, int interval) : this(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(interval)) - { - } - - public static PrmWait DefaultParams { get; } = new PrmWait(DefaultTimeout, DefaultPollInterval); - - public TimeSpan Timeout { get; } = timeout.Ticks == 0 ? DefaultTimeout : timeout; - - public TimeSpan PollInterval { get; } = pollInterval.Ticks == 0 ? DefaultPollInterval : pollInterval; - - public readonly DateTime GetDeadline() - { - return DateTime.UtcNow.AddTicks(Timeout.Ticks); - } - - public override readonly bool Equals(object obj) - { - if (obj == null || obj is not PrmWait) - return false; - - return Equals((PrmWait)obj); - } - - public override readonly int GetHashCode() - { - return DefaultTimeout.GetHashCode() ^ DefaultPollInterval.GetHashCode(); - } - - public readonly bool Equals(PrmWait other) - { - return Timeout == other.Timeout && PollInterval == other.PollInterval; - } - - public static bool operator ==(PrmWait left, PrmWait right) - { - return left.Equals(right); - } - - public static bool operator !=(PrmWait left, PrmWait right) - { - return !(left == right); - } -} diff --git a/src/FrostFS.SDK.Client/Parameters/PutObjectContext.cs b/src/FrostFS.SDK.Client/Parameters/PutObjectContext.cs deleted file mode 100644 index 2976ec5..0000000 --- a/src/FrostFS.SDK.Client/Parameters/PutObjectContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FrostFS.SDK.Client; - -internal sealed class PutObjectContext -{ - internal int MaxObjectSizeCache { get; set; } - - internal ulong CurrentStreamPosition { get; set; } - - internal ulong FullLength { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs b/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs deleted file mode 100644 index ea45b37..0000000 --- a/src/FrostFS.SDK.Client/Services/AccountingServiceProvider.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Threading.Tasks; - -using FrostFS.Accounting; - -namespace FrostFS.SDK.Client; - -internal sealed class AccountingServiceProvider : ContextAccessor -{ - private readonly AccountingService.AccountingServiceClient? _accountingServiceClient; - - internal AccountingServiceProvider( - AccountingService.AccountingServiceClient? accountingServiceClient, - ClientContext context) - : base(context) - { - _accountingServiceClient = accountingServiceClient; - } - - internal async Task GetBallance(CallContext ctx) - { - BalanceRequest request = new() - { - Body = new() - { - OwnerId = ClientContext.Owner.OwnerID - } - }; - - request.AddMetaHeader([]); - request.Sign(ClientContext.Key); - - var response = await _accountingServiceClient!.BalanceAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return response.Body.Balance; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs deleted file mode 100644 index ebd60de..0000000 --- a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Frostfs.V2.Apemanager; -using Google.Protobuf; - -namespace FrostFS.SDK.Client.Services; - -internal sealed class ApeManagerServiceProvider : ContextAccessor -{ - private readonly APEManagerService.APEManagerServiceClient? _apeManagerServiceClient; - - internal ApeManagerServiceProvider(APEManagerService.APEManagerServiceClient? apeManagerServiceClient, ClientContext context) - : base(context) - { - _apeManagerServiceClient = apeManagerServiceClient; - } - - internal async Task> AddChainAsync(PrmApeChainAdd args, CallContext ctx) - { - var binary = RuleSerializer.Serialize(args.Chain); - - AddChainRequest request = new() - { - Body = new() - { - Chain = new() { Raw = UnsafeByteOperations.UnsafeWrap(binary) }, - Target = args.Target.GetChainTarget() - } - }; - - request.AddMetaHeader(args.XHeaders); - request.Sign(ClientContext.Key); - - var response = await _apeManagerServiceClient!.AddChainAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return response.Body.ChainId.Memory; - } - - internal async Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx) - { - RemoveChainRequest request = new() - { - Body = new() - { - ChainId = UnsafeByteOperations.UnsafeWrap(args.ChainId), - Target = args.Target.GetChainTarget() - } - }; - - request.AddMetaHeader(args.XHeaders); - request.Sign(ClientContext.Key); - - var response = await _apeManagerServiceClient!.RemoveChainAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - } - - internal async Task ListChainAsync(PrmApeChainList args, CallContext ctx) - { - ListChainsRequest request = new() - { - Body = new() - { - Target = args.Target.GetChainTarget() - } - }; - - request.AddMetaHeader(args.XHeaders); - request.Sign(ClientContext.Key); - - var response = await _apeManagerServiceClient!.ListChainsAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return [.. response.Body.Chains.Select(c => RuleSerializer.Deserialize([.. c.Raw]))]; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs deleted file mode 100644 index c0f8b1a..0000000 --- a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -using FrostFS.Container; -using FrostFS.Refs; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.Session; - -namespace FrostFS.SDK.Client; - -internal sealed class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientContext clientCtx) : ContextAccessor(clientCtx) -{ - private SessionProvider? sessions; - - public async ValueTask GetDefaultSession(ISessionToken args, CallContext ctx) - { - sessions ??= new(ClientContext); - - if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token)) - { - var protoToken = await sessions.GetDefaultSession(args, ctx).ConfigureAwait(false); - - token = new FrostFsSessionToken(protoToken); - - ClientContext.SessionCache.SetValue(ClientContext.SessionCacheKey, token); - } - - if (token == null) - { - throw new FrostFsException("Cannot create session"); - } - - return token; - } - - internal async Task GetContainerAsync(PrmContainerGet args, CallContext ctx) - { - GetRequest request = GetContainerRequest(args.Container.GetContainerID(), args.XHeaders, ClientContext.Key); - - var response = await service.GetAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return response.Body.Container.ToModel(); - } - - internal async IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args, CallContext ctx) - { - var request = new ListRequest - { - Body = new() - { - OwnerId = ClientContext.Owner.OwnerID - } - }; - - request.AddMetaHeader(args.XHeaders); - request.Sign(ClientContext.Key); - - var response = await service.ListAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - foreach (var cid in response.Body.ContainerIds) - { - yield return new FrostFsContainerId(Base58.Encode(cid.Value.Span)); - } - } - - internal async Task PutContainerAsync(PrmContainerCreate args, CallContext ctx) - { - var grpcContainer = args.Container.GetContainer(); - - grpcContainer.OwnerId ??= ClientContext.Owner.OwnerID; - grpcContainer.Version ??= ClientContext.Version.VersionID; - - var request = new PutRequest - { - Body = new PutRequest.Types.Body - { - Container = grpcContainer, - Signature = ClientContext.Key.SignRFC6979(grpcContainer) - } - }; - - var sessionToken = (args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false)) ?? throw new FrostFsException("Cannot create session token"); - - var protoToken = sessionToken.CreateContainerToken( - null, - ContainerSessionContext.Types.Verb.Put, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var response = await service.PutAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - await WaitForContainer(WaitExpects.Exists, response.Body.ContainerId, args.WaitParams, ctx).ConfigureAwait(false); - - return new FrostFsContainerId(response.Body.ContainerId); - } - - internal async Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx) - { - var request = new DeleteRequest - { - Body = new DeleteRequest.Types.Body - { - ContainerId = args.ContainerId.GetContainerID(), - Signature = ClientContext.Key.SignRFC6979(args.ContainerId.GetContainerID().Value) - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateContainerToken( - request.Body.ContainerId, - ContainerSessionContext.Types.Verb.Delete, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var response = await service.DeleteAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx) - .ConfigureAwait(false); - - Verifier.CheckResponse(response); - } - - private static GetRequest GetContainerRequest(ContainerID id, string[] xHeaders, ClientKey key) - { - var request = new GetRequest - { - Body = new GetRequest.Types.Body - { - ContainerId = id - } - }; - - request.AddMetaHeader(xHeaders); - request.Sign(key); - - return request; - } - - private enum WaitExpects - { - Exists, - Removed - } - - private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait waitParams, CallContext ctx) - { - var request = GetContainerRequest(id, [], ClientContext.Key); - - async Task action() - { - var response = await service.GetAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - Verifier.CheckResponse(response); - } - - await WaitFor(action, expect, waitParams).ConfigureAwait(false); - } - - private static async Task WaitFor( - Func action, - WaitExpects expect, - PrmWait waitParams) - { - var deadLine = waitParams.GetDeadline(); - - while (true) - { - try - { - await action().ConfigureAwait(false); - - if (expect == WaitExpects.Exists) - return; - - if (DateTime.UtcNow >= deadLine) - throw new TimeoutException(); - - await Task.Delay(waitParams.PollInterval).ConfigureAwait(false); - } - catch (FrostFsResponseException ex) - { - if (DateTime.UtcNow >= deadLine) - throw new TimeoutException(); - - if (ex.Status?.Code != FrostFsStatusCode.ContainerNotFound) - throw; - - if (expect == WaitExpects.Removed) - return; - - await Task.Delay(waitParams.PollInterval).ConfigureAwait(false); - } - } - } -} diff --git a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs deleted file mode 100644 index 672c010..0000000 --- a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Threading.Tasks; - -using FrostFS.Netmap; - -using static FrostFS.Netmap.NetworkConfig.Types; - -namespace FrostFS.SDK.Client; - -internal sealed class NetmapServiceProvider : ContextAccessor -{ - private readonly NetmapService.NetmapServiceClient netmapServiceClient; - - internal NetmapServiceProvider(NetmapService.NetmapServiceClient netmapServiceClient, ClientContext context) - : base(context) - { - this.netmapServiceClient = netmapServiceClient; - } - - internal async Task GetNetworkSettingsAsync(CallContext ctx) - { - if (ClientContext.NetworkSettings != null) - return ClientContext.NetworkSettings; - - var response = await GetNetworkInfoAsync(ctx).ConfigureAwait(false); - - var settings = new NetworkSettings(); - - var info = response.Body.NetworkInfo; - - settings.Epoch = info.CurrentEpoch; - settings.MagicNumber = info.MagicNumber; - settings.MsPerBlock = info.MsPerBlock; - - foreach (var param in info.NetworkConfig.Parameters) - { - SetNetworksParam(param, settings); - } - - ClientContext.NetworkSettings = settings; - - return settings; - } - - internal async Task GetLocalNodeInfoAsync(CallContext ctx) - { - var request = new LocalNodeInfoRequest - { - Body = new LocalNodeInfoRequest.Types.Body { } - }; - - request.AddMetaHeader([]); - request.Sign(ClientContext.Key); - - var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return response.Body.ToModel(); - } - - internal async Task GetNetworkInfoAsync(CallContext ctx) - { - var request = new NetworkInfoRequest(); - - request.AddMetaHeader([]); - request.Sign(ClientContext.Key); - - var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken) - .ConfigureAwait(false); - - Verifier.CheckResponse(response); - - return response; - } - - internal async Task GetNetmapSnapshotAsync(CallContext ctx) - { - var request = new NetmapSnapshotRequest(); - - request.AddMetaHeader([]); - request.Sign(ClientContext.Key); - - var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return response.ToModel(); - } - - private static bool GetBoolValue(ReadOnlySpan bytes) - { - for (int i = bytes.Length - 1; i >= 0; i--) - if (bytes[i] != 0) - return true; - - return false; - } - - private static ulong GetLongValue(ReadOnlySpan bytes) - { - ulong val = 0; - for (var i = bytes.Length - 1; i >= 0; i--) - val = (val << 8) + bytes[i]; - - return val; - } - - private static void SetNetworksParam(Parameter param, NetworkSettings settings) - { - var key = param.Key.ToStringUtf8(); - - var valueBytes = param.Value.Span; - switch (key) - { - case "AuditFee": - settings.AuditFee = GetLongValue(valueBytes); - break; - case "BasicIncomeRate": - settings.BasicIncomeRate = GetLongValue(valueBytes); - break; - case "ContainerFee": - settings.ContainerFee = GetLongValue(valueBytes); - break; - case "ContainerAliasFee": - settings.ContainerAliasFee = GetLongValue(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; - } - } -} - - diff --git a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs deleted file mode 100644 index b3c1f50..0000000 --- a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs +++ /dev/null @@ -1,894 +0,0 @@ -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -using FrostFS.Object; -using FrostFS.Refs; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client; - -internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientContext clientCtx) - : ContextAccessor(clientCtx) -{ - private SessionProvider? sessions; - private readonly ObjectService.ObjectServiceClient client = client; - - public async ValueTask GetDefaultSession(ISessionToken args, CallContext ctx) - { - sessions ??= new(ClientContext); - - if (!ClientContext.SessionCache!.TryGetValue(ClientContext.SessionCacheKey, out var token)) - { - var protoToken = await sessions.GetDefaultSession(args, ctx).ConfigureAwait(false); - - token = new FrostFsSessionToken(protoToken); - - ClientContext.SessionCache.SetValue(ClientContext.SessionCacheKey, token); - } - - if (token == null) - { - throw new FrostFsException("Cannot create session"); - } - - return token; - } - - internal async Task GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx) - { - var request = new HeadRequest - { - Body = new HeadRequest.Types.Body - { - Address = new Address - { - ContainerId = args.ContainerId.GetContainerID(), - ObjectId = args.ObjectId.ToMessage() - }, - Raw = args.Raw - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - request.Body.Address, - ObjectSessionContext.Types.Verb.Head, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var response = await client!.HeadAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken).ConfigureAwait(false); - - Verifier.CheckResponse(response); - - var result = new FrostFsHeaderResult(); - - if (response.Body.Header != null) - { - result.HeaderInfo = response.Body.Header?.Header.ToModel(); - } - - if (response.Body.SplitInfo != null) - { - result.SplitInfo = new FrostFsSplitInfo(response.Body.SplitInfo); - } - - return result; - } - - internal async Task GetObjectAsync(PrmObjectGet args, CallContext ctx) - { - var request = new GetRequest - { - Body = new GetRequest.Types.Body - { - Address = new Address - { - ContainerId = args.ContainerId.GetContainerID(), - ObjectId = args.ObjectId.ToMessage() - } - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - request.Body.Address, - ObjectSessionContext.Types.Verb.Get, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - return await GetObject(request, ctx).ConfigureAwait(false); - } - - internal async Task GetRangeAsync(PrmRangeGet args, CallContext ctx) - { - var request = new GetRangeRequest - { - Body = new GetRangeRequest.Types.Body - { - Address = new Address - { - ContainerId = args.ContainerId.GetContainerID(), - ObjectId = args.ObjectId.ToMessage() - }, - Range = new Object.Range - { - Offset = args.Range.Offset, - Length = args.Range.Length - }, - Raw = args.Raw - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - request.Body.Address, - ObjectSessionContext.Types.Verb.Range, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var call = client.GetRange(request, null, ctx.GetDeadline(), ctx.CancellationToken); - return new RangeReader(call); - } - - internal async Task[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx) - { - var request = new GetRangeHashRequest - { - Body = new GetRangeHashRequest.Types.Body - { - Address = new Address - { - ContainerId = args.ContainerId.GetContainerID(), - ObjectId = args.ObjectId.ToMessage() - }, - Type = ChecksumType.Sha256, - Salt = ByteString.CopyFrom(args.Salt) // TODO: create a type with calculated cashed ByteString inside - } - }; - - foreach (var range in args.Ranges) - { - request.Body.Ranges.Add(new Object.Range - { - Length = range.Length, - Offset = range.Offset - }); - } - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - request.Body.Address, - ObjectSessionContext.Types.Verb.Rangehash, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var response = await client.GetRangeHashAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - var hashCollection = response.Body.HashList.Select(h => h.Memory).ToArray(); - - return hashCollection; - } - - internal async Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx) - { - var request = new DeleteRequest - { - Body = new DeleteRequest.Types.Body - { - Address = new Address - { - ContainerId = args.ContainerId.GetContainerID(), - ObjectId = args.ObjectId.ToMessage() - } - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - request.Body.Address, - ObjectSessionContext.Types.Verb.Delete, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - request.Sign(ClientContext.Key); - - var response = await client.DeleteAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - } - - internal async IAsyncEnumerable SearchObjectsAsync(PrmObjectSearch args, CallContext ctx) - { - var request = new SearchRequest - { - Body = new SearchRequest.Types.Body - { - ContainerId = args.ContainerId.GetContainerID(), - Version = 1 // TODO: clarify this param - } - }; - - request.Body.Filters.AddRange(args.Filters.Select(f => f.ToMessage())); - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - new Address { ContainerId = request.Body.ContainerId }, - ObjectSessionContext.Types.Verb.Search, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - using var stream = GetSearchReader(request, ctx); - - while (true) - { - var ids = await stream.Read(ctx.CancellationToken).ConfigureAwait(false); - - if (ids == null) - yield break; - - foreach (var oid in ids) - { - yield return FrostFsObjectId.FromHash(oid.Value.Span); - } - } - } - - internal async Task PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx) - { - var grpcObject = ObjectTools.CreateSingleObject(args.FrostFsObject, ClientContext); - - var request = new PutSingleRequest - { - Body = new() { Object = grpcObject } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - new Address { ContainerId = grpcObject.Header.ContainerId }, - ObjectSessionContext.Types.Verb.Put, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - request.Sign(ClientContext.Key); - - var response = await client.PutSingleAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken).ConfigureAwait(false); - - Verifier.CheckResponse(response); - - return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.Span); - } - - internal async Task PatchObjectAsync(PrmObjectPatch args, CallContext ctx) - { - var chunkSize = args.MaxChunkLength; - - var call = client.Patch(null, ctx.GetDeadline(), ctx.CancellationToken); - - var address = new Address - { - ObjectId = args.Address.ObjectId, - ContainerId = args.Address.ContainerId - }; - - if (args.Payload != null && args.Payload.Length > 0) - { - byte[]? chunkBuffer = null; - try - { - chunkBuffer = ArrayPool.Shared.Rent(chunkSize); - - bool isFirstChunk = true; - ulong currentPos = args.Range.Offset; - - while (true) - { - var bytesCount = await args.Payload.ReadAsync(chunkBuffer, 0, chunkSize, ctx.CancellationToken).ConfigureAwait(false); - - if (bytesCount == 0) - { - break; - } - - PatchRequest request; - - if (isFirstChunk) - { - request = await CreateFirstRequest(args, ctx, address).ConfigureAwait(false); - - request.Body.Patch = new PatchRequest.Types.Body.Types.Patch - { - Chunk = UnsafeByteOperations.UnsafeWrap(chunkBuffer.AsMemory(0, bytesCount)), - SourceRange = new Range { Offset = currentPos, Length = (ulong)bytesCount } - }; - - isFirstChunk = false; - } - else - { - request = new PatchRequest() - { - Body = new() - { - Address = address, - Patch = new PatchRequest.Types.Body.Types.Patch - { - Chunk = UnsafeByteOperations.UnsafeWrap(chunkBuffer.AsMemory(0, bytesCount)), - SourceRange = new Range { Offset = currentPos, Length = (ulong)bytesCount } - } - } - }; - - request.AddMetaHeader(args.XHeaders); - } - - request.Sign(ClientContext.Key); - - await call.RequestStream.WriteAsync(request).ConfigureAwait(false); - - currentPos += (ulong)bytesCount; - } - } - finally - { - if (chunkBuffer != null) - { - ArrayPool.Shared.Return(chunkBuffer); - } - } - } - else if (args.NewAttributes != null && args.NewAttributes.Length > 0) - { - PatchRequest request = await CreateFirstRequest(args, ctx, address).ConfigureAwait(false); - - request.Sign(ClientContext.Key); - - await call.RequestStream.WriteAsync(request).ConfigureAwait(false); - } - - await call.RequestStream.CompleteAsync().ConfigureAwait(false); - var response = await call.ResponseAsync.ConfigureAwait(false); - - Verifier.CheckResponse(response); - - return response.Body.ObjectId.ToModel(); - - async Task CreateFirstRequest(PrmObjectPatch args, CallContext ctx, Address address) - { - var body = new PatchRequest.Types.Body() { Address = address }; - - if (args.NewAttributes != null) - { - body.ReplaceAttributes = args.ReplaceAttributes; - - foreach (var attr in args.NewAttributes!) - { - body.NewAttributes.Add(attr.ToMessage()); - } - } - - var request = new PatchRequest() { Body = body }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - address, - ObjectSessionContext.Types.Verb.Patch, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - return request; - } - } - - internal async Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx) - { - if (args.Payload == null) - throw new ArgumentException(nameof(args.Payload)); - - if (args.Header == null) - throw new ArgumentException(nameof(args.Header)); - - var networkSettings = await ClientContext.Client.GetNetworkSettingsAsync(ctx).ConfigureAwait(false); - int partSize = (int)networkSettings.MaxObjectSize; - - int chunkSize = args.BufferMaxSize > 0 ? args.BufferMaxSize : Constants.ObjectChunkSize; - - ulong fullLength; - - // Information about the uploaded parts. - var progressInfo = args.Progress; // - var offset = 0L; - - if (progressInfo != null && progressInfo.GetParts().Count > 0) - { - if (!args.Payload.CanSeek) - { - throw new FrostFsException("Cannot resume client cut upload for this stream. Seek must be supported."); - } - - var lastPart = progressInfo.GetLast(); - args.Payload.Position = lastPart.Offset + lastPart.Length; - fullLength = (ulong)(args.Payload.Length - args.Payload.Position); - offset = args.Payload.Position; - } - else - { - if (args.Header.PayloadLength > 0) - fullLength = args.Header.PayloadLength; - else if (args.Payload.CanSeek) - fullLength = (ulong)args.Payload.Length; - else - throw new ArgumentException("The stream does not have a length and payload length is not defined"); - } - - //define collection capacity - var restPart = (fullLength % (ulong)partSize) > 0 ? 1 : 0; - var objectsCount = fullLength > 0 ? (int)(fullLength / (ulong)partSize) + restPart : 0; - - progressInfo ??= new UploadProgressInfo(Guid.NewGuid(), objectsCount); - - var remain = fullLength; - - byte[]? buffer = null; - bool isRentBuffer = false; - - try - { - if (args.CustomBuffer != null) - { - if (args.CustomBuffer.Length < chunkSize) - throw new ArgumentException($"Buffer size is too small. At least {chunkSize} required"); - - buffer = args.CustomBuffer; - } - else - { - buffer = ArrayPool.Shared.Rent(chunkSize); - isRentBuffer = true; - } - - FrostFsObjectId? resultObjectId = null; - FrostFsObjectHeader? parentHeader = null; - - while (remain > 0) - { - var bytesToWrite = Math.Min((ulong)partSize, remain); - var isLastPart = remain <= (ulong)partSize; - - // When the last part of the object is uploaded, all metadata for the object must be added - if (isLastPart && objectsCount > 1) - { - parentHeader = new FrostFsObjectHeader(args.Header.ContainerId, FrostFsObjectType.Regular) - { - Attributes = args.Header.Attributes, - PayloadLength = fullLength - }; - } - - // Uploading the next part of the object. Note: the request must contain a non-null SplitId parameter - var header = objectsCount == 1 ? args.Header : new FrostFsObjectHeader( - args.Header.ContainerId, - FrostFsObjectType.Regular, - [], - new FrostFsSplit(progressInfo.SplitId, progressInfo.GetLast().ObjectId, parentHeader: parentHeader)); - - var prm = new PrmObjectPut(header); - using var stream = await PutStreamObjectAsync(prm, ctx).ConfigureAwait(false); - var uploaded = 0; - - // If an error occurs while uploading a part of the object, there is no need to re-upload the parts - // that were successfully uploaded before. It is sufficient to re-upload only the failed part - - var thisPartRest = (int)Math.Min((ulong)partSize, remain); - while (thisPartRest > 0) - { - var nextChunkSize = Math.Min(thisPartRest, chunkSize); - var size = await args.Payload.ReadAsync(buffer, 0, nextChunkSize).ConfigureAwait(false); - - if (size == 0) - break; - - await stream.WriteAsync(buffer.AsMemory(0, size)).ConfigureAwait(false); - uploaded += size; - thisPartRest -= size; - } - - var objectId = await stream.CompleteAsync().ConfigureAwait(false); - var part = new ObjectPartInfo(offset, uploaded, objectId); - offset += uploaded; - progressInfo.AddPart(part); - - remain -= bytesToWrite; - - if (isLastPart) - { - if (objectsCount == 1) - { - return progressInfo.GetPart(0).ObjectId; - } - - if (parentHeader == null) continue; - - // Once all parts of the object are uploaded, they must be linked into a single entity - var linkObject = new FrostFsLinkObject(header.ContainerId, progressInfo.SplitId, parentHeader, - [.. progressInfo.GetParts().Select(p => p.ObjectId)]); - - await PutSingleObjectAsync(new PrmSingleObjectPut(linkObject), ctx).ConfigureAwait(false); - - // Retrieve the ID of the linked object - resultObjectId = FrostFsObjectId.FromHash(prm.Header!.GetHeader().Split!.Parent.Value.Span); - return resultObjectId; - } - } - - throw new FrostFsException("Unexpected error: cannot send object"); - } - finally - { - if (isRentBuffer && buffer != null) - { - ArrayPool.Shared.Return(buffer); - } - } - } - - internal async Task PutClientCutSingleObjectAsync(PrmObjectClientCutPut args, CallContext ctx) - { - if (args.Payload == null) - throw new ArgumentException(nameof(args.Payload)); - - if (args.Header == null) - throw new ArgumentException(nameof(args.Header)); - - var networkSettings = await ClientContext.Client.GetNetworkSettingsAsync(ctx).ConfigureAwait(false); - int partSize = (int)networkSettings.MaxObjectSize; - - int chunkSize = args.BufferMaxSize > 0 ? args.BufferMaxSize : Constants.ObjectChunkSize; - - ulong fullLength; - - // Information about the uploaded parts. - var progressInfo = args.Progress; // - var offset = 0L; - - if (progressInfo != null && progressInfo.GetParts().Count > 0) - { - if (!args.Payload.CanSeek) - { - throw new FrostFsException("Cannot resume client cut upload for this stream. Seek must be supported."); - } - - var lastPart = progressInfo.GetLast(); - args.Payload.Position = lastPart.Offset + lastPart.Length; - fullLength = (ulong)(args.Payload.Length - args.Payload.Position); - offset = args.Payload.Position; - } - else - { - if (args.Header.PayloadLength > 0) - fullLength = args.Header.PayloadLength; - else if (args.Payload.CanSeek) - fullLength = (ulong)args.Payload.Length; - else - throw new ArgumentException("The stream does not have a length and payload length is not defined"); - } - - //define collection capacity - var restPart = (fullLength % (ulong)partSize) > 0 ? 1 : 0; - var objectsCount = fullLength > 0 ? (int)(fullLength / (ulong)partSize) + restPart : 0; - - progressInfo ??= new UploadProgressInfo(Guid.NewGuid(), objectsCount); - - // if the object fits one part, it can be loaded as non-complex object, but if it is not upload resuming - if (objectsCount == 1 && progressInfo.GetLast().Length == 0) - { - args.PutObjectContext.MaxObjectSizeCache = partSize; - args.PutObjectContext.FullLength = fullLength; - var singlePartResult = await PutMultipartStreamObjectAsync(args, default).ConfigureAwait(false); - return singlePartResult.ObjectId; - } - - var remain = fullLength; - - byte[]? buffer = null; - bool isRentBuffer = false; - - try - { - if (args.CustomBuffer != null) - { - if (args.CustomBuffer.Length < partSize) - { - throw new ArgumentException($"Buffer size is too small. A buffer with capacity {partSize} is required"); - } - - buffer = args.CustomBuffer; - } - else - { - buffer = ArrayPool.Shared.Rent(partSize); - isRentBuffer = true; - } - - FrostFsObjectHeader? parentHeader = null; - - for (int i = 0; i < objectsCount;) - { - i++; - var bytesToWrite = Math.Min((ulong)partSize, remain); - - var size = await args.Payload.ReadAsync(buffer, 0, (int)bytesToWrite).ConfigureAwait(false); - - if (i == objectsCount) - { - parentHeader = new FrostFsObjectHeader(args.Header.ContainerId, FrostFsObjectType.Regular) - { - PayloadLength = args.PutObjectContext.FullLength, - Attributes = args.Header.Attributes - }; - } - - // Uploading the next part of the object. Note: the request must contain a non-null SplitId parameter - var partHeader = new FrostFsObjectHeader( - args.Header.ContainerId, - FrostFsObjectType.Regular, - [], - new FrostFsSplit(progressInfo.SplitId, progressInfo.GetLast().ObjectId, - parentHeader: parentHeader)) - { - PayloadLength = (ulong)size - }; - - var obj = new FrostFsObject(partHeader) - { - SingleObjectPayload = buffer.AsMemory(0, size) - }; - - var prm = new PrmSingleObjectPut(obj); - - var objectId = await PutSingleObjectAsync(prm, ctx).ConfigureAwait(false); - - var part = new ObjectPartInfo(offset, size, objectId); - progressInfo.AddPart(part); - - offset += size; - - if (i < objectsCount) - { - continue; - } - - // Once all parts of the object are uploaded, they must be linked into a single entity - var linkObject = new FrostFsLinkObject(args.Header.ContainerId, progressInfo.SplitId, parentHeader!, [.. progressInfo.GetParts().Select(p => p.ObjectId)]); - - _ = await PutSingleObjectAsync(new PrmSingleObjectPut(linkObject), ctx).ConfigureAwait(false); - - // Retrieve the ID of the linked object - return partHeader.GetHeader().Split!.Parent.ToModel(); - } - - throw new FrostFsException("Unexpected error"); - } - finally - { - if (isRentBuffer && buffer != null) - { - ArrayPool.Shared.Return(buffer); - } - } - } - - struct PutObjectResult(FrostFsObjectId objectId, int objectSize) - { - public FrostFsObjectId ObjectId = objectId; - public int ObjectSize = objectSize; - } - - private async Task PutMultipartStreamObjectAsync(PrmObjectClientCutPut args, CallContext ctx) - { - var payload = args.Payload!; - - var chunkSize = args.BufferMaxSize > 0 ? args.BufferMaxSize : Constants.ObjectChunkSize; - var restBytes = args.PutObjectContext.FullLength - args.PutObjectContext.CurrentStreamPosition; - - chunkSize = (int)Math.Min(restBytes, (ulong)chunkSize); - - bool isRentBuffer = false; - byte[]? chunkBuffer = null; - - try - { - // 0 means no limit from client, so server side cut is performed - var objectLimitSize = args.PutObjectContext.MaxObjectSizeCache; - - if (args.CustomBuffer != null) - { - if (args.CustomBuffer.Length < chunkSize) - { - throw new ArgumentException($"Buffer size is too small. At least {chunkSize} required"); - } - - chunkBuffer = args.CustomBuffer; - } - else - { - chunkBuffer = ArrayPool.Shared.Rent(chunkSize); - isRentBuffer = true; - } - - var sentBytes = 0; - - using var stream = await GetUploadStream(args, ctx).ConfigureAwait(false); - - while (objectLimitSize == 0 || sentBytes < objectLimitSize) - { - // send chunks limited to default or user's settings - var bufferSize = objectLimitSize > 0 ? - Math.Min(objectLimitSize - sentBytes, chunkSize) - : chunkSize; - - var bytesCount = await payload.ReadAsync(chunkBuffer, 0, bufferSize, ctx.CancellationToken).ConfigureAwait(false); - - if (bytesCount == 0) - break; - - sentBytes += bytesCount; - - var chunkRequest = new PutRequest - { - Body = new PutRequest.Types.Body - { - Chunk = UnsafeByteOperations.UnsafeWrap(chunkBuffer.AsMemory(0, bytesCount)) - } - }; - - chunkRequest.AddMetaHeader(args.XHeaders); - chunkRequest.Sign(ClientContext.Key); - - await stream.Write(chunkRequest).ConfigureAwait(false); - } - - args.PutObjectContext.CurrentStreamPosition += (ulong)sentBytes; - - var response = await stream.Close().ConfigureAwait(false); - Verifier.CheckResponse(response); - - return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.Span), sentBytes); - } - finally - { - if (isRentBuffer && chunkBuffer != null) - { - ArrayPool.Shared.Return(chunkBuffer); - } - } - } - - internal async Task PutStreamObjectAsync(PrmObjectPutBase args, CallContext ctx) - { - var stream = await GetUploadStream(args, ctx).ConfigureAwait(false); - - return new ObjectWriter(ClientContext, args, stream); - } - - private async Task> GetUploadStream(PrmObjectPutBase args, CallContext ctx) - { - var header = args.Header!; - - header.OwnerId ??= ClientContext.Owner; - header.Version ??= ClientContext.Version; - - var grpcHeader = header.GetHeader(); - - if (header.Split != null) - { - ObjectTools.SetSplitValues(grpcHeader, header.Split, ClientContext.Owner, ClientContext.Version, ClientContext.Key); - } - - var initRequest = new PutRequest - { - Body = new PutRequest.Types.Body - { - Init = new PutRequest.Types.Body.Types.Init - { - Header = grpcHeader, - } - } - }; - - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - new Address { ContainerId = grpcHeader.ContainerId }, - ObjectSessionContext.Types.Verb.Put, - ClientContext.Key); - - initRequest.AddMetaHeader(args.XHeaders, protoToken); - - initRequest.Sign(ClientContext.Key); - - return await PutObjectInit(initRequest, ctx).ConfigureAwait(false); - } - - private async Task> PutObjectInit(PutRequest initRequest, CallContext ctx) - { - if (initRequest is null) - { - throw new ArgumentNullException(nameof(initRequest)); - } - - var call = client.Put(null, ctx.GetDeadline(), ctx.CancellationToken); - - await call.RequestStream.WriteAsync(initRequest).ConfigureAwait(false); - - return new ObjectStreamer(call); - } - - private async Task GetObject(GetRequest request, CallContext ctx) - { - var reader = GetObjectInit(request, ctx); - - var grpcObject = await reader.ReadHeader().ConfigureAwait(false); - var modelObject = grpcObject.ToModel(); - - modelObject.ObjectReader = reader; - - return modelObject; - } - - private ObjectReader GetObjectInit(GetRequest initRequest, CallContext ctx) - { - if (initRequest is null) - throw new ArgumentNullException(nameof(initRequest)); - - var call = client.Get(initRequest, null, ctx.GetDeadline(), ctx.CancellationToken); - - return new ObjectReader(call); - } - - private SearchReader GetSearchReader(SearchRequest initRequest, CallContext ctx) - { - if (initRequest is null) - { - throw new ArgumentNullException(nameof(initRequest)); - } - - var call = client.Search(initRequest, null, ctx.GetDeadline(), ctx.CancellationToken); - - return new SearchReader(call); - } -} diff --git a/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs b/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs deleted file mode 100644 index cde2b9e..0000000 --- a/src/FrostFS.SDK.Client/Services/SessionServiceProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Threading.Tasks; - -using FrostFS.Session; - -namespace FrostFS.SDK.Client; - -internal sealed class SessionServiceProvider : ContextAccessor -{ - private readonly SessionService.SessionServiceClient? _sessionServiceClient; - - internal SessionServiceProvider(SessionService.SessionServiceClient? sessionServiceClient, ClientContext context) - : base(context) - { - _sessionServiceClient = sessionServiceClient; - } - - internal async Task CreateSessionAsync(PrmSessionCreate args, CallContext ctx) - { - var request = new CreateRequest - { - Body = new CreateRequest.Types.Body - { - OwnerId = ClientContext.Owner.OwnerID, - Expiration = args.Expiration - } - }; - - request.AddMetaHeader(args.XHeaders); - request.Sign(ClientContext.Key); - - return await CreateSession(request, ctx).ConfigureAwait(false); - } - - internal async Task CreateSession(CreateRequest request, CallContext ctx) - { - var response = await _sessionServiceClient!.CreateAsync(request, null, ctx.GetDeadline(), ctx.CancellationToken); - - Verifier.CheckResponse(response); - - return new SessionToken - { - Body = new SessionToken.Types.Body - { - Id = response.Body.Id, - SessionKey = response.Body.SessionKey, - OwnerId = request.Body.OwnerId, - Lifetime = new SessionToken.Types.Body.Types.TokenLifetime - { - Exp = request.Body.Expiration, - Iat = response.MetaHeader.Epoch, - Nbf = response.MetaHeader.Epoch, - } - } - }; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Services/Shared/ContextAccessor.cs b/src/FrostFS.SDK.Client/Services/Shared/ContextAccessor.cs deleted file mode 100644 index e127146..0000000 --- a/src/FrostFS.SDK.Client/Services/Shared/ContextAccessor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FrostFS.SDK.Client; - -internal class ContextAccessor(ClientContext context) -{ - protected ClientContext ClientContext { get; set; } = context; -} diff --git a/src/FrostFS.SDK.Client/Services/Shared/SessionCache.cs b/src/FrostFS.SDK.Client/Services/Shared/SessionCache.cs deleted file mode 100644 index a02436f..0000000 --- a/src/FrostFS.SDK.Client/Services/Shared/SessionCache.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Concurrent; - -namespace FrostFS.SDK.Client; - -internal sealed class SessionCache(ulong sessionExpirationDuration) -{ - private ConcurrentDictionary _cache { get; } = []; - - internal ulong CurrentEpoch { get; set; } - - internal ulong TokenDuration { get; set; } = sessionExpirationDuration; - - internal bool Contains(string key) - { - return _cache.ContainsKey(key); - } - - internal bool TryGetValue(string? key, out FrostFsSessionToken? value) - { - if (key == null) - { - value = null; - return false; - } - - var ok = _cache.TryGetValue(key, out value); - - return ok && value != null; - } - - internal void SetValue(string? key, FrostFsSessionToken value) - { - if (key != null) - { - _cache[key] = value; - } - } -} diff --git a/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs b/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs deleted file mode 100644 index da5a26d..0000000 --- a/src/FrostFS.SDK.Client/Services/Shared/SessionProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; - -namespace FrostFS.SDK.Client; - -internal interface ISessionProvider -{ - ValueTask GetOrCreateSession(ISessionToken args, CallContext ctx); -} - -internal sealed class SessionProvider(ClientContext envCtx) -{ - public async Task CreateSession(ISessionToken args, CallContext ctx) - { - var token = await GetDefaultSession(args, ctx).ConfigureAwait(false); - - return new FrostFsSessionToken(token); - } - - internal async Task GetDefaultSession(ISessionToken args, CallContext ctx) - { - return await envCtx.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue), ctx).ConfigureAwait(false); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Tools/ClientContext.cs b/src/FrostFS.SDK.Client/Tools/ClientContext.cs deleted file mode 100644 index 104cb3d..0000000 --- a/src/FrostFS.SDK.Client/Tools/ClientContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -using Grpc.Core; -using Grpc.Core.Interceptors; - -namespace FrostFS.SDK.Client; - -public class ClientContext(FrostFSClient client, ClientKey key, FrostFsOwner owner, ChannelBase channel, FrostFsVersion version) -{ - private string? sessionKey; - - internal FrostFsOwner Owner { get; } = owner; - - internal string? Address { get; } = channel.Target; - - internal ChannelBase Channel { get; private set; } = channel; - - internal FrostFsVersion Version { get; } = version; - - internal NetworkSettings? NetworkSettings { get; set; } - - internal FrostFSClient Client { get; } = client; - - internal ClientKey Key { get; } = key; - - internal SessionCache? SessionCache { get; set; } - - internal Action? Callback { get; set; } - - internal Collection? Interceptors { get; set; } - - internal Action? PoolErrorHandler { get; set; } - - - internal string? SessionCacheKey - { - get - { - if (sessionKey == null && Key != null && Address != null) - { - sessionKey = $"{Address}{Key}"; - } - - return sessionKey; - } - } -} diff --git a/src/FrostFS.SDK.Client/Tools/NetworkSettings.cs b/src/FrostFS.SDK.Client/Tools/NetworkSettings.cs deleted file mode 100644 index d8f5e12..0000000 --- a/src/FrostFS.SDK.Client/Tools/NetworkSettings.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; - -namespace FrostFS.SDK.Client; - -public class NetworkSettings -{ - public ulong Epoch { get; internal set; } - public ulong MagicNumber { get; internal set; } - public long MsPerBlock { get; internal set; } - - public ulong AuditFee { get; internal set; } - public ulong BasicIncomeRate { get; internal set; } - public ulong ContainerFee { get; internal set; } - public ulong ContainerAliasFee { get; internal set; } - public ulong EpochDuration { get; internal set; } - public ulong InnerRingCandidateFee { get; internal set; } - public ulong MaxObjectSize { get; internal set; } - public ulong MaxECDataCount { get; internal set; } - public ulong MaxECParityCount { get; internal set; } - public ulong WithdrawFee { get; internal set; } - public bool HomomorphicHashingDisabled { get; internal set; } - public bool MaintenanceModeAllowed { get; internal set; } - - public Dictionary UnnamedSettings { get; } = []; -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Tools/ObjectReader.cs b/src/FrostFS.SDK.Client/Tools/ObjectReader.cs deleted file mode 100644 index 7d5dbd7..0000000 --- a/src/FrostFS.SDK.Client/Tools/ObjectReader.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -using FrostFS.Object; - -using Grpc.Core; - -namespace FrostFS.SDK.Client; - -public sealed class ObjectReader(AsyncServerStreamingCall call) : IObjectReader -{ - private bool disposed; - - public AsyncServerStreamingCall Call { get; private set; } = call; - - internal async Task ReadHeader() - { - if (!await Call.ResponseStream.MoveNext().ConfigureAwait(false)) - throw new FrostFsStreamException("unexpected end of stream"); - - var response = Call.ResponseStream.Current; - Verifier.CheckResponse(response); - - if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init) - throw new FrostFsStreamException("unexpected message type"); - - return new Object.Object - { - ObjectId = response.Body.Init.ObjectId, - Header = response.Body.Init.Header, - }; - } - - public async ValueTask?> ReadChunk(CancellationToken cancellationToken = default) - { - if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) - return null; - - var response = Call.ResponseStream.Current; - Verifier.CheckResponse(response); - - if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk) - throw new FrostFsStreamException("unexpected message type"); - - return response.Body.Chunk.Memory; - } - - public void Dispose() - { - if (!disposed) - { - Call?.Dispose(); - GC.SuppressFinalize(this); - - disposed = true; - } - } -} diff --git a/src/FrostFS.SDK.Client/Tools/ObjectStreamer.cs b/src/FrostFS.SDK.Client/Tools/ObjectStreamer.cs deleted file mode 100644 index 81a6b3b..0000000 --- a/src/FrostFS.SDK.Client/Tools/ObjectStreamer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading.Tasks; - -using Grpc.Core; - -namespace FrostFS.SDK.Client; - -internal sealed class ObjectStreamer(AsyncClientStreamingCall call) : IDisposable -{ - public AsyncClientStreamingCall Call { get; private set; } = call; - - public async Task Write(TRequest request) - { - if (request is null) - { - throw new ArgumentNullException(nameof(request)); - } - - await Call.RequestStream.WriteAsync(request).ConfigureAwait(false); - } - - public async Task Close() - { - await Call.RequestStream.CompleteAsync().ConfigureAwait(false); - - return await Call.ResponseAsync.ConfigureAwait(false); - } - - public void Dispose() - { - Call?.Dispose(); - } -} diff --git a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs b/src/FrostFS.SDK.Client/Tools/ObjectTools.cs deleted file mode 100644 index c8c5caf..0000000 --- a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Linq; -using System.Security.Cryptography; -using FrostFS.Object; -using FrostFS.Refs; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client; - -public static class ObjectTools -{ - public static FrostFsObjectId CalculateObjectId( - FrostFsObjectHeader header, - ReadOnlyMemory payloadHash, - FrostFsOwner owner, - FrostFsVersion version, - ClientKey key) - { - if (header is null) - { - throw new ArgumentNullException(nameof(header)); - } - - if (owner is null) - { - throw new ArgumentNullException(nameof(owner)); - } - - if (version is null) - { - throw new ArgumentNullException(nameof(version)); - } - - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - var grpcHeader = CreateHeader(header, payloadHash, owner, version); - - if (header.Split != null) - SetSplitValues(grpcHeader, header.Split, owner, version, key); - - using var sha256 = SHA256.Create(); - using HashStream stream = new(sha256); - grpcHeader.WriteTo(stream); - - return new FrostFsObjectId(Base58.Encode(stream.Hash())); - } - - internal static Object.Object CreateSingleObject(FrostFsObject @object, ClientContext ctx) - { - @object.Header.OwnerId ??= ctx.Owner; - @object.Header.Version ??= ctx.Version; - - var grpcHeader = @object.Header.GetHeader(); - - grpcHeader.PayloadLength = (ulong)@object.SingleObjectPayload.Length; - - if (@object.PayloadHash != null) - { - grpcHeader.PayloadHash = ChecksumFromSha256(@object.PayloadHash); - } - else - { - grpcHeader.PayloadHash = Sha256Checksum(@object.SingleObjectPayload); - } - - var split = @object.Header.Split; - - if (split != null) - { - SetSplitValues(grpcHeader, split, ctx.Owner, ctx.Version, ctx.Key); - } - - var obj = new Object.Object - { - Header = grpcHeader, - ObjectId = new ObjectID { Value = UnsafeByteOperations.UnsafeWrap(grpcHeader.Sha256()) }, - Payload = UnsafeByteOperations.UnsafeWrap(@object.SingleObjectPayload) - }; - - obj.Signature = new Signature - { - Key = ctx.Key.PublicKeyProto, - Sign = ctx.Key.ECDsaKey.SignData(obj.ObjectId.ToByteArray()), - }; - - return obj; - } - - internal static void SetSplitValues( - Header grpcHeader, - FrostFsSplit split, - FrostFsOwner owner, - FrostFsVersion version, - ClientKey key) - { - if (split == null) - { - return; - } - - if (key == null) - { - throw new FrostFsInvalidObjectException(nameof(key)); - } - - grpcHeader.Split = new Header.Types.Split - { - SplitId = split.SplitId?.GetSplitId() - }; - - if (split.Children != null && split.Children.Count != 0) - { - grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToMessage())); - } - - if (split.ParentHeader is not null) - { - var grpcParentHeader = CreateHeader(split.ParentHeader, DataHasher.Sha256([]), owner, version); - - grpcHeader.Split.Parent = new ObjectID { Value = UnsafeByteOperations.UnsafeWrap(grpcParentHeader.Sha256()) }; - grpcHeader.Split.ParentHeader = grpcParentHeader; - grpcHeader.Split.ParentSignature = new Signature - { - Key = key.PublicKeyProto, - Sign = key.ECDsaKey.SignData(grpcHeader.Split.Parent.ToByteArray()), - }; - } - - grpcHeader.Split.Previous = split.Previous?.ToMessage(); - } - - internal static Header CreateHeader( - FrostFsObjectHeader header, - ReadOnlyMemory payloadChecksum, - FrostFsOwner owner, - FrostFsVersion version) - { - header.OwnerId ??= owner; - header.Version ??= version; - - var grpcHeader = header.GetHeader(); - - grpcHeader.PayloadHash = ChecksumFromSha256(payloadChecksum); - - return grpcHeader; - } - - internal static Checksum Sha256Checksum(ReadOnlyMemory data) - { - return new Checksum - { - Type = ChecksumType.Sha256, - Sum = UnsafeByteOperations.UnsafeWrap(DataHasher.Sha256(data)) - }; - } - - internal static Checksum ChecksumFromSha256(ReadOnlyMemory dataHash) - { - return new Checksum - { - Type = ChecksumType.Sha256, - Sum = UnsafeByteOperations.UnsafeWrap(dataHash) - }; - } -} diff --git a/src/FrostFS.SDK.Client/Tools/RangeReader.cs b/src/FrostFS.SDK.Client/Tools/RangeReader.cs deleted file mode 100644 index a92d710..0000000 --- a/src/FrostFS.SDK.Client/Tools/RangeReader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -using FrostFS.Object; - -using Grpc.Core; - -namespace FrostFS.SDK.Client; - -public sealed class RangeReader(AsyncServerStreamingCall call) : IObjectReader -{ - private bool disposed; - - public AsyncServerStreamingCall Call { get; private set; } = call; - - public async ValueTask?> ReadChunk(CancellationToken cancellationToken = default) - { - if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) - return null; - - var response = Call.ResponseStream.Current; - Verifier.CheckResponse(response); - - return response.Body.Chunk.Memory; - } - - public void Dispose() - { - if (!disposed) - { - Call?.Dispose(); - GC.SuppressFinalize(this); - - disposed = true; - } - } -} diff --git a/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs b/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs deleted file mode 100644 index 102eeb5..0000000 --- a/src/FrostFS.SDK.Client/Tools/RequestConstructor.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -namespace FrostFS.SDK.Client; - -public static class RequestConstructor -{ - public static void AddMetaHeader(this IRequest request, - string[] xHeaders, - SessionToken? sessionToken = null) - { - if (request is null) - throw new ArgumentNullException(nameof(request)); - - if (request.MetaHeader is not null) - return; - - var metaHeader = MetaHeader.Default(); - metaHeader.Ttl = 2; - - request.MetaHeader = metaHeader.ToMessage(); - - if (sessionToken != null) - request.MetaHeader.SessionToken = sessionToken; - - if (xHeaders != null && xHeaders.Length > 0) - { - if (xHeaders.Length % 2 != 0) - throw new ArgumentException("xHeaders with odd length"); - - for (var i = 0; i < xHeaders.Length; i += 2) - { - request.MetaHeader.XHeaders.Add(new XHeader { Key = xHeaders[i], Value = xHeaders[i + 1] }); - } - } - } -} diff --git a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs b/src/FrostFS.SDK.Client/Tools/RequestSigner.cs deleted file mode 100644 index 8df1b7c..0000000 --- a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Security.Cryptography; - -using FrostFS.Refs; -using FrostFS.SDK.Cryptography; -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -using Org.BouncyCastle.Asn1.Sec; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Crypto.Signers; -using Org.BouncyCastle.Math; -using Signature = FrostFS.Refs.Signature; - -namespace FrostFS.SDK.Client; - -public static class RequestSigner -{ - internal const int RFC6979SignatureSize = 64; - - internal static ByteString SignRFC6979(this ECDsa key, byte[] data) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - var digest = new Sha256Digest(); - var secp256R1 = SecNamedCurves.GetByName("secp256r1"); - var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); - var privateKey = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ecParameters); - var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); - - var hash = new byte[digest.GetDigestSize()]; - - digest.BlockUpdate(data, 0, data.Length); - digest.DoFinal(hash, 0); - signer.Init(true, privateKey); - - var rs = signer.GenerateSignature(hash); - - Span signature = stackalloc byte[RFC6979SignatureSize]; - - var rbytes = rs[0].ToByteArrayUnsigned(); - var sbytes = rs[1].ToByteArrayUnsigned(); - var index = RFC6979SignatureSize / 2 - rbytes.Length; - - rbytes.AsSpan().CopyTo(signature.Slice(index)); - index = RFC6979SignatureSize - sbytes.Length; - sbytes.AsSpan().CopyTo(signature.Slice(index)); - - return ByteString.CopyFrom(signature); - } - - internal static SignatureRFC6979 SignRFC6979(this ClientKey key, IMessage message) - { - return new SignatureRFC6979 - { - Key = key.PublicKeyProto, - Sign = key.ECDsaKey.SignRFC6979(message.ToByteArray()), - }; - } - - internal static SignatureRFC6979 SignRFC6979(this ClientKey key, ByteString data) - { - return new SignatureRFC6979 - { - Key = key.PublicKeyProto, - Sign = key.ECDsaKey.SignRFC6979(data.ToByteArray()), - }; - } - - public static ByteString SignData(this ECDsa key, ReadOnlyMemory data) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - Span result = stackalloc byte[65]; - result[0] = 0x04; - - key.SignHash(DataHasher.Sha512(data)).AsSpan().CopyTo(result.Slice(1)); - - return ByteString.CopyFrom(result); - } - - public static ByteString SignDataByHash(this ECDsa key, byte[] hash) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - Span result = stackalloc byte[65]; - result[0] = 0x04; - - key.SignHash(hash).AsSpan().CopyTo(result.Slice(1)); - - return ByteString.CopyFrom(result); - } - - internal static Signature SignMessagePart(this ClientKey key, IMessage? data) - { - if (data is null || data.CalculateSize() == 0) - { - return new Signature - { - Key = key.PublicKeyProto, - Sign = key.ECDsaKey.SignData(ReadOnlyMemory.Empty), - }; - } - - using var sha512 = SHA512.Create(); - using HashStream stream = new(sha512); - data.WriteTo(stream); - - var sig = new Signature - { - Key = key.PublicKeyProto, - Sign = key.ECDsaKey.SignDataByHash(stream.Hash()) - }; - - return sig; - } - - internal static void Sign(this IVerifiableMessage message, ClientKey key) - { - var meta = message.GetMetaHeader(); - IVerificationHeader verify = message switch - { - IRequest => new RequestVerificationHeader(), - IResponse => new ResponseVerificationHeader(), - _ => throw new InvalidOperationException("Unsupported message type") - }; - - var verifyOrigin = message.GetVerificationHeader(); - - if (verifyOrigin is null) - verify.BodySignature = key.SignMessagePart(message.GetBody()); - else - verify.SetOrigin(verifyOrigin); - - verify.MetaSignature = key.SignMessagePart(meta); - verify.OriginSignature = key.SignMessagePart(verifyOrigin); - - message.SetVerificationHeader(verify); - } -} diff --git a/src/FrostFS.SDK.Client/Tools/SearchReader.cs b/src/FrostFS.SDK.Client/Tools/SearchReader.cs deleted file mode 100644 index ba9b1d1..0000000 --- a/src/FrostFS.SDK.Client/Tools/SearchReader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -using FrostFS.Object; -using FrostFS.Refs; - -using Google.Protobuf.Collections; - -using Grpc.Core; - -namespace FrostFS.SDK.Client; - -internal sealed class SearchReader(AsyncServerStreamingCall call) : IDisposable -{ - internal AsyncServerStreamingCall Call { get; private set; } = call; - - internal async Task?> Read(CancellationToken cancellationToken) - { - if (!await Call.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) - return null; - - var response = Call.ResponseStream.Current; - - Verifier.CheckResponse(response); - - return response.Body?.IdList; - } - - public void Dispose() - { - Call?.Dispose(); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Tools/Verifier.cs b/src/FrostFS.SDK.Client/Tools/Verifier.cs deleted file mode 100644 index 90eb3ce..0000000 --- a/src/FrostFS.SDK.Client/Tools/Verifier.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Security.Cryptography; - -using FrostFS.Refs; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.SDK.Client; - -public static class Verifier -{ - public const int RFC6979SignatureSize = 64; - - public static bool VerifyData(this ECDsa key, IMessage data, ByteString sig) - { - if (key is null) - throw new ArgumentNullException(nameof(key)); - - if (sig is null) - throw new ArgumentNullException(nameof(sig)); - - var signature = sig.Span.Slice(1).ToArray(); - using var sha = SHA512.Create(); - - if (data is null) - { - return key.VerifyHash(DataHasher.Sha512(new Span([])), signature); - } - - using var stream = new HashStream(sha); - data.WriteTo(stream); - - return key.VerifyHash(stream.Hash(), signature); - } - - public static bool VerifyMessagePart(this Signature sig, IMessage data) - { - if (sig is null || sig.Key is null || sig.Sign is null) - return false; - - using var key = sig.Key.ToByteArray().LoadPublicKey(); - - return key.VerifyData(data, sig.Sign); - } - - internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) - { - if (!verification.MetaSignature.VerifyMessagePart(meta)) - return false; - - var origin = verification.GetOrigin(); - - if (!verification.OriginSignature.VerifyMessagePart(origin)) - return false; - - if (origin is null) - return verification.BodySignature.VerifyMessagePart(body); - - return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin); - } - - public static bool Verify(this IVerifiableMessage message) - { - if (message is null) - { - throw new ArgumentNullException(nameof(message)); - } - - return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader()); - } - - internal static void CheckResponse(IResponse resp) - { - if (!resp.Verify()) - { - throw new FormatException($"invalid response, type={resp.GetType()}"); - } - - if (resp.MetaHeader != null) - { - var status = resp.MetaHeader.Status.ToModel(); - - if (status != null && !status.IsSuccess) - { - throw new FrostFsResponseException(status); - } - } - } - - /// - /// This method is intended for unit tests for request verification. - /// - /// Created by SDK request to gRpc proxy - public static void CheckRequest(IRequest request) - { - if (request is null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (!request.Verify()) - { - throw new FrostFsResponseException($"invalid response, type={request.GetType()}"); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Tools/WalletTools.cs b/src/FrostFS.SDK.Client/Tools/WalletTools.cs deleted file mode 100644 index 45532a7..0000000 --- a/src/FrostFS.SDK.Client/Tools/WalletTools.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Text.Json; -using System; -using FrostFS.SDK.Client.Wallets; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK.Client; - -public static class WalletTools -{ - public static string GetWifFromWallet(string walletJsonText, byte[] password, int accountIndex = 0) - { - var wallet = JsonSerializer.Deserialize(walletJsonText) ?? throw new ArgumentException("Wrong wallet format"); - - if (wallet.Accounts == null || wallet.Accounts.Length < accountIndex + 1) - { - throw new ArgumentException("Wrong wallet content"); - } - - var encryptedKey = wallet.Accounts[accountIndex].Key; - - if (string.IsNullOrEmpty(encryptedKey)) - { - throw new ArgumentException("Cannot get encrypted WIF"); - } - - var privateKey = CryptoWallet.GetKeyFromEncodedWif(password, encryptedKey!); - var wif = privateKey.GetWIFFromPrivateKey(); - - return wif; - } -} diff --git a/src/FrostFS.SDK.Client/Wallets/Account.cs b/src/FrostFS.SDK.Client/Wallets/Account.cs deleted file mode 100644 index 1d5b3eb..0000000 --- a/src/FrostFS.SDK.Client/Wallets/Account.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FrostFS.SDK.Client.Wallets; - -public class Account -{ - [JsonPropertyName("address")] - public string? Address { get; set; } - - [JsonPropertyName("key")] - public string? Key { get; set; } - - [JsonPropertyName("label")] - public string? Label { get; set; } - - [JsonPropertyName("contract")] - public Contract? Contract { get; set; } - - [JsonPropertyName("lock")] - public bool Lock { get; set; } - - [JsonPropertyName("isDefault")] - public bool IsDefault { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Wallets/Contract.cs b/src/FrostFS.SDK.Client/Wallets/Contract.cs deleted file mode 100644 index b15d21d..0000000 --- a/src/FrostFS.SDK.Client/Wallets/Contract.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FrostFS.SDK.Client.Wallets; - -public class Contract -{ - [JsonPropertyName("script")] - public string? Script { get; set; } - - [JsonPropertyName("parameters")] - public Parameter[]? Parameters { get; set; } - - [JsonPropertyName("deployed")] - public bool Deployed { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Wallets/Extra.cs b/src/FrostFS.SDK.Client/Wallets/Extra.cs deleted file mode 100644 index bff8b8d..0000000 --- a/src/FrostFS.SDK.Client/Wallets/Extra.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FrostFS.SDK.Client.Wallets; - -public class Extra -{ - public string? Tokens { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Wallets/Parameter.cs b/src/FrostFS.SDK.Client/Wallets/Parameter.cs deleted file mode 100644 index c831e36..0000000 --- a/src/FrostFS.SDK.Client/Wallets/Parameter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FrostFS.SDK.Client.Wallets; - -public class Parameter -{ - [JsonPropertyName("name")] - public string? Name { get; set; } - - [JsonPropertyName("type")] - public string? Type { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Wallets/ScryptValue.cs b/src/FrostFS.SDK.Client/Wallets/ScryptValue.cs deleted file mode 100644 index 2548b32..0000000 --- a/src/FrostFS.SDK.Client/Wallets/ScryptValue.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FrostFS.SDK.Client.Wallets; - -public class ScryptValue -{ - [JsonPropertyName("n")] - public int N { get; set; } - - [JsonPropertyName("r")] - public int R { get; set; } - - [JsonPropertyName("p")] - public int P { get; set; } -} diff --git a/src/FrostFS.SDK.Client/Wallets/Wallet.cs b/src/FrostFS.SDK.Client/Wallets/Wallet.cs deleted file mode 100644 index e1439e0..0000000 --- a/src/FrostFS.SDK.Client/Wallets/Wallet.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FrostFS.SDK.Client.Wallets; - -public class Wallet -{ - [JsonPropertyName("version")] - public string? Version { get; set; } - - [JsonPropertyName("accounts")] - public Account[]? Accounts { get; set; } - - [JsonPropertyName("scrypt")] - public ScryptValue? Scrypt { get; set; } - - [JsonPropertyName("extra")] - public Extra? Extra { get; set; } -} diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs new file mode 100644 index 0000000..7cb47d6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Client.cs @@ -0,0 +1,103 @@ +using System; +using System.Security.Cryptography; +using FrostFS.Container; +using FrostFS.Netmap; +using FrostFS.Object; +using FrostFS.SDK.ClientV2.Interfaces; +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ModelsV2; +using FrostFS.Session; +using Grpc.Core; +using Grpc.Net.Client; +using Version = FrostFS.SDK.ModelsV2.Version; + +namespace FrostFS.SDK.ClientV2; + +public partial class Client: IFrostFSClient +{ + private GrpcChannel? _channel; + private readonly ECDsa _key; + public readonly OwnerId OwnerId; + public readonly Version Version = new(2, 13); + + private ContainerService.ContainerServiceClient? _containerServiceClient; + private NetmapService.NetmapServiceClient? _netmapServiceClient; + private ObjectService.ObjectServiceClient? _objectServiceClient; + private SessionService.SessionServiceClient? _sessionServiceClient; + + public Client(string key, string host) + { + // TODO: Развязать клиент и реализацию GRPC + _key = key.LoadWif(); + OwnerId = OwnerId.FromKey(_key); + InitGrpcChannel(host); + InitContainerClient(); + InitNetmapClient(); + InitObjectClient(); + InitSessionClient(); + CheckFrostFsVersionSupport(); + } + + private async void CheckFrostFsVersionSupport() + { + var localNodeInfo = await GetLocalNodeInfoAsync(); + if (!localNodeInfo.Version.IsSupported(Version)) + { + var msg = $"FrostFS {localNodeInfo.Version} is not supported."; + Console.WriteLine(msg); + throw new ApplicationException(msg); + } + } + + private void InitGrpcChannel(string host) + { + Uri uri; + try + { + uri = new Uri(host); + } + catch (UriFormatException e) + { + var msg = $"Host '{host}' has invalid format. Error: {e.Message}"; + Console.WriteLine(msg); + throw new ArgumentException(msg); + } + + ChannelCredentials grpcCredentials; + switch (uri.Scheme) + { + case "https": + grpcCredentials = ChannelCredentials.SecureSsl; + break; + case "http": + grpcCredentials = ChannelCredentials.Insecure; + break; + default: + var msg = $"Host '{host}' has invalid URI scheme: '{uri.Scheme}'."; + Console.WriteLine(msg); + throw new ArgumentException(msg); + } + + _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions { Credentials = grpcCredentials }); + } + + private void InitContainerClient() + { + _containerServiceClient = new ContainerService.ContainerServiceClient(_channel); + } + + private void InitNetmapClient() + { + _netmapServiceClient = new NetmapService.NetmapServiceClient(_channel); + } + + private void InitObjectClient() + { + _objectServiceClient = new ObjectService.ObjectServiceClient(_channel); + } + + private void InitSessionClient() + { + _sessionServiceClient = new SessionService.SessionServiceClient(_channel); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj new file mode 100644 index 0000000..0208178 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + 12.0 + enable + + + + + + + + + + + + + diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs new file mode 100644 index 0000000..8412c6c --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +using FrostFS.SDK.ModelsV2; + +namespace FrostFS.SDK.ClientV2.Interfaces; + +public interface IFrostFSClient +{ + Task GetContainerAsync(ContainerId containerId); + IAsyncEnumerable ListContainersAsync(); + Task CreateContainerAsync(ModelsV2.Container container); + Task DeleteContainerAsync(ContainerId containerId); + Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId); + Task GetObjectAsync(ContainerId containerId, ObjectId objectId); + Task PutObjectAsync(ObjectHeader header, Stream payload); + Task PutObjectAsync(ObjectHeader header, byte[] payload); + Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId); + IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters); +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs new file mode 100644 index 0000000..93eac3f --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs @@ -0,0 +1,40 @@ +using System; + +using Google.Protobuf; + +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ModelsV2.Enums; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ContainerMapper +{ + public static Container.Container ToGrpcMessage(this ModelsV2.Container container) + { + return new Container.Container + { + BasicAcl = (uint)container.BasicAcl, + PlacementPolicy = container.PlacementPolicy.ToGrpcMessage(), + Nonce = ByteString.CopyFrom(container.Nonce.ToBytes()) + }; + } + + public static ModelsV2.Container ToModel(this Container.Container container) + { + var basicAclName = Enum.GetName(typeof(BasicAcl), container.BasicAcl); + if (basicAclName is null) + { + throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'."); + } + + return new ModelsV2.Container( + (BasicAcl)Enum.Parse(typeof(BasicAcl), basicAclName), + container.PlacementPolicy.ToModel() + ) + { + Nonce = container.Nonce.ToUuid(), + Version = container.Version.ToModel() + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs new file mode 100644 index 0000000..1bfd614 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs @@ -0,0 +1,16 @@ +using FrostFS.Refs; +using FrostFS.SDK.ModelsV2; +using Google.Protobuf; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ContainerIdMapper +{ + public static ContainerID ToGrpcMessage(this ContainerId containerId) + { + return new ContainerID + { + Value = ByteString.CopyFrom(containerId.ToHash()) + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs new file mode 100644 index 0000000..d3bf04f --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs @@ -0,0 +1,23 @@ +using FrostFS.SDK.ModelsV2; +using FrostFS.Session; +using Version = FrostFS.Refs.Version; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class MetaHeaderMapper +{ + public static RequestMetaHeader ToGrpcMessage(this MetaHeader metaHeader) + { + return new RequestMetaHeader + { + Version = new Version + { + Major = (uint)metaHeader.Version.Major, + Minor = (uint)metaHeader.Version.Minor, + + }, + Epoch = (uint)metaHeader.Epoch, + Ttl = (uint)metaHeader.Ttl + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs new file mode 100644 index 0000000..f8f2d56 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs @@ -0,0 +1,24 @@ +using System; + +using FrostFS.Netmap; +using FrostFS.SDK.ModelsV2.Enums; +using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +public static class NodeInfoMapper +{ + public static NodeInfo ToModel(this LocalNodeInfoResponse.Types.Body nodeInfo) + { + var nodeStateName = Enum.GetName(typeof(NodeState), nodeInfo.NodeInfo.State); + if (nodeStateName is null) + { + throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.NodeInfo.State}'."); + } + return new NodeInfo + { + State = (NodeState)Enum.Parse(typeof(NodeState), nodeStateName), + Version = nodeInfo.Version.ToModel() + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs new file mode 100644 index 0000000..a4cbb0d --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs @@ -0,0 +1,32 @@ +using System.Linq; +using FrostFS.Netmap; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +public static class PlacementPolicyMapper +{ + public static PlacementPolicy ToGrpcMessage(this ModelsV2.Netmap.PlacementPolicy placementPolicy) + { + var pp = new PlacementPolicy + { + Filters = { }, + Selectors = { }, + Replicas = { }, + Unique = placementPolicy.Unique + }; + foreach (var replica in placementPolicy.Replicas) + { + pp.Replicas.Add(replica.ToGrpcMessage()); + } + + return pp; + } + + public static ModelsV2.Netmap.PlacementPolicy ToModel(this PlacementPolicy placementPolicy) + { + return new ModelsV2.Netmap.PlacementPolicy( + placementPolicy.Unique, + placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray() + ); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs new file mode 100644 index 0000000..7435085 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs @@ -0,0 +1,20 @@ +using FrostFS.Netmap; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +public static class ReplicaMapper +{ + public static Replica ToGrpcMessage(this ModelsV2.Netmap.Replica replica) + { + return new Replica + { + Count = (uint)replica.Count, + Selector = replica.Selector + }; + } + + public static ModelsV2.Netmap.Replica ToModel(this Replica replica) + { + return new ModelsV2.Netmap.Replica((int)replica.Count, replica.Selector); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs new file mode 100644 index 0000000..b6e68f6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs @@ -0,0 +1,91 @@ +using System; +using System.Linq; + +using FrostFS.Object; +using FrostFS.SDK.ModelsV2; +using MatchType = FrostFS.Object.MatchType; +using ObjectType = FrostFS.Object.ObjectType; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ObjectAttributeMapper +{ + public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute) + { + return new Header.Types.Attribute + { + Key = attribute.Key, + Value = attribute.Value + }; + } + + public static ObjectAttribute ToModel(this Header.Types.Attribute attribute) + { + return new ObjectAttribute(attribute.Key, attribute.Value); + } +} + +public static class ObjectFilterMapper +{ + public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter) + { + var objMatchTypeName = Enum.GetName(typeof(MatchType), filter.MatchType) + ?? throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'."); + return new SearchRequest.Types.Body.Types.Filter + { + MatchType = (MatchType)Enum.Parse(typeof(MatchType), objMatchTypeName), + Key = filter.Key, + Value = filter.Value + }; + } +} + +public static class ObjectHeaderMapper +{ + public static Header ToGrpcMessage(this ObjectHeader header) + { + var objTypeName = Enum.GetName(typeof(ObjectType), header.ObjectType) + ?? throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'."); + var head = new Header + { + Attributes = { }, + ContainerId = header.ContainerId.ToGrpcMessage(), + ObjectType = (ObjectType)Enum.Parse(typeof(ObjectType), objTypeName) + }; + + foreach (var attribute in header.Attributes) + { + head.Attributes.Add(attribute.ToGrpcMessage()); + } + + return head; + } + + public static ObjectHeader ToModel(this Header header) + { + var objTypeName = Enum.GetName(typeof(ModelsV2.Enums.ObjectType), header.ObjectType) + ?? throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'."); + return new ObjectHeader( + ContainerId.FromHash(header.ContainerId.Value.ToByteArray()), + (ModelsV2.Enums.ObjectType)Enum.Parse(typeof(ModelsV2.Enums.ObjectType), objTypeName), + header.Attributes.Select(attribute => attribute.ToModel()).ToArray() + ) + { + Size = (long)header.PayloadLength, + 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() + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs new file mode 100644 index 0000000..4c28035 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs @@ -0,0 +1,16 @@ +using FrostFS.Refs; +using FrostFS.SDK.ModelsV2; +using Google.Protobuf; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ObjectIdMapper +{ + public static ObjectID ToGrpcMessage(this ObjectId objectId) + { + return new ObjectID + { + Value = ByteString.CopyFrom(objectId.ToHash()) + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs new file mode 100644 index 0000000..4bea66d --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs @@ -0,0 +1,16 @@ +using FrostFS.Refs; +using FrostFS.SDK.ModelsV2; +using Google.Protobuf; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class OwnerIdMapper +{ + public static OwnerID ToGrpcMessage(this OwnerId ownerId) + { + return new OwnerID + { + Value = ByteString.CopyFrom(ownerId.ToHash()) + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs new file mode 100644 index 0000000..9e3d2eb --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs @@ -0,0 +1,17 @@ +using System; +using FrostFS.SDK.ModelsV2.Enums; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class StatusMapper +{ + public static ModelsV2.Status ToModel(this Status.Status status) + { + if (status is null) return new ModelsV2.Status(StatusCode.Success); + var codeName = Enum.GetName(typeof(StatusCode), status.Code); + + return codeName is null + ? throw new ArgumentException($"Unknown StatusCode. Value: '{status.Code}'.") + : new ModelsV2.Status((StatusCode)Enum.Parse(typeof(StatusCode), codeName), status.Message); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs new file mode 100644 index 0000000..549ff1a --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs @@ -0,0 +1,20 @@ +using Version = FrostFS.Refs.Version; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class VersionMapper +{ + public static Version ToGrpcMessage(this ModelsV2.Version version) + { + return new Version + { + Major = (uint)version.Major, + Minor = (uint)version.Minor + }; + } + + public static ModelsV2.Version ToModel(this Version version) + { + return new ModelsV2.Version((int)version.Major, (int)version.Minor); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Range.cs b/src/FrostFS.SDK.ClientV2/Range.cs new file mode 100644 index 0000000..8158314 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Range.cs @@ -0,0 +1,271 @@ +using System.Runtime.CompilerServices; + +namespace System +{ + /// Represent a type can be used to index a collection either from the start or the end. + /// + /// Index is used by the C# compiler to support the new index syntax + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; + /// int lastElement = someArray[^1]; // lastElement = 5 + /// + /// + internal readonly struct Index : IEquatable + { + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new Index(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new Index(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + { + return ~_value; + } + else + { + return _value; + } + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetOffset(int length) + { + var offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + public override string ToString() + { + if (IsFromEnd) + return "^" + ((uint)Value).ToString(); + + return ((uint)Value).ToString(); + } + } + + /// Represent a range has start and end indexes. + /// + /// Range is used by the C# compiler to support the range syntax. + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; + /// int[] subArray1 = someArray[0..2]; // { 1, 2 } + /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } + /// + /// + internal readonly struct Range : IEquatable + { + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return Start.GetHashCode() * 31 + End.GetHashCode(); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start + ".." + End; + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new Range(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new Range(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new Range(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + var startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + var endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } + } +} + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T) != null || typeof(T[]) == array.GetType()) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + return Array.Empty(); + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + var dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} diff --git a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/RequestConstructor.cs new file mode 100644 index 0000000..fb632ee --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/RequestConstructor.cs @@ -0,0 +1,40 @@ +using System.Security.Cryptography; +using FrostFS.Refs; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.ModelsV2; +using FrostFS.Session; + +namespace FrostFS.SDK.ClientV2; + +public static class RequestConstructor +{ + public static void AddMetaHeader(this IRequest request, RequestMetaHeader? metaHeader = null) + { + if (request.MetaHeader is not null) return; + metaHeader ??= MetaHeader.Default().ToGrpcMessage(); + request.MetaHeader = metaHeader; + } + + public static void AddObjectSessionToken( + this IRequest request, + SessionToken sessionToken, + ContainerID cid, + ObjectID oid, + ObjectSessionContext.Types.Verb verb, + ECDsa key) + { + if (request.MetaHeader.SessionToken is not null) return; + request.MetaHeader.SessionToken = sessionToken; + var ctx = new ObjectSessionContext + { + Target = new ObjectSessionContext.Types.Target + { + Container = cid, + Objects = { oid } + }, + Verb = verb + }; + request.MetaHeader.SessionToken.Body.Object = ctx; + request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body); + } +} diff --git a/src/FrostFS.SDK.ClientV2/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/RequestSigner.cs new file mode 100644 index 0000000..3747c92 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/RequestSigner.cs @@ -0,0 +1,106 @@ +using System; +using System.Security.Cryptography; + +using Google.Protobuf; + +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math; + +using FrostFS.Refs; +using FrostFS.SDK.Cryptography; +using FrostFS.Session; + +namespace FrostFS.SDK.ClientV2; + +public static class RequestSigner +{ + public const int RFC6979SignatureSize = 64; + + public static byte[] SignRFC6979(this ECDsa key, byte[] data) + { + var digest = new Sha256Digest(); + var secp256R1 = SecNamedCurves.GetByName("secp256r1"); + var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); + var privateKey = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ecParameters); + var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); + var hash = new byte[digest.GetDigestSize()]; + + digest.BlockUpdate(data, 0, data.Length); + digest.DoFinal(hash, 0); + signer.Init(true, privateKey); + + var rs = signer.GenerateSignature(hash); + var signature = new byte[RFC6979SignatureSize]; + var rbytes = rs[0].ToByteArrayUnsigned(); + var sbytes = rs[1].ToByteArrayUnsigned(); + var index = RFC6979SignatureSize / 2 - rbytes.Length; + + rbytes.CopyTo(signature, index); + index = RFC6979SignatureSize - sbytes.Length; + sbytes.CopyTo(signature, index); + + return signature; + } + + public static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message) + { + return new SignatureRFC6979 + { + Key = ByteString.CopyFrom(key.PublicKey()), + Sign = ByteString.CopyFrom(key.SignRFC6979(message.ToByteArray())), + }; + } + + public static SignatureRFC6979 SignRFC6979(this ECDsa key, ByteString data) + { + return new SignatureRFC6979 + { + Key = ByteString.CopyFrom(key.PublicKey()), + Sign = ByteString.CopyFrom(key.SignRFC6979(data.ToByteArray())), + }; + } + + public static byte[] SignData(this ECDsa key, byte[] data) + { + var hash = new byte[65]; + hash[0] = 0x04; + key + .SignHash(SHA512.Create().ComputeHash(data)) + .CopyTo(hash, 1); + return hash; + } + + public static Signature SignMessagePart(this ECDsa key, IMessage? data) + { + var data2Sign = data is null ? Array.Empty() : data.ToByteArray(); + var sig = new Signature + { + Key = ByteString.CopyFrom(key.PublicKey()), + Sign = ByteString.CopyFrom(key.SignData(data2Sign)), + }; + return sig; + } + + public static void Sign(this IVerificableMessage message, ECDsa key) + { + var meta = message.GetMetaHeader(); + IVerificationHeader verify = message switch + { + IRequest => new RequestVerificationHeader(), + IResponse => new ResponseVerificationHeader(), + _ => throw new InvalidOperationException("Unsopported message type") + }; + + var verifyOrigin = message.GetVerificationHeader(); + if (verifyOrigin is null) + verify.BodySignature = key.SignMessagePart(message.GetBody()); + + verify.MetaSignature = key.SignMessagePart(meta); + verify.OriginSignature = key.SignMessagePart(verifyOrigin); + verify.SetOrigin(verifyOrigin); + message.SetVerificationHeader(verify); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Services/Container.cs b/src/FrostFS.SDK.ClientV2/Services/Container.cs new file mode 100644 index 0000000..886768c --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/Container.cs @@ -0,0 +1,81 @@ +using FrostFS.Container; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.ModelsV2; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace FrostFS.SDK.ClientV2; + +public partial class Client +{ + public async Task GetContainerAsync(ContainerId cid) + { + var request = new GetRequest + { + Body = new GetRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage() + }, + }; + request.AddMetaHeader(); + request.Sign(_key); + var response = await _containerServiceClient.GetAsync(request); + Verifier.CheckResponse(response); + return response.Body.Container.ToModel(); + } + + public async IAsyncEnumerable ListContainersAsync() + { + var request = new ListRequest + { + Body = new ListRequest.Types.Body + { + OwnerId = OwnerId.ToGrpcMessage() + } + }; + request.AddMetaHeader(); + request.Sign(_key); + var response = await _containerServiceClient.ListAsync(request); + Verifier.CheckResponse(response); + foreach (var cid in response.Body.ContainerIds) + { + yield return ContainerId.FromHash(cid.Value.ToByteArray()); + } + } + + public async Task CreateContainerAsync(ModelsV2.Container container) + { + var cntnr = container.ToGrpcMessage(); + cntnr.OwnerId = OwnerId.ToGrpcMessage(); + cntnr.Version = Version.ToGrpcMessage(); + var request = new PutRequest + { + Body = new PutRequest.Types.Body + { + Container = cntnr, + Signature = _key.SignRFC6979(cntnr), + } + }; + request.AddMetaHeader(); + request.Sign(_key); + var response = await _containerServiceClient.PutAsync(request); + Verifier.CheckResponse(response); + return ContainerId.FromHash(response.Body.ContainerId.Value.ToByteArray()); + } + + public async Task DeleteContainerAsync(ContainerId cid) + { + var request = new DeleteRequest + { + Body = new DeleteRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage(), + Signature = _key.SignRFC6979(cid.ToGrpcMessage().Value) + } + }; + request.AddMetaHeader(); + request.Sign(_key); + var response = await _containerServiceClient.DeleteAsync(request); + Verifier.CheckResponse(response); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs b/src/FrostFS.SDK.ClientV2/Services/Netmap.cs new file mode 100644 index 0000000..ce439cc --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/Netmap.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; + +using FrostFS.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; + +namespace FrostFS.SDK.ClientV2; + +public partial class Client +{ + public async Task GetLocalNodeInfoAsync() + { + var request = new LocalNodeInfoRequest + { + Body = new LocalNodeInfoRequest.Types.Body { } + }; + + request.AddMetaHeader(); + request.Sign(_key); + var response = await _netmapServiceClient.LocalNodeInfoAsync(request); + + return response.Body.ToModel(); + } + + public async Task GetNetworkInfoAsync() + { + var request = new NetworkInfoRequest + { + Body = new NetworkInfoRequest.Types.Body { } + }; + + request.AddMetaHeader(); + request.Sign(_key); + + return await _netmapServiceClient.NetworkInfoAsync(request); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/Object.cs new file mode 100644 index 0000000..180e351 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/Object.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +using Google.Protobuf; + +using FrostFS.Object; +using FrostFS.Refs; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ModelsV2; +using FrostFS.Session; + +namespace FrostFS.SDK.ClientV2; + +public partial class Client +{ + public async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid) + { + var request = new HeadRequest + { + Body = new HeadRequest.Types.Body + { + Address = new Address + { + ContainerId = cid.ToGrpcMessage(), + ObjectId = oid.ToGrpcMessage() + } + } + }; + + request.AddMetaHeader(); + request.Sign(_key); + var response = await _objectServiceClient.HeadAsync(request); + Verifier.CheckResponse(response); + + return response.Body.Header.Header.ToModel(); + } + + public async Task 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.ToGrpcMessage(), + ObjectId = oid.ToGrpcMessage() + } + } + }; + + request.AddMetaHeader(); + request.AddObjectSessionToken( + sessionToken, + cid.ToGrpcMessage(), + oid.ToGrpcMessage(), + ObjectSessionContext.Types.Verb.Get, + _key + ); + + request.Sign(_key); + var obj = await GetObject(request); + + return obj.ToModel(); + } + + public async Task PutObjectAsync(ObjectHeader header, Stream payload) + { + return await PutObject(header, payload); + } + + public async Task PutObjectAsync(ObjectHeader header, byte[] payload) + { + return await PutObject(header, new MemoryStream(payload)); + } + + public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid) + { + var request = new DeleteRequest + { + Body = new DeleteRequest.Types.Body + { + Address = new Address + { + ContainerId = cid.ToGrpcMessage(), + ObjectId = oid.ToGrpcMessage() + } + } + }; + + request.AddMetaHeader(); + request.Sign(_key); + var response = await _objectServiceClient.DeleteAsync(request); + Verifier.CheckResponse(response); + } + + public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters) + { + var request = new SearchRequest + { + Body = new SearchRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage(), + Filters = { }, + Version = 1 + } + }; + + foreach (var filter in filters) + { + request.Body.Filters.Add(filter.ToGrpcMessage()); + } + + request.AddMetaHeader(); + request.Sign(_key); + var objectsIds = SearchObjects(request); + + await foreach (var oid in objectsIds) + { + yield return ObjectId.FromHash(oid.Value.ToByteArray()); + } + } + + private async Task 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) + }; + } + + private async Task PutObject(ObjectHeader header, Stream payload) + { + var sessionToken = await CreateSessionAsync(uint.MaxValue); + var hdr = header.ToGrpcMessage(); + hdr.OwnerId = OwnerId.ToGrpcMessage(); + hdr.Version = Version.ToGrpcMessage(); + + var oid = new ObjectID + { + Value = hdr.Sha256() + }; + + var request = new PutRequest + { + Body = new PutRequest.Types.Body + { + Init = new PutRequest.Types.Body.Types.Init + { + Header = hdr + }, + } + }; + + request.AddMetaHeader(); + request.AddObjectSessionToken( + sessionToken, + hdr.ContainerId, + oid, + ObjectSessionContext.Types.Verb.Put, + _key + ); + + request.Sign(_key); + + using var stream = await PutObjectInit(request); + var buffer = new byte[Constants.ObjectChunkSize]; + var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize); + + while (bufferLength > 0) + { + request.Body = new PutRequest.Types.Body + { + Chunk = ByteString.CopyFrom(buffer[..bufferLength]), + }; + request.VerifyHeader = null; + request.Sign(_key); + await stream.Write(request); + bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize); + } + + var response = await stream.Close(); + Verifier.CheckResponse(response); + + return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()); + } + + private async Task PutObjectInit(PutRequest initRequest) + { + if (initRequest is null) + { + throw new ArgumentNullException(nameof(initRequest)); + } + + var call = _objectServiceClient.Put(); + await call.RequestStream.WriteAsync(initRequest); + + return new ObjectStreamer(call); + } + + private async IAsyncEnumerable SearchObjects(SearchRequest request) + { + using var stream = GetSearchReader(request); + var ids = await stream.Read(); + while (ids is not null) + { + foreach (var oid in ids) + { + yield return oid; + } + + ids = await stream.Read(); + } + } + + private SearchReader GetSearchReader(SearchRequest initRequest) + { + if (initRequest is null) + { + throw new ArgumentNullException(nameof(initRequest)); + } + + return new SearchReader(_objectServiceClient.Search(initRequest)); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs new file mode 100644 index 0000000..415c001 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; + +using Grpc.Core; + +using FrostFS.Object; + +namespace FrostFS.SDK.ClientV2; + +internal class ObjectReader : IDisposable +{ + public AsyncServerStreamingCall Call { get; set; } + + public async Task ReadHeader() + { + if (!await Call.ResponseStream.MoveNext()) + throw new InvalidOperationException("unexpect end of stream"); + + var response = Call.ResponseStream.Current; + Verifier.CheckResponse(response); + + 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 ReadChunk() + { + if (!await Call.ResponseStream.MoveNext()) + return null; + + var response = Call.ResponseStream.Current; + Verifier.CheckResponse(response); + + if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk) + throw new InvalidOperationException("unexpect message type"); + + return response.Body.Chunk.ToByteArray(); + } + + public void Dispose() + { + Call.Dispose(); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectStreamer.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectStreamer.cs new file mode 100644 index 0000000..11858c3 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectStreamer.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; + +using Grpc.Core; + +using FrostFS.Object; + +namespace FrostFS.SDK.ClientV2; + +internal class ObjectStreamer(AsyncClientStreamingCall call) : IDisposable +{ + public AsyncClientStreamingCall Call { get; private set; } = call; + + public async Task Write(PutRequest request) + { + if (request is null) + { + throw new ArgumentNullException(nameof(request)); + } + + await Call.RequestStream.WriteAsync(request); + } + + public async Task Close() + { + await Call.RequestStream.CompleteAsync(); + + return await Call.ResponseAsync; + } + + public void Dispose() + { + Call.Dispose(); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs new file mode 100644 index 0000000..c3d425a --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using Grpc.Core; + +using FrostFS.Object; +using FrostFS.Refs; + +namespace FrostFS.SDK.ClientV2; + +internal class SearchReader(AsyncServerStreamingCall call) : IDisposable +{ + public AsyncServerStreamingCall Call { get; private set; } = call; + + public async Task?> Read() + { + if (!await Call.ResponseStream.MoveNext()) + { + return null; + } + + var response = Call.ResponseStream.Current; + Verifier.CheckResponse(response); + + return response.Body?.IdList.ToList(); + } + + public void Dispose() + { + Call.Dispose(); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Services/Session.cs b/src/FrostFS.SDK.ClientV2/Services/Session.cs new file mode 100644 index 0000000..3dc52a1 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/Session.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; + +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.Session; + +namespace FrostFS.SDK.ClientV2; + +public partial class Client +{ + private async Task CreateSessionAsync(ulong expiration) + { + var request = new CreateRequest + { + Body = new CreateRequest.Types.Body + { + OwnerId = OwnerId.ToGrpcMessage(), + Expiration = expiration, + } + }; + + request.AddMetaHeader(); + request.Sign(_key); + + return await CreateSession(request); + } + + private async Task CreateSession(CreateRequest request) + { + var resp = await _sessionServiceClient.CreateAsync(request); + + return new SessionToken + { + Body = new SessionToken.Types.Body + { + Id = resp.Body.Id, + SessionKey = resp.Body.SessionKey, + OwnerId = request.Body.OwnerId, + Lifetime = new SessionToken.Types.Body.Types.TokenLifetime + { + Exp = request.Body.Expiration, + Iat = resp.MetaHeader.Epoch, + Nbf = resp.MetaHeader.Epoch, + } + } + }; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Verifier.cs b/src/FrostFS.SDK.ClientV2/Verifier.cs new file mode 100644 index 0000000..ad6b3e6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Verifier.cs @@ -0,0 +1,106 @@ +using System; +using System.Security.Cryptography; + +using Google.Protobuf; + +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math; + +using FrostFS.Refs; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using FrostFS.Session; + +namespace FrostFS.SDK.ClientV2; + +public static class Verifier +{ + public const int RFC6979SignatureSize = 64; + + private static BigInteger[] DecodeSignature(byte[] sig) + { + if (sig.Length != RFC6979SignatureSize) + throw new FormatException($"Wrong signature size, expect={RFC6979SignatureSize}, actual={sig.Length}"); + + var rs = new BigInteger[2]; + rs[0] = new BigInteger(1, sig[..32]); + rs[1] = new BigInteger(1, sig[32..]); + + return rs; + } + + public static bool VerifyRFC6979(this byte[] publicKey, byte[] data, byte[] sig) + { + if (publicKey is null || data is null || sig is null) + return false; + + var rs = DecodeSignature(sig); + var digest = new Sha256Digest(); + var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); + var secp256R1 = SecNamedCurves.GetByName("secp256r1"); + var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); + var bcPublicKey = new ECPublicKeyParameters(secp256R1.Curve.DecodePoint(publicKey), ecParameters); + var hash = new byte[digest.GetDigestSize()]; + + digest.BlockUpdate(data, 0, data.Length); + digest.DoFinal(hash, 0); + signer.Init(false, bcPublicKey); + + return signer.VerifySignature(hash, rs[0], rs[1]); + } + + public static bool VerifyRFC6979(this SignatureRFC6979 signature, IMessage message) + { + return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray()); + } + + public static bool VerifyData(this ECDsa key, byte[] data, byte[] sig) + { + return key.VerifyHash(SHA512.Create().ComputeHash(data), sig[1..]); + } + + public static bool VerifyMessagePart(this Signature sig, IMessage data) + { + if (sig is null || sig.Key is null || sig.Sign is null) + return false; + + using var key = sig.Key.ToByteArray().LoadPublicKey(); + var data2Verify = data is null ? Array.Empty() : data.ToByteArray(); + + return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); + } + + public static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) + { + if (!verification.MetaSignature.VerifyMessagePart(meta)) + return false; + + var origin = verification.GetOrigin(); + + if (!verification.OriginSignature.VerifyMessagePart(origin)) + return false; + + if (origin is null) + return verification.BodySignature.VerifyMessagePart(body); + + return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin); + } + + public static bool Verify(this IVerificableMessage message) + { + return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader()); + } + + public static void CheckResponse(IResponse resp) + { + if (!resp.Verify()) + throw new FormatException($"invalid response, type={resp.GetType()}"); + + var status = resp.MetaHeader.Status.ToModel(); + if (!status.IsSuccess()) + throw new ApplicationException(status.ToString()); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/ArrayHelper.cs b/src/FrostFS.SDK.Cryptography/ArrayHelper.cs index c88134d..00c356d 100644 --- a/src/FrostFS.SDK.Cryptography/ArrayHelper.cs +++ b/src/FrostFS.SDK.Cryptography/ArrayHelper.cs @@ -21,18 +21,4 @@ internal static class ArrayHelper return dst; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void GetRevertedArray(ReadOnlySpan source, byte[] data) - { - if (source.Length != 0) - { - int i = 0; - int j = source.Length - 1; - while (i < source.Length) - { - data[i++] = source[j--]; - } - } - } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs index 64bc8e9..a593c3a 100644 --- a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs +++ b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs @@ -1,7 +1,20 @@ -using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -[assembly: AssemblyCompany("TrueCloudLab")] -[assembly: AssemblyFileVersion("1.0.7.0")] -[assembly: AssemblyProduct("FrostFS.SDK.Cryptography")] -[assembly: AssemblyTitle("FrostFS.SDK.Cryptography")] -[assembly: AssemblyVersion("1.0.7.0")] +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("08a8487e-39ce-41fb-9c24-13f73ff2bde0")] + +[assembly: InternalsVisibleToAttribute("FrostFS.SDK.Cryptography.Test")] \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/Base58.cs b/src/FrostFS.SDK.Cryptography/Base58.cs index 4dc6187..36711c0 100644 --- a/src/FrostFS.SDK.Cryptography/Base58.cs +++ b/src/FrostFS.SDK.Cryptography/Base58.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Numerics; using System.Text; +using Org.BouncyCastle.Security.Certificates; + namespace FrostFS.SDK.Cryptography; public static class Base58 @@ -13,41 +15,35 @@ public static class Base58 { if (input is null) throw new ArgumentNullException(nameof(input)); - + byte[] buffer = Decode(input); - if (buffer.Length < 4) + if (buffer.Length < 4) throw new FormatException(); + + byte[] checksum = buffer[0..(buffer.Length - 4)].Sha256().Sha256(); - var check = buffer.AsSpan(0, buffer.Length - 4); - byte[] checksum = DataHasher.Sha256(DataHasher.Sha256(check).AsSpan()); - - if (!buffer.AsSpan(buffer.Length - 4).SequenceEqual(checksum.AsSpan(0, 4))) + if (!buffer.AsSpan(buffer.Length - 4).SequenceEqual(checksum[..4].AsSpan())) throw new FormatException(); - - var result = check.ToArray(); + + var ret = buffer[..^4]; Array.Clear(buffer, 0, buffer.Length); - return result; + return ret; } - public static string Base58CheckEncode(this Span data) + public static string Base58CheckEncode(this ReadOnlySpan data) { - byte[] checksum = DataHasher.Sha256(DataHasher.Sha256(data).AsSpan()); + byte[] checksum = data.ToArray().Sha256().Sha256(); Span buffer = stackalloc byte[data.Length + 4]; data.CopyTo(buffer); - - checksum.AsSpan(0, 4).CopyTo(buffer.Slice(data.Length)); + checksum[..4].AsSpan().CopyTo(buffer[data.Length..]); var ret = Encode(buffer); buffer.Clear(); - return ret; } public static byte[] Decode(string input) { - if (input == null) - throw new ArgumentNullException(nameof(input)); - // Decode Base58 string to BigInteger var bi = BigInteger.Zero; for (int i = 0; i < input.Length; i++) @@ -55,44 +51,31 @@ public static class Base58 int digit = Alphabet.IndexOf(input[i]); if (digit < 0) throw new FormatException($"Invalid Base58 character '{input[i]}' at position {i}"); - + bi = bi * Alphabet.Length + digit; } int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).Count(); - + var leadingZeros = new byte[leadingZeroCount]; + if (bi.IsZero) - return new byte[leadingZeroCount]; + return leadingZeros; - var bytesBigEndian = bi.ToByteArray().Reverse().ToArray(); + var bytesBigEndian = bi.ToByteArray().Reverse(); var firstNonZeroIndex = 0; - while (bytesBigEndian.ElementAt(firstNonZeroIndex) == 0x0) + while(bytesBigEndian.ElementAt(firstNonZeroIndex) == 0x0) firstNonZeroIndex++; - + var bytesWithoutLeadingZeros = bytesBigEndian.Skip(firstNonZeroIndex).ToArray(); - var result = new byte[leadingZeroCount + bytesBigEndian.Length - firstNonZeroIndex]; - - int p = 0; - while (p < leadingZeroCount) - result[p++] = 0; - - for (int j = firstNonZeroIndex; j < bytesBigEndian.Length; j++) - result[p++] = bytesBigEndian[j]; - - return result; + return ArrayHelper.Concat(leadingZeros, bytesWithoutLeadingZeros); } public static string Encode(ReadOnlySpan input) { - var data = new byte[input.Length + 1]; - - ArrayHelper.GetRevertedArray(input, data); - - data[input.Length] = 0; - + var data = input.ToArray().Reverse().Concat(new byte[] { 0 }).ToArray(); BigInteger value = new(data); // Encode BigInteger to Base58 string diff --git a/src/FrostFS.SDK.Cryptography/DataHasher.cs b/src/FrostFS.SDK.Cryptography/DataHasher.cs deleted file mode 100644 index fceeeaa..0000000 --- a/src/FrostFS.SDK.Cryptography/DataHasher.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Buffers; -using System.Security.Cryptography; - -namespace FrostFS.SDK.Cryptography; - -public static class DataHasher -{ - private const int LargeBlockSize = 1024 * 1024; - private const int SmallBlockSize = 256; - - public static byte[] Hash(this ReadOnlyMemory bytes, HashAlgorithm algorithm) - { - if (algorithm is null) - { - throw new ArgumentNullException(nameof(algorithm)); - } - - if (bytes.Length == 0) - { - return algorithm.ComputeHash([]); - } - - int rest, pos = 0; - - var blockSize = bytes.Length <= SmallBlockSize ? SmallBlockSize : LargeBlockSize; - - byte[] buffer = ArrayPool.Shared.Rent(blockSize); - - try - { - while ((rest = bytes.Length - pos) > 0) - { - var size = Math.Min(rest, blockSize); - - bytes.Slice(pos, size).CopyTo(buffer); - - algorithm.TransformBlock(buffer, 0, size, buffer, 0); - - pos += size; - } - - algorithm.TransformFinalBlock([], 0, 0); - return algorithm.Hash; - } - finally - { - if (buffer != null) - { - ArrayPool.Shared.Return(buffer); - } - } - } - - public static byte[] Hash(this ReadOnlySpan bytes, HashAlgorithm algorithm) - { - if (algorithm is null) - { - throw new ArgumentNullException(nameof(algorithm)); - } - - if (bytes.Length == 0) - { - return algorithm.ComputeHash([]); - } - - int rest, pos = 0; - - var blockSize = bytes.Length <= SmallBlockSize ? SmallBlockSize : LargeBlockSize; - - byte[] buffer = ArrayPool.Shared.Rent(blockSize); - - try - { - while ((rest = bytes.Length - pos) > 0) - { - var size = Math.Min(rest, blockSize); - - bytes.Slice(pos, size).CopyTo(buffer); - - algorithm.TransformBlock(buffer, 0, size, buffer, 0); - - pos += size; - } - - algorithm.TransformFinalBlock([], 0, 0); - return algorithm.Hash; - } - finally - { - if (buffer != null) - { - ArrayPool.Shared.Return(buffer); - } - } - } - - public static byte[] Sha256(ReadOnlyMemory value) - { - using SHA256 sha = SHA256.Create(); - return Hash(value, sha); - } - - public static byte[] Sha256(ReadOnlySpan value) - { - using SHA256 sha = SHA256.Create(); - return Hash(value, sha); - } - - public static byte[] Sha512(ReadOnlyMemory value) - { - using SHA512 sha = SHA512.Create(); - return Hash(value, sha); - } - - public static byte[] Sha512(ReadOnlySpan value) - { - using SHA512 sha = SHA512.Create(); - return Hash(value, sha); - } -} diff --git a/src/FrostFS.SDK.Cryptography/Extentions.cs b/src/FrostFS.SDK.Cryptography/Extentions.cs deleted file mode 100644 index 5f4fdd9..0000000 --- a/src/FrostFS.SDK.Cryptography/Extentions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Org.BouncyCastle.Crypto.Digests; - -namespace FrostFS.SDK.Cryptography; - -public static class Extentions -{ - internal static byte[] RIPEMD160(this byte[] value) - { - var hash = new byte[20]; - - var digest = new RipeMD160Digest(); - digest.BlockUpdate(value, 0, value.Length); - digest.DoFinal(hash, 0); - return hash; - } -} diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index faeabb1..0534720 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -4,40 +4,12 @@ netstandard2.0 12.0 enable - FrostFS.SDK.Cryptography - 1.0.7 - - Cryptography tools for C# SDK - - true - - - - true - - - - <_SkipUpgradeNetAnalyzersNuGetWarning>true - - - - true - - - - false - True - .\\..\\..\\keyfile.snk - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + - diff --git a/src/FrostFS.SDK.Cryptography/HashStream.cs b/src/FrostFS.SDK.Cryptography/HashStream.cs deleted file mode 100644 index 2893f26..0000000 --- a/src/FrostFS.SDK.Cryptography/HashStream.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.IO; -using System.Security.Cryptography; - -namespace FrostFS.SDK.Cryptography; - -public sealed class HashStream(HashAlgorithm algorithm) : Stream -{ - private long position; - - private readonly HashAlgorithm _hash = algorithm; - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length => long.MaxValue; - - public override long Position - { - get { return position; } - set { position = value; } - } - - public override void Flush() - { } - - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return 0; - } - - public override void SetLength(long value) - { } - - public override void Write(byte[] buffer, int offset, int count) - { - _hash.TransformBlock(buffer, offset, count, buffer, offset); - } - - public byte[] Hash() - { - _hash.TransformFinalBlock([], 0, 0); - return _hash.Hash; - } - - protected override void Dispose(bool disposing) - { - _hash?.Dispose(); - base.Dispose(disposing); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/Helper.cs b/src/FrostFS.SDK.Cryptography/Helper.cs new file mode 100644 index 0000000..93058d9 --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Helper.cs @@ -0,0 +1,54 @@ +using Google.Protobuf; +using Org.BouncyCastle.Crypto.Digests; +using System; +using System.Buffers.Binary; +using System.Security.Cryptography; + +namespace FrostFS.SDK.Cryptography; + +public static class Helper +{ + internal static byte[] RIPEMD160(this byte[] value) + { + var hash = new byte[20]; + var digest = new RipeMD160Digest(); + digest.BlockUpdate(value, 0, value.Length); + digest.DoFinal(hash, 0); + return hash; + } + + public static byte[] Sha256(this byte[] value) + { + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(value); + } + + internal static byte[] Sha256(this byte[] value, int offset, int count) + { + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(value, offset, count); + } + + internal static byte[] Sha256(this ReadOnlySpan value) + { + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(value.ToArray()); + } + + public static ByteString Sha256(this IMessage data) + { + return ByteString.CopyFrom(data.ToByteArray().Sha256()); + } + + public static ByteString Sha256(this ByteString data) + { + return ByteString.CopyFrom(data.ToByteArray().Sha256()); + } + + + public static ulong Murmur64(this byte[] value, uint seed) + { + using var murmur = new Murmur3_128(seed); + return BinaryPrimitives.ReadUInt64LittleEndian(murmur.ComputeHash(value)); + } +} diff --git a/src/FrostFS.SDK.Cryptography/Key.cs b/src/FrostFS.SDK.Cryptography/Key.cs index 9eca104..56f71cb 100644 --- a/src/FrostFS.SDK.Cryptography/Key.cs +++ b/src/FrostFS.SDK.Cryptography/Key.cs @@ -16,13 +16,10 @@ public static class KeyExtension private const int UncompressedPublicKeyLength = 65; private static readonly uint CheckSigDescriptor = - BinaryPrimitives.ReadUInt32LittleEndian(DataHasher.Sha256(Encoding.ASCII.GetBytes("System.Crypto.CheckSig").AsSpan())); + BinaryPrimitives.ReadUInt32LittleEndian(Encoding.ASCII.GetBytes("System.Crypto.CheckSig").Sha256()); public static byte[] Compress(this byte[] publicKey) { - if (publicKey == null) - throw new ArgumentNullException(nameof(publicKey)); - if (publicKey.Length != UncompressedPublicKeyLength) throw new FormatException( $"{nameof(Compress)} argument isn't uncompressed public key. " + @@ -37,9 +34,6 @@ public static class KeyExtension public static byte[] Decompress(this byte[] publicKey) { - if (publicKey == null) - throw new ArgumentNullException(nameof(publicKey)); - if (publicKey.Length != CompressedPublicKeyLength) throw new FormatException( $"{nameof(Decompress)} argument isn't compressed public key. " + @@ -60,77 +54,52 @@ public static class KeyExtension $"expected length={CompressedPublicKeyLength}, actual={publicKey.Length}" ); - var signDescriptor = BitConverter.GetBytes(CheckSigDescriptor); - - var script = new byte[3 + publicKey.Length + signDescriptor.Length]; - - script[0] = 0x0c; - script[1] = CompressedPublicKeyLength; //PUSHDATA1 33 - Buffer.BlockCopy(publicKey, 0, script, 2, publicKey.Length); - script[publicKey.Length + 2] = 0x41; //SYSCALL - Buffer.BlockCopy(signDescriptor, 0, script, publicKey.Length + 3, signDescriptor.Length); + var script = new byte[] { 0x0c, CompressedPublicKeyLength }; //PUSHDATA1 33 + script = ArrayHelper.Concat(script, publicKey); + script = ArrayHelper.Concat(script, new byte[] { 0x41 }); //SYSCALL + script = ArrayHelper.Concat(script, BitConverter.GetBytes(CheckSigDescriptor)); //Neo_Crypto_CheckSig return script; } public static byte[] GetScriptHash(this byte[] publicKey) { - if (publicKey == null) - throw new ArgumentNullException(nameof(publicKey)); - var script = publicKey.CreateSignatureRedeemScript(); - return DataHasher.Sha256(script.AsSpan()).RIPEMD160(); - } - - public static string GetWIFFromPrivateKey(this byte[] privateKey) - { - if (privateKey == null || privateKey.Length != 32) - { - throw new ArgumentNullException(nameof(privateKey)); - } - - Span wifSpan = stackalloc byte[34]; - - wifSpan[0] = 0x80; - wifSpan[33] = 0x01; - - privateKey.AsSpan().CopyTo(wifSpan.Slice(1)); - - var wif = Base58.Base58CheckEncode(wifSpan); - - return wif; + return script.Sha256().RIPEMD160(); } private static string ToAddress(this byte[] scriptHash, byte version) { Span data = stackalloc byte[21]; data[0] = version; - scriptHash.CopyTo(data.Slice(1)); + scriptHash.CopyTo(data[1..]); return Base58.Base58CheckEncode(data); } private static byte[] GetPrivateKeyFromWIF(string wif) { - if (wif == null) - throw new ArgumentNullException(nameof(wif)); - + if (wif == null) + throw new ArgumentNullException(); + var data = wif.Base58CheckDecode(); - + if (data.Length != 34 || data[0] != 0x80 || data[33] != 0x01) throw new FormatException(); - + var privateKey = new byte[32]; Buffer.BlockCopy(data, 1, privateKey, 0, privateKey.Length); Array.Clear(data, 0, data.Length); return privateKey; } + public static string Address(this ECDsa key) + { + return key.PublicKey().PublicKeyToAddress(); + } + public static string PublicKeyToAddress(this byte[] publicKey) { - if (publicKey == null) - throw new ArgumentNullException(nameof(publicKey)); - if (publicKey.Length != CompressedPublicKeyLength) throw new FormatException( nameof(publicKey) + @@ -143,48 +112,37 @@ public static class KeyExtension public static byte[] PublicKey(this ECDsa key) { - if (key == null) - throw new ArgumentNullException(nameof(key)); - var param = key.ExportParameters(false); var pubkey = new byte[33]; var pos = 33 - param.Q.X.Length; param.Q.X.CopyTo(pubkey, pos); - - var y = new byte[33]; - ArrayHelper.GetRevertedArray(param.Q.Y, y); - y[32] = 0; - - pubkey[0] = new BigInteger(y).IsEven ? (byte)0x2 : (byte)0x3; + if (new BigInteger(param.Q.Y.Reverse().Concat(new byte[] { 0x00 }).ToArray()).IsEven) + pubkey[0] = 0x2; + else + pubkey[0] = 0x3; return pubkey; } public static byte[] PrivateKey(this ECDsa key) { - if (key == null) - throw new ArgumentNullException(nameof(key)); - return key.ExportParameters(true).D; } public static ECDsa LoadPrivateKey(this byte[] privateKey) { var secp256R1 = SecNamedCurves.GetByName("secp256r1"); - var publicKey = secp256R1.G.Multiply(new Org.BouncyCastle.Math.BigInteger(1, privateKey)) - .GetEncoded(false) - .Skip(1) - .ToArray(); - + var publicKey = + secp256R1.G.Multiply(new Org.BouncyCastle.Math.BigInteger(1, privateKey)).GetEncoded(false)[1..]; var key = ECDsa.Create(new ECParameters { Curve = ECCurve.NamedCurves.nistP256, D = privateKey, Q = new ECPoint { - X = publicKey.Take(32).ToArray(), - Y = publicKey.Skip(32).ToArray() + X = publicKey[..32], + Y = publicKey[32..] } }); @@ -194,20 +152,20 @@ public static class KeyExtension public static ECDsa LoadWif(this string wif) { var privateKey = GetPrivateKeyFromWIF(wif); - + return LoadPrivateKey(privateKey); } public static ECDsa LoadPublicKey(this byte[] publicKey) { - var publicKeyFull = publicKey.Decompress().Skip(1).ToArray(); + var publicKeyFull = publicKey.Decompress()[1..]; var key = ECDsa.Create(new ECParameters { Curve = ECCurve.NamedCurves.nistP256, Q = new ECPoint { - X = publicKeyFull.Take(32).ToArray(), - Y = publicKeyFull.Skip(32).ToArray() + X = publicKeyFull[..32], + Y = publicKeyFull[32..] } }); diff --git a/src/FrostFS.SDK.Cryptography/Murmur3_128.cs b/src/FrostFS.SDK.Cryptography/Murmur3_128.cs index 3e0ae5f..1a846ab 100644 --- a/src/FrostFS.SDK.Cryptography/Murmur3_128.cs +++ b/src/FrostFS.SDK.Cryptography/Murmur3_128.cs @@ -4,7 +4,7 @@ using System.Security.Cryptography; namespace FrostFS.SDK.Cryptography; -public class Murmur3 : HashAlgorithm +internal class Murmur3_128 : HashAlgorithm { private const ulong c1 = 0x87c37b91114253d5; private const ulong c2 = 0x4cf5ad432745937f; @@ -17,35 +17,18 @@ public class Murmur3 : HashAlgorithm private readonly uint seed; private int length; - public Murmur3(uint seed) + public Murmur3_128(uint seed) { this.seed = seed; Initialize(); } - public ulong GetCheckSum64(byte[] bytes) - { - if (bytes is null) - { - throw new ArgumentNullException(nameof(bytes)); - } - - Initialize(); - HashCore(bytes, 0, bytes.Length); - return HashFinalUlong(); - } - protected override void HashCore(byte[] array, int ibStart, int cbSize) { - if (array is null) - { - throw new ArgumentNullException(nameof(array)); - } - length += cbSize; int remainder = cbSize & 15; int alignedLength = ibStart + (cbSize - remainder); - + for (int i = ibStart; i < alignedLength; i += 16) { ulong k1 = BinaryPrimitives.ReadUInt64LittleEndian(array.AsSpan(i)); @@ -67,7 +50,7 @@ public class Murmur3 : HashAlgorithm h2 += h1; h2 = h2 * m + n2; } - + if (remainder > 0) { ulong k1 = 0, k2 = 0; @@ -109,11 +92,6 @@ public class Murmur3 : HashAlgorithm } protected override byte[] HashFinal() - { - return BitConverter.GetBytes(HashFinalUlong()); - } - - protected ulong HashFinalUlong() { h1 ^= (ulong)length; h2 ^= (ulong)length; @@ -123,8 +101,8 @@ public class Murmur3 : HashAlgorithm h2 = Fimix64(h2); h1 += h2; h2 += h1; - - return h1; + + return BitConverter.GetBytes(h1); } public override void Initialize() @@ -134,7 +112,7 @@ public class Murmur3 : HashAlgorithm h2 = seed; } - private static ulong Fimix64(ulong k) + private ulong Fimix64(ulong k) { k ^= k >> 33; k *= 0xff51afd7ed558ccd; diff --git a/src/FrostFS.SDK.Cryptography/Range.cs b/src/FrostFS.SDK.Cryptography/Range.cs new file mode 100644 index 0000000..8158314 --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Range.cs @@ -0,0 +1,271 @@ +using System.Runtime.CompilerServices; + +namespace System +{ + /// Represent a type can be used to index a collection either from the start or the end. + /// + /// Index is used by the C# compiler to support the new index syntax + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; + /// int lastElement = someArray[^1]; // lastElement = 5 + /// + /// + internal readonly struct Index : IEquatable + { + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new Index(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new Index(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative"); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + { + return ~_value; + } + else + { + return _value; + } + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetOffset(int length) + { + var offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + public override string ToString() + { + if (IsFromEnd) + return "^" + ((uint)Value).ToString(); + + return ((uint)Value).ToString(); + } + } + + /// Represent a range has start and end indexes. + /// + /// Range is used by the C# compiler to support the range syntax. + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; + /// int[] subArray1 = someArray[0..2]; // { 1, 2 } + /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } + /// + /// + internal readonly struct Range : IEquatable + { + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? value) => + value is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return Start.GetHashCode() * 31 + End.GetHashCode(); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start + ".." + End; + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new Range(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new Range(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new Range(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + var startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + var endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } + } +} + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + /// + /// Slices the specified array using the specified range. + /// + public static T[] GetSubArray(T[] array, Range range) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + (int offset, int length) = range.GetOffsetAndLength(array.Length); + + if (default(T) != null || typeof(T[]) == array.GetType()) + { + // We know the type of the array to be exactly T[]. + + if (length == 0) + { + return Array.Empty(); + } + + var dest = new T[length]; + Array.Copy(array, offset, dest, 0, length); + return dest; + } + else + { + // The array is actually a U[] where U:T. + var dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length); + Array.Copy(array, offset, dest, 0, length); + return dest; + } + } + } +} diff --git a/src/FrostFS.SDK.Cryptography/Tz/GF127.cs b/src/FrostFS.SDK.Cryptography/Tz/GF127.cs new file mode 100644 index 0000000..d1abdcf --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Tz/GF127.cs @@ -0,0 +1,253 @@ +using System; +using System.Security.Cryptography; + +namespace FrostFS.SDK.Cryptography.Tz; + +// GF127 represents element of GF(2^127) +public class GF127 : IEquatable +{ + public const int ByteSize = 16; + public const ulong MSB64 = (ulong)1 << 63; // 2^63 + public static readonly GF127 Zero = new(0, 0); + public static readonly GF127 One = new(1, 0); + public static readonly GF127 X127X631 = new(MSB64 + 1, MSB64); // x^127+x^63+1 + + private readonly ulong[] _data; + + public ulong this[int index] + { + get { return _data[index]; } + set { _data[index] = value; } + } + + public GF127(ulong[] value) + { + if (value is null || value.Length != 2) + throw new ArgumentException(nameof(value) + "is invalid"); + _data = value; + } + + // Constructs new element of GF(2^127) as u1*x^64 + u0. + // It is assumed that u1 has zero MSB. + public GF127(ulong u0, ulong u1) : this(new ulong[] { u0, u1 }) + { + } + + public GF127() : this(0, 0) + { + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj is GF127 b) + return Equals(b); + return false; + } + + public override int GetHashCode() + { + return this[0].GetHashCode() + this[1].GetHashCode(); + } + + public bool Equals(GF127 other) + { + if (other is null) + return false; + if (ReferenceEquals(this, other)) + return true; + return this[0] == other[0] && this[1] == other[1]; + } + + // return the index of MSB + private int IndexOfMSB() + { + int i = Helper.GetLeadingZeros(this[1]); + if (i == 64) + i += Helper.GetLeadingZeros(this[0]); + return 127 - i; + } + + // Set index n to 1 + public static GF127 SetN(int n) + { + if (n < 64) + return new GF127((ulong)1 << n, 0); + return new GF127(0, (ulong)1 << (n - 64)); + } + + // Add + public static GF127 operator +(GF127 a, GF127 b) + { + return new GF127(a[0] ^ b[0], a[1] ^ b[1]); + } + + // Bitwise-and + public static GF127 operator &(GF127 a, GF127 b) + { + return new GF127(a[0] & b[0], a[1] & b[1]); + } + + // Multiply + public static GF127 operator *(GF127 a, GF127 b) // 2^63 * 2, 10 + { + GF127 r = new(); + GF127 c = a; + + if (b[1] == 0) + { + for (int i = 0; i < b[0].GetNonZeroLength(); i++) + { + if ((b[0] & ((ulong)1 << i)) != 0) + r += c; + c = Mul10(c); // c = c * 2 + } + } + else + { + for (int i = 0; i < 64; i++) + { + if ((b[0] & ((ulong)1 << i)) != 0) + r += c; + c = Mul10(c); // c = c * 2 + } + + for (int i = 0; i < b[1].GetNonZeroLength(); i++) + { + if ((b[1] & ((ulong)1 << i)) != 0) + r += c; + c = Mul10(c); + } + } + + return r; + } + + // Inverse, returns a^-1 + // Extended Euclidean Algorithm + // https://link.springer.com/content/pdf/10.1007/3-540-44499-8_1.pdf + public static GF127 Inv(GF127 a) + { + GF127 v = X127X631, + u = a, + c = new(1, 0), + d = new(0, 0), + t, + x; + + int du = u.IndexOfMSB(); + int dv = v.IndexOfMSB(); + // degree of polynomial is a position of most significant bit + while (du != 0) + { + if (du < dv) + { + (v, u) = (u, v); + (dv, du) = (du, dv); + (d, c) = (c, d); + } + + x = SetN(du - dv); + t = x * v; + u += t; + // because * performs reduction on t, manually reduce u at first step + if (u.IndexOfMSB() == 127) + u += X127X631; + + t = x * d; + c += t; + + du = u.IndexOfMSB(); + dv = v.IndexOfMSB(); + } + + return c; + } + + // Mul10 returns a*x + public static GF127 Mul10(GF127 a) + { + GF127 b = new(); + var c = (a[0] & MSB64) >> 63; + b[0] = a[0] << 1; + b[1] = (a[1] << 1) ^ c; + if ((b[1] & MSB64) != 0) + { + b[0] ^= X127X631[0]; + b[1] ^= X127X631[1]; + } + + return b; + } + + // Mul11 returns a*(x+1) + public static GF127 Mul11(GF127 a) + { + GF127 b = new(); + var c = (a[0] & MSB64) >> 63; + b[0] = a[0] ^ (a[0] << 1); + b[1] = a[1] ^ (a[1] << 1) ^ c; + if ((b[1] & MSB64) == 0) return b; + b[0] ^= X127X631[0]; + b[1] ^= X127X631[1]; + return b; + } + + // Random returns random element from GF(2^127). + // Is used mostly for testing. + public static GF127 Random() + { + using RandomNumberGenerator rng = RandomNumberGenerator.Create(); + return new GF127(rng.NextUlong(), rng.NextUlong() >> 1); + } + + // FromByteArray does the deserialization stuff + public GF127 FromByteArray(byte[] data) + { + if (data.Length != ByteSize) + throw new ArgumentException( + nameof(data) + $" wrong data lenght, {nameof(GF127)} expect={ByteSize}, actual={data.Length}" + ); + var t0 = new byte[8]; + var t1 = new byte[8]; + Array.Copy(data, 0, t1, 0, 8); + Array.Copy(data, 8, t0, 0, 8); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(t0); + Array.Reverse(t1); + } + + _data[0] = BitConverter.ToUInt64(t0, 0); + _data[1] = BitConverter.ToUInt64(t1, 0); + if ((_data[1] & MSB64) != 0) + throw new ArgumentException(nameof(data) + " invalid data"); + return this; + } + + // ToArray() represents element of GF(2^127) as byte array of length 16. + public byte[] ToByteArray() + { + var buff = new byte[16]; + var b0 = BitConverter.GetBytes(_data[0]); + var b1 = BitConverter.GetBytes(_data[1]); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(b0); + Array.Reverse(b1); + } + + Array.Copy(b1, 0, buff, 0, 8); + Array.Copy(b0, 0, buff, 8, 8); + return buff; + } + + // ToString() returns hex-encoded representation, starting with MSB. + public override string ToString() + { + return BitConverter.ToString(ToByteArray()).Replace("-", ""); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/Tz/Helper.cs b/src/FrostFS.SDK.Cryptography/Tz/Helper.cs new file mode 100644 index 0000000..807870e --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Tz/Helper.cs @@ -0,0 +1,30 @@ +using System; +using System.Security.Cryptography; + +namespace FrostFS.SDK.Cryptography.Tz; + +public static class Helper +{ + public static ulong NextUlong(this RandomNumberGenerator rng) + { + var buff = new byte[8]; + rng.GetBytes(buff); + return BitConverter.ToUInt64(buff, 0); + } + + public static int GetLeadingZeros(ulong value) + { + var i = 64; + while (value != 0) + { + value >>= 1; + i--; + } + return i; + } + + public static int GetNonZeroLength(this ulong value) + { + return 64 - GetLeadingZeros(value); + } +} diff --git a/src/FrostFS.SDK.Cryptography/Tz/SL2.cs b/src/FrostFS.SDK.Cryptography/Tz/SL2.cs new file mode 100644 index 0000000..6802501 --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Tz/SL2.cs @@ -0,0 +1,179 @@ +using System; +using System.Linq; + +namespace FrostFS.SDK.Cryptography.Tz; + +public class SL2 : IEquatable +{ + // 2x2 matrix + private readonly GF127[][] data; + + public static readonly SL2 ID = new( + new GF127(1, 0), new GF127(0, 0), new GF127(0, 0), new GF127(1, 0)); + + public static readonly SL2 A = new( + new GF127(2, 0), new GF127(1, 0), new GF127(1, 0), new GF127(0, 0)); + + public static readonly SL2 B = new( + new GF127(2, 0), new GF127(3, 0), new GF127(1, 0), new GF127(1, 0)); + + // Indexer + public GF127[] this[int i] + { + get { return data[i]; } + set { data[i] = value; } + } + + public SL2(GF127[][] value) + { + if (value is null || value.Length != 2 || !value.All(p => p.Length == 2)) + throw new ArgumentException(nameof(value) + $" invalid {nameof(GF127)} matrics"); + data = value; + } + + public SL2(GF127 g00, GF127 g01, GF127 g10, GF127 g11) + : this([[g00, g01], [g10, g11]]) + { + } + + public SL2() : this(GF127.One, GF127.Zero, GF127.Zero, GF127.One) + { + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj is SL2 b) + return Equals(b); + return false; + } + + public override int GetHashCode() + { + return this[0][0].GetHashCode() + + this[0][1].GetHashCode() + + this[1][0].GetHashCode() + + this[1][1].GetHashCode(); + } + + public bool Equals(SL2 other) + { + if (other is null) + return false; + if (ReferenceEquals(this, other)) + return true; + return this[0][0].Equals(other[0][0]) && + this[0][1].Equals(other[0][1]) && + this[1][0].Equals(other[1][0]) && + this[1][1].Equals(other[1][1]); + } + + // 2X2 matrix multiplication + public static SL2 operator *(SL2 a, SL2 b) + { + return new SL2( + a[0][0] * b[0][0] + a[0][1] * b[1][0], + a[0][0] * b[0][1] + a[0][1] * b[1][1], + a[1][0] * b[0][0] + a[1][1] * b[1][0], + a[1][0] * b[0][1] + a[1][1] * b[1][1]); + } + + // Multiplication using strassen algorithm + public static SL2 MulStrassen(SL2 a, SL2 b) + { + GF127[] t = + [ + (a[0][0] + a[1][1]) * (b[0][0] + b[1][1]), // t[0] == (a11 + a22) * (b11 + b22) + (a[1][0] + a[1][1]) * b[0][0], // t[1] == (a21 + a22) * b11 + (b[0][1] + b[1][1]) * a[0][0], // t[2] == (b12 + b22) * a11 + (b[1][0] + b[0][0]) * a[1][1], // t[3] == (b21 + b11) * a22 + (a[0][0] + a[0][1]) * b[1][1], // t[4] == (a11 + a12) * b22 + (a[1][0] + a[0][0]) * (b[0][0] + b[0][1]), // t[5] == (a21 + a11) * (b11 + b12) + (a[0][1] + a[1][1]) * (b[1][0] + b[1][1]), // t[6] == (a12 + a22) * (b21 + b22) + ]; + + SL2 r = new(); + r[0][1] = t[2] + t[4]; // r12 == a11*b12 + a11*b22 + a11*b22 + a12*b22 == a11*b12 + a12*b22 + r[1][0] = t[1] + t[3]; // r21 == a21*b11 + a22*b11 + a22*b21 + a22*b11 == a21*b11 + a22*b21 + // r11 == (a11*b11 + a22*b11` + a11*b22` + a22*b22`) + (a22*b21` + a22*b11`) + (a11*b22` + a12*b22`) + + // (a12*b21 + a22*b21` + a12*b22` + a22*b22`) == a11*b11 + a12*b21 + r[0][0] = t[0] + t[3] + t[4] + t[6]; + // r22 == (a11*b11` + a22*b11` + a11*b22` + a22*b22) + (a21*b11` + a22*b11`) + (a11*b12` + a11*b22`) + + // (a21*b11` + a11*b11` + a21*b12 + a11*b12`) == a21*b12 + a22*b22 + r[1][1] = t[0] + t[1] + t[2] + t[5]; + + return r; + } + + // Inv() returns inverse of a in SL2(GF(2^127)) + public static SL2 Inv(SL2 a) + { + GF127[] t = new GF127[2]; + t[0] = a[0][0] * a[1][1] + a[0][1] * a[1][0]; + t[1] = GF127.Inv(t[0]); + + SL2 r = new(); + r[1][1] = t[1] * a[0][0]; + r[0][1] = t[1] * a[0][1]; + r[1][0] = t[1] * a[1][0]; + r[0][0] = t[1] * a[1][1]; + + return r; + } + + // MulA() returns this*A, A = {{x, 1}, {1, 0}} + public SL2 MulA() + { + var r = new SL2(); + r[0][0] = GF127.Mul10(this[0][0]) + this[0][1]; // r11 == t11*x + t12 + r[0][1] = this[0][0]; // r12 == t11 + + r[1][0] = GF127.Mul10(this[1][0]) + this[1][1]; // r21 == t21*x + t22 + r[1][1] = this[1][0]; // r22 == t21 + + return r; + } + + // MulB() returns this*B, B = {{x, x+1}, {1, 1}} + public SL2 MulB() + { + var r = new SL2(); + r[0][0] = GF127.Mul10(this[0][0]) + this[0][1]; // r11 == t11*x + t12 + r[0][1] = GF127.Mul10(this[0][0]) + this[0][0] + this[0][1]; // r12 == t11*x + t11 + t12 + + r[1][0] = GF127.Mul10(this[1][0]) + this[1][1]; // r21 == t21*x + t22 + r[1][1] = GF127.Mul10(this[1][0]) + this[1][0] + this[1][1]; // r22 == t21*x + t21 + t22 + + return r; + } + + public SL2 FromByteArray(byte[] data) + { + if (data.Length != 64) + throw new ArgumentException(nameof(SL2) + $" invalid data, exect={64}, ecatual={data.Length}"); + this[0][0] = new GF127().FromByteArray(data[0..16]); + this[0][1] = new GF127().FromByteArray(data[16..32]); + this[1][0] = new GF127().FromByteArray(data[32..48]); + this[1][1] = new GF127().FromByteArray(data[48..64]); + return this; + } + + public byte[] ToByteArray() + { + var buff = new byte[64]; + Array.Copy(this[0][0].ToByteArray(), 0, buff, 0, 16); + Array.Copy(this[0][1].ToByteArray(), 0, buff, 16, 16); + Array.Copy(this[1][0].ToByteArray(), 0, buff, 32, 16); + Array.Copy(this[1][1].ToByteArray(), 0, buff, 48, 16); + return buff; + } + + public override string ToString() + { + return this[0][0].ToString() + this[0][1].ToString() + + this[1][0].ToString() + this[1][1].ToString(); + } +} diff --git a/src/FrostFS.SDK.Cryptography/Tz/TzHash.cs b/src/FrostFS.SDK.Cryptography/Tz/TzHash.cs new file mode 100644 index 0000000..7114bed --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/Tz/TzHash.cs @@ -0,0 +1,140 @@ +//using System; +//using System.Collections.Generic; +//using System.Security; +//using System.Security.Cryptography; + +//namespace FrostFS.SDK.Cryptography.Tz; + +//public class TzHash : HashAlgorithm +//{ +// private const int TzHashLength = 64; +// private GF127[] x; +// public override int HashSize => TzHashLength; + +// public TzHash() +// { +// Initialize(); +// } + +// public override void Initialize() +// { +// x = new GF127[4]; +// Reset(); +// HashValue = null; +// } + +// public void Reset() +// { +// x[0] = new GF127(1, 0); +// x[1] = new GF127(0, 0); +// x[2] = new GF127(0, 0); +// x[3] = new GF127(1, 0); +// } + +// public byte[] ToByteArray() +// { +// var buff = new byte[HashSize]; +// for (int i = 0; i < 4; i++) +// { +// Array.Copy(x[i].ToByteArray(), 0, buff, i * 16, 16); +// } +// return buff; +// } + +// [SecurityCritical] +// protected override void HashCore(byte[] array, int ibStart, int cbSize) +// { +// _ = HashData(array[ibStart..(ibStart + cbSize)]); +// } + +// [SecurityCritical] +// protected override byte[] HashFinal() +// { +// return HashValue = ToByteArray(); +// } + +// [SecurityCritical] +// private int HashData(byte[] data) +// { +// var n = data.Length; +// for (int i = 0; i < n; i++) +// { +// for (int j = 7; j >= 0; j--) +// { +// MulBitRight(ref x[0], ref x[1], ref x[2], ref x[3], (data[i] & (1 << j)) != 0); +// } +// } +// return n; +// } + +// // MulBitRight() multiply A (if the bit is 0) or B (if the bit is 1) on the right side +// private void MulBitRight(ref GF127 c00, ref GF127 c01, ref GF127 c10, ref GF127 c11, bool bit) +// { +// // plan 1 +// GF127 t; +// if (bit) +// { // MulB +// t = c00; +// c00 = GF127.Mul10(c00) + c01; // c00 = c00 * x + c01 +// c01 = GF127.Mul11(t) + c01; // c01 = c00 * (x+1) + c01 + +// t = c10; +// c10 = GF127.Mul10(c10) + c11; // c10 = c10 * x + c11 +// c11 = GF127.Mul11(t) + c11; // c11 = c10 * (x+1) + c11 +// } +// else +// { // MulA +// t = c00; +// c00 = GF127.Mul10(c00) + c01; // c00 = c00 * x + c01 +// c01 = t; // c01 = c00 + +// t = c10; +// c10 = GF127.Mul10(c10) + c11; // c10 = c10 * x + c11 +// c11 = t; // c11 = c10; +// } + +// //// plan 2 +// //var r = new SL2(c00, c01, c10, c11); +// //if (bit) +// // r.MulB(); +// //else +// // r.MulA(); +// } + +// // Concat() performs combining of hashes based on homomorphic characteristic. +// public static byte[] Concat(List hs) +// { +// var r = SL2.ID; +// foreach (var h in hs) +// { +// r *= new SL2().FromByteArray(h); +// } +// return r.ToByteArray(); +// } + +// // Validate() checks if hashes in hs combined are equal to h. +// public static bool Validate(byte[] h, List hs) +// { +// var expected = new SL2().FromByteArray(h); +// var actual = new SL2().FromByteArray(Concat(hs)); +// return expected.Equals(actual); +// } + +// // SubtractR() returns hash a, such that Concat(a, b) == c +// public static byte[] SubstractR(byte[] b, byte[] c) +// { +// var t1 = new SL2().FromByteArray(b); +// var t2 = new SL2().FromByteArray(c); +// var r = t2 * SL2.Inv(t1); +// return r.ToByteArray(); +// } + +// // SubtractL() returns hash b, such that Concat(a, b) == c +// public static byte[] SubstractL(byte[] a, byte[] c) +// { +// var t1 = new SL2().FromByteArray(a); +// var t2 = new SL2().FromByteArray(c); +// var r = SL2.Inv(t1) * t2; +// return r.ToByteArray(); +// } +//} diff --git a/src/FrostFS.SDK.Cryptography/UUID.cs b/src/FrostFS.SDK.Cryptography/UUID.cs new file mode 100644 index 0000000..656acaf --- /dev/null +++ b/src/FrostFS.SDK.Cryptography/UUID.cs @@ -0,0 +1,24 @@ +using Google.Protobuf; +using System; + +namespace FrostFS.SDK.Cryptography; + +public static class UUIDExtension +{ + public static Guid ToUuid(this ByteString id) + { + return Guid.Parse(BitConverter.ToString(id.ToByteArray()).Replace("-", "")); + } + + public static byte[] ToBytes(this Guid id) + { + var str = id.ToString("N"); + var len = str.Length; + var bytes = new byte[len/2]; + + for (int i = 0; i < len; i += 2) + bytes[i/2] = Convert.ToByte(str.Substring(i, 2), 16); + + return bytes; + } +} diff --git a/src/FrostFS.SDK.Cryptography/WalletExtractor.cs b/src/FrostFS.SDK.Cryptography/WalletExtractor.cs deleted file mode 100644 index 3e940f5..0000000 --- a/src/FrostFS.SDK.Cryptography/WalletExtractor.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Text; -using System.Linq; -using System.Security.Cryptography; -using Org.BouncyCastle.Crypto.Generators; - -namespace FrostFS.SDK.Cryptography; - -public static class CryptoWallet -{ - private static int N = 16384; - private static int R = 8; - private static int P = 8; - - private enum CipherAction - { - Encrypt, - Decrypt - } - - public static byte[] GetKeyFromEncodedWif(byte[] password, string encryptedWIF) - { - var nep2Data = Base58.Base58CheckDecode(encryptedWIF); - - if (nep2Data.Length == 39 && nep2Data[0] == 1 && nep2Data[1] == 66 && nep2Data[2] == 224) - { - var addressHash = nep2Data.AsSpan(3, 4).ToArray(); - var derivedKey = SCrypt.Generate(password, addressHash, N, R, P, 64); - var derivedKeyHalf1 = derivedKey.Take(32).ToArray(); - var derivedKeyHalf2 = derivedKey.Skip(32).ToArray(); - var encrypted = nep2Data.AsSpan(7, 32).ToArray(); - var decrypted = Aes(encrypted, derivedKeyHalf2, CipherAction.Decrypt); - var plainPrivateKey = XorRange(decrypted, derivedKeyHalf1, 0, decrypted.Length); - - return plainPrivateKey; - } - else - { - throw new ArgumentException("Not valid NEP2 prefix."); - } - } - - public static string EncryptWif(string plainText, ECDsa key) - { - var addressHash = GetAddressHash(key); - var derivedKey = SCrypt.Generate(Encoding.UTF8.GetBytes(plainText), addressHash, N, R, P, 64); - var derivedHalf1 = derivedKey.Take(32).ToArray(); - var derivedHalf2 = derivedKey.Skip(32).ToArray(); - var encryptedHalf1 = Aes(XorRange(key.PrivateKey(), derivedHalf1, 0, 16), derivedHalf2, CipherAction.Encrypt); - var encryptedHalf2 = Aes(XorRange(key.PrivateKey(), derivedHalf1, 16, 32), derivedHalf2, CipherAction.Encrypt); - var prefixes = new byte[] { 1, 66, 224 }; - var concatenation = ArrayHelper.Concat([prefixes, addressHash, encryptedHalf1, encryptedHalf2]); - - return Base58.Base58CheckEncode(concatenation); - } - - public static byte[] GetAddressHash(ECDsa key) - { - string address = key.PublicKey().PublicKeyToAddress(); - - using SHA256 sha256 = SHA256.Create(); - byte[] addressHashed = sha256.ComputeHash(Encoding.UTF8.GetBytes(address)); - return [.. addressHashed.Take(4)]; - } - - private static byte[] XorRange(byte[] arr1, byte[] arr2, int from, int length) - { - byte[] result = new byte[length]; - - var j = 0; - for (var i = from; i < length; ++i) - { - result[j++] = (byte)(arr1[i] ^ arr2[i]); - } - - return result; - } - - private static byte[] Aes(byte[] data, byte[] key, CipherAction action) - { - using var aes = System.Security.Cryptography.Aes.Create(); - aes.Key = key; - aes.Mode = CipherMode.ECB; - aes.Padding = PaddingMode.None; - - if (action == CipherAction.Encrypt) - { - return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length); - } - - if (action == CipherAction.Decrypt) - { - return aes.CreateDecryptor().TransformFinalBlock(data, 0, data.Length); - } - - throw new ArgumentException("Wrong cippher action", nameof(action)); - } -} diff --git a/src/FrostFS.SDK.ModelsV2/Constants.cs b/src/FrostFS.SDK.ModelsV2/Constants.cs new file mode 100644 index 0000000..eeb2b67 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Constants.cs @@ -0,0 +1,7 @@ +namespace FrostFS.SDK.ModelsV2; + +public class Constants +{ + public const int ObjectChunkSize = 3 * (1 << 20); + public const int Sha256HashLength = 32; +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Container.cs b/src/FrostFS.SDK.ModelsV2/Container.cs new file mode 100644 index 0000000..d32ff8f --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Container.cs @@ -0,0 +1,21 @@ +using System; + +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; + +namespace FrostFS.SDK.ModelsV2; + +public class Container +{ + public Guid Nonce { get; set; } + public BasicAcl BasicAcl { get; set; } + public PlacementPolicy PlacementPolicy { get; set; } + public Version Version { get; set; } + + public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) + { + Nonce = Guid.NewGuid(); + BasicAcl = basicAcl; + PlacementPolicy = placementPolicy; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/ContainerId.cs b/src/FrostFS.SDK.ModelsV2/ContainerId.cs new file mode 100644 index 0000000..8bfbc54 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/ContainerId.cs @@ -0,0 +1,33 @@ +using System; + +using FrostFS.SDK.Cryptography; + +namespace FrostFS.SDK.ModelsV2; + +public class ContainerId +{ + public string Value { get; set; } + + public ContainerId(string id) + { + Value = id; + } + + public static ContainerId FromHash(byte[] hash) + { + if (hash.Length != Constants.Sha256HashLength) + throw new FormatException("ContainerID must be a sha256 hash."); + + return new ContainerId(Base58.Encode(hash)); + } + + public byte[] ToHash() + { + return Base58.Decode(Value); + } + + public override string ToString() + { + return Value; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs new file mode 100644 index 0000000..5c222fc --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace FrostFS.SDK.ModelsV2.Enums; + +public enum BasicAcl +{ + [Description("Basic ACL for private container")] + Private = 0x1C8C8CCC, + + [Description("Basic ACL for public RO container")] + PublicRO = 0x1FBF8CFF, + + [Description("Basic ACL for public RW container")] + PublicRW = 0x1FBFBFFF, + + [Description("Basic ACL for public append container")] + PublicAppend = 0x1FBF9FFF, +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Enums/NodeState.cs b/src/FrostFS.SDK.ModelsV2/Enums/NodeState.cs similarity index 72% rename from src/FrostFS.SDK.Client/Models/Enums/NodeState.cs rename to src/FrostFS.SDK.ModelsV2/Enums/NodeState.cs index 2821e55..2293c0f 100644 --- a/src/FrostFS.SDK.Client/Models/Enums/NodeState.cs +++ b/src/FrostFS.SDK.ModelsV2/Enums/NodeState.cs @@ -1,4 +1,4 @@ -namespace FrostFS.SDK; +namespace FrostFS.SDK.ModelsV2.Enums; public enum NodeState { diff --git a/src/FrostFS.SDK.Client/Models/Enums/FrostFsMatchType.cs b/src/FrostFS.SDK.ModelsV2/Enums/ObjectMatchType.cs similarity index 59% rename from src/FrostFS.SDK.Client/Models/Enums/FrostFsMatchType.cs rename to src/FrostFS.SDK.ModelsV2/Enums/ObjectMatchType.cs index b9b3659..4d6026c 100644 --- a/src/FrostFS.SDK.Client/Models/Enums/FrostFsMatchType.cs +++ b/src/FrostFS.SDK.ModelsV2/Enums/ObjectMatchType.cs @@ -1,6 +1,6 @@ -namespace FrostFS.SDK; +namespace FrostFS.SDK.ModelsV2.Enums; -public enum FrostFsMatchType +public enum ObjectMatchType { Unspecified = 0, Equals = 1, diff --git a/src/FrostFS.SDK.ModelsV2/Enums/ObjectType.cs b/src/FrostFS.SDK.ModelsV2/Enums/ObjectType.cs new file mode 100644 index 0000000..6f8a905 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Enums/ObjectType.cs @@ -0,0 +1,8 @@ +namespace FrostFS.SDK.ModelsV2.Enums; + +public enum ObjectType +{ + Regular = 0, + Tombstone = 1, + Lock = 3 +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Enums/FrostFsStatusCode.cs b/src/FrostFS.SDK.ModelsV2/Enums/StatusCode.cs similarity index 88% rename from src/FrostFS.SDK.Client/Models/Enums/FrostFsStatusCode.cs rename to src/FrostFS.SDK.ModelsV2/Enums/StatusCode.cs index 7eb185c..df0fa57 100644 --- a/src/FrostFS.SDK.Client/Models/Enums/FrostFsStatusCode.cs +++ b/src/FrostFS.SDK.ModelsV2/Enums/StatusCode.cs @@ -1,6 +1,6 @@ -namespace FrostFS.SDK; +namespace FrostFS.SDK.ModelsV2.Enums; -public enum FrostFsStatusCode +public enum StatusCode { Success = 0, Internal = 1024, diff --git a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj new file mode 100644 index 0000000..96a477c --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + 12.0 + enable + + + + + + + diff --git a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs new file mode 100644 index 0000000..68abbb3 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs @@ -0,0 +1,27 @@ +namespace FrostFS.SDK.ModelsV2; + +public class MetaHeader +{ + public Version Version { get; set; } + public int Epoch { get; set; } + public int Ttl { get; set; } + + public MetaHeader(Version version, int epoch, int ttl) + { + Version = version; + Epoch = epoch; + Ttl = ttl; + } + + public static MetaHeader Default() + { + return new MetaHeader( + new Version( + major: 2, + minor: 13 + ), + epoch: 0, + ttl: 2 + ); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs new file mode 100644 index 0000000..7e86912 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs @@ -0,0 +1,9 @@ +using FrostFS.SDK.ModelsV2.Enums; + +namespace FrostFS.SDK.ModelsV2.Netmap; + +public class NodeInfo +{ + public NodeState State { get; set; } + public Version Version { get; set; } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs new file mode 100644 index 0000000..b638bb6 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs @@ -0,0 +1,13 @@ +namespace FrostFS.SDK.ModelsV2.Netmap; + +public class PlacementPolicy +{ + public Replica[] Replicas { get; private set; } + public bool Unique { get; private set; } + + public PlacementPolicy(bool unique, params Replica[] replicas) + { + Replicas = replicas; + Unique = unique; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/Replica.cs b/src/FrostFS.SDK.ModelsV2/Netmap/Replica.cs new file mode 100644 index 0000000..a6aeac9 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Netmap/Replica.cs @@ -0,0 +1,15 @@ +namespace FrostFS.SDK.ModelsV2.Netmap; + +public class Replica +{ + public int Count { get; set; } + public string Selector { get; set; } + + public Replica(int count, string? selector = null) + { + selector ??= string.Empty; + + Count = count; + Selector = selector; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Object.cs b/src/FrostFS.SDK.ModelsV2/Object.cs new file mode 100644 index 0000000..7db6289 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Object.cs @@ -0,0 +1,77 @@ +using FrostFS.SDK.ModelsV2.Enums; + +namespace FrostFS.SDK.ModelsV2; + +public class ObjectAttribute +{ + public string Key { get; set; } + public string Value { get; set; } + + public ObjectAttribute(string key, string value) + { + Key = key; + Value = value; + } +} + +public class ObjectFilter +{ + private const string HeaderPrefix = "$Object:"; + public ObjectMatchType MatchType { get; set; } + public string Key { get; set; } + public string Value { get; set; } + + public ObjectFilter(ObjectMatchType matchType, string key, string value) + { + MatchType = matchType; + Key = key; + Value = value; + } + + public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId) + { + return new ObjectFilter(matchType, HeaderPrefix + "objectID", objectId.Value); + } + + public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId) + { + return new ObjectFilter(matchType, HeaderPrefix + "ownerID", ownerId.Value); + } + + public static ObjectFilter RootFilter() + { + return new ObjectFilter(ObjectMatchType.Unspecified, HeaderPrefix + "ROOT", ""); + } + + public static ObjectFilter VersionFilter(ObjectMatchType matchType, Version version) + { + return new ObjectFilter(matchType, HeaderPrefix + "version", version.ToString()); + } +} + +public class ObjectHeader +{ + public ObjectAttribute[] Attributes { get; set; } + public ContainerId ContainerId { get; set; } + public long Size { get; set; } + public ObjectType ObjectType { get; set; } + public Version Version { get; set; } + + public ObjectHeader( + ContainerId containerId, + ObjectType type = ObjectType.Regular, + params ObjectAttribute[] attributes + ) + { + Attributes = attributes; + ContainerId = containerId; + ObjectType = type; + } +} + +public class Object +{ + public ObjectHeader Header { get; set; } + public ObjectId ObjectId { get; set; } + public byte[] Payload { get; set; } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs b/src/FrostFS.SDK.ModelsV2/ObjectId.cs similarity index 56% rename from src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs rename to src/FrostFS.SDK.ModelsV2/ObjectId.cs index 57300a3..2bcf3ab 100644 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs +++ b/src/FrostFS.SDK.ModelsV2/ObjectId.cs @@ -2,18 +2,24 @@ using FrostFS.SDK.Cryptography; -namespace FrostFS.SDK; +namespace FrostFS.SDK.ModelsV2; -public class FrostFsObjectId(string id) +public class ObjectId { - public string Value { get; } = id; + public string Value { get; } - public static FrostFsObjectId FromHash(ReadOnlySpan hash) + public ObjectId(string id) + { + Value = id; + } + + public static ObjectId FromHash(byte[] hash) { if (hash.Length != Constants.Sha256HashLength) + { throw new FormatException("ObjectID must be a sha256 hash."); - - return new FrostFsObjectId(Base58.Encode(hash)); + } + return new ObjectId(Base58.Encode(hash)); } public byte[] ToHash() diff --git a/src/FrostFS.SDK.ModelsV2/OwnerId.cs b/src/FrostFS.SDK.ModelsV2/OwnerId.cs new file mode 100644 index 0000000..85cf239 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/OwnerId.cs @@ -0,0 +1,25 @@ +using System.Security.Cryptography; + +using FrostFS.SDK.Cryptography; + +namespace FrostFS.SDK.ModelsV2; + +public class OwnerId +{ + public string Value { get; } + + public OwnerId(string id) + { + Value = id; + } + + public static OwnerId FromKey(ECDsa key) + { + return new OwnerId(key.PublicKey().PublicKeyToAddress()); + } + + public byte[] ToHash() + { + return Base58.Decode(Value); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Status.cs b/src/FrostFS.SDK.ModelsV2/Status.cs new file mode 100644 index 0000000..2d56441 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Status.cs @@ -0,0 +1,25 @@ +using FrostFS.SDK.ModelsV2.Enums; + +namespace FrostFS.SDK.ModelsV2; + +public class Status +{ + public StatusCode Code { get; set; } + public string Message { get; set; } + + public Status(StatusCode code, string? message = null) + { + Code = code; + Message = message ?? string.Empty; + } + + public bool IsSuccess() + { + return Code == StatusCode.Success; + } + + public override string ToString() + { + return $"Response status: {Code}. Message: {Message}."; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Version.cs b/src/FrostFS.SDK.ModelsV2/Version.cs new file mode 100644 index 0000000..3d852fb --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Version.cs @@ -0,0 +1,23 @@ +namespace FrostFS.SDK.ModelsV2; + +public class Version +{ + public int Major { get; set; } + public int Minor { get; set; } + + public Version(int major, int minor) + { + Major = major; + Minor = minor; + } + + public bool IsSupported(Version version) + { + return Major == version.Major; + } + + public override string ToString() + { + return $"v{Major}.{Minor}"; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/AssemblyInfo.cs b/src/FrostFS.SDK.Protos/AssemblyInfo.cs deleted file mode 100644 index d7fab05..0000000 --- a/src/FrostFS.SDK.Protos/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyCompany("TrueCloudLab")] -[assembly: AssemblyFileVersion("1.0.7.0")] -[assembly: AssemblyProduct("FrostFS.SDK.Protos")] -[assembly: AssemblyTitle("FrostFS.SDK.Protos")] -[assembly: AssemblyVersion("1.0.7.0")] diff --git a/src/FrostFS.SDK.Protos/Interfaces/IRequest.cs b/src/FrostFS.SDK.Protos/Interfaces/IRequest.cs deleted file mode 100644 index a3a269f..0000000 --- a/src/FrostFS.SDK.Protos/Interfaces/IRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FrostFS.Session; - -namespace FrostFS.SDK.Proto.Interfaces; - -public interface IRequest : IVerifiableMessage -{ - RequestMetaHeader MetaHeader { get; set; } - RequestVerificationHeader VerifyHeader { get; set; } -} diff --git a/src/FrostFS.SDK.Protos/accounting/Extension.Message.cs b/src/FrostFS.SDK.Protos/accounting/Extension.Message.cs deleted file mode 100644 index 82b7c78..0000000 --- a/src/FrostFS.SDK.Protos/accounting/Extension.Message.cs +++ /dev/null @@ -1,62 +0,0 @@ -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.Accounting; - -public partial class BalanceRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class BalanceResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/apemanager/Extension.Message.cs b/src/FrostFS.SDK.Protos/apemanager/Extension.Message.cs deleted file mode 100644 index 53b7fdb..0000000 --- a/src/FrostFS.SDK.Protos/apemanager/Extension.Message.cs +++ /dev/null @@ -1,174 +0,0 @@ -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace Frostfs.V2.Apemanager; - -public partial class AddChainRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class AddChainResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class RemoveChainRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class RemoveChainResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class ListChainsRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class ListChainsResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} diff --git a/src/FrostFS.SDK.Protos/container/Extension.Message.cs b/src/FrostFS.SDK.Protos/container/Extension.Message.cs deleted file mode 100644 index 7cb69a8..0000000 --- a/src/FrostFS.SDK.Protos/container/Extension.Message.cs +++ /dev/null @@ -1,230 +0,0 @@ -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.Container; - -public partial class GetRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class GetResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class PutRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class PutResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class DeleteRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class DeleteResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class ListRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class ListResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} diff --git a/src/FrostFS.SDK.Protos/netmap/Extension.Message.cs b/src/FrostFS.SDK.Protos/netmap/Extension.Message.cs deleted file mode 100644 index 8579baf..0000000 --- a/src/FrostFS.SDK.Protos/netmap/Extension.Message.cs +++ /dev/null @@ -1,175 +0,0 @@ -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.Netmap; - -public partial class LocalNodeInfoRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class LocalNodeInfoResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class NetworkInfoRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class NetworkInfoResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - - -public partial class NetmapSnapshotRequest : IRequest -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} - -public partial class NetmapSnapshotResponse : IResponse -{ - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/object/Extension.Message.cs b/src/FrostFS.SDK.Protos/object/Extension.Message.cs deleted file mode 100644 index f9348ff..0000000 --- a/src/FrostFS.SDK.Protos/object/Extension.Message.cs +++ /dev/null @@ -1,517 +0,0 @@ -using System.Diagnostics; - -using FrostFS.SDK.Proto.Interfaces; -using FrostFS.Session; - -using Google.Protobuf; - -namespace FrostFS.Object -{ - public partial class GetRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class GetResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PutRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PutResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PutSingleRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PutSingleResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class DeleteRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class DeleteResponse : IResponse - { - [DebuggerStepThrough] - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - [DebuggerStepThrough] - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - [DebuggerStepThrough] - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - [DebuggerStepThrough] - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - [DebuggerStepThrough] - public IMessage GetBody() - { - return Body; - } - } - - public partial class HeadRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class HeadResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - public partial class SearchRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class SearchResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class GetRangeRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class GetRangeResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class GetRangeHashRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class GetRangeHashResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PatchRequest : IRequest - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (RequestMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (RequestVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } - - public partial class PatchResponse : IResponse - { - IMetaHeader IVerifiableMessage.GetMetaHeader() - { - return MetaHeader; - } - - IVerificationHeader IVerifiableMessage.GetVerificationHeader() - { - return VerifyHeader; - } - - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) - { - MetaHeader = (ResponseMetaHeader)metaHeader; - } - - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) - { - VerifyHeader = (ResponseVerificationHeader)verificationHeader; - } - - public IMessage GetBody() - { - return Body; - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj similarity index 53% rename from src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj rename to src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj index 222acd1..746a8b4 100644 --- a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj +++ b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj @@ -4,45 +4,21 @@ netstandard2.0 12.0 enable - FrostFS.SDK.Protos - 1.0.7 - - Protobuf client for C# SDK - - true - - - - true - - - - <_SkipUpgradeNetAnalyzersNuGetWarning>true - - - - true - - - - false - True - .\\..\\..\\keyfile.snk - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + - diff --git a/src/FrostFS.SDK.Protos/Interfaces/IMetaHeader.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IMetaHeader.cs similarity index 100% rename from src/FrostFS.SDK.Protos/Interfaces/IMetaHeader.cs rename to src/FrostFS.SDK.ProtosV2/Interfaces/IMetaHeader.cs diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs new file mode 100644 index 0000000..f75db43 --- /dev/null +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs @@ -0,0 +1,7 @@ +namespace FrostFS.Session; + +public interface IRequest : IVerificableMessage +{ + RequestMetaHeader MetaHeader { get; set; } + RequestVerificationHeader VerifyHeader { get; set; } +} diff --git a/src/FrostFS.SDK.Protos/Interfaces/IResponse.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs similarity index 73% rename from src/FrostFS.SDK.Protos/Interfaces/IResponse.cs rename to src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs index ac6382a..609be8e 100644 --- a/src/FrostFS.SDK.Protos/Interfaces/IResponse.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs @@ -1,6 +1,6 @@ namespace FrostFS.Session; -public interface IResponse : IVerifiableMessage +public interface IResponse : IVerificableMessage { ResponseMetaHeader MetaHeader { get; set; } ResponseVerificationHeader VerifyHeader { get; set; } diff --git a/src/FrostFS.SDK.Protos/Interfaces/IVerifiableMessage.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs similarity index 85% rename from src/FrostFS.SDK.Protos/Interfaces/IVerifiableMessage.cs rename to src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs index 57fce64..3903757 100644 --- a/src/FrostFS.SDK.Protos/Interfaces/IVerifiableMessage.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs @@ -2,7 +2,7 @@ using Google.Protobuf; namespace FrostFS.Session; -public interface IVerifiableMessage : IMessage +public interface IVerificableMessage : IMessage { IMetaHeader GetMetaHeader(); void SetMetaHeader(IMetaHeader metaHeader); diff --git a/src/FrostFS.SDK.Protos/Interfaces/IVerificationHeader.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs similarity index 99% rename from src/FrostFS.SDK.Protos/Interfaces/IVerificationHeader.cs rename to src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs index 32ba5d4..6a637c3 100644 --- a/src/FrostFS.SDK.Protos/Interfaces/IVerificationHeader.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerificationHeader.cs @@ -1,5 +1,4 @@ using FrostFS.Refs; - using Google.Protobuf; namespace FrostFS.Session; diff --git a/src/FrostFS.SDK.Protos/accounting/service.proto b/src/FrostFS.SDK.ProtosV2/accounting/service.proto similarity index 79% rename from src/FrostFS.SDK.Protos/accounting/service.proto rename to src/FrostFS.SDK.ProtosV2/accounting/service.proto index 6049e0f..715ef63 100644 --- a/src/FrostFS.SDK.Protos/accounting/service.proto +++ b/src/FrostFS.SDK.ProtosV2/accounting/service.proto @@ -9,13 +9,13 @@ import "accounting/types.proto"; import "refs/types.proto"; import "session/types.proto"; -// Accounting service provides methods for interaction with FrostFS sidechain -// via other FrostFS nodes to get information about the account balance. Deposit -// and Withdraw operations can't be implemented here, as they require Mainnet -// FrostFS smart contract invocation. Transfer operations between internal -// FrostFS accounts are possible if both use the same token type. +// Accounting service provides methods for interaction with NeoFS sidechain via +// other NeoFS nodes to get information about the account balance. Deposit and +// Withdraw operations can't be implemented here, as they require Mainnet NeoFS +// smart contract invocation. Transfer operations between internal NeoFS +// accounts are possible if both use the same token type. service AccountingService { - // Returns the amount of funds in GAS token for the requested FrostFS account. + // Returns the amount of funds in GAS token for the requested NeoFS account. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -27,9 +27,9 @@ service AccountingService { // BalanceRequest message message BalanceRequest { // To indicate the account for which the balance is requested, its identifier - // is used. It can be any existing account in FrostFS sidechain `Balance` - // smart contract. If omitted, client implementation MUST set it to the - // request's signer `OwnerID`. + // is used. It can be any existing account in NeoFS sidechain `Balance` smart + // contract. If omitted, client implementation MUST set it to the request's + // signer `OwnerID`. message Body { // Valid user identifier in `OwnerID` format for which the balance is // requested. Required field. diff --git a/src/FrostFS.SDK.Protos/accounting/types.proto b/src/FrostFS.SDK.ProtosV2/accounting/types.proto similarity index 100% rename from src/FrostFS.SDK.Protos/accounting/types.proto rename to src/FrostFS.SDK.ProtosV2/accounting/types.proto diff --git a/src/FrostFS.SDK.Protos/acl/types.proto b/src/FrostFS.SDK.ProtosV2/acl/types.proto similarity index 86% rename from src/FrostFS.SDK.Protos/acl/types.proto rename to src/FrostFS.SDK.ProtosV2/acl/types.proto index a1d9ae2..186f08f 100644 --- a/src/FrostFS.SDK.Protos/acl/types.proto +++ b/src/FrostFS.SDK.ProtosV2/acl/types.proto @@ -6,7 +6,6 @@ option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl/grpc;ac option csharp_namespace = "FrostFS.Acl"; import "refs/types.proto"; -import "ape/types.proto"; // Target role of the access control rule in access control list. enum Role { @@ -89,14 +88,14 @@ enum HeaderType { // Filter object headers OBJECT = 2; - // Filter service headers. These are not processed by FrostFS nodes and + // Filter service headers. These are not processed by NeoFS nodes and // exist for service use only. SERVICE = 3; } // Describes a single eACL rule. message EACLRecord { - // FrostFS request Verb to match + // NeoFS request Verb to match Operation operation = 1 [ json_name = "operation" ]; // Rule execution result. Either allows or denies access if filters match. @@ -165,7 +164,7 @@ message EACLRecord { // Extended ACL rules table. A list of ACL rules defined additionally to Basic // ACL. Extended ACL rules can be attached to a container and can be updated // or may be defined in `BearerToken` structure. Please see the corresponding -// FrostFS Technical Specification section for detailed description. +// NeoFS Technical Specification section for detailed description. message EACLTable { // eACL format version. Effectively, the version of API library used to create // eACL Table. @@ -195,9 +194,6 @@ message BearerToken { // container. If it contains `container_id` field, bearer token is only // valid for this specific container. Otherwise, any container of the same // owner is allowed. - // - // Deprecated: eACL tables are no longer relevant - `APEOverrides` should be - // used instead. EACLTable eacl_table = 1 [ json_name = "eaclTable" ]; // `OwnerID` defines to whom the token was issued. It must match the request @@ -222,24 +218,6 @@ message BearerToken { // AllowImpersonate flag to consider token signer as request owner. // If this field is true extended ACL table in token body isn't processed. bool allow_impersonate = 4 [ json_name = "allowImpersonate" ]; - - // APEOverride is the list of APE chains defined for a target. - // These chains are meant to serve as overrides to the already defined (or - // even undefined) APE chains for the target (see contract `Policy`). - // - // The server-side processing of the bearer token with set APE overrides - // must verify if a client is permitted to override chains for the target, - // preventing unauthorized access through the APE mechanism. - message APEOverride { - // Target for which chains are applied. - frostfs.v2.ape.ChainTarget target = 1 [ json_name = "target" ]; - - // The list of APE chains. - repeated frostfs.v2.ape.Chain chains = 2 [ json_name = "chains" ]; - } - - // APE override for the target. - APEOverride ape_override = 5 [ json_name = "apeOverride" ]; } // Bearer Token body Body body = 1 [ json_name = "body" ]; diff --git a/src/FrostFS.SDK.Protos/apemanager/service.proto b/src/FrostFS.SDK.ProtosV2/apemanager/service.proto similarity index 95% rename from src/FrostFS.SDK.Protos/apemanager/service.proto rename to src/FrostFS.SDK.ProtosV2/apemanager/service.proto index 166ba4d..6b9da60 100644 --- a/src/FrostFS.SDK.Protos/apemanager/service.proto +++ b/src/FrostFS.SDK.ProtosV2/apemanager/service.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package frostfs.v2.apemanager; -import "ape/types.proto"; +import "apemanager/types.proto"; import "session/types.proto"; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; @@ -52,10 +52,10 @@ service APEManagerService { message AddChainRequest { message Body { // A target for which a rule chain is added. - frostfs.v2.ape.ChainTarget target = 1; + ChainTarget target = 1; // The chain to set for the target. - frostfs.v2.ape.Chain chain = 2; + Chain chain = 2; } // The request's body. @@ -95,7 +95,7 @@ message AddChainResponse { message RemoveChainRequest { message Body { // Target for which a rule chain is removed. - frostfs.v2.ape.ChainTarget target = 1; + ChainTarget target = 1; // Chain ID assigned for the rule chain. bytes chain_id = 2; @@ -135,7 +135,7 @@ message RemoveChainResponse { message ListChainsRequest { message Body { // Target for which rule chains are listed. - frostfs.v2.ape.ChainTarget target = 1; + ChainTarget target = 1; } // The request's body. @@ -154,7 +154,7 @@ message ListChainsRequest { message ListChainsResponse { message Body { // The list of chains defined for the reqeusted target. - repeated frostfs.v2.ape.Chain chains = 1; + repeated Chain chains = 1; } // The response's body. @@ -168,4 +168,4 @@ message ListChainsResponse { // authenticate the nodes of the message route and check the correctness of // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/ape/types.proto b/src/FrostFS.SDK.ProtosV2/apemanager/types.proto similarity index 86% rename from src/FrostFS.SDK.Protos/ape/types.proto rename to src/FrostFS.SDK.ProtosV2/apemanager/types.proto index 71468c3..c064627 100644 --- a/src/FrostFS.SDK.Protos/ape/types.proto +++ b/src/FrostFS.SDK.ProtosV2/apemanager/types.proto @@ -1,8 +1,8 @@ -syntax = "proto3"; +syntax = "proto3"; -package frostfs.v2.ape; +package frostfs.v2.apemanager; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/ape/grpc;ape"; +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; // TargetType is a type target to which a rule chain is defined. enum TargetType { diff --git a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs new file mode 100644 index 0000000..a46ddc2 --- /dev/null +++ b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs @@ -0,0 +1,397 @@ +using Google.Protobuf; + +using FrostFS.Session; + +namespace FrostFS.Container; + +public partial class AnnounceUsedSpaceRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class AnnounceUsedSpaceResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class GetRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class GetResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class PutRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class PutResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class DeleteRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class DeleteResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class ListRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class ListResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class SetExtendedACLRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class SetExtendedACLResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class GetExtendedACLRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class GetExtendedACLResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} diff --git a/src/FrostFS.SDK.Protos/container/service.proto b/src/FrostFS.SDK.ProtosV2/container/service.proto similarity index 55% rename from src/FrostFS.SDK.Protos/container/service.proto rename to src/FrostFS.SDK.ProtosV2/container/service.proto index abf3e9d..4a5cc02 100644 --- a/src/FrostFS.SDK.Protos/container/service.proto +++ b/src/FrostFS.SDK.ProtosV2/container/service.proto @@ -11,8 +11,8 @@ import "refs/types.proto"; import "session/types.proto"; // `ContainerService` provides API to interact with `Container` smart contract -// in FrostFS sidechain via other FrostFS nodes. All of those actions can be -// done equivalently by directly issuing transactions and RPC calls to sidechain +// in NeoFS sidechain via other NeoFS nodes. All of those actions can be done +// equivalently by directly issuing transactions and RPC calls to sidechain // nodes. service ContainerService { // `Put` invokes `Container` smart contract's `Put` method and returns @@ -25,7 +25,7 @@ service ContainerService { // request to save the container has been sent to the sidechain; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container create access denied. + // container create access denied. rpc Put(PutRequest) returns (PutResponse); // `Delete` invokes `Container` smart contract's `Delete` method and returns @@ -38,7 +38,7 @@ service ContainerService { // request to remove the container has been sent to the sidechain; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container delete access denied. + // container delete access denied. rpc Delete(DeleteRequest) returns (DeleteResponse); // Returns container structure from `Container` smart contract storage. @@ -50,7 +50,7 @@ service ContainerService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // requested container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied. + // access to container is denied. rpc Get(GetRequest) returns (GetResponse); // Returns all owner's containers from 'Container` smart contract' storage. @@ -60,11 +60,47 @@ service ContainerService { // container list has been successfully read; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container list access denied. + // container list access denied. rpc List(ListRequest) returns (ListResponse); + + // Invokes 'SetEACL' method of 'Container` smart contract and returns response + // immediately. After one more block in sidechain, changes in an Extended ACL + // are added into smart contract storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // request to save container eACL has been sent to the sidechain; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // set container eACL access denied. + rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse); + + // Returns Extended ACL table and signature from `Container` smart contract + // storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // container eACL has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // container not found; + // - **EACL_NOT_FOUND** (3073, SECTION_CONTAINER): \ + // eACL table not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container eACL is denied. + rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse); + + // Announces the space values used by the container for P2P synchronization. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // estimation of used space has been successfully announced; + // - Common failures (SECTION_FAILURE_COMMON). + rpc AnnounceUsedSpace(AnnounceUsedSpaceRequest) + returns (AnnounceUsedSpaceResponse); } -// New FrostFS Container creation request +// New NeoFS Container creation request message PutRequest { // Container creation request has container structure's signature as a // separate field. It's not stored in sidechain, just verified on container @@ -72,7 +108,7 @@ message PutRequest { // the stable-marshalled container strucutre, hence there is no need for // additional signature checks. message Body { - // Container structure to register in FrostFS + // Container structure to register in NeoFS container.Container container = 1; // Signature of a stable-marshalled container according to RFC-6979. @@ -91,7 +127,7 @@ message PutRequest { neo.fs.v2.session.RequestVerificationHeader verify_header = 3; } -// New FrostFS Container creation response +// New NeoFS Container creation response message PutResponse { // Container put response body contains information about the newly registered // container as seen by `Container` smart contract. `ContainerID` can be @@ -120,7 +156,7 @@ message DeleteRequest { // the container owner's intent. The signature will be verified by `Container` // smart contract, so signing algorithm must be supported by NeoVM. message Body { - // Identifier of the container to delete from FrostFS + // Identifier of the container to delete from NeoFS neo.fs.v2.refs.ContainerID container_id = 1; // `ContainerID` signed with the container owner's key according to @@ -246,3 +282,150 @@ message ListResponse { // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } + +// Set Extended ACL +message SetExtendedACLRequest { + // Set Extended ACL request body does not have separate `ContainerID` + // reference. It will be taken from `EACLTable.container_id` field. + message Body { + // Extended ACL table to set for the container + neo.fs.v2.acl.EACLTable eacl = 1; + + // Signature of stable-marshalled Extended ACL table according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + } + // Body of set extended acl request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Set Extended ACL +message SetExtendedACLResponse { + // `SetExtendedACLResponse` has an empty body because the operation is + // asynchronous and the update should be reflected in `Container` smart + // contract's storage after next block is issued in sidechain. + message Body {} + + // Body of set extended acl response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get Extended ACL +message GetExtendedACLRequest { + // Get Extended ACL request body + message Body { + // Identifier of the container having Extended ACL + neo.fs.v2.refs.ContainerID container_id = 1; + } + + // Body of get extended acl request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Get Extended ACL +message GetExtendedACLResponse { + // Get Extended ACL Response body can be empty if the requested container does + // not have Extended ACL Table attached or Extended ACL has not been allowed + // at the time of container creation. + message Body { + // Extended ACL requested, if available + neo.fs.v2.acl.EACLTable eacl = 1; + + // Signature of stable-marshalled Extended ACL according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + + // Session token if Extended ACL was set within a session + neo.fs.v2.session.SessionToken session_token = 3; + } + // Body of get extended acl response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Announce container used space +message AnnounceUsedSpaceRequest { + // Container used space announcement body. + message Body { + // Announcement contains used space information for a single container. + message Announcement { + // Epoch number for which the container size estimation was produced. + uint64 epoch = 1; + + // Identifier of the container. + neo.fs.v2.refs.ContainerID container_id = 2; + + // Used space is a sum of object payload sizes of a specified + // container, stored in the node. It must not include inhumed objects. + uint64 used_space = 3; + } + + // List of announcements. If nodes share several containers, + // announcements are transferred in a batch. + repeated Announcement announcements = 1; + } + + // Body of announce used space request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Announce container used space +message AnnounceUsedSpaceResponse { + // `AnnounceUsedSpaceResponse` has an empty body because announcements are + // one way communication. + message Body {} + + // Body of announce used space response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/src/FrostFS.SDK.Protos/container/types.proto b/src/FrostFS.SDK.ProtosV2/container/types.proto similarity index 97% rename from src/FrostFS.SDK.Protos/container/types.proto rename to src/FrostFS.SDK.ProtosV2/container/types.proto index c657d1c..075081a 100644 --- a/src/FrostFS.SDK.Protos/container/types.proto +++ b/src/FrostFS.SDK.ProtosV2/container/types.proto @@ -50,7 +50,7 @@ message Container { // (`__NEOFS__DISABLE_HOMOMORPHIC_HASHING` is deprecated) \ // Disables homomorphic hashing for the container if the value equals "true" // string. Any other values are interpreted as missing attribute. Container - // could be accepted in a FrostFS network only if the global network hashing + // could be accepted in a NeoFS network only if the global network hashing // configuration value corresponds with that attribute's value. After // container inclusion, network setting is ignored. // diff --git a/src/FrostFS.SDK.Protos/lock/types.proto b/src/FrostFS.SDK.ProtosV2/lock/types.proto similarity index 100% rename from src/FrostFS.SDK.Protos/lock/types.proto rename to src/FrostFS.SDK.ProtosV2/lock/types.proto diff --git a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs new file mode 100644 index 0000000..207568d --- /dev/null +++ b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs @@ -0,0 +1,116 @@ +using FrostFS.Session; +using Google.Protobuf; + +namespace FrostFS.Netmap; + +public partial class LocalNodeInfoRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class LocalNodeInfoResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class NetworkInfoRequest : IRequest +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class NetworkInfoResponse : IResponse +{ + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} diff --git a/src/FrostFS.SDK.Protos/netmap/service.proto b/src/FrostFS.SDK.ProtosV2/netmap/service.proto similarity index 96% rename from src/FrostFS.SDK.Protos/netmap/service.proto rename to src/FrostFS.SDK.ProtosV2/netmap/service.proto index 11f8f96..8611d9a 100644 --- a/src/FrostFS.SDK.Protos/netmap/service.proto +++ b/src/FrostFS.SDK.ProtosV2/netmap/service.proto @@ -12,7 +12,7 @@ import "session/types.proto"; // `NetmapService` provides methods to work with `Network Map` and the // information required to build it. The resulting `Network Map` is stored in // sidechain `Netmap` smart contract, while related information can be obtained -// from other FrostFS nodes. +// from other NeoFS nodes. service NetmapService { // Get NodeInfo structure from the particular node directly. // Node information can be taken from `Netmap` smart contract. In some cases, @@ -27,7 +27,7 @@ service NetmapService { // - Common failures (SECTION_FAILURE_COMMON). rpc LocalNodeInfo(LocalNodeInfoRequest) returns (LocalNodeInfoResponse); - // Read recent information about the FrostFS network. + // Read recent information about the NeoFS network. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -35,7 +35,7 @@ service NetmapService { // - Common failures (SECTION_FAILURE_COMMON). rpc NetworkInfo(NetworkInfoRequest) returns (NetworkInfoResponse); - // Returns network map snapshot of the current FrostFS epoch. + // Returns network map snapshot of the current NeoFS epoch. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -65,7 +65,7 @@ message LocalNodeInfoRequest { message LocalNodeInfoResponse { // Local Node Info, including API Version in use. message Body { - // Latest FrostFS API version in use + // Latest NeoFS API version in use neo.fs.v2.refs.Version version = 1; // NodeInfo structure with recent information from node itself diff --git a/src/FrostFS.SDK.Protos/netmap/types.proto b/src/FrostFS.SDK.ProtosV2/netmap/types.proto similarity index 82% rename from src/FrostFS.SDK.Protos/netmap/types.proto rename to src/FrostFS.SDK.ProtosV2/netmap/types.proto index 5f0e93e..baaca04 100644 --- a/src/FrostFS.SDK.Protos/netmap/types.proto +++ b/src/FrostFS.SDK.ProtosV2/netmap/types.proto @@ -36,9 +36,6 @@ enum Operation { // Logical negation NOT = 9; - - // Matches pattern - LIKE = 10; } // Selector modifier shows how the node set will be formed. By default selector @@ -122,7 +119,7 @@ message PlacementPolicy { // bucket repeated Replica replicas = 1 [ json_name = "replicas" ]; - // Container backup factor controls how deep FrostFS will search for nodes + // Container backup factor controls how deep NeoFS will search for nodes // alternatives to include into container's nodes subset uint32 container_backup_factor = 2 [ json_name = "containerBackupFactor" ]; @@ -136,25 +133,25 @@ message PlacementPolicy { bool unique = 5 [ json_name = "unique" ]; } -// FrostFS node description +// NeoFS node description message NodeInfo { - // Public key of the FrostFS node in a binary format + // Public key of the NeoFS node in a binary format bytes public_key = 1 [ json_name = "publicKey" ]; // Ways to connect to a node repeated string addresses = 2 [ json_name = "addresses" ]; - // Administrator-defined Attributes of the FrostFS Storage Node. + // Administrator-defined Attributes of the NeoFS Storage Node. // // `Attribute` is a Key-Value metadata pair. Key name must be a valid UTF-8 // string. Value can't be empty. // // Attributes can be constructed into a chain of attributes: any attribute can // have a parent attribute and a child attribute (except the first and the - // last one). A string representation of the chain of attributes in FrostFS + // last one). A string representation of the chain of attributes in NeoFS // Storage Node configuration uses ":" and "/" symbols, e.g.: // - // `FrostFS_NODE_ATTRIBUTE_1=key1:val1/key2:val2` + // `NEOFS_NODE_ATTRIBUTE_1=key1:val1/key2:val2` // // Therefore the string attribute representation in the Node configuration // must use "\:", "\/" and "\\" escaped symbols if any of them appears in an @@ -201,8 +198,8 @@ message NodeInfo { // [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2). Calculated // automatically from `UN-LOCODE` attribute. // * Continent \ - // Node's continent name according to the [Seven-Continent - // model](https://en.wikipedia.org/wiki/Continent#Number). Calculated + // Node's continent name according to the [Seven-Continent model] + // (https://en.wikipedia.org/wiki/Continent#Number). Calculated // automatically from `UN-LOCODE` attribute. // * ExternalAddr // Node's preferred way for communications with external clients. @@ -210,7 +207,7 @@ message NodeInfo { // Must contain a comma-separated list of multi-addresses. // // For detailed description of each well-known attribute please see the - // corresponding section in FrostFS Technical Specification. + // corresponding section in NeoFS Technical Specification. message Attribute { // Key of the node attribute string key = 1 [ json_name = "key" ]; @@ -222,13 +219,13 @@ message NodeInfo { // `Country`. repeated string parents = 3 [ json_name = "parents" ]; } - // Carries list of the FrostFS node attributes in a key-value form. Key name + // Carries list of the NeoFS node attributes in a key-value form. Key name // must be a node-unique valid UTF-8 string. Value can't be empty. NodeInfo // structures with duplicated attribute names or attributes with empty values // will be considered invalid. repeated Attribute attributes = 3 [ json_name = "attributes" ]; - // Represents the enumeration of various states of the FrostFS node. + // Represents the enumeration of various states of the NeoFS node. enum State { // Unknown state UNSPECIFIED = 0; @@ -243,7 +240,7 @@ message NodeInfo { MAINTENANCE = 3; } - // Carries state of the FrostFS node + // Carries state of the NeoFS node State state = 4 [ json_name = "state" ]; } @@ -256,7 +253,7 @@ message Netmap { repeated NodeInfo nodes = 2 [ json_name = "nodes" ]; } -// FrostFS network configuration +// NeoFS network configuration message NetworkConfig { // Single configuration parameter. Key MUST be network-unique. // @@ -275,7 +272,7 @@ message NetworkConfig { // Fee paid for container creation by the container owner. // Value: little-endian integer. Default: 0. // - **EpochDuration** \ - // FrostFS epoch duration measured in Sidechain blocks. + // NeoFS epoch duration measured in Sidechain blocks. // Value: little-endian integer. Default: 0. // - **HomomorphicHashingDisabled** \ // Flag of disabling the homomorphic hashing of objects' payload. @@ -287,39 +284,8 @@ message NetworkConfig { // Flag allowing setting the MAINTENANCE state to storage nodes. // Value: true if any byte != 0. Default: false. // - **MaxObjectSize** \ - // Maximum size of physically stored FrostFS object measured in bytes. + // Maximum size of physically stored NeoFS object measured in bytes. // Value: little-endian integer. Default: 0. - // - // This value refers to the maximum size of a **physically** stored object - // in FrostFS. However, from a user's perspective, the **logical** size of a - // stored object can be significantly larger. The relationship between the - // physical and logical object sizes is governed by the following formula - // - // ```math - // \mathrm{Stored\ Object\ Size} \le - // \frac{ - // \left(\mathrm{Max\ Object\ Size}\right)^2 - // }{ - // \mathrm{Object\ ID\ Size} - // } - // ``` - // - // This arises from the fact that a tombstone, also being an object, stores - // the IDs of inhumed objects and cannot be divided into smaller objects, - // thus having an upper limit for its size. - // - // For example, if: - // * Max Object Size Size = 64 MiB; - // * Object ID Size = 32 B; - // - // then: - // ```math - // \mathrm{Stored\ Object\ Size} \le - // \frac{\left(64\ \mathrm{MiB}\right)^2}{32\ \mathrm{B}} = - // \frac{2^{52}}{2^5}\ \mathrm{B} = - // 2^{47}\ \mathrm{B} = - // 128\ \mathrm{TiB} - // ``` // - **WithdrawFee** \ // Fee paid for withdrawal of funds paid by the account owner. // Value: little-endian integer. Default: 0. @@ -340,18 +306,18 @@ message NetworkConfig { repeated Parameter parameters = 1 [ json_name = "parameters" ]; } -// Information about FrostFS network +// Information about NeoFS network message NetworkInfo { - // Number of the current epoch in the FrostFS network + // Number of the current epoch in the NeoFS network uint64 current_epoch = 1 [ json_name = "currentEpoch" ]; - // Magic number of the sidechain of the FrostFS network + // Magic number of the sidechain of the NeoFS network uint64 magic_number = 2 [ json_name = "magicNumber" ]; - // MillisecondsPerBlock network parameter of the sidechain of the FrostFS + // MillisecondsPerBlock network parameter of the sidechain of the NeoFS // network int64 ms_per_block = 3 [ json_name = "msPerBlock" ]; - // FrostFS network configuration + // NeoFS network configuration NetworkConfig network_config = 4 [ json_name = "networkConfig" ]; } diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs new file mode 100644 index 0000000..243ab00 --- /dev/null +++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs @@ -0,0 +1,396 @@ +using FrostFS.Session; +using Google.Protobuf; + +namespace FrostFS.Object +{ + public partial class GetRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class GetResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class PutRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class PutResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class DeleteRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class DeleteResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class HeadRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class HeadResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + public partial class SearchRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class SearchResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class GetRangeRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class GetRangeResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class GetRangeHashRequest : IRequest + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } + + public partial class GetRangeHashResponse : IResponse + { + IMetaHeader IVerificableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerificableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/object/service.proto b/src/FrostFS.SDK.ProtosV2/object/service.proto similarity index 85% rename from src/FrostFS.SDK.Protos/object/service.proto rename to src/FrostFS.SDK.ProtosV2/object/service.proto index 2b8042b..383e83b 100644 --- a/src/FrostFS.SDK.Protos/object/service.proto +++ b/src/FrostFS.SDK.ProtosV2/object/service.proto @@ -151,7 +151,7 @@ service ObjectService { rpc Head(HeadRequest) returns (HeadResponse); // Search objects in container. Search query allows to match by Object - // Header's filed values. Please see the corresponding FrostFS Technical + // Header's filed values. Please see the corresponding NeoFS Technical // Specification section for more details. // // Extended headers can change `Search` behaviour: @@ -283,55 +283,6 @@ service ObjectService { // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc PutSingle(PutSingleRequest) returns (PutSingleResponse); - - // Patch the object. Request uses gRPC stream. First message must set - // the address of the object that is going to get patched. If the object's - // attributes are patched, then these attrubutes must be set only within the - // first stream message. - // - // If the patch request is performed by NOT the object's owner but if the - // actor has the permission to perform the patch, then `OwnerID` of the object - // is changed. In this case the object's owner loses the object's ownership - // after the patch request is successfully done. - // - // As objects are content-addressable the patching causes new object ID - // generation for the patched object. This object id is set witihn - // `PatchResponse`. But the object id may remain unchanged in such cases: - // 1. The chunk of the applying patch contains the same value as the object's - // payload within the same range; - // 2. The patch that reverts the changes applied by preceding patch; - // 3. The application of the same patches for the object a few times. - // - // Extended headers can change `Patch` behaviour: - // * [ __SYSTEM__NETMAP_EPOCH \ - // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ - // Will use the requsted version of Network Map for object placement - // calculation. - // - // Please refer to detailed `XHeader` description. - // - // Statuses: - // - **OK** (0, SECTION_SUCCESS): \ - // object has been successfully patched and saved in the container; - // - Common failures (SECTION_FAILURE_COMMON); - // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ - // write access to the container is denied; - // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ - // object not found in container; - // - **OBJECT_ALREADY_REMOVED** (2052, SECTION_OBJECT): \ - // the requested object has been marked as deleted. - // - **OUT_OF_RANGE** (2053, SECTION_OBJECT): \ - // the requested range is out of bounds; - // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ - // object storage container not found; - // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; - // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ - // (for trusted object preparation) session private key does not exist or - // has been deleted; - // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ - // provided session token has expired. - rpc Patch(stream PatchRequest) returns (PatchResponse); } // GET object request @@ -632,9 +583,6 @@ message SearchRequest { // object_id of parent // * $Object:split.splitID \ // 16 byte UUIDv4 used to identify the split object hierarchy parts - // * $Object:ec.parent \ - // If the object is stored according to EC policy, then ec_parent - // attribute is set to return an id list of all related EC chunks. // // There are some well-known filter aliases to match objects by certain // properties: @@ -865,75 +813,4 @@ message PutSingleResponse { // authenticate the nodes of the message route and check the correctness of // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} - -// Object PATCH request -message PatchRequest { - // PATCH request body - message Body { - // The address of the object that is requested to get patched. - neo.fs.v2.refs.Address address = 1; - - // New attributes for the object. See `replace_attributes` flag usage to - // define how new attributes should be set. - repeated neo.fs.v2.object.Header.Attribute new_attributes = 2; - - // If this flag is set, then the object's attributes will be entirely - // replaced by `new_attributes` list. The empty `new_attributes` list with - // `replace_attributes = true` just resets attributes list for the object. - // - // Default `false` value for this flag means the attributes will be just - // merged. If the incoming `new_attributes` list contains already existing - // key, then it just replaces it while merging the lists. - bool replace_attributes = 3; - - // The patch for the object's payload. - message Patch { - // The range of the source object for which the payload is replaced by the - // patch's chunk. If the range's `length = 0`, then the patch's chunk is - // just appended to the original payload starting from the `offest` - // without any replace. - Range source_range = 1; - - // The chunk that is being appended to or that replaces the original - // payload on the given range. - bytes chunk = 2; - } - - // The patch that is applied for the object. - Patch patch = 4; - } - - // Body for patch request message. - Body body = 1; - - // Carries request meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.RequestMetaHeader meta_header = 2; - - // Carries request verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.RequestVerificationHeader verify_header = 3; -} - -// Object PATCH response -message PatchResponse { - // PATCH response body - message Body { - // The object ID of the saved patched object. - neo.fs.v2.refs.ObjectID object_id = 1; - } - - // Body for patch response message. - Body body = 1; - - // Carries response meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.ResponseMetaHeader meta_header = 2; - - // Carries response verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Protos/object/types.proto b/src/FrostFS.SDK.ProtosV2/object/types.proto similarity index 92% rename from src/FrostFS.SDK.Protos/object/types.proto rename to src/FrostFS.SDK.ProtosV2/object/types.proto index b838c8e..6e62b86 100644 --- a/src/FrostFS.SDK.Protos/object/types.proto +++ b/src/FrostFS.SDK.ProtosV2/object/types.proto @@ -155,7 +155,7 @@ message Header { // MIME Content Type of object's payload // // For detailed description of each well-known attribute please see the - // corresponding section in FrostFS Technical Specification. + // corresponding section in NeoFS Technical Specification. message Attribute { // string key to the object attribute string key = 1 [ json_name = "key" ]; @@ -208,18 +208,6 @@ message Header { uint32 header_length = 4 [ json_name = "headerLength" ]; // Chunk of a parent header. bytes header = 5 [ json_name = "header" ]; - // As the origin object is EC-splitted its identifier is known to all - // chunks as parent. But parent itself can be a part of Split (does not - // relate to EC-split). In this case parent_split_id should be set. - bytes parent_split_id = 6 [ json_name = "parentSplitID" ]; - // EC-parent's parent ID. parent_split_parent_id is set if EC-parent, - // itself, is a part of Split and if an object ID of its parent is - // presented. The field allows to determine how EC-chunk is placed in Split - // hierarchy. - neo.fs.v2.refs.ObjectID parent_split_parent_id = 7 - [ json_name = "parentSplitParentID" ]; - // EC parent's attributes. - repeated Attribute parent_attributes = 8 [ json_name = "parentAttributes" ]; } // Erasure code chunk information. EC ec = 12 [ json_name = "ec" ]; diff --git a/src/FrostFS.SDK.Protos/refs/types.proto b/src/FrostFS.SDK.ProtosV2/refs/types.proto similarity index 93% rename from src/FrostFS.SDK.Protos/refs/types.proto rename to src/FrostFS.SDK.ProtosV2/refs/types.proto index 014c736..15d32c1 100644 --- a/src/FrostFS.SDK.Protos/refs/types.proto +++ b/src/FrostFS.SDK.ProtosV2/refs/types.proto @@ -5,7 +5,7 @@ package neo.fs.v2.refs; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc;refs"; option csharp_namespace = "FrostFS.Refs"; -// Objects in FrostFS are addressed by their ContainerID and ObjectID. +// Objects in NeoFS are addressed by their ContainerID and ObjectID. // // String presentation of `Address` is a concatenation of string encoded // `ContainerID` and `ObjectID` delimited by '/' character. @@ -16,9 +16,8 @@ message Address { ObjectID object_id = 2 [ json_name = "objectID" ]; } -// FrostFS Object unique identifier. Objects are immutable and -// content-addressed. It means `ObjectID` will change if the `header` or the -// `payload` changes. +// NeoFS Object unique identifier. Objects are immutable and content-addressed. +// It means `ObjectID` will change if the `header` or the `payload` changes. // // `ObjectID` is a 32 byte long // [SHA256](https://csrc.nist.gov/publications/detail/fips/180/4/final) hash of @@ -38,7 +37,7 @@ message ObjectID { bytes value = 1 [ json_name = "value" ]; } -// FrostFS container identifier. Container structures are immutable and +// NeoFS container identifier. Container structures are immutable and // content-addressed. // // `ContainerID` is a 32 byte long @@ -91,7 +90,7 @@ message Version { uint32 minor = 2 [ json_name = "minor" ]; } -// Signature of something in FrostFS. +// Signature of something in NeoFS. message Signature { // Public key used for signing bytes key = 1 [ json_name = "key" ]; diff --git a/src/FrostFS.SDK.Protos/session/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs similarity index 54% rename from src/FrostFS.SDK.Protos/session/Extension.Message.cs rename to src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs index b7ca64e..fd78adc 100644 --- a/src/FrostFS.SDK.Protos/session/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs @@ -1,27 +1,25 @@ -using FrostFS.SDK.Proto.Interfaces; - -using Google.Protobuf; +using Google.Protobuf; namespace FrostFS.Session; public partial class CreateResponse : IResponse { - IMetaHeader IVerifiableMessage.GetMetaHeader() + IMetaHeader IVerificableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerifiableMessage.GetVerificationHeader() + IVerificationHeader IVerificableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -34,22 +32,22 @@ public partial class CreateResponse : IResponse public partial class CreateRequest : IRequest { - IMetaHeader IVerifiableMessage.GetMetaHeader() + IMetaHeader IVerificableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerifiableMessage.GetVerificationHeader() + IVerificationHeader IVerificableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } diff --git a/src/FrostFS.SDK.Protos/session/Extension.MetaHeader.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.MetaHeader.cs similarity index 100% rename from src/FrostFS.SDK.Protos/session/Extension.MetaHeader.cs rename to src/FrostFS.SDK.ProtosV2/session/Extension.MetaHeader.cs diff --git a/src/FrostFS.SDK.Protos/session/Extension.VerificationHeader.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.VerificationHeader.cs similarity index 100% rename from src/FrostFS.SDK.Protos/session/Extension.VerificationHeader.cs rename to src/FrostFS.SDK.ProtosV2/session/Extension.VerificationHeader.cs diff --git a/src/FrostFS.SDK.Protos/session/Extension.XHeader.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs similarity index 79% rename from src/FrostFS.SDK.Protos/session/Extension.XHeader.cs rename to src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs index c0c5b25..f8f2695 100644 --- a/src/FrostFS.SDK.Protos/session/Extension.XHeader.cs +++ b/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs @@ -2,7 +2,7 @@ public partial class XHeader { - public const string ReservedXHeaderPrefix = "__SYSTEM__"; + public const string ReservedXHeaderPrefix = "__NEOFS__"; public const string XHeaderNetmapEpoch = ReservedXHeaderPrefix + "NETMAP_EPOCH"; public const string XHeaderNetmapLookupDepth = ReservedXHeaderPrefix + "NETMAP_LOOKUP_DEPTH"; } diff --git a/src/FrostFS.SDK.Protos/session/service.proto b/src/FrostFS.SDK.ProtosV2/session/service.proto similarity index 97% rename from src/FrostFS.SDK.Protos/session/service.proto rename to src/FrostFS.SDK.ProtosV2/session/service.proto index 6511f3b..6f48e3a 100644 --- a/src/FrostFS.SDK.Protos/session/service.proto +++ b/src/FrostFS.SDK.ProtosV2/session/service.proto @@ -11,7 +11,7 @@ import "session/types.proto"; // `SessionService` allows to establish a temporary trust relationship between // two peer nodes and generate a `SessionToken` as the proof of trust to be // attached in requests for further verification. Please see corresponding -// section of FrostFS Technical Specification for details. +// section of NeoFS Technical Specification for details. service SessionService { // Open a new session between two peers. // diff --git a/src/FrostFS.SDK.Protos/session/types.proto b/src/FrostFS.SDK.ProtosV2/session/types.proto similarity index 96% rename from src/FrostFS.SDK.Protos/session/types.proto rename to src/FrostFS.SDK.ProtosV2/session/types.proto index bc0d7f1..d1a9ef1 100644 --- a/src/FrostFS.SDK.Protos/session/types.proto +++ b/src/FrostFS.SDK.ProtosV2/session/types.proto @@ -36,9 +36,6 @@ message ObjectSessionContext { // Refers to object.GetRangeHash RPC call RANGEHASH = 7; - - // Refers to object.Patch RPC call - PATCH = 8; } // Type of request for which the token is issued Verb verb = 1 [ json_name = "verb" ]; @@ -50,7 +47,7 @@ message ObjectSessionContext { refs.ContainerID container = 1 [ json_name = "container" ]; // Indicates which objects the session is spread to. Objects are expected - // to be stored in the FrostFS container referenced by `container` field. + // to be stored in the NeoFS container referenced by `container` field. // Each element MUST have correct format. repeated refs.ObjectID objects = 2 [ json_name = "objects" ]; } @@ -88,7 +85,7 @@ message ContainerSessionContext { refs.ContainerID container_id = 3 [ json_name = "containerID" ]; } -// FrostFS Session Token. +// NeoFS Session Token. message SessionToken { // Session Token body message Body { @@ -126,7 +123,7 @@ message SessionToken { } // Session Token contains the proof of trust between peers to be attached in // requests for further verification. Please see corresponding section of - // FrostFS Technical Specification for details. + // NeoFS Technical Specification for details. Body body = 1 [ json_name = "body" ]; // Signature of `SessionToken` information @@ -186,7 +183,7 @@ message RequestMetaHeader { // `RequestMetaHeader` of the origin request RequestMetaHeader origin = 7 [ json_name = "origin" ]; - // FrostFS network magic. Must match the value for the network + // NeoFS network magic. Must match the value for the network // that the server belongs to. uint64 magic_number = 8 [ json_name = "magicNumber" ]; } diff --git a/src/FrostFS.SDK.Protos/status/types.proto b/src/FrostFS.SDK.ProtosV2/status/types.proto similarity index 86% rename from src/FrostFS.SDK.Protos/status/types.proto rename to src/FrostFS.SDK.ProtosV2/status/types.proto index 694f969..8ab2f40 100644 --- a/src/FrostFS.SDK.Protos/status/types.proto +++ b/src/FrostFS.SDK.ProtosV2/status/types.proto @@ -5,12 +5,12 @@ package neo.fs.v2.status; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status/grpc;status"; option csharp_namespace = "FrostFS.Status"; -// Declares the general format of the status returns of the FrostFS RPC -// protocol. Status is present in all response messages. Each RPC of FrostFS -// protocol describes the possible outcomes and details of the operation. +// Declares the general format of the status returns of the NeoFS RPC protocol. +// Status is present in all response messages. Each RPC of NeoFS protocol +// describes the possible outcomes and details of the operation. // // Each status is assigned a one-to-one numeric code. Any unique result of an -// operation in FrostFS is unambiguously associated with the code value. +// operation in NeoFS is unambiguously associated with the code value. // // Numerical set of codes is split into 1024-element sections. An enumeration // is defined for each section. Values can be referred to in the following ways: @@ -78,7 +78,7 @@ enum Section { SECTION_APE_MANAGER = 5; } -// Section of FrostFS successful return codes. +// Section of NeoFS successful return codes. enum Success { // [**0**] Default success. Not detailed. // If the server cannot match successful outcome to the code, it should @@ -93,9 +93,9 @@ enum CommonFail { // use this code. INTERNAL = 0; - // [**1025**] Wrong magic of the FrostFS network. + // [**1025**] Wrong magic of the NeoFS network. // Details: - // - [**0**] Magic number of the served FrostFS network (big-endian 64-bit + // - [**0**] Magic number of the served NeoFS network (big-endian 64-bit // unsigned integer). WRONG_MAGIC_NUMBER = 1; @@ -104,11 +104,6 @@ enum CommonFail { // [**1027**] Node is under maintenance. NODE_UNDER_MAINTENANCE = 3; - - // [**1028**] Invalid argument error. If the server fails on validation of a - // request parameter as the client sent it incorrectly, then this code should - // be used. - INVALID_ARGUMENT = 4; } // Section of statuses for object-related operations. diff --git a/src/FrostFS.SDK.Protos/tombstone/types.proto b/src/FrostFS.SDK.ProtosV2/tombstone/types.proto similarity index 83% rename from src/FrostFS.SDK.Protos/tombstone/types.proto rename to src/FrostFS.SDK.ProtosV2/tombstone/types.proto index 8780317..739bef4 100644 --- a/src/FrostFS.SDK.Protos/tombstone/types.proto +++ b/src/FrostFS.SDK.ProtosV2/tombstone/types.proto @@ -8,10 +8,10 @@ option csharp_namespace = "FrostFS.Tombstone"; import "refs/types.proto"; // Tombstone keeps record of deleted objects for a few epochs until they are -// purged from the FrostFS network. +// purged from the NeoFS network. message Tombstone { - // Last FrostFS epoch number of the tombstone lifetime. It's set by the - // tombstone creator depending on the current FrostFS network settings. A + // Last NeoFS epoch number of the tombstone lifetime. It's set by the + // tombstone creator depending on the current NeoFS network settings. A // tombstone object must have the same expiration epoch value in // `__SYSTEM__EXPIRATION_EPOCH` (`__NEOFS__EXPIRATION_EPOCH` is deprecated) // attribute. Otherwise, the tombstone will be rejected by a storage node. diff --git a/src/FrostFS.SDK.Tests/CallbackInterceptor.cs b/src/FrostFS.SDK.Tests/CallbackInterceptor.cs deleted file mode 100644 index c4eb726..0000000 --- a/src/FrostFS.SDK.Tests/CallbackInterceptor.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Grpc.Core; -using Grpc.Core.Interceptors; - -namespace FrostFS.SDK.SmokeTests; - -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", - Justification = "parameters are provided by GRPC infrastructure")] -public class CallbackInterceptor(Action? callback = null) : Interceptor -{ - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - var call = continuation(request, context); - - return new AsyncUnaryCall( - HandleUnaryResponse(call), - call.ResponseHeadersAsync, - call.GetStatus, - call.GetTrailers, - call.Dispose); - } - - private async Task HandleUnaryResponse(AsyncUnaryCall call) - { - var response = await call; - - callback?.Invoke($"elapsed"); - - return response; - } -} diff --git a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj deleted file mode 100644 index 37bbace..0000000 --- a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - net8.0 - enable - enable - - false - true - - - - true - - - - true - - - - True - .\\..\\..\\keyfile.snk - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs deleted file mode 100644 index bd9d858..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs +++ /dev/null @@ -1,38 +0,0 @@ -using FrostFS.Object; -using FrostFS.Session; - -using Google.Protobuf; - -using Grpc.Core; - -namespace FrostFS.SDK.Tests; - -public class AsyncStreamRangeReaderMock(string key, byte[] response) : ServiceBase(key), IAsyncStreamReader -{ - private readonly byte[] _response = response; - - public GetRangeResponse Current - { - get - { - var response = new GetRangeResponse - { - Body = new GetRangeResponse.Types.Body - { - Chunk = ByteString.CopyFrom(_response) - }, - MetaHeader = new ResponseMetaHeader() - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return response; - } - } - - public Task MoveNext(CancellationToken cancellationToken) - { - return Task.FromResult(true); - } -} - diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs deleted file mode 100644 index 58a9d40..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Security.Cryptography; - -using FrostFS.Object; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.Session; - -using Google.Protobuf; - -using Grpc.Core; - -namespace FrostFS.SDK.Tests; - -public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader) : ServiceBase(key), IAsyncStreamReader -{ - public GetResponse Current - { - get - { - var header = new Header - { - ContainerId = objectHeader.ContainerId.ToMessage(), - PayloadLength = objectHeader.PayloadLength, - Version = objectHeader.Version!.ToMessage(), - OwnerId = objectHeader.OwnerId!.ToMessage() - }; - - if (objectHeader.Attributes != null) - { - foreach (var attr in objectHeader.Attributes) - header.Attributes.Add(attr.ToMessage()); - } - - var response = new GetResponse - { - Body = new GetResponse.Types.Body - { - Init = new GetResponse.Types.Body.Types.Init - { - Header = header, - ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Array.Empty())) }, - Signature = new Refs.Signature - { - Key = Key.PublicKeyProto, - Sign = Key.ECDsaKey.SignData(header.ToByteArray()), - } - } - }, - MetaHeader = new ResponseMetaHeader() - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return response; - } - } - - public Task MoveNext(CancellationToken cancellationToken) - { - return Task.FromResult(true); - } -} - diff --git a/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs b/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs deleted file mode 100644 index 7fba4f6..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.ObjectModel; - -using FrostFS.SDK.Proto.Interfaces; - -using Grpc.Core; - -namespace FrostFS.SDK.Tests; - -public class ClientStreamWriter : IClientStreamWriter -{ - private WriteOptions? _options; - - public Collection Messages { get; } = []; - public bool CompletedTask { get; private set; } - - public WriteOptions? WriteOptions - { - get => _options; - set => _options = value; - } - - public Task CompleteAsync() - { - CompletedTask = true; - return Task.CompletedTask; - } - - public Task WriteAsync(IRequest message) - { - Object.PutRequest pr = new((Object.PutRequest)message); - Messages.Add(pr); - return Task.CompletedTask; - } -} - diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs deleted file mode 100644 index eafdda5..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs +++ /dev/null @@ -1,86 +0,0 @@ -using FrostFS.Container; -using FrostFS.Object; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.Session; - -using Google.Protobuf; - -using Grpc.Core; - -using Moq; - -namespace FrostFS.SDK.Tests; - -public abstract class ServiceBase(string key) -{ - public string StringKey { get; private set; } = key; - public ClientKey Key { get; private set; } = new ClientKey(key.LoadWif()); - public FrostFsVersion Version { get; set; } = DefaultVersion; - public FrostFsPlacementPolicy PlacementPolicy { get; set; } = DefaultPlacementPolicy; - - public static FrostFsVersion DefaultVersion { get; } = new(2, 13); - public static FrostFsPlacementPolicy DefaultPlacementPolicy { get; } = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)); - -#pragma warning disable CA2227 // this is specific object, should be treated as is - public Metadata? Metadata { get; set; } -#pragma warning restore CA2227 - - public DateTime? DateTime { get; protected set; } - - public CancellationToken CancellationToken { get; protected set; } - public CancellationTokenSource CancellationTokenSource { get; protected set; } = new CancellationTokenSource(); - - public static Metadata ResponseMetaData => []; - - protected ResponseVerificationHeader GetResponseVerificationHeader(IResponse response) - { - ArgumentNullException.ThrowIfNull(response); - - var verifyHeader = new ResponseVerificationHeader - { - MetaSignature = new Refs.Signature - { - Key = Key.PublicKeyProto, - Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = Key.ECDsaKey.SignData(response.MetaHeader.ToByteArray()) - }, - BodySignature = new Refs.Signature - { - Key = Key.PublicKeyProto, - Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = Key.ECDsaKey.SignData(response.GetBody().ToByteArray()) - }, - OriginSignature = new Refs.Signature - { - Key = Key.PublicKeyProto, - Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = Key.ECDsaKey.SignData(ReadOnlyMemory.Empty) - } - }; - - return verifyHeader; - } - - public ResponseMetaHeader ResponseMetaHeader => new() - { - Version = Version.ToMessage(), - Epoch = 100, - Ttl = 1 - }; -} - -public abstract class ContainerServiceBase(string key) : ServiceBase(key) -{ - public Guid ContainerGuid { get; set; } = Guid.NewGuid(); - - public abstract Mock GetMock(); -} - -public abstract class ObjectServiceBase(string key) : ServiceBase(key) -{ - public abstract Mock GetMock(); - - public Guid ContainerGuid { get; set; } = Guid.NewGuid(); -} diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs deleted file mode 100644 index 2734a26..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FrostFS.Container; - -using Moq; - -namespace FrostFS.SDK.Tests; - -public class ContainerStub(string key) : ContainerServiceBase(key) -{ - public override Mock GetMock() - { - return new Mock(); - } -} diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs deleted file mode 100644 index 7491950..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System.Collections.ObjectModel; - -using FrostFS.Container; -using FrostFS.Refs; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.Session; - -using Google.Protobuf; - -using Grpc.Core; - -using Moq; - -namespace FrostFS.SDK.Tests; - -public class ContainerMocker(string key) : ContainerServiceBase(key) -{ - public override Mock GetMock() - { - var mock = new Mock(); - - var grpcVersion = Version.ToMessage(); - - Span ContainerGuidSpan = stackalloc byte[16]; - ContainerGuid.ToBytes(ContainerGuidSpan); - - PutResponse putResponse = new() - { - Body = new PutResponse.Types.Body - { - ContainerId = new ContainerID - { - Value = ByteString.CopyFrom(ContainerGuidSpan) - } - }, - MetaHeader = new ResponseMetaHeader - { - Version = (Version is null ? DefaultVersion : Version).ToMessage(), - Epoch = 100, - Ttl = 1 - } - }; - - putResponse.VerifyHeader = GetResponseVerificationHeader(putResponse); - - var metadata = new Metadata(); - using var putContainerResponse = new AsyncUnaryCall( - Task.FromResult(putResponse), - Task.FromResult(metadata), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => metadata, - () => { }); - - mock.Setup(x => x.PutAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - return putContainerResponse; - }); - - var getResponse = new GetResponse - { - Body = new GetResponse.Types.Body - { - Container = new Container.Container - { - Version = grpcVersion, - Nonce = ByteString.CopyFrom(ContainerGuidSpan), - PlacementPolicy = PlacementPolicy.GetPolicy() - } - }, - MetaHeader = ResponseMetaHeader - }; - - getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse); - - var getNoContainerResponse = new GetResponse - { - Body = new(), - MetaHeader = new ResponseMetaHeader - { - Status = new Status.Status - { - Code = 3072, - Message = "container not found" - } - } - }; - - getNoContainerResponse.VerifyHeader = GetResponseVerificationHeader(getNoContainerResponse); - - mock.Setup(x => x.GetAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - if (ReturnContainerRemoved) - { - return new AsyncUnaryCall( - Task.FromResult(getNoContainerResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.NotFound, string.Empty), - () => ResponseMetaData, - () => { }); - } - - return new AsyncUnaryCall( - Task.FromResult(getResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - var listResponse = new ListResponse - { - Body = new ListResponse.Types.Body(), - MetaHeader = ResponseMetaHeader - }; - - foreach (var item in ContainerIds) - { - listResponse.Body.ContainerIds.Add(new ContainerID { Value = ByteString.CopyFrom(item) }); - } - - listResponse.VerifyHeader = GetResponseVerificationHeader(listResponse); - - mock.Setup(x => x.ListAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((ListRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - return new AsyncUnaryCall( - Task.FromResult(listResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - var v = mock.Setup(x => x.DeleteAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Requests.Add(new RequestData(r, m, dt, ct)); - - var response = new DeleteResponse - { - Body = new DeleteResponse.Types.Body(), - MetaHeader = new ResponseMetaHeader() - }; - - var metadata = new Metadata(); - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(metadata), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => metadata, - () => { }); - }); - - return mock; - } - - public bool ReturnContainerRemoved { get; set; } - - public Collection ContainerIds { get; } = []; - - public Collection> Requests { get; } = []; -} diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs deleted file mode 100644 index f60a2c8..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Grpc.Core; - -namespace FrostFS.SDK.Tests; - -public class RequestData(T request, Metadata m, DateTime? dt, CancellationToken ct) -{ - public T Request => request; - public Metadata Metadata => m; - public DateTime? Deadline => dt; - public CancellationToken CancellationToken => ct; -} diff --git a/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs b/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs deleted file mode 100644 index 003d7ac..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs +++ /dev/null @@ -1,148 +0,0 @@ -using FrostFS.Netmap; - -using Google.Protobuf; - -using Grpc.Core; - -using Moq; - -namespace FrostFS.SDK.Tests; - -public class NetworkMocker(string key) : ServiceBase(key) -{ - internal static readonly string[] ParameterKeys = [ - "AuditFee", - "BasicIncomeRate", - "ContainerFee", - "ContainerAliasFee", - "EpochDuration", - "InnerRingCandidateFee", - "MaxECDataCount", - "MaxECParityCount", - "MaxObjectSize", - "WithdrawFee", - "HomomorphicHashingDisabled", - "MaintenanceModeAllowed" - ]; - - public Dictionary Parameters { get; } = []; - - public LocalNodeInfoResponse? NodeInfoResponse { get; set; } - - public LocalNodeInfoRequest? LocalNodeInfoRequest { get; set; } - - public NetworkInfoRequest? NetworkInfoRequest { get; set; } - - public NetmapSnapshotResponse? NetmapSnapshotResponse { get; set; } - - public NetmapSnapshotRequest? NetmapSnapshotRequest { get; set; } - - public Mock GetMock() - { - var mock = new Mock(); - - var networkInfoResponse = new NetworkInfoResponse(); - - var networkConfig = new NetworkConfig(); - - foreach (var key in ParameterKeys) - { - networkConfig.Parameters.Add(new NetworkConfig.Types.Parameter - { - Key = ByteString.CopyFromUtf8(key), - Value = (Parameters != null && Parameters.TryGetValue(key, out byte[]? value)) ? ByteString.CopyFrom(value) : ByteString.CopyFrom(0) - }); - } - - var response = new NetworkInfoResponse - { - Body = new NetworkInfoResponse.Types.Body - { - NetworkInfo = new NetworkInfo - { - CurrentEpoch = 99, - MagicNumber = 13, - MsPerBlock = 999, - NetworkConfig = networkConfig - } - }, - MetaHeader = ResponseMetaHeader - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - mock.Setup(x => x.NetworkInfoAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((NetworkInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - NetworkInfoRequest = r; - Metadata = m; - DateTime = dt; - CancellationToken = ct; - - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - if (NodeInfoResponse != null) - { - NodeInfoResponse.MetaHeader = ResponseMetaHeader; - NodeInfoResponse.VerifyHeader = GetResponseVerificationHeader(NodeInfoResponse); - - mock.Setup(x => x.LocalNodeInfoAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((LocalNodeInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - LocalNodeInfoRequest = r; - Metadata = m; - DateTime = dt; - CancellationToken = ct; - - return new AsyncUnaryCall( - Task.FromResult(NodeInfoResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - } - - if (NetmapSnapshotResponse != null) - { - NetmapSnapshotResponse.MetaHeader = ResponseMetaHeader; - NetmapSnapshotResponse.VerifyHeader = GetResponseVerificationHeader(NetmapSnapshotResponse); - - mock.Setup(x => x.NetmapSnapshotAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((NetmapSnapshotRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - NetmapSnapshotRequest = r; - Metadata = m; - DateTime = dt; - CancellationToken = ct; - - return new AsyncUnaryCall( - Task.FromResult(NetmapSnapshotResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - } - - return mock; - } -} diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs deleted file mode 100644 index 772f92e..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System.Collections.ObjectModel; -using System.Security.Cryptography; - -using FrostFS.Object; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; - -using Google.Protobuf; - -using Grpc.Core; - -using Moq; - -namespace FrostFS.SDK.Tests; - -public class ObjectMocker(string key) : ObjectServiceBase(key) -{ - public FrostFsObjectId? ObjectId { get; set; } - - public FrostFsObjectHeader? ObjectHeader { get; set; } - - public Header? HeadResponse { get; set; } - - public Collection? ResultObjectIds { get; } = []; - - public ClientStreamWriter? ClientStreamWriter { get; } = new(); - - public PatchStreamWriter? PatchStreamWriter { get; } = new(); - - public Collection PutSingleRequests { get; } = []; - - public Collection DeleteRequests { get; } = []; - - public Collection HeadRequests { get; } = []; - - public byte[] RangeResponse { get; set; } = []; - - public GetRangeRequest? GetRangeRequest { get; set; } - - public GetRangeHashRequest? GetRangeHashRequest { get; set; } - - public Collection RangeHashResponses { get; } = []; - - public Action? Callback; - - public override Mock GetMock() - { - var mock = new Mock(); - - if (ObjectHeader != null) - { - mock.Setup(x => x.Get( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - return new AsyncServerStreamingCall( - new AsyncStreamReaderMock(StringKey, ObjectHeader), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - HeadResponse ??= new Header - { - CreationEpoch = 99, - ContainerId = ObjectHeader.ContainerId.ToMessage(), - ObjectType = ObjectType.Regular, - OwnerId = ObjectHeader.OwnerId!.ToMessage(), - PayloadLength = 1, - PayloadHash = new Refs.Checksum { Type = Refs.ChecksumType.Sha256, Sum = ByteString.CopyFrom(SHA256.HashData([0xff])) }, - Version = ObjectHeader.Version!.ToMessage() - }; - - HeadResponse headResponse = new() - { - Body = new HeadResponse.Types.Body - { - Header = new HeaderWithSignature - { - Header = HeadResponse - } - }, - MetaHeader = ResponseMetaHeader - }; - - headResponse.Body.Header.Header.Attributes.Add(new Header.Types.Attribute { Key = "k", Value = "v" }); - - headResponse.Body.Header.Signature = new Refs.Signature - { - Key = Key.PublicKeyProto, - Sign = Key.ECDsaKey.SignData(headResponse.Body.Header.ToByteArray()), - }; - - headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse); - - mock.Setup(x => x.HeadAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((HeadRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - HeadRequests.Add(r); - - return new AsyncUnaryCall( - Task.FromResult(headResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - } - - if (ResultObjectIds != null && ResultObjectIds.Count > 0) - { - PutResponse putResponse = new() - { - Body = new PutResponse.Types.Body - { - ObjectId = new Refs.ObjectID - { - Value = ByteString.CopyFrom(ResultObjectIds.ElementAt(0)) - } - }, - MetaHeader = ResponseMetaHeader, - }; - - putResponse.VerifyHeader = GetResponseVerificationHeader(putResponse); - - mock.Setup(x => x.Put( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((Metadata m, DateTime? dt, CancellationToken ct) => - { - return new AsyncClientStreamingCall( - ClientStreamWriter!, - Task.FromResult(putResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - } - - PutSingleResponse putSingleResponse = new() - { - Body = new PutSingleResponse.Types.Body(), - MetaHeader = ResponseMetaHeader, - }; - - putSingleResponse.VerifyHeader = GetResponseVerificationHeader(putSingleResponse); - - mock.Setup(x => x.PutSingleAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((PutSingleRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Callback?.Invoke(); - Verifier.CheckRequest(r); - - var req = r.Clone(); - - // Clone method does not clone the payload but keeps a reference - req.Body.Object.Payload = ByteString.CopyFrom(r.Body.Object.Payload.ToByteArray()); - PutSingleRequests.Add(req); - - return new AsyncUnaryCall( - Task.FromResult(putSingleResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - if (ObjectId != null) - { - DeleteResponse deleteResponse = new() - { - Body = new DeleteResponse.Types.Body - { - Tombstone = new Refs.Address - { - ContainerId = ObjectHeader!.ContainerId.ToMessage(), - ObjectId = ObjectId.ToMessage() - } - }, - MetaHeader = ResponseMetaHeader - }; - - deleteResponse.VerifyHeader = GetResponseVerificationHeader(deleteResponse); - - mock.Setup(x => x.DeleteAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - DeleteRequests.Add(r); - - return new AsyncUnaryCall( - Task.FromResult(deleteResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - } - - mock.Setup(x => x.GetRange( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((GetRangeRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - GetRangeRequest = r; - - return new AsyncServerStreamingCall( - new AsyncStreamRangeReaderMock(StringKey, RangeResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - mock.Setup(x => x.GetRangeHashAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((GetRangeHashRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - GetRangeHashRequest = r; - - var response = new GetRangeHashResponse - { - Body = new GetRangeHashResponse.Types.Body(), - MetaHeader = ResponseMetaHeader - }; - - if (RangeHashResponses != null) - { - foreach (var hash in RangeHashResponses) - { - response.Body.HashList.Add(hash); - } - } - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - - mock.Setup(x => x.Patch( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((Metadata m, DateTime? dt, CancellationToken ct) => - { - var patchResponse = new PatchResponse - { - Body = new PatchResponse.Types.Body - { - ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1, 2, 3])) }, - }, - MetaHeader = ResponseMetaHeader - }; - - patchResponse.VerifyHeader = GetResponseVerificationHeader(patchResponse); - - return new AsyncClientStreamingCall( - PatchStreamWriter!, - Task.FromResult(patchResponse), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - return mock; - } -} - diff --git a/src/FrostFS.SDK.Tests/Mocks/PatchStreamWriter.cs b/src/FrostFS.SDK.Tests/Mocks/PatchStreamWriter.cs deleted file mode 100644 index 7eb53bc..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/PatchStreamWriter.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.ObjectModel; - -using FrostFS.SDK.Proto.Interfaces; - -using Grpc.Core; - -namespace FrostFS.SDK.Tests; - -public class PatchStreamWriter : IClientStreamWriter -{ - private WriteOptions? _options; - - public Collection Messages { get; } = []; - - public bool CompletedTask { get; private set; } - - public WriteOptions? WriteOptions - { - get => _options; - set => _options = value; - } - - public Task CompleteAsync() - { - CompletedTask = true; - return Task.CompletedTask; - } - - public Task WriteAsync(IRequest message) - { - Object.PatchRequest pr = new((Object.PatchRequest)message); - Messages.Add(pr); - return Task.CompletedTask; - } -} - diff --git a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs deleted file mode 100644 index ebea821..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs +++ /dev/null @@ -1,72 +0,0 @@ -using FrostFS.Session; - -using Google.Protobuf; - -using Grpc.Core; - -using Moq; - -namespace FrostFS.SDK.Tests; - -[System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class SessionMocker(string key) : ServiceBase(key) -{ - public byte[]? SessionId { get; set; } - - public byte[]? SessionKey { get; set; } - - public CreateRequest? CreateSessionRequest { get; private set; } - - public Mock GetMock() - { - var mock = new Mock(); - - Random rand = new(); - - if (SessionId == null) - { - SessionId = new byte[16]; - rand.NextBytes(SessionId); - } - - if (SessionKey == null) - { - SessionKey = new byte[33]; - rand.NextBytes(SessionKey); - } - - CreateResponse response = new() - { - Body = new CreateResponse.Types.Body - { - Id = ByteString.CopyFrom(SessionId), - SessionKey = ByteString.CopyFrom(SessionKey) - }, - MetaHeader = ResponseMetaHeader - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - mock.Setup(x => x.CreateAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((CreateRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - CreateSessionRequest = r; - Metadata = m; - DateTime = dt; - CancellationToken = ct; - - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(ResponseMetaData), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => ResponseMetaData, - () => { }); - }); - - return mock; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs deleted file mode 100644 index f1032db..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs +++ /dev/null @@ -1,286 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using FrostFS.SDK.Client; -using Grpc.Core; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class ContainerTests : SmokeTestsBase -{ - [Fact] - public async void FailCreateContainerByTimeoutTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - try - { - _ = await CreateContainer(client, - ctx: new CallContext(TimeSpan.FromMilliseconds(1)), - token: null, - unique: true, - backupFactor: 1, - selectors: [], - filter: [], - containerAttributes: [new("testKey1", "testValue1")], - new FrostFsReplica(1)); - - Assert.Fail("Exception is expected"); - } - catch (RpcException ex) - { - Assert.Equal(StatusCode.DeadlineExceeded, ex.Status.StatusCode); - } - } - - [Fact] - public async void CreateContainerTest1() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - await Cleanup(client); - - client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - FrostFsContainerId containerId = await CreateContainer(client, - ctx: default, - token: null, - unique: true, - backupFactor: 1, - selectors: [], - filter: [], - containerAttributes: [new("testKey1", "testValue1")], - new FrostFsReplica(3)); - - Assert.NotNull(containerId); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - - Assert.NotNull(container); - - Assert.NotNull(container.Attributes); - Assert.Equal("testKey1", container.Attributes[0].Key); - Assert.Equal("testValue1", container.Attributes[0].Value); - - //Assert.Equal("true", container.Attributes[1].Value); - - // for cluster - //Assert.Equal(2, container.Attributes.Count); - //Assert.Equal("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", container.Attributes[1].Key); - - Assert.True(container.PlacementPolicy.HasValue); - - Assert.Equal(1u, container.PlacementPolicy.Value.BackupFactor); - Assert.True(container.PlacementPolicy.Value.Unique); - Assert.Empty(container.PlacementPolicy.Value.Selectors); - Assert.Empty(container.PlacementPolicy.Value.Filters); - - Assert.Single(container.PlacementPolicy.Value.Replicas); - Assert.Equal(3u, container.PlacementPolicy.Value.Replicas[0].Count); - Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcParityCount); - Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcDataCount); - Assert.Equal("", container.PlacementPolicy.Value.Replicas[0].Selector); - - Assert.Equal(OwnerId!.ToString(), container.Owner!.Value); - - Assert.NotNull(container.Version); - - Assert.Equal(Version!.Major, container.Version.Major); - Assert.Equal(Version.Minor, container.Version.Minor); - } - - [Fact] - public async void CreateContainerTest2() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - Collection filters = [ - new ("filter1", "filterKey1", 1, "testValue1", []), - new ("filter2", "filterKey2", 2, "testValue2", [new ("subFilter2", "subFilterKey2", 3, "testValue3",[])]) - ]; - - Collection selectors = [ - new ("selector1") { - Count = 1, - Clause = 1, - Attribute = "attribute1", - Filter = "filter1" - }, - new ("selector2") { - Count = 2, - Clause = 2, - Attribute = "attribute2", - Filter = "filter2" - }, - ]; - - FrostFsContainerId containerId = await CreateContainer(client, - ctx: default, - token: null, - unique: false, - backupFactor: 2, - selectors, - filter: filters, - containerAttributes: [], - new FrostFsReplica(1)); - - Assert.NotNull(containerId); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - - Assert.NotNull(container); - - Assert.NotNull(container.Attributes); - //Assert.Single(container.Attributes); - //Assert.Equal("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", container.Attributes[0].Key); - //Assert.Equal("true", container.Attributes[0].Value); - - Assert.True(container.PlacementPolicy.HasValue); - - Assert.Equal(2u, container.PlacementPolicy.Value.BackupFactor); - Assert.False(container.PlacementPolicy.Value.Unique); - - Assert.NotEmpty(container.PlacementPolicy.Value.Selectors); - - Assert.Equal(2, container.PlacementPolicy.Value.Selectors.Count); - - var selector1 = container.PlacementPolicy.Value.Selectors[0]; - Assert.Equal("selector1", selector1.Name); - Assert.Equal(1, selector1.Clause); - Assert.Equal(1u, selector1.Count); - Assert.Equal("attribute1", selector1.Attribute); - Assert.Equal("filter1", selector1.Filter); - - var selector2 = container.PlacementPolicy.Value.Selectors[1]; - Assert.Equal("selector2", selector2.Name); - Assert.Equal(2, selector2.Clause); - Assert.Equal(2u, selector2.Count); - Assert.Equal("attribute2", selector2.Attribute); - Assert.Equal("filter2", selector2.Filter); - - Assert.NotEmpty(container.PlacementPolicy.Value.Filters); - - Assert.Equal(2, container.PlacementPolicy.Value.Filters.Count); - - var filter1 = container.PlacementPolicy.Value.Filters[0]; - Assert.Equal("filter1", filter1.Name); - Assert.Equal("filterKey1", filter1.Key); - Assert.Equal("testValue1", filter1.Value); - Assert.Equal(1, filter1.Operation); - Assert.Empty(filter1.Filters); - - var filter2 = container.PlacementPolicy.Value.Filters[1]; - Assert.Equal("filter2", filter2.Name); - Assert.Equal("filterKey2", filter2.Key); - Assert.Equal("testValue2", filter2.Value); - Assert.Equal(2, filter2.Operation); - Assert.NotEmpty(filter2.Filters); - - Assert.Single(filter2.Filters); - - var subFilter = filter2.Filters.First(); - Assert.Equal("subFilter2", subFilter.Name); - Assert.Equal("subFilterKey2", subFilter.Key); - Assert.Equal("testValue3", subFilter.Value); - Assert.Equal(3, subFilter.Operation); - Assert.Empty(subFilter.Filters); - - Assert.Single(container.PlacementPolicy.Value.Replicas); - Assert.Equal(1u, container.PlacementPolicy.Value.Replicas[0].Count); - Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcParityCount); - Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcDataCount); - Assert.Equal("", container.PlacementPolicy.Value.Replicas[0].Selector); - - Assert.Equal(OwnerId!.ToString(), container.Owner!.Value); - - Assert.NotNull(container.Version); - - Assert.Equal(Version!.Major, container.Version.Major); - Assert.Equal(Version.Minor, container.Version.Minor); - } - - [Theory] - [InlineData(1)] - [InlineData(10)] - public async void ListAndDeleteContainersTest(int countainerCount) - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - var tasks = new Task[countainerCount]; - - var createdContainers = new ConcurrentDictionary(); - - for (int i = 0; i < countainerCount; i++) - { - int index = i; - tasks[i] = Task.Run(async () => - { - FrostFsContainerId containerId = await CreateContainer(client, - ctx: default, - token: null, - unique: true, - backupFactor: 1, - selectors: [], - filter: [], - containerAttributes: [new($"testKey{index}", $"testValue{index}")], - new FrostFsReplica(3)); - - createdContainers.TryAdd(containerId.ToString(), index); - }); - } - -#pragma warning disable xUnit1031 // Timeout is used - if (!Task.WaitAll(tasks, 20000)) - { - Assert.Fail("cannot create containers"); - } -#pragma warning restore xUnit1031 - - var containers = client.ListContainersAsync(new PrmContainerGetAll(), default); - - var receivedContainers = new List(); - await foreach (var cntr in containers) - { - receivedContainers.Add(cntr.ToString()); - } - - Assert.Equal(countainerCount, receivedContainers.Count); - - foreach (var cntrId in receivedContainers) - { - if (!createdContainers.TryGetValue(cntrId, out var index)) - { - Assert.Fail("Cannot find corresponding container"); - } - - FrostFsContainerId container = new(cntrId); - var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), default); - - Assert.NotNull(containerInfo); - Assert.NotNull(containerInfo.Attributes); - - Assert.Contains(new FrostFsAttributePair($"testKey{index}", $"testValue{index}"), containerInfo.Attributes); - } - - tasks = new Task[countainerCount]; - - for (int i = 0; i < receivedContainers.Count; i++) - { - tasks[i] = client.DeleteContainerAsync(new PrmContainerDelete(new FrostFsContainerId(receivedContainers[i]), lightWait), default); - } - - await Task.WhenAll(tasks); - - await foreach (var _ in client.ListContainersAsync(default, default)) - { - Assert.Fail("Containers exist"); - } - } -} diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs b/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs deleted file mode 100644 index 9d2fd63..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FrostFS.SDK.Client; -using FrostFS.SDK.SmokeTests; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class InterceptorTests() : SmokeTestsBase -{ - [Fact] - public async void NodeInfoCallbackAndInterceptorTest() - { - bool callbackInvoked = false; - bool interceptorInvoked = false; - - var options = ClientOptions; - options.Value.Callback = (cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }; - - options.Value.Interceptors.Add(new CallbackInterceptor(s => interceptorInvoked = true)); - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - var result = await client.GetNodeInfoAsync(default); - - Assert.True(callbackInvoked); - Assert.True(interceptorInvoked); - - Assert.Equal(2u, result.Version.Major); - Assert.Equal(13u, result.Version.Minor); - Assert.Equal(NodeState.Online, result.State); - Assert.Equal(33, result.PublicKey.Length); - Assert.NotNull(result.Addresses); - Assert.True(result.Attributes.Count > 0); - } -} diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs deleted file mode 100644 index 229531a..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FrostFS.SDK.Client; -using FrostFS.SDK.SmokeTests; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class SmokeClientTests : SmokeTestsBase -{ - [Fact] - public async void AccountTest() - { - var test = lightWait.Timeout; - - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - var result = await client.GetBalanceAsync(default); - - Assert.NotNull(result); - Assert.True(result.Value == 0); - } - - [Fact] - public async void NodeInfoTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - var result = await client.GetNodeInfoAsync(default); - - Assert.Equal(2u, result.Version.Major); - Assert.Equal(13u, result.Version.Minor); - Assert.Equal(NodeState.Online, result.State); - Assert.Equal(33, result.PublicKey.Length); - Assert.Single(result.Addresses); - Assert.Equal(9, result.Attributes.Count); - //Assert.Equal(2, result.Addresses.Count); - //Assert.Equal(11, result.Attributes.Count); - } - - [Fact] - public async void NodeInfoStatisticsTest() - { - var options = ClientOptions; - - var callbackContent = string.Empty; - options.Value.Callback = (cs) => callbackContent = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"; - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - var result = await client.GetNodeInfoAsync(default); - - Assert.NotEmpty(callbackContent); - } - - [Fact] - public async void NetworkSettingsTest() - { - var options = ClientOptions; - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - var result = await client.GetNetworkSettingsAsync(default); - - Assert.NotNull(result); - - Assert.True(0u < result.Epoch); - Assert.True(result.HomomorphicHashingDisabled); - Assert.True(result.MaintenanceModeAllowed); - Assert.True(0u < result.MagicNumber); - - Assert.Equal(0u, result.AuditFee); - Assert.Equal(0u, result.BasicIncomeRate); - Assert.Equal(0u, result.ContainerAliasFee); - Assert.Equal(0u, result.ContainerFee); - Assert.Equal(75u, result.EpochDuration); - Assert.Equal(10_000_000_000u, result.InnerRingCandidateFee); - Assert.Equal(12u, result.MaxECDataCount); - Assert.Equal(4u, result.MaxECParityCount); - Assert.Equal(5242880u, result.MaxObjectSize); - Assert.Equal(8000u, result.MsPerBlock); - Assert.Equal(100_000_000u, result.WithdrawFee); - } - - [Fact] - public async void NodeInfoCallbackAndInterceptorTest() - { - bool callbackInvoked = false; - bool interceptorInvoked = false; - - var options = ClientOptions; - options.Value.Callback = (cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }; - - options.Value.Interceptors.Add(new CallbackInterceptor(s => interceptorInvoked = true)); - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - var result = await client.GetNodeInfoAsync(default); - - Assert.True(callbackInvoked); - Assert.True(interceptorInvoked); - - Assert.Equal(2u, result.Version.Major); - Assert.Equal(13u, result.Version.Minor); - Assert.Equal(NodeState.Online, result.State); - Assert.Equal(33, result.PublicKey.Length); - Assert.NotNull(result.Addresses); - Assert.True(result.Attributes.Count > 0); - } -} diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs deleted file mode 100644 index 473b076..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs +++ /dev/null @@ -1,426 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; -using Xunit.Abstractions; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase -{ - private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; - - const string clientCut = "clientCut"; - const string serverCut = "serverCut"; - const string singleObject = "singleObject"; - - [Theory] - [InlineData(true, 1, 1)] - [InlineData(false, 1, 1)] - [InlineData(true, 1, 3)] - [InlineData(false, 1, 3)] - [InlineData(true, 2, 3)] - [InlineData(false, 2, 3)] - [InlineData(true, 2, 1)] - [InlineData(false, 2, 1)] - public async void FullScenario(bool unique, uint backupFactor, uint replicas) - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - _testOutputHelper.WriteLine("client created"); - - await Cleanup(client); - _testOutputHelper.WriteLine("existing containers removed"); - - FrostFsContainerId containerId = await CreateContainer(client, - ctx: default, - token: null, - unique: unique, - backupFactor: backupFactor, - selectors: [], - filter: [], - containerAttributes: [new FrostFsAttributePair("contAttrKey", "contAttrValue")], - new FrostFsReplica(replicas)); - - Assert.NotNull(containerId); - _testOutputHelper.WriteLine("container created"); - - await AddObjectRules(client, containerId); - _testOutputHelper.WriteLine("rules added"); - - await RunSuite(client, containerId); - } - - private async Task RunSuite(IFrostFSClient client, FrostFsContainerId containerId) - { - int[] objectSizes = [1, 257, 5 * 1024 * 1024, 20 * 1024 * 1024]; - - string[] objectTypes = [clientCut, serverCut, singleObject]; - - foreach (var objectSize in objectSizes) - { - _testOutputHelper.WriteLine($"test set for object size {objectSize}"); - - var bytes = GetRandomBytes(objectSize); - var hash = SHA256.HashData(bytes); - - FrostFsObjectId objectId; - foreach (var type in objectTypes) - { - switch (type) - { - case serverCut: - objectId = await CreateObjectServerCut(client, containerId, bytes); - _testOutputHelper.WriteLine($"\tserver side cut"); - break; - case clientCut: - objectId = await CreateObjectClientCut(client, containerId, bytes); - _testOutputHelper.WriteLine($"\tclient side cut"); - break; - case singleObject: - if (objectSize > 1 * 1024 * 1024) - continue; - objectId = await PutSingleObject(client, containerId, bytes); - _testOutputHelper.WriteLine($"\tput single object"); - - break; - default: - throw new ArgumentException("unexpected object type"); - } - - Assert.NotNull(objectId); - - _testOutputHelper.WriteLine($"\tobject created"); - - var ecdsaKey = ClientOptions.Value.Key.LoadWif(); - var owner = FrostFsOwner.FromKey(ecdsaKey); - - FrostFsHeaderResult expected = new() - { - HeaderInfo = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - attributes: [new FrostFsAttributePair("fileName", "test")], - split: null, - owner: owner, - version: new FrostFsVersion(2, 13)) - { - PayloadLength = (ulong)objectSize, - PayloadCheckSum = hash - } - }; - - await ValidateHeader(client, containerId, objectId, expected); - _testOutputHelper.WriteLine($"\theader validated"); - - await ValidateContent(client, containerId, hash, objectId); - _testOutputHelper.WriteLine($"\tcontent validated"); - - await ValidateFilters(client, containerId, objectId, null, (ulong)bytes.Length); - _testOutputHelper.WriteLine($"\tfilters validated"); - - if (type != clientCut && bytes.Length > 1024 + 64 && bytes.Length < 20 * 1024 * 1024) - { - // patch payload only - await ValidatePatch(client, containerId, bytes, true, objectId, [], false); - - // patch attributes only - await ValidatePatch(client, containerId, bytes, false, objectId, [new("a1", "v1"), new("a2", "v2")], false); - - // patch payload and attributes - await ValidatePatch(client, containerId, bytes, true, objectId, [new("a3", "v3"), new("a4", "v4")], true); - _testOutputHelper.WriteLine($"\tpatch validated"); - } - - await ValidateRange(client, containerId, bytes, objectId); - _testOutputHelper.WriteLine($"\trange validated"); - - await ValidateRangeHash(client, containerId, bytes, objectId); - _testOutputHelper.WriteLine($"\trange hash validated"); - - await RemoveObject(client, containerId, objectId); - _testOutputHelper.WriteLine($"\tobject removed"); - } - } - } - - private static async Task ValidateRangeHash(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) - { - if (bytes.Length < 200) - return; - - var rangeParam = new PrmRangeHashGet(containerId, objectId, [new FrostFsRange(100, 64)], bytes); - - var hashes = await client.GetRangeHashAsync(rangeParam, default); - - var objectRange = bytes.AsMemory().Slice(100, 64).ToArray(); - var expectedHash = SHA256.HashData(objectRange); - - foreach (var h in hashes) - { - var x = h[..32].ToArray(); - Assert.NotNull(x); - Assert.True(x.Length > 0); - - // Assert.True(expectedHash.SequenceEqual(h.ToArray())); - } - } - - private async Task ValidateRange(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) - { - if (bytes.Length < 100) - return; - - await CheckRange(client, containerId, bytes, objectId, new FrostFsRange(0, 50)); - await CheckRange(client, containerId, bytes, objectId, new FrostFsRange(50, 50)); - - await CheckRange(client, containerId, bytes, objectId, new FrostFsRange((ulong)bytes.Length - 100, 100)); - - if (bytes.Length >= 6200) - await CheckRange(client, containerId, bytes, objectId, new FrostFsRange(6000, 100)); - } - - private async Task CheckRange(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId, FrostFsRange range) - { - var rangeParam = new PrmRangeGet(containerId, objectId, range); - - var rangeReader = await client.GetRangeAsync(rangeParam, default); - - var rangeBytes = new byte[rangeParam.Range.Length]; - MemoryStream ms = new(rangeBytes); - - ReadOnlyMemory? chunk; - int readBytes = 0; - while ((chunk = await rangeReader!.ReadChunk()) != null) - { - readBytes += chunk.Value.Length; - ms.Write(chunk.Value.Span); - } - - Assert.Equal(range.Length, (ulong)readBytes); - - Assert.Equal(SHA256.HashData(bytes.AsSpan().Slice((int)range.Offset, (int)range.Length)), SHA256.HashData(rangeBytes)); - - _testOutputHelper.WriteLine($"\t\trange {range.Offset};{range.Length} validated"); - } - - private static async Task ValidatePatch( - IFrostFSClient client, - FrostFsContainerId containerId, - byte[] bytes, - bool patchPayload, - FrostFsObjectId objectId, - FrostFsAttributePair[] attributes, - bool replaceAttributes) - { - byte[]? patch = null; - FrostFsRange range = new(); - if (patchPayload) - { - patch = new byte[1024]; - for (int i = 0; i < patch.Length; i++) - { - patch[i] = 32; - } - - range = new FrostFsRange(64, (ulong)patch.Length); - } - - var patchParams = new PrmObjectPatch( - new FrostFsAddress(containerId, objectId), - payload: new MemoryStream(patch ?? []), - maxChunkLength: 1024, - range: range, - replaceAttributes: replaceAttributes, - newAttributes: attributes); - - var newIbjId = await client.PatchObjectAsync(patchParams, default); - - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, newIbjId), default); - - if (patchPayload) - { - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - for (int i = 0; i < (int)range.Offset; i++) - Assert.Equal(downloadedBytes[i], bytes[i]); - - var rangeEnd = range.Offset + range.Length; - - for (int i = (int)range.Offset; i < (int)rangeEnd; i++) - Assert.Equal(downloadedBytes[i], patch[i - (int)range.Offset]); - - for (int i = (int)rangeEnd; i < bytes.Length; i++) - Assert.Equal(downloadedBytes[i], bytes[i]); - } - - if (attributes != null && attributes.Length > 0) - { - foreach (var newAttr in attributes) - { - var i = @object!.Header!.Attributes!.Count(p => p.Key == newAttr.Key && p.Value == newAttr.Value); - Assert.Equal(1, i); - } - } - } - - private async Task ValidateFilters(IFrostFSClient client, FrostFsContainerId containerId, FrostFsObjectId objectId, SplitId? splitId, ulong length) - { - var ecdsaKey = keyString.LoadWif(); - - var networkInfo = await client.GetNetmapSnapshotAsync(default); - - await CheckFilter(client, containerId, new FilterByContainerId(FrostFsMatchType.Equals, containerId)); - - await CheckFilter(client, containerId, new FilterByOwnerId(FrostFsMatchType.Equals, FrostFsOwner.FromKey(ecdsaKey))); - - if (splitId != null) - { - await CheckFilter(client, containerId, new FilterBySplitId(FrostFsMatchType.Equals, splitId)); - } - - await CheckFilter(client, containerId, new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test")); - - await CheckFilter(client, containerId, new FilterByObjectId(FrostFsMatchType.Equals, objectId)); - - await CheckFilter(client, containerId, new FilterByVersion(FrostFsMatchType.Equals, networkInfo.NodeInfoCollection[0].Version)); - - await CheckFilter(client, containerId, new FilterByPayloadLength(FrostFsMatchType.Equals, length)); - - await CheckFilter(client, containerId, new FilterByPhysicallyStored()); - } - - private static async Task RemoveObject(IFrostFSClient client, FrostFsContainerId containerId, FrostFsObjectId objectId) - { - await client.DeleteObjectAsync(new PrmObjectDelete(containerId, objectId), default); - - try - { - _ = await client.GetObjectAsync( - new PrmObjectGet(containerId, objectId), - default); - - Assert.Fail("Exception is expected here"); - } - catch (FrostFsResponseException ex) - { - Assert.Equal("object already removed", ex.Status!.Message); - } - } - - private static async Task ValidateContent(IFrostFSClient client, FrostFsContainerId containerId, byte[] hash, FrostFsObjectId objectId) - { - var @object = await client.GetObjectAsync( - new PrmObjectGet(containerId, objectId), - default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(hash, SHA256.HashData(downloadedBytes)); - } - - private static async Task ValidateHeader( - IFrostFSClient client, - FrostFsContainerId containerId, - FrostFsObjectId objectId, - FrostFsHeaderResult expected) - { - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId, default), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - - Assert.Equal(containerId.GetValue(), objHeader.ContainerId.GetValue()); - - Assert.Equal(expected.HeaderInfo!.OwnerId!.Value, objHeader.OwnerId!.Value); - Assert.Equal(expected.HeaderInfo.Version!.Major, objHeader.Version!.Major); - Assert.Equal(expected.HeaderInfo.Version!.Minor, objHeader.Version!.Minor); - - Assert.Equal(expected.HeaderInfo.PayloadLength, objHeader.PayloadLength); - - Assert.Equal(expected.HeaderInfo.ObjectType, objHeader.ObjectType); - - if (expected.HeaderInfo.Attributes != null) - { - Assert.NotNull(objHeader.Attributes); - Assert.Equal(expected.HeaderInfo.Attributes.Count, objHeader.Attributes.Count); - - Assert.True(expected.HeaderInfo.Attributes.SequenceEqual(objHeader.Attributes)); - } - - Assert.Null(objHeader.Split); - } - - private static async Task CreateObjectServerCut(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) - { - var header = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]); - - var param = new PrmObjectPut(header); - - var objectWriter = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await objectWriter.WriteAsync(bytes); - return await objectWriter.CompleteAsync(); - } - - private static async Task CreateObjectClientCut(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) - { - var header = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]); - - var param = new PrmObjectClientCutPut(header, payload: new MemoryStream(bytes)); - - return await client.PutClientCutObjectAsync(param, default).ConfigureAwait(true); - } - - private static async Task PutSingleObject(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) - { - var header = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]); - - - var obj = new FrostFsObject(header) { SingleObjectPayload = bytes }; - - var param = new PrmSingleObjectPut(obj); - - return await client.PutSingleObjectAsync(param, default).ConfigureAwait(true); - } - - private static async Task CheckFilter(IFrostFSClient client, FrostFsContainerId containerId, IObjectFilter filter) - { - var resultObjectsCount = 0; - - PrmObjectSearch searchParam = new(containerId, null, [], filter); - - await foreach (var objId in client.SearchObjectsAsync(searchParam, default)) - { - resultObjectsCount++; - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objId), default); - } - - Assert.True(0 < resultObjectsCount, $"Filter for {filter.Key} doesn't work"); - } -} diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/SessionTests/SessionTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/SessionTests/SessionTests.cs deleted file mode 100644 index ab14d8e..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/Client/SessionTests/SessionTests.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class SessionTests : SmokeTestsBase -{ - [Fact] - public async void GetSessionTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - var token = await client.CreateSessionAsync(new(100), default); - - Assert.NotNull(token); - Assert.NotEqual(Guid.Empty, token.Id); - Assert.Equal(33, token.SessionKey.Length); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs b/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs deleted file mode 100644 index 5e6998b..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; - -using Grpc.Core; - -using Microsoft.Extensions.Options; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public abstract class SmokeTestsBase -{ - // cluster - // internal readonly string url = "http://10.78.128.190:8080"; - // internal readonly string keyString = "L47c3bunc6bJd7uEAfPUae2VkyupFR9nizoH6jfPonzQxijqH2Ba"; - - // WSL2 - internal readonly string url = "http://172.29.238.97:8080"; // "http://172.20.8.23:8080"; - internal readonly string keyString = "KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK"; - - protected ECDsa? Key { get; } - - protected FrostFsOwner? OwnerId { get; } - - protected FrostFsVersion? Version { get; } - - protected CallContext? Ctx { get; } - - protected SmokeTestsBase() - { - Key = keyString.LoadWif(); - OwnerId = FrostFsOwner.FromKey(Key); - Version = new FrostFsVersion(2, 13); - } - - protected static Func GrpcChannel => (url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url)); - - protected IOptions ClientOptions => Options.Create(new ClientSettings { Key = keyString, Host = url }); - - protected static readonly PrmWait lightWait = new(100, 1); - - protected static byte[] GetRandomBytes(int size) - { - Random rnd = new(); - var bytes = new byte[size]; - rnd.NextBytes(bytes); - return bytes; - } - - protected static async Task CreateContainer(IFrostFSClient client, - CallContext ctx, - FrostFsSessionToken? token, - bool unique, - uint backupFactor, - Collection selectors, - Collection filter, - Collection containerAttributes, - params FrostFsReplica[] replicas) - { - ArgumentNullException.ThrowIfNull(client); - - var networkSettings = await client.GetNetworkSettingsAsync(ctx); - - var attributes = containerAttributes ?? []; - - if (networkSettings.HomomorphicHashingDisabled) - attributes.Add(new("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")); - - var containerInfo = new FrostFsContainerInfo( - new FrostFsPlacementPolicy(unique, backupFactor, selectors, filter, replicas), - [.. attributes]); - - var createContainerParam = new PrmContainerCreate( - containerInfo, - PrmWait.DefaultParams, - token, - xheaders: ["key1", "value1"]); - - return await client.PutContainerAsync(createContainerParam, ctx); - } - - protected static async Task Cleanup(IFrostFSClient client) - { - ArgumentNullException.ThrowIfNull(client); - - var tasks = new List(); - await foreach (var cid in client.ListContainersAsync(default, default)) - { - tasks.Add(client.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default)); - } - - await Task.WhenAll(tasks); - } - - protected static async Task AddObjectRules(IFrostFSClient client, FrostFsContainerId containerId) - { - ArgumentNullException.ThrowIfNull(client); - ArgumentNullException.ThrowIfNull(containerId); - - var addChainPrm = new PrmApeChainAdd( - new FrostFsChainTarget(FrostFsTargetType.Container, containerId.GetValue()), - new FrostFsChain - { - ID = Encoding.ASCII.GetBytes("chain-id-test"), - Rules = [ - new FrostFsRule - { - Status = RuleStatus.Allow, - Actions = new Actions(inverted: false, names: ["*"]), - Resources = new Resources (inverted: false, names: [$"native:object/*"]), - Any = false, - Conditions = [] - } - ], - MatchType = RuleMatchType.DenyPriority - } - ); - - await client.AddChainAsync(addChainPrm, default); - - await Task.Delay(8000); - } -} diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_default.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_default.json deleted file mode 100644 index 25675f2..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_default.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "name": "default CBF is 3", - "nodes": [ - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "Moscow" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "DE" - }, - { - "key": "City", - "value": "Berlin" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "FR" - }, - { - "key": "City", - "value": "Paris" - } - ] - } - ], - "tests": [ - { - "name": "set default CBF", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "EU" - } - ], - "containerBackupFactor": 0, - "selectors": [ - { - "name": "EU", - "count": 1, - "clause": "SAME", - "attribute": "Location", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_minimal.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_minimal.json deleted file mode 100644 index 7553dd6..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_minimal.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "name": "Real node count multiplier is in range [1, specified CBF]", - "nodes": [ - { - "attributes": [ - { - "key": "ID", - "value": "1" - }, - { - "key": "Country", - "value": "DE" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "2" - }, - { - "key": "Country", - "value": "DE" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "3" - }, - { - "key": "Country", - "value": "DE" - } - ] - } - ], - "tests": [ - { - "name": "select 2, CBF is 2", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "X" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "X", - "count": 2, - "clause": "SAME", - "attribute": "Country", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2 - ] - ] - }, - { - "name": "select 3, CBF is 2", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "X" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "X", - "count": 3, - "clause": "SAME", - "attribute": "Country", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_requirements.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_requirements.json deleted file mode 100644 index 279d919..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/cbf_requirements.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "name": "CBF requirements", - "nodes": [ - { - "attributes": [ - { - "key": "ID", - "value": "1" - }, - { - "key": "Attr", - "value": "Same" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "2" - }, - { - "key": "Attr", - "value": "Same" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "3" - }, - { - "key": "Attr", - "value": "Same" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "4" - }, - { - "key": "Attr", - "value": "Same" - } - ] - } - ], - "tests": [ - { - "name": "default CBF, no selector", - "policy": { - "replicas": [ - { - "count": 2 - } - ], - "containerBackupFactor": 0, - "selectors": [], - "filters": [] - }, - "result": [ - [ - 0, - 2, - 1, - 3 - ] - ] - }, - { - "name": "explicit CBF, no selector", - "policy": { - "replicas": [ - { - "count": 2 - } - ], - "containerBackupFactor": 3, - "selectors": [], - "filters": [] - }, - "result": [ - [ - 0, - 2, - 1, - 3 - ] - ] - }, - { - "name": "select distinct, weak CBF", - "policy": { - "replicas": [ - { - "count": 2, - "selector": "X" - } - ], - "containerBackupFactor": 3, - "selectors": [ - { - "name": "X", - "count": 2, - "clause": "DISTINCT", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 2, - 1, - 3 - ] - ] - }, - { - "name": "select same, weak CBF", - "policy": { - "replicas": [ - { - "count": 2, - "selector": "X" - } - ], - "containerBackupFactor": 3, - "selectors": [ - { - "name": "X", - "count": 2, - "clause": "SAME", - "attribute": "Attr", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2, - 3 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_complex.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_complex.json deleted file mode 100644 index c2c19c0..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_complex.json +++ /dev/null @@ -1,345 +0,0 @@ -{ - "name": "compound filter", - "nodes": [ - { - "attributes": [ - { - "key": "Storage", - "value": "SSD" - }, - { - "key": "Rating", - "value": "10" - }, - { - "key": "IntField", - "value": "100" - }, - { - "key": "Param", - "value": "Value1" - } - ] - } - ], - "tests": [ - { - "name": "good", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "StorageSSD", - "key": "Storage", - "op": "EQ", - "value": "SSD", - "filters": [] - }, - { - "name": "GoodRating", - "key": "Rating", - "op": "GE", - "value": "4", - "filters": [] - }, - { - "name": "Main", - "op": "AND", - "filters": [ - { - "name": "StorageSSD", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "key": "IntField", - "op": "LT", - "value": "123", - "filters": [] - }, - { - "name": "GoodRating", - "op": "Unspecified", - "filters": [] - }, - { - "op": "OR", - "filters": [ - { - "key": "Param", - "op": "EQ", - "value": "Value1", - "filters": [] - }, - { - "key": "Param", - "op": "EQ", - "value": "Value2", - "filters": [] - } - ] - } - ] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "bad storage type", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "StorageSSD", - "key": "Storage", - "op": "EQ", - "value": "HDD", - "filters": [] - }, - { - "name": "GoodRating", - "key": "Rating", - "op": "GE", - "value": "4", - "filters": [] - }, - { - "name": "Main", - "op": "AND", - "filters": [ - { - "name": "StorageSSD", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "key": "IntField", - "op": "LT", - "value": "123", - "filters": [] - }, - { - "name": "GoodRating", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "op": "OR", - "filters": [ - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value1", - "filters": [] - }, - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value2", - "filters": [] - } - ] - } - ] - } - ] - } - }, - { - "name": "bad rating", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "StorageSSD", - "key": "Storage", - "op": "EQ", - "value": "SSD", - "filters": [] - }, - { - "name": "GoodRating", - "key": "Rating", - "op": "GE", - "value": "15", - "filters": [] - }, - { - "name": "Main", - "op": "AND", - "filters": [ - { - "name": "StorageSSD", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "key": "IntField", - "op": "LT", - "value": "123", - "filters": [] - }, - { - "name": "GoodRating", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "op": "OR", - "filters": [ - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value1", - "filters": [] - }, - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value2", - "filters": [] - } - ] - } - ] - } - ] - } - }, - { - "name": "bad param", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "StorageSSD", - "key": "Storage", - "op": "EQ", - "value": "SSD", - "filters": [] - }, - { - "name": "GoodRating", - "key": "Rating", - "op": "GE", - "value": "4", - "filters": [] - }, - { - "name": "Main", - "op": "AND", - "filters": [ - { - "name": "StorageSSD", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "key": "IntField", - "op": "LT", - "value": "123", - "filters": [] - }, - { - "name": "GoodRating", - "op": "Unspecified", - "filters": [] - }, - { - "name": "", - "op": "OR", - "filters": [ - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value0", - "filters": [] - }, - { - "name": "", - "key": "Param", - "op": "EQ", - "value": "Value2", - "filters": [] - } - ] - } - ] - } - ] - } - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_invalid_integer.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_invalid_integer.json deleted file mode 100644 index 16cdb9e..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_invalid_integer.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "invalid integer field", - "nodes": [ - { - "attributes": [ - { - "key": "IntegerField", - "value": "true" - } - ] - }, - { - "attributes": [ - { - "key": "IntegerField", - "value": "str" - } - ] - } - ], - "tests": [ - { - "name": "empty string is not casted to 0", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "IntegerField", - "op": "LE", - "value": "8", - "filters": [] - } - ] - } - }, - { - "name": "non-empty string is not casted to a number", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "IntegerField", - "op": "GE", - "value": "0", - "filters": [] - } - ] - } - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_simple.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_simple.json deleted file mode 100644 index 43142d4..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/filter_simple.json +++ /dev/null @@ -1,397 +0,0 @@ -{ - "name": "single-op filters", - "nodes": [ - { - "attributes": [ - { - "key": "Rating", - "value": "4" - }, - { - "key": "Country", - "value": "Germany" - } - ] - } - ], - "tests": [ - { - "name": "GE true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "GE", - "value": "4", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "GE false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "GE", - "value": "5", - "filters": [] - } - ] - } - }, - { - "name": "GT true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "GT", - "value": "3", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "GT false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "GT", - "value": "4", - "filters": [] - } - ] - } - }, - { - "name": "LE true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "LE", - "value": "4", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "LE false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "LE", - "value": "3", - "filters": [] - } - ] - } - }, - { - "name": "LT true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "LT", - "value": "5", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "LT false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Rating", - "op": "LT", - "value": "4", - "filters": [] - } - ] - } - }, - { - "name": "EQ true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Country", - "op": "EQ", - "value": "Germany", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "EQ false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Country", - "op": "EQ", - "value": "China", - "filters": [] - } - ] - } - }, - { - "name": "NE true", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Country", - "op": "NE", - "value": "France", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "NE false", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "S" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "S", - "count": 1, - "clause": "DISTINCT", - "filter": "Main" - } - ], - "filters": [ - { - "name": "Main", - "key": "Country", - "op": "NE", - "value": "Germany", - "filters": [] - } - ] - } - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/hrw_sort.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/hrw_sort.json deleted file mode 100644 index a6dc75c..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/hrw_sort.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "name": "HRW ordering", - "nodes": [ - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - }, - { - "key": "Price", - "value": "2" - }, - { - "key": "Capacity", - "value": "10000" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - }, - { - "key": "Price", - "value": "4" - }, - { - "key": "Capacity", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "France" - }, - { - "key": "Price", - "value": "3" - }, - { - "key": "Capacity", - "value": "10" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Price", - "value": "2" - }, - { - "key": "Capacity", - "value": "10000" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Price", - "value": "1" - }, - { - "key": "Capacity", - "value": "10000" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Capacity", - "value": "10000" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "France" - }, - { - "key": "Price", - "value": "100" - }, - { - "key": "Capacity", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "France" - }, - { - "key": "Price", - "value": "7" - }, - { - "key": "Capacity", - "value": "10000" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Price", - "value": "2" - }, - { - "key": "Capacity", - "value": "1" - } - ] - } - ], - "tests": [ - { - "name":"select 3 nodes in 3 distinct countries, same placement", - "policy": { - "containerBackupFactor": 1, - "filters": [], - "replicas": [ - { - "count": 1, - "selector": "Main" - } - ], - "selectors": [ - { - "attribute": "Country", - "clause": "DISTINCT", - "count": 3, - "filter": "*", - "name": "Main" - } - ] - }, - "pivot": "Y29udGFpbmVySUQ=", - "result": [[ - 5, - 0, - 7 - ]], - "placement": { - "pivot": "b2JqZWN0SUQ=", - "result": [[ - 5, - 0, - 7 - ]] - } - }, - { - "name":"select 6 nodes in 3 distinct countries, different placement", - "policy": { - "containerBackupFactor": 2, - "filters": [ - ], - "replicas": [ - { - "count": 1, - "selector": "Main" - } - ], - "selectors": [ - { - "attribute": "Country", - "clause": "DISTINCT", - "count": 3, - "filter": "*", - "name": "Main" - } - ] - }, - "pivot": "Y29udGFpbmVySUQ=", - "result": [[ - 5, - 4, - 0, - 1, - 7, - 2]], - - "placement": { - "pivot": "b2JqZWN0SUQ=", - "result": [[ - 5, - 4, - 0, - 7, - 2, - 1]] - } - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/issue213.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/issue213.json deleted file mode 100644 index 4430297..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/issue213.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "name": "unnamed selector (nspcc-dev/neofs-api-go#213)", - "nodes": [ - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "Russia" - }, - { - "key": "City", - "value": "Moscow" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "Russia" - }, - { - "key": "City", - "value": "Saint-Petersburg" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "Sweden" - }, - { - "key": "City", - "value": "Stockholm" - } - ] - }, - { - "attributes": [ - { - "key": "Location", - "value": "Europe" - }, - { - "key": "Country", - "value": "Finalnd" - }, - { - "key": "City", - "value": "Helsinki" - } - ] - } - ], - "tests": [ - { - "name": "test", - "policy": { - "replicas": [ - { - "count": 4 - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "", - "count": 4, - "clause": "DISTINCT", - "filter": "LOC_EU" - } - ], - "filters": [ - { - "name": "LOC_EU", - "key": "Location", - "op": "EQ", - "value": "Europe", - "filters": [] - } - ] - }, - "result": [ - [ - 0, - 1, - 2, - 3 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/many_selects.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/many_selects.json deleted file mode 100644 index e76a441..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/many_selects.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "name": "single-op filters", - "nodes": [ - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "1" - }, - { - "key": "City", - "value": "SPB" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - }, - { - "key": "Rating", - "value": "5" - }, - { - "key": "City", - "value": "Berlin" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "6" - }, - { - "key": "City", - "value": "Moscow" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "France" - }, - { - "key": "Rating", - "value": "4" - }, - { - "key": "City", - "value": "Paris" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "France" - }, - { - "key": "Rating", - "value": "1" - }, - { - "key": "City", - "value": "Lyon" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "5" - }, - { - "key": "City", - "value": "SPB" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "7" - }, - { - "key": "City", - "value": "Moscow" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - }, - { - "key": "Rating", - "value": "3" - }, - { - "key": "City", - "value": "Darmstadt" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - }, - { - "key": "Rating", - "value": "7" - }, - { - "key": "City", - "value": "Frankfurt" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "9" - }, - { - "key": "City", - "value": "SPB" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - }, - { - "key": "Rating", - "value": "9" - }, - { - "key": "City", - "value": "SPB" - } - ] - } - ], - "tests": [ - { - "name": "Select", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "SameRU" - }, - { - "count": 1, - "selector": "DistinctRU" - }, - { - "count": 1, - "selector": "Good" - }, - { - "count": 1, - "selector": "Main" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "SameRU", - "count": 2, - "clause": "SAME", - "attribute": "City", - "filter": "FromRU" - }, - { - "name": "DistinctRU", - "count": 2, - "clause": "DISTINCT", - "attribute": "City", - "filter": "FromRU" - }, - { - "name": "Good", - "count": 2, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "Good" - }, - { - "name": "Main", - "count": 3, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "*" - } - ], - "filters": [ - { - "name": "FromRU", - "key": "Country", - "op": "EQ", - "value": "Russia" - }, - { - "name": "Good", - "key": "Rating", - "op": "GE", - "value": "4" - } - ] - }, - "result": [ - [ - 0, - 5, - 9, - 10 - ], - [ - 2, - 6, - 0, - 5 - ], - [ - 1, - 8, - 2, - 5 - ], - [ - 3, - 4, - 1, - 7, - 0, - 2 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep.json deleted file mode 100644 index e01ad62..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "name": "multiple replicas (#215)", - "nodes": [ - { - "attributes": [ - { - "key": "City", - "value": "Saint-Petersburg" - } - ] - }, - { - "attributes": [ - { - "key": "City", - "value": "Moscow" - } - ] - }, - { - "attributes": [ - { - "key": "City", - "value": "Berlin" - } - ] - }, - { - "attributes": [ - { - "key": "City", - "value": "Paris" - } - ] - } - ], - "tests": [ - { - "name": "test", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "LOC_SPB_PLACE" - }, - { - "count": 1, - "selector": "LOC_MSK_PLACE" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "LOC_SPB_PLACE", - "count": 1, - "clause": "UNSPECIFIED", - "filter": "LOC_SPB" - }, - { - "name": "LOC_MSK_PLACE", - "count": 1, - "clause": "UNSPECIFIED", - "filter": "LOC_MSK" - } - ], - "filters": [ - { - "name": "LOC_SPB", - "key": "City", - "op": "EQ", - "value": "Saint-Petersburg", - "filters": [] - }, - { - "name": "LOC_MSK", - "key": "City", - "op": "EQ", - "value": "Moscow", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ], - [ - 1 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep_asymmetric.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep_asymmetric.json deleted file mode 100644 index 55390b2..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/multiple_rep_asymmetric.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - "name": "multiple REP, asymmetric", - "nodes": [ - { - "attributes": [ - { - "key": "ID", - "value": "1" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - }, - { - "key": "SSD", - "value": "0" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "2" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "3" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "Moscow" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "4" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "Moscow" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "5" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "6" - }, - { - "key": "Continent", - "value": "NA" - }, - { - "key": "City", - "value": "NewYork" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "7" - }, - { - "key": "Continent", - "value": "AF" - }, - { - "key": "City", - "value": "Cairo" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "8" - }, - { - "key": "Continent", - "value": "AF" - }, - { - "key": "City", - "value": "Cairo" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "9" - }, - { - "key": "Continent", - "value": "SA" - }, - { - "key": "City", - "value": "Lima" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "10" - }, - { - "key": "Continent", - "value": "AF" - }, - { - "key": "City", - "value": "Cairo" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "11" - }, - { - "key": "Continent", - "value": "NA" - }, - { - "key": "City", - "value": "NewYork" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "12" - }, - { - "key": "Continent", - "value": "NA" - }, - { - "key": "City", - "value": "LosAngeles" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "13" - }, - { - "key": "Continent", - "value": "SA" - }, - { - "key": "City", - "value": "Lima" - } - ] - } - ], - "tests": [ - { - "name": "test", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "SPB" - }, - { - "count": 2, - "selector": "Americas" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "SPB", - "count": 1, - "clause": "SAME", - "attribute": "City", - "filter": "SPBSSD" - }, - { - "name": "Americas", - "count": 2, - "clause": "DISTINCT", - "attribute": "City", - "filter": "Americas" - } - ], - "filters": [ - { - "name": "SPBSSD", - "op": "AND", - "filters": [ - { - "name": "", - "key": "Country", - "op": "EQ", - "value": "RU", - "filters": [] - }, - { - "name": "", - "key": "City", - "op": "EQ", - "value": "St.Petersburg", - "filters": [] - }, - { - "name": "", - "key": "SSD", - "op": "EQ", - "value": "1", - "filters": [] - } - ] - }, - { - "name": "Americas", - "op": "OR", - "filters": [ - { - "name": "", - "key": "Continent", - "op": "EQ", - "value": "NA", - "filters": [] - }, - { - "name": "", - "key": "Continent", - "op": "EQ", - "value": "SA", - "filters": [] - } - ] - } - ] - }, - "result": [ - [ - 1, - 4 - ], - [ - 8, - 12, - 5, - 10 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/non_strict.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/non_strict.json deleted file mode 100644 index d8e010e..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/non_strict.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "name": "non-strict selections", - "comment": "These test specify loose selection behaviour, to allow fetching already PUT objects even when there is not enough nodes to select from.", - "nodes": [ - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - } - ] - }, - { - "attributes": [] - } - ], - "tests": [ - { - "name": "not enough nodes (backup factor)", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "MyStore" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "MyStore", - "count": 2, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "FromRU" - } - ], - "filters": [ - { - "name": "FromRU", - "key": "Country", - "op": "EQ", - "value": "Russia", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - }, - { - "name": "not enough nodes (buckets)", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "MyStore" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "MyStore", - "count": 2, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "FromRU" - } - ], - "filters": [ - { - "name": "FromRU", - "key": "Country", - "op": "EQ", - "value": "Russia", - "filters": [] - } - ] - }, - "result": [ - [ - 0 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/rep_only.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/rep_only.json deleted file mode 100644 index 7af5eb0..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/rep_only.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "name": "REP X", - "nodes": [ - { - "publicKey": "", - "addresses": [], - "attributes": [ - { - "key": "City", - "value": "Saint-Petersburg" - } - ], - "state": "Unspecified" - }, - { - "publicKey": "", - "addresses": [], - "attributes": [ - { - "key": "City", - "value": "Moscow" - } - ], - "state": "Unspecified" - }, - { - "publicKey": "", - "addresses": [], - "attributes": [ - { - "key": "City", - "value": "Berlin" - } - ], - "state": "Unspecified" - }, - { - "publicKey": "", - "addresses": [], - "attributes": [ - { - "key": "City", - "value": "Paris" - } - ], - "state": "Unspecified" - } - ], - "tests": [ - { - "name": "REP 1", - "policy": { - "replicas": [ - { - "count": 1 - } - ], - "containerBackupFactor": 0, - "selectors": [], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2 - ] - ] - }, - { - "name": "REP 3", - "policy": { - "replicas": [ - { - "count": 3 - } - ], - "containerBackupFactor": 0, - "selectors": [], - "filters": [] - }, - "result": [ - [ - 0, - 3, - 1, - 2 - ] - ] - }, - { - "name": "REP 5", - "policy": { - "replicas": [ - { - "count": 5 - } - ], - "containerBackupFactor": 0, - "selectors": [], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2, - 3 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/select_no_attribute.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/select_no_attribute.json deleted file mode 100644 index 6a49d68..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/select_no_attribute.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "name": "select with unspecified attribute", - "nodes": [ - { - "attributes": [ - { - "key": "ID", - "value": "1" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - }, - { - "key": "SSD", - "value": "0" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "2" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "St.Petersburg" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "3" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "Moscow" - }, - { - "key": "SSD", - "value": "1" - } - ] - }, - { - "attributes": [ - { - "key": "ID", - "value": "4" - }, - { - "key": "Country", - "value": "RU" - }, - { - "key": "City", - "value": "Moscow" - }, - { - "key": "SSD", - "value": "1" - } - ] - } - ], - "tests": [ - { - "name": "test", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "X" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "X", - "count": 4, - "clause": "DISTINCT", - "filter": "*" - } - ], - "filters": [] - }, - "result": [ - [ - 0, - 1, - 2, - 3 - ] - ] - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/PlacementTests/selector_invalid.json b/src/FrostFS.SDK.Tests/TestData/PlacementTests/selector_invalid.json deleted file mode 100644 index 08e1a8d..0000000 --- a/src/FrostFS.SDK.Tests/TestData/PlacementTests/selector_invalid.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "name": "invalid selections", - "nodes": [ - { - "attributes": [ - { - "key": "Country", - "value": "Russia" - } - ] - }, - { - "attributes": [ - { - "key": "Country", - "value": "Germany" - } - ] - }, - { - "attributes": [] - } - ], - "tests": [ - { - "name": "missing filter", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "MyStore" - } - ], - "containerBackupFactor": 1, - "selectors": [ - { - "name": "MyStore", - "count": 1, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "FromNL" - } - ], - "filters": [ - { - "name": "FromRU", - "key": "Country", - "op": "EQ", - "value": "Russia", - "filters": [] - } - ] - }, - "error": "filter not found" - }, - { - "name": "not enough nodes (filter results in empty set)", - "policy": { - "replicas": [ - { - "count": 1, - "selector": "MyStore" - } - ], - "containerBackupFactor": 2, - "selectors": [ - { - "name": "MyStore", - "count": 2, - "clause": "DISTINCT", - "attribute": "Country", - "filter": "FromMoon" - } - ], - "filters": [ - { - "name": "FromMoon", - "key": "Country", - "op": "EQ", - "value": "Moon", - "filters": [] - } - ] - } - } - ] -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/TestData/wallet.json b/src/FrostFS.SDK.Tests/TestData/wallet.json deleted file mode 100644 index 7a4bdd4..0000000 --- a/src/FrostFS.SDK.Tests/TestData/wallet.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": "1.0", - "accounts": [ - { - "address": "NWeByJPgNC97F83hTUnSbnZSBKaFvk5HNw", - "key": "6PYVCcS2yp89JpcfR61FGhdhhzyYjSErNedmpZErnybNTxUZMRdhzJLrek", - "label": "", - "contract": { - "script": "DCEDJOdiiPy5ABANAYAqFO+XfMpFrQc1YSMERt8Us0TIWLZBVuezJw==", - "parameters": [ - { - "name": "parameter0", - "type": "Signature" - } - ], - "deployed": false - }, - "lock": false, - "isDefault": false - } - ], - "scrypt": { - "n": 16384, - "r": 8, - "p": 8 - }, - "extra": { - "Tokens": null - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/ApeTests.cs b/src/FrostFS.SDK.Tests/Unit/ApeTests.cs deleted file mode 100644 index dea17fb..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ApeTests.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System.Text; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK.Tests.Unit; - -public class ApeTests : ContainerTestsBase -{ - [Fact] - public void ApeRule1Test() - { - var chain = new FrostFsChain - { - ID = Encoding.ASCII.GetBytes("chain-id-test"), - Rules = [ - new FrostFsRule - { - Status = RuleStatus.Allow, - Actions = new Actions(inverted: false, names: ["*"]), - Resources = new Resources (inverted: false, names: [$"native:object/*"]), - Any = false, - Conditions = [] - } - ], - MatchType = RuleMatchType.DenyPriority - }; - - var serialized = RuleSerializer.Serialize(chain); - var restoredChain = RuleSerializer.Deserialize(serialized); - - Assert.True(chain.ID.SequenceEqual(restoredChain.ID)); - - Assert.Equal(chain.MatchType, restoredChain.MatchType); - CompareRules(chain.Rules, restoredChain.Rules); - } - - [Fact] - public void ApeRule2Test() - { - var chain = new FrostFsChain - { - ID = Encoding.ASCII.GetBytes("dumptext"), - Rules = [ - new FrostFsRule - { - Status = RuleStatus.AccessDenied, - Actions = new Actions(inverted: true, names: ["put,get"]), - Resources = new Resources (inverted: true, names: [$"native:object/*,blablabla"]), - Any = true, - Conditions = [ - new () { - Key = "key", - Value = "value", - Kind = ConditionKindType.Resource, - Op = ConditionType.CondStringEquals - }, - new () { - Key = "key1", - Value = "value1", - Kind = ConditionKindType.Request, - Op = ConditionType.CondNumericGreaterThan - } - ] - } - ], - MatchType = RuleMatchType.FirstMatch - }; - - var serialized = RuleSerializer.Serialize(chain); - var restoredChain = RuleSerializer.Deserialize(serialized); - - Assert.True(chain.ID.SequenceEqual(restoredChain.ID)); - - Assert.Equal(chain.MatchType, restoredChain.MatchType); - CompareRules(chain.Rules, restoredChain.Rules); - } - - [Fact] - public void NegativeDeserialize1Test() - { - try - { - _ = RuleSerializer.Deserialize(null); - Assert.Fail("Error is expected"); - } - catch (ArgumentNullException) - { - } - } - - [Fact] - public void NegativeDeserialize2Test() - { - try - { - _ = RuleSerializer.Deserialize([]); - Assert.Fail("Error is expected"); - } - catch (FrostFsException) - { - } - } - - [Fact] - public void NegativeDeserialize3Test() - { - try - { - _ = RuleSerializer.Deserialize([1, 2, 3]); - Assert.Fail("Error is expected"); - } - catch (FrostFsException) - { - } - } - - [Fact] - public void NegativeDeserialize4Test() - { - try - { - //"\x00\x00:aws:iam::namespace:group/so\x82\x82\x82\x82\x82\x82u\x82" - _ = RuleSerializer.Deserialize([0x00, 0x00, 0x3A, 0x77, 0x73, 0x3A, 0x69, 0x61, 0x6D, 0x3A, 0x3A, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3A, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x2F, 0x73, 0x6F, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x75, 0x82]); - Assert.Fail("Error is expected"); - } - catch (FrostFsException) - { - } - } - - private static void CompareRules(FrostFsRule[] rules1, FrostFsRule[] rules2) - { - Assert.NotNull(rules1); - Assert.NotNull(rules2); - - Assert.Equal(rules1.Length, rules2.Length); - - for (int ri = 0; ri < rules1.Length; ri++) - { - var rule1 = rules1[ri]; - var rule2 = rules2[ri]; - - Assert.Equal(rule1.Status, rule2.Status); - Assert.Equal(rule1.Any, rule2.Any); - - Assert.Equal(rule1.Actions, rule2.Actions); - - Assert.Equal(rule1.Resources, rule2.Resources); - - bool cond1Empty = rule1.Conditions == null || rule1.Conditions.Length == 0; - bool cond2Empty = rule2.Conditions == null || rule2.Conditions.Length == 0; - - if (cond1Empty && cond2Empty) - { - return; - } - - Assert.Equal(cond1Empty, cond2Empty); - - Assert.Equal(rule1.Conditions!.Length, rule2.Conditions!.Length); - - for (int i = 0; i < rule1.Conditions.Length; i++) - { - Assert.Equal(rule1.Conditions[i], rule2.Conditions[i]); - } - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs deleted file mode 100644 index ce1d06c..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FrostFS.Netmap; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; - -using Google.Protobuf; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class ContainerTest : ContainerTestsBase -{ - [Theory] - [InlineData(1, "test", 0, 0)] - - public void ReplicaToMessagelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) - { - FrostFsReplica replica = new() - { - Count = count, - Selector = selector, - EcDataCount = ecDataCount, - EcParityCount = ecParityCount - }; - - Replica message = replica.ToMessage(); - - Assert.Equal(count, message.Count); - Assert.Equal(selector, message.Selector); - Assert.Equal(ecDataCount, message.EcDataCount); - Assert.Equal(ecParityCount, message.EcParityCount); - } - - [Fact] - public async void CreateContainerTest() - { - var param = new PrmContainerCreate(new FrostFsContainerInfo(Mocker.PlacementPolicy), PrmWait.DefaultParams); - - var result = await GetClient().PutContainerAsync(param, default); - - Assert.NotNull(result); - Assert.NotNull(result.GetValue()); - - var bytes = Mocker.ContainerGuid.ToByteArray(true); - - Assert.True(Base58.Encode(new Span(bytes)) == result.GetValue()); - } - - [Fact] - public async void GetContainerTest() - { - var cid = new FrostFsContainerId(Base58.Encode(new Span(Mocker.ContainerGuid.ToByteArray(true)))); - - var result = await GetClient().GetContainerAsync(new PrmContainerGet(cid), default); - - Assert.NotNull(result); - Assert.Equal(Mocker.ContainerGuid, result.Nonce); - Assert.Equal(Mocker.PlacementPolicy, result.PlacementPolicy); - Assert.Equal(Mocker.Version.ToString(), result.Version!.ToString()); - } - - [Fact] - public async void GetContainerListTest() - { - Mocker.ContainerIds.Add([0xaa]); - Mocker.ContainerIds.Add([0xbb]); - Mocker.ContainerIds.Add([0xcc]); - - var result = GetClient().ListContainersAsync(default, default); - - Assert.NotNull(result); - - int i = 0; - await foreach (var cid in result) - { - var val = Base58.Encode(ByteString.CopyFrom(Mocker.ContainerIds[i++]).ToByteArray()); - Assert.Equal(val, cid.GetValue()); - } - - Assert.Equal(3, i); - } - - [Fact] - public async void DeleteContainerAsyncTest() - { - Mocker.ReturnContainerRemoved = true; - var cid = new FrostFsContainerId(Base58.Encode(new Span(Mocker.ContainerGuid.ToByteArray(true)))); - - await GetClient().DeleteContainerAsync(new PrmContainerDelete(cid, PrmWait.DefaultParams), default); - - Assert.Single(Mocker.Requests); - - var request = Mocker.Requests.First(); - - Assert.Equal(cid.ToMessage(), request.Request.Body.ContainerId); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/ContainerTestsBase.cs b/src/FrostFS.SDK.Tests/Unit/ContainerTestsBase.cs deleted file mode 100644 index 60579f6..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ContainerTestsBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using FrostFS.SDK.Client.Interfaces; - -using Microsoft.Extensions.Options; - -namespace FrostFS.SDK.Tests.Unit; - -public abstract class ContainerTestsBase -{ - internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - - protected IOptions Settings { get; set; } - protected ContainerMocker Mocker { get; set; } - - protected ContainerTestsBase() - { - Settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); - - Mocker = new ContainerMocker(key) - { - PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), - Version = new FrostFsVersion(2, 13), - ContainerGuid = Guid.NewGuid() - }; - } - - protected IFrostFSClient GetClient() - { - return Client.FrostFSClient.GetTestInstance( - Settings, - (url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url)), - new NetworkMocker(key).GetMock().Object, - new SessionMocker(key).GetMock().Object, - Mocker.GetMock().Object, - new ObjectMocker(key).GetMock().Object); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs b/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs deleted file mode 100644 index 3a71d78..0000000 --- a/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK.Tests.Unit; - -public class MetaheaderTests -{ - [Theory] - [InlineData(2, 13, 1, 1)] - [InlineData(200, 0, 1000000, 8)] - public void MetaheaderTest(int major, int minor, int epoch, int ttl) - { - MetaHeader metaHeader = new MetaHeader( - new FrostFsVersion( - major: 2, - minor: 13 - ), - epoch: 0, - ttl: 2 - ); - - var result = metaHeader.ToMessage(); - - Assert.Equal((ulong)metaHeader.Epoch, result.Epoch); - Assert.Equal((ulong)metaHeader.Ttl, result.Ttl); - Assert.Equal((ulong)metaHeader.Version.Major, result.Version.Major); - Assert.Equal((ulong)metaHeader.Version.Minor, result.Version.Minor); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs b/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs deleted file mode 100644 index 233e12e..0000000 --- a/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs +++ /dev/null @@ -1,101 +0,0 @@ -using FrostFS.Netmap; -using FrostFS.SDK.Client; - -using Google.Protobuf; - -namespace FrostFS.SDK.Tests.Unit; - -public class NetmapSnapshotTests : NetworkTestsBase -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - - public async void NetmapSnapshotTest(bool useContext) - { - var body = new NetmapSnapshotResponse.Types.Body - { - Netmap = new Netmap.Netmap { Epoch = 99 } - }; - - var nodeInfo1 = new NodeInfo - { - State = NodeInfo.Types.State.Online, - PublicKey = ByteString.CopyFrom([1, 2, 3]) - }; - - nodeInfo1.Addresses.Add("address1"); - nodeInfo1.Addresses.Add("address2"); - nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); - nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); - - var nodeInfo2 = new NodeInfo - { - State = NodeInfo.Types.State.Offline, - PublicKey = ByteString.CopyFrom([3, 4, 5]) - }; - - nodeInfo2.Addresses.Add("address3"); - nodeInfo2.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key3", Value = "value3" }); - - body.Netmap.Nodes.Add(nodeInfo1); - body.Netmap.Nodes.Add(nodeInfo2); - - Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body }; - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNetmapSnapshotAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(99u, result.Epoch); - Assert.Equal(2, result.NodeInfoCollection.Count); - - var node1 = result.NodeInfoCollection[0]; - Assert.Equal(NodeState.Online, node1.State); - Assert.Equal(2, node1.Addresses.Count); - Assert.Equal("address1", node1.Addresses.ElementAt(0)); - Assert.Equal("address2", node1.Addresses.ElementAt(1)); - - Assert.Equal(2, node1.Attributes.Count); - - Assert.Equal("key1", node1.Attributes.ElementAt(0).Key); - Assert.Equal("value1", node1.Attributes.ElementAt(0).Value); - Assert.Equal("key2", node1.Attributes.ElementAt(1).Key); - Assert.Equal("value2", node1.Attributes.ElementAt(1).Value); - - var node2 = result.NodeInfoCollection[1]; - Assert.Equal(NodeState.Offline, node2.State); - Assert.Single(node2.Addresses); - Assert.Equal("address3", node2.Addresses.ElementAt(0)); - - Assert.Single(node2.Attributes); - - Assert.Equal("key3", node2.Attributes.ElementAt(0).Key); - Assert.Equal("value3", node2.Attributes.ElementAt(0).Value); - - if (useContext) - { - Assert.NotNull(Mocker.NetmapSnapshotRequest); - Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); - - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.NotNull(Mocker.NetmapSnapshotRequest); - Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs b/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs deleted file mode 100644 index 3f3fb2f..0000000 --- a/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class NetworkSettingsTests : NetworkTestsBase -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void NetworkSettingsTest(bool useContext) - { - Mocker.Parameters.Add("AuditFee", [1]); - Mocker.Parameters.Add("BasicIncomeRate", [2]); - Mocker.Parameters.Add("ContainerFee", [3]); - Mocker.Parameters.Add("ContainerAliasFee", [4]); - Mocker.Parameters.Add("EpochDuration", [5]); - Mocker.Parameters.Add("InnerRingCandidateFee", [6]); - Mocker.Parameters.Add("MaxECDataCount", [7]); - Mocker.Parameters.Add("MaxECParityCount", [8]); - Mocker.Parameters.Add("MaxObjectSize", [9]); - Mocker.Parameters.Add("WithdrawFee", [10]); - Mocker.Parameters.Add("HomomorphicHashingDisabled", [1]); - Mocker.Parameters.Add("MaintenanceModeAllowed", [1]); - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNetworkSettingsAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(Mocker.Parameters["AuditFee"], [(byte)result.AuditFee]); - Assert.Equal(Mocker.Parameters["BasicIncomeRate"], [(byte)result.BasicIncomeRate]); - Assert.Equal(Mocker.Parameters["ContainerFee"], [(byte)result.ContainerFee]); - Assert.Equal(Mocker.Parameters["ContainerAliasFee"], [(byte)result.ContainerAliasFee]); - Assert.Equal(Mocker.Parameters["EpochDuration"], [(byte)result.EpochDuration]); - Assert.Equal(Mocker.Parameters["InnerRingCandidateFee"], [(byte)result.InnerRingCandidateFee]); - Assert.Equal(Mocker.Parameters["MaxECDataCount"], [(byte)result.MaxECDataCount]); - Assert.Equal(Mocker.Parameters["MaxECParityCount"], [(byte)result.MaxECParityCount]); - Assert.Equal(Mocker.Parameters["MaxObjectSize"], [(byte)result.MaxObjectSize]); - Assert.Equal(Mocker.Parameters["WithdrawFee"], [(byte)result.WithdrawFee]); - - Assert.True(result.HomomorphicHashingDisabled); - Assert.True(result.MaintenanceModeAllowed); - - if (useContext) - { - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.NotNull(Mocker.NetworkInfoRequest); - Assert.Empty(Mocker.NetworkInfoRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/NetworkTestsBase.cs b/src/FrostFS.SDK.Tests/Unit/NetworkTestsBase.cs deleted file mode 100644 index 9452220..0000000 --- a/src/FrostFS.SDK.Tests/Unit/NetworkTestsBase.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using FrostFS.SDK.Client.Interfaces; - -using Microsoft.Extensions.Options; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public abstract class NetworkTestsBase -{ - internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - - // protected FrostFsVersion Version { get; set; } = new FrostFsVersion(2, 13); - - protected NetworkMocker Mocker { get; set; } - - protected ClientSettings DefaultSettings { get; } - - protected NetworkTestsBase() - { - DefaultSettings = new ClientSettings - { - Key = key, - Host = "http://localhost:8080", - }; - - Mocker = new NetworkMocker(key); - } - - protected IFrostFSClient GetClient(ClientSettings settings) - { - return Client.FrostFSClient.GetTestInstance( - Options.Create(settings), - (url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url)), - Mocker.GetMock().Object, - new SessionMocker(key).GetMock().Object, - new ContainerMocker(key).GetMock().Object, - new ObjectMocker(key).GetMock().Object); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs b/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs deleted file mode 100644 index 6449beb..0000000 --- a/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -using FrostFS.Netmap; -using FrostFS.SDK.Client; -using Google.Protobuf; - -namespace FrostFS.SDK.Tests.Unit; - -public class NodeInfoTests : NetworkTestsBase -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void NodeInfoTest(bool useContext) - { - var body = new LocalNodeInfoResponse.Types.Body - { - NodeInfo = new NodeInfo() - { - State = NodeInfo.Types.State.Online, - PublicKey = ByteString.CopyFrom([1, 2, 3]) - }, - Version = new Refs.Version { Major = 2, Minor = 12 } - }; - - body.NodeInfo.Addresses.Add("address1"); - body.NodeInfo.Addresses.Add("address2"); - body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); - body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); - - Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body }; - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNodeInfoAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(NodeState.Online, result.State); - - Assert.Equal(2, result.Addresses.Count); - Assert.Equal("address1", result.Addresses.ElementAt(0)); - Assert.Equal("address2", result.Addresses.ElementAt(1)); - - Assert.Equal(2, result.Attributes.Count); - Assert.Equal("value1", result.Attributes["key1"]); - Assert.Equal("value2", result.Attributes["key2"]); - - Assert.NotNull(Mocker.LocalNodeInfoRequest); - if (useContext) - { - Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs b/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs deleted file mode 100644 index 985f02e..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs +++ /dev/null @@ -1,639 +0,0 @@ -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text; - -using FrostFS.Refs; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using Google.Protobuf; -using Org.BouncyCastle.Utilities; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class ObjectTest : ObjectTestsBase -{ - [Fact] - public async void PutObjectTest() - { - Mocker.ResultObjectIds!.Add(SHA256.HashData([])); - - Random rnd = new(); - var bytes = new byte[1024]; - rnd.NextBytes(bytes); - - var param = new PrmObjectPut(Mocker.ObjectHeader); - - var stream = await GetClient().PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var result = await stream.CompleteAsync(); - - var sentMessages = Mocker.ClientStreamWriter!.Messages; - - var body1 = sentMessages.ElementAt(0).GetBody() as Object.PutRequest.Types.Body; - var body2 = sentMessages.ElementAt(1).GetBody() as Object.PutRequest.Types.Body; - - Assert.NotNull(result); - Assert.Equal(Mocker.ResultObjectIds.First(), result.ToHash()); - - Assert.True(Mocker.ClientStreamWriter.CompletedTask); - - Assert.Equal(0, body1!.Chunk.Length); - Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Init, body1!.ObjectPartCase); - - Assert.Equal(1024, body2!.Chunk.Length); - Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Chunk, body2!.ObjectPartCase); - } - - [Fact] - public async void ClientCutTest() - { - NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); - - var blockSize = 2560; - byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); - var fileLength = bytes.Length; - - var param = new PrmObjectClientCutPut( - Mocker.ObjectHeader, - payload: new MemoryStream(bytes), - bufferMaxSize: blockSize); - - Random rnd = new(); - - Collection objIds = new([new byte[32], new byte[32], new byte[32]]); - rnd.NextBytes(objIds.ElementAt(0)); - rnd.NextBytes(objIds.ElementAt(1)); - rnd.NextBytes(objIds.ElementAt(2)); - - foreach (var objId in objIds) - Mocker.ResultObjectIds!.Add(objId); - - var result = await GetClient().PutClientCutObjectAsync(param, default); - - var singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.NotNull(Mocker.ClientStreamWriter?.Messages); - - var objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); - - Assert.Equal(4, objects.Length); - - // linked object - Assert.Equal(0, objects[3].Payload.Length); - - // PART1 - Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); - - Assert.NotNull(objects[0].Header.Split.SplitId); - Assert.Null(objects[0].Header.Split.Previous); - Assert.True(objects[0].Header.Attributes.Count == 0); - Assert.Null(objects[0].Header.Split.Parent); - - // PART2 - Assert.Equal(blockSize, objects[1].Payload.Length); - Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); - - Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); - Assert.True(objects[1].Header.Attributes.Count == 0); - Assert.Null(objects[1].Header.Split.Parent); - - // last part - Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); - Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); - - Assert.NotNull(objects[3].Header.Split.Parent); - Assert.NotNull(objects[3].Header.Split.ParentHeader); - Assert.NotNull(objects[3].Header.Split.ParentSignature); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.True(objects[2].Header.Attributes.Count == 0); - - // link object - Assert.Equal(objects[2].Header.Split.Parent, objects[3].Header.Split.Parent); - Assert.Equal(objects[2].Header.Split.ParentHeader, objects[3].Header.Split.ParentHeader); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.Equal(0, (int)objects[3].Header.PayloadLength); - Assert.True(objects[3].Header.Attributes.Count == 0); - - Assert.Single(objects[3].Header.Split.ParentHeader.Attributes); - Assert.Equal("k", objects[3].Header.Split.ParentHeader.Attributes[0].Key); - Assert.Equal("v", objects[3].Header.Split.ParentHeader.Attributes[0].Value); - - var modelObjId = FrostFsObjectId.FromHash(objects[3].Header.Split.Parent.Value.ToByteArray()); - - Assert.Equal(result.Value, modelObjId.ToString()); - } - - [Fact] - public async void ClientCutWithInterruptionOnFirstPartTest() - { - NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); - - var blockSize = 2560; - byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); - var fileLength = bytes.Length; - - var splitId = Guid.NewGuid(); - var progress = new UploadProgressInfo(splitId); - - var param = new PrmObjectClientCutPut( - Mocker.ObjectHeader, - payload: new MemoryStream(bytes), - bufferMaxSize: blockSize, - progress: progress); - - Random rnd = new(); - - Collection objIds = new([new byte[32], new byte[32], new byte[32]]); - rnd.NextBytes(objIds.ElementAt(0)); - rnd.NextBytes(objIds.ElementAt(1)); - rnd.NextBytes(objIds.ElementAt(2)); - - foreach (var objId in objIds) - Mocker.ResultObjectIds!.Add(objId); - - int sentBlockCount = 0; - Mocker.Callback = () => - { - if (++sentBlockCount == 1) - throw new FrostFsException("some error"); - }; - - bool gotException = false; - try - { - var result = await GetClient().PutClientCutObjectAsync(param, default); - } - catch (FrostFsException ex) - { - if (ex.Message == "some error") - gotException = true; - } - - Assert.True(gotException); - - var singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.Empty(singleObjects); - } - - [Fact] - public async void ClientCutWithInterruptionOnMiddlePartTest() - { - NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); - - var blockSize = 2560; - byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); - var fileLength = bytes.Length; - - var splitId = Guid.Parse("67e4bbe9-86ca-474d-9385-6569ce89db61"); - var progress = new UploadProgressInfo(splitId); - - var param = new PrmObjectClientCutPut( - Mocker.ObjectHeader, - payload: new MemoryStream(bytes), - bufferMaxSize: blockSize, - progress: progress); - - Random rnd = new(); - - Collection objIds = new([new byte[32], new byte[32], new byte[32]]); - rnd.NextBytes(objIds.ElementAt(0)); - rnd.NextBytes(objIds.ElementAt(1)); - rnd.NextBytes(objIds.ElementAt(2)); - - foreach (var objId in objIds) - Mocker.ResultObjectIds!.Add(objId); - - int sentBlockCount = 0; - Mocker.Callback = () => - { - if (++sentBlockCount == 2) - throw new FrostFsException("some error"); - }; - - bool gotException = false; - try - { - _ = await GetClient().PutClientCutObjectAsync(param, default); - } - catch (FrostFsException ex) - { - if (ex.Message == "some error") - gotException = true; - } - - Assert.True(gotException); - - var singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.NotNull(Mocker.ClientStreamWriter?.Messages); - - var objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); - - Assert.Single(objects); - - Assert.Single(progress.GetParts()); - - var part = progress.GetPart(0); - Assert.Equal(0, part.Offset); - Assert.Equal(2560, part.Length); - - // PART1 - Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); - - Assert.NotNull(objects[0].Header.Split.SplitId); - Assert.Null(objects[0].Header.Split.Previous); - Assert.True(objects[0].Header.Attributes.Count == 0); - Assert.Null(objects[0].Header.Split.Parent); - - // resume uploading - sentBlockCount = 10; - - var result = await GetClient().PutClientCutObjectAsync(param, default); - - singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.NotNull(Mocker.ClientStreamWriter?.Messages); - - objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); - - Assert.Equal(4, objects.Length); - - // PART1 - Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); - - Assert.NotNull(objects[0].Header.Split.SplitId); - Assert.Null(objects[0].Header.Split.Previous); - Assert.True(objects[0].Header.Attributes.Count == 0); - Assert.Null(objects[0].Header.Split.Parent); - - // PART2 - Assert.Equal(blockSize, objects[1].Payload.Length); - Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); - - Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); - Assert.True(objects[1].Header.Attributes.Count == 0); - Assert.Null(objects[1].Header.Split.Parent); - - // last part - Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); - Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); - - Assert.NotNull(objects[3].Header.Split.Parent); - Assert.NotNull(objects[3].Header.Split.ParentHeader); - Assert.NotNull(objects[3].Header.Split.ParentSignature); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.True(objects[2].Header.Attributes.Count == 0); - - // link object - Assert.Equal(objects[2].Header.Split.Parent, objects[3].Header.Split.Parent); - Assert.Equal(objects[2].Header.Split.ParentHeader, objects[3].Header.Split.ParentHeader); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.Equal(0, (int)objects[3].Header.PayloadLength); - Assert.True(objects[3].Header.Attributes.Count == 0); - - Assert.Single(objects[3].Header.Split.ParentHeader.Attributes); - Assert.Equal("k", objects[3].Header.Split.ParentHeader.Attributes[0].Key); - Assert.Equal("v", objects[3].Header.Split.ParentHeader.Attributes[0].Value); - - var modelObjId = FrostFsObjectId.FromHash(objects[3].Header.Split.Parent.Value.ToByteArray()); - - Assert.Equal(result.Value, modelObjId.ToString()); - - Assert.Equal(3, progress.GetParts().Count); - part = progress.GetPart(0); - Assert.Equal(0, part.Offset); - Assert.Equal(2560, part.Length); - - part = progress.GetPart(1); - Assert.Equal(2560, part.Offset); - Assert.Equal(2560, part.Length); - - part = progress.GetPart(2); - Assert.Equal(2 * 2560, part.Offset); - Assert.Equal(1620, part.Length); - } - - [Fact] - public async void ClientCutWithInterruptionOnLastPartTest() - { - NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); - - var blockSize = 2560; - byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); - var fileLength = bytes.Length; - - var splitId = Guid.Parse("67e4bbe9-86ca-474d-9385-6569ce89db61"); - var progress = new UploadProgressInfo(splitId); - - var param = new PrmObjectClientCutPut( - Mocker.ObjectHeader, - payload: new MemoryStream(bytes), - bufferMaxSize: blockSize, - progress: progress); - - Random rnd = new(); - - Collection objIds = new([new byte[32], new byte[32], new byte[32]]); - rnd.NextBytes(objIds.ElementAt(0)); - rnd.NextBytes(objIds.ElementAt(1)); - rnd.NextBytes(objIds.ElementAt(2)); - - foreach (var objId in objIds) - Mocker.ResultObjectIds!.Add(objId); - - int sentBlockCount = 0; - Mocker.Callback = () => - { - if (++sentBlockCount == 3) - throw new FrostFsException("some error"); - }; - - bool gotException = false; - try - { - _ = await GetClient().PutClientCutObjectAsync(param, default); - } - catch (FrostFsException ex) - { - if (ex.Message == "some error") - gotException = true; - } - - Assert.True(gotException); - - var singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.NotNull(Mocker.ClientStreamWriter?.Messages); - - var objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); - - Assert.Equal(2, objects.Length); - - Assert.Equal(2, progress.GetParts().Count); - - var part = progress.GetPart(0); - Assert.Equal(0, part.Offset); - Assert.Equal(2560, part.Length); - - part = progress.GetPart(1); - Assert.Equal(2560, part.Offset); - Assert.Equal(2560, part.Length); - - // PART1 - Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); - - Assert.NotNull(objects[0].Header.Split.SplitId); - Assert.Null(objects[0].Header.Split.Previous); - Assert.True(objects[0].Header.Attributes.Count == 0); - Assert.Null(objects[0].Header.Split.Parent); - - // PART2 - Assert.Equal(blockSize, objects[1].Payload.Length); - Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); - - Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); - Assert.True(objects[1].Header.Attributes.Count == 0); - Assert.Null(objects[1].Header.Split.Parent); - - // resume uploading - sentBlockCount = 10; - - var result = await GetClient().PutClientCutObjectAsync(param, default); - - singleObjects = Mocker.PutSingleRequests.ToArray(); - - Assert.NotNull(Mocker.ClientStreamWriter?.Messages); - - objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); - - Assert.Equal(4, objects.Length); - - // PART1 - Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); - - Assert.NotNull(objects[0].Header.Split.SplitId); - Assert.Null(objects[0].Header.Split.Previous); - Assert.True(objects[0].Header.Attributes.Count == 0); - Assert.Null(objects[0].Header.Split.Parent); - - // PART2 - Assert.Equal(blockSize, objects[1].Payload.Length); - Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); - - Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); - Assert.True(objects[1].Header.Attributes.Count == 0); - Assert.Null(objects[1].Header.Split.Parent); - - // last part - Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); - Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); - - Assert.NotNull(objects[3].Header.Split.Parent); - Assert.NotNull(objects[3].Header.Split.ParentHeader); - Assert.NotNull(objects[3].Header.Split.ParentSignature); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.True(objects[2].Header.Attributes.Count == 0); - - // link object - Assert.Equal(objects[2].Header.Split.Parent, objects[3].Header.Split.Parent); - Assert.Equal(objects[2].Header.Split.ParentHeader, objects[3].Header.Split.ParentHeader); - Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); - Assert.Equal(0, (int)objects[3].Header.PayloadLength); - Assert.True(objects[3].Header.Attributes.Count == 0); - - Assert.Single(objects[3].Header.Split.ParentHeader.Attributes); - Assert.Equal("k", objects[3].Header.Split.ParentHeader.Attributes[0].Key); - Assert.Equal("v", objects[3].Header.Split.ParentHeader.Attributes[0].Value); - - var modelObjId = FrostFsObjectId.FromHash(objects[3].Header.Split.Parent.Value.ToByteArray()); - - Assert.Equal(result.Value, modelObjId.ToString()); - - Assert.Equal(3, progress.GetParts().Count); - part = progress.GetPart(0); - Assert.Equal(0, part.Offset); - Assert.Equal(2560, part.Length); - - part = progress.GetPart(1); - Assert.Equal(2560, part.Offset); - Assert.Equal(2560, part.Length); - - part = progress.GetPart(2); - Assert.Equal(2 * 2560, part.Offset); - Assert.Equal(1620, part.Length); - } - - [Fact] - public async void DeleteObject() - { - Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); - - await GetClient().DeleteObjectAsync(new PrmObjectDelete(ContainerId, Mocker.ObjectId), default); - - var request = Mocker.DeleteRequests.FirstOrDefault(); - Assert.NotNull(request); - Assert.Equal(ContainerId.ToMessage().Value, request.Body.Address.ContainerId.Value); - Assert.Equal(Mocker.ObjectId.ToMessage().Value, request.Body.Address.ObjectId.Value); - } - - [Fact] - public async void GetHeaderTest() - { - Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); - - var res = await GetClient().GetObjectHeadAsync(new PrmObjectHeadGet(ContainerId, Mocker.ObjectId), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - - var request = Mocker.HeadRequests.FirstOrDefault(); - Assert.NotNull(request); - Assert.Equal(ContainerId.ToMessage(), request.Body.Address.ContainerId); - Assert.Equal(Mocker.ObjectId.ToMessage(), request.Body.Address.ObjectId); - - Assert.NotNull(objHeader); - Assert.Equal(ContainerId.GetValue(), objHeader.ContainerId.GetValue()); - - Assert.Equal(Mocker.ObjectHeader!.OwnerId!.Value, objHeader.OwnerId!.Value); - Assert.Equal(Mocker.ObjectHeader!.Version!.ToString(), objHeader.Version!.ToString()); - - Assert.Equal(Mocker.HeadResponse!.PayloadLength, objHeader.PayloadLength); - - Assert.Equal(FrostFsObjectType.Regular, objHeader.ObjectType); - - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - - Assert.Equal(Mocker.HeadResponse.Attributes[0].Key, objHeader.Attributes.First().Key); - Assert.Equal(Mocker.HeadResponse.Attributes[0].Value, objHeader.Attributes.First().Value); - - Assert.Null(objHeader.Split); - } - - [Fact] - public async void GetRangeTest() - { - Mocker.ResultObjectIds!.Add(SHA256.HashData([])); - - Random rnd = new(); - var bytes = new byte[1024]; - rnd.NextBytes(bytes); - - Mocker.RangeResponse = bytes; - - Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); - - var param = new PrmRangeGet(ContainerId, Mocker.ObjectId, new FrostFsRange(100, (ulong)Mocker.RangeResponse.Length)); - - var result = await GetClient().GetRangeAsync(param, default); - - Assert.NotNull(Mocker.GetRangeRequest); - - Assert.Equal(param.Range.Offset, Mocker.GetRangeRequest.Body.Range.Offset); - Assert.Equal(param.Range.Length, Mocker.GetRangeRequest.Body.Range.Length); - - Assert.NotNull(result); - - var chunk = await result.ReadChunk(); - - var chunkBytes = chunk.Value.Span.ToArray(); - - Assert.Equal(chunkBytes.Length, Mocker.RangeResponse.Length); - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(Mocker.RangeResponse)); - } - - [Fact] - public async void GetRangeHashTest() - { - Mocker.ResultObjectIds!.Add(SHA256.HashData([])); - - Random rnd = new(); - var bytes = new byte[1024]; - rnd.NextBytes(bytes); - - var salt = new byte[32]; - rnd.NextBytes(salt); - - var hash = new byte[32]; - rnd.NextBytes(hash); - - Mocker.RangeResponse = bytes; - var len = (ulong)bytes.Length; - - Mocker.RangeHashResponses.Add(ByteString.CopyFrom(hash)); - - Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); - - var param = new PrmRangeHashGet(ContainerId, Mocker.ObjectId, [new FrostFsRange(100, len)], salt); - - var result = await GetClient().GetRangeHashAsync(param, default); - - Assert.NotNull(Mocker.GetRangeHashRequest); - - Assert.Equal(param.Ranges[0].Offset, Mocker.GetRangeHashRequest.Body.Ranges[0].Offset); - Assert.Equal(param.Ranges[0].Length, Mocker.GetRangeHashRequest.Body.Ranges[0].Length); - - Assert.NotNull(result); - Assert.Single(result); - - Assert.Equal(SHA256.HashData(hash), SHA256.HashData(result.First().ToArray())); - } - - [Fact] - public async void PatchTest() - { - Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); - - var address = new FrostFsAddress(ContainerId, Mocker.ObjectId); - - Mocker.ResultObjectIds!.Add(SHA256.HashData([])); - - Random rnd = new(); - var patch = new byte[32]; - rnd.NextBytes(patch); - - var range = new FrostFsRange(8, (ulong)patch.Length); - - var param = new PrmObjectPatch( - address, - payload: new MemoryStream(patch), - maxChunkLength: 32, - range: range); - - var result = await GetClient().PatchObjectAsync(param, default); - - Assert.NotNull(result); - - Assert.NotNull(result.Value); - - Assert.NotNull(Mocker.PatchStreamWriter); - Assert.Single(Mocker.PatchStreamWriter.Messages); - - var sentMessages = Mocker.PatchStreamWriter!.Messages; - - var body = sentMessages.First().GetBody() as Object.PatchRequest.Types.Body; - - Assert.NotNull(body); - - Assert.True(Mocker.PatchStreamWriter.CompletedTask); - - Assert.Equal(address.ContainerId, body.Address.ContainerId); - Assert.Equal(address.ObjectId, body.Address.ObjectId); - - Assert.Equal(32, body.Patch.Chunk.Length); - - Assert.Equal(SHA256.HashData(patch), SHA256.HashData(body.Patch.Chunk.ToArray())); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/ObjectTestsBase.cs b/src/FrostFS.SDK.Tests/Unit/ObjectTestsBase.cs deleted file mode 100644 index c3681ce..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ObjectTestsBase.cs +++ /dev/null @@ -1,59 +0,0 @@ -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; - -using Microsoft.Extensions.Options; - -namespace FrostFS.SDK.Tests.Unit; - -public abstract class ObjectTestsBase -{ - protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - - protected IOptions Settings { get; set; } - protected FrostFsContainerId ContainerId { get; set; } - - protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key); - protected SessionMocker SessionMocker { get; set; } = new SessionMocker(key); - protected ContainerMocker ContainerMocker { get; set; } = new ContainerMocker(key); - protected ObjectMocker Mocker { get; set; } - - protected ObjectTestsBase() - { - var ecdsaKey = key.LoadWif(); - - Settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); - - Mocker = new ObjectMocker(key) - { - PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), - Version = new FrostFsVersion(2, 13), - ContainerGuid = Guid.NewGuid() - }; - - ContainerId = new FrostFsContainerId(Base58.Encode(Mocker.ContainerGuid.ToByteArray(true))); - - Mocker.ObjectHeader = new( - ContainerId, - FrostFsObjectType.Regular, - [new FrostFsAttributePair("k", "v")], - null, - FrostFsOwner.FromKey(ecdsaKey), - new FrostFsVersion(2, 13)); - } - - protected IFrostFSClient GetClient() - { - return FrostFSClient.GetTestInstance( - Settings, - (url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url)), - NetworkMocker.GetMock().Object, - SessionMocker.GetMock().Object, - ContainerMocker.GetMock().Object, - Mocker.GetMock().Object); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs b/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs deleted file mode 100644 index 3c0245b..0000000 --- a/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using FrostFS.SDK.Client; -using FrostFS.SDK.Cryptography; - -namespace FrostFS.SDK.Tests.Unit; - -public class ObjectToolsTests -{ - internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - - [Fact] - public void CalculateObjectIdTest() - { - var payload = Encoding.UTF8.GetBytes("testPayload"); - - var payloadHash = SHA256.HashData(payload); - - FrostFsContainerId containerId = new("test"); - FrostFsObjectHeader header = new(containerId); - - var ecdsaKey = key.LoadWif(); - var owner = FrostFsOwner.FromKey(ecdsaKey); - - var clientKey = new ClientKey(ecdsaKey); - - var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); - - Assert.NotNull(objId.Value); - Assert.Equal("HuAojwCYi62iUKr1FtSCCkMLLWv1uAnznF8iSb1bRV1N", objId.Value); - } - - [Fact] - public void CalculateObjectIdTest1() - { - var payload = Encoding.UTF8.GetBytes("testPayload"); - - var payloadHash = SHA256.HashData(payload); - var ecdsaKey = key.LoadWif(); - var owner = FrostFsOwner.FromKey(ecdsaKey); - - var clientKey = new ClientKey(ecdsaKey); - FrostFsContainerId containerId = new("test"); - FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, null, null, owner, new FrostFsVersion(2, 13)); - - var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); - - Assert.NotNull(objId.Value); - Assert.Equal("HuAojwCYi62iUKr1FtSCCkMLLWv1uAnznF8iSb1bRV1N", objId.Value); - } - - [Fact] - public void CalculateObjectIdWithAttrTest() - { - var payload = Encoding.UTF8.GetBytes("testPayload"); - - var payloadHash = SHA256.HashData(payload); - var ecdsaKey = key.LoadWif(); - var owner = FrostFsOwner.FromKey(ecdsaKey); - - var clientKey = new ClientKey(ecdsaKey); - FrostFsContainerId containerId = new("test"); - - FrostFsAttributePair[] attribs = [new("key", "val")]; - - FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, attribs, null, owner, new FrostFsVersion(2, 13)); - - var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); - - Assert.NotNull(objId.Value); - Assert.Equal("4zq5NYEbzkrfmdKne3GnpavE24gU2PnuV17ZExb9hcn3", objId.Value); - } - - [Fact] - public void CalculateObjectIdWithSplitIdTest() - { - var payload = Encoding.UTF8.GetBytes("testPayload"); - - var payloadHash = SHA256.HashData(payload); - var ecdsaKey = key.LoadWif(); - var owner = FrostFsOwner.FromKey(ecdsaKey); - - var clientKey = new ClientKey(ecdsaKey); - FrostFsContainerId containerId = new("test"); - - FrostFsAttributePair[] attribs = [new("key", "val")]; - - var guid = Guid.Parse("790a8d04-f5c3-4cd6-b46f-a78ee7e325f2"); - SplitId splitId = new(guid); - FrostFsSplit split = new (splitId); - - FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, attribs, split, owner, new FrostFsVersion(2, 13)); - - var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); - - Assert.NotNull(objId.Value); - Assert.Equal("HCYzsuXyfe5LmQzi58hPQxExGPAFv7dU5TzEACLxM1os", objId.Value); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs deleted file mode 100644 index 46f7a3d..0000000 --- a/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System.Xml.Linq; -using FrostFS.Netmap; -using FrostFS.SDK.Client; -using Google.Protobuf.WellKnownTypes; - -namespace FrostFS.SDK.Tests.Unit; - -public class PlacementPolicyTests : NetworkTestsBase -{ - [Theory] - [InlineData(true, 1)] - [InlineData(true, 3)] - [InlineData(true, 5)] - [InlineData(false, 1)] - [InlineData(false, 3)] - [InlineData(false, 5)] - public void PlacementPolicySimpleFullTest(bool unique, uint backupFactor) - { - PlacementPolicy policy = new() - { - ContainerBackupFactor = backupFactor, - Unique = unique - }; - - var result = policy.ToModel(); - - Assert.Equal(backupFactor, result.BackupFactor); - Assert.Equal(unique, result.Unique); - Assert.Empty(result.Filters); - Assert.Empty(result.Replicas); - Assert.Empty(result.Selectors); - } - - [Fact] - public void PlacementPolicyFullTest() - { - PlacementPolicy policy = new() - { - ContainerBackupFactor = 3, - Unique = true - }; - - policy.Filters.AddRange( - [ - new () { Name = "filter1", Key = "filterKey1", Op =Operation.Eq, Value = "testValue1" }, - new () { Name = "filter2", Key = "filterKey2", Op =Operation.And, Value = "testValue2" } - ]); - - policy.Selectors.AddRange( - [ - new () { Name = "name1", Attribute = "attrib1", Clause = Clause.Same, Count = 5, Filter = "filter1" }, - new () { Name = "name2", Attribute = "attrib2", Clause = Clause.Distinct, Count = 4, Filter = "filter2" } - ]); - - policy.Replicas.AddRange( - [ - new () { EcDataCount = 2, EcParityCount = 3, Count = 4, Selector = "selector1"}, - new () { EcDataCount = 5, EcParityCount = 6, Count = 7, Selector = "selector2"}, - ]); - - var result = policy.ToModel(); - - Assert.Equal(3L, result.BackupFactor); - Assert.True(result.Unique); - Assert.Equal(2, result.Filters.Count); - Assert.Equal(2, result.Replicas.Length); - Assert.Equal(2, result.Selectors.Count); - - var rep0 = result.Replicas[0]; - Assert.Equal(2u, rep0.EcDataCount); - Assert.Equal(3u, rep0.EcParityCount); - Assert.Equal(4u, rep0.Count); - Assert.Equal("selector1", rep0.Selector); - - var rep1 = result.Replicas[1]; - Assert.Equal(5u, rep1.EcDataCount); - Assert.Equal(6u, rep1.EcParityCount); - Assert.Equal(7u, rep1.Count); - Assert.Equal("selector2", rep1.Selector); - - var f0 = result.Filters[0]; - Assert.Equal("filterKey1", f0.Key); - Assert.Equal("filter1", f0.Name); - Assert.Equal(1, f0.Operation); - Assert.Equal("testValue1", f0.Value); - - var f1 = result.Filters[1]; - Assert.Equal("filterKey2", f1.Key); - Assert.Equal("filter2", f1.Name); - Assert.Equal(8, f1.Operation); - Assert.Equal("testValue2", f1.Value); - - var s0 = result.Selectors[0]; - Assert.Equal("name1", s0.Name); - Assert.Equal("attrib1", s0.Attribute); - Assert.Equal(1, s0.Clause); - Assert.Equal(5L, s0.Count); - Assert.Equal("filter1", s0.Filter); - - var s1 = result.Selectors[1]; - Assert.Equal("name2", s1.Name); - Assert.Equal("attrib2", s1.Attribute); - Assert.Equal(2, s1.Clause); - Assert.Equal(4L, s1.Count); - Assert.Equal("filter2", s1.Filter); - } - - - [Theory] - [InlineData(1, "test" , 0, 0)] - [InlineData(1, "", 1000, 9999)] - [InlineData(1, "some long text to test reasonable length of the selector name", 100000000, 100000001)] - [InlineData(100, "test2", 1, 1)] - [InlineData(1, " ", 2, 3)] - [InlineData(10, "!", 0, 0)] - [InlineData(1, "123", 0, 0)] - public void ReplicaToModelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) - { - Replica replica = new () - { - Count = count, - Selector = selector, - EcDataCount = ecDataCount, - EcParityCount = ecParityCount - }; - - FrostFsReplica model = replica.ToModel(); - - Assert.Equal(count, model.Count); - Assert.Equal(selector, model.Selector); - Assert.Equal(ecDataCount, model.EcDataCount); - Assert.Equal(ecParityCount, model.EcParityCount); - } - - [Theory] - [InlineData(1, "test", 0, 0)] - [InlineData(1, "", 1000, 9999)] - [InlineData(1, "some long text to test reasonable length of the selector name", 100000000, 100000001)] - [InlineData(100, "test2", 1, 1)] - [InlineData(1, " ", 2, 3)] - [InlineData(10, "!", 0, 0)] - [InlineData(1, "123", 0, 0)] - public void ReplicaToMessagelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) - { - FrostFsReplica replica = new () - { - Count = count, - Selector = selector, - EcDataCount = ecDataCount, - EcParityCount = ecParityCount - }; - - Replica message = replica.ToMessage(); - - Assert.Equal(count, message.Count); - Assert.Equal(selector, message.Selector); - Assert.Equal(ecDataCount, message.EcDataCount); - Assert.Equal(ecParityCount, message.EcParityCount); - } - - [Theory] - [InlineData("test", 1, 2, "attribute", "filter")] - [InlineData("test", 0, 0, "longlonglonglonglonglonglonglonglonglonglonglonglong attribute", "longlonglonglonglonglonglonglonglonglonglonglonglong filter")] - [InlineData("test", 0, 1, "attribute", "filter")] - public void SelectorToMessageTest(string name, uint count, int clause, string attr, string filter) - { - FrostFsSelector selector = new (name) - { - Count = count, - Clause = clause, - Attribute = attr, - Filter = filter, - }; - - var message = selector.ToMessage(); - - Assert.Equal(name, message.Name); - Assert.Equal(count, message.Count); - Assert.Equal(clause, (int)message.Clause); - Assert.Equal(attr, message.Attribute); - Assert.Equal(filter, message.Filter); - - } - - [Theory] - [InlineData("test", 1, Clause.Same, "attribute", "filter")] - [InlineData("test", 0, Clause.Distinct, "longlonglonglonglonglonglonglonglonglonglonglonglong attribute", "longlonglonglonglonglonglonglonglonglonglonglonglong filter")] - public void SelectorToModelTest(string name, uint count, Clause clause, string attr, string filter) - { - Selector selector = new () - { - Name = name, - Count = count, - Clause = clause, - Attribute = attr, - Filter = filter - }; - - var model = selector.ToModel(); - - Assert.Equal(name, model.Name); - Assert.Equal(count, model.Count); - Assert.Equal((int)clause, model.Clause); - Assert.Equal(attr, model.Attribute); - Assert.Equal(filter, model.Filter); - } - - [Theory] - [InlineData("", "", 1, "")] - [InlineData("name", "key", 1, "val")] - [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", 10, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] - public void FilterToMessageTest(string name, string key, int operation, string value) - { - FrostFsFilter filter = new (name, key, operation, value, []); - - var message = filter.ToMessage(); - - Assert.Equal(name, message.Name); - Assert.Equal(key, message.Key); - Assert.Equal(operation, (int)message.Op); - Assert.Equal(value, message.Value); - } - - [Theory] - [InlineData("", "", 1, "")] - [InlineData("name", "key", 2, "val")] - [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", 10, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] - public void SubFilterToMessageTest(string name, string key, int operation, string value) - { - FrostFsFilter subFilter = new(name, key, operation, value, []); - - FrostFsFilter filter = new("name", "key", 1, "value", [subFilter]); - - var message = filter.ToMessage(); - - Assert.Single(message.Filters); - - var grpcFilter = message.Filters[0]; - Assert.Equal(name, grpcFilter.Name); - Assert.Equal(key, grpcFilter.Key); - Assert.Equal(operation, (int)grpcFilter.Op); - Assert.Equal(value, grpcFilter.Value); - } - - [Fact] - public void SubFiltersToMessageTest() - { - string[] names = ["", "name1", "some pretty long name for name test"]; - string[] keys = ["", "key1", "some pretty long key for name test"]; - int[] operations = [1, 2, 10]; - string[] values = ["", "val1", "some pretty long value for name test"]; - - var subFilter = new FrostFsFilter[3]; - - for (int i = 0; i < 3; i++) - { - subFilter[i] = new FrostFsFilter(names[i], keys[i], operations[i], values[i], []); - } - - FrostFsFilter filter = new("name", "key", 1, "value", subFilter); - - var message = filter.ToMessage(); - - Assert.Equal(3, message.Filters.Count); - - for (int i = 0; i < 3; i++) - { - var grpcFilter = message.Filters[i]; - Assert.Equal(names[i], grpcFilter.Name); - Assert.Equal(keys[i], grpcFilter.Key); - Assert.Equal(operations[i], (int)grpcFilter.Op); - Assert.Equal(values[i], grpcFilter.Value); - } - } - - [Theory] - [InlineData("", "", Operation.Unspecified, "")] - [InlineData("name", "key", Operation.Unspecified, "val")] - [InlineData("name", "key", Operation.And, "val")] - [InlineData("name", "key", Operation.Eq, "val")] - [InlineData("name", "key", Operation.Le, "val")] - [InlineData("name", "key", Operation.Like, "val")] - [InlineData("name", "key", Operation.Ge, "val")] - [InlineData("name", "key", Operation.Gt, "val")] - [InlineData("name", "key", Operation.Lt, "val")] - [InlineData("name", "key", Operation.Ne, "val")] - [InlineData("name", "key", Operation.Not, "val")] - [InlineData("name", "key", Operation.Or, "val")] - [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", Operation.Like, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] - public void FrostFsFilterToModelTest(string name, string key, Operation operation, string value) - { - Filter filter = new() - { - Name = name, - Key = key, - Op = operation, - Value = value - }; - - var model = filter.ToModel(); - - Assert.Equal(name, model.Name); - Assert.Equal(key, model.Key); - Assert.Equal((int)operation, model.Operation); - Assert.Equal(value, model.Value); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs deleted file mode 100644 index 9833ae1..0000000 --- a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; -using System.Text.Json.Serialization; - -using FrostFS.SDK.Client.Models.Netmap.Placement; - -using Xunit.Abstractions; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class PlacementVectorTests(ITestOutputHelper testOutputHelper) -{ - private static readonly JsonSerializerOptions serializeOptions = new() - { - PropertyNameCaseInsensitive = true - }; - - private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; - - [Fact] - public void PlacementTest() - { - var path = ".\\..\\..\\..\\TestData\\PlacementTests"; - Assert.True(Directory.Exists(path)); - - var files = Directory.GetFiles(path); - - FrostFsVersion v = new(2, 13); - var addresses = new string[] { "localhost", "server1" }; - - foreach (var file in files.Where(f => f.EndsWith(".json", StringComparison.OrdinalIgnoreCase))) - { - //if (!file.EndsWith("selector_invalid.json")) - // continue; - - var fileName = file[(file.LastIndexOf("..\\", StringComparison.OrdinalIgnoreCase) + 3)..]; - _testOutputHelper.WriteLine($"Open file {fileName}"); - - var str = File.ReadAllText(file); - Assert.False(string.IsNullOrEmpty(str)); - - var testCase = JsonSerializer.Deserialize(str, serializeOptions); - - Assert.NotNull(testCase); - Assert.NotNull(testCase.Nodes); - Assert.True(testCase.Nodes.Length > 0); - - _testOutputHelper.WriteLine($"Test case: \"{testCase.Name}\""); - - var nodes = testCase.Nodes - .Select(n => new FrostFsNodeInfo(v, - n.State, - addresses.AsReadOnly(), - n.Attributes?.ToDictionary(x => x.Key, x => x.Value) ?? [], - n.PublicKeyBytes - ) - ) - .ToArray() - .AsReadOnly(); - - var netmap = new FrostFsNetmapSnapshot(100, nodes); - - Assert.NotNull(testCase.Tests); - - foreach (var test in testCase.Tests) - { - _testOutputHelper.WriteLine($"Start test \"{test.Name}\""); - - var policy = new FrostFsPlacementPolicy( - test.Policy!.Unique, - test.Policy.ContainerBackupFactor, - new Collection(test.Policy.Selectors?.Select(s => s.Selector).ToList() ?? []), - new Collection(test.Policy.Filters?.Select(f => f.Filter).ToList() ?? []), - test.Policy.Replicas?.Select(r => new FrostFsReplica(r.Count, r.Selector)).ToArray() ?? [] - ); - - try - { - var result = netmap.ContainerNodes(policy, test.PivotBytes); - - if (test.Result == null) - { - if (!string.IsNullOrEmpty(test.Error)) - { - Assert.Fail("Error is expected but has not been thrown"); - } - else - { - Assert.NotNull(test.Policy?.Replicas); - Assert.Equal(result.Length, test.Policy.Replicas.Length); - - for (int i = 0; i < result.Length; i++) - { - Assert.Empty(result[i]); - } - } - } - else - { - Assert.Equal(test.Result.Length, result.Length); - - for (var i = 0; i < test.Result.Length; i++) - { - Assert.Equal(test.Result[i].Length, result[i].Length); - for (var j = 0; j < test.Result[i].Length; j++) - { - CompareNodes(nodes[test.Result[i][j]].Attributes, result[i][j]); - } - } - - if (test.Placement?.Result != null && test.Placement.PivotBytes != null) - { - var placementResult = netmap.PlacementVectors(result, test.Placement.PivotBytes); - - Assert.Equal(test.Placement.Result.Length, placementResult.Length); - - for (int i = 0; i < placementResult.Length; i++) - { - Assert.Equal(test.Placement.Result[i].Length, placementResult[i].Length); - for (int j = 0; j < placementResult[i].Length; j++) - { - CompareNodes(nodes[test.Placement.Result[i][j]].Attributes, placementResult[i][j]); - } - } - } - } - } - catch (Exception ex) - { - if (!string.IsNullOrEmpty(test.Error)) - { - Assert.Contains(test.Error, ex.Message, StringComparison.InvariantCulture); - } - else - { - throw; - } - } - - _testOutputHelper.WriteLine($"Done"); - } - } - } - - - private static void CompareNodes(IReadOnlyDictionary attrs, FrostFsNodeInfo nodeInfo) - { - Assert.Equal(attrs.Count, nodeInfo.Attributes.Count); - Assert.True(attrs.OrderBy(k => k.Key).SequenceEqual(nodeInfo.Attributes.OrderBy(x => x.Key))); - } -} - -public class TestCase -{ - public string? Name { get; set; } - - public Node[]? Nodes { get; set; } - - public TestData[]? Tests { get; set; } -} - - - -public class Node -{ - [JsonPropertyName("attributes")] - public KeyValuePair[]? Attributes { get; set; } - - public string? PublicKey { get; set; } - - internal byte[]? PublicKeyBytes => string.IsNullOrEmpty(PublicKey) ? [] : Convert.FromBase64String(PublicKey); - - public string[]? Addresses { get; set; } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public NodeState State { get; set; } = NodeState.Online; -} - -public class TestData -{ - public string? Name { get; set; } - - public PolicyDto? Policy { get; set; } - - public string? Pivot { get; set; } - - public int[][]? Result { get; set; } - - public string? Error { get; set; } - - internal byte[]? PivotBytes => Pivot != null ? Convert.FromBase64String(Pivot) : null; - - public ResultData? Placement { get; set; } -} - -public class PolicyDto -{ - public bool Unique { get; set; } - - public uint ContainerBackupFactor { get; set; } - - public FilterDto[]? Filters { get; set; } - - public ReplicaDto[]? Replicas { get; set; } - - public SelectorDto[]? Selectors { get; set; } -} - -public class SelectorDto() -{ - public uint Count { get; set; } - - public string? Name { get; set; } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public ClauseValues Clause { get; set; } - - public string? Attribute { get; set; } - - public string? Filter { get; set; } - - public FrostFsSelector Selector => new(Name ?? string.Empty) - { - Count = Count, - Clause = (int)Clause, - Filter = Filter, - Attribute = Attribute - }; -} - -public class FilterDto -{ - public string? Name { get; set; } - - public string? Key { get; set; } - - [JsonConverter(typeof(JsonStringEnumConverter))] - public Operation Op { get; set; } - - public string? Value { get; set; } - - public FilterDto[]? Filters { get; set; } - - public FrostFsFilter Filter => new( - Name ?? string.Empty, - Key ?? string.Empty, - (int)Op, - Value ?? string.Empty, - Filters != null ? [.. Filters.Select(f => f.Filter)] : []); -} - -public class ReplicaDto -{ - public uint Count { get; set; } - - public string? Selector { get; set; } -} - -public class ResultData -{ - public string? Pivot { get; set; } - - public int[][]? Result { get; set; } - - internal byte[]? PivotBytes => Pivot != null ? Convert.FromBase64String(Pivot) : null; -} - -public enum ClauseValues -{ - UNSPECIFIED = 0, - SAME, - DISTINCT -} diff --git a/src/FrostFS.SDK.Tests/Unit/SessionTests.cs b/src/FrostFS.SDK.Tests/Unit/SessionTests.cs deleted file mode 100644 index be2e072..0000000 --- a/src/FrostFS.SDK.Tests/Unit/SessionTests.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class SessionTest : SessionTestsBase -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void CreateSessionTest(bool useContext) - { - var exp = 100u; - PrmSessionCreate param; - - CallContext ctx; - if (useContext) - { - ctx = new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token); - param = new PrmSessionCreate(exp, ["headerKey1", "headerValue1"]); - } - else - { - ctx = default; - param = new PrmSessionCreate(exp); - } - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient().CreateSessionAsync(param, ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - Assert.NotEqual(Guid.Empty, result.Id); - - Assert.Equal(Mocker.SessionId, result.Id.ToByteArray(true)); - Assert.Equal(Mocker.SessionKey, result.SessionKey.ToArray()); - - //Assert.Equal(OwnerId.ToMessage(), result.Token.Body.OwnerId); - //Assert.Equal(exp, result.Token.Body.Lifetime.Exp); - //Assert.Equal(exp, result.Token.Body.Lifetime.Iat); - //Assert.Equal(exp, result.Token.Body.Lifetime.Nbf); - //Assert.Null(result.Token.Body.Container); - - Assert.NotNull(Mocker.CreateSessionRequest); - - Assert.Equal(OwnerId.ToMessage(), Mocker.CreateSessionRequest.Body.OwnerId); - Assert.Equal(exp, Mocker.CreateSessionRequest.Body.Expiration); - Assert.NotNull(Mocker.CreateSessionRequest.MetaHeader); - Assert.Equal(Mocker.Version.ToMessage(), Mocker.CreateSessionRequest.MetaHeader.Version); - - Assert.Null(Mocker.Metadata); - - if (useContext) - { - Assert.Single(Mocker.CreateSessionRequest.MetaHeader.XHeaders); - Assert.Equal(param.XHeaders[0], Mocker.CreateSessionRequest.MetaHeader.XHeaders.First().Key); - Assert.Equal(param.XHeaders[1], Mocker.CreateSessionRequest.MetaHeader.XHeaders.First().Value); - - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - Assert.True(validTimeoutTo.Ticks >= Mocker.DateTime.Value.Ticks); - } - else - { - Assert.Empty(Mocker.CreateSessionRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/SessionTestsBase.cs b/src/FrostFS.SDK.Tests/Unit/SessionTestsBase.cs deleted file mode 100644 index eef8a45..0000000 --- a/src/FrostFS.SDK.Tests/Unit/SessionTestsBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Security.Cryptography; - -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; - -using Microsoft.Extensions.Options; - -namespace FrostFS.SDK.Tests.Unit; - -public abstract class SessionTestsBase -{ - internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - - protected IOptions Settings { get; set; } - - protected ECDsa ECDsaKey { get; set; } - protected FrostFsOwner OwnerId { get; set; } - protected SessionMocker Mocker { get; set; } - - protected SessionTestsBase() - { - Settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); - - ECDsaKey = key.LoadWif(); - OwnerId = FrostFsOwner.FromKey(ECDsaKey); - - Mocker = new SessionMocker(key) - { - PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)), - Version = new FrostFsVersion(2, 13) - }; - } - - protected IFrostFSClient GetClient() - { - return Client.FrostFSClient.GetTestInstance( - Settings, - (url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url)), - new NetworkMocker(key).GetMock().Object, - Mocker.GetMock().Object, - new ContainerMocker(key).GetMock().Object, - new ObjectMocker(key).GetMock().Object); - } -} diff --git a/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs b/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs deleted file mode 100644 index 5062319..0000000 --- a/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; - -namespace FrostFS.SDK.Tests.Unit; - -public class SignatureTests -{ - [Theory] - [InlineData(Refs.SignatureScheme.EcdsaSha512)] - [InlineData(Refs.SignatureScheme.EcdsaRfc6979Sha256)] - [InlineData(Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect)] - - public void SignatureToMessageTest(Refs.SignatureScheme scheme) - { - var key = Encoding.UTF8.GetBytes("datafortest"); - var sign = Encoding.UTF8.GetBytes("signdatafortest"); - - var frostFsScheme = scheme switch - { - Refs.SignatureScheme.EcdsaRfc6979Sha256 => SignatureScheme.EcdsaRfc6979Sha256, - Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect => SignatureScheme.EcdsaRfc6979Sha256WalletConnect, - Refs.SignatureScheme.EcdsaSha512 => SignatureScheme.EcdsaSha512 - }; - - FrostFsSignature signature = new() - { - Key = key, - Scheme = frostFsScheme, - Sign = sign - }; - - var result = signature.ToMessage(); - - Assert.Equal(scheme, result.Scheme); - Assert.Equal(sign, result.Sign.ToByteArray()); - Assert.Equal(key, result.Key.ToByteArray()); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/WalletTests.cs b/src/FrostFS.SDK.Tests/Unit/WalletTests.cs deleted file mode 100644 index 2acd77e..0000000 --- a/src/FrostFS.SDK.Tests/Unit/WalletTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class WalletTest : SessionTestsBase -{ - [Fact] - public void TestWallet() - { - var password = Encoding.UTF8.GetBytes(""); - - var d = Directory.GetCurrentDirectory(); - var path = ".\\..\\..\\..\\TestData\\wallet.json"; - Assert.True(File.Exists(path)); - - var content = File.ReadAllText(path); - - var wif = WalletTools.GetWifFromWallet(content, password); - - Assert.Equal("KzPXA6669m2pf18XmUdoR8MnP1pi1PMmefiFujStVFnv7WR5SRmK", wif); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/cat.jpg b/src/FrostFS.SDK.Tests/cat.jpg deleted file mode 100644 index 03236d5..0000000 Binary files a/src/FrostFS.SDK.Tests/cat.jpg and /dev/null differ