small fixes to ss and wabi

This commit is contained in:
Kukks
2023-10-31 14:12:43 +01:00
parent 04c4fa0cf7
commit 64dc8501ae
7 changed files with 266 additions and 295 deletions

View File

@@ -9,7 +9,7 @@
<PropertyGroup>
<Product>SideShift</Product>
<Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description>
<Version>1.1.11</Version>
<Version>1.1.12</Version>
</PropertyGroup>
<!-- Plugin development properties -->
<PropertyGroup>

View File

@@ -25,7 +25,7 @@ public class PrismClaimCreate : IPluginHookFilter
var network = _networkProvider.GetNetwork<BTCPayNetwork>("BTC");
if (args is not ClaimRequest claimRequest || network is null)
{
return Task.FromResult(args);
return args;
}
if (claimRequest.Destination?.ToString() is not { } args1 || !args1.StartsWith("sideshift:")) return args;

View File

@@ -195,22 +195,32 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
{
continue;
}
//if we have less than the max suggested output registration, we should add more coins to reach that number to avoid breaking up into too many coins?
var isLessThanMaxOutputRegistration = solution.Coins.Count < 8;
var isLessThanMaxOutputRegistration = solution.Coins.Count < Math.Max(solution.HandledPayments.Count +1, 8);
var rand = Random.Shared.Next(1, 101);
//let's check how many coins we are allowed to add max and how many we added, and use that percentage as the random chance of not adding it.
// if max coins = 20, and current coins = 5 then 5/20 = 0.25 * 100 = 25
var maxCoinCapacityPercentage = Math.Floor((solution.Coins.Count / (decimal)maxCoins) * 100);
//aggressively attempt to reach max coin target if consolidation mode is on
//if we're less than the max output registration, we should be more aggressive in adding coins
var chance = consolidationMode ? (isLessThanMaxOutputRegistration? 100: 90 ): 100m - Math.Min(maxCoinCapacityPercentage, isLessThanMaxOutputRegistration ? 10m : maxCoinCapacityPercentage);
_logger.LogDebug(
$"coin selection: no payms left but at {solution.Coins.Count()} coins. random chance to add another coin if: {chance} <= {rand} (random 0-100) {chance <= rand} ");
decimal chance = 100;
if (consolidationMode && !isLessThanMaxOutputRegistration)
{
chance -= maxCoinCapacityPercentage / random.GetInt(2, 8);
}
else if (!isLessThanMaxOutputRegistration)
{
chance -= maxCoinCapacityPercentage;
}
_logger.LogDebug($"coin selection: no payments left but at {solution.Coins.Count()} coins. random chance to add another coin if: {chance} > {rand} (random 0-100) continue: {chance > rand}");
if (chance <= rand)
{
break;
}
}
}

View File

@@ -13,7 +13,7 @@
<PropertyGroup>
<Product>Wabisabi Coinjoin</Product>
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
<Version>1.0.59</Version>
<Version>1.0.60</Version>
</PropertyGroup>
<!-- Plugin development properties -->

View File

@@ -1,42 +1,47 @@
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using WabiSabi.Crypto;
using WalletWasabi.Affiliation;
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Crypto;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WabiSabi.Models.Serialization;
namespace WalletWasabi.Backend.Filters;
public class ExceptionTranslateAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception.InnerException ?? context.Exception;
context.Result = exception switch
{
WabiSabiProtocolException e => new JsonResult(new Error(
Type: ProtocolConstants.ProtocolViolationType,
ErrorCode: e.ErrorCode.ToString(),
Description: e.Message,
ExceptionData: e.ExceptionData ?? EmptyExceptionData.Instance),
JsonSerializationOptions.Default.Settings )
{
StatusCode = (int)HttpStatusCode.InternalServerError
},
WabiSabiCryptoException e => new JsonResult(new Error(
Type: ProtocolConstants.ProtocolViolationType,
ErrorCode: WabiSabiProtocolErrorCode.CryptoException.ToString(),
Description: e.Message,
ExceptionData: EmptyExceptionData.Instance),
JsonSerializationOptions.Default.Settings)
{
StatusCode = (int)HttpStatusCode.InternalServerError
},
_ => new StatusCodeResult((int)HttpStatusCode.InternalServerError)
};
}
}
public override void OnException(ExceptionContext context)
{
var exception = context.Exception.InnerException ?? context.Exception;
context.Result = exception switch
{
WabiSabiProtocolException e => new JsonResult(new Error(
Type: ProtocolConstants.ProtocolViolationType,
ErrorCode: e.ErrorCode.ToString(),
Description: e.Message,
ExceptionData: e.ExceptionData ?? EmptyExceptionData.Instance))
{
StatusCode = (int) HttpStatusCode.InternalServerError
},
WabiSabiCryptoException e => new JsonResult(new Error(
Type: ProtocolConstants.ProtocolViolationType,
ErrorCode: WabiSabiProtocolErrorCode.CryptoException.ToString(),
Description: e.Message,
ExceptionData: EmptyExceptionData.Instance))
{
StatusCode = (int) HttpStatusCode.InternalServerError
},
AffiliationException e => new JsonResult(new Error(
Type: AffiliationConstants.RequestSecrecyViolationType,
ErrorCode: "undefined",
Description: e.Message,
ExceptionData: EmptyExceptionData.Instance))
{
StatusCode = (int) HttpStatusCode.InternalServerError
},
_ => new StatusCodeResult((int) HttpStatusCode.InternalServerError)
};
}
}

View File

@@ -95,7 +95,7 @@ public class NostrWabisabiApiServer: IHostedService
Content = Serialize(response)
};
await _client.PublishEvent(nostrEvent, cancellationToken);
_logger.LogInformation($"NOSTR SERVER: PUBLISHED ROUND STATE {nostrEvent.Id}");
_logger.LogDebug($"NOSTR SERVER: PUBLISHED ROUND STATE {nostrEvent.Id}");
await Task.Delay(1000, cancellationToken);
}
}
@@ -115,7 +115,7 @@ public class NostrWabisabiApiServer: IHostedService
{
try
{
_logger.LogInformation($"NOSTR SERVER: Received request {evt.Id} {action}");
_logger.LogDebug($"NOSTR SERVER: Received request {evt.Id} {action}");
switch (action)
{
case RemoteAction.GetStatus:
@@ -205,7 +205,7 @@ public class NostrWabisabiApiServer: IHostedService
CancellationToken cancellationToken)
{
_logger.LogInformation($"NOSTR SERVER: REPLYING TO {originaltEvent.Id} WITH {response}");
_logger.LogDebug($"NOSTR SERVER: REPLYING TO {originaltEvent.Id} WITH {response}");
var evt = new NostrEvent()
{
Content = Serialize(response),

View File

@@ -65,8 +65,7 @@
}
else
{
<partial name="Wabisabi/CoinjoinHistoryTable" model="cjHistory"/>
<partial name="Wabisabi/CoinjoinHistoryTable" model="cjHistory"/>
}
</div>
@@ -79,272 +78,229 @@
var colorCoins = coins.GroupBy(coin => coin.CoinColor(wallet.AnonScoreTarget)).ToDictionary(grouping => grouping.Key, grouping => grouping);
<div class="widget store-numbers">
@if (wallet is { })
{
@if (!((BTCPayKeyChain) wallet.KeyChain).KeysAvailable)
@if (wallet is { })
{
<div class="alert alert-danger d-flex align-items-center" role="alert">
<vc:icon symbol="warning"/>
<span class="ms-3">This wallet is either not a hot wallet, or enabled in yout store settings and will not be able to participate in coinjoins.</span>
</div>
@if (!((BTCPayKeyChain) wallet.KeyChain).KeysAvailable)
{
<div class="alert alert-danger d-flex align-items-center" role="alert">
<vc:icon symbol="warning"/>
<span class="ms-3">This wallet is either not a hot wallet, or enabled in yout store settings and will not be able to participate in coinjoins.</span>
</div>
}
}
}
<header>
<h4>Coinjoin stats</h4>
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="fw-semibold">
Configure coinjoin settings
</a>
<header>
<h4>Coinjoin stats</h4>
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="fw-semibold">
Configure coinjoin settings
</a>
</header>
<div class="w-100">
</header>
<div class="w-100">
<div>
<h6 class="mb-2">Privacy progress</h6>
<div class="progress mb-2 position-relative" style="height: 2rem;">
<div class="w-100 text-center position-absolute bg-transparent progress-bar h-100"> @privacyPercentage%</div>
<div class="progress-bar bg-success" role="progressbar" style="width: @privacyPercentage%"></div>
</div>
</div>
<div>
<h6 class="mb-2">Coins per privacy</h6>
<div class="progress mb-2" style="height: 2rem;">
@foreach (var cc in colorCoins)
{
var cssClass = cc.Key == AnonsetType.Green ? "bg-success" : cc.Key == AnonsetType.Orange ? "bg-warning" :
"bg-danger";
var text = cc.Key == AnonsetType.Green ? "private" : cc.Key == AnonsetType.Orange ? "semi-private" :
"non-private";
var tooltiptext = $"{cc.Value.Count()} {text} coins";
text = cc.Value.Count().ToString();
var percentage = decimal.Divide(cc.Value.Count(), coins.Count()) * 100;
<div class="progress-bar @cssClass" role="progressbar" style="width: @percentage%" data-bs-toggle="tooltip" title="@tooltiptext">@text</div>
}
</div>
</div>
<div>
<h6 class="mb-2">Value per privacy</h6>
<div class="progress mb-2" style="height: 2rem;">
@foreach (var cc in colorCoins)
{
var cssClass = cc.Key == AnonsetType.Green ? "bg-success" : cc.Key == AnonsetType.Orange ? "bg-warning" :
"bg-danger";
var text = cc.Key == AnonsetType.Green ? "private" : cc.Key == AnonsetType.Orange ? "semi-private" :
"non-private";
var percentage = decimal.Divide(cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)), coins.TotalAmount().ToDecimal(MoneyUnit.BTC)) * 100;
var tooltiptext = $"{cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} {text} BTC";
text = cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)).ToString();
<div class="progress-bar @cssClass" role="progressbar" style="width: @percentage%" data-bs-toggle="tooltip" title="@tooltiptext">@text</div>
}
</div>
</div>
@* @{ *@
@* var coinjoined = @coins.CoinJoinInProcess(); *@
@* } *@
@* @if (coinjoined.Any()) *@
@* { *@
@* var count = @coins.CoinJoinInProcess().Count(); *@
@* var totalCount = @coins.Count(); *@
@* var sum = @coinjoined.TotalAmount().ToDecimal(MoneyUnit.BTC); *@
@* var totalSum = @coins.TotalAmount().ToDecimal(MoneyUnit.BTC); *@
@* var sumPercentage = decimal.Divide(sum, totalSum) * 100; *@
@* var countPercentage = decimal.Divide(count, totalCount) * 100; *@
@* *@
@* <div> *@
@* <h6 class="mb-2">Coins currently joining</h6> *@
@* <div class="progress mb-2 position-relative" style="height: 2rem;"> *@
@* <div class="w-100 text-center position-absolute bg-transparent progress-bar h-100">@count </div> *@
@* <div class="progress-bar bg-info progress-bar-striped progress-bar-animated w-100" role="progressbar"></div> *@
@* </div> *@
@* </div> *@
@* <div> *@
@* <h6 class="mb-2">Value currently joining</h6> *@
@* <div class="progress mb-2 position-relative" style="height: 2rem;"> *@
@* <div class="w-100 text-center position-absolute bg-transparent progress-bar h-100">@sum BTC</div> *@
@* <div class="progress-bar bg-info progress-bar-striped progress-bar-animated w-100" role="progressbar"></div> *@
@* *@
@* *@
@* </div> *@
@* </div> *@
@* } *@
<!-- Modal -->
<div class="modal modal-lg fade" id="coins" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Your coins</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@{
var clusters = coins
.GroupBy(coin => coin.HdPubKey.Cluster);
}
@* <table class="table table-striped"> *@
@* <thead> *@
@* <tr> *@
@* <th colspan="3">Clusters</th> *@
@* </tr> *@
@* <tr> *@
@* <th> *@
@* Anonset *@
@* </th> *@
@* <th> *@
@* Value *@
@* </th> *@
@* <th> *@
@* Labels *@
@* </th> *@
@* </tr> *@
@* </thead> *@
@* *@
@* @foreach (var cluster in clusters) *@
@* { *@
@* var wavg = *@
@* CoinjoinAnalyzer.WeightedAverage(cluster.Select(coin => new CoinjoinAnalyzer.AmountWithAnonymity(coin.AnonymitySet, coin.Amount))); *@
@* *@
@* <tr> *@
@* <td> *@
@* @wavg *@
@* </td> *@
@* <td> *@
@* @cluster.Sum(c => c.Amount.ToDecimal(MoneyUnit.BTC)) *@
@* </td> *@
@* <td> *@
@* @cluster.Key.Labels.ToString() *@
@* </td> *@
@* </tr> *@
@* } *@
@* </table> *@
<table class="table">
<thead>
<tr>
<th colspan="3">Coins</th>
</tr>
<tr>
<th>
Anonset
</th>
<th>
Value
</th>
<th>
Labels
</th>
</tr>
</thead>
@foreach (var coin in coins.OrderByDescending(coin => coin.AnonymitySet).ThenByDescending(coin => coin.Amount))
{
<tr>
<td>
@coin.AnonymitySet.ToString("0.##")
</td>
<td>
@coin.Amount.ToDecimal(MoneyUnit.BTC) BTC
</td>
<td>
<ul class="list-group list-group-flush">
@for (var index = 0; index < coin.HdPubKey.Labels.Count; index++)
{
var label = coin.HdPubKey.Labels.ElementAt(index);
<li class="list-group-item">
<vc:truncate-center text="@label" classes="truncate-center-id"></vc:truncate-center>
</li>
}
</ul>
@coin.HdPubKey.Labels.ToString()
</td>
</tr>
}
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<div>
<h6 class="mb-2">Privacy progress</h6>
<div class="progress mb-2 position-relative" style="height: 2rem;">
<div class="w-100 text-center position-absolute bg-transparent progress-bar h-100"> @privacyPercentage%</div>
<div class="progress-bar bg-success" role="progressbar" style="width: @privacyPercentage%"></div>
</div>
</div>
</div>
</div>
<div class="list-group list-group-flush mt-4 mb-3">
<h5 class="list-group-item-heading text-muted">Enabled coordinators</h5>
<div>
<h6 class="mb-2">Coins per privacy</h6>
<div class="progress mb-2" style="height: 2rem;">
@foreach (var cc in colorCoins)
{
var cssClass = cc.Key == AnonsetType.Green ? "bg-success" : cc.Key == AnonsetType.Orange ? "bg-warning" :
"bg-danger";
var text = cc.Key == AnonsetType.Green ? "private" : cc.Key == AnonsetType.Orange ? "semi-private" :
"non-private";
@{
foreach (var setting in enabledSettings)
{
if (!WabisabiCoordinatorClientInstanceManager.HostedServices.TryGetValue(setting.Coordinator, out var coordinator))
{
continue;
}
var tooltiptext = $"{cc.Value.Count()} {text} coins";
text = cc.Value.Count().ToString();
var percentage = decimal.Divide(cc.Value.Count(), coins.Count()) * 100;
<div class="progress-bar @cssClass" role="progressbar" style="width: @percentage%" data-bs-toggle="tooltip" title="@tooltiptext">@text</div>
}
</div>
</div>
<div>
<h6 class="mb-2">Value per privacy</h6>
<div class="progress mb-2" style="height: 2rem;">
@foreach (var cc in colorCoins)
{
var cssClass = cc.Key == AnonsetType.Green ? "bg-success" : cc.Key == AnonsetType.Orange ? "bg-warning" :
"bg-danger";
var text = cc.Key == AnonsetType.Green ? "private" : cc.Key == AnonsetType.Orange ? "semi-private" :
"non-private";
var percentage = decimal.Divide(cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)), coins.TotalAmount().ToDecimal(MoneyUnit.BTC)) * 100;
var tooltiptext = $"{cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} {text} BTC";
RoundState currentRound = null;
CoinJoinTracker tracker = null;
if (coordinator.CoinJoinManager.TrackedCoinJoins?.TryGetValue(wallet.WalletName, out tracker) is true &&
tracker?.CoinJoinClient?.CurrentRoundId is { } &&
tracker?.CoinJoinClient?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
{
}
var statusMsg = coordinator.WasabiCoordinatorStatusFetcher.Connected ? $"Connected to {(coordinator.Coordinator?.ToString() ?? "local")}" : $"Not connected to {(coordinator.Coordinator?.ToString() ?? "local")}";
<div class="list-group-item">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3" data-bs-toggle="tooltip" title="@statusMsg">
<span class="btcpay-status btcpay-status--@(coordinator.WasabiCoordinatorStatusFetcher.Connected ? "enabled" : "disabled")"></span>
<h6>@coordinator.CoordinatorDisplayName</h6>
text = cc.Value.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)).ToString();
<div class="progress-bar @cssClass" role="progressbar" style="width: @percentage%" data-bs-toggle="tooltip" title="@tooltiptext">@text</div>
}
</div>
</div>
@* @{ *@
@* var coinjoined = @coins.CoinJoinInProcess(); *@
@* } *@
@* @if (coinjoined.Any()) *@
@* { *@
@* var count = @coins.CoinJoinInProcess().Count(); *@
@* var totalCount = @coins.Count(); *@
@* var sum = @coinjoined.TotalAmount().ToDecimal(MoneyUnit.BTC); *@
@* var totalSum = @coins.TotalAmount().ToDecimal(MoneyUnit.BTC); *@
@* var sumPercentage = decimal.Divide(sum, totalSum) * 100; *@
@* var countPercentage = decimal.Divide(count, totalCount) * 100; *@
@* *@
@* <div> *@
@* <h6 class="mb-2">Coins currently joining</h6> *@
@* <div class="progress mb-2 position-relative" style="height: 2rem;"> *@
@* <div class="w-100 text-center position-absolute bg-transparent progress-bar h-100">@count </div> *@
@* <div class="progress-bar bg-info progress-bar-striped progress-bar-animated w-100" role="progressbar"></div> *@
@* </div> *@
@* </div> *@
@* <div> *@
@* <h6 class="mb-2">Value currently joining</h6> *@
@* <div class="progress mb-2 position-relative" style="height: 2rem;"> *@
@* <div class="w-100 text-center position-absolute bg-transparent progress-bar h-100">@sum BTC</div> *@
@* <div class="progress-bar bg-info progress-bar-striped progress-bar-animated w-100" role="progressbar"></div> *@
@* *@
@* *@
@* </div> *@
@* </div> *@
@* } *@
<!-- Modal -->
<div class="modal modal-lg fade" id="coins" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Your coins</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<table class="table">
<thead>
<tr>
<th colspan="3">Coins</th>
</tr>
<tr>
<th>
Anonset
</th>
<th>
Value
</th>
<th>
Labels
</th>
</tr>
</thead>
@foreach (var coin in coins.OrderByDescending(coin => coin.AnonymitySet).ThenByDescending(coin => coin.Amount))
{
<tr>
<td>
@coin.AnonymitySet.ToString("0.##")
</td>
<td>
@coin.Amount.ToDecimal(MoneyUnit.BTC) BTC
</td>
<td>
@for (var index = 0; index < coin.HdPubKey.Labels.Count; index++)
{
var label = coin.HdPubKey.Labels.ElementAt(index);
<vc:truncate-center text="@label" classes="truncate-center-id"></vc:truncate-center>
}
</td>
</tr>
}
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
@if(currentRound is not null)
{
<div class="timer cursor-pointer" data-bs-toggle="collapse" data-bs-target="#cj-@currentRound.Id">
<span class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden"></span>
</span>
<span class="h6">Mixing</span>
<vc:icon symbol="caret-down" />
</div>
}else if( coordinator.WasabiCoordinatorStatusFetcher.Connected)
{
<span class="h6">Idle</span>
}
</div>
@{
if (coordinator.CoinPrison is not null)
</div>
</div>
<div class="list-group list-group-flush mt-4 mb-3">
<h5 class="list-group-item-heading text-muted">Enabled coordinators</h5>
@{
foreach (var setting in enabledSettings)
{
if (!WabisabiCoordinatorClientInstanceManager.HostedServices.TryGetValue(setting.Coordinator, out var coordinator))
{
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin.Outpoint, out _));
@if (bannedCoins.Any())
{
<div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
}
continue;
}
if (currentRound is not null)
RoundState currentRound = null;
CoinJoinTracker tracker = null;
if (coordinator.CoinJoinManager.TrackedCoinJoins?.TryGetValue(wallet.WalletName, out tracker) is true &&
tracker?.CoinJoinClient?.CurrentRoundId is { } &&
tracker?.CoinJoinClient?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
{
<div class="collapse table-responsive @(enabledSettings.Count() ==1? "show": "")" id="cj-@currentRound.Id">
<table class="table ">
}
var statusMsg = coordinator.WasabiCoordinatorStatusFetcher.Connected ? $"Connected to {(coordinator.Coordinator?.ToString() ?? "local")}" : $"Not connected to {(coordinator.Coordinator?.ToString() ?? "local")}";
<div class="list-group-item">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3" data-bs-toggle="tooltip" title="@statusMsg">
<span class="btcpay-status btcpay-status--@(coordinator.WasabiCoordinatorStatusFetcher.Connected ? "enabled" : "disabled")"></span>
<h6>@coordinator.CoordinatorDisplayName</h6>
</div>
@if (currentRound is not null)
{
<div class="timer cursor-pointer" data-bs-toggle="collapse" data-bs-target="#cj-@currentRound.Id">
<span class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden"></span>
</span>
<span class="h6">Mixing</span>
<vc:icon symbol="caret-down"/>
</div>
}
else if (coordinator.WasabiCoordinatorStatusFetcher.Connected)
{
<span class="h6">Idle</span>
}
</div>
@{
if (coordinator.CoinPrison is not null)
{
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin.Outpoint, out _));
@if (bannedCoins.Any())
{
<div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
}
}
if (currentRound is not null)
{
<div class="collapse table-responsive @(enabledSettings.Count() == 1 ? "show" : "") m-0 w-100" id="cj-@currentRound.Id">
<table class="table w-100">
<tr>
<th scope="row">Status</th>
<th scope="">Status</th>
<td class="text-truncate">@currentRound.Phase.ToString().ToSentenceCase()</td>
</tr><tr>
<th scope="row">Round id</th>
<td class="text-truncate" style="max-width: 200px" title="@currentRound.Id.ToString()">@currentRound.Id.ToString()</td>
</tr>
<tr>
<th scope="row">Mining feerate</th>
<th scope="">Round id</th>
<td class="text-truncate"><vc:truncate-center text="@currentRound.Id.ToString()" classes="truncate-center-id"></vc:truncate-center></td>
</tr>
<tr>
<th scope="">Mining feerate</th>
<td >@currentRound.CoinjoinState.Parameters.MiningFeeRate.ToString()</td>
</tr>
<tr>
<th scope="row">Coinjoin total inputs</th>
<th scope="">Coinjoin total inputs</th>
<td >@currentRound.CoinjoinState.Inputs.Count() inputs (@currentRound.CoinjoinState.Inputs.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC)</td>
</tr>
@if (!tracker.CoinJoinClient.CoinsToRegister.IsEmpty)
{
<tr>
<th scope="row">Your inputs</th>
<td class="row" >
<th scope="">Your inputs</th>
<td class="">
<span class="w-100">Registered @tracker.CoinJoinClient.CoinsInCriticalPhase.Count() inputs (@tracker.CoinJoinClient.CoinsInCriticalPhase.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) / @tracker.CoinJoinClient.CoinsToRegister.Count() inputs (@tracker.CoinJoinClient.CoinsToRegister.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) </span>
@if (tracker.BannedCoins.Any())
{
@@ -357,13 +313,13 @@
@if (currentRound.Phase >= Phase.OutputRegistration)
{
<tr>
<th scope="row">Coinjoin total outputs</th>
<th scope="">Coinjoin total outputs</th>
<td >@currentRound.CoinjoinState.Outputs.Count() outputs (@currentRound.CoinjoinState.Outputs.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC)) BTC)</td>
</tr>
if (tracker.CoinJoinClient.OutputTxOuts is { } outputs)
{
<tr>
<th scope="row">Your outputs</th>
<th scope="">Your outputs</th>
<td >@outputs.outputTxOuts.Count() outputs (@outputs.outputTxOuts.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC)) BTC, @outputs.batchedPayments.Count() batched payments)</td>
</tr>
}
@@ -371,7 +327,7 @@
</table>
</div>
@* <div class="collapse table-responsive" id="cj-@currentRound.Id"> *@
@* *@
@* <dl> *@
@@ -444,17 +400,17 @@
@* </dl> *@
@* *@
@* </div> *@
}
}
}
</div>
}
</div>
}
}
</div>
}
</div>
<button type="button" class="btn btn-text p-1" data-bs-toggle="modal" data-bs-target="#coins">
View coins
</button>
</div>
<button type="button" class="btn btn-text p-1" data-bs-toggle="modal" data-bs-target="#coins">
View coins
</button>
</div>
</div>
}
}