Skip to content

Commit

Permalink
Filter Transaction History by Transaction Id (#393)
Browse files Browse the repository at this point in the history
* Update WalletService.cs

* Add Filter

* Fix Test

* Perform check for cold staking txs
  • Loading branch information
fassadlr authored Jan 25, 2021
1 parent 0a2bb6b commit 16d355a
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ public void GetHistoryWithValidModelWithoutTransactionSpendingDetailsReturnsWall
List<FlatHistory> flat = addresses.SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToList();

var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };
this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny<string>())).Returns(accountsHistory);
this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny<string>(), null)).Returns(accountsHistory);
this.walletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);
this.walletManager.Setup(w => w.GetAccounts(walletName)).Returns(new List<HdAccount> { account });

var receipt = new Receipt(null, 12345, new Log[0], null, null, null, uint160.Zero, true, null, null, 2, 100000);
this.receiptRepository.Setup(x => x.RetrieveMany(It.IsAny<IList<uint256>>()))
.Returns(new List<Receipt> {receipt});
.Returns(new List<Receipt> { receipt });
this.callDataSerializer.Setup(x => x.Deserialize(It.IsAny<byte[]>()))
.Returns(Result.Ok(new ContractTxData(0, 0, (Stratis.SmartContracts.RuntimeObserver.Gas)0, new uint160(0), null, null)));

Expand Down Expand Up @@ -151,12 +151,12 @@ public void GetHistoryWithValidModelWithSkipAndTakeReturnsWalletHistoryModel()

for (int i = 0; i < totalHistoryLength; i++)
{
TransactionData createTransaction = WalletTestsHelpers.CreateTransaction(new uint256((ulong) i), new Money(500000), 100 + i);
TransactionData createTransaction = WalletTestsHelpers.CreateTransaction(new uint256((ulong)i), new Money(500000), 100 + i);
createTransaction.SpendingDetails = new SpendingDetails
{
BlockHeight = 100 + i,
CreationTime = DateTimeOffset.Now,
TransactionId = new uint256((ulong) i),
TransactionId = new uint256((ulong)i),
Payments = new List<PaymentDetails>
{
new PaymentDetails
Expand All @@ -180,7 +180,7 @@ public void GetHistoryWithValidModelWithSkipAndTakeReturnsWalletHistoryModel()
List<FlatHistory> flat = addresses.SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToList();

var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };
this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny<string>())).Returns(accountsHistory);
this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny<string>(), null)).Returns(accountsHistory);
this.walletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);
this.walletManager.Setup(w => w.GetAccounts(walletName)).Returns(new List<HdAccount> { account });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public IActionResult GetHistory(GetHistoryRequest request)
HdAccount account = this.walletManager.GetAccounts(request.WalletName).First();

// Get a list of all the transactions found in an account (or in a wallet if no account is specified), with the addresses associated with them.
IEnumerable<AccountHistory> accountsHistory = this.walletManager.GetHistory(request.WalletName, account.Name);
IEnumerable<AccountHistory> accountsHistory = this.walletManager.GetHistory(request.WalletName, account.Name, null);

// Wallet manager returns only 1 when an account name is specified.
AccountHistory accountHistory = accountsHistory.First();
Expand Down
20 changes: 10 additions & 10 deletions src/Stratis.Bitcoin.Features.Wallet.Tests/WalletControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ public async Task RecoverWalletWithFileNotFoundExceptionReturnsNotFound()
[Fact]
public async Task RecoverWalletWithExceptionReturnsBadRequest()
{
var mockWalletManager = this.ConfigureMock<IWalletManager>(mock => {
var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
{
mock.Setup(w => w.RecoverWallet(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<DateTime>(), null, null))
.Throws(new FormatException("Formatting failed."));
Expand Down Expand Up @@ -768,7 +769,7 @@ public async Task GetHistoryWithoutAddressesReturnsEmptyModel()
{
string walletName = "myWallet";
var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount)).Returns(
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null)).Returns(
new List<AccountHistory>
{
new AccountHistory
Expand Down Expand Up @@ -815,7 +816,7 @@ public async Task GetHistoryWithValidModelWithoutTransactionSpendingDetailsRetur

var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };
var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount))
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null))
.Returns(accountsHistory));
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);

Expand Down Expand Up @@ -891,7 +892,7 @@ public async Task GetHistoryWithCoinStakeWithMultipleInputs()
var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };
var mockWalletManager = this.ConfigureMock<IWalletManager>();

mockWalletManager.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount))
mockWalletManager.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null))
.Returns(accountsHistory);
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);

Expand Down Expand Up @@ -957,7 +958,7 @@ public async Task GetHistoryWithValidModelWithTransactionSpendingDetailsReturnsW
var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };

var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount))
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null))
.Returns(accountsHistory));
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);

Expand Down Expand Up @@ -1037,7 +1038,7 @@ public async Task GetHistoryWithValidModelWithFeeBelowZeroSetsFeeToZero()
var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };

var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount))
mock.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null))
.Returns(accountsHistory));
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);

Expand Down Expand Up @@ -1140,7 +1141,7 @@ public async Task GetHistoryWithDuplicateSpentTransactionsSelectsDistinctsSpentT

var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetWallet(walletName)).Returns(wallet));
mockWalletManager.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount))
mockWalletManager.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null))
.Returns(accountsHistory);

var controller = this.GetWalletController();
Expand Down Expand Up @@ -1177,7 +1178,7 @@ public async Task GetHistoryWithExceptionReturnsBadRequest()
{
string walletName = "myWallet";
var mockWalletManager = this.ConfigureMock<IWalletManager>(mock =>
mock.Setup(w => w.GetHistory("myWallet", WalletManager.DefaultAccount))
mock.Setup(w => w.GetHistory("myWallet", WalletManager.DefaultAccount, null))
.Throws(new InvalidOperationException("Issue retrieving wallets.")));
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(new Wallet());

Expand Down Expand Up @@ -1254,8 +1255,7 @@ public async Task GetHistoryWithChangeAddressesShouldIncludeSpentChangeAddesses(
var mockWalletManager = this.ConfigureMock<IWalletManager>();

var accountsHistory = new List<AccountHistory> { new AccountHistory { History = flat, Account = account } };
mockWalletManager.Setup(w =>
w.GetHistory(walletName, WalletManager.DefaultAccount)).Returns(accountsHistory);
mockWalletManager.Setup(w => w.GetHistory(walletName, WalletManager.DefaultAccount, null)).Returns(accountsHistory);
mockWalletManager.Setup(w => w.GetWallet(walletName)).Returns(wallet);

var controller = this.GetWalletController();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public interface IWalletManager
/// <param name="extPubKey">The extended public key.</param>
/// <param name="accountIndex">The account number.</param>
/// <param name="creationTime">The date and time this wallet was created.</param>
/// <returns></returns>
/// <returns>The recovered wallet.</returns>
Wallet RecoverWallet(string name, ExtPubKey extPubKey, int accountIndex, DateTime creationTime, ChainedHeader lastBlockSynced = null);

/// <summary>
Expand Down Expand Up @@ -244,8 +244,9 @@ public interface IWalletManager
/// </summary>
/// <param name="walletName">The wallet name.</param>
/// <param name="accountName">The account name.</param>
/// <param name="searchQuery">For now this can only be a transaction Id.</param>
/// <returns>Collection of address history and transaction pairs.</returns>
IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null);
IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null, string searchQuery = null);

/// <summary>
/// Gets the history of transactions contained in an account.
Expand All @@ -256,8 +257,9 @@ public interface IWalletManager
/// <param name="prevOutputTxTime">Previous OutputTxTime, used for pagination</param>
/// <param name="prevOutputIndex">Previous prevOutputIndex, used for pagination</param>
/// <param name="take">Number of records to Take</param>
/// <param name="searchQuery">For now this can only be a transaction Id.</param>
/// <returns>Collection of address history and transaction pairs.</returns>
IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null, long? prevOutputTxTime = null, int? prevOutputIndex = null, int? take = int.MaxValue);
IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null, long? prevOutputTxTime = null, int? prevOutputIndex = null, int? take = int.MaxValue, string searchQuery = null);

/// <summary>
/// Gets the history of the transactions in addresses contained in this account.
Expand Down
11 changes: 4 additions & 7 deletions src/Stratis.Bitcoin.Features.Wallet/Services/WalletService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,18 +186,16 @@ public async Task<WalletBalanceModel> GetBalance(
}, cancellationToken);
}

public async Task<WalletHistoryModel> GetHistory(WalletHistoryRequest request,
CancellationToken cancellationToken)
public async Task<WalletHistoryModel> GetHistory(WalletHistoryRequest request, CancellationToken cancellationToken)
{
return await Task.Run(() =>
{
var model = new WalletHistoryModel();

// Get a list of all the transactions found in an account (or in a wallet if no account is specified), with the addresses associated with them.
IEnumerable<AccountHistory> accountsHistory = request.Take == null
? this.walletManager.GetHistory(request.WalletName, request.AccountName)
: this.walletManager.GetHistory(request.WalletName, request.AccountName, request.PrevOutputTxTime,
request.PrevOutputIndex, request.Take);
? this.walletManager.GetHistory(request.WalletName, request.AccountName, request.SearchQuery)
: this.walletManager.GetHistory(request.WalletName, request.AccountName, request.PrevOutputTxTime, request.PrevOutputIndex, request.Take);

foreach (AccountHistory accountHistory in accountsHistory)
{
Expand Down Expand Up @@ -230,8 +228,7 @@ public async Task<WalletHistoryModel> GetHistory(WalletHistoryRequest request,

// Represents a sublist of transactions associated with receive addresses + a sublist of already spent transactions associated with change addresses.
// In effect, we filter out 'change' transactions that are not spent, as we don't want to show these in the history.
foreach (FlatHistory item in items.Where(t =>
!t.Address.IsChangeAddress() || (t.Address.IsChangeAddress() && t.Transaction.IsSpent())))
foreach (FlatHistory item in items.Where(t => !t.Address.IsChangeAddress() || (t.Address.IsChangeAddress() && t.Transaction.IsSpent())))
{
// Count only unique transactions and limit it to MaxHistoryItemsPerAccount.
int processedTransactions = uniqueProcessedTxIds.Count;
Expand Down
23 changes: 13 additions & 10 deletions src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -870,13 +870,13 @@ public IEnumerable<HdAddress> GetNewAddresses(WalletAccountReference accountRefe
return this.WalletRepository.GetUsedAddresses(accountReference, isChange);
}

public IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null)
public IEnumerable<AccountHistory> GetHistory(string walletName, string accountName = null, string searchQuery = null)
{
return this.GetHistory(walletName, accountName, null, null, int.MaxValue);
return this.GetHistory(walletName, accountName, null, null, int.MaxValue, searchQuery);
}

/// <inheritdoc />
public IEnumerable<AccountHistory> GetHistory(string walletName, string accountName, long? prevOutputTxTime, int? prevOutputIndex, int? take = int.MaxValue)
public IEnumerable<AccountHistory> GetHistory(string walletName, string accountName, long? prevOutputTxTime, int? prevOutputIndex, int? take = int.MaxValue, string searchQuery = null)
{
Guard.NotEmpty(walletName, nameof(walletName));

Expand All @@ -903,7 +903,7 @@ public IEnumerable<AccountHistory> GetHistory(string walletName, string accountN

foreach (HdAccount account in accounts)
{
accountsHistory.Add(this.GetHistoryForAccount(account, prevOutputTxTime, prevOutputIndex, take.GetValueOrDefault()));
accountsHistory.Add(this.GetHistoryForAccount(account, prevOutputTxTime, prevOutputIndex, take.GetValueOrDefault(), searchQuery));
}
}

Expand All @@ -915,8 +915,8 @@ public AccountHistory GetHistory(HdAccount account)
{
return this.GetHistoryForAccount(account, null, null, int.MaxValue);
}
protected AccountHistory GetHistoryForAccount(HdAccount account, long? prevOutputTxTime = null, int? prevOutputIndex = null, int take = int.MaxValue)

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;
Expand All @@ -926,10 +926,13 @@ protected AccountHistory GetHistoryForAccount(HdAccount account, long? prevOutpu
// Get transactions contained in the account.
var query = account.GetCombinedAddresses().Where(a => a.Transactions.Any());

// When the account is a normal one, we want to filter out all cold stake UTXOs.
if (account.IsNormalAccount())
{
// When the account is a normal one, we want to filter out all cold stake UTXOs.
items = query.SelectMany(s => s.Transactions.Where(t => t.IsColdCoinStake == null || t.IsColdCoinStake == false).Select(t => new FlatHistory { Address = s, Transaction = t })).ToArray();
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();
else
items = query.SelectMany(s => s.Transactions.Where(t => t.IsColdCoinStake == null || t.IsColdCoinStake == false).Select(t => new FlatHistory { Address = s, Transaction = t })).ToArray();
}
else
{
Expand Down Expand Up @@ -1207,7 +1210,7 @@ public void ProcessBlocks(Func<ChainedHeader, IEnumerable<(ChainedHeader, Block)

if (this.WalletRepository.RewindWallet(walletName, fork).RewindExecuted)
this.logger.LogDebug("Rewound wallet, {0}='{1}', {2}='{3}'", nameof(fork), fork, nameof(this.ChainIndexer.Tip), this.ChainIndexer.Tip?.HashBlock);

// Update the lowest common tip.
walletTip = (fork == null) ? null : walletTip?.FindFork(fork);
}
Expand Down

0 comments on commit 16d355a

Please sign in to comment.