mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
improve ww
This commit is contained in:
@@ -469,9 +469,9 @@ public class BringinService : EventHostedServiceBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetBalance(string storeId, PaymentMethodId pmi)
|
public async Task ResetBalance(string storeId, PaymentMethodId pmi)
|
||||||
{
|
{
|
||||||
_ = HandleStoreAction(storeId, async bringinStoreSettings =>
|
await HandleStoreAction(storeId, async bringinStoreSettings =>
|
||||||
{
|
{
|
||||||
if (bringinStoreSettings.MethodSettings.TryGetValue(pmi.ToString(), out var methodSettings) && methodSettings.CurrentBalance > 0)
|
if (bringinStoreSettings.MethodSettings.TryGetValue(pmi.ToString(), out var methodSettings) && methodSettings.CurrentBalance > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -292,9 +292,19 @@
|
|||||||
|
|
||||||
public string OnboardLink;
|
public string OnboardLink;
|
||||||
|
|
||||||
private void ResetBalance(PaymentMethodId pmi)
|
private async void ResetBalance(PaymentMethodId pmi)
|
||||||
{
|
{
|
||||||
BringinService.ResetBalance(StoreId, pmi);
|
if (_saving)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_saving = true;
|
||||||
|
await BringinService.ResetBalance(StoreId, pmi);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -475,15 +485,14 @@
|
|||||||
<h3 class="d-inline-block me-1" data-balance="@method.Value.CurrentBalance" data-sensitive>@DisplayFormatter.Currency(method.Value.CurrentBalance, "BTC", DisplayFormatter.CurrencyFormat.None)</h3>
|
<h3 class="d-inline-block me-1" data-balance="@method.Value.CurrentBalance" data-sensitive>@DisplayFormatter.Currency(method.Value.CurrentBalance, "BTC", DisplayFormatter.CurrencyFormat.None)</h3>
|
||||||
<span class="text-secondary fw-semibold currency">BTC</span>
|
<span class="text-secondary fw-semibold currency">BTC</span>
|
||||||
<span class="text-secondary"> (@DisplayFormatter.Currency(balanceInFiat.Value, "EUR", DisplayFormatter.CurrencyFormat.Code)) pending to forward once @DisplayFormatter.Currency(thresholdinBtc.Value, "BTC", DisplayFormatter.CurrencyFormat.Code) (@DisplayFormatter.Currency(method.Value.Threshold, "EUR")) is reached.</span>
|
<span class="text-secondary"> (@DisplayFormatter.Currency(balanceInFiat.Value, "EUR", DisplayFormatter.CurrencyFormat.Code)) pending to forward once @DisplayFormatter.Currency(thresholdinBtc.Value, "BTC", DisplayFormatter.CurrencyFormat.Code) (@DisplayFormatter.Currency(method.Value.Threshold, "EUR")) is reached.</span>
|
||||||
@if (method.Value.CurrentBalance > 0)
|
@if (method.Value.CurrentBalance > 0)
|
||||||
{
|
{
|
||||||
<button class="btn btn-link" @onclick="() => ResetBalance(pmi)">Reset balance</button>
|
<button class="btn btn-link" @onclick="() => ResetBalance(pmi)" disabled="@_saving">Reset balance</button>
|
||||||
//Clear balance
|
//Clear balance
|
||||||
|
}
|
||||||
}
|
</div>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (method.Value.PendingPayouts.Any())
|
@if (method.Value.PendingPayouts.Any())
|
||||||
{
|
{
|
||||||
<div class="balance d-flex align-items-baseline gap-1">
|
<div class="balance d-flex align-items-baseline gap-1">
|
||||||
|
|||||||
@@ -37,8 +37,6 @@
|
|||||||
<PackageReference Include="AsyncKeyedLock" Version="6.2.6" />
|
<PackageReference Include="AsyncKeyedLock" Version="6.2.6" />
|
||||||
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" />
|
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="8.0.0" />
|
||||||
<PackageReference Include="Laraue.EfCoreTriggers.PostgreSql" Version="8.0.1" />
|
<PackageReference Include="Laraue.EfCoreTriggers.PostgreSql" Version="8.0.1" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -2,20 +2,24 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
using WalletWasabi.Blockchain.Analysis;
|
||||||
|
using WalletWasabi.Blockchain.Analysis.Clustering;
|
||||||
using WalletWasabi.Blockchain.Keys;
|
using WalletWasabi.Blockchain.Keys;
|
||||||
using WalletWasabi.Blockchain.TransactionOutputs;
|
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||||
|
using WalletWasabi.Blockchain.Transactions;
|
||||||
using WalletWasabi.Crypto.Randomness;
|
using WalletWasabi.Crypto.Randomness;
|
||||||
using WalletWasabi.Exceptions;
|
using WalletWasabi.Exceptions;
|
||||||
using WalletWasabi.Extensions;
|
using WalletWasabi.Extensions;
|
||||||
using WalletWasabi.Helpers;
|
using WalletWasabi.Helpers;
|
||||||
|
using WalletWasabi.Models;
|
||||||
using WalletWasabi.WabiSabi;
|
using WalletWasabi.WabiSabi;
|
||||||
using WalletWasabi.WabiSabi.Backend.Rounds;
|
using WalletWasabi.WabiSabi.Backend.Rounds;
|
||||||
using WalletWasabi.WabiSabi.Client;
|
using WalletWasabi.WabiSabi.Client;
|
||||||
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
|
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
|
||||||
|
using WalletWasabi.WabiSabi.Models;
|
||||||
|
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
|
||||||
using WalletWasabi.Wallets;
|
using WalletWasabi.Wallets;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.Wabisabi;
|
namespace BTCPayServer.Plugins.Wabisabi;
|
||||||
@@ -24,15 +28,25 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
{
|
{
|
||||||
private readonly BTCPayWallet _wallet;
|
private readonly BTCPayWallet _wallet;
|
||||||
|
|
||||||
|
private static BlockchainAnalyzer BlockchainAnalyzer { get; } = new();
|
||||||
public BTCPayCoinjoinCoinSelector(BTCPayWallet wallet)
|
public BTCPayCoinjoinCoinSelector(BTCPayWallet wallet)
|
||||||
{
|
{
|
||||||
_wallet = wallet;
|
_wallet = wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(ImmutableList<SmartCoin> selected, Func<IEnumerable<SmartCoin>, Task<bool>> acceptableSigned)> SelectCoinsAsync(
|
public async
|
||||||
(IEnumerable<SmartCoin> Candidates, IEnumerable<SmartCoin> Ineligible) coinCandidates,
|
Task<(
|
||||||
UtxoSelectionParameters utxoSelectionParameters,
|
ImmutableList<SmartCoin> selected,
|
||||||
Money liquidityClue, SecureRandom secureRandom)
|
Func<IEnumerable<AliceClient>, Task<bool>> acceptableRegistered,
|
||||||
|
Func<
|
||||||
|
ImmutableArray<AliceClient>,
|
||||||
|
(IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment> batchedPayments),
|
||||||
|
TransactionWithPrecomputedData,
|
||||||
|
RoundState,
|
||||||
|
Task<bool>> acceptableOutputs
|
||||||
|
)>
|
||||||
|
SelectCoinsAsync((IEnumerable<SmartCoin> Candidates, IEnumerable<SmartCoin> Ineligible) coinCandidates,
|
||||||
|
UtxoSelectionParameters utxoSelectionParameters, Money liquidityClue, SecureRandom secureRandom)
|
||||||
{
|
{
|
||||||
SmartCoin[] FilterCoinsMore(IEnumerable<SmartCoin> coins)
|
SmartCoin[] FilterCoinsMore(IEnumerable<SmartCoin> coins)
|
||||||
{
|
{
|
||||||
@@ -145,18 +159,114 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
_wallet.LogTrace(solution.ToString());
|
_wallet.LogTrace(solution.ToString());
|
||||||
return (solution.Coins.ToImmutableList(), async coins =>
|
|
||||||
|
async Task<bool> AcceptableRegistered(IEnumerable<AliceClient> coins)
|
||||||
{
|
{
|
||||||
var onlyForConsolidation = mixReasons.Length == 1 && mixReasons.Contains(IWallet.MixingReason.Consolidation);
|
var remainingMixReasons = mixReasons.ToList();
|
||||||
if(onlyForConsolidation && coins.Count() < 10)
|
// var onlyForConsolidation = mixReasons.Length == 1 && mixReasons.Contains(IWallet.MixingReason.Consolidation);
|
||||||
|
if (mixReasons.Contains(IWallet.MixingReason.Consolidation) && coins.Count() < 10)
|
||||||
{
|
{
|
||||||
_wallet.LogTrace("ShouldMix returned true only for consolidation, but less than 10 coins were registered successfully, so we will not mix");
|
remainingMixReasons.Remove(IWallet.MixingReason.Consolidation);
|
||||||
return false;
|
//
|
||||||
|
// _wallet.LogTrace("ShouldMix returned true only for consolidation, but less than 10 coins were registered successfully, so we will not mix");
|
||||||
|
// return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
|
||||||
|
if (mixReasons.Contains(IWallet.MixingReason.Payment) && !solution.HandledPayments.Any())
|
||||||
|
{
|
||||||
|
if (!solution.HandledPayments.Any()) remainingMixReasons.Remove(IWallet.MixingReason.Payment);
|
||||||
|
// can the registered coins handle any of the solution payments?
|
||||||
|
// if not, then we should not proceed
|
||||||
|
// _wallet.LogTrace("ShouldMix returned true only for payments, but no handled payments were found, so we will not mix");
|
||||||
|
//check if we can handle any of the payments in solution.HandledPayments with coins
|
||||||
|
|
||||||
|
var effectiveSumOfRegisteredCoins = coins.Sum(coin => coin.EffectiveValue);
|
||||||
|
var canHandleAPayment = solution.HandledPayments.Any(payment =>
|
||||||
|
{
|
||||||
|
var cost = payment.ToTxOut().EffectiveCost(utxoSelectionParameters.MiningFeeRate) + payment.Value;
|
||||||
|
return effectiveSumOfRegisteredCoins >= cost;
|
||||||
|
});
|
||||||
|
if (!canHandleAPayment)
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.Payment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mixReasons.Contains(IWallet.MixingReason.NotPrivate) && coins.All(coin => coin.SmartCoin.IsPrivate(_wallet.AnonScoreTarget)))
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.NotPrivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coins.Count() != solution.Coins.Count() && mixReasons.Contains(IWallet.MixingReason.ExtraJoin))
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.ExtraJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMixReasons.Count != mixReasons.Length)
|
||||||
|
{
|
||||||
|
_wallet.LogDebug($"Some mix reasons were removed due to the difference in registered coins: {string.Join(", ", mixReasons.Except(remainingMixReasons))}. Remaining: {string.Join(", ", remainingMixReasons)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainingMixReasons.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<bool> AcceptableOutputs(ImmutableArray<AliceClient> registeredAliceClients, (IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment> batchedPayments) outputTxOuts, TransactionWithPrecomputedData unsignedCoinJoin, RoundState roundState)
|
||||||
|
{
|
||||||
|
var remainingMixReasons = mixReasons.ToList();
|
||||||
|
var ourCoins = registeredAliceClients.Select(client => client.SmartCoin);
|
||||||
|
var ourOutputsThatAreNotPayments = outputTxOuts.outputTxOuts.ToList();
|
||||||
|
foreach (var batchedPayment in outputTxOuts.batchedPayments)
|
||||||
|
{
|
||||||
|
ourOutputsThatAreNotPayments.Remove(ourOutputsThatAreNotPayments.First(@out => @out.ScriptPubKey == batchedPayment.Key.ScriptPubKey && @out.Value == batchedPayment.Key.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var smartTx = new SmartTransaction(unsignedCoinJoin.Transaction, new Height(HeightType.Unknown));
|
||||||
|
foreach (var smartCoin in ourCoins)
|
||||||
|
{
|
||||||
|
smartTx.TryAddWalletInput(SmartCoin.Clone(smartCoin));
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputCoins = new List<SmartCoin>();
|
||||||
|
var matchedIndexes = new List<uint>();
|
||||||
|
foreach (var txOut in ourOutputsThatAreNotPayments)
|
||||||
|
{
|
||||||
|
var index = unsignedCoinJoin.Transaction.Outputs.AsIndexedOutputs().First(@out => !matchedIndexes.Contains(@out.N) && @out.TxOut.ScriptPubKey == txOut.ScriptPubKey && @out.TxOut.Value == txOut.Value).N;
|
||||||
|
matchedIndexes.Add(index);
|
||||||
|
var coin = new SmartCoin(smartTx, index, new HdPubKey(new Key().PubKey, new KeyPath(0, 0, 0, 0, 0, 0), LabelsArray.Empty, KeyState.Clean));
|
||||||
|
smartTx.TryAddWalletOutput(coin);
|
||||||
|
outputCoins.Add(coin);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockchainAnalyzer.Analyze(smartTx);
|
||||||
|
var wavgInAnon = CoinjoinAnalyzer.WeightedAverage.Invoke(ourCoins.Select(coin => new CoinjoinAnalyzer.AmountWithAnonymity(coin.AnonymitySet, new Money(coin.Amount, MoneyUnit.BTC))));
|
||||||
|
var wavgOutAnon = CoinjoinAnalyzer.WeightedAverage.Invoke(outputCoins.Select(coin => new CoinjoinAnalyzer.AmountWithAnonymity(coin.AnonymitySet, new Money(coin.Amount, MoneyUnit.BTC))));
|
||||||
|
|
||||||
|
|
||||||
|
if (!outputTxOuts.batchedPayments.Any())
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.Payment);
|
||||||
|
if (wavgOutAnon < wavgInAnon - CoinJoinCoinSelector.MaxWeightedAnonLoss)
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.NotPrivate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wavgOutAnon < wavgInAnon)
|
||||||
|
{
|
||||||
|
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.NotPrivate);
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.ExtraJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingMixReasons.Contains(IWallet.MixingReason.Consolidation) && ourOutputsThatAreNotPayments.Count > registeredAliceClients.Length)
|
||||||
|
{
|
||||||
|
remainingMixReasons.Remove(IWallet.MixingReason.Consolidation);
|
||||||
|
}
|
||||||
|
|
||||||
} );
|
|
||||||
|
return remainingMixReasons.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (solution.Coins.ToImmutableList(), (IEnumerable<AliceClient> coins) => AcceptableRegistered(coins) , AcceptableOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SubsetSolution> SelectCoinsInternal(UtxoSelectionParameters utxoSelectionParameters,
|
private async Task<SubsetSolution> SelectCoinsInternal(UtxoSelectionParameters utxoSelectionParameters,
|
||||||
@@ -180,37 +290,6 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
|
|
||||||
solution.ConsolidationMode = consolidationMode;
|
solution.ConsolidationMode = consolidationMode;
|
||||||
|
|
||||||
// if (consolidationMode && coins.Count() < 8 && !coinjoiningOnlyForPayments &&
|
|
||||||
// ineligibleCoins.Any(coin => !coin.Confirmed))
|
|
||||||
// {
|
|
||||||
// //if we're in consolidation mode, and there are coins not eligible for a reason that will be ok in the near future, we should try to wait for them to become eligible instead of entering multiple coinjoins, which costs more.
|
|
||||||
// // why are they ineligible? banned if not too far in the future is ok, unconfrmed as well
|
|
||||||
//
|
|
||||||
// // if there are unconfirmed coins, we should wait for them to confirm, but since we cant determine if they will be unconfirmed for a long time,. let's play a random chance game: the more coins we have towards the 8 coin goal, the bigger the chance we proceed with the coinjoin
|
|
||||||
// var rand = Random.Shared.Next(1, 101);
|
|
||||||
// var chance = (coins.Count() / 8) * 100;
|
|
||||||
// _wallet.LogDebug(
|
|
||||||
// $"coin selection: consolidation mode, and there are coins not eligible for a reason that will be ok in the near future, we should try to wait for them to become eligible instead of entering multiple coinjoins, which costs more. random chance to proceed: {chance} > {rand} (random 0-100) continue: {chance > rand}");
|
|
||||||
// if (chance > rand)
|
|
||||||
// {
|
|
||||||
// return solution;
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (fullyPrivate && !coinjoiningOnlyForPayments && percentage )
|
|
||||||
// {
|
|
||||||
// var rand = Random.Shared.Next(1, 1001);
|
|
||||||
// if (rand > _wallet.WabisabiStoreSettings.ExtraJoinProbability)
|
|
||||||
// {
|
|
||||||
// _wallet.LogTrace($"All coins are private and we have no pending payments. Skipping join.");
|
|
||||||
// return solution;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// _wallet.LogTrace(
|
|
||||||
// "All coins are private and we have no pending payments but will join just to reduce timing analysis");
|
|
||||||
// }
|
|
||||||
|
|
||||||
while (remainingCoins.Any())
|
while (remainingCoins.Any())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>Coinjoin</Product>
|
<Product>Coinjoin</Product>
|
||||||
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
||||||
<Version>1.0.71</Version>
|
<Version>1.0.72</Version>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
Submodule submodules/walletwasabi updated: f1feba62af...32d6c82f6e
Reference in New Issue
Block a user