Skip to content

Commit

Permalink
Merge pull request #453 from NicolasDorier/txts3
Browse files Browse the repository at this point in the history
Add WalletTrackedSource to backend
  • Loading branch information
NicolasDorier authored Dec 11, 2023
2 parents ceef965 + cfedff0 commit eca963d
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 100 deletions.
84 changes: 45 additions & 39 deletions NBXplorer.Client/ExplorerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ public PruneResponse Prune(DerivationStrategyBase extKey, PruneRequest pruneRequ
return PruneAsync(extKey, pruneRequest, cancellation).GetAwaiter().GetResult();
}

internal class RawStr {
internal class RawStr
{
private string str;
public RawStr(string str)
{
Expand Down Expand Up @@ -269,7 +270,7 @@ public void Track(DerivationStrategyBase strategy, CancellationToken cancellatio
}
public Task TrackAsync(DerivationStrategyBase strategy, CancellationToken cancellation = default)
{
return TrackAsync(TrackedSource.Create(strategy), cancellation);
return TrackAsync(TrackedSource.Create(strategy), cancellation: cancellation);
}

public void Track(DerivationStrategyBase strategy, TrackWalletRequest trackDerivationRequest, CancellationToken cancellation = default)
Expand All @@ -285,14 +286,13 @@ public async Task TrackAsync(DerivationStrategyBase strategy, TrackWalletRequest

public void Track(TrackedSource trackedSource, CancellationToken cancellation = default)
{
TrackAsync(trackedSource, cancellation).GetAwaiter().GetResult();
TrackAsync(trackedSource, cancellation: cancellation).GetAwaiter().GetResult();
}
public Task TrackAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
public Task TrackAsync(TrackedSource trackedSource, TrackWalletRequest trackDerivationRequest = null, CancellationToken cancellation = default)
{
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));

return SendAsync<string>(HttpMethod.Post, null, GetBasePath(trackedSource), cancellation);
return SendAsync<string>(HttpMethod.Post, trackDerivationRequest, GetBasePath(trackedSource), cancellation);
}

private Exception UnSupported(TrackedSource trackedSource)
Expand All @@ -311,7 +311,7 @@ public GetBalanceResponse GetBalance(DerivationStrategyBase userDerivationScheme
}
public Task<GetBalanceResponse> GetBalanceAsync(DerivationStrategyBase userDerivationScheme, CancellationToken cancellation = default)
{
return SendAsync<GetBalanceResponse>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{userDerivationScheme}/balance", cancellation);
return GetBalanceAsync(TrackedSource.Create(userDerivationScheme), cancellation);
}


Expand All @@ -321,9 +321,12 @@ public GetBalanceResponse GetBalance(BitcoinAddress address, CancellationToken c
}
public Task<GetBalanceResponse> GetBalanceAsync(BitcoinAddress address, CancellationToken cancellation = default)
{
return SendAsync<GetBalanceResponse>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/addresses/{address}/balance", cancellation);
return GetBalanceAsync(TrackedSource.Create(address), cancellation);
}
public Task<GetBalanceResponse> GetBalanceAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
{
return SendAsync<GetBalanceResponse>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/balance", cancellation);
}

public Task CancelReservationAsync(DerivationStrategyBase strategy, KeyPath[] keyPaths, CancellationToken cancellation = default)
{
return SendAsync<string>(HttpMethod.Post, keyPaths, $"v1/cryptos/{CryptoCode}/derivations/{strategy}/addresses/cancelreservation", cancellation);
Expand Down Expand Up @@ -363,18 +366,7 @@ public Task<GetTransactionsResponse> GetTransactionsAsync(DerivationStrategyBase
}
public Task<GetTransactionsResponse> GetTransactionsAsync(TrackedSource trackedSource, CancellationToken cancellation = default)
{
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));
if (trackedSource is DerivationSchemeTrackedSource dsts)
{
return SendAsync<GetTransactionsResponse>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{dsts.DerivationStrategy}/transactions", cancellation);
}
else if (trackedSource is AddressTrackedSource asts)
{
return SendAsync<GetTransactionsResponse>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/addresses/{asts.Address}/transactions", cancellation);
}
else
throw UnSupported(trackedSource);
return SendAsync<GetTransactionsResponse>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/transactions", cancellation);
}


Expand All @@ -399,16 +391,7 @@ public Task<TransactionInformation> GetTransactionAsync(TrackedSource trackedSou
throw new ArgumentNullException(nameof(txId));
if (trackedSource == null)
throw new ArgumentNullException(nameof(trackedSource));
if (trackedSource is DerivationSchemeTrackedSource dsts)
{
return SendAsync<TransactionInformation>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{dsts.DerivationStrategy}/transactions/{txId}", cancellation);
}
else if (trackedSource is AddressTrackedSource asts)
{
return SendAsync<TransactionInformation>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/addresses/{asts.Address}/transactions/{txId}", cancellation);
}
else
throw UnSupported(trackedSource);
return SendAsync<TransactionInformation>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/transactions/{txId}", cancellation);
}

public Task RescanAsync(RescanRequest rescanRequest, CancellationToken cancellation = default)
Expand Down Expand Up @@ -447,16 +430,17 @@ public KeyPathInformation GetKeyInformation(DerivationStrategyBase strategy, Scr

public async Task<KeyPathInformation> GetKeyInformationAsync(DerivationStrategyBase strategy, Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/derivations/{strategy}/scripts/{script.ToHex()}", cancellation).ConfigureAwait(false);
return await GetKeyInformationAsync(new DerivationSchemeTrackedSource(strategy), script, cancellation).ConfigureAwait(false);
}
public async Task<KeyPathInformation> GetKeyInformationAsync(TrackedSource trackedSource, Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation>(HttpMethod.Get, null, $"{GetBasePath(trackedSource)}/scripts/{script.ToHex()}", cancellation).ConfigureAwait(false);
}

[Obsolete("Use GetKeyInformationAsync(DerivationStrategyBase strategy, Script script) instead")]
public async Task<KeyPathInformation[]> GetKeyInformationsAsync(Script script, CancellationToken cancellation = default)
{
return await SendAsync<KeyPathInformation[]>(HttpMethod.Get, null, $"v1/cryptos/{CryptoCode}/scripts/{script.ToHex()}", cancellation).ConfigureAwait(false);
}

[Obsolete("Use GetKeyInformation(DerivationStrategyBase strategy, Script script) instead")]
public KeyPathInformation[] GetKeyInformations(Script script, CancellationToken cancellation = default)
{
return GetKeyInformationsAsync(script, cancellation).GetAwaiter().GetResult();
Expand Down Expand Up @@ -611,8 +595,8 @@ static FormattableString EncodeUrlParameters(FormattableString url)
return FormattableStringFactory.Create(
url.Format,
url.GetArguments()
.Select(a =>
a is RawStr ? a :
.Select(a =>
a is RawStr ? a :
a is FormattableString o ? EncodeUrlParameters(o) :
Uri.EscapeDataString(a?.ToString() ?? ""))
.ToArray());
Expand Down Expand Up @@ -653,11 +637,30 @@ internal async Task<T> SendAsync<T>(HttpMethod method, object body, FormattableS
if (Auth.RefreshCache())
{
message = CreateMessage(method, body, relativePath);
result = await Client.SendAsync(message).ConfigureAwait(false);
result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
}
}
return await ParseResponse<T>(result).ConfigureAwait(false);
}
internal async Task SendAsync(HttpMethod method, object body, FormattableString relativePath, CancellationToken cancellation)
{
var message = CreateMessage(method, body, relativePath);
var result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);

if (result.StatusCode == HttpStatusCode.GatewayTimeout || result.StatusCode == HttpStatusCode.RequestTimeout)
{
throw new HttpRequestException($"HTTP error {(int)result.StatusCode}", new TimeoutException());
}
if ((int)result.StatusCode == 401)
{
if (Auth.RefreshCache())
{
message = CreateMessage(method, body, relativePath);
result = await Client.SendAsync(message, cancellation).ConfigureAwait(false);
}
}
await ParseResponse(result).ConfigureAwait(false);
}

internal HttpRequestMessage CreateMessage(HttpMethod method, object body, FormattableString relativePath)
{
Expand Down Expand Up @@ -717,11 +720,14 @@ private async Task ParseResponse(HttpResponseMessage response)

private FormattableString GetBasePath(TrackedSource trackedSource)
{
if (trackedSource is null)
throw new ArgumentNullException(nameof(trackedSource));
return trackedSource switch
{
DerivationSchemeTrackedSource dsts => $"v1/cryptos/{CryptoCode}/derivations/{dsts.DerivationStrategy}",
AddressTrackedSource asts => $"v1/cryptos/{CryptoCode}/addresses/{asts.Address}",
_ => throw UnSupported(trackedSource)
WalletTrackedSource wts => $"v1/cryptos/{CryptoCode}/wallets/{wts.WalletId}",
_ => $"v1/cryptos/{CryptoCode}/tracked-sources/{trackedSource}"
};
}
}
Expand Down
40 changes: 40 additions & 0 deletions NBXplorer.Client/Models/TrackedSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public static bool TryParse(string str, out TrackedSource trackedSource, NBXplor
return false;
trackedSource = addressTrackedSource;
}
else if (strSpan.StartsWith("WALLET:".AsSpan(), StringComparison.Ordinal))
{
if (!WalletTrackedSource.TryParse(strSpan, out var walletTrackedSource))
return false;
trackedSource = walletTrackedSource;
}
else
{
return false;
Expand Down Expand Up @@ -97,6 +103,40 @@ public static TrackedSource Parse(string str, NBXplorerNetwork network)
}
}

public class WalletTrackedSource : TrackedSource
{
public string WalletId { get; }

public WalletTrackedSource(string walletId)
{
WalletId = walletId;
}

public static bool TryParse(ReadOnlySpan<char> strSpan, out WalletTrackedSource walletTrackedSource)
{
if (strSpan == null)
throw new ArgumentNullException(nameof(strSpan));
walletTrackedSource = null;
if (!strSpan.StartsWith("WALLET:".AsSpan(), StringComparison.Ordinal))
return false;
try
{
walletTrackedSource = new WalletTrackedSource(strSpan.Slice("WALLET:".Length).ToString());
return true;
}
catch { return false; }
}

public override string ToString()
{
return "WALLET:" + WalletId;
}
public override string ToPrettyString()
{
return WalletId;
}
}

public class AddressTrackedSource : TrackedSource, IDestination
{
// Note that we should in theory access BitcoinAddress. But parsing BitcoinAddress is very expensive, so we keep storing plain strings
Expand Down
1 change: 0 additions & 1 deletion NBXplorer.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
using System.Globalization;
using System.Net;
using NBXplorer.HostedServices;
using System.Reflection;

namespace NBXplorer.Tests
{
Expand Down
3 changes: 2 additions & 1 deletion NBXplorer.Tests/xunit.runner.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"parallelizeTestCollections": false
"parallelizeTestCollections": false,
"methodDisplay": "method"
}
Loading

0 comments on commit eca963d

Please sign in to comment.