From 16e0b5353a7531a13c70ee27d93dbcafe157842f Mon Sep 17 00:00:00 2001 From: Blockchain Projects BV Date: Tue, 27 Mar 2018 19:30:36 +0300 Subject: [PATCH 1/4] Added Pending-transactions --- libraries/app/api.cpp | 50 +++++ libraries/app/include/graphene/app/api.hpp | 23 +++ tests/tests/network_node_api_tests.cpp | 202 +++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 tests/tests/network_node_api_tests.cpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 6c6359c2..12f2cb9d 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -44,6 +44,18 @@ namespace graphene { namespace app { +namespace { + + std::vector::const_iterator find_transaction( const std::vector& transactions, const transaction& transaction_to_find ) + { + auto transaction_it = std::find_if(transactions.begin(), transactions.end(), + [&]( const signed_transaction& transaction ){ + return transaction.id() == transaction_to_find.id(); + }); + return transaction_it; + } +} + login_api::login_api(application& a) :_app(a) { @@ -193,6 +205,29 @@ namespace graphene { namespace app { network_node_api::network_node_api( application& a ) : _app( a ) { + _pending_trx_connection = _app.chain_database()->on_pending_transaction.connect([this]( const signed_transaction& transaction ){ + auto transaction_it = find_transaction(_pending_transactions, transaction); + if (_pending_transactions.end() == transaction_it) + { + _pending_transactions.push_back(transaction); + } + + if (_on_pending_transaction) + { + _on_pending_transaction(fc::variant(transaction)); + } + }); + + _applied_block_connection = _app.chain_database()->applied_block.connect([this]( const signed_block& block ){ + for (const auto& transaction: block.transactions) + { + auto transaction_it = find_transaction(_pending_transactions, transaction); + if (_pending_transactions.end() != transaction_it) + { + _pending_transactions.erase(transaction_it); + } + } + }); } fc::variant_object network_node_api::get_info() const @@ -227,6 +262,21 @@ namespace graphene { namespace app { return _app.p2p_node()->set_advanced_node_parameters(params); } + std::vector network_node_api::list_pending_transactions() const + { + return _pending_transactions; + } + + void network_node_api::subscribe_to_pending_transactions( std::function callback ) + { + _on_pending_transaction = callback; + } + + void network_node_api::unsubscribe_from_pending_transactions() + { + _on_pending_transaction = std::function(); + } + fc::api login_api::network_broadcast()const { FC_ASSERT(_network_broadcast_api); diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index d4532b42..aa28fd6f 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -264,8 +264,28 @@ namespace graphene { namespace app { */ std::vector get_potential_peers() const; + /** + * @brief Return list of pending transactions. + */ + std::vector list_pending_transactions() const; + + /** + * @brief Subscribes caller for notifications about pending transactions. + * @param callback a functional object which will be called when new transaction is created. + */ + void subscribe_to_pending_transactions(std::function callback); + + /** + * @brief Unsubscribes caller from notifications about pending transactions. + */ + void unsubscribe_from_pending_transactions(); + private: application& _app; + std::vector< signed_transaction > _pending_transactions; + boost::signals2::scoped_connection _pending_trx_connection; + boost::signals2::scoped_connection _applied_block_connection; + std::function _on_pending_transaction; }; class crypto_api @@ -418,6 +438,9 @@ FC_API(graphene::app::network_node_api, (get_potential_peers) (get_advanced_node_parameters) (set_advanced_node_parameters) + (list_pending_transactions) + (subscribe_to_pending_transactions) + (unsubscribe_from_pending_transactions) ) FC_API(graphene::app::crypto_api, (blind_sign) diff --git a/tests/tests/network_node_api_tests.cpp b/tests/tests/network_node_api_tests.cpp new file mode 100644 index 00000000..93dcf443 --- /dev/null +++ b/tests/tests/network_node_api_tests.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2017 Blockchain BV + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +struct network_node_api_tests_fixture : public database_fixture +{ + processed_transaction push_transaction_for_account_creation(const std::string& account_name) + { + auto account_key = generate_private_key(account_name); + signed_transaction trx; + set_expiration(db, trx); + trx.operations.push_back(make_account(account_name, account_key.get_public_key())); + trx.validate(); + return db.push_transaction(trx, ~0); + } + + void trigger_transactions_applying() + { + db.generate_block(db.get_slot_time(1), + db.get_scheduled_witness(1), + generate_private_key("null_key"), + ~0 | database::skip_undo_history_check); + } + + void check_transactions_equal(const transaction& left, const transaction& right) + { + BOOST_CHECK(left.id() == right.id()); + BOOST_CHECK(left.digest() == right.digest()); + } +}; + +BOOST_FIXTURE_TEST_SUITE(network_node_api_tests, network_node_api_tests_fixture) + +BOOST_AUTO_TEST_CASE(list_pending_proposals_empty) { + try { + graphene::app::network_node_api network_node_api(app); + + auto pending_transactions = network_node_api.list_pending_transactions(); + BOOST_CHECK(pending_transactions.empty()); + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(list_pending_proposals_one) { + try { + graphene::app::network_node_api network_node_api(app); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + + auto pending_transactions = network_node_api.list_pending_transactions(); + + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); + check_transactions_equal(pending_transactions[0], sam_transaction); + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(list_pending_proposals_several) { + try { + graphene::app::network_node_api network_node_api(app); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + auto dan_transaction = push_transaction_for_account_creation("dan"); + + auto pending_transactions = network_node_api.list_pending_transactions(); + + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 2); + check_transactions_equal(pending_transactions[0], sam_transaction); + check_transactions_equal(pending_transactions[1], dan_transaction); + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(list_pending_proposals_one_after_applying) { + try { + graphene::app::network_node_api network_node_api(app); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + + auto pending_transactions = network_node_api.list_pending_transactions(); + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); + check_transactions_equal(pending_transactions[0], sam_transaction); + + trigger_transactions_applying(); + + pending_transactions = network_node_api.list_pending_transactions(); + BOOST_CHECK(pending_transactions.empty()); + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(list_pending_proposals_several_after_applying) { + try { + graphene::app::network_node_api network_node_api(app); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + auto dan_transaction = push_transaction_for_account_creation("dan"); + + auto pending_transactions = network_node_api.list_pending_transactions(); + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 2); + check_transactions_equal(pending_transactions[0], sam_transaction); + check_transactions_equal(pending_transactions[1], dan_transaction); + + trigger_transactions_applying(); + + pending_transactions = network_node_api.list_pending_transactions(); + BOOST_CHECK(pending_transactions.empty()); + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(list_pending_proposals_postponed) { + try { + graphene::app::network_node_api network_node_api(app); + + db.modify(db.get_global_properties(), [](global_property_object& properties) { + //Size in bytes. Empiricaly found to limit block size for two test transactions + properties.parameters.maximum_block_size = 650; + }); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + auto dan_transaction = push_transaction_for_account_creation("dan"); + auto jon_transaction = push_transaction_for_account_creation("jon"); + + auto pending_transactions = network_node_api.list_pending_transactions(); + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 3); + check_transactions_equal(pending_transactions[0], sam_transaction); + check_transactions_equal(pending_transactions[1], dan_transaction); + check_transactions_equal(pending_transactions[2], jon_transaction); + + trigger_transactions_applying(); + + pending_transactions = network_node_api.list_pending_transactions(); + BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); + check_transactions_equal(pending_transactions[0], jon_transaction); + + trigger_transactions_applying(); + + pending_transactions = network_node_api.list_pending_transactions(); + BOOST_CHECK(pending_transactions.empty()); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(subscribe_to_pending_transactions) { + try { + graphene::app::network_node_api network_node_api(app); + + signed_transaction transaction_in_notification; + network_node_api.subscribe_to_pending_transactions([&]( const variant& signed_transaction_object ){ + transaction_in_notification = signed_transaction_object.as(); + }); + + auto sam_transaction = push_transaction_for_account_creation("sam"); + check_transactions_equal(sam_transaction, transaction_in_notification); + + auto dan_transaction = push_transaction_for_account_creation("dan"); + check_transactions_equal(dan_transaction, transaction_in_notification); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(unsubscribe_from_pending_transactions) { + try { + graphene::app::network_node_api network_node_api(app); + + network_node_api.subscribe_to_pending_transactions([&]( const variant& signed_transaction_object ){ + BOOST_FAIL("This callback should not be called, because subscription was canceled."); + }); + + network_node_api.unsubscribe_from_pending_transactions(); + + push_transaction_for_account_creation("sam"); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() From cbe88eb51135b403a608e738df81f5ad8798e5fa Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Wed, 11 Apr 2018 14:48:38 +0200 Subject: [PATCH 2/4] [pending] remove expired transactions from list --- libraries/app/api.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 12f2cb9d..9bb00872 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -227,6 +227,21 @@ namespace { _pending_transactions.erase(transaction_it); } } + + /* + * Remove expired transactions from pending_transactions + */ + for (const auto& transaction: _pending_transactions) + { + if (transaction.expiration < block.timestamp) + { + auto transaction_it = find_transaction(_pending_transactions, transaction); + if (_pending_transactions.end() != transaction_it) + { + _pending_transactions.erase(transaction_it); + } + } + } }); } From 2be2826f3c06c96e330ad29c6a30ab8c2c03b470 Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Wed, 11 Apr 2018 15:16:51 +0200 Subject: [PATCH 3/4] [pending] use map instead of vector --- libraries/app/api.cpp | 25 ++++++---------------- libraries/app/include/graphene/app/api.hpp | 4 ++-- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 9bb00872..d80a8c00 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -44,18 +44,6 @@ namespace graphene { namespace app { -namespace { - - std::vector::const_iterator find_transaction( const std::vector& transactions, const transaction& transaction_to_find ) - { - auto transaction_it = std::find_if(transactions.begin(), transactions.end(), - [&]( const signed_transaction& transaction ){ - return transaction.id() == transaction_to_find.id(); - }); - return transaction_it; - } -} - login_api::login_api(application& a) :_app(a) { @@ -206,10 +194,11 @@ namespace { network_node_api::network_node_api( application& a ) : _app( a ) { _pending_trx_connection = _app.chain_database()->on_pending_transaction.connect([this]( const signed_transaction& transaction ){ - auto transaction_it = find_transaction(_pending_transactions, transaction); + + auto transaction_it = _pending_transactions.find(transaction.id()); if (_pending_transactions.end() == transaction_it) { - _pending_transactions.push_back(transaction); + _pending_transactions[transaction.id()] = transaction; } if (_on_pending_transaction) @@ -221,7 +210,7 @@ namespace { _applied_block_connection = _app.chain_database()->applied_block.connect([this]( const signed_block& block ){ for (const auto& transaction: block.transactions) { - auto transaction_it = find_transaction(_pending_transactions, transaction); + auto transaction_it = _pending_transactions.find(transaction.id()); if (_pending_transactions.end() != transaction_it) { _pending_transactions.erase(transaction_it); @@ -233,9 +222,9 @@ namespace { */ for (const auto& transaction: _pending_transactions) { - if (transaction.expiration < block.timestamp) + if (transaction.second.expiration < block.timestamp) { - auto transaction_it = find_transaction(_pending_transactions, transaction); + auto transaction_it = _pending_transactions.find(transaction.second.id()); if (_pending_transactions.end() != transaction_it) { _pending_transactions.erase(transaction_it); @@ -277,7 +266,7 @@ namespace { return _app.p2p_node()->set_advanced_node_parameters(params); } - std::vector network_node_api::list_pending_transactions() const + map network_node_api::list_pending_transactions() const { return _pending_transactions; } diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index aa28fd6f..7aaf06fa 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -267,7 +267,7 @@ namespace graphene { namespace app { /** * @brief Return list of pending transactions. */ - std::vector list_pending_transactions() const; + map list_pending_transactions() const; /** * @brief Subscribes caller for notifications about pending transactions. @@ -282,7 +282,7 @@ namespace graphene { namespace app { private: application& _app; - std::vector< signed_transaction > _pending_transactions; + map _pending_transactions; boost::signals2::scoped_connection _pending_trx_connection; boost::signals2::scoped_connection _applied_block_connection; std::function _on_pending_transaction; From d6d565be82a7f46f4e691b3394ee41a224083ad2 Mon Sep 17 00:00:00 2001 From: Fabian Schuh Date: Wed, 11 Apr 2018 15:39:48 +0200 Subject: [PATCH 4/4] fix unit tests --- tests/tests/network_node_api_tests.cpp | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/tests/network_node_api_tests.cpp b/tests/tests/network_node_api_tests.cpp index 93dcf443..b857cdfe 100644 --- a/tests/tests/network_node_api_tests.cpp +++ b/tests/tests/network_node_api_tests.cpp @@ -51,10 +51,9 @@ struct network_node_api_tests_fixture : public database_fixture ~0 | database::skip_undo_history_check); } - void check_transactions_equal(const transaction& left, const transaction& right) + void check_transaction_in_list(const map& left, const transaction& right) { - BOOST_CHECK(left.id() == right.id()); - BOOST_CHECK(left.digest() == right.digest()); + BOOST_CHECK(left.find(right.id()) != left.end()); } }; @@ -78,7 +77,7 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_one) { auto pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); - check_transactions_equal(pending_transactions[0], sam_transaction); + check_transaction_in_list(pending_transactions, sam_transaction); } FC_LOG_AND_RETHROW() } @@ -92,8 +91,8 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_several) { auto pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 2); - check_transactions_equal(pending_transactions[0], sam_transaction); - check_transactions_equal(pending_transactions[1], dan_transaction); + check_transaction_in_list(pending_transactions, sam_transaction); + check_transaction_in_list(pending_transactions, dan_transaction); } FC_LOG_AND_RETHROW() } @@ -105,7 +104,7 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_one_after_applying) { auto pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); - check_transactions_equal(pending_transactions[0], sam_transaction); + check_transaction_in_list(pending_transactions, sam_transaction); trigger_transactions_applying(); @@ -123,8 +122,8 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_several_after_applying) { auto pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 2); - check_transactions_equal(pending_transactions[0], sam_transaction); - check_transactions_equal(pending_transactions[1], dan_transaction); + check_transaction_in_list(pending_transactions, sam_transaction); + check_transaction_in_list(pending_transactions, dan_transaction); trigger_transactions_applying(); @@ -148,15 +147,15 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_postponed) { auto pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 3); - check_transactions_equal(pending_transactions[0], sam_transaction); - check_transactions_equal(pending_transactions[1], dan_transaction); - check_transactions_equal(pending_transactions[2], jon_transaction); + check_transaction_in_list(pending_transactions, sam_transaction); + check_transaction_in_list(pending_transactions, dan_transaction); + check_transaction_in_list(pending_transactions, jon_transaction); trigger_transactions_applying(); pending_transactions = network_node_api.list_pending_transactions(); BOOST_REQUIRE_EQUAL(pending_transactions.size(), 1); - check_transactions_equal(pending_transactions[0], jon_transaction); + check_transaction_in_list(pending_transactions, jon_transaction); trigger_transactions_applying(); @@ -176,10 +175,10 @@ BOOST_AUTO_TEST_CASE(subscribe_to_pending_transactions) { }); auto sam_transaction = push_transaction_for_account_creation("sam"); - check_transactions_equal(sam_transaction, transaction_in_notification); + BOOST_CHECK(sam_transaction.id() == transaction_in_notification.id()); auto dan_transaction = push_transaction_for_account_creation("dan"); - check_transactions_equal(dan_transaction, transaction_in_notification); + BOOST_CHECK(dan_transaction.id() == transaction_in_notification.id()); } FC_LOG_AND_RETHROW() }