Skip to content

Commit

Permalink
Soroban improvements (#24)
Browse files Browse the repository at this point in the history
* Soroban related improvements
- Added unit tests for GetLedgerEntries of type ConfigSetting and TTL.
- Added new SCInt128 constructor that accepts a numeric string.
- Reordered parameters in SCInt128 constructor, from SCInt128(ulong lo, long hi) to SCInt128(long hi, ulong lo) to match with other SDKs.
- Updated SorobanServer constructor SorobanServer(string uri) to SorobanServer(string uri, string? bearerToken = null), allowing direct injection of custom bearer tokens.
- Added new DefaultStellarSdkHttpClient class.
  • Loading branch information
cuongph87 authored Aug 30, 2024
1 parent 8fde5da commit a04f9f7
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 19 deletions.
39 changes: 37 additions & 2 deletions StellarDotnetSdk.Tests/ScValTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,9 @@ public void TestScUint128()
}

[TestMethod]
public void TestScInt128()
public void TestScInt128FromParts()
{
var scInt128 = new SCInt128(18446744073709551615, -9223372036854775807);
var scInt128 = new SCInt128(-9223372036854775807, 18446744073709551615);

// Act
var scInt128XdrBase64 = scInt128.ToXdrBase64();
Expand All @@ -354,6 +354,41 @@ public void TestScInt128()
Assert.AreEqual(scInt128.Hi, fromXdrBase64ScInt128.Hi);
}

[TestMethod]
public void TestScInt128ConstructedFromValidString()
{
var scInt128FromString = new SCInt128("18446744073709551616");

var scInt128FromParts = new SCInt128(1, 0);
// Act
var scInt128XdrBase64 = scInt128FromString.ToXdrBase64();
var fromXdrBase64ScInt128 = (SCInt128)SCVal.FromXdrBase64(scInt128XdrBase64);

// Assert
Assert.AreEqual(scInt128FromString.Lo, fromXdrBase64ScInt128.Lo);
Assert.AreEqual(scInt128FromString.Hi, fromXdrBase64ScInt128.Hi);

Assert.AreEqual(scInt128FromString.Lo, scInt128FromParts.Lo);
Assert.AreEqual(scInt128FromString.Hi, scInt128FromParts.Hi);
}

[TestMethod]
public void TestScInt128ConstructedFromTooBigNumericString()
{
var ex = Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
_ = new SCInt128("170141183460469231731687303715884105728");
});
Assert.IsTrue(ex.Message.Contains("Value must be between -2^127 and 2^127 - 1."));
}

[TestMethod]
public void TestScInt128ConstructedFromInvalidNumericString()
{
var ex = Assert.ThrowsException<ArgumentException>(() => { _ = new SCInt128("9,223,372,036,854,775,807"); });
Assert.IsTrue(ex.Message.Contains("Invalid numeric string."));
}

[TestMethod]
public void TestScUint256()
{
Expand Down
298 changes: 296 additions & 2 deletions StellarDotnetSdk.Tests/SorobanServerTest.cs

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions StellarDotnetSdk/Requests/DefaultStellarSdkHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;

namespace StellarDotnetSdk.Requests;

public class DefaultStellarSdkHttpClient : HttpClient
{
/// <summary>
/// Creates an HTTP client with some default request headers and the given bearer token.
/// </summary>
/// <param name="bearerToken">Bearer token in case the server requires it.</param>
/// <param name="clientName">Name of the client.</param>
/// <param name="clientVersion">Version of the client.</param>
public DefaultStellarSdkHttpClient(string? bearerToken = null, string? clientName = null, string? clientVersion = null)
{
var assembly = Assembly.GetAssembly(GetType())!.GetName();
DefaultRequestHeaders.Add("X-Client-Name", clientName ?? "stellar-dotnet-sdk");
DefaultRequestHeaders.Add("X-Client-Version", clientVersion ?? assembly.Version!.ToString());
if (!string.IsNullOrEmpty(bearerToken))
{
DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
public class GetTransactionsRequest
{
/// <summary>
/// Ledger sequence number to start fetching responses from (inclusive). This method will return an error if startLedger is less than the oldest ledger stored in this node, or greater than the latest ledger seen by this node. If a cursor is included in the request, startLedger must be omitted.
/// Ledger sequence number to start fetching responses from (inclusive). This method will return an error if
/// startLedger is less than the oldest ledger stored in this node, or greater than the latest ledger seen by this
/// node. If a cursor is included in the request, startLedger must be omitted.
/// </summary>
public long? StartLedger { get; set; }

Expand Down
10 changes: 6 additions & 4 deletions StellarDotnetSdk/Requests/SorobanRpc/PaginationOptions.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
namespace StellarDotnetSdk.Requests.SorobanRpc;

/// <summary>
/// Pagination in RPC is similar to pagination in Horizon.
/// See: <a href="https://developers.stellar.org/docs/data/rpc/api-reference/pagination">Pagination</a>
/// Pagination in RPC is similar to pagination in Horizon.
/// See: <a href="https://developers.stellar.org/docs/data/rpc/api-reference/pagination">Pagination</a>
/// </summary>
public class PaginationOptions
{
/// <summary>
/// A unique identifier (specifically, a TOID) that points to a specific location in a collection of responses and is pulled from the paging_token value of a record. When a cursor is provided, RPC will not include the element whose ID matches the cursor in the response: only elements which appear after the cursor will be included.
/// A unique identifier (specifically, a TOID) that points to a specific location in a collection of responses and is
/// pulled from the paging_token value of a record. When a cursor is provided, RPC will not include the element whose
/// ID matches the cursor in the response: only elements which appear after the cursor will be included.
/// </summary>
public string? Cursor { get; set; }

/// <summary>
/// The maximum number of records returned. For getTransactions, this ranges from 1 to 200 and defaults to 50.
/// The maximum number of records returned. For getTransactions, this ranges from 1 to 200 and defaults to 50.
/// </summary>
public long? Limit { get; set; }
}
19 changes: 15 additions & 4 deletions StellarDotnetSdk/Responses/SorobanRpc/TransactionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum TransactionStatus
{
NOT_FOUND,
SUCCESS,
FAILED
FAILED,
}

public TransactionInfo(TransactionStatus status, long? ledger, long? createdAt, int? applicationOrder,
Expand Down Expand Up @@ -86,7 +86,10 @@ public SCVal? ResultValue
{
get
{
if (Status != TransactionStatus.SUCCESS || ResultMetaXdr == null) return null;
if (Status != TransactionStatus.SUCCESS || ResultMetaXdr == null)
{
return null;
}

var bytes = Convert.FromBase64String(ResultMetaXdr);
var reader = new XdrDataInputStream(bytes);
Expand All @@ -102,7 +105,10 @@ public Soroban_TransactionMetaV3? TransactionMeta
{
get
{
if (ResultMetaXdr == null) return null;
if (ResultMetaXdr == null)
{
return null;
}
try
{
return Soroban_TransactionMetaV3.FromXdrBase64(ResultMetaXdr);
Expand All @@ -118,7 +124,10 @@ public string? WasmHash
{
get
{
if (ResultValue is SCBytes bytes) return Convert.ToHexString(bytes.InnerValue);
if (ResultValue is SCBytes bytes)
{
return Convert.ToHexString(bytes.InnerValue);
}

return null;
}
Expand All @@ -129,7 +138,9 @@ public string? CreatedContractId
get
{
if (ResultValue is SCContractId contract)
{
return contract.InnerValue;
}
return null;
}
}
Expand Down
17 changes: 17 additions & 0 deletions StellarDotnetSdk/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using stellar_dotnet_sdk;
Expand Down Expand Up @@ -272,11 +273,15 @@ public async Task CheckMemoRequired(TransactionBase transaction)
}
}

[Obsolete(
"Pass your own HttpClient instance to Server(string uri, HttpClient httpClient) instead. Otherwise call Server(string uri). Will be removed in the next major version.")]
public static HttpClient CreateHttpClient()
{
return CreateHttpClient(new HttpClientHandler());
}

[Obsolete(
"Pass your own HttpClient instance to Server(string uri, HttpClient httpClient) instead. Otherwise call Server(string uri). Will be removed in the next major version.")]
public static HttpClient CreateHttpClient(HttpMessageHandler handler)
{
var httpClient = new HttpClient(handler);
Expand All @@ -286,6 +291,18 @@ public static HttpClient CreateHttpClient(HttpMessageHandler handler)
return httpClient;
}

/// <summary>
/// Constructs a new instance that will interact with the provided URL.
/// </summary>
/// <param name="uri">URL of the Horizon server.</param>
/// <param name="bearerToken">Bearer token in case the server requires it.</param>
public Server(string uri, string? bearerToken = null)
{
_serverUri = new Uri(uri);
_httpClient = new DefaultStellarSdkHttpClient(bearerToken);
_internalHttpClient = true;
}

private Transaction GetTransactionToCheck(TransactionBase transaction)
{
switch (transaction)
Expand Down
32 changes: 29 additions & 3 deletions StellarDotnetSdk/Soroban/SCVal.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Numerics;
using StellarDotnetSdk.Accounts;
using StellarDotnetSdk.Xdr;
using Int32 = StellarDotnetSdk.Xdr.Int32;
Expand Down Expand Up @@ -773,12 +774,37 @@ public static SCUint128 FromSCValXdr(Xdr.SCVal xdrVal)

public class SCInt128 : SCVal
{
public SCInt128(ulong lo, long hi)
/// <summary>
/// Constructs a new SCInt128 object from high and low parts.
/// </summary>
/// <param name="hi">High parts.</param>
/// <param name="lo">Low parts.</param>
public SCInt128(long hi, ulong lo)
{
Hi = hi;
Lo = lo;
}

/// <summary>
/// Constructs a new SCInt128 object from a numeric string.
/// </summary>
/// <param name="input">A string represents a 128-bit signed integer.</param>
public SCInt128(string input)
{
if (!BigInteger.TryParse(input, out var bigInt))
{
throw new ArgumentException("Invalid numeric string.", nameof(input));
}
if (bigInt < BigInteger.MinusOne << 127 || bigInt > (BigInteger.One << 127) - 1)
{
throw new ArgumentOutOfRangeException(nameof(input), "Value must be between -2^127 and 2^127 - 1.");
}
var low = (ulong)(bigInt & ulong.MaxValue);
var high = (long)(bigInt >> 64);
Hi = high;
Lo = low;
}

public ulong Lo { get; set; }
public long Hi { get; set; }

Expand Down Expand Up @@ -809,7 +835,7 @@ public Xdr.SCVal ToSCValXdr()

public static SCInt128 FromXdr(Int128Parts xdrInt128Parts)
{
return new SCInt128(xdrInt128Parts.Lo.InnerValue, xdrInt128Parts.Hi.InnerValue);
return new SCInt128(xdrInt128Parts.Hi.InnerValue, xdrInt128Parts.Lo.InnerValue);
}

public static SCInt128 FromSCValXdr(Xdr.SCVal xdrVal)
Expand All @@ -819,7 +845,7 @@ public static SCInt128 FromSCValXdr(Xdr.SCVal xdrVal)
throw new ArgumentException("Not an SCInt128", nameof(xdrVal));
}

return new SCInt128(xdrVal.I128.Lo.InnerValue, xdrVal.I128.Hi.InnerValue);
return new SCInt128(xdrVal.I128.Hi.InnerValue, xdrVal.I128.Lo.InnerValue);
}
}

Expand Down
24 changes: 22 additions & 2 deletions StellarDotnetSdk/Soroban/SorobanServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

namespace StellarDotnetSdk.Soroban;

/// <summary>
/// This class helps you to connect to a local or remote Soroban RPC server
/// and send requests to the server. It parses the results and provides
/// corresponding response objects.
/// </summary>
public class SorobanServer : IDisposable
{
private const string ClientNameHeader = "X-Client-Name";
Expand All @@ -24,16 +29,27 @@ public class SorobanServer : IDisposable
private readonly bool _internalHttpClient;
private readonly Uri _serverUri;

/// <summary>
/// Constructs a new instance that will interact with the provided URL.
/// </summary>
/// <param name="uri">URL of the Soroban RPC server.</param>
/// <param name="httpClient">HttpClient instance to use for requests.</param>
public SorobanServer(string uri, HttpClient httpClient)
{
_httpClient = httpClient;
_serverUri = new Uri(uri);
_internalHttpClient = false;
}

public SorobanServer(string uri)
: this(uri, CreateHttpClient())
/// <summary>
/// Constructs a new instance that will interact with the provided URL.
/// </summary>
/// <param name="uri">URL of the Soroban RPC server.</param>
/// <param name="bearerToken">Bearer token in case the server requires it.</param>
public SorobanServer(string uri, string? bearerToken = null)
{
_serverUri = new Uri(uri);
_httpClient = new DefaultStellarSdkHttpClient(bearerToken);
_internalHttpClient = true;
}

Expand All @@ -45,11 +61,15 @@ public void Dispose()
}
}

[Obsolete(
"Pass your own HttpClient instance to SorobanServer(string uri, HttpClient httpClient) instead. Otherwise call SorobanServer(string uri). Will be removed in the next major version.")]
public static HttpClient CreateHttpClient()
{
return CreateHttpClient(new HttpClientHandler());
}

[Obsolete(
"Pass your own HttpClient instance to SorobanServer(string uri, HttpClient httpClient) instead. Otherwise call SorobanServer(string uri). Will be removed in the next major version.")]
public static HttpClient CreateHttpClient(HttpMessageHandler handler)
{
var httpClient = new HttpClient(handler);
Expand Down
2 changes: 1 addition & 1 deletion StellarDotnetSdk/StellarDotnetSdk.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Version>10.0.0</Version>
<Version>12.0.0</Version>
<ApplicationIcon/>
<OutputType>Library</OutputType>
<StartupObject/>
Expand Down

0 comments on commit a04f9f7

Please sign in to comment.