Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MultiSig] Exclude dust from input selection in the MS wallet. #520

Merged
merged 4 commits into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ SpendingDetails spendingDetails()
if (!hasSpendingDetails)
return null;

return new SpendingDetails() {
WithdrawalDetails = hasWithdrawalDetails ? new WithdrawalDetails() {
MatchingDepositId = spendingDepositId
return new SpendingDetails()
{
WithdrawalDetails = hasWithdrawalDetails ? new WithdrawalDetails()
{
MatchingDepositId = spendingDepositId
} : null,
BlockHeight = spendingBlockHeight(),
TransactionId = spendingTransactionId
Expand All @@ -107,6 +109,7 @@ SpendingDetails spendingDetails()
var transactionData = new TransactionData()
{
Id = transactionId,
Amount = Money.Coins(1),
Index = transactionIndex,
BlockHeight = blockHeight(),
SpendingDetails = spendingDetails()
Expand Down
3 changes: 3 additions & 0 deletions src/Stratis.Features.FederatedPeg/FederatedPegSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace Stratis.Features.FederatedPeg
/// <inheritdoc />
public sealed class FederatedPegSettings : IFederatedPegSettings
{
/// <summary>The amount to filter dust inputs by.</summary>
public const decimal DustThreshold = 0.001m;

public const string WalletSyncFromHeightParam = "walletsyncfromheight";

public const string RedeemScriptParam = "redeemscript";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,15 @@ public ConsolidationSignatureResult CombineSignatures(Transaction incomingPartia
/// <summary>
/// Builds a list of consolidation transactions that will need to pass before the next withdrawal transaction can come through.
/// </summary>
/// <returns>A list of consolidation transactions.</returns>
public List<ConsolidationTransaction> CreateRequiredConsolidationTransactions(Money amount)
{
// TODO: This method doesn't need to be public.

lock (this.txLock)
{
// Get all of the inputs
List<UnspentOutputReference> unspentOutputs = this.walletManager
.GetSpendableTransactionsInWallet(WithdrawalTransactionBuilder.MinConfirmations)
.Where(x => x.Transaction.Amount > Money.Coins(0.001m)).ToList();
List<UnspentOutputReference> unspentOutputs = this.walletManager.GetSpendableTransactionsInWallet(WithdrawalTransactionBuilder.MinConfirmations).ToList();

// We shouldn't be consolidating transactions if we have less than 50 UTXOs to spend.
if (unspentOutputs.Count < FederatedPegSettings.MaxInputs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public interface IFederationWalletManager : ILockProtected
/// <summary>
/// Lists all spendable transactions from all accounts in the wallet.
/// </summary>
/// <param name="confirmations">The minimum confirmations to filter by.</param>
/// <returns>A collection of spendable outputs</returns>
IEnumerable<UnspentOutputReference> GetSpendableTransactionsInWallet(int confirmations = 0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ public class FederationWalletManager : LockProtected, IFederationWalletManager
/// </summary>
private Dictionary<OutPoint, TransactionData> outpointLookup => this.Wallet.MultiSigAddress.Transactions.GetOutpointLookup();

// Gateway settings picked up from the node config.
/// <summary>
/// Gateway settings picked up from the node config.
/// </summary>
private readonly IFederatedPegSettings federatedPegSettings;

public FederationWalletManager(
Expand Down Expand Up @@ -322,16 +324,12 @@ public IEnumerable<UnspentOutputReference> GetSpendableTransactionsInWallet(int
{
lock (this.lockObject)
{

if (this.Wallet == null)
{
return Enumerable.Empty<UnspentOutputReference>();
}

UnspentOutputReference[] res;
res = this.GetSpendableTransactions(this.chainIndexer.Tip.Height, confirmations).ToArray();

return res;
return this.GetSpendableTransactions(this.chainIndexer.Tip.Height, confirmations).ToArray();
}
}

Expand Down Expand Up @@ -1040,7 +1038,10 @@ public ValidateTransactionResult ValidateTransaction(Transaction transaction, bo

// Verify that the transaction has valid UTXOs.
if (!this.TransactionHasValidUTXOs(transaction, coins))
{
this.logger.Error($"Transaction '{transaction.GetHash()}' does not have valid UTXOs.");
return ValidateTransactionResult.Failed("Transaction does not have valid UTXOs.");
}

// Verify that there are no earlier unspent UTXOs.
var comparer = Comparer<TransactionData>.Create(DeterministicCoinOrdering.CompareTransactionData);
Expand All @@ -1053,7 +1054,10 @@ public ValidateTransactionResult ValidateTransaction(Transaction transaction, bo
.OrderByDescending(t => t, comparer)
.FirstOrDefault();
if (oldestInput != null && DeterministicCoinOrdering.CompareTransactionData(earliestUnspent, oldestInput) < 0)
{
this.logger.Error($"Earlier unspent UTXOs exist for tx '{transaction.GetHash()}'");
return ValidateTransactionResult.Failed("Earlier unspent UTXOs exist.");
}
}

// Verify that all inputs are signed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ private void AddCoins(TransactionBuilder transactionBuilder, TransactionBuildCon
/// <returns>The coins and unspent outputs that will be used.</returns>
public static (List<Coin>, List<UnspentOutputReference>) DetermineCoins(IFederationWalletManager walletManager, Network network, TransactionBuildContext context, IFederatedPegSettings settings)
{
List<UnspentOutputReference> unspentOutputs = walletManager.GetSpendableTransactionsInWallet(context.MinConfirmations).Where(x => x.Transaction.Amount > Money.Coins(0.001m)).ToList();
List<UnspentOutputReference> unspentOutputs = walletManager.GetSpendableTransactionsInWallet(context.MinConfirmations).ToList();

// Get total spendable balance in the account.
long balance = unspentOutputs.Sum(t => t.Transaction.Amount);
Expand Down
10 changes: 8 additions & 2 deletions src/Stratis.Features.FederatedPeg/Wallet/MultiSigTransactions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,19 @@ public void AfterBlockHeightChanged(TransactionData transactionData)
/// <summary>
/// List all spendable transactions in a multisig address.
/// </summary>
/// <param name="filterDustTransactions">Filter spendable inputs below <see cref="FederatedPegSettings.DustThreshold"/>.</param>
/// <returns>Returns all the unspent <see cref="TransactionData"/> objects.</returns>
[NoTrace]
public TransactionData[] GetUnspentTransactions()
public TransactionData[] GetUnspentTransactions(bool filterDustTransactions = true)
{
lock (this.lockObject)
{
return this.spendableTransactionList.Keys.ToArray();
IList<TransactionData> result = this.spendableTransactionList.Keys;

if (filterDustTransactions)
result = result.Where(x => x.Amount > Money.Coins(FederatedPegSettings.DustThreshold)).ToArray();

return result.ToArray();
}
}

Expand Down