Skip to content

Commit

Permalink
[MultiSig] Dust inputs should not be indexed/added in the MS wallet (s…
Browse files Browse the repository at this point in the history
…tratisproject#529)

* Dust inputs are not added to the multisig wallet

* Review changes

* Remove extra filter
  • Loading branch information
fassadlr committed May 27, 2021
1 parent 84e00b6 commit 9b6a66d
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public void CanChangeHeightOfSpendableTransaction()
var transactionData1 = new TransactionData()
{
Id = 1,
Amount = Money.Coins(1),
Index = 1,
BlockHeight = null,
SpendingDetails = null
Expand All @@ -53,14 +54,17 @@ public void CanChangeHeightOfSpendableTransaction()
var transactionData2 = new TransactionData()
{
Id = 2,
Amount = Money.Coins(1),
Index = 0,
BlockHeight = null,
SpendingDetails = null
};

var transactions = new MultiSigTransactions();
transactions.Add(transactionData2);
transactions.Add(transactionData1);
var transactions = new MultiSigTransactions
{
transactionData2,
transactionData1
};

transactionData2.BlockHeight = 1;

Expand All @@ -71,6 +75,37 @@ public void CanChangeHeightOfSpendableTransaction()
transactions.Add(transactionData2);
}

[Fact]
public void DustInputsNotAddedToMultiSigWallet()
{
var transactionData1 = new TransactionData()
{
Id = 1,
Amount = Money.Coins(1),
Index = 1,
BlockHeight = null,
SpendingDetails = null
};

var transactionData2 = new TransactionData()
{
Id = 2,
Amount = Money.Coins(0.0001m),
Index = 0,
BlockHeight = null,
SpendingDetails = null
};

var transactions = new MultiSigTransactions
{
transactionData2,
transactionData1
};

Assert.Equal(2, transactions.Count);
Assert.Single(transactions.GetUnspentTransactions());
}

[Theory]
[ClassData(typeof(TestData))]
public void TransactionDataAddedToMultiSigTransactionsExistsInExpectedLookups(bool hasBlockHeight, bool hasSpendingDetails, bool hasWithdrawalDetails, bool flipBlockHeight, bool flipSpendingDetails, bool flipWithdrawalDetails)
Expand Down
4 changes: 2 additions & 2 deletions src/Stratis.Features.FederatedPeg/FederatedPegSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ 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;
/// <summary>The amount to filter inputs by.</summary>
public const decimal UtxoAmountThreshold = 0.001m;

public const string WalletSyncFromHeightParam = "walletsyncfromheight";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1009,8 +1009,8 @@ private bool Synchronize()
}

// As the CCTS syncs from the federation wallet, it needs to be
// responsible for cleaning transaciton past max reorg.
// Doing this from the federatrion wallet manager could mean transactions
// responsible for cleaning transactions past max reorg.
// Doing this from the federation wallet manager could mean transactions
// are cleaned before they are processed by the CCTS (which means they will
// be wrongly added back.
if (this.federationWalletManager.CleanTransactionsPastMaxReorg(this.TipHashAndHeight.Height))
Expand Down
16 changes: 5 additions & 11 deletions src/Stratis.Features.FederatedPeg/Wallet/MultiSigTransactions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private void RemoveWithdrawal(TransactionData transactionData)

private void AddSpentTransactionByHeight(TransactionData transactionData)
{
if (transactionData.IsSpendable() || transactionData.SpendingDetails.BlockHeight == null)
if (!transactionData.IsSpentAndConfirmed())
return;

if (!this.spentTransactionsByHeightDict.TryGetValue((int)transactionData.SpendingDetails.BlockHeight, out List<TransactionData> txList))
Expand All @@ -84,7 +84,7 @@ private void AddSpentTransactionByHeight(TransactionData transactionData)

private void RemoveSpentTransactionByHeight(TransactionData transactionData)
{
if (transactionData.SpendingDetails?.BlockHeight == null)
if (!transactionData.IsSpentAndConfirmed())
return;

if (this.spentTransactionsByHeightDict.TryGetValue((int)transactionData.SpendingDetails.BlockHeight, out List<TransactionData> txList))
Expand Down Expand Up @@ -120,7 +120,7 @@ public void Add(TransactionData transactionData)
}
catch (Exception err)
{
throw new System.Exception("An error occurred during transaction data addition.", err);
throw new Exception("An error occurred during transaction data addition.", err);
}
}
}
Expand Down Expand Up @@ -293,19 +293,13 @@ 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(bool filterDustTransactions = true)
public TransactionData[] GetUnspentTransactions()
{
lock (this.lockObject)
{
IList<TransactionData> result = this.spendableTransactionList.Keys;

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

return result.ToArray();
return this.spendableTransactionList.Keys.ToArray();
}
}

Expand Down
26 changes: 23 additions & 3 deletions src/Stratis.Features.FederatedPeg/Wallet/TransactionData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public int? BlockHeight
[JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset CreationTime { get; set; }


[JsonProperty(PropertyName = "scriptPubKey")]
[JsonConverter(typeof(ScriptJsonConverter))]
public Script ScriptPubKey { get; set; }
Expand Down Expand Up @@ -131,9 +132,29 @@ public bool IsConfirmed()
return this.BlockHeight != null;
}

/// <summary>
/// Determines if the transaction is spent and confirmed.
/// </summary>
/// <returns><c>true</c> if spending details is not null and block height is set on spending details.</returns>
[NoTrace]
public bool IsSpentAndConfirmed()
{
if (this.spendingDetails == null)
return false;

return this.spendingDetails.BlockHeight != null;
}

/// <summary>
/// Determines whether the transaction is spendable.
/// </summary>
/// <returns><c>true</c> if the amount is more than the dust threshold and spending details are null.</returns>
[NoTrace]
public bool IsSpendable()
{
if (this.Amount < Money.Coins(FederatedPegSettings.UtxoAmountThreshold))
return false;

// TODO: Coinbase maturity check?
return this.SpendingDetails == null;
}
Expand All @@ -143,12 +164,11 @@ public Money SpendableAmount(bool confirmedOnly)
{
// This method only returns a UTXO that has no spending output.
// If a spending output exists (even if its not confirmed) this will return as zero balance.
if (!this.IsSpendable()) return Money.Zero;
if (!this.IsSpendable())
return Money.Zero;

if (confirmedOnly && !this.IsConfirmed())
{
return Money.Zero;
}

return this.Amount;
}
Expand Down

0 comments on commit 9b6a66d

Please sign in to comment.