Skip to content

Commit

Permalink
Merge Wallet Get Transaction Performance fix (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
fassadlr authored Mar 24, 2021
1 parent a57bf78 commit f8edec7
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,14 @@ public interface IWalletRepository
/// <returns>The Transaction Count</returns>
int GetTransactionCount(string walletName, string accountName = null);

/// <summary>
/// Returns transaction data and address based on a transaction id.
/// </summary>
/// <param name="walletName">The wallet to query</param>
/// <param name="transactionId">The id of the transaction to find.</param>
/// <returns>The requested transaction data as well as the address it relates to.</returns>
IEnumerable<(HdAddress, IEnumerable<TransactionData>)> GetTransactionsById(string walletName, uint256 transactionId);

Func<string, string> Bech32AddressFunc { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Stratis.Bitcoin.Features.Wallet/WalletFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ private void AddInlineStats(StringBuilder log)
log.AppendLine("Wallet Height".PadRight(LoggingConfiguration.ColumnLength) + $": {this.walletManager.WalletTipHeight}".PadRight(10) + $"(Hash: {this.walletManager.WalletTipHash})");
else
log.AppendLine("Wallet Height".PadRight(LoggingConfiguration.ColumnLength) + ": No Wallet");

log.AppendLine();
}

private void AddComponentStats(StringBuilder log)
Expand Down
60 changes: 48 additions & 12 deletions src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -919,30 +919,66 @@ public AccountHistory GetHistory(HdAccount account)
protected AccountHistory GetHistoryForAccount(HdAccount account, long? prevOutputTxTime = null, int? prevOutputIndex = null, int take = int.MaxValue, string searchQuery = null)
{
Guard.NotNull(account, nameof(account));
FlatHistory[] items;
var historyItems = new List<FlatHistory>();

lock (this.lockObject)
{
// Get transactions contained in the account.
var query = account.GetCombinedAddresses().Where(a => a.Transactions.Any());
static bool coldStakeUtxoFilter(TransactionData d) => d.IsColdCoinStake == null || d.IsColdCoinStake == false;

// When the account is a normal one, we want to filter out all cold stake UTXOs.
if (account.IsNormalAccount())
// If the search query contains a transaction Id, the result needs to be
// built differently.
if (searchQuery != null && uint256.TryParse(searchQuery, out uint256 parsedTxId))
{
if (searchQuery != null && uint256.TryParse(searchQuery, out uint256 parsedTxId))
items = query.SelectMany(s => s.Transactions.Where(t => (t.IsColdCoinStake == null || t.IsColdCoinStake == false) && t.Id == parsedTxId).Select(t => new FlatHistory { Address = s, Transaction = t })).ToArray();
// First get the transaction and associated addresses by transaction id.
IEnumerable<(HdAddress address, IEnumerable<TransactionData> transactionData)> result = this.WalletRepository.GetTransactionsById(account.AccountRoot.Wallet.Name, parsedTxId);

// When the account is a normal one, filter out all cold stake UTXOs.
if (account.IsNormalAccount())
{
foreach (var (address, transactionData) in result)
{
historyItems.AddRange(transactionData.Where(coldStakeUtxoFilter).Select(t => new FlatHistory { Address = address, Transaction = t }));
}
}
// Else just add the set as is.
else
items = query.SelectMany(s => s.Transactions.Where(t => t.IsColdCoinStake == null || t.IsColdCoinStake == false).Select(t => new FlatHistory { Address = s, Transaction = t })).ToArray();
{
foreach (var (address, transactionData) in result)
{
historyItems.AddRange(transactionData.Select(t => new FlatHistory { Address = address, Transaction = t }));
}
}

// Lastly, populate the payment collections.
for (int i = 0; i < historyItems.Count; i++)
{
var toCheck = historyItems[i];
if (toCheck.Transaction.SpendingDetails != null)
{
var paymentDetailsChange = this.WalletRepository.GetPaymentDetails(account.AccountRoot.Wallet.Name, historyItems[i].Transaction, true);
toCheck.Transaction.SpendingDetails.Change = new PaymentCollection(toCheck.Transaction.SpendingDetails, paymentDetailsChange.ToList(), true);

var paymentDetails = this.WalletRepository.GetPaymentDetails(account.AccountRoot.Wallet.Name, historyItems[i].Transaction, false);
toCheck.Transaction.SpendingDetails.Payments = new PaymentCollection(toCheck.Transaction.SpendingDetails, paymentDetails.ToList(), false);

historyItems[i] = toCheck;
}
}
}
// Else query over the transaction set.
else
{
items = account.GetCombinedAddresses()
.Where(a => a.Transactions.Any())
.SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToArray();
// Get transactions contained in the account.
var allAddresses = account.GetCombinedAddresses().Where(a => a.Transactions.Any());

if (account.IsNormalAccount())
historyItems = allAddresses.SelectMany(s => s.Transactions.Where(coldStakeUtxoFilter).Select(t => new FlatHistory { Address = s, Transaction = t })).ToList();
else
historyItems = account.GetCombinedAddresses().Where(a => a.Transactions.Any()).SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToList();
}
}

return new AccountHistory { Account = account, History = items };
return new AccountHistory { Account = account, History = historyItems };
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ public void AddWatchOnlyTransactions(string walletName, string accountName, HdAd

throw;
}
finally
finally
{
walletContainer.WriteLockRelease();
}
Expand Down Expand Up @@ -1724,5 +1724,19 @@ FROM HDTransactionData
yield return addressBase58s;
}
}

/// <inheritdoc />
public IEnumerable<(HdAddress, IEnumerable<TransactionData>)> GetTransactionsById(string walletName, uint256 transactionId)
{
WalletContainer walletContainer = this.GetWalletContainer(walletName);
var hdTransactionData = HDTransactionData.GetTransactionsById(walletContainer.Conn, walletContainer.Wallet.WalletId, transactionId.ToString());
var grouped = hdTransactionData.GroupBy(x => x.Address);

foreach (var group in grouped)
{
var hdAddress = new HdAddress() { Address = group.Key, AddressType = group.First().AddressType };
yield return (hdAddress, group.Select(t => this.ToTransactionData(t, null)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,18 @@ AND AccountIndex IN (SELECT AccountIndex FROM HDAccount WHERE WalletId = {st
AND OutputTxTime = {strTransactionTime}")}
AND OutputTxId = {strTransactionId}");
}

// Retrieves a transaction by it's id.
internal static IEnumerable<HDTransactionData> GetTransactionsById(DBConnection conn, int walletId, string transactionId)
{
string strTransactionId = DBParameter.Create(transactionId);
string strWalletId = DBParameter.Create(walletId);

return conn.Query<HDTransactionData>($@"
SELECT *
FROM HDTransactionData
WHERE WalletId = {strWalletId}
AND (OutputTxId = {strTransactionId} OR SpendTxId = {strTransactionId})");
}
}
}

0 comments on commit f8edec7

Please sign in to comment.