Add dependant check
MODIFY
WalletWasabi/Extensions/RPCClientExtensions.cs
WalletWasabi/Extensions/RPCClientExtensions.cs
@@ -206,20 +206,58 @@ namespace NBitcoin.RPC
}
/// <summary>
- /// Decides if any of the transactions are unconfirmed using getrawmempool.
+ /// Gets the transactions those are unconfirmed using getrawmempool.
/// This is efficient when many transaction ids are provided.
/// </summary>
- public static async Task<bool> AnyUnconfirmedAsync(this RPCClient rpcClient, ISet<uint256> transactionHashes)
+ public static async Task<IEnumerable<uint256>> GetUnconfirmedAsync(this RPCClient rpc, IEnumerable<uint256> transactionHashes)
{
- uint256[] unconfirmedTransactionHashes = await rpcClient.GetRawMempoolAsync();
- if (transactionHashes.Intersect(unconfirmedTransactionHashes).Any()) // If there are common elements, then there's unconfirmed.
+ uint256[] unconfirmedTransactionHashes = await rpc.GetRawMempoolAsync();
+ // If there are common elements, then there's unconfirmed.
+ return transactionHashes.Intersect(unconfirmedTransactionHashes);
+ }
+
+ /// <summary>
+ /// Recursively gathers all the dependents of the mempool transactions provided.
+ /// </summary>
+ /// <param name="transactionHashes">Mempool transactions to gather their dependents.</param>
+ /// <param name="includingProvided">Should it include in the result the unconfirmed ones from the provided transactionHashes.</param>
+ /// <param name="likelyProvidedManyConfirmedOnes">If many provided transactionHashes are not confirmed then it optimizes by doing a check in the beginning of which ones are unconfirmed.</param>
+ /// <returns>All the dependents of the provided transactionHashes.</returns>
+ public static async Task<ISet<uint256>> GetAllDependentsAsync(this RPCClient rpc, IEnumerable<uint256> transactionHashes, bool includingProvided, bool likelyProvidedManyConfirmedOnes)
{
- return true;
+ IEnumerable<uint256> workingTxHashes;
+ if (likelyProvidedManyConfirmedOnes) // If confirmed txids are provided, then do a big check first.
+ {
+ workingTxHashes = await rpc.GetUnconfirmedAsync(transactionHashes);
}
else
{
- return false;
+ workingTxHashes = transactionHashes;
}
+
+ var hashSet = new HashSet<uint256>();
+ foreach (var txid in workingTxHashes)
+ {
+ // Go through all the txids provided and getmempoolentry to get the dependents and the confirmation status.
+ var entry = await rpc.GetMempoolEntryAsync(txid, throwIfNotFound: false);
+ if (entry != null)
+ {
+ // If we asked to include the provided transaction hashes into the result then check which ones are confirmed and do so.
+ if (includingProvided)
+ {
+ hashSet.Add(txid);
+ }
+
+ // Get all the dependents of all the dependents except the ones we already know of.
+ var except = entry.Depends.Except(hashSet);
+ var dependentsOfDependents = await rpc.GetAllDependentsAsync(except, includingProvided: true, likelyProvidedManyConfirmedOnes: false);
+
+ // Add them to the hashset.
+ hashSet.UnionWith(dependentsOfDependents);
+ }
+ }
+
+ return hashSet;
}
}
}

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
@@ -34,6 +34,11 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
/// </summary>
public int ConfiguredConfirmationTarget { get; }
+ /// <summary>
+ /// The rate of confirmation target reduction rate that is present in the config file.
+ /// </summary>
+ public double ConfiguredConfirmationTargetReductionRate { get; }
+
public decimal CoordinatorFeePercent { get; }
public int AnonymitySet { get; private set; }
@@ -145,7 +150,7 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
public UtxoReferee UtxoReferee { get; }
- public CcjRound(RPCClient rpc, UtxoReferee utxoReferee, CcjRoundConfig config, int adjustedConfirmationTarget, int configuredConfirmationTarget)
+ public CcjRound(RPCClient rpc, UtxoReferee utxoReferee, CcjRoundConfig config, int adjustedConfirmationTarget, int configuredConfirmationTarget, double configuredConfirmationTargetReductionRate)
{
try
{
@@ -157,6 +162,7 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
AdjustedConfirmationTarget = adjustedConfirmationTarget;
ConfiguredConfirmationTarget = configuredConfirmationTarget;
+ ConfiguredConfirmationTargetReductionRate = configuredConfirmationTargetReductionRate;
CoordinatorFeePercent = (decimal)config.CoordinatorFeePercent;
AnonymitySet = (int)config.AnonymitySet;
InputRegistrationTimeout = TimeSpan.FromSeconds((long)config.InputRegistrationTimeout);
@@ -750,14 +756,9 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
// If the transaction doesn't spend unconfirmed coins then the confirmation target can be the one that's been set in the config.
var originalConfirmationTarget = AdjustedConfirmationTarget;
- if (await RpcClient.AnyUnconfirmedAsync(transactionHashes))
- {
- return;
- }
- else
- {
- AdjustedConfirmationTarget = ConfiguredConfirmationTarget;
- }
+ // Note that only dependents matter, spenders doesn't matter much or at all, they just make this transaction to be faster to confirm faster.
+ var dependents = await RpcClient.GetAllDependentsAsync(transactionHashes, includingProvided: true, likelyProvidedManyConfirmedOnes: true);
+ AdjustedConfirmationTarget = AdjustConfirmationTarget(dependents.Count, ConfiguredConfirmationTarget, ConfiguredConfirmationTargetReductionRate); ;
Logger.LogInfo<CcjRound>($"Confirmation target is optimized from {originalConfirmationTarget} sat/b to {AdjustedConfirmationTarget} sat/b.");
}
@@ -1021,6 +1022,17 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
}
+ public static int AdjustConfirmationTarget(int unconfirmedCount, int startingConfirmationTarget, double confirmationTargetReductionRate)
+ {
+ for (int i = 0; i < unconfirmedCount; i++)
+ {
+ startingConfirmationTarget = (int)(startingConfirmationTarget * confirmationTargetReductionRate);
+ }
+
+ startingConfirmationTarget = Math.Max(startingConfirmationTarget, 2); // Conf target should never be less than 2.
+ return startingConfirmationTarget;
+ }
+
#region Modifiers
public async Task BroadcastCoinJoinIfFullySignedAsync()

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -209,13 +209,13 @@ namespace WalletWasabi.Services
if (runningRoundCount == 0)
{
- var round = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value);
+ var round = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value, RoundConfig.ConfirmationTargetReductionRate.Value);
round.CoinJoinBroadcasted += Round_CoinJoinBroadcasted;
round.StatusChanged += Round_StatusChangedAsync;
await round.ExecuteNextPhaseAsync(CcjRoundPhase.InputRegistration, feePerInputs, feePerOutputs);
Rounds.Add(round);
- var round2 = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value);
+ var round2 = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value, RoundConfig.ConfirmationTargetReductionRate.Value);
round2.StatusChanged += Round_StatusChangedAsync;
round2.CoinJoinBroadcasted += Round_CoinJoinBroadcasted;
await round2.ExecuteNextPhaseAsync(CcjRoundPhase.InputRegistration, feePerInputs, feePerOutputs);
@@ -223,7 +223,7 @@ namespace WalletWasabi.Services
}
else if (runningRoundCount == 1)
{
- var round = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value);
+ var round = new CcjRound(RpcClient, UtxoReferee, RoundConfig, confirmationTarget, RoundConfig.ConfirmationTarget.Value, RoundConfig.ConfirmationTargetReductionRate.Value);
round.StatusChanged += Round_StatusChangedAsync;
round.CoinJoinBroadcasted += Round_CoinJoinBroadcasted;
await round.ExecuteNextPhaseAsync(CcjRoundPhase.InputRegistration, feePerInputs, feePerOutputs);
@@ -254,13 +254,7 @@ namespace WalletWasabi.Services
unconfirmedCoinJoinsCount = CoinJoins.Intersect(mempoolHashes).Count();
}
- int confirmationTarget = RoundConfig.ConfirmationTarget.Value;
- for (int i = 0; i < unconfirmedCoinJoinsCount; i++)
- {
- confirmationTarget = (int)(confirmationTarget * RoundConfig.ConfirmationTargetReductionRate.Value);
- }
-
- confirmationTarget = Math.Max(confirmationTarget, 2); // Conf target should never be less than 2.
+ int confirmationTarget = CcjRound.AdjustConfirmationTarget(unconfirmedCoinJoinsCount, RoundConfig.ConfirmationTarget.Value, RoundConfig.ConfirmationTargetReductionRate.Value);
return confirmationTarget;
}
catch (Exception ex)

Add dependant check