Skip to content

Commit

Permalink
Extending get pending transactions (#1300)
Browse files Browse the repository at this point in the history
* [client-api] extending TxPaginationMeta in GetAccountTransactions, GetAccountAssetsTransactions

Signed-off-by: Piotr Pawlowski <ppiotru@gmail.com>

Co-authored-by: iceseer <iceseer@gmail.com>
  • Loading branch information
Pawlak00 and iceseer committed Aug 30, 2021
1 parent 6d7df9f commit 8f59f79
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 24 deletions.
4 changes: 3 additions & 1 deletion irohad/ametsuchi/impl/postgres_specific_query_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,9 @@ namespace iroha {
return pending_txs_storage_
->getPendingTransactions(creator_id,
q.paginationMeta()->get().pageSize(),
q.paginationMeta()->get().firstTxHash())
q.paginationMeta()->get().firstTxHash(),
q.paginationMeta()->get().firstTxTime(),
q.paginationMeta()->get().lastTxTime())
.match(
[this, &response_txs, &query_hash](auto &&response) {
auto &interface_txs = response.value.transactions;
Expand Down
4 changes: 3 additions & 1 deletion irohad/ametsuchi/impl/rocksdb_specific_query_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,9 @@ operator()(
return pending_txs_storage_
->getPendingTransactions(creator_id,
q.paginationMeta()->get().pageSize(),
q.paginationMeta()->get().firstTxHash())
q.paginationMeta()->get().firstTxHash(),
q.paginationMeta()->get().firstTxTime(),
q.paginationMeta()->get().lastTxTime())
.match(
[this, &response_txs, &query_hash](auto &&response) {
auto &interface_txs = response.value.transactions;
Expand Down
22 changes: 16 additions & 6 deletions irohad/pending_txs_storage/impl/pending_txs_storage_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ PendingTransactionStorageImpl::getPendingTransactions(
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::TransactionsNumberType page_size,
const std::optional<shared_model::interface::types::HashType>
&first_tx_hash) const {
&first_tx_hash,
const std::optional<shared_model::interface::types::TimestampType>
&first_tx_time,
const std::optional<shared_model::interface::types::TimestampType>
&last_tx_time) const {
BOOST_ASSERT_MSG(page_size > 0, "Page size has to be positive");
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
auto account_batches_iterator = storage_.find(account_id);
Expand All @@ -60,13 +64,19 @@ PendingTransactionStorageImpl::getPendingTransactions(

PendingTransactionStorage::Response response;
response.all_transactions_size = account_batches.all_transactions_quantity;
auto remaining_space = page_size;
while (account_batches.batches.end() != batch_iterator
and remaining_space >= batch_iterator->get()->transactions().size()) {
and (response.transactions.size()
+ (*batch_iterator)->transactions().size())
<= page_size) {
auto &txs = batch_iterator->get()->transactions();
response.transactions.insert(
response.transactions.end(), txs.begin(), txs.end());
remaining_space -= txs.size();
std::copy_if(txs.begin(),
txs.end(),
std::back_inserter(response.transactions),
[&first_tx_time, &last_tx_time](auto const &tx) {
auto const ts = tx->createdTime();
return (!first_tx_time || ts >= *first_tx_time)
&& (!last_tx_time || ts <= *last_tx_time);
});
++batch_iterator;
}
if (account_batches.batches.end() != batch_iterator) {
Expand Down
6 changes: 5 additions & 1 deletion irohad/pending_txs_storage/impl/pending_txs_storage_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ namespace iroha {
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::TransactionsNumberType page_size,
const std::optional<shared_model::interface::types::HashType>
&first_tx_hash) const override;
&first_tx_hash,
const std::optional<shared_model::interface::types::TimestampType>
&first_tx_time=std::nullopt,
const std::optional<shared_model::interface::types::TimestampType>
&last_tx_time=std::nullopt) const override;

void insertPresenceCache(
std::shared_ptr<ametsuchi::TxPresenceCache> &cache) override;
Expand Down
10 changes: 9 additions & 1 deletion irohad/pending_txs_storage/pending_txs_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,22 @@ namespace iroha {
* @param page_size - requested page size
* @param first_tx_hash - an optional hash of the first transaction in the
* batch that will be the starting point of returned transactions sequence
* @param first_tx_time - an optional timestamp of first transaction that
* will be included
* @param last_tx_time - an optional timestamp of last transaction that
* will be included
* @return - Response message when query succeeded (next_batch_info might
* not be set when the end is reached). One of ErrorCode in case of error.
*/
virtual expected::Result<Response, ErrorCode> getPendingTransactions(
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::TransactionsNumberType page_size,
const std::optional<shared_model::interface::types::HashType>
&first_tx_hash) const = 0;
&first_tx_hash,
const std::optional<shared_model::interface::types::TimestampType>
&first_tx_time,
const std::optional<shared_model::interface::types::TimestampType>
&last_tx_time) const = 0;

virtual void removeTransaction(
shared_model::interface::types::HashType const &hash) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,15 @@ namespace shared_model {
auto getPendingTransactions(
interface::types::TransactionsNumberType page_size,
const std::optional<interface::types::HashType> &first_hash =
std::nullopt,
const std::optional<interface::types::TimestampType> &first_tx_time =
std::nullopt,
const std::optional<interface::types::TimestampType> &last_tx_time =
std::nullopt) const {
return queryField([&](auto proto_query) {
auto query = proto_query->mutable_get_pending_transactions();
setTxPaginationMeta(
query->mutable_pagination_meta(), page_size, first_hash);
query->mutable_pagination_meta(), page_size, first_hash, nullptr, first_tx_time, last_tx_time);
});
}

Expand Down
7 changes: 6 additions & 1 deletion shared_model/validators/protobuf/proto_query_validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "validators/protobuf/proto_query_validator.hpp"

#include <google/protobuf/util/time_util.h>

#include <ciso646>

#include "validators/validation_error_helpers.hpp"
Expand Down Expand Up @@ -114,6 +113,12 @@ namespace shared_model {
error_creator |= validateTxPaginationMeta(gaat.pagination_meta());
break;
}
//this lines validate TxPaginationMeta in GetPendingTransactions
case iroha::protocol::Query_Payload::kGetPendingTransactions: {
const auto &gaat = qry.payload().get_pending_transactions();
error_creator |= validateTxPaginationMeta(gaat.pagination_meta());
break;
}
default:
break;
}
Expand Down
12 changes: 8 additions & 4 deletions test/module/irohad/ametsuchi/postgres_query_executor_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1935,8 +1935,10 @@ namespace iroha {
.getPendingTransactions(kPageSize)
.build();

EXPECT_CALL(*pending_txs_storage,
getPendingTransactions(account_id, kPageSize, ::testing::_))
EXPECT_CALL(
*pending_txs_storage,
getPendingTransactions(
account_id, kPageSize, ::testing::_, ::testing::_, ::testing::_))
.Times(1);

executeQuery(query);
Expand All @@ -1955,8 +1957,10 @@ namespace iroha {
.getPendingTransactions(kPageSize, kFirstTxHash)
.build();

EXPECT_CALL(*pending_txs_storage,
getPendingTransactions(account_id, kPageSize, ::testing::_))
EXPECT_CALL(
*pending_txs_storage,
getPendingTransactions(
account_id, kPageSize, ::testing::_, ::testing::_, ::testing::_))
.WillOnce(Return(iroha::expected::makeError(
PendingTransactionStorage::ErrorCode::kNotFound)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ namespace iroha {
getPendingTransactions,
shared_model::interface::types::SharedTxsCollectionType(
const shared_model::interface::types::AccountIdType &account_id));
MOCK_CONST_METHOD3(
MOCK_METHOD(
(expected::Result<Response, ErrorCode>),
getPendingTransactions,
expected::Result<Response, ErrorCode>(
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::TransactionsNumberType
page_size,
const std::optional<shared_model::interface::types::HashType>
&first_tx_hash));
(const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::TransactionsNumberType page_size,
const std::optional<shared_model::interface::types::HashType>
&first_tx_hash,
const std::optional<shared_model::interface::types::TimestampType>
&first_tx_time,
const std::optional<shared_model::interface::types::TimestampType>
&last_tx_time), (const));
MOCK_METHOD1(insertPresenceCache,
void(std::shared_ptr<ametsuchi::TxPresenceCache> &cache));
MOCK_METHOD(void,
Expand Down
130 changes: 129 additions & 1 deletion test/module/irohad/pending_txs_storage/pending_txs_storage_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/

#include <gtest/gtest.h>

#include "common/result.hpp"
#include "datetime/time.hpp"
#include "framework/crypto_literals.hpp"
Expand Down Expand Up @@ -52,6 +51,14 @@ class PendingTxsStorageFixture : public ::testing::Test {
makeSignature("1"_hex_sig, "pub_key_1"_hex_pubkey));
}

auto twoTransactionsBatch(const int64_t first_tx_time,
const int64_t last_tx_time) {
return addSignatures(
makeTestBatch(txBuilder(2, first_tx_time, 2, "alice@iroha"),
txBuilder(2, last_tx_time, 2, "bob@iroha")),
0,
makeSignature("1"_hex_sig, "pub_key_1"_hex_pubkey));
}
void checkResponse(const Response &actual, const Response &expected) {
EXPECT_EQ(actual.transactions.size(), expected.transactions.size());
// generally it's illegal way to verify the correctness.
Expand Down Expand Up @@ -130,6 +137,127 @@ TEST_F(PendingTxsStorageFixture, InsertionTest) {
}
}

/**
* Timestamp in PaginationMeta works in PendingTxsStorage
* @given two batches of two transactions and storage
* @when storage receives updated mst state with the batch
* @then list of pending transactions beetwen two timestamps can be obtained
*/
TEST_F(PendingTxsStorageFixture, TxPaginationTestFirsTimeLastTimeSpecified) {
auto state = emptyState();
auto first_time = 1000001;
auto transactions = twoTransactionsBatch(1000010, 1000015);
auto last_time = 1000020;
*state += transactions;
auto transactions1 = twoTransactionsBatch(1000025, 1000030);
*state += transactions1;
const auto kPageSize = 100u;
Response expected;
expected.transactions.insert(expected.transactions.end(),
transactions->transactions().begin(),
transactions->transactions().end());
expected.all_transactions_size = transactions->transactions().size();
storage_->updatedBatchesHandler(state);
const auto &creator = "alice@iroha";
auto pending = storage_->getPendingTransactions(creator, kPageSize, std::nullopt, first_time, last_time);

IROHA_ASSERT_RESULT_VALUE(pending);
ASSERT_EQ(expected.all_transactions_size, pending.assumeValue().transactions.size() );
}

/**
* Timestamp in PaginationMeta works in PendingTxsStorage
* @given two batches of two transactions and storage
* @when storage receives updated mst state with the batch
* @then list of pending transactions starting from specified timestamp can be obtained
*/
TEST_F(PendingTxsStorageFixture, TxPaginationTestFirstTimeSpecified) {
auto state = emptyState();
auto transactions = twoTransactionsBatch(1000020,1000030);
*state += transactions;
auto first_time = 1000040;
auto transactions1 = twoTransactionsBatch(1000050, 1000060);
*state += transactions1;
const auto kPageSize = 100u;
Response expected;
expected.transactions.insert(expected.transactions.end(),
transactions1->transactions().begin(),
transactions1->transactions().end());
expected.all_transactions_size = transactions1->transactions().size();

storage_->updatedBatchesHandler(state);
const auto &creator = "alice@iroha";
auto pending = storage_->getPendingTransactions(
creator, kPageSize, std::nullopt, first_time);

IROHA_ASSERT_RESULT_VALUE(pending);
ASSERT_EQ(expected.all_transactions_size,
pending.assumeValue().transactions.size());
}

/**
* Timestamp in PaginationMeta works in PendingTxsStorage
* @given two batches of two transactions and storage
* @when storage receives updated mst state with the batch
* @then list of pending transactions up to specified timestamp can be
* obtained
*/
TEST_F(PendingTxsStorageFixture, TxPaginationTestLastTimeSpecified) {
auto state = emptyState();
auto transactions = twoTransactionsBatch(1000040, 1000050);
*state += transactions;
auto transactions1 = twoTransactionsBatch(1000060, 1000070);
*state += transactions1;
auto last_time = 1000080;
const auto kPageSize = 100u;
Response expected;
expected.transactions.insert(expected.transactions.end(),
transactions->transactions().begin(),
transactions->transactions().end());
expected.transactions.insert(expected.transactions.end(),
transactions1->transactions().begin(),
transactions1->transactions().end());
expected.all_transactions_size =
transactions->transactions().size() + transactions1->transactions().size();

storage_->updatedBatchesHandler(state);
const auto &creator = "alice@iroha";
auto pending = storage_->getPendingTransactions(
creator, kPageSize, std::nullopt, std::nullopt, last_time);

IROHA_ASSERT_RESULT_VALUE(pending);
ASSERT_EQ(expected.all_transactions_size,
pending.assumeValue().transactions.size());
}

/**
* Timestamp in PaginationMeta works in PendingTxsStorage
* @given Batch of two transactions and storage
* @when storage receives updated mst state with the batch
* @then list of pending transactions up to specified timestamp can be
* obtained
*/
TEST_F(PendingTxsStorageFixture, TxPaginationTestFirstTimeAfterLastTransactionSpecified) {
auto state = emptyState();
auto transactions = twoTransactionsBatch(1000030, 1000040);
*state += transactions;
auto first_time = 1000050;
const auto kPageSize = 100u;
Response expected;
expected.transactions.insert(expected.transactions.end(),
transactions->transactions().begin(),
transactions->transactions().end());
expected.all_transactions_size = transactions->transactions().size();

storage_->updatedBatchesHandler(state);
const auto &creator = "alice@iroha";
auto pending = storage_->getPendingTransactions(
creator, kPageSize, std::nullopt, first_time);

IROHA_ASSERT_RESULT_VALUE(pending);
ASSERT_EQ(0, pending.assumeValue().transactions.size());
}

/**
* All the transactions can be received when exact page size is specified
* @given a storage with a batch with two transactions
Expand Down

0 comments on commit 8f59f79

Please sign in to comment.