From 7d44c3abb3621b1237254ad887bb09d74d62f8ab Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 16 Oct 2018 11:06:54 -0500 Subject: [PATCH 001/132] initial fix for verify_account_authority --- libraries/app/database_api.cpp | 34 +++++++++++++------ .../app/include/graphene/app/database_api.hpp | 7 ++-- tests/tests/wallet_tests.cpp | 19 +++++++++++ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 77407e1045..d2ec51c2b4 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -167,7 +167,7 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - //private: + // private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); template @@ -272,7 +272,9 @@ class database_api_impl : public std::enable_shared_from_this map< pair, std::function > _market_subscriptions; graphene::chain::database& _db; const application_options* _app_options = nullptr; - + private: + bool public_key_found(const flat_set& to_be_found, + const vector& collection_to_search)const; }; ////////////////////////////////////////////////////////////////////// @@ -2068,17 +2070,29 @@ bool database_api::verify_account_authority( const string& account_name_or_id, c return my->verify_account_authority( account_name_or_id, signers ); } -bool database_api_impl::verify_account_authority( const string& account_name_or_id, const flat_set& keys )const +bool database_api_impl::public_key_found(const flat_set& to_be_found, + const vector& collection_to_search) const { - const account_object* account = get_account_from_string(account_name_or_id); + for (public_key_type from_collection : collection_to_search) + { + for(public_key_type passed_in : to_be_found ) + { + if (passed_in == from_collection) + { + return true; + } + } + } + return false; +} - /// reuse trx.verify_authority by creating a dummy transfer - signed_transaction trx; - transfer_operation op; - op.from = account->id; - trx.operations.emplace_back(op); +bool database_api_impl::verify_account_authority( const string& account_name_or_id, + const flat_set& keys )const +{ + const account_object* account = get_account_from_string(account_name_or_id); - return verify_authority( trx ); + return public_key_found(keys, account->active.get_keys()) + && public_key_found(keys, account->owner.get_keys()); } processed_transaction database_api::validate_transaction( const signed_transaction& trx )const diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 7102ffe62c..1e4e296d2b 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -663,9 +663,12 @@ class database_api bool verify_authority( const signed_transaction& trx )const; /** - * @return true if the signers have enough authority to authorize an account + * @brief Verify that the public keys have enough authority to authorize a transaction + * @param account_name_or_id the account to check + * @param signers the public keys + * @return true if the passed in keys have enough authority to authorize a transaction */ - bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; + bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; /** * Validates a transaction against the current state without broadcasting it on the network. diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 554b729703..c95e39d741 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -76,4 +76,23 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) } FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE(verify_account_authority) { + try { + + ACTORS( (nathan) ); + graphene::app::database_api db_api(db); + + // good keys + flat_set public_keys; + public_keys.emplace(nathan_public_key); + BOOST_CHECK(db_api.verify_account_authority( "nathan", public_keys)); + + // bad keys + flat_set bad_public_keys; + bad_public_keys.emplace(public_key_type("BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); + BOOST_CHECK(!db_api.verify_account_authority( "nathan", bad_public_keys)); + + } FC_LOG_AND_RETHROW() + } + BOOST_AUTO_TEST_SUITE_END() From 7dc0de7f9c494e842fe504149ba7abb1c8d3b90d Mon Sep 17 00:00:00 2001 From: Valera Cogut Date: Fri, 19 Oct 2018 15:59:44 +0300 Subject: [PATCH 002/132] Capture Ctrl+C in cli_wallet when not in daemon mode #1193 --- programs/cli_wallet/main.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 7a50d86cbe..88c027a20e 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -279,6 +279,17 @@ int main( int argc, char** argv ) { wallet_cli->register_api( wapi ); wallet_cli->start(); + + fc::set_signal_handler([](int signal) { + ilog( "Captured SIGINT not in daemon mode" ); + fclose(stdin); + }, SIGINT); + + fc::set_signal_handler([](int signal) { + ilog( "Captured SIGTERM not in daemon mode" ); + fclose(stdin); + }, SIGTERM); + wallet_cli->wait(); } else @@ -288,6 +299,10 @@ int main( int argc, char** argv ) exit_promise->set_value(signal); }, SIGINT); + fc::set_signal_handler([&exit_promise](int signal) { + exit_promise->set_value(signal); + }, SIGTERM); + ilog( "Entering Daemon Mode, ^C to exit" ); exit_promise->wait(); } From 824ed4f2cc5eddee2af8361be8ee8c626739af7d Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 19 Oct 2018 16:09:31 -0500 Subject: [PATCH 003/132] improved way to verify account and keys --- libraries/app/database_api.cpp | 36 ++++++++++++++++------------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index d2ec51c2b4..fb240c4316 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2070,29 +2070,27 @@ bool database_api::verify_account_authority( const string& account_name_or_id, c return my->verify_account_authority( account_name_or_id, signers ); } -bool database_api_impl::public_key_found(const flat_set& to_be_found, - const vector& collection_to_search) const -{ - for (public_key_type from_collection : collection_to_search) - { - for(public_key_type passed_in : to_be_found ) - { - if (passed_in == from_collection) - { - return true; - } - } - } - return false; -} - bool database_api_impl::verify_account_authority( const string& account_name_or_id, const flat_set& keys )const { - const account_object* account = get_account_from_string(account_name_or_id); + // create a dummy transfer + transfer_operation op; + op.from = get_account_from_string(account_name_or_id)->id; + std::vector ops; + ops.emplace_back(op); + + try + { + graphene::chain::verify_authority(ops, keys, + [this]( account_id_type id ){ return &id(_db).active; }, + [this]( account_id_type id ){ return &id(_db).owner; } ); + } + catch (fc::exception& ex) + { + return false; + } - return public_key_found(keys, account->active.get_keys()) - && public_key_found(keys, account->owner.get_keys()); + return true; } processed_transaction database_api::validate_transaction( const signed_transaction& trx )const From 48fa40023a57496fb438a0f5f640d0ec06c969c9 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 19 Oct 2018 16:13:44 -0500 Subject: [PATCH 004/132] Removed unneeded method definition --- libraries/app/database_api.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index fb240c4316..acb90c8af5 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -167,7 +167,7 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - // private: + //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); template @@ -272,9 +272,6 @@ class database_api_impl : public std::enable_shared_from_this map< pair, std::function > _market_subscriptions; graphene::chain::database& _db; const application_options* _app_options = nullptr; - private: - bool public_key_found(const flat_set& to_be_found, - const vector& collection_to_search)const; }; ////////////////////////////////////////////////////////////////////// From c2b63f561bb1e54703c75053f56cbbcc8c304d86 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 22 Oct 2018 20:10:59 -0500 Subject: [PATCH 005/132] Added multiple signatures to test --- tests/tests/wallet_tests.cpp | 55 ++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index c95e39d741..7311a43ce3 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -45,7 +46,7 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) /*** * Act */ - int nbr_keys_desired = 3; + unsigned int nbr_keys_desired = 3; vector derived_keys = graphene::wallet::utility::derive_owner_keys_from_brain_key("SOME WORDS GO HERE", nbr_keys_desired); @@ -70,7 +71,7 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) string expected_prefix = GRAPHENE_ADDRESS_PREFIX; for (auto info : derived_keys) { string description = (string) info.pub_key; - BOOST_CHECK_EQUAL(0, description.find(expected_prefix)); + BOOST_CHECK_EQUAL(0u, description.find(expected_prefix)); } } FC_LOG_AND_RETHROW() @@ -95,4 +96,54 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( any_two_of_three ) +{ + try { + fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); + fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); + fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3")); + const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() ); + fund(nathan); + graphene::app::database_api db_api(db); + + try { + account_update_operation op; + op.account = nathan.id; + op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1); + op.owner = *op.active; + trx.operations.push_back(op); + sign(trx, nathan_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + public_keys.emplace(nathan_key2.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // the other two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key2.get_public_key()); + public_keys.emplace(nathan_key3.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // just one key should not work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() + From 5db31f5e8f2d27427825d28fb68d3763d295f572 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 23 Oct 2018 05:06:58 -0500 Subject: [PATCH 006/132] shorten line length --- tests/tests/wallet_tests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 7311a43ce3..4e91969e10 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -109,7 +109,8 @@ BOOST_AUTO_TEST_CASE( any_two_of_three ) try { account_update_operation op; op.account = nathan.id; - op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1); + op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, + public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1); op.owner = *op.active; trx.operations.push_back(op); sign(trx, nathan_key1); From 7e4e033039bc52a339370954a68d4082a0cd5929 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 23 Oct 2018 12:47:26 -0500 Subject: [PATCH 007/132] add test for multisig --- tests/tests/wallet_tests.cpp | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 4e91969e10..766435e974 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -146,5 +146,47 @@ BOOST_AUTO_TEST_CASE( any_two_of_three ) } } +BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) +{ + try { + fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::digest("key1")); + fc::ecc::private_key alice_key = fc::ecc::private_key::regenerate(fc::digest("key2")); + fc::ecc::private_key bob_key = fc::ecc::private_key::regenerate(fc::digest("key3")); + + const account_object& nathan = create_account("nathan", nathan_key.get_public_key() ); + fund(nathan); + + create_account("alice", alice_key.get_public_key() ); + create_account("bob", bob_key.get_public_key() ); + + graphene::app::database_api db_api(db); + + try { + account_update_operation op; + op.account = nathan.id; + op.active = authority(3, public_key_type(nathan_key.get_public_key()), 1, + public_key_type(alice_key.get_public_key()), 1, public_key_type(bob_key.get_public_key()), 1); + op.owner = *op.active; + trx.operations.push_back(op); + sign(trx, nathan_key); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // requires 3 signatures + { + flat_set public_keys; + public_keys.emplace(nathan_key.get_public_key()); + public_keys.emplace(alice_key.get_public_key()); + public_keys.emplace(bob_key.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() From e03d32914540a8e82fca37504f82aa3715c9d647 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 23 Oct 2018 13:21:38 -0500 Subject: [PATCH 008/132] improved multisig test --- tests/tests/wallet_tests.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 766435e974..569c090ba8 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -149,26 +149,18 @@ BOOST_AUTO_TEST_CASE( any_two_of_three ) BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) { try { - fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::digest("key1")); - fc::ecc::private_key alice_key = fc::ecc::private_key::regenerate(fc::digest("key2")); - fc::ecc::private_key bob_key = fc::ecc::private_key::regenerate(fc::digest("key3")); - - const account_object& nathan = create_account("nathan", nathan_key.get_public_key() ); - fund(nathan); - - create_account("alice", alice_key.get_public_key() ); - create_account("bob", bob_key.get_public_key() ); + ACTORS( (nathan) (alice) (bob) ); graphene::app::database_api db_api(db); try { account_update_operation op; op.account = nathan.id; - op.active = authority(3, public_key_type(nathan_key.get_public_key()), 1, - public_key_type(alice_key.get_public_key()), 1, public_key_type(bob_key.get_public_key()), 1); + op.active = authority(3, nathan_public_key, 1, + alice.id, 1, bob.id, 1); op.owner = *op.active; trx.operations.push_back(op); - sign(trx, nathan_key); + sign(trx, nathan_private_key); PUSH_TX( db, trx, database::skip_transaction_dupe_check ); trx.clear(); } FC_CAPTURE_AND_RETHROW ((nathan.active)) @@ -176,12 +168,19 @@ BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) // requires 3 signatures { flat_set public_keys; - public_keys.emplace(nathan_key.get_public_key()); - public_keys.emplace(alice_key.get_public_key()); - public_keys.emplace(bob_key.get_public_key()); + public_keys.emplace(nathan_public_key); + public_keys.emplace(alice_public_key); + public_keys.emplace(bob_public_key); BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); } + // only 2 signatures given + { + flat_set public_keys; + public_keys.emplace(nathan_public_key); + public_keys.emplace(bob_public_key); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } } catch (fc::exception& e) { edump((e.to_detail_string())); throw; From 673ac2b86574f39fbe420c71c5829c15ef9664a6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 23 Oct 2018 13:49:54 -0500 Subject: [PATCH 009/132] fix short line --- tests/tests/wallet_tests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 569c090ba8..02babadb9c 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -156,8 +156,7 @@ BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) try { account_update_operation op; op.account = nathan.id; - op.active = authority(3, nathan_public_key, 1, - alice.id, 1, bob.id, 1); + op.active = authority(3, nathan_public_key, 1, alice.id, 1, bob.id, 1); op.owner = *op.active; trx.operations.push_back(op); sign(trx, nathan_private_key); From f523ad6eb9c535ad04cb0cfeec96b26f575847e2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 23 Oct 2018 14:51:27 -0500 Subject: [PATCH 010/132] moved tests, fixed comments --- .../app/include/graphene/app/database_api.hpp | 4 +- tests/tests/database_api_tests.cpp | 110 +++++++++++++++++ tests/tests/wallet_tests.cpp | 111 +----------------- 3 files changed, 113 insertions(+), 112 deletions(-) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 1e4e296d2b..65de6e2ff6 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -663,10 +663,10 @@ class database_api bool verify_authority( const signed_transaction& trx )const; /** - * @brief Verify that the public keys have enough authority to authorize a transaction + * @brief Verify that the public keys have enough authority to approve an operation * @param account_name_or_id the account to check * @param signers the public keys - * @return true if the passed in keys have enough authority to authorize a transaction + * @return true if the passed in keys have enough authority to approve an operation */ bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 2577f57710..1eeb177b42 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -831,4 +831,114 @@ BOOST_AUTO_TEST_CASE( get_transaction_hex ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(verify_account_authority) +{ + try { + + ACTORS( (nathan) ); + graphene::app::database_api db_api(db); + + // good keys + flat_set public_keys; + public_keys.emplace(nathan_public_key); + BOOST_CHECK(db_api.verify_account_authority( "nathan", public_keys)); + + // bad keys + flat_set bad_public_keys; + bad_public_keys.emplace(public_key_type("BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); + BOOST_CHECK(!db_api.verify_account_authority( "nathan", bad_public_keys)); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( any_two_of_three ) +{ + try { + fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); + fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); + fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3")); + const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() ); + fund(nathan); + graphene::app::database_api db_api(db); + + try { + account_update_operation op; + op.account = nathan.id; + op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, + public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1); + op.owner = *op.active; + trx.operations.push_back(op); + sign(trx, nathan_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + public_keys.emplace(nathan_key2.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // the other two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key2.get_public_key()); + public_keys.emplace(nathan_key3.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // just one key should not work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) +{ + try { + ACTORS( (nathan) (alice) (bob) ); + + graphene::app::database_api db_api(db); + + try { + account_update_operation op; + op.account = nathan.id; + op.active = authority(3, nathan_public_key, 1, alice.id, 1, bob.id, 1); + op.owner = *op.active; + trx.operations.push_back(op); + sign(trx, nathan_private_key); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // requires 3 signatures + { + flat_set public_keys; + public_keys.emplace(nathan_public_key); + public_keys.emplace(alice_public_key); + public_keys.emplace(bob_public_key); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // only 2 signatures given + { + flat_set public_keys; + public_keys.emplace(nathan_public_key); + public_keys.emplace(bob_public_key); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 02babadb9c..8601f747d5 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -76,115 +76,6 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) } FC_LOG_AND_RETHROW() } - - BOOST_AUTO_TEST_CASE(verify_account_authority) { - try { - - ACTORS( (nathan) ); - graphene::app::database_api db_api(db); - - // good keys - flat_set public_keys; - public_keys.emplace(nathan_public_key); - BOOST_CHECK(db_api.verify_account_authority( "nathan", public_keys)); - - // bad keys - flat_set bad_public_keys; - bad_public_keys.emplace(public_key_type("BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); - BOOST_CHECK(!db_api.verify_account_authority( "nathan", bad_public_keys)); - - } FC_LOG_AND_RETHROW() - } - -BOOST_AUTO_TEST_CASE( any_two_of_three ) -{ - try { - fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); - fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); - fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3")); - const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() ); - fund(nathan); - graphene::app::database_api db_api(db); - - try { - account_update_operation op; - op.account = nathan.id; - op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, - public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1); - op.owner = *op.active; - trx.operations.push_back(op); - sign(trx, nathan_key1); - PUSH_TX( db, trx, database::skip_transaction_dupe_check ); - trx.clear(); - } FC_CAPTURE_AND_RETHROW ((nathan.active)) - - // two keys should work - { - flat_set public_keys; - public_keys.emplace(nathan_key1.get_public_key()); - public_keys.emplace(nathan_key2.get_public_key()); - BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); - } - - // the other two keys should work - { - flat_set public_keys; - public_keys.emplace(nathan_key2.get_public_key()); - public_keys.emplace(nathan_key3.get_public_key()); - BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); - } - - // just one key should not work - { - flat_set public_keys; - public_keys.emplace(nathan_key1.get_public_key()); - BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); - } - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) -{ - try { - ACTORS( (nathan) (alice) (bob) ); - - graphene::app::database_api db_api(db); - - try { - account_update_operation op; - op.account = nathan.id; - op.active = authority(3, nathan_public_key, 1, alice.id, 1, bob.id, 1); - op.owner = *op.active; - trx.operations.push_back(op); - sign(trx, nathan_private_key); - PUSH_TX( db, trx, database::skip_transaction_dupe_check ); - trx.clear(); - } FC_CAPTURE_AND_RETHROW ((nathan.active)) - - // requires 3 signatures - { - flat_set public_keys; - public_keys.emplace(nathan_public_key); - public_keys.emplace(alice_public_key); - public_keys.emplace(bob_public_key); - BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); - } - - // only 2 signatures given - { - flat_set public_keys; - public_keys.emplace(nathan_public_key); - public_keys.emplace(bob_public_key); - BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); - } - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - + BOOST_AUTO_TEST_SUITE_END() From b0783108b72f8c784ff90c8a6101afc9ada0874f Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 24 Oct 2018 07:12:00 -0500 Subject: [PATCH 011/132] clarify comment --- libraries/app/include/graphene/app/database_api.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 65de6e2ff6..9a546eef42 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -663,10 +663,10 @@ class database_api bool verify_authority( const signed_transaction& trx )const; /** - * @brief Verify that the public keys have enough authority to approve an operation + * @brief Verify that the public keys have enough authority to approve an operation for this account * @param account_name_or_id the account to check * @param signers the public keys - * @return true if the passed in keys have enough authority to approve an operation + * @return true if the passed in keys have enough authority to approve an operation for this account */ bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; From c293b4058ca03fbc0db01cf984cc9240a348d4f0 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 26 Oct 2018 18:54:31 -0300 Subject: [PATCH 012/132] bump fc and fix node --- libraries/fc | 2 +- libraries/net/node.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fc b/libraries/fc index a1d84f22c0..8b6a2dd450 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit a1d84f22c07a2875e21a1bab051851f6809a188b +Subproject commit 8b6a2dd450e2c683f234fc8e7af7c8aff5d89607 diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index b2eb5185cb..612529f93c 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -317,7 +317,7 @@ namespace graphene { namespace net { namespace detail { _maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING) { _rate_limiter.set_actual_rate_time_constant(fc::seconds(2)); - fc::rand_pseudo_bytes(&_node_id.data[0], (int)_node_id.size()); + fc::rand_bytes(&_node_id.data[0], (int)_node_id.size()); } node_impl::~node_impl() From 3fb6c44d9e7f951102b222f4678bfe1ce54e6b41 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 28 Oct 2018 11:05:30 -0300 Subject: [PATCH 013/132] try to fix dockercloud --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index fa58c2da1b..363460d2c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ RUN \ git submodule update --init --recursive && \ cmake \ -DCMAKE_BUILD_TYPE=Release \ + -DGRAPHENE_DISABLE_UNITY_BUILD=ON \ . && \ make witness_node cli_wallet && \ install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \ From 3e1de10ddafe1c4ec5c4da50ec7ccb417de38ebc Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 28 Oct 2018 12:05:17 -0300 Subject: [PATCH 014/132] remove DGRAPHENE_DISABLE_UNITY_BUILD from cmake command in dockerfile as it will not fix the dockercloud issue. --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 363460d2c5..fa58c2da1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,6 @@ RUN \ git submodule update --init --recursive && \ cmake \ -DCMAKE_BUILD_TYPE=Release \ - -DGRAPHENE_DISABLE_UNITY_BUILD=ON \ . && \ make witness_node cli_wallet && \ install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \ From c6ca2c8d861c8b805fe4bd1f8a198b080c6cf39a Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 10:26:08 -0500 Subject: [PATCH 015/132] Initial HTLC commit --- libraries/chain/CMakeLists.txt | 3 +- libraries/chain/assert_evaluator.cpp | 8 ++ libraries/chain/db_init.cpp | 9 +- libraries/chain/db_maint.cpp | 2 + libraries/chain/db_update.cpp | 11 ++ libraries/chain/hardfork.d/HTLC.hf | 4 + libraries/chain/htlc_evaluator.cpp | 112 ++++++++++++++++++ .../chain/include/graphene/chain/database.hpp | 1 + .../include/graphene/chain/htlc_evaluator.hpp | 49 ++++++++ .../include/graphene/chain/htlc_object.hpp | 77 ++++++++++++ .../graphene/chain/protocol/assert.hpp | 21 +++- .../include/graphene/chain/protocol/htlc.hpp | 75 ++++++++++++ .../graphene/chain/protocol/operations.hpp | 5 +- .../include/graphene/chain/protocol/types.hpp | 7 +- libraries/chain/protocol/htlc.cpp | 36 ++++++ 15 files changed, 415 insertions(+), 5 deletions(-) create mode 100644 libraries/chain/hardfork.d/HTLC.hf create mode 100644 libraries/chain/htlc_evaluator.cpp create mode 100644 libraries/chain/include/graphene/chain/htlc_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/htlc_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/htlc.hpp create mode 100644 libraries/chain/protocol/htlc.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 6fe3f47883..71d2fd2d2e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -58,7 +58,7 @@ add_library( graphene_chain protocol/fee_schedule.cpp protocol/confidential.cpp protocol/vote.cpp - + protocol/htlc.cpp genesis_state.cpp get_config.cpp @@ -77,6 +77,7 @@ add_library( graphene_chain vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp + htlc_evaluator.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index 3d1b11d257..febf15aeb3 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -49,6 +49,14 @@ struct predicate_evaluator { FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id ); } + void operator()(const hash_operation_predicate& p)const + { + // TODO: Implement this method + } + void operator()(const timeout_operation_predicate& p)const + { + // TODO: Implement this method + } }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 263c5df3e5..63e0738634 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include @@ -125,6 +127,9 @@ const uint8_t witness_object::type_id; const uint8_t worker_object::space_id; const uint8_t worker_object::type_id; +const uint8_t htlc_object::space_id; +const uint8_t htlc_object::type_id; + void database::initialize_evaluators() { @@ -173,6 +178,8 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -216,7 +223,7 @@ void database::initialize_indexes() add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); add_index< primary_index >(); - + add_index< primary_index< htlc_index > >(); add_index< primary_index< simple_index< fba_accumulator_object > > >(); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7f1a8a4c6c..a766084131 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1259,6 +1259,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); + + remove_expired_htlcs(); } } } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 9a5bcad1eb..bd5b9e3c23 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -558,4 +558,15 @@ void database::update_withdraw_permissions() remove(*permit_index.begin()); } +void database::remove_expired_htlcs() +{ + const auto& htlc_idx = get_index_type().indices().get(); + while ( !htlc_idx.empty() && escrow_idx.begin() != htlc_idx.end() + && htlc_idx.begin()->htlc_expiration <= head_block_time() && !htlc_idx.begin()->disputed ) + { + adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + remove( *htlc_idx.begin()); + } +} + } } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf new file mode 100644 index 0000000000..47df3c9f4a --- /dev/null +++ b/libraries/chain/hardfork.d/HTLC.hf @@ -0,0 +1,4 @@ +// htlc support for bitshares-core +#ifndef HTLC_ESCROW_TIME +#define HTLC_ESCROW_TIME (fc::time_point_sec( 1600000000 )) +#endif \ No newline at end of file diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp new file mode 100644 index 0000000000..781aa0f660 --- /dev/null +++ b/libraries/chain/htlc_evaluator.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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 +#include +#include + +namespace graphene { + namespace chain { + + void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) + { + //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, + // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + + FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); + // TODO: what is the fee denominated in? + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount + o.fee) ); + return void_result(); + } + + object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) + { + try { + //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, + // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + + db().adjust_balance( o.source, -o.amount ); + + const htlc_object& esc = db().create([&]( htlc_object& esc ) { + esc.from = o.source; + esc.to = o.destination; + esc.amount = o.amount; + esc.preimage_hash = o.key_hash; + esc.preimage_size = o.key_size; + esc.expiration = o.epoch; + }); + return esc.id; + + } FC_CAPTURE_AND_RETHROW( (o) ) + } + + void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) + { + htlc_obj = &db().get(o.htlc_id); + + // TODO: Use signatures to determine what to do, not whether preimage was provided + if (o.preimage.size() > 0) + { + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + + // hash the preimage given by the user + fc::sha256 attempted_hash = fc::sha256::hash(o.preimage); + // put the preimage hash in a format we can compare + std::vector passed_hash(attempted_hash.data_size()); + char* data = attempted_hash.data(); + for(size_t i = 0; i < attempted_hash.data_size(); ++i) + { + passed_hash[i] = data[i]; + } + + FC_ASSERT(passed_hash == htlc_obj->preimage_hash, "Provided preimage does not generate correct hash."); + } + else + { + FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); + FC_ASSERT(htlc_obj->preimage.size() == 0, "Preimage already provided."); + } + return void_result(); + } + + void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) + { + if (o.preimage.size() > 0) + { + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().modify(*htlc_obj, [&o](htlc_object& obj){ + obj.preimage = o.preimage; + }); + } + else + { + db().adjust_balance(htlc_obj->from, htlc_obj->amount); + db().remove(*htlc_obj); + } + return void_result(); + } + } // namespace chain +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..202b8bcb05 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -466,6 +466,7 @@ namespace graphene { namespace chain { void update_withdraw_permissions(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); + void remove_expired_htlcs(); ///Steps performed only at maintenance intervals ///@{ diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp new file mode 100644 index 0000000000..1181541cc7 --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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. + */ +#pragma once +#include + +namespace graphene { + namespace chain { + + class htlc_create_evaluator : public evaluator + { + public: + typedef htlc_create_operation operation_type; + + void_result do_evaluate( const htlc_create_operation& o); + object_id_type do_apply( const htlc_create_operation& o); + }; + + class htlc_update_evaluator : public evaluator + { + public: + typedef htlc_update_operation operation_type; + + void_result do_evaluate( const htlc_update_operation& o); + void_result do_apply( const htlc_update_operation& o); + const htlc_object* htlc_obj = nullptr; + }; + } // namespace graphene +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp new file mode 100644 index 0000000000..47ef6f492c --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + + /** + * Temporally save escrow transactions until funds are released or operation expired. + */ + class htlc_object : public graphene::db::abstract_object { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_htlc_object_type; + + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; + uint16_t preimage_size; + vector preimage; + }; + + struct by_from_id; + struct by_expiration; + typedef multi_index_container< + htlc_object, + indexed_by< + ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, + + ordered_non_unique< tag< by_expiration >, member< htlc_object, fc::time_point_sec, &htlc_object::expiration > >, + + ordered_non_unique< tag< by_from_id >, + composite_key< htlc_object, + member< htlc_object, account_id_type, &htlc_object::from > + > + > + > + + > htlc_object_index_type; + + typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; + + } } + +FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), + (from)(to)(amount)(expiration)(pending_fee) + (preimage_hash)(preimage_size)(preimage) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index c9f3b2774a..2b01ddd262 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -70,6 +70,21 @@ namespace graphene { namespace chain { bool validate()const{ return true; } }; + struct hash_operation_predicate + { + account_id_type pay_to; + std::vector key_hash; + int64_t key_size; + bool validate() const { return true; } + }; + + struct timeout_operation_predicate + { + account_id_type pay_to; + uint64_t epoch; + bool validate() const { return true; } + }; + /** * When defining predicates do not make the protocol dependent upon * implementation details. @@ -77,7 +92,9 @@ namespace graphene { namespace chain { typedef static_variant< account_name_eq_lit_predicate, asset_symbol_eq_lit_predicate, - block_id_predicate + block_id_predicate, + hash_operation_predicate, + timeout_operation_predicate > predicate; @@ -109,6 +126,8 @@ FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) ) FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) ) FC_REFLECT( graphene::chain::block_id_predicate, (id) ) +FC_REFLECT( graphene::chain::hash_operation_predicate, (pay_to)(key_hash)(key_size) ) +FC_REFLECT( graphene::chain::timeout_operation_predicate, (pay_to)(epoch) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp new file mode 100644 index 0000000000..107a9a9036 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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. + */ +#pragma once +#include +#include +#include +#include +#include + +namespace graphene { + namespace chain { + + struct htlc_create_operation : public base_operation { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; // paid to network + account_id_type source; // where the held monies are to come from + account_id_type destination; // where the held monies will go if the preimage is provided + asset amount; // the amount to hold + std::vector key_hash; // the hash of the preimage + uint16_t key_size; // the size of the preimage + fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed + extensions_type extensions; // for future expansion + + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + account_id_type fee_payer()const { return source; } + + }; + + struct htlc_update_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; // paid to network + htlc_id_type htlc_id; // the object we are attempting to update + account_id_type update_issuer; // who is attempting to update the transaction + std::vector preimage; // the preimage (not used if after epoch timeout) + extensions_type extensions; // for future expansion + + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(update_issuer); } + account_id_type fee_payer()const { return update_issuer; } + }; + } +} + +FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) + +FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)) +FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(update_issuer)(preimage)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index de2cfa7fd9..a666a844ab 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -95,7 +96,9 @@ namespace graphene { namespace chain { bid_collateral_operation, execute_bid_operation, // VIRTUAL asset_claim_pool_operation, - asset_update_issuer_operation + asset_update_issuer_operation, + htlc_create_operation, + htlc_update_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 06ab7ff291..3c334bad44 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -162,7 +162,8 @@ namespace graphene { namespace chain { impl_special_authority_object_type, impl_buyback_object_type, impl_fba_accumulator_object_type, - impl_collateral_bid_object_type + impl_collateral_bid_object_type, + impl_htlc_object_type }; //typedef fc::unsigned_int object_id_type; @@ -214,6 +215,7 @@ namespace graphene { namespace chain { class buyback_object; class fba_accumulator_object; class collateral_bid_object; + class htlc_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -234,6 +236,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; + typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object > htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; @@ -364,6 +367,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_buyback_object_type) (impl_fba_accumulator_object_type) (impl_collateral_bid_object_type) + (impl_htlc_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) @@ -395,6 +399,7 @@ FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type ) FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type ) FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type ) FC_REFLECT_TYPENAME( graphene::chain::collateral_bid_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::htlc_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp new file mode 100644 index 0000000000..1c8aa44dde --- /dev/null +++ b/libraries/chain/protocol/htlc.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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 + +namespace graphene { namespace chain { + + void htlc_create_operation::validate()const { + //TODO: Implement + // signer must equal from + } + void htlc_update_operation::validate()const { + //TODO: Implement + // if preimage is provided, size must equal the size stored in the contract + } +} } From bab9fb75e51ef43e66d1ac97ac2f9feb0ca36253 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 11:09:22 -0500 Subject: [PATCH 016/132] Fix compile errors --- libraries/chain/db_notify.cpp | 8 ++++++++ libraries/chain/db_update.cpp | 4 ++-- libraries/chain/htlc_evaluator.cpp | 4 ++-- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index ff44a177df..24ad3fb342 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -256,6 +256,14 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); // account_id } + void operator()( const htlc_create_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_update_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index bd5b9e3c23..fc10bd6ad8 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -561,8 +561,8 @@ void database::update_withdraw_permissions() void database::remove_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); - while ( !htlc_idx.empty() && escrow_idx.begin() != htlc_idx.end() - && htlc_idx.begin()->htlc_expiration <= head_block_time() && !htlc_idx.begin()->disputed ) + while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() + && htlc_idx.begin()->expiration <= head_block_time() ) { adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); remove( *htlc_idx.begin()); diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 781aa0f660..9d806baca3 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -22,8 +22,8 @@ * THE SOFTWARE. */ #include -#include -#include +#include +#include #include #include diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 107a9a9036..607deddb52 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -24,7 +24,7 @@ #pragma once #include #include -#include +#include #include #include From 81996c43b3e16520b4d017d374add017c6e419ff Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 11:23:18 -0500 Subject: [PATCH 017/132] Fix tests --- tests/common/database_fixture.cpp | 12 ++ tests/tests/htlc_tests.cpp | 279 ++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 tests/tests/htlc_tests.cpp diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 534c03de33..2ac16f2106 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -285,6 +285,18 @@ void database_fixture::verify_asset_supplies( const database& db ) BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value); } + // htlc + const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); + for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) + { + // do not add in htlc escrow objects that are completed + if (itr->preimage.size() == 0) + { + total_balances[itr->amount.asset_id] += itr->amount.amount; + total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; + } + } + for( const asset_object& asset_obj : db.get_index_type().indices() ) { BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp new file mode 100644 index 0000000000..44b49b199e --- /dev/null +++ b/tests/tests/htlc_tests.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * 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. + */ + +// below are for random bytes for htlc +#include +#include +#include +#include +#include +// for htlc timeout +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) + +void generate_random_preimage(uint16_t key_size, std::vector& vec) +{ + std::independent_bits_engine rbe; + std::generate(begin(vec), end(vec), std::ref(rbe)); + return; +} + +std::vector hash_it(std::vector preimage) +{ + fc::sha256 hash = fc::sha256::hash(preimage); + std::vector ret_val(hash.data_size()); + char* data = hash.data(); + for(size_t i = 0; i < hash.data_size(); i++) + { + ret_val[i] = data[i]; + } + return ret_val; +} + +BOOST_AUTO_TEST_CASE( htlc_expires ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); + } + + // verify funds on hold (make sure this can cover fees) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + //BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) > 98000 ); + + // make sure Alice can't get it back before the timeout + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + + // make sure Alice can't spend it. + // make sure Bob (or anyone) can see the details of the transaction + // let it expire (wait for timeout) + std::this_thread::sleep_for(std::chrono::seconds(4)); + // send an update operation to reclaim the funds + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + generate_block(); + trx.clear(); + } + // verify funds return (what about fees?) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 100000 ); + // verify Bob cannot execute the contract after the fact +} + +BOOST_AUTO_TEST_CASE( htlc_fulfilled ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); + } + + // verify funds on hold (make sure this can cover fees) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + + // make sure Alice can't get it back before the timeout + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + + // balance should not have changed + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + // make sure Bob (or anyone) can see the details of the transaction + // send an update operation to claim the funds + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = bob_id; + update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = pre_image; + trx.operations.push_back(update_operation); + sign(trx, bob_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + generate_block(); + trx.clear(); + } + // verify Alice cannot execute the contract after the fact + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + // verify funds end up in Bob's account + BOOST_TEST_CHECK( get_balance(bob_id, graphene::chain::asset_id_type()) == 10000 ); + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); +} + +BOOST_AUTO_TEST_SUITE_END() From 3afa87eb04e15a7609098f3d195f6962c92eec38 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 7 Sep 2018 10:42:19 -0500 Subject: [PATCH 018/132] added signature test --- tests/tests/htlc_tests.cpp | 65 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 44b49b199e..723bea78c3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -276,4 +276,69 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); } +BOOST_AUTO_TEST_CASE( other_peoples_money ) +{ + ACTORS((alice)(bob)); + + fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000; + + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Bob attempts to put a contract on the blockchain using Alice's funds + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, bob_private_key); + try + { + PUSH_TX(db, trx, database::skip_nothing); + BOOST_FAIL( "Bob stole money from Alice!" ); + } catch (fc::exception& ex) + { + // this is supposed to happen + //BOOST_TEST_MESSAGE("This is the error thrown (expected):"); + //BOOST_TEST_MESSAGE(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + trx.clear(); + } + // now try the same but with Alice's signature (should work) + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, database::skip_nothing); + } catch (fc::exception& ex) + { + BOOST_FAIL( "Alice cannot create a contract!" ); + } + trx.clear(); + } +} + BOOST_AUTO_TEST_SUITE_END() From 6e960efb9316c45d70c422aa936ec15f8a379b38 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 14 Sep 2018 05:54:11 -0500 Subject: [PATCH 019/132] Intermediate commit for adding htlc to wallet api --- libraries/app/database_api.cpp | 21 +++++ .../app/include/graphene/app/database_api.hpp | 10 +++ .../include/graphene/chain/protocol/htlc.hpp | 37 ++++---- .../wallet/include/graphene/wallet/wallet.hpp | 34 +++++++ libraries/wallet/wallet.cpp | 72 +++++++++++++++ tests/cli/main.cpp | 88 +++++++++++++++++++ 6 files changed, 247 insertions(+), 15 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index acb90c8af5..3f053b911f 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -167,6 +167,9 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; + // Escrow / HTLC + fc::optional get_htlc(const std::string htlc_id) const; + //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); @@ -416,6 +419,10 @@ void database_api_impl::cancel_all_subscriptions( bool reset_callback, bool rese // // ////////////////////////////////////////////////////////////////////// +optional database_api::get_htlc(const std::string& htlc_id) const +{ + return my->get_htlc( htlc_id ); +} optional database_api::get_block_header(uint32_t block_num)const { return my->get_block_header( block_num ); @@ -2288,6 +2295,20 @@ vector database_api_impl::get_withdraw_permissions_b return result; } +////////////////////////////////////////////////////////////////////// +// // +// Escrow / HTLC // +// // +////////////////////////////////////////////////////////////////////// +fc::optional database_api_impl::get_htlc(const std::string htlc_id) const +{ + FC_ASSERT( htlc_id.size() > 0 ); + const htlc_object* htlc = _db.find(fc::variant(htlc_id, 1).as(1)); + if (htlc) + return *htlc; + return {}; +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 506b427597..2bfd27c6d7 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -267,6 +267,13 @@ class database_api */ account_id_type get_account_id_from_string(const std::string& name_or_id) const; + /** + * @brief Get HTLC object from an ID + * @param htlc_id the id of the HTLC + * @return the HTLC object + */ + fc::optional get_htlc( const std::string& htlc_id ) const; + /** * @brief Get a list of accounts by ID * @param account_names_or_ids IDs or names of the accounts to retrieve @@ -791,6 +798,9 @@ FC_API(graphene::app::database_api, (lookup_asset_symbols) (get_asset_count) + // HTLC / Escrow + (get_htlc) + // Markets / feeds (get_order_book) (get_limit_orders) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 607deddb52..7b9f242d60 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,22 +31,29 @@ namespace graphene { namespace chain { - struct htlc_create_operation : public base_operation { - struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - asset fee; // paid to network - account_id_type source; // where the held monies are to come from - account_id_type destination; // where the held monies will go if the preimage is provided - asset amount; // the amount to hold - std::vector key_hash; // the hash of the preimage - uint16_t key_size; // the size of the preimage - fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed - extensions_type extensions; // for future expansion + struct htlc_create_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + enum hash_algorithm { + UNKNOWN, + RIPEMD160, + SHA256 + }; + asset fee; // paid to network + account_id_type source; // where the held monies are to come from + account_id_type destination; // where the held monies will go if the preimage is provided + asset amount; // the amount to hold + enum hash_algorithm hash_type = hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + std::vector key_hash; // the hash of the preimage + uint16_t key_size; // the size of the preimage + fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed + extensions_type extensions; // for future expansion - void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } - account_id_type fee_payer()const { return source; } + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + account_id_type fee_payer()const { return source; } }; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index ca13357f0c..97ba29cc05 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -490,6 +490,13 @@ class wallet_api */ asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const; + /** + * Returns information about the given HTLC object. + * @param htlc_id the id of the HTLC object. + * @returns the information about the HTLC object + */ + htlc_object get_htlc(string htlc_id) const; + /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up * @returns the id of the named account @@ -1448,6 +1455,33 @@ class wallet_api bool broadcast = false ); + /** + * Create a hashed time lock contract + * + * @param source The account that will reserve the funds (and pay the fee) + * @param destination The account that will receive the funds if the preimage is presented + * @param asset_symbol The asset that is to be traded + * @param amount the amount of the asset that is to be traded + * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. + * @param preimage_hash the hash of the preimage + * @param preimage_size the size of the preimage in bytes + * @param timelock when the time lock expires + * @param broadcast true if you wish to broadcast the transaction + */ + signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, + fc::time_point_sec timelock, bool broadcast = false ); + + /**** + * Update a hashed time lock contract + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param preimage the preimage that should evaluate to the preimage_hash + */ + signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, + bool broadcast = false ); + /** * Get information about a vesting balance object. * diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ac33c180e9..ea4d05ec3f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -645,6 +645,13 @@ class wallet_api_impl return *opt; } + htlc_object get_htlc(string htlc_id) const + { + auto obj = _remote_db->get_htlc(htlc_id); + FC_ASSERT(obj, "HTLC not found"); + return *obj; + } + asset_id_type get_asset_id(string asset_symbol_or_id) const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); @@ -1735,6 +1742,63 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, fc::time_point_sec timelock, bool broadcast = false ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + + htlc_create_operation create_op; + create_op.source = get_account(source).id; + create_op.destination = get_account(destination).id; + create_op.amount = asset_obj->amount_from_string(amount); + create_op.epoch = timelock; + create_op.key_hash = preimage_hash; + create_op.key_size = preimage_size; + if ( "SHA256" == hash_algorithm ) + create_op.hash_type = htlc_create_operation::hash_algorithm::SHA256; + if ( "RIPEMD160" == hash_algorithm ) + create_op.hash_type = htlc_create_operation::hash_algorithm::RIPEMD160; + FC_ASSERT(create_op.hash_type != htlc_create_operation::hash_algorithm::UNKNOWN, + "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); + + signed_transaction tx; + tx.operations.push_back(create_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) + (preimage_hash)(preimage_size)(timelock)(broadcast) ) + } + + signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, bool broadcast ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_update_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.update_issuer = issuer_obj.id; + update_op.preimage = preimage; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -3012,6 +3076,14 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } +signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, + fc::time_point_sec timelock, bool broadcast) +{ + return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + timelock, broadcast); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 393dce4d83..2a9bec5448 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -566,3 +566,91 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) throw; } } + +/////////////////////// +// Start a server and connect using the same calls as the CLI +// Create an HTLC +/////////////////////// +BOOST_AUTO_TEST_CASE( cli_create_htlc ) +{ + using namespace graphene::chain; + using namespace graphene::app; + std::shared_ptr app1; + try { + fc::temp_directory app_dir( graphene::utilities::temp_directory_path() ); + + int server_port_number = 0; + app1 = start_application(app_dir, server_port_number); + + // connect to the server + client_connection con(app1, app_dir, server_port_number); + + BOOST_TEST_MESSAGE("Setting wallet password"); + con.wallet_api_ptr->set_password("supersecret"); + con.wallet_api_ptr->unlock("supersecret"); + + // import Nathan account + BOOST_TEST_MESSAGE("Importing nathan key"); + std::vector nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"}; + BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); + BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); + + BOOST_TEST_MESSAGE("Importing nathan's balance"); + std::vector import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true); + account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan"); + + // upgrade nathan + BOOST_TEST_MESSAGE("Upgrading Nathan to LTM"); + signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true); + account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan"); + + // verify that the upgrade was successful + BOOST_CHECK_PREDICATE( std::not_equal_to(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) + (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); + BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); + + // create a new account for alice + graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", + "nathan", "nathan", true); + // save the private key for this new account in the wallet file + BOOST_CHECK(con.wallet_api_ptr->import_key("alice", bki.wif_priv_key)); + con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + + // attempt to give alice some bitsahres + BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); + signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", + "Here are some CORE token for your new account", true); + + // create an HTLC + std::string preimage_string = "My Secret"; + fc::ripemd160 preimage = fc::ripemd160::hash(preimage_string); + std::vector hash(preimage.data_size()); + for(size_t i = 0; i < preimage.data_size(); ++i) + hash[i] = preimage.data()[i]; + fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); + graphene::chain::signed_transaction result_tx + = con.wallet_api_ptr->create_htlc("alice", "nathan", + "1.3.0", "1000", "RIPEMD160", hash, preimage.data_size(), timelock, true); + + // share the id with bob + graphene::chain::transaction_id_type htlc_trans_id = con.wallet_api_ptr->get_transaction_id(result_tx); + BOOST_TEST_MESSAGE("The hash is " + std::string(htlc_trans_id)); + + // generate a block to get things started + BOOST_CHECK(generate_block(app1)); + // wait for a maintenance interval + BOOST_CHECK(generate_maintenance_block(app1)); + + // send another block to trigger maintenance interval + BOOST_CHECK(generate_maintenance_block(app1)); + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } + app1->shutdown(); +} From f470aa361abc950de3043a9b24504d6a87a3d8f9 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Sep 2018 21:15:17 -0500 Subject: [PATCH 020/132] Improvements to cli_wallet test --- libraries/app/database_api.cpp | 35 ++++++- libraries/chain/db_init.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 41 ++++---- .../include/graphene/chain/htlc_object.hpp | 12 ++- .../include/graphene/chain/protocol/htlc.hpp | 8 +- .../include/graphene/chain/protocol/types.hpp | 4 +- libraries/wallet/wallet.cpp | 11 ++- tests/cli/main.cpp | 95 +++++++++++++------ tests/common/database_fixture.cpp | 8 +- 9 files changed, 148 insertions(+), 68 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 3f053b911f..6e3115b674 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -2300,13 +2301,37 @@ vector database_api_impl::get_withdraw_permissions_b // Escrow / HTLC // // // ////////////////////////////////////////////////////////////////////// +htlc_id_type htlc_string_to_id(std::string incoming) +{ + boost::replace_all(incoming, "\"", ""); + boost::replace_all(incoming, ".", ","); + std::stringstream ss(incoming); + htlc_id_type id; + int i; + int pos = 0; + while(ss >> i) + { + if (pos == 0 && i != id.space_id) + FC_ASSERT("Invalid HTLC ID"); + if (pos == 1 && i != id.type_id) + FC_ASSERT("Invalid HTLC ID"); + if (pos == 2) + id.instance = i; + pos++; + if (ss.peek() == ',') + ss.ignore(); + } + return id; +} + fc::optional database_api_impl::get_htlc(const std::string htlc_id) const { - FC_ASSERT( htlc_id.size() > 0 ); - const htlc_object* htlc = _db.find(fc::variant(htlc_id, 1).as(1)); - if (htlc) - return *htlc; - return {}; + htlc_id_type id = htlc_string_to_id(htlc_id); + const auto& htlc_idx = _db.get_index_type().indices().get(); + auto itr = htlc_idx.find(id); + if (itr == htlc_idx.end()) + return {}; + return (*itr); } ////////////////////////////////////////////////////////////////////// diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 63e0738634..dd9ac41ebb 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -208,6 +208,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index< htlc_index> >(); //Implementation object indexes add_index< primary_index >(); @@ -223,7 +224,6 @@ void database::initialize_indexes() add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); add_index< primary_index >(); - add_index< primary_index< htlc_index > >(); add_index< primary_index< simple_index< fba_accumulator_object > > >(); } diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 9d806baca3..155f26c89d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -56,12 +56,27 @@ namespace graphene { esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; esc.expiration = o.epoch; + esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; } FC_CAPTURE_AND_RETHROW( (o) ) } + template + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + { + T attempted_hash = T::hash(incoming_preimage); + if (attempted_hash.data_size() != valid_hash.size()) + return false; + for(size_t i = 0; i < attempted_hash.data_size(); ++i) + { + if (attempted_hash.data()[i] != valid_hash[i]) + return false; + } + return true; + } + void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) { htlc_obj = &db().get(o.htlc_id); @@ -69,25 +84,21 @@ namespace graphene { // TODO: Use signatures to determine what to do, not whether preimage was provided if (o.preimage.size() > 0) { - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); - // hash the preimage given by the user - fc::sha256 attempted_hash = fc::sha256::hash(o.preimage); - // put the preimage hash in a format we can compare - std::vector passed_hash(attempted_hash.data_size()); - char* data = attempted_hash.data(); - for(size_t i = 0; i < attempted_hash.data_size(); ++i) - { - passed_hash[i] = data[i]; - } + // see if the preimages match + bool match = false; + if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::SHA256) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::RIPEMD160) + match = test_hash(o.preimage, htlc_obj->preimage_hash); - FC_ASSERT(passed_hash == htlc_obj->preimage_hash, "Provided preimage does not generate correct hash."); + FC_ASSERT(match, "Provided preimage does not generate correct hash."); } else { FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); - FC_ASSERT(htlc_obj->preimage.size() == 0, "Preimage already provided."); } return void_result(); } @@ -97,9 +108,7 @@ namespace graphene { if (o.preimage.size() > 0) { db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().modify(*htlc_obj, [&o](htlc_object& obj){ - obj.preimage = o.preimage; - }); + db().remove(*htlc_obj); } else { diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 47ef6f492c..b4d3e1d6b8 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -40,14 +40,20 @@ namespace graphene { namespace chain { static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_htlc_object_type; + enum hash_algorithm { + UNKNOWN, + RIPEMD160, + SHA256 + }; + account_id_type from; account_id_type to; asset amount; fc::time_point_sec expiration; asset pending_fee; vector preimage_hash; + enum hash_algorithm preimage_hash_algorithm; uint16_t preimage_size; - vector preimage; }; struct by_from_id; @@ -72,6 +78,8 @@ namespace graphene { namespace chain { } } +FC_REFLECT_ENUM( graphene::chain::htlc_object::hash_algorithm, (UNKNOWN)(RIPEMD160)(SHA256)); + FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration)(pending_fee) - (preimage_hash)(preimage_size)(preimage) ); \ No newline at end of file + (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7b9f242d60..3a661017cd 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -35,17 +35,13 @@ namespace graphene { { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - enum hash_algorithm { - UNKNOWN, - RIPEMD160, - SHA256 }; asset fee; // paid to network account_id_type source; // where the held monies are to come from account_id_type destination; // where the held monies will go if the preimage is provided asset amount; // the amount to hold - enum hash_algorithm hash_type = hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + enum graphene::chain::htlc_object::hash_algorithm hash_type + = graphene::chain::htlc_object::hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash std::vector key_hash; // the hash of the preimage uint16_t key_size; // the size of the preimage fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 3c334bad44..4d04426527 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -163,7 +163,7 @@ namespace graphene { namespace chain { impl_buyback_object_type, impl_fba_accumulator_object_type, impl_collateral_bid_object_type, - impl_htlc_object_type + impl_htlc_object_type, }; //typedef fc::unsigned_int object_id_type; @@ -236,7 +236,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; - typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object > htlc_id_type; + typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object> htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ea4d05ec3f..6f42a44975 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1759,10 +1759,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = htlc_create_operation::hash_algorithm::SHA256; + create_op.hash_type = htlc_object::hash_algorithm::SHA256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = htlc_create_operation::hash_algorithm::RIPEMD160; - FC_ASSERT(create_op.hash_type != htlc_create_operation::hash_algorithm::UNKNOWN, + create_op.hash_type = htlc_object::hash_algorithm::RIPEMD160; + FC_ASSERT(create_op.hash_type != htlc_object::hash_algorithm::UNKNOWN, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; @@ -3084,6 +3084,11 @@ signed_transaction wallet_api::create_htlc( string source, string destination, s timelock, broadcast); } +graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const +{ + return my->get_htlc(htlc_id); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 2a9bec5448..d93a81aaa9 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -49,6 +49,7 @@ #include #include #endif +#include #include @@ -143,13 +144,15 @@ std::shared_ptr start_application(fc::temp_directory /////////// /// Send a block to the db /// @param app the application +/// @param returned_block the signed block /// @returns true on success /////////// -bool generate_block(std::shared_ptr app) { +bool generate_block(std::shared_ptr app, graphene::chain::signed_block& returned_block) +{ try { fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); auto db = app->chain_database(); - auto block_1 = db->generate_block( db->get_slot_time(1), + returned_block = db->generate_block( db->get_slot_time(1), db->get_scheduled_witness(1), committee_key, database::skip_nothing ); @@ -159,6 +162,12 @@ bool generate_block(std::shared_ptr app) { } } +bool generate_block(std::shared_ptr app) +{ + graphene::chain::signed_block returned_block; + return generate_block(app, returned_block); +} + /////////// /// @brief Skip intermediate blocks, and generate a maintenance block /// @param app the application @@ -609,43 +618,75 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); - // create a new account for alice - graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); - BOOST_CHECK(!bki.brain_priv_key.empty()); - signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", - "nathan", "nathan", true); - // save the private key for this new account in the wallet file - BOOST_CHECK(con.wallet_api_ptr->import_key("alice", bki.wif_priv_key)); - con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + // create a new account for Alice + { + graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", + "nathan", "nathan", true); + // save the private key for this new account in the wallet file + BOOST_CHECK(con.wallet_api_ptr->import_key("alice", bki.wif_priv_key)); + con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + } + + // create a new account for Bob + { + graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "bob", + "nathan", "nathan", true); + // save the private key for this new account in the wallet file + BOOST_CHECK(con.wallet_api_ptr->import_key("bob", bki.wif_priv_key)); + con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + } // attempt to give alice some bitsahres BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", "Here are some CORE token for your new account", true); + BOOST_TEST_MESSAGE("Creating HTLC"); // create an HTLC std::string preimage_string = "My Secret"; - fc::ripemd160 preimage = fc::ripemd160::hash(preimage_string); - std::vector hash(preimage.data_size()); - for(size_t i = 0; i < preimage.data_size(); ++i) - hash[i] = preimage.data()[i]; + fc::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); + std::vector hash(preimage_md.data_size()); + for(size_t i = 0; i < preimage_md.data_size(); ++i) + hash[i] = preimage_md.data()[i]; fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx - = con.wallet_api_ptr->create_htlc("alice", "nathan", - "1.3.0", "1000", "RIPEMD160", hash, preimage.data_size(), timelock, true); + = con.wallet_api_ptr->create_htlc("alice", "bob", + "1.3.0", "1000", "RIPEMD160", hash, preimage_string.size(), timelock, true); + + // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: + BOOST_TEST_MESSAGE("Generating Block"); + graphene::chain::signed_block result_block; + BOOST_CHECK(generate_block(app1, result_block)); + + // get the ID: + htlc_id_type htlc_id = result_block.transactions[5].operation_results[0].get(); + std::string htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + BOOST_TEST_MESSAGE("The HTLC ID is: " + htlc_id_as_string); + + // Bob can now look over Alice's HTLC + BOOST_TEST_MESSAGE("Retrieving HTLC Object by ID"); + try + { + //con.wallet_api_ptr->get_object(htlc_id); + graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + } + catch(std::exception& e) + { + BOOST_FAIL(e.what()); + } + catch(...) + { + BOOST_FAIL("Unknown exception attempting to retrieve HTLC"); + } - // share the id with bob - graphene::chain::transaction_id_type htlc_trans_id = con.wallet_api_ptr->get_transaction_id(result_tx); - BOOST_TEST_MESSAGE("The hash is " + std::string(htlc_trans_id)); + // TODO: API for Bob to claim the funds by giving the preimage + // TODO: Clean the HTLC object (no need to have preimage stored there, nor reference to claim tx) - // generate a block to get things started - BOOST_CHECK(generate_block(app1)); - // wait for a maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); - - // send another block to trigger maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); - // wait for everything to finish up fc::usleep(fc::seconds(1)); } catch( fc::exception& e ) { diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 2ac16f2106..9bbb22ab51 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -289,12 +289,8 @@ void database_fixture::verify_asset_supplies( const database& db ) const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) { - // do not add in htlc escrow objects that are completed - if (itr->preimage.size() == 0) - { - total_balances[itr->amount.asset_id] += itr->amount.amount; - total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; - } + total_balances[itr->amount.asset_id] += itr->amount.amount; + total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; } for( const asset_object& asset_obj : db.get_index_type().indices() ) From b5510acd9501f172c8a4741b8f0ac3e677834652 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Sep 2018 07:41:19 -0500 Subject: [PATCH 021/132] Added update_htlc to wallet api --- libraries/chain/htlc_evaluator.cpp | 29 +++-- .../include/graphene/chain/htlc_object.hpp | 60 +++++----- .../include/graphene/chain/protocol/htlc.hpp | 8 +- .../wallet/include/graphene/wallet/wallet.hpp | 6 +- libraries/wallet/wallet.cpp | 21 ++-- tests/cli/main.cpp | 110 +++++++++++++----- 6 files changed, 146 insertions(+), 88 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 155f26c89d..d0b684193b 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -36,8 +36,10 @@ namespace graphene { // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); - // TODO: what is the fee denominated in? - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount + o.fee) ); + // make sure we have the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ) ); + // make sure we have the funds for the fee + FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee ); return void_result(); } @@ -66,12 +68,15 @@ namespace graphene { template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - T attempted_hash = T::hash(incoming_preimage); + std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); + //T attempted_hash = T::hash(incoming_string); + fc::ripemd160 attempted_hash = fc::ripemd160::hash(incoming_string); if (attempted_hash.data_size() != valid_hash.size()) return false; + char* data = attempted_hash.data(); for(size_t i = 0; i < attempted_hash.data_size(); ++i) { - if (attempted_hash.data()[i] != valid_hash[i]) + if ( ((unsigned char)data[i]) != valid_hash[i]) return false; } return true; @@ -89,9 +94,9 @@ namespace graphene { // see if the preimages match bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::SHA256) + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::RIPEMD160) + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) match = test_hash(o.preimage, htlc_obj->preimage_hash); FC_ASSERT(match, "Provided preimage does not generate correct hash."); @@ -105,16 +110,8 @@ namespace graphene { void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) { - if (o.preimage.size() > 0) - { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - } - else - { - db().adjust_balance(htlc_obj->from, htlc_obj->amount); - db().remove(*htlc_obj); - } + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); return void_result(); } } // namespace chain diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index b4d3e1d6b8..3a833d6334 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -32,33 +32,33 @@ namespace graphene { namespace chain { - /** - * Temporally save escrow transactions until funds are released or operation expired. - */ - class htlc_object : public graphene::db::abstract_object { - public: - static const uint8_t space_id = implementation_ids; - static const uint8_t type_id = impl_htlc_object_type; + enum hash_algorithm { + unknown = 0x00, + ripemd160 = 0x01, + sha256 = 0x02 + }; + + /** + * Temporally save escrow transactions until funds are released or operation expired. + */ + class htlc_object : public graphene::db::abstract_object { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_htlc_object_type; - enum hash_algorithm { - UNKNOWN, - RIPEMD160, - SHA256 - }; + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; + fc::enum_type preimage_hash_algorithm; + uint16_t preimage_size; + }; - account_id_type from; - account_id_type to; - asset amount; - fc::time_point_sec expiration; - asset pending_fee; - vector preimage_hash; - enum hash_algorithm preimage_hash_algorithm; - uint16_t preimage_size; - }; - - struct by_from_id; - struct by_expiration; - typedef multi_index_container< + struct by_from_id; + struct by_expiration; + typedef multi_index_container< htlc_object, indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, @@ -72,14 +72,14 @@ namespace graphene { namespace chain { > > - > htlc_object_index_type; + > htlc_object_index_type; - typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; + typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; - } } +} } -FC_REFLECT_ENUM( graphene::chain::htlc_object::hash_algorithm, (UNKNOWN)(RIPEMD160)(SHA256)); +FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), - (from)(to)(amount)(expiration)(pending_fee) + (from)(to)(amount)(expiration)(pending_fee) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 3a661017cd..5b4e1f0e3b 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -40,8 +40,8 @@ namespace graphene { account_id_type source; // where the held monies are to come from account_id_type destination; // where the held monies will go if the preimage is provided asset amount; // the amount to hold - enum graphene::chain::htlc_object::hash_algorithm hash_type - = graphene::chain::htlc_object::hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + fc::enum_type hash_type + = graphene::chain::hash_algorithm::unknown; // hash algorithm used to create key_hash std::vector key_hash; // the hash of the preimage uint16_t key_size; // the size of the preimage fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed @@ -74,5 +74,5 @@ namespace graphene { FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)) -FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 97ba29cc05..89b9bcb4b1 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1469,8 +1469,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, - fc::time_point_sec timelock, bool broadcast = false ); + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1479,7 +1479,7 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, + signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast = false ); /** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6f42a44975..93229d24ed 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1743,7 +1743,8 @@ class wallet_api_impl } signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, fc::time_point_sec timelock, bool broadcast = false ) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ) { try { @@ -1759,10 +1760,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = htlc_object::hash_algorithm::SHA256; + create_op.hash_type = hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = htlc_object::hash_algorithm::RIPEMD160; - FC_ASSERT(create_op.hash_type != htlc_object::hash_algorithm::UNKNOWN, + create_op.hash_type = hash_algorithm::ripemd160; + FC_ASSERT(create_op.hash_type != hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; @@ -1775,7 +1776,7 @@ class wallet_api_impl (preimage_hash)(preimage_size)(timelock)(broadcast) ) } - signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, bool broadcast ) + signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -3077,8 +3078,8 @@ uint64_t wallet_api::get_asset_count()const } signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, - fc::time_point_sec timelock, bool broadcast) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast) { return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, timelock, broadcast); @@ -3089,6 +3090,12 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } +signed_transaction wallet_api::update_htlc( std::string htlc_id, std::string issuer, const std::vector& preimage, + bool broadcast) +{ + return my->update_htlc(htlc_id, issuer, preimage, broadcast); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index d93a81aaa9..80c289a04c 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -618,6 +618,24 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); + // Create new asset called BOBCOIN + try + { + graphene::chain::asset_options asset_ops; + asset_ops.max_supply = 1000000; + asset_ops.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1))); + fc::optional bit_opts; + con.wallet_api_ptr->create_asset("nathan", "BOBCOIN", 5, asset_ops, bit_opts, true); + } + catch(exception& e) + { + BOOST_FAIL(e.what()); + } + catch(...) + { + BOOST_FAIL("Unknown exception creating BOBCOIN"); + } + // create a new account for Alice { graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); @@ -627,6 +645,10 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // save the private key for this new account in the wallet file BOOST_CHECK(con.wallet_api_ptr->import_key("alice", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + // attempt to give alice some bitsahres + BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); + signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", + "Here are some CORE token for your new account", true); } // create a new account for Bob @@ -638,14 +660,15 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // save the private key for this new account in the wallet file BOOST_CHECK(con.wallet_api_ptr->import_key("bob", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + // attempt to give bob some bitsahres + BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to Bob"); + signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "bob", "10000", "1.3.0", + "Here are some CORE token for your new account", true); + con.wallet_api_ptr->issue_asset("bob", "5", "BOBCOIN", "Here are your BOBCOINs", true); } - // attempt to give alice some bitsahres - BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); - signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", - "Here are some CORE token for your new account", true); - BOOST_TEST_MESSAGE("Creating HTLC"); + BOOST_TEST_MESSAGE("Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC"); // create an HTLC std::string preimage_string = "My Secret"; fc::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); @@ -655,38 +678,69 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->create_htlc("alice", "bob", - "1.3.0", "1000", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); + + // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: + std::string alice_htlc_id_as_string; + { + BOOST_TEST_MESSAGE("The system is generating a block"); + graphene::chain::signed_block result_block; + BOOST_CHECK(generate_block(app1, result_block)); + + // get the ID: + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + alice_htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + BOOST_TEST_MESSAGE("Alice shares the HTLC ID with Bob. The HTLC ID is: " + alice_htlc_id_as_string); + } + + // Bob can now look over Alice's HTLC, to see if it is what was agreed to. + BOOST_TEST_MESSAGE("Bob retrieves the HTLC Object by ID to examine it."); + graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + + // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC + con.wallet_api_ptr->create_htlc("bob", "alice", + "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: - BOOST_TEST_MESSAGE("Generating Block"); - graphene::chain::signed_block result_block; - BOOST_CHECK(generate_block(app1, result_block)); - - // get the ID: - htlc_id_type htlc_id = result_block.transactions[5].operation_results[0].get(); - std::string htlc_id_as_string = fc::json::to_pretty_string(htlc_id); - BOOST_TEST_MESSAGE("The HTLC ID is: " + htlc_id_as_string); - - // Bob can now look over Alice's HTLC - BOOST_TEST_MESSAGE("Retrieving HTLC Object by ID"); - try + std::string bob_htlc_id_as_string; { - //con.wallet_api_ptr->get_object(htlc_id); - graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(htlc_id_as_string); - BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + BOOST_TEST_MESSAGE("The system is generating a block"); + graphene::chain::signed_block result_block; + BOOST_CHECK(generate_block(app1, result_block)); + + // get the ID: + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + bob_htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); } - catch(std::exception& e) + + // Alice can now look over Bob's HTLC, to see if it is what was agreed to: + BOOST_TEST_MESSAGE("Alice retrieves the HTLC Object by ID to examine it."); + graphene::chain::htlc_object bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(bob_htlc)); + + // Alice likes what she sees, so uses her preimage to get her BOBCOIN { - BOOST_FAIL(e.what()); + BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + vector secret_vector(secret.begin(), secret.end()); + con.wallet_api_ptr->update_htlc(bob_htlc_id_as_string, "alice", secret_vector, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); } - catch(...) + + // TODO: Bob can look at Alice's history to see her preimage + // Bob can use the preimage to retrieve his BTS { - BOOST_FAIL("Unknown exception attempting to retrieve HTLC"); + BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + vector secret_vector(secret.begin(), secret.end()); + con.wallet_api_ptr->update_htlc(alice_htlc_id_as_string, "bob", secret_vector, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); } - // TODO: API for Bob to claim the funds by giving the preimage - // TODO: Clean the HTLC object (no need to have preimage stored there, nor reference to claim tx) - // wait for everything to finish up fc::usleep(fc::seconds(1)); } catch( fc::exception& e ) { From 40d22aa7b014dd59606c524a8ed8275da6b615ae Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Sep 2018 17:11:08 -0500 Subject: [PATCH 022/132] Removed debugging code --- libraries/chain/htlc_evaluator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index d0b684193b..4aef54fbb5 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -69,8 +69,7 @@ namespace graphene { bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); - //T attempted_hash = T::hash(incoming_string); - fc::ripemd160 attempted_hash = fc::ripemd160::hash(incoming_string); + T attempted_hash = T::hash(incoming_string); if (attempted_hash.data_size() != valid_hash.size()) return false; char* data = attempted_hash.data(); From eb1225ecb594167671fdf91df2c89d15fd12af45 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 27 Sep 2018 13:02:20 -0500 Subject: [PATCH 023/132] add htlc to switch-case --- libraries/chain/db_notify.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 24ad3fb342..d8f8c09ba3 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -414,7 +414,9 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } + } case impl_htlc_object_type: + break; + } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) From d367c2c86f959d29733db0d42c828fd80097e252 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 12 Oct 2018 13:49:53 -0500 Subject: [PATCH 024/132] Change method names to match spec --- .../wallet/include/graphene/wallet/wallet.hpp | 25 +++++++++++++++---- libraries/wallet/wallet.cpp | 23 ++++++++++++----- tests/cli/main.cpp | 8 +++--- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 89b9bcb4b1..2fd8ef1cb1 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1468,9 +1468,9 @@ class wallet_api * @param timelock when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ); + signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1479,8 +1479,19 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, - bool broadcast = false ); + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, + bool broadcast = false ); + + /***** + * Set a new timelock on an existing HTLC + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param timelock the new time of expiry + * @param broadcast true to broadcast to the network + */ + signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const fc::time_point_sec& timelock, + bool broadcast = false); /** * Get information about a vesting balance object. @@ -1811,6 +1822,7 @@ FC_API( graphene::wallet::wallet_api, (update_asset) (update_asset_issuer) (update_bitasset) + (get_htlc) (update_asset_feed_producers) (publish_asset_feed) (issue_asset) @@ -1832,6 +1844,9 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) + (htlc_prepare) + (htlc_redeem) + (htlc_extend_expiry) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 93229d24ed..cd8d15f87d 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1742,7 +1742,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const fc::time_point_sec& timelock, bool broadcast = false ) { @@ -1776,7 +1776,7 @@ class wallet_api_impl (preimage_hash)(preimage_size)(timelock)(broadcast) ) } - signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -1800,6 +1800,11 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } + signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) + { + throw std::logic_error("Not Implemented"); + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -3077,11 +3082,11 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, +signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const fc::time_point_sec& timelock, bool broadcast) { - return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, timelock, broadcast); } @@ -3090,10 +3095,16 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } -signed_transaction wallet_api::update_htlc( std::string htlc_id, std::string issuer, const std::vector& preimage, +signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::vector& preimage, + bool broadcast) +{ + return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); +} + +signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const fc::time_point_sec& timelock, bool broadcast) { - return my->update_htlc(htlc_id, issuer, preimage, broadcast); + return my->htlc_extend_expiry(htlc_id, issuer, timelock, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 80c289a04c..3af43dbc4f 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -677,7 +677,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) hash[i] = preimage_md.data()[i]; fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx - = con.wallet_api_ptr->create_htlc("alice", "bob", + = con.wallet_api_ptr->htlc_prepare("alice", "bob", "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: @@ -699,7 +699,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC - con.wallet_api_ptr->create_htlc("bob", "alice", + con.wallet_api_ptr->htlc_prepare("bob", "alice", "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->update_htlc(bob_htlc_id_as_string, "alice", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret_vector, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } @@ -736,7 +736,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->update_htlc(alice_htlc_id_as_string, "bob", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret_vector, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } From 6ded68887f025ef401c1419adb364bd408ef87c6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 12 Oct 2018 20:41:18 -0500 Subject: [PATCH 025/132] Boost 1.58 compatibility --- libraries/wallet/wallet.cpp | 6 +++--- tests/tests/htlc_tests.cpp | 16 ++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index cd8d15f87d..fb9c74d71f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1760,10 +1760,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = hash_algorithm::sha256; + create_op.hash_type = graphene::chain::hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = hash_algorithm::ripemd160; - FC_ASSERT(create_op.hash_type != hash_algorithm::unknown, + create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; + FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 723bea78c3..6acebbfaeb 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -119,8 +119,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) } // verify funds on hold (make sure this can cover fees) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); - //BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) > 98000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Alice can't get it back before the timeout { @@ -163,7 +162,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) trx.clear(); } // verify funds return (what about fees?) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 100000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); // verify Bob cannot execute the contract after the fact } @@ -211,7 +210,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // verify funds on hold (make sure this can cover fees) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Alice can't get it back before the timeout { @@ -233,7 +232,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // balance should not have changed - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Bob (or anyone) can see the details of the transaction // send an update operation to claim the funds { @@ -272,17 +271,14 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_TEST_CHECK( get_balance(bob_id, graphene::chain::asset_id_type()) == 10000 ); - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { ACTORS((alice)(bob)); - fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000; - - int64_t init_balance(100000); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); From 9a502a35453973da71669371130b5303eb363304 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sun, 14 Oct 2018 21:46:51 -0500 Subject: [PATCH 026/132] add get_typename for enum --- libraries/chain/htlc_evaluator.cpp | 1 - libraries/chain/include/graphene/chain/htlc_object.hpp | 7 ++++++- programs/build_helpers/member_enumerator.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 4aef54fbb5..89e408c332 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace graphene { diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 3a833d6334..0a3b571e6f 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -76,7 +76,12 @@ namespace graphene { namespace chain { typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; -} } +} } // namespace graphene::chain + +namespace fc +{ + template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; +} FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp index 16452c5bf8..5dcc6156a3 100644 --- a/programs/build_helpers/member_enumerator.cpp +++ b/programs/build_helpers/member_enumerator.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include From dc826109541fe9c51e0ee00b35267d82223edce4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 15 Oct 2018 15:16:31 -0500 Subject: [PATCH 027/132] Addition of SHA1 and committee parameters --- libraries/chain/htlc_evaluator.cpp | 26 +++++++++----- .../include/graphene/chain/htlc_object.hpp | 7 ++-- .../chain/protocol/chain_parameters.hpp | 36 +++++++++++++++++-- libraries/wallet/wallet.cpp | 2 ++ 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 89e408c332..cf84f89f3a 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include #include #include @@ -31,15 +32,22 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, - // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, + // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); - // make sure we have the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ) ); - // make sure we have the funds for the fee - FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee ); - return void_result(); + const graphene::chain::global_property_object& global_properties = db().get_global_properties(); + // make sure the expiration is reasonable + FC_ASSERT( o.epoch.sec_since_epoch() < fc::time_point::now().sec_since_epoch() + + global_properties.parameters.get_committee_updatable_parameters().htlc_max_timeout_secs, + "HTLC Timeout exceeds allowed length" ); + // make sure the preimage length is reasonable + FC_ASSERT( o.key_size < global_properties.parameters.get_committee_updatable_parameters().htlc_max_preimage_size, + "HTLC preimage length exceeds allowed length" ); + // make sure sender has the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); + // make sure sender has the funds for the fee + FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee, "Insufficient funds" ); + return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) @@ -96,6 +104,8 @@ namespace graphene { match = test_hash(o.preimage, htlc_obj->preimage_hash); if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) + match = test_hash(o.preimage, htlc_obj->preimage_hash); FC_ASSERT(match, "Provided preimage does not generate correct hash."); } diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 0a3b571e6f..a2ceac0271 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -35,7 +35,8 @@ namespace graphene { namespace chain { enum hash_algorithm { unknown = 0x00, ripemd160 = 0x01, - sha256 = 0x02 + sha256 = 0x02, + sha1 = 0x03 }; /** @@ -53,7 +54,7 @@ namespace graphene { namespace chain { asset pending_fee; vector preimage_hash; fc::enum_type preimage_hash_algorithm; - uint16_t preimage_size; + uint16_t preimage_size; }; struct by_from_id; @@ -83,7 +84,7 @@ namespace fc template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; } -FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); +FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration)(pending_fee) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 695d9541ee..f04fdcea78 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,7 +30,6 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { - typedef static_variant<> parameter_extension; struct chain_parameters { /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ @@ -63,14 +62,47 @@ namespace graphene { namespace chain { uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH; + + struct ext + { + struct committee_updatable_parameters + { + uint32_t htlc_max_timeout_secs; + uint32_t htlc_max_preimage_size; + }; + }; + typedef static_variant parameter_extension; + typedef flat_set extensions_type; + extensions_type extensions; /** defined in fee_schedule.cpp */ void validate()const; + + const ext::committee_updatable_parameters get_committee_updatable_parameters() const + { + if (extensions.size() > 0) + { + for ( const parameter_extension& e : extensions ) + { + if ( e.which() == parameter_extension::tag::value ) + return e.get(); + } + } + return ext::committee_updatable_parameters(); + } }; } } // graphene::chain +FC_REFLECT( graphene::chain::chain_parameters::ext::committee_updatable_parameters, + (htlc_max_timeout_secs) + (htlc_max_preimage_size) + ) + +FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::parameter_extension ) +FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::extensions_type ) + FC_REFLECT( graphene::chain::chain_parameters, (current_fees) (block_interval) @@ -101,4 +133,4 @@ FC_REFLECT( graphene::chain::chain_parameters, (account_fee_scale_bitshifts) (max_authority_depth) (extensions) - ) + ) \ No newline at end of file diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fb9c74d71f..ae4fe270f8 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1763,6 +1763,8 @@ class wallet_api_impl create_op.hash_type = graphene::chain::hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; + if ( "SHA1" == hash_algorithm ) + create_op.hash_type = graphene::chain::hash_algorithm::sha1; FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); From 49822e18982ab685336ecc6e62f004698d195465 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 24 Oct 2018 12:09:21 -0500 Subject: [PATCH 028/132] Refactor committee parameters --- libraries/chain/db_block.cpp | 1 + libraries/chain/db_maint.cpp | 2 - libraries/chain/db_update.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 31 +++++++++++----- .../chain/include/graphene/chain/database.hpp | 2 +- .../chain/protocol/chain_parameters.hpp | 37 +++++-------------- 6 files changed, 35 insertions(+), 40 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..20022ce4c4 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -584,6 +584,7 @@ void database::_apply_block( const signed_block& next_block ) clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); + clear_expired_htlcs(); update_expired_feeds(); // this will update expired feeds and some core exchange rates update_core_exchange_rates(); // this will update remaining core exchange rates update_withdraw_permissions(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index a766084131..7f1a8a4c6c 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1259,8 +1259,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); - - remove_expired_htlcs(); } } } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index fc10bd6ad8..7552bc5528 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -558,7 +558,7 @@ void database::update_withdraw_permissions() remove(*permit_index.begin()); } -void database::remove_expired_htlcs() +void database::clear_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index cf84f89f3a..826e0581c2 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,19 +30,29 @@ namespace graphene { namespace chain { + optional get_committee_htlc_parameters(graphene::chain::database& db) + { + return db.get_global_properties().parameters.extensions.value.committee_updatable_options; + } + void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - const graphene::chain::global_property_object& global_properties = db().get_global_properties(); + optional committee_parameters = get_committee_htlc_parameters(db()); + + FC_ASSERT(committee_parameters, "HTLC Committee parameters are not set."); + // make sure the expiration is reasonable - FC_ASSERT( o.epoch.sec_since_epoch() < fc::time_point::now().sec_since_epoch() - + global_properties.parameters.get_committee_updatable_parameters().htlc_max_timeout_secs, + FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() + + committee_parameters->htlc_max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < global_properties.parameters.get_committee_updatable_parameters().htlc_max_preimage_size, + FC_ASSERT( o.key_size < committee_parameters->htlc_max_preimage_size, "HTLC preimage length exceeds allowed length" ); + // make sure we have a hash algorithm set + FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); // make sure sender has the funds for the fee @@ -75,8 +85,11 @@ namespace graphene { template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); - T attempted_hash = T::hash(incoming_string); + // convert incoming_preimage to an array + unsigned char incoming_array[incoming_preimage.size()]; + for(int i = 0; i < incoming_preimage.size(); ++i) + incoming_array[i] = incoming_preimage[i]; + T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) return false; char* data = attempted_hash.data(); @@ -96,7 +109,7 @@ namespace graphene { if (o.preimage.size() > 0) { FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); // see if the preimages match bool match = false; @@ -107,11 +120,11 @@ namespace graphene { if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) match = test_hash(o.preimage, htlc_obj->preimage_hash); - FC_ASSERT(match, "Provided preimage does not generate correct hash."); + FC_ASSERT(match, "Provided preimage does not generate correct hash."); } else { - FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); + FC_ASSERT(db().head_block_time().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); } return void_result(); } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 202b8bcb05..5ad65950bc 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -466,7 +466,7 @@ namespace graphene { namespace chain { void update_withdraw_permissions(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); - void remove_expired_htlcs(); + void clear_expired_htlcs(); ///Steps performed only at maintenance intervals ///@{ diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index f04fdcea78..1f24fd1c2f 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,6 +30,12 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { + struct committee_updatable_parameters + { + uint32_t htlc_max_timeout_secs; + uint32_t htlc_max_preimage_size; + }; + struct chain_parameters { /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ @@ -65,43 +71,20 @@ namespace graphene { namespace chain { struct ext { - struct committee_updatable_parameters - { - uint32_t htlc_max_timeout_secs; - uint32_t htlc_max_preimage_size; - }; + optional< committee_updatable_parameters > committee_updatable_options; }; - typedef static_variant parameter_extension; - typedef flat_set extensions_type; - extensions_type extensions; + extension extensions; /** defined in fee_schedule.cpp */ void validate()const; - const ext::committee_updatable_parameters get_committee_updatable_parameters() const - { - if (extensions.size() > 0) - { - for ( const parameter_extension& e : extensions ) - { - if ( e.which() == parameter_extension::tag::value ) - return e.get(); - } - } - return ext::committee_updatable_parameters(); - } }; } } // graphene::chain -FC_REFLECT( graphene::chain::chain_parameters::ext::committee_updatable_parameters, - (htlc_max_timeout_secs) - (htlc_max_preimage_size) - ) - -FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::parameter_extension ) -FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::extensions_type ) +FC_REFLECT( graphene::chain::committee_updatable_parameters, (htlc_max_timeout_secs) (htlc_max_preimage_size) ) +FC_REFLECT( graphene::chain::chain_parameters::ext, (committee_updatable_options)) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) From 3ac8323250c3426f82c338a1f0e632837da869c7 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 24 Oct 2018 12:10:36 -0500 Subject: [PATCH 029/132] Test cleanup. Hardfork protection still needed. --- tests/tests/htlc_tests.cpp | 281 ++++++++++++++++++++----------------- 1 file changed, 151 insertions(+), 130 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 6acebbfaeb..5399f2ff9f 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -65,7 +65,11 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec std::vector hash_it(std::vector preimage) { - fc::sha256 hash = fc::sha256::hash(preimage); + // convert the preimage to a char array + unsigned char char_array[preimage.size()]; + for(unsigned int i = 0; i < preimage.size(); ++i) + char_array[i] = preimage[i]; + fc::sha256 hash = fc::sha256::hash((char*)char_array, preimage.size()); std::vector ret_val(hash.data_size()); char* data = hash.data(); for(size_t i = 0; i < hash.data_size(); i++) @@ -75,8 +79,21 @@ std::vector hash_it(std::vector preimage) return ret_val; } +void set_committee_parameters(database_fixture* db_fixture) +{ + // set the committee parameters + db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { + graphene::chain::committee_updatable_parameters params; + params.htlc_max_preimage_size = 1024; + params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; + p.parameters.extensions.value.committee_updatable_options = params; + }); +} + BOOST_AUTO_TEST_CASE( htlc_expires ) { + set_committee_parameters(this); + ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -92,14 +109,16 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // cler everything out generate_block(); trx.clear(); + fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = expiration; create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); @@ -116,35 +135,94 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); + generate_block(); + } + + // verify funds on hold (TODO: make sure this can cover fees) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + + // make sure Bob (or anyone) can see the details of the transaction + graphene::app::database_api db_api(db); + optional htlc = db_api.get_htlc(fc::json::to_pretty_string(alice_htlc_id)); + BOOST_CHECK(htlc); + + // let it expire (wait for timeout) + generate_blocks(expiration + fc::seconds(120) ); + // verify funds return (what about fees?) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); + // verify Bob cannot execute the contract after the fact +} + +BOOST_AUTO_TEST_CASE( htlc_fulfilled ) +{ + set_committee_parameters(this); + + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(key_size); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = db.head_block_time() + fc::seconds(10); + create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); } // verify funds on hold (make sure this can cover fees) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - // make sure Alice can't get it back before the timeout + // TODO: make sure Bob (or anyone) can see the details of the transaction + + // send an update operation to claim the funds { graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; + update_operation.update_issuer = bob_id; update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = pre_image; trx.operations.push_back(update_operation); - sign(trx, alice_private_key); + sign(trx, bob_private_key); try { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + PUSH_TX(db, trx, ~0); } catch (fc::exception& ex) { - // this should happen + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); } generate_block(); trx.clear(); } - - // make sure Alice can't spend it. - // make sure Bob (or anyone) can see the details of the transaction - // let it expire (wait for timeout) - std::this_thread::sleep_for(std::chrono::seconds(4)); - // send an update operation to reclaim the funds + // verify Alice cannot execute the contract after the fact { graphene::chain::htlc_update_operation update_operation; update_operation.update_issuer = alice_id; @@ -153,130 +231,24 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) sign(trx, alice_private_key); try { - PUSH_TX(db, trx, ~0); + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); } catch (fc::exception& ex) { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + // this should happen } generate_block(); trx.clear(); } - // verify funds return (what about fees?) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); - // verify Bob cannot execute the contract after the fact -} - -BOOST_AUTO_TEST_CASE( htlc_fulfilled ) -{ - ACTORS((alice)(bob)); - - int64_t init_balance(100000); - - transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - - uint16_t key_size = 256; - std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); - - graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out - generate_block(); - trx.clear(); - // Alice puts a contract on the blockchain - { - graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); - create_operation.key_hash = key_hash; - create_operation.key_size = key_size; - create_operation.source = alice_id; - trx.operations.push_back(create_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } - trx.clear(); - graphene::chain::signed_block blk = generate_block(); - // can we assume that alice's transaction will be the only one in this block? - processed_transaction alice_trx = blk.transactions[0]; - alice_htlc_id = alice_trx.operation_results[0].get(); - } - - // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - - // make sure Alice can't get it back before the timeout - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); - } catch (fc::exception& ex) - { - // this should happen - } - generate_block(); - trx.clear(); - } - - // balance should not have changed - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - // make sure Bob (or anyone) can see the details of the transaction - // send an update operation to claim the funds - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = bob_id; - update_operation.htlc_id = alice_htlc_id; - update_operation.preimage = pre_image; - trx.operations.push_back(update_operation); - sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } - generate_block(); - trx.clear(); - } - // verify Alice cannot execute the contract after the fact - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); - } catch (fc::exception& ex) - { - // this should happen - } - generate_block(); - trx.clear(); - } - // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + // verify funds end up in Bob's account + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { + set_committee_parameters(this); + ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -297,7 +269,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = db.head_block_time() + fc::seconds(3); create_operation.key_hash = key_hash; create_operation.key_size = key_size; create_operation.source = alice_id; @@ -320,8 +292,9 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = db.head_block_time() + fc::seconds(3); create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); @@ -337,4 +310,52 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) } } +BOOST_AUTO_TEST_CASE( set_htlc_params ) +{ try { + set_committee_parameters(this); + + BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); + { + proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + graphene::chain::committee_updatable_parameters new_params; + new_params.htlc_max_preimage_size = 2048; + new_params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.committee_updatable_options = new_params; + cop.proposed_ops.emplace_back(uop); + trx.operations.push_back(cop); + db.push_transaction(trx); + } + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + } + BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(proposal_id_type()(db).expiration_time + 5); + BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + + BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 2048u); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From a30df565ec007b26276d3f6c3417e74a69441cc6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 26 Oct 2018 12:33:10 -0500 Subject: [PATCH 030/132] modified name of optional committee parameters --- libraries/chain/htlc_evaluator.cpp | 12 +++++----- .../chain/protocol/chain_parameters.hpp | 14 ++++++------ tests/tests/htlc_tests.cpp | 22 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 826e0581c2..b3f43d99ad 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,9 +30,9 @@ namespace graphene { namespace chain { - optional get_committee_htlc_parameters(graphene::chain::database& db) + optional get_committee_htlc_options(graphene::chain::database& db) { - return db.get_global_properties().parameters.extensions.value.committee_updatable_options; + return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) @@ -40,16 +40,16 @@ namespace graphene { //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - optional committee_parameters = get_committee_htlc_parameters(db()); + optional htlc_options = get_committee_htlc_options(db()); - FC_ASSERT(committee_parameters, "HTLC Committee parameters are not set."); + FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() - + committee_parameters->htlc_max_timeout_secs, + + htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < committee_parameters->htlc_max_preimage_size, + FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 1f24fd1c2f..f155ecafb4 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,10 +30,10 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { - struct committee_updatable_parameters + struct htlc_options { - uint32_t htlc_max_timeout_secs; - uint32_t htlc_max_preimage_size; + uint32_t max_timeout_secs; + uint32_t max_preimage_size; }; struct chain_parameters @@ -71,7 +71,7 @@ namespace graphene { namespace chain { struct ext { - optional< committee_updatable_parameters > committee_updatable_options; + optional< htlc_options > updatable_htlc_options; }; extension extensions; @@ -83,8 +83,8 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT( graphene::chain::committee_updatable_parameters, (htlc_max_timeout_secs) (htlc_max_preimage_size) ) -FC_REFLECT( graphene::chain::chain_parameters::ext, (committee_updatable_options)) +FC_REFLECT( graphene::chain::htlc_options, (max_timeout_secs) (max_preimage_size) ) +FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) @@ -116,4 +116,4 @@ FC_REFLECT( graphene::chain::chain_parameters, (account_fee_scale_bitshifts) (max_authority_depth) (extensions) - ) \ No newline at end of file + ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 5399f2ff9f..77a394ca29 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -83,10 +83,10 @@ void set_committee_parameters(database_fixture* db_fixture) { // set the committee parameters db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { - graphene::chain::committee_updatable_parameters params; - params.htlc_max_preimage_size = 1024; - params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; - p.parameters.extensions.value.committee_updatable_options = params; + graphene::chain::htlc_options params; + params.max_preimage_size = 1024; + params.max_timeout_secs = 60 * 60 * 24 * 28; + p.parameters.extensions.value.updatable_htlc_options = params; }); } @@ -320,10 +320,10 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; committee_member_update_global_parameters_operation uop; - graphene::chain::committee_updatable_parameters new_params; - new_params.htlc_max_preimage_size = 2048; - new_params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; - uop.new_parameters.extensions.value.committee_updatable_options = new_params; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); db.push_transaction(trx); @@ -343,19 +343,19 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) } BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); generate_blocks(proposal_id_type()(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); // get the maintenance skip slots out of the way BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 2048u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From b5dae57b8473a1f112d505efccf502ac20c884ba Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 26 Oct 2018 16:34:08 -0500 Subject: [PATCH 031/132] beginning of hardfork protection --- .../chain/committee_member_evaluator.cpp | 4 + libraries/chain/hardfork.d/HTLC.hf | 8 +- libraries/chain/htlc_evaluator.cpp | 2 +- tests/tests/htlc_tests.cpp | 147 +++++++++++------- 4 files changed, 101 insertions(+), 60 deletions(-) diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 4e7eb827e5..62e658303d 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,9 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con { try { FC_ASSERT(trx_state->_is_proposed_trx); + FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), + "Unable to set HTLC parameters until hardfork." ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf index 47df3c9f4a..2ab8857d78 100644 --- a/libraries/chain/hardfork.d/HTLC.hf +++ b/libraries/chain/hardfork.d/HTLC.hf @@ -1,4 +1,4 @@ -// htlc support for bitshares-core -#ifndef HTLC_ESCROW_TIME -#define HTLC_ESCROW_TIME (fc::time_point_sec( 1600000000 )) -#endif \ No newline at end of file +// HTLC implementation +#ifndef HARDFORK_HTLC_TIME +#define HARDFORK_HTLC_TIME (fc::time_point_sec( 1600000000 ) ) +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index b3f43d99ad..4b2bd5cd00 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -87,7 +87,7 @@ namespace graphene { { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; - for(int i = 0; i < incoming_preimage.size(); ++i) + for(unsigned i = 0; i < incoming_preimage.size(); ++i) incoming_array[i] = incoming_preimage[i]; T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 77a394ca29..dc73a28daa 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -90,9 +90,51 @@ void set_committee_parameters(database_fixture* db_fixture) }); } +void advance_past_hardfork(database_fixture* db_fixture) +{ + db_fixture->generate_blocks(HARDFORK_HTLC_TIME); + set_committee_parameters(db_fixture); + set_expiration(db_fixture->db, db_fixture->trx); +} + +BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = expiration; + create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + } +} + BOOST_AUTO_TEST_CASE( htlc_expires ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -123,13 +165,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } + PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); // can we assume that alice's transaction will be the only one in this block? @@ -155,7 +191,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -185,13 +221,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } + PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); // can we assume that alice's transaction will be the only one in this block? @@ -212,13 +242,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.preimage = pre_image; trx.operations.push_back(update_operation); sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } + PUSH_TX(db, trx, ~0); generate_block(); trx.clear(); } @@ -229,14 +253,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.htlc_id = alice_htlc_id; trx.operations.push_back(update_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); - } catch (fc::exception& ex) - { - // this should happen - } + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); generate_block(); trx.clear(); } @@ -247,7 +264,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) BOOST_AUTO_TEST_CASE( other_peoples_money ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -275,16 +292,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, database::skip_nothing); - BOOST_FAIL( "Bob stole money from Alice!" ); - } catch (fc::exception& ex) - { - // this is supposed to happen - //BOOST_TEST_MESSAGE("This is the error thrown (expected):"); - //BOOST_TEST_MESSAGE(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); trx.clear(); } // now try the same but with Alice's signature (should work) @@ -299,21 +307,48 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, database::skip_nothing); - } catch (fc::exception& ex) - { - BOOST_FAIL( "Alice cannot create a contract!" ); - } + PUSH_TX(db, trx, database::skip_nothing); trx.clear(); } } BOOST_AUTO_TEST_CASE( set_htlc_params ) -{ try { - set_committee_parameters(this); +{ + { + // try to set committee parameters before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + // update with signatures + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + generate_blocks(proposal_id_type()(db).expiration_time + 5); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // the proposal should have failed + BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); + trx.clear(); + } + + // now things should start working... + advance_past_hardfork(this); + proposal_id_type good_proposal_id; BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); { proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); @@ -326,11 +361,13 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) uop.new_parameters.extensions.value.updatable_htlc_options = new_params; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - db.push_transaction(trx); + graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); + good_proposal_id = proc_trx.operation_results[0].get(); } BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { proposal_update_operation uop; + uop.proposal = good_proposal_id; uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), get_account("init2").get_id(), get_account("init3").get_id(), @@ -339,14 +376,14 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) trx.operations.push_back(uop); sign( trx, init_account_priv_key ); db.push_transaction(trx); - BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); } BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); - generate_blocks(proposal_id_type()(db).expiration_time + 5); + generate_blocks(good_proposal_id(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); @@ -356,6 +393,6 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); -} FC_LOG_AND_RETHROW() } +} BOOST_AUTO_TEST_SUITE_END() From bc48b3632118586a95ef596ac9203c242c3e716b Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 29 Oct 2018 08:56:04 -0500 Subject: [PATCH 032/132] Code cleanup and comments --- libraries/chain/assert_evaluator.cpp | 8 -- libraries/chain/htlc_evaluator.cpp | 4 +- .../include/graphene/chain/htlc_evaluator.hpp | 23 +++-- .../include/graphene/chain/htlc_object.hpp | 35 +++++--- .../graphene/chain/protocol/assert.hpp | 21 +---- .../include/graphene/chain/protocol/htlc.hpp | 85 ++++++++++++++----- libraries/chain/protocol/htlc.cpp | 10 ++- 7 files changed, 110 insertions(+), 76 deletions(-) diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index febf15aeb3..3d1b11d257 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -49,14 +49,6 @@ struct predicate_evaluator { FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id ); } - void operator()(const hash_operation_predicate& p)const - { - // TODO: Implement this method - } - void operator()(const timeout_operation_predicate& p)const - { - // TODO: Implement this method - } }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 4b2bd5cd00..0fc587c7c5 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,7 +30,7 @@ namespace graphene { namespace chain { - optional get_committee_htlc_options(graphene::chain::database& db) + optional htlc_create_evaluator::get_committee_htlc_options(graphene::chain::database& db) { return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } @@ -83,7 +83,7 @@ namespace graphene { } template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_update_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 1181541cc7..0be6fb323a 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -29,21 +29,26 @@ namespace graphene { class htlc_create_evaluator : public evaluator { - public: - typedef htlc_create_operation operation_type; + public: + typedef htlc_create_operation operation_type; - void_result do_evaluate( const htlc_create_operation& o); - object_id_type do_apply( const htlc_create_operation& o); + void_result do_evaluate( const htlc_create_operation& o); + object_id_type do_apply( const htlc_create_operation& o); + private: + optional get_committee_htlc_options(graphene::chain::database& db); }; class htlc_update_evaluator : public evaluator { - public: - typedef htlc_update_operation operation_type; + public: + typedef htlc_update_operation operation_type; - void_result do_evaluate( const htlc_update_operation& o); - void_result do_apply( const htlc_update_operation& o); - const htlc_object* htlc_obj = nullptr; + void_result do_evaluate( const htlc_update_operation& o); + void_result do_apply( const htlc_update_operation& o); + const htlc_object* htlc_obj = nullptr; + private: + template + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; } // namespace graphene } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index a2ceac0271..324dbc706e 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -40,21 +40,25 @@ namespace graphene { namespace chain { }; /** - * Temporally save escrow transactions until funds are released or operation expired. + * @brief database object to store HTLCs + * + * This object is stored in the database while an HTLC is active. The HTLC will + * become inactive at expiration or when unlocked via the preimage. */ class htlc_object : public graphene::db::abstract_object { public: + // uniquely identify this object in the database static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_htlc_object_type; - account_id_type from; - account_id_type to; - asset amount; - fc::time_point_sec expiration; - asset pending_fee; - vector preimage_hash; + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; fc::enum_type preimage_hash_algorithm; - uint16_t preimage_size; + uint16_t preimage_size; }; struct by_from_id; @@ -64,11 +68,11 @@ namespace graphene { namespace chain { indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag< by_expiration >, member< htlc_object, fc::time_point_sec, &htlc_object::expiration > >, + ordered_non_unique< tag< by_expiration >, member< htlc_object, + fc::time_point_sec, &htlc_object::expiration > >, ordered_non_unique< tag< by_from_id >, - composite_key< htlc_object, - member< htlc_object, account_id_type, &htlc_object::from > + composite_key< htlc_object, member< htlc_object, account_id_type, &htlc_object::from > > > > @@ -81,7 +85,14 @@ namespace graphene { namespace chain { namespace fc { - template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; + template<> + struct get_typename> + { + static const char* name() + { + return "fc::enum_type"; + } + }; } FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index 2b01ddd262..c9f3b2774a 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -70,21 +70,6 @@ namespace graphene { namespace chain { bool validate()const{ return true; } }; - struct hash_operation_predicate - { - account_id_type pay_to; - std::vector key_hash; - int64_t key_size; - bool validate() const { return true; } - }; - - struct timeout_operation_predicate - { - account_id_type pay_to; - uint64_t epoch; - bool validate() const { return true; } - }; - /** * When defining predicates do not make the protocol dependent upon * implementation details. @@ -92,9 +77,7 @@ namespace graphene { namespace chain { typedef static_variant< account_name_eq_lit_predicate, asset_symbol_eq_lit_predicate, - block_id_predicate, - hash_operation_predicate, - timeout_operation_predicate + block_id_predicate > predicate; @@ -126,8 +109,6 @@ FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) ) FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) ) FC_REFLECT( graphene::chain::block_id_predicate, (id) ) -FC_REFLECT( graphene::chain::hash_operation_predicate, (pay_to)(key_hash)(key_size) ) -FC_REFLECT( graphene::chain::timeout_operation_predicate, (pay_to)(epoch) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 5b4e1f0e3b..1ba59198c6 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -36,37 +36,80 @@ namespace graphene { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; - asset fee; // paid to network - account_id_type source; // where the held monies are to come from - account_id_type destination; // where the held monies will go if the preimage is provided - asset amount; // the amount to hold + // paid to network + asset fee; + // where the held monies are to come from + account_id_type source; + // where the held monies will go if the preimage is provided + account_id_type destination; + // the amount to hold + asset amount; + // hash algorithm used to create key_hash fc::enum_type hash_type - = graphene::chain::hash_algorithm::unknown; // hash algorithm used to create key_hash - std::vector key_hash; // the hash of the preimage - uint16_t key_size; // the size of the preimage - fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed - extensions_type extensions; // for future expansion + = graphene::chain::hash_algorithm::unknown; + // the hash of the preimage + std::vector key_hash; + // the size of the preimage + uint16_t key_size; + // The time the funds will be returned to the source if not claimed + fc::time_point_sec epoch; + // for future expansion + extensions_type extensions; + /*** + * @brief Does simple validation of this object + */ void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + + /** + * @brief Determines who is required to sign + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(source); + } + + /** + * @brief who will pay the fee + */ account_id_type fee_payer()const { return source; } }; struct htlc_update_operation : public base_operation { - struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - asset fee; // paid to network - htlc_id_type htlc_id; // the object we are attempting to update - account_id_type update_issuer; // who is attempting to update the transaction - std::vector preimage; // the preimage (not used if after epoch timeout) - extensions_type extensions; // for future expansion + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type update_issuer; + // the preimage (not used if after epoch timeout) + std::vector preimage; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /*** + * @determines who should have signed this object + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(update_issuer); + } - void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(update_issuer); } - account_id_type fee_payer()const { return update_issuer; } + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return update_issuer; } }; } } diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 1c8aa44dde..0d433c57e2 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -26,11 +26,13 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - //TODO: Implement - // signer must equal from + FC_ASSERT( source != destination ); + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount.amount > 0 ); } + void htlc_update_operation::validate()const { - //TODO: Implement - // if preimage is provided, size must equal the size stored in the contract + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( preimage.size() > 0 ); } } } From 5c5a0d71ad7a536c371686ec19c07eb75a2e2b3f Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 29 Oct 2018 17:23:41 -0500 Subject: [PATCH 033/132] Beginning of fee implementation --- .../include/graphene/chain/htlc_evaluator.hpp | 1 + .../include/graphene/chain/protocol/htlc.hpp | 20 ++++++++++++++++ tests/cli/main.cpp | 7 ++++++ tests/common/database_fixture.hpp | 4 ++++ tests/tests/htlc_tests.cpp | 23 ++++++++++++++----- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 0be6fb323a..01bf87a0c0 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -34,6 +34,7 @@ namespace graphene { void_result do_evaluate( const htlc_create_operation& o); object_id_type do_apply( const htlc_create_operation& o); + private: optional get_committee_htlc_options(graphene::chain::database& db); }; diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 1ba59198c6..a7a63c663a 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -35,6 +35,7 @@ namespace graphene { { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t price_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network asset fee; @@ -74,6 +75,17 @@ namespace graphene { */ account_id_type fee_payer()const { return source; } + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + // TODO: somehow base this on head block time instead of fc::time_point::now + uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); + uint32_t days = secs / (60 * 60 * 24); + return fee_params.fee + (fee_params.price_per_day * days); + } + }; struct htlc_update_operation : public base_operation @@ -110,6 +122,14 @@ namespace graphene { * @brief Who is to pay the fee */ account_id_type fee_payer()const { return update_issuer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + return fee_params.fee; + } }; } } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3af43dbc4f..4dd606ea4c 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -590,6 +590,13 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) int server_port_number = 0; app1 = start_application(app_dir, server_port_number); + // set committee parameters + app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) { + graphene::chain::htlc_options params; + params.max_preimage_size = 1024; + params.max_timeout_secs = 60 * 60 * 24 * 28; + p.parameters.extensions.value.updatable_htlc_options = params; + }); // connect to the server client_connection con(app1, app_dir, server_port_number); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 78f7b65d19..7ece7ab41d 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -330,6 +330,10 @@ struct database_fixture { void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() ); void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); + /** + * NOTE: This modifies the database directly. You will probably have to call this each time you + * finish creating a block + */ void enable_fees(); void change_fees( const flat_set< fee_parameters >& new_params, uint32_t new_scale = 0 ); void upgrade_to_lifetime_member( account_id_type account ); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index dc73a28daa..e33cc254c9 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -195,9 +195,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(1000000); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); uint16_t key_size = 256; std::vector pre_image(key_size); @@ -208,17 +209,19 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // cler everything out generate_block(); trx.clear(); + enable_fees(); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; create_operation.epoch = db.head_block_time() + fc::seconds(10); create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -230,7 +233,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -240,9 +243,17 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.update_issuer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; + update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); trx.operations.push_back(update_operation); sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); + try + { + PUSH_TX(db, trx, ~0); + } + catch (fc::exception& ex) + { + BOOST_FAIL(ex.what()); + } generate_block(); trx.clear(); } @@ -258,8 +269,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) From ac29e84c7b62ab9eab7de5b4d6f3c4edb171728b Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 30 Oct 2018 15:08:43 -0500 Subject: [PATCH 034/132] committee fee flexibility and bug fixes Modified method names to more closely match the spec. Fixed a misunderstanding on how fees work. Added template operation for finding the correct fee_parameter so that fees can more easily be adjusted within a proposal. --- libraries/chain/db_init.cpp | 3 +- libraries/chain/db_notify.cpp | 6 +- libraries/chain/htlc_evaluator.cpp | 93 ++++++++++++------- .../include/graphene/chain/htlc_evaluator.hpp | 21 +++-- .../chain/protocol/chain_parameters.hpp | 6 +- .../graphene/chain/protocol/fee_schedule.hpp | 6 ++ .../include/graphene/chain/protocol/htlc.hpp | 67 +++++++++++-- .../graphene/chain/protocol/operations.hpp | 3 +- libraries/chain/protocol/htlc.cpp | 5 +- libraries/wallet/wallet.cpp | 23 ++++- tests/tests/htlc_tests.cpp | 18 +--- 11 files changed, 185 insertions(+), 66 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index dd9ac41ebb..69d0f68f52 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -179,7 +179,8 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index d8f8c09ba3..ba8ff5f1ac 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -260,7 +260,11 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); } - void operator()( const htlc_update_operation& op ) + void operator()( const htlc_redeem_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_extend_operation& op ) { _impacted.insert( op.fee_payer() ); } diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 0fc587c7c5..d84b8a50dd 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,7 +30,7 @@ namespace graphene { namespace chain { - optional htlc_create_evaluator::get_committee_htlc_options(graphene::chain::database& db) + optional get_committee_htlc_options(graphene::chain::database& db) { return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } @@ -83,7 +83,7 @@ namespace graphene { } template - bool htlc_update_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; @@ -101,39 +101,70 @@ namespace graphene { return true; } - void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) + void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { - htlc_obj = &db().get(o.htlc_id); - - // TODO: Use signatures to determine what to do, not whether preimage was provided - if (o.preimage.size() > 0) - { - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); - - // see if the preimages match - bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - - FC_ASSERT(match, "Provided preimage does not generate correct hash."); - } - else - { - FC_ASSERT(db().head_block_time().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); - } - return void_result(); + htlc_obj = &db().get(o.htlc_id); + + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + + // see if the preimages match + bool match = false; + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + + FC_ASSERT(match, "Provided preimage does not generate correct hash."); + return void_result(); } - void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) + void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - return void_result(); + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); + return void_result(); } + + void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) + { + htlc_obj = &db().get(o.htlc_id); + + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); + + return void_result(); + } + + void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) + { + db().modify(*htlc_obj, [&o](htlc_object& db_obj) { + db_obj.expiration = o.epoch; + }); + + return void_result(); + } + + + + + + + + + + + + + + + + + + + + + } // namespace chain } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 01bf87a0c0..7c6b641db5 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -34,22 +34,29 @@ namespace graphene { void_result do_evaluate( const htlc_create_operation& o); object_id_type do_apply( const htlc_create_operation& o); - - private: - optional get_committee_htlc_options(graphene::chain::database& db); }; - class htlc_update_evaluator : public evaluator + class htlc_redeem_evaluator : public evaluator { public: - typedef htlc_update_operation operation_type; + typedef htlc_redeem_operation operation_type; - void_result do_evaluate( const htlc_update_operation& o); - void_result do_apply( const htlc_update_operation& o); + void_result do_evaluate( const htlc_redeem_operation& o); + void_result do_apply( const htlc_redeem_operation& o); const htlc_object* htlc_obj = nullptr; private: template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; + + class htlc_extend_evaluator : public evaluator + { + public: + typedef htlc_extend_operation operation_type; + + void_result do_evaluate( const htlc_extend_operation& o); + void_result do_apply( const htlc_extend_operation& o); + const htlc_object* htlc_obj = nullptr; + }; } // namespace graphene } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index f155ecafb4..4d0863f8b5 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -83,7 +83,11 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT( graphene::chain::htlc_options, (max_timeout_secs) (max_preimage_size) ) +FC_REFLECT( graphene::chain::htlc_options, + (max_timeout_secs) + (max_preimage_size) +) + FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index a08ee98a2c..3a054cf2ec 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,6 +43,12 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } + typename Operation::fee_parameters_type& get(flat_set& parameters) + { + auto itr = parameters.find( typename Operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->template get(); + } }; template<> diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index a7a63c663a..7ab9421aab 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -34,8 +34,8 @@ namespace graphene { struct htlc_create_operation : public base_operation { struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - uint64_t price_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network asset fee; @@ -83,12 +83,12 @@ namespace graphene { // TODO: somehow base this on head block time instead of fc::time_point::now uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); uint32_t days = secs / (60 * 60 * 24); - return fee_params.fee + (fee_params.price_per_day * days); + return fee_params.fee + (fee_params.fee_per_day * days); } }; - struct htlc_update_operation : public base_operation + struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; @@ -131,11 +131,62 @@ namespace graphene { return fee_params.fee; } }; + + struct htlc_extend_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type update_issuer; + // the new expiry + fc::time_point_sec epoch; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /*** + * @determines who should have signed this object + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(update_issuer); + } + + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return update_issuer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + // TODO: somehow base this on head block time instead of fc::time_point::now + uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); + uint32_t days = secs / (60 * 60 * 24); + return fee_params.fee + (fee_params.fee_per_day * days); + } + }; } } -FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) -FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) -FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_create_operation, + (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(epoch)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index a666a844ab..2eded4a707 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -98,7 +98,8 @@ namespace graphene { namespace chain { asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, - htlc_update_operation + htlc_redeem_operation, + htlc_extend_operation > operation; /// @} // operations group diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 0d433c57e2..7d21d7ace4 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -31,8 +31,11 @@ namespace graphene { namespace chain { FC_ASSERT( amount.amount > 0 ); } - void htlc_update_operation::validate()const { + void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( preimage.size() > 0 ); } + void htlc_extend_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + } } } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ae4fe270f8..7e7929b855 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1788,7 +1788,7 @@ class wallet_api_impl account_object issuer_obj = get_account(issuer); - htlc_update_operation update_op; + htlc_redeem_operation update_op; update_op.htlc_id = htlc_obj->id; update_op.update_issuer = issuer_obj.id; update_op.preimage = preimage; @@ -1804,7 +1804,26 @@ class wallet_api_impl signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) { - throw std::logic_error("Not Implemented"); + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_extend_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.update_issuer = issuer_obj.id; + update_op.epoch = timeout; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(timeout)(broadcast) ) } vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index e33cc254c9..38c55fd2ec 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -44,7 +44,6 @@ #include #include -#include #include #include @@ -83,10 +82,14 @@ void set_committee_parameters(database_fixture* db_fixture) { // set the committee parameters db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { + // htlc options graphene::chain::htlc_options params; params.max_preimage_size = 1024; params.max_timeout_secs = 60 * 60 * 24 * 28; p.parameters.extensions.value.updatable_htlc_options = params; + // htlc operation fees + p.parameters.current_fees->get().fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + p.parameters.current_fees->get().fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; }); } @@ -239,7 +242,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // send an update operation to claim the funds { - graphene::chain::htlc_update_operation update_operation; + graphene::chain::htlc_redeem_operation update_operation; update_operation.update_issuer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; @@ -257,17 +260,6 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify Alice cannot execute the contract after the fact - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); - generate_block(); - trx.clear(); - } // verify funds end up in Bob's account BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); From 50f65846ceae12aae10ccca18a9d37eff882a41c Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 30 Oct 2018 16:07:50 -0500 Subject: [PATCH 035/132] Calculate fees based on # days --- libraries/chain/htlc_evaluator.cpp | 11 ++++------ .../include/graphene/chain/protocol/htlc.hpp | 18 +++++++---------- .../wallet/include/graphene/wallet/wallet.hpp | 6 +++--- libraries/wallet/wallet.cpp | 20 +++++++++---------- tests/cli/main.cpp | 2 +- tests/tests/htlc_tests.cpp | 18 ++++++++--------- 6 files changed, 33 insertions(+), 42 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index d84b8a50dd..15eb029bd4 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -45,12 +45,9 @@ namespace graphene { FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() - + htlc_options->max_timeout_secs, - "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.seconds_in_force < htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < htlc_options->max_preimage_size, - "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure sender has the funds for the HTLC @@ -74,7 +71,7 @@ namespace graphene { esc.amount = o.amount; esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; - esc.expiration = o.epoch; + esc.expiration = db().head_block_time() + o.seconds_in_force; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -140,7 +137,7 @@ namespace graphene { void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) { db().modify(*htlc_obj, [&o](htlc_object& db_obj) { - db_obj.expiration = o.epoch; + db_obj.expiration = fc::time_point_sec(db_obj.expiration.sec_since_epoch() + o.seconds_to_add); }); return void_result(); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7ab9421aab..503398ba27 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -53,7 +53,7 @@ namespace graphene { // the size of the preimage uint16_t key_size; // The time the funds will be returned to the source if not claimed - fc::time_point_sec epoch; + uint32_t seconds_in_force; // for future expansion extensions_type extensions; @@ -80,9 +80,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - // TODO: somehow base this on head block time instead of fc::time_point::now - uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); - uint32_t days = secs / (60 * 60 * 24); + uint32_t days = seconds_in_force / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -145,8 +143,8 @@ namespace graphene { htlc_id_type htlc_id; // who is attempting to update the transaction account_id_type update_issuer; - // the new expiry - fc::time_point_sec epoch; + // how much to add + uint32_t seconds_to_add; // for future expansion extensions_type extensions; @@ -173,9 +171,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - // TODO: somehow base this on head block time instead of fc::time_point::now - uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); - uint32_t days = secs / (60 * 60 * 24); + uint32_t days = seconds_to_add / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } }; @@ -187,6 +183,6 @@ FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) + (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) -FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(epoch)(extensions)) +FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 2fd8ef1cb1..d8cf519d71 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1465,12 +1465,12 @@ class wallet_api * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param timelock when the time lock expires + * @param seconds_in_force when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ); + const uint32_t seconds_in_force, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1490,7 +1490,7 @@ class wallet_api * @param timelock the new time of expiry * @param broadcast true to broadcast to the network */ - signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const fc::time_point_sec& timelock, + signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast = false); /** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7e7929b855..0b709add69 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1744,7 +1744,7 @@ class wallet_api_impl signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ) + const uint32_t seconds_in_force, bool broadcast = false ) { try { @@ -1756,7 +1756,7 @@ class wallet_api_impl create_op.source = get_account(source).id; create_op.destination = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); - create_op.epoch = timelock; + create_op.seconds_in_force = seconds_in_force; create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) @@ -1775,7 +1775,7 @@ class wallet_api_impl return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) - (preimage_hash)(preimage_size)(timelock)(broadcast) ) + (preimage_hash)(preimage_size)(seconds_in_force)(broadcast) ) } signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) @@ -1802,7 +1802,7 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } - signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) + signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) { try { @@ -1815,7 +1815,7 @@ class wallet_api_impl htlc_extend_operation update_op; update_op.htlc_id = htlc_obj->id; update_op.update_issuer = issuer_obj.id; - update_op.epoch = timeout; + update_op.seconds_to_add = seconds_to_add; signed_transaction tx; tx.operations.push_back(update_op); @@ -1823,7 +1823,7 @@ class wallet_api_impl tx.validate(); return sign_transaction(tx, broadcast); - } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(timeout)(broadcast) ) + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(seconds_to_add)(broadcast) ) } vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) @@ -3105,10 +3105,10 @@ uint64_t wallet_api::get_asset_count()const signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast) + const uint32_t seconds_in_force, bool broadcast) { return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, - timelock, broadcast); + seconds_in_force, broadcast); } graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const @@ -3122,10 +3122,10 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); } -signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const fc::time_point_sec& timelock, +signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, bool broadcast) { - return my->htlc_extend_expiry(htlc_id, issuer, timelock, broadcast); + return my->htlc_extend_expiry(htlc_id, issuer, seconds_to_add, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 4dd606ea4c..6af62e98fe 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -682,7 +682,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::vector hash(preimage_md.data_size()); for(size_t i = 0; i < preimage_md.data_size(); ++i) hash[i] = preimage_md.data()[i]; - fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); + uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->htlc_prepare("alice", "bob", "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 38c55fd2ec..13b0d49390 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,14 +117,13 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) // cler everything out generate_block(); trx.clear(); - fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = expiration; + create_operation.seconds_in_force = 60; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -154,14 +153,13 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // cler everything out generate_block(); trx.clear(); - fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = expiration; + create_operation.seconds_in_force = 60; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -186,9 +184,9 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) BOOST_CHECK(htlc); // let it expire (wait for timeout) - generate_blocks(expiration + fc::seconds(120) ); - // verify funds return (what about fees?) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); + generate_blocks(fc::time_point_sec(120) ); + // verify funds return (minus the fees) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // verify Bob cannot execute the contract after the fact } @@ -219,7 +217,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(10); + create_operation.seconds_in_force = 86400; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -289,7 +287,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(3); + create_operation.seconds_in_force = 3; create_operation.key_hash = key_hash; create_operation.key_size = key_size; create_operation.source = alice_id; @@ -303,7 +301,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(3); + create_operation.seconds_in_force = 3; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; From ca884e2b9f079cece2912c722a5a60106fbd1d0c Mon Sep 17 00:00:00 2001 From: Haruka Ma Date: Tue, 30 Oct 2018 12:28:35 +0900 Subject: [PATCH 036/132] Change description of delayed_node option --- libraries/plugins/delayed_node/delayed_node_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 24a46cc066..f780d8b496 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -58,7 +58,7 @@ delayed_node_plugin::~delayed_node_plugin() void delayed_node_plugin::plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) { cli.add_options() - ("trusted-node", boost::program_options::value(), "RPC endpoint of a trusted validating node (required)") + ("trusted-node", boost::program_options::value(), "RPC endpoint of a trusted validating node (required for delayed_node)") ; cfg.add(cli); } From 9237a716d744472ee19bf17017489574c1dce575 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Mon, 5 Nov 2018 16:27:42 +0100 Subject: [PATCH 037/132] Bump fc after re-improving compile time resource usage --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 8b6a2dd450..12e4962891 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 8b6a2dd450e2c683f234fc8e7af7c8aff5d89607 +Subproject commit 12e4962891b3f1e23bcd49867bd5a9146c3db03e From 55aa07ee538b1b68255a740aea68d792a5723da2 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 7 Nov 2018 14:26:21 +0100 Subject: [PATCH 038/132] Skip tests if compile time is too long --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e21993904..dc295168fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,8 +34,9 @@ script: - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON . - 'which build-wrapper-linux-x86-64 && build-wrapper-linux-x86-64 --out-dir bw-output make -j 2 cli_wallet witness_node chain_test cli_test || make -j 2 cli_wallet witness_node chain_test cli_test' - set -o pipefail + - '[ $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && touch _empty_cache' - '[ -r _empty_cache ] || tests/chain_test 2>&1 | cat' - '[ -r _empty_cache ] || tests/cli_test 2>&1 | cat' - 'find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d | while read d; do gcov -o "$d" "${d/CMakeFiles*.dir//}"/*.cpp; done >/dev/null' - - '( [ -r _empty_cache -o $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && echo "WARNING! Skipping sonar scanner due to time constraints!" ) || ( which sonar-scanner && sonar-scanner || true )' - - '[ ! -r _empty_cache ] || ( echo "Please restart with populated cache" && false )' + - '( [ -r _empty_cache ] || ( which sonar-scanner && sonar-scanner || true )' + - '[ ! -r _empty_cache ] || ( echo "WARNING! Skipped some tests due to compile time! Please restart with populated cache." && false )' From 1695076f5040618005e4d6788e9de8d2cf88a28d Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 7 Nov 2018 16:13:41 +0100 Subject: [PATCH 039/132] Bump fc again for fc#88 --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 12e4962891..9cce60c917 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 12e4962891b3f1e23bcd49867bd5a9146c3db03e +Subproject commit 9cce60c91773ad99cfdfa42c5e86ba6ceb3f3ee9 From 3aa689ea60a951896e45ce40fdd7689fef4c6027 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 7 Nov 2018 16:14:22 +0100 Subject: [PATCH 040/132] Fixed travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dc295168fd..236fa6ef78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,9 +34,9 @@ script: - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON . - 'which build-wrapper-linux-x86-64 && build-wrapper-linux-x86-64 --out-dir bw-output make -j 2 cli_wallet witness_node chain_test cli_test || make -j 2 cli_wallet witness_node chain_test cli_test' - set -o pipefail - - '[ $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && touch _empty_cache' + - '[ $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && touch _empty_cache || true' - '[ -r _empty_cache ] || tests/chain_test 2>&1 | cat' - '[ -r _empty_cache ] || tests/cli_test 2>&1 | cat' - 'find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d | while read d; do gcov -o "$d" "${d/CMakeFiles*.dir//}"/*.cpp; done >/dev/null' - - '( [ -r _empty_cache ] || ( which sonar-scanner && sonar-scanner || true )' + - '[ -r _empty_cache ] || ( which sonar-scanner && sonar-scanner || true )' - '[ ! -r _empty_cache ] || ( echo "WARNING! Skipped some tests due to compile time! Please restart with populated cache." && false )' From e8faf8115455d451c843514cec4137c44de07f75 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 7 Nov 2018 13:16:46 -0500 Subject: [PATCH 041/132] Added hardfork protection to proposal_create --- libraries/chain/proposal_evaluator.cpp | 6 ++++++ tests/tests/htlc_tests.cpp | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 8124cc4fda..650f360e7e 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -144,6 +144,12 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for( auto& op : o.proposed_ops ) { operation_get_required_authorities(op.op, auths, auths, other); + // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. + if (op.op.which() == operation::tag::value) + if (d.head_block_time() < HARDFORK_HTLC_TIME) + if (op.op.get().new_parameters.extensions + .value.updatable_htlc_options.valid()) + FC_ASSERT(false, "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 13b0d49390..9414ef1ce3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) // cler everything out generate_block(); trx.clear(); - // Alice puts a contract on the blockchain + // Alice tries to put a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -132,6 +132,17 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); } + // attempt to create a proposal that contains htlc stuff + { + + graphene::chain::committee_member_update_global_parameters_operation param_op; + + graphene::chain::proposal_create_operation create_operation; + + create_operation.fee_paying_account = committee_account; + create_operation.review_period_seconds = 60 * 60 * 48; + create_operation.proposed_ops.emplace_back(param_op); + } } BOOST_AUTO_TEST_CASE( htlc_expires ) @@ -337,11 +348,9 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) get_account("init6").get_id(), get_account("init7").get_id()}; trx.operations.push_back(uop); sign( trx, init_account_priv_key ); - db.push_transaction(trx); - BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); - generate_blocks(proposal_id_type()(db).expiration_time + 5); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // the proposal should have failed + BOOST_TEST_MESSAGE("Sending proposal."); + GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); trx.clear(); } From 2c7f7f99e0491377b310804293e917c8dc5f061c Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 7 Nov 2018 16:50:34 -0500 Subject: [PATCH 042/132] Removed debugging code --- libraries/chain/proposal_evaluator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 650f360e7e..23c83199e2 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -145,11 +145,10 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati { operation_get_required_authorities(op.op, auths, auths, other); // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. - if (op.op.which() == operation::tag::value) - if (d.head_block_time() < HARDFORK_HTLC_TIME) - if (op.op.get().new_parameters.extensions - .value.updatable_htlc_options.valid()) - FC_ASSERT(false, "Unable to set HTLC parameters before hardfork."); + FC_ASSERT( op.op.which() != operation::tag::value + || d.head_block_time() >= HARDFORK_HTLC_TIME + || !op.op.get().new_parameters.extensions + .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? From 15e903717fdad07eba083dd8505a2f2d5f7926ca Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 9 Nov 2018 16:20:59 -0300 Subject: [PATCH 043/132] refactor es_objects to use templates --- libraries/plugins/es_objects/es_objects.cpp | 194 +++--------------- .../graphene/es_objects/es_objects.hpp | 157 +++++--------- 2 files changed, 82 insertions(+), 269 deletions(-) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index d0c35010ce..0cd4bc256d 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -30,10 +30,12 @@ #include #include #include +#include +#include +#include #include - namespace graphene { namespace es_objects { namespace detail @@ -72,12 +74,8 @@ class es_objects_plugin_impl fc::time_point_sec block_time; private: - void prepare_proposal(const proposal_object& proposal_object); - void prepare_account(const account_object& account_object); - void prepare_asset(const asset_object& asset_object); - void prepare_balance(const account_balance_object& account_balance_object); - void prepare_limit(const limit_order_object& limit_object); - void prepare_bitasset(const asset_bitasset_data_object& bitasset_object); + template + void prepareTemplate(T blockchain_object, string index_name); }; bool es_objects_plugin_impl::index_database( const vector& ids, std::string action) @@ -102,7 +100,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(p->id, "proposal"); else - prepare_proposal(*p); + prepareTemplate(*p, "proposal"); } } else if(value.is() && _es_objects_accounts) { @@ -112,7 +110,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(a->id, "account"); else - prepare_account(*a); + prepareTemplate(*a, "account"); } } else if(value.is() && _es_objects_assets) { @@ -122,7 +120,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(a->id, "asset"); else - prepare_asset(*a); + prepareTemplate(*a, "asset"); } } else if(value.is() && _es_objects_balances) { @@ -132,7 +130,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(b->id, "balance"); else - prepare_balance(*b); + prepareTemplate(*b, "balance"); } } else if(value.is() && _es_objects_limit_orders) { @@ -142,7 +140,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(l->id, "limitorder"); else - prepare_limit(*l); + prepareTemplate(*l, "limitorder"); } } else if(value.is() && _es_objects_asset_bitasset) { @@ -152,7 +150,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(ba->id, "bitasset"); else - prepare_bitasset(*ba); + prepareTemplate(*ba, "bitasset"); } } } @@ -190,181 +188,39 @@ void es_objects_plugin_impl::remove_from_database( object_id_type id, std::strin } } -void es_objects_plugin_impl::prepare_proposal(const proposal_object& proposal_object) -{ - proposal_struct prop; - prop.object_id = proposal_object.id; - prop.block_time = block_time; - prop.block_number = block_number; - prop.expiration_time = proposal_object.expiration_time; - prop.review_period_time = proposal_object.review_period_time; - prop.proposed_transaction = fc::json::to_string(proposal_object.proposed_transaction); - prop.required_owner_approvals = fc::json::to_string(proposal_object.required_owner_approvals); - prop.available_owner_approvals = fc::json::to_string(proposal_object.available_owner_approvals); - prop.required_active_approvals = fc::json::to_string(proposal_object.required_active_approvals); - prop.available_key_approvals = fc::json::to_string(proposal_object.available_key_approvals); - prop.proposer = proposal_object.proposer; - - std::string data = fc::json::to_string(prop); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "proposal"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(prop.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} - -void es_objects_plugin_impl::prepare_account(const account_object& account_object) -{ - account_struct acct; - acct.object_id = account_object.id; - acct.block_time = block_time; - acct.block_number = block_number; - acct.membership_expiration_date = account_object.membership_expiration_date; - acct.registrar = account_object.registrar; - acct.referrer = account_object.referrer; - acct.lifetime_referrer = account_object.lifetime_referrer; - acct.network_fee_percentage = account_object.network_fee_percentage; - acct.lifetime_referrer_fee_percentage = account_object.lifetime_referrer_fee_percentage; - acct.referrer_rewards_percentage = account_object.referrer_rewards_percentage; - acct.name = account_object.name; - acct.owner_account_auths = fc::json::to_string(account_object.owner.account_auths); - acct.owner_key_auths = fc::json::to_string(account_object.owner.key_auths); - acct.owner_address_auths = fc::json::to_string(account_object.owner.address_auths); - acct.active_account_auths = fc::json::to_string(account_object.active.account_auths); - acct.active_key_auths = fc::json::to_string(account_object.active.key_auths); - acct.active_address_auths = fc::json::to_string(account_object.active.address_auths); - acct.voting_account = account_object.options.voting_account; - acct.votes = fc::json::to_string(account_object.options.votes); - - std::string data = fc::json::to_string(acct); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "account"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(acct.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} - -void es_objects_plugin_impl::prepare_asset(const asset_object& asset_object) +template +void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_name) { - asset_struct asset; - asset.object_id = asset_object.id; - asset.block_time = block_time; - asset.block_number = block_number; - asset.symbol = asset_object.symbol; - asset.issuer = asset_object.issuer; - asset.is_market_issued = asset_object.is_market_issued(); - asset.dynamic_asset_data_id = asset_object.dynamic_asset_data_id; - asset.bitasset_data_id = asset_object.bitasset_data_id; - - std::string data = fc::json::to_string(asset); - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "asset"; + bulk_header["_index"] = _es_objects_index_prefix + index_name; bulk_header["_type"] = "data"; if(_es_objects_keep_only_current) { - bulk_header["_id"] = string(asset.object_id); + bulk_header["_id"] = string(blockchain_object.id); } - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} + auto blockchain_object_string = fc::json::to_string(blockchain_object, fc::json::legacy_generator); -void es_objects_plugin_impl::prepare_balance(const account_balance_object& account_balance_object) -{ - balance_struct balance; - balance.object_id = account_balance_object.id; - balance.block_time = block_time; - balance.block_number = block_number; - balance.owner = account_balance_object.owner; - balance.asset_type = account_balance_object.asset_type; - balance.balance = account_balance_object.balance; - balance.maintenance_flag = account_balance_object.maintenance_flag; + variant j = fc::json::from_string(blockchain_object_string); - std::string data = fc::json::to_string(balance); + fc::mutable_variant_object o; - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "balance"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(balance.object_id); - } + adaptor_struct adaptor; + auto adapted = adaptor.adapt(j.get_object()); - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} + fc::from_variant(adapted, o, FC_PACK_MAX_DEPTH); -void es_objects_plugin_impl::prepare_limit(const limit_order_object& limit_object) -{ - limit_order_struct limit; - limit.object_id = limit_object.id; - limit.block_time = block_time; - limit.block_number = block_number; - limit.expiration = limit_object.expiration; - limit.seller = limit_object.seller; - limit.for_sale = limit_object.for_sale; - limit.sell_price = limit_object.sell_price; - limit.deferred_fee = limit_object.deferred_fee; - - std::string data = fc::json::to_string(limit); + o["object_id"] = string(blockchain_object.id); + o["block_time"] = block_time; + o["block_number"] = block_number; - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "limitorder"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(limit.object_id); - } + string data = fc::json::to_string(o, fc::json::legacy_generator); prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); prepare.clear(); } -void es_objects_plugin_impl::prepare_bitasset(const asset_bitasset_data_object& bitasset_object) -{ - if(!bitasset_object.is_prediction_market) { - - bitasset_struct bitasset; - bitasset.object_id = bitasset_object.id; - bitasset.block_time = block_time; - bitasset.block_number = block_number; - bitasset.current_feed = fc::json::to_string(bitasset_object.current_feed); - bitasset.current_feed_publication_time = bitasset_object.current_feed_publication_time; - - std::string data = fc::json::to_string(bitasset); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "bitasset"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(bitasset.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); - } -} - es_objects_plugin_impl::~es_objects_plugin_impl() { return; diff --git a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp index 54ecbdc90d..a66e299551 100644 --- a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp +++ b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp @@ -30,7 +30,6 @@ namespace graphene { namespace es_objects { using namespace chain; - namespace detail { class es_objects_plugin_impl; @@ -54,105 +53,63 @@ class es_objects_plugin : public graphene::app::plugin std::unique_ptr my; }; -struct proposal_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec expiration_time; - optional review_period_time; - string proposed_transaction; - string required_active_approvals; - string available_active_approvals; - string required_owner_approvals; - string available_owner_approvals; - string available_key_approvals; - account_id_type proposer; -}; -struct account_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec membership_expiration_date; - account_id_type registrar; - account_id_type referrer; - account_id_type lifetime_referrer; - uint16_t network_fee_percentage; - uint16_t lifetime_referrer_fee_percentage; - uint16_t referrer_rewards_percentage; - string name; - string owner_account_auths; - string owner_key_auths; - string owner_address_auths; - string active_account_auths; - string active_key_auths; - string active_address_auths; - account_id_type voting_account; - string votes; -}; -struct asset_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - string symbol; - account_id_type issuer; - bool is_market_issued; - asset_dynamic_data_id_type dynamic_asset_data_id; - optional bitasset_data_id; -}; -struct balance_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - account_id_type owner; - asset_id_type asset_type; - share_type balance; - bool maintenance_flag; -}; -struct limit_order_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec expiration; - account_id_type seller; - share_type for_sale; - price sell_price; - share_type deferred_fee; -}; -struct bitasset_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - string current_feed; - time_point_sec current_feed_publication_time; - time_point_sec feed_expiration_time; +struct adaptor_struct { + variant adapt(const variant_object &obj) { + fc::mutable_variant_object o(obj); + vector keys_to_rename; + for (auto i = o.begin(); i != o.end(); ++i) { + auto &element = (*i).value(); + if (element.is_object()) { + const string &name = (*i).key(); + auto &vo = element.get_object(); + if (vo.contains(name.c_str())) + keys_to_rename.emplace_back(name); + element = adapt(vo); + } else if (element.is_array()) + adapt(element.get_array()); + } + for (const auto &i : keys_to_rename) { + string new_name = i + "_"; + o[new_name] = variant(o[i]); + o.erase(i); + } + if (o.find("owner") != o.end() && o["owner"].is_string()) + { + o["owner_"] = o["owner"].as_string(); + o.erase("owner"); + } + if (o.find("active_special_authority") != o.end()) + { + o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]); + } + if (o.find("owner_special_authority") != o.end()) + { + o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]); + } + if (o.find("feeds") != o.end()) + { + o["feeds"] = fc::json::to_string(o["feeds"]); + } + if (o.find("operations") != o.end()) + { + o["operations"] = fc::json::to_string(o["operations"]); + } + + variant v; + fc::to_variant(o, v, FC_PACK_MAX_DEPTH); + return v; + } + + void adapt(fc::variants &v) { + for (auto &array_element : v) { + if (array_element.is_object()) + array_element = adapt(array_element.get_object()); + else if (array_element.is_array()) + adapt(array_element.get_array()); + else + array_element = array_element.as_string(); + } + } }; } } //graphene::es_objects - -FC_REFLECT( - graphene::es_objects::proposal_struct, - (object_id)(block_time)(block_number)(expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals) - (available_active_approvals)(required_owner_approvals)(available_owner_approvals)(available_key_approvals)(proposer) -) -FC_REFLECT( - graphene::es_objects::account_struct, - (object_id)(block_time)(block_number)(membership_expiration_date)(registrar)(referrer)(lifetime_referrer) - (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)(name)(owner_account_auths) - (owner_key_auths)(owner_address_auths)(active_account_auths)(active_key_auths)(active_address_auths)(voting_account)(votes) -) -FC_REFLECT( - graphene::es_objects::asset_struct, - (object_id)(block_time)(block_number)(symbol)(issuer)(is_market_issued)(dynamic_asset_data_id)(bitasset_data_id) -) -FC_REFLECT( - graphene::es_objects::balance_struct, - (object_id)(block_time)(block_number)(owner)(asset_type)(balance)(maintenance_flag) -) -FC_REFLECT( - graphene::es_objects::limit_order_struct, - (object_id)(block_time)(block_number)(expiration)(seller)(for_sale)(sell_price)(deferred_fee) -) -FC_REFLECT( - graphene::es_objects::bitasset_struct, - (object_id)(block_time)(block_number)(current_feed)(current_feed_publication_time) -) \ No newline at end of file From 86fb00648019cbd87fa9c07694c4c1b623010a3f Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 1 Nov 2018 19:06:11 -0600 Subject: [PATCH 044/132] Fix warning Potential optimization: don't move a temporary as this prevents copy elision --- libraries/app/database_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index acb90c8af5..6e05f5fd0d 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2448,7 +2448,7 @@ void database_api_impl::on_applied_block() } if( market.valid() && _market_subscriptions.count(*market) ) // FIXME this may cause fill_order_operation be pushed before order creation - subscribed_markets_ops[*market].emplace_back( std::move( std::make_pair( op.op, op.result ) ) ); + subscribed_markets_ops[*market].emplace_back(std::make_pair(op.op, op.result)); } /// we need to ensure the database_api is not deleted for the life of the async operation auto capture_this = shared_from_this(); From 3d24032ade2c3e72250877669eb0158c89027fa9 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Oct 2018 17:13:08 -0500 Subject: [PATCH 045/132] Fix build errors - Add missing FC_REFLECT_TYPENAME on account operation extentions - Replace deprecated call to fc::rand_pseudo_bytes with fc::rand_bytes. --- libraries/chain/include/graphene/chain/protocol/account.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index f6178d3e24..f2be53837b 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -274,6 +274,7 @@ FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, (no_listing)(white_listed)(black_listed)(white_and_black_listed)) FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) ) +FC_REFLECT_TYPENAME(graphene::chain::extension) FC_REFLECT( graphene::chain::account_create_operation, (fee)(registrar) (referrer)(referrer_percent) @@ -281,6 +282,7 @@ FC_REFLECT( graphene::chain::account_create_operation, ) FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) ) +FC_REFLECT_TYPENAME(graphene::chain::extension) FC_REFLECT( graphene::chain::account_update_operation, (fee)(account)(owner)(active)(new_options)(extensions) ) From 9952121981381d5bc62abd87192b94d01d61a708 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 7 Nov 2018 12:09:01 -0600 Subject: [PATCH 046/132] Include smart_ref_impl from fork_database.cpp Required for build on some configurations --- libraries/chain/fork_database.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 39e9d3b83f..b94a2cdc29 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace graphene { namespace chain { fork_database::fork_database() { From 6225ee42f6ea847c7370f7e407c0f15c09ee464e Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 10 Nov 2018 16:18:24 -0300 Subject: [PATCH 047/132] remove not needed include --- libraries/plugins/es_objects/es_objects.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 0cd4bc256d..766b7ce886 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include @@ -74,8 +73,8 @@ class es_objects_plugin_impl fc::time_point_sec block_time; private: - template - void prepareTemplate(T blockchain_object, string index_name); + template + void prepareTemplate(T blockchain_object, string index_name); }; bool es_objects_plugin_impl::index_database( const vector& ids, std::string action) From 81c276921a8ffef9923b73b47d9f85a4cab69e5c Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 10 Nov 2018 16:40:39 -0300 Subject: [PATCH 048/132] change variable name --- libraries/plugins/es_objects/es_objects.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 766b7ce886..01ec4722a6 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -199,16 +199,13 @@ void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_n } auto blockchain_object_string = fc::json::to_string(blockchain_object, fc::json::legacy_generator); - - variant j = fc::json::from_string(blockchain_object_string); - + variant blockchain_object_variant = fc::json::from_string(blockchain_object_string); fc::mutable_variant_object o; adaptor_struct adaptor; - auto adapted = adaptor.adapt(j.get_object()); + auto adapted = adaptor.adapt(blockchain_object_variant.get_object()); fc::from_variant(adapted, o, FC_PACK_MAX_DEPTH); - o["object_id"] = string(blockchain_object.id); o["block_time"] = block_time; o["block_number"] = block_number; From 5d1c15cdd7faa955463902ea1348ba302eb65681 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sun, 25 Nov 2018 21:13:09 -0300 Subject: [PATCH 049/132] add last_vote_time --- libraries/chain/account_evaluator.cpp | 12 +- .../include/graphene/chain/account_object.hpp | 3 + tests/tests/voting_tests.cpp | 125 ++++++++++++++++++ 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index d550007c76..1ef78dd48c 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -57,7 +57,7 @@ void verify_authority_accounts( const database& db, const authority& a ) } } -void verify_account_votes( const database& db, const account_options& options ) +void verify_account_votes( database& db, const account_options& options, const account_id_type account = account_id_type(0)) { // ensure account's votes satisfy requirements // NB only the part of vote checking that requires chain state is here, @@ -117,9 +117,15 @@ void verify_account_votes( const database& db, const account_options& options ) } } } + if(account != account_id_type(0) && + (options.votes != account(db).options.votes || options.voting_account != account(db).options.voting_account)) { + auto &stats_obj = db.get_account_stats_by_owner(account); + db.modify(stats_obj, [&](account_statistics_object &obj) { + obj.last_vote_time = db.head_block_time(); + }); + } } - void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) { try { database& d = db(); @@ -306,7 +312,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); if( o.new_options.valid() ) - verify_account_votes( d, *o.new_options ); + verify_account_votes(d, *o.new_options, o.account); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index cae9d35984..8e9297a52d 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -70,6 +70,8 @@ namespace graphene { namespace chain { bool is_voting = false; ///< redundately store whether this account is voting for better maintenance performance + time_point_sec last_vote_time; // add last time voted + /// Whether this account owns some CORE asset and is voting inline bool has_some_core_voting() const { @@ -453,6 +455,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (core_in_balance) (has_cashback_vb) (is_voting) + (last_vote_time) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) ) diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index 34f39e48c1..b4af210637 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -414,5 +414,130 @@ BOOST_AUTO_TEST_CASE(invalid_voting_account) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(last_voting_date) +{ + try + { + ACTORS((alice)); + + transfer(committee_account, alice_id, asset(100)); + + // we are going to vote for this witness + auto witness1 = witness_id_type(1)(db); + + auto stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), 0); + + // alice votes + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice.options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0 ); + + auto now = db.head_block_time().sec_since_epoch(); + + // last_vote_time is updated for alice + stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), now); + + } FC_LOG_AND_RETHROW() +} +BOOST_AUTO_TEST_CASE(last_voting_date_proxy) +{ + try + { + ACTORS((alice)(proxy)(bob)); + + transfer(committee_account, alice_id, asset(100)); + transfer(committee_account, bob_id, asset(200)); + transfer(committee_account, proxy_id, asset(300)); + + generate_block(); + + // witness to vote for + auto witness1 = witness_id_type(1)(db); + + // alice changes proxy, this is voting activity + { + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice_id(db).options; + op.new_options->voting_account = proxy_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0 ); + } + // alice last_vote_time is updated + auto alice_stats_obj = db.get_account_stats_by_owner(alice_id); + auto now = db.head_block_time().sec_since_epoch(); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), now); + + generate_block(); + + // alice update account but no proxy or voting changes are done + { + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice_id(db).options; + trx.operations.push_back(op); + sign(trx, alice_private_key); + set_expiration( db, trx ); + PUSH_TX( db, trx, ~0 ); + } + // last_vote_time is not updated + now = db.head_block_time().sec_since_epoch(); + alice_stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK(alice_stats_obj.last_vote_time.sec_since_epoch() != now); + + generate_block(); + + // bob votes + { + graphene::chain::account_update_operation op; + op.account = bob_id; + op.new_options = bob_id(db).options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, bob_private_key); + set_expiration( db, trx ); + PUSH_TX(db, trx, ~0); + } + + // last_vote_time for bob is updated as he voted + now = db.head_block_time().sec_since_epoch(); + auto bob_stats_obj = db.get_account_stats_by_owner(bob_id); + BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), now); + + generate_block(); + + // proxy votes + { + graphene::chain::account_update_operation op; + op.account = proxy_id; + op.new_options = proxy_id(db).options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, proxy_private_key); + PUSH_TX(db, trx, ~0); + } + + // proxy just voted so the last_vote_time is updated + now = db.head_block_time().sec_since_epoch(); + auto proxy_stats_obj = db.get_account_stats_by_owner(proxy_id); + BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), now); + + // alice haves proxy, proxy votes but last_vote_time is not updated for alice + alice_stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK(alice_stats_obj.last_vote_time.sec_since_epoch() != now); + + // bob haves nothing to do with proxy so last_vote_time is not updated + bob_stats_obj = db.get_account_stats_by_owner(bob_id); + BOOST_CHECK(bob_stats_obj.last_vote_time.sec_since_epoch() != now); + + } FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() From 5909b13da4820bab475b25ecb908b0f44bdaee5d Mon Sep 17 00:00:00 2001 From: Alfredo Date: Wed, 28 Nov 2018 20:17:20 -0300 Subject: [PATCH 050/132] add option elasticsearch-start-es-after-block --- .../plugins/elasticsearch/elasticsearch_plugin.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index dcffaea5f9..bfde1e02e8 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -59,6 +59,7 @@ class elasticsearch_plugin_impl std::string _elasticsearch_basic_auth = ""; std::string _elasticsearch_index_prefix = "bitshares-"; bool _elasticsearch_operation_object = false; + uint32_t _elasticsearch_start_es_after_block = 0; // disabled CURL *curl; // curl handler vector bulk_lines; // vector of op lines vector prepare; @@ -428,6 +429,7 @@ void elasticsearch_plugin::plugin_set_program_options( ("elasticsearch-basic-auth", boost::program_options::value(), "Pass basic auth to elasticsearch database('')") ("elasticsearch-index-prefix", boost::program_options::value(), "Add a prefix to the index(bitshares-)") ("elasticsearch-operation-object", boost::program_options::value(), "Save operation as object(false)") + ("elasticsearch-start-es-after-block", boost::program_options::value(), "Start doing ES job after block(0)") ; cfg.add(cli); } @@ -435,11 +437,14 @@ void elasticsearch_plugin::plugin_set_program_options( void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) { database().applied_block.connect( [&]( const signed_block& b) { - if(!my->update_account_histories(b)) - { - FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Error populating ES database, we are going to keep trying."); + if(my->_elasticsearch_start_es_after_block == 0 || b.block_num() > my->_elasticsearch_start_es_after_block) { + if (!my->update_account_histories(b)) { + FC_THROW_EXCEPTION(graphene::chain::plugin_exception, + "Error populating ES database, we are going to keep trying."); + } } } ); + my->_oho_index = database().add_index< primary_index< operation_history_index > >(); database().add_index< primary_index< account_transaction_history_index > >(); @@ -464,6 +469,9 @@ void elasticsearch_plugin::plugin_initialize(const boost::program_options::varia if (options.count("elasticsearch-operation-object")) { my->_elasticsearch_operation_object = options["elasticsearch-operation-object"].as(); } + if (options.count("elasticsearch-start-es-after-block")) { + my->_elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as(); + } } void elasticsearch_plugin::plugin_startup() From ee4cc4b96ca278e2b8ed28fe5b52cecd9119f755 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Wed, 28 Nov 2018 20:50:05 -0300 Subject: [PATCH 051/132] move start_es_after_block check more deeper --- .../elasticsearch/elasticsearch_plugin.cpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index bfde1e02e8..6af2df19ac 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -75,7 +75,7 @@ class elasticsearch_plugin_impl std::string index_name; bool is_sync = false; private: - bool add_elasticsearch( const account_id_type account_id, const optional& oho ); + bool add_elasticsearch( const account_id_type account_id, const optional& oho, const uint32_t block_number ); const account_transaction_history_object& addNewEntry(const account_statistics_object& stats_obj, const account_id_type& account_id, const optional & oho); @@ -164,7 +164,7 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b for( auto& account_id : impacted ) { - if(!add_elasticsearch( account_id, oho )) + if(!add_elasticsearch( account_id, oho, b.block_num() )) return false; } } @@ -277,13 +277,16 @@ void elasticsearch_plugin_impl::doVisitor(const optional & oho) + const optional & oho, + const uint32_t block_number) { const auto &stats_obj = getStatsObject(account_id); const auto &ath = addNewEntry(stats_obj, account_id, oho); growStats(stats_obj, ath); - createBulkLine(ath); - prepareBulk(ath.id); + if(_elasticsearch_start_es_after_block == 0 || block_number > _elasticsearch_start_es_after_block) { + createBulkLine(ath); + prepareBulk(ath.id); + } cleanObjects(ath.id, account_id); if (curl && bulk_lines.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech @@ -437,12 +440,8 @@ void elasticsearch_plugin::plugin_set_program_options( void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) { database().applied_block.connect( [&]( const signed_block& b) { - if(my->_elasticsearch_start_es_after_block == 0 || b.block_num() > my->_elasticsearch_start_es_after_block) { - if (!my->update_account_histories(b)) { - FC_THROW_EXCEPTION(graphene::chain::plugin_exception, - "Error populating ES database, we are going to keep trying."); - } - } + if (!my->update_account_histories(b)) + FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Error populating ES database, we are going to keep trying."); } ); my->_oho_index = database().add_index< primary_index< operation_history_index > >(); From 5e24f652b9e81a8289bdc3c9a99e111e92518ada Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 30 Nov 2018 19:18:39 -0300 Subject: [PATCH 052/132] change last_vote_time to do_apply --- libraries/chain/account_evaluator.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 1ef78dd48c..23891ef555 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -57,7 +57,7 @@ void verify_authority_accounts( const database& db, const authority& a ) } } -void verify_account_votes( database& db, const account_options& options, const account_id_type account = account_id_type(0)) +void verify_account_votes( const database& db, const account_options& options) { // ensure account's votes satisfy requirements // NB only the part of vote checking that requires chain state is here, @@ -117,13 +117,6 @@ void verify_account_votes( database& db, const account_options& options, const a } } } - if(account != account_id_type(0) && - (options.votes != account(db).options.votes || options.voting_account != account(db).options.voting_account)) { - auto &stats_obj = db.get_account_stats_by_owner(account); - db.modify(stats_obj, [&](account_statistics_object &obj) { - obj.last_vote_time = db.head_block_time(); - }); - } } void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) @@ -312,7 +305,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); if( o.new_options.valid() ) - verify_account_votes(d, *o.new_options, o.account); + verify_account_votes(d, *o.new_options); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } @@ -324,11 +317,16 @@ void_result account_update_evaluator::do_apply( const account_update_operation& bool sa_before = acnt->has_special_authority(); // update account statistics - if( o.new_options.valid() && o.new_options->is_voting() != acnt->options.is_voting() ) + if( o.new_options.valid() ) { - d.modify( acnt->statistics( d ), []( account_statistics_object& aso ) + d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso ) { - aso.is_voting = !aso.is_voting; + if(o.new_options->is_voting() != acnt->options.is_voting()) + aso.is_voting = !aso.is_voting; + + if((o.new_options->votes != acnt->options.votes || + o.new_options->voting_account != acnt->options.voting_account)) + aso.last_vote_time = d.head_block_time(); } ); } From 5688e7b0fa7fc9d4f594e8ec6929c9c0dc8cbb57 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 30 Nov 2018 19:22:02 -0300 Subject: [PATCH 053/132] minor spacing changes --- libraries/chain/account_evaluator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 23891ef555..086953dcfb 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -57,7 +57,7 @@ void verify_authority_accounts( const database& db, const authority& a ) } } -void verify_account_votes( const database& db, const account_options& options) +void verify_account_votes( const database& db, const account_options& options ) { // ensure account's votes satisfy requirements // NB only the part of vote checking that requires chain state is here, @@ -305,7 +305,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); if( o.new_options.valid() ) - verify_account_votes(d, *o.new_options); + verify_account_votes( d, *o.new_options ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } From d8323d4c9d36b7c4651e0e2462ad9fdf451fd812 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 2 Dec 2018 10:38:47 +0100 Subject: [PATCH 054/132] Introduced direct_index --- libraries/db/include/graphene/db/index.hpp | 108 ++++++++++++++++++++- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index bcf9b24f13..fd2923f599 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -190,6 +190,95 @@ namespace graphene { namespace db { object_database& _db; }; + /** @class direct_index + * @brief A secondary index that tracks objects in vectors indexed by object + * id. It is meant for fully (or almost fully) populated indexes only (will + * fail when loading an object_database with large gaps). + */ + template + class direct_index : public secondary_index + { + static_assert( chunkbits < 64, "Do you really want arrays with more than 2^63 elements???" ); + + private: + static const size_t MAX_HOLE = 100; + static const size_t _mask = ((1 << chunkbits) - 1); + size_t next = 0; + vector< vector< const Object* > > content; + + public: + direct_index() { + FC_ASSERT( (1ULL << chunkbits) > MAX_HOLE, "Small chunkbits is inefficient." ); + } + + virtual ~direct_index(){} + + virtual void object_inserted( const object& obj ) + { + uint64_t instance = obj.id.instance(); + if( instance == next ) + { + if( !(next & _mask) ) + { + content.resize((next >> chunkbits) + 1); + content[next >> chunkbits].reserve( 1 << chunkbits ); + } + next++; + } + else if( instance < next ) + FC_ASSERT( !content[instance >> chunkbits][instance & _mask], "Overwriting insert at {id}!", ("id",obj.id) ); + else // instance > next, allow small "holes" + { + FC_ASSERT( instance <= next + MAX_HOLE, "Out-of-order insert: {id} > {next}!", ("id",obj.id)("next",next) ); + if( !(next & _mask) || (next & (~_mask)) != (instance & (~_mask)) ) + { + content.resize((instance >> chunkbits) + 1); + content[instance >> chunkbits].reserve( 1 << chunkbits ); + } + while( next <= instance ) + { + content[next >> chunkbits][next & _mask] = nullptr; + next++; + } + } + FC_ASSERT( nullptr != dynamic_cast(&obj), "Wrong object type!" ); + content[instance >> chunkbits][instance & _mask] = static_cast( &obj ); + } + + virtual void object_removed( const object& obj ) + { + FC_ASSERT( nullptr != dynamic_cast(&obj), "Wrong object type!" ); + uint64_t instance = obj.id.instance(); + FC_ASSERT( instance < next, "Removing out-of-range object: {id} > {next}!", ("id",obj.id)("next",next) ); + FC_ASSERT( content[instance >> chunkbits][instance & _mask], "Removing non-existent object {id}!", ("id",obj.id) ); + content[instance >> chunkbits][instance & _mask] = nullptr; + } + + template< typename object_id > + const Object* find( const object_id& id )const + { + static_assert( object_id::space_id == Object::space_id, "Space ID mismatch!" ); + static_assert( object_id::type_id == Object::type_id, "Type_ID mismatch!" ); + if( id.instance >= next ) return nullptr; + return content[id.instance >> chunkbits][id.instance & _mask]; + }; + + template< typename object_id > + const Object& get( const object_id& id )const + { + const Object* ptr = find( id ); + FC_ASSERT( ptr != nullptr, "Object not found!" ); + return *ptr; + }; + + const Object* find( const object_id_type& id )const + { + FC_ASSERT( id.space() == Object::space_id, "Space ID mismatch!" ); + FC_ASSERT( id.type() == Object::type_id, "Type_ID mismatch!" ); + if( id.instance() >= next ) return nullptr; + return content[id.instance() >> chunkbits][id.instance() & ((1 << chunkbits) - 1)]; + }; + }; /** * @class primary_index @@ -198,14 +287,18 @@ namespace graphene { namespace db { * * @see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - template + template class primary_index : public DerivedIndex, public base_primary_index { public: typedef typename DerivedIndex::object_type object_type; primary_index( object_database& db ) - :base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0) {} + :base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0) + { + if( DirectBits > 0 ) + _direct_by_id = add_secondary_index< direct_index< object_type, DirectBits > >(); + } virtual uint8_t object_space_id()const override { return object_type::space_id; } @@ -217,6 +310,14 @@ namespace graphene { namespace db { virtual void use_next_id()override { ++_next_id.number; } virtual void set_next_id( object_id_type id )override { _next_id = id; } + /** @return the object with id or nullptr if not found */ + virtual const object* find( object_id_type id )const override + { + if( DirectBits > 0 ) + return _direct_by_id->find( id ); + return DerivedIndex::find( id ); + } + fc::sha256 get_object_version()const { std::string desc = "1.0";//get_type_description(); @@ -329,7 +430,8 @@ namespace graphene { namespace db { } private: - object_id_type _next_id; + object_id_type _next_id; + const direct_index< object_type, DirectBits >* _direct_by_id = nullptr; }; } } // graphene::db From 6d79167a570e9e73d6adc3c4524296e96b8ec3a8 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 2 Dec 2018 10:45:43 +0100 Subject: [PATCH 055/132] Use new derived_index for some --- libraries/chain/db_init.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 34d16f4b82..c87d9ec6a6 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -182,15 +182,15 @@ void database::initialize_indexes() _undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY ); //Protocol object indexes - add_index< primary_index >(); + add_index< primary_index >(); // 8192 assets per chunk add_index< primary_index >(); - auto acnt_index = add_index< primary_index >(); + auto acnt_index = add_index< primary_index >(); // ~1 million accounts per chunk acnt_index->add_secondary_index(); acnt_index->add_secondary_index(); - add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); // 256 members per chunk + add_index< primary_index >(); // 1024 witnesses per chunk add_index< primary_index >(); add_index< primary_index >(); @@ -206,10 +206,10 @@ void database::initialize_indexes() //Implementation object indexes add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); // 8192 add_index< primary_index> >(); add_index< primary_index> >(); - add_index< primary_index >(); + add_index< primary_index >(); // 1 Mi add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index > >(); From c367219cbad3c264a9d0c92abeaeb967dd6e133d Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 2 Dec 2018 10:46:12 +0100 Subject: [PATCH 056/132] Fixed casts --- libraries/app/database_api.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6e05f5fd0d..9316cfa4b6 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -547,6 +547,10 @@ vector> database_api::get_key_references( vector> database_api_impl::get_key_references( vector keys )const { + const auto& idx = _db.get_index_type(); + const auto& aidx = dynamic_cast(idx); + const auto& refs = aidx.get_secondary_index(); + vector< vector > final_result; final_result.reserve(keys.size()); @@ -566,10 +570,6 @@ vector> database_api_impl::get_key_references( vector(); - const auto& aidx = dynamic_cast&>(idx); - const auto& refs = aidx.get_secondary_index(); - auto itr = refs.account_to_key_memberships.find(key); vector result; for( auto& a : {a1,a2,a3,a4,a5} ) @@ -585,6 +585,7 @@ vector> database_api_impl::get_key_references( vectorsecond.size() ); @@ -620,7 +621,7 @@ bool database_api_impl::is_public_key_registered(string public_key) const return false; } const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); + const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); auto itr = refs.account_to_key_memberships.find(key); bool is_known = itr != refs.account_to_key_memberships.end(); @@ -755,6 +756,10 @@ std::map database_api::get_full_accounts( const vector database_api_impl::get_full_accounts( const vector& names_or_ids, bool subscribe) { + const auto& proposal_idx = _db.get_index_type(); + const auto& pidx = dynamic_cast(proposal_idx); + const auto& proposals_by_account = pidx.get_secondary_index(); + std::map results; for (const std::string& account_name_or_id : names_or_ids) @@ -784,9 +789,6 @@ std::map database_api_impl::get_full_accounts( const acnt.cashback_balance = account->cashback_balance(_db); } // Add the account's proposals - const auto& proposal_idx = _db.get_index_type(); - const auto& pidx = dynamic_cast&>(proposal_idx); - const auto& proposals_by_account = pidx.get_secondary_index(); auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id ); if( required_approvals_itr != proposals_by_account._account_to_proposals.end() ) { @@ -869,7 +871,7 @@ vector database_api::get_account_references( const std::string vector database_api_impl::get_account_references( const std::string account_id_or_name )const { const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); + const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); const account_id_type account_id = get_account_from_string(account_id_or_name)->id; auto itr = refs.account_to_account_memberships.find(account_id); From bcbf5d3c13c6277f83112754d4826f790179c035 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 2 Dec 2018 10:47:14 +0100 Subject: [PATCH 057/132] Fixed wrong reserve() calls --- libraries/app/database_api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6e05f5fd0d..ba77512d52 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -577,7 +577,7 @@ vector> database_api_impl::get_key_references( vectorsecond.size() ); + result.reserve( result.size() + itr->second.size() ); for( auto item : itr->second ) { result.push_back(item); @@ -587,7 +587,7 @@ vector> database_api_impl::get_key_references( vectorsecond.size() ); + result.reserve( result.size() + itr->second.size() ); for( auto item : itr->second ) result.push_back(item); } final_result.emplace_back( std::move(result) ); From 59db3f56d239cdc57c7df67f0ed5bb6fe9f4a8c2 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 2 Dec 2018 15:13:35 +0100 Subject: [PATCH 058/132] Do not ignore exceptions while reading object_database --- libraries/db/include/graphene/db/index.hpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index bcf9b24f13..f98412d45b 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -234,14 +234,12 @@ namespace graphene { namespace db { fc::raw::unpack(ds, _next_id); fc::raw::unpack(ds, open_ver); FC_ASSERT( open_ver == get_object_version(), "Incompatible Version, the serialization of objects in this index has changed" ); - try { - vector tmp; - while( true ) - { - fc::raw::unpack( ds, tmp ); - load( tmp ); - } - } catch ( const fc::exception& ){} + vector tmp; + while( ds.remaining() > 0 ) + { + fc::raw::unpack( ds, tmp ); + load( tmp ); + } } virtual void save( const path& db ) override From 8999eacf934eb85e1ed9923fdcffa42d365a16e5 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Mon, 8 Oct 2018 17:19:31 +0200 Subject: [PATCH 059/132] Removed unused _unlinked_index --- libraries/chain/fork_database.cpp | 70 +++---------------- .../include/graphene/chain/fork_database.hpp | 3 - 2 files changed, 11 insertions(+), 62 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index b94a2cdc29..a9b4a95fad 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -52,7 +52,7 @@ void fork_database::start_block(signed_block b) } /** - * Pushes the block into the fork database and caches it if it doesn't link + * Pushes the block into the fork database * */ shared_ptr fork_database::push_block(const signed_block& b) @@ -66,7 +66,6 @@ shared_ptr fork_database::push_block(const signed_block& b) wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) ); wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) ); throw; - _unlinked_index.insert( item ); } return _head; } @@ -94,35 +93,10 @@ void fork_database::_push_block(const item_ptr& item) { _head = item; uint32_t min_num = _head->num - std::min( _max_size, _head->num ); -// ilog( "min block in fork DB ${n}, max_size: ${m}", ("n",min_num)("m",_max_size) ); auto& num_idx = _index.get(); while( num_idx.size() && (*num_idx.begin())->num < min_num ) num_idx.erase( num_idx.begin() ); - - _unlinked_index.get().erase(_head->num - _max_size); } - //_push_next( item ); -} - -/** - * Iterate through the unlinked cache and insert anything that - * links to the newly inserted item. This will start a recursive - * set of calls performing a depth-first insertion of pending blocks as - * _push_next(..) calls _push_block(...) which will in turn call _push_next - */ -void fork_database::_push_next( const item_ptr& new_item ) -{ - auto& prev_idx = _unlinked_index.get(); - - auto itr = prev_idx.find( new_item->id ); - while( itr != prev_idx.end() ) - { - auto tmp = *itr; - prev_idx.erase( itr ); - _push_block( tmp ); - - itr = prev_idx.find( new_item->id ); - } } void fork_database::set_max_size( uint32_t s ) @@ -130,29 +104,15 @@ void fork_database::set_max_size( uint32_t s ) _max_size = s; if( !_head ) return; - { /// index - auto& by_num_idx = _index.get(); - auto itr = by_num_idx.begin(); - while( itr != by_num_idx.end() ) - { - if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) - by_num_idx.erase(itr); - else - break; - itr = by_num_idx.begin(); - } - } - { /// unlinked_index - auto& by_num_idx = _unlinked_index.get(); - auto itr = by_num_idx.begin(); - while( itr != by_num_idx.end() ) - { - if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) - by_num_idx.erase(itr); - else - break; - itr = by_num_idx.begin(); - } + auto& by_num_idx = _index.get(); + auto itr = by_num_idx.begin(); + while( itr != by_num_idx.end() ) + { + if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) + by_num_idx.erase(itr); + else + break; + itr = by_num_idx.begin(); } } @@ -160,11 +120,7 @@ bool fork_database::is_known_block(const block_id_type& id)const { auto& index = _index.get(); auto itr = index.find(id); - if( itr != index.end() ) - return true; - auto& unlinked_index = _unlinked_index.get(); - auto unlinked_itr = unlinked_index.find(id); - return unlinked_itr != unlinked_index.end(); + return itr != index.end(); } item_ptr fork_database::fetch_block(const block_id_type& id)const @@ -173,10 +129,6 @@ item_ptr fork_database::fetch_block(const block_id_type& id)const auto itr = index.find(id); if( itr != index.end() ) return *itr; - auto& unlinked_index = _unlinked_index.get(); - auto unlinked_itr = unlinked_index.find(id); - if( unlinked_itr != unlinked_index.end() ) - return *unlinked_itr; return item_ptr(); } diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index be3991ed80..363a21f2ce 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -93,12 +93,10 @@ namespace graphene { namespace chain { struct block_id; struct block_num; - struct by_previous; typedef multi_index_container< item_ptr, indexed_by< hashed_unique, member, std::hash>, - hashed_non_unique, const_mem_fun, std::hash>, ordered_non_unique, member> > > fork_multi_index_type; @@ -112,7 +110,6 @@ namespace graphene { namespace chain { uint32_t _max_size = 1024; - fork_multi_index_type _unlinked_index; fork_multi_index_type _index; shared_ptr _head; }; From 684998432f41ed1dc4fb03df67fc9103a7800bf5 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Mon, 8 Oct 2018 21:02:18 +0200 Subject: [PATCH 060/132] Fixed fork_db handling in pop_block --- libraries/chain/db_block.cpp | 18 ++++++++++-------- libraries/chain/fork_database.cpp | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..d6d083a967 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -465,15 +465,17 @@ signed_block database::_generate_block( void database::pop_block() { try { _pending_tx_session.reset(); - auto head_id = head_block_id(); - optional head_block = fetch_block_by_id( head_id ); - GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); - - _fork_db.pop_block(); + auto fork_db_head = _fork_db.head(); + FC_ASSERT( fork_db_head, "Trying to pop() from empty fork database!?" ); + if( fork_db_head->id == head_block_id() ) + _fork_db.pop_block(); + else + { + fork_db_head = _fork_db.fetch_block( head_block_id() ); + FC_ASSERT( fork_db_head, "Trying to pop() block that's not in fork database!?" ); + } pop_undo(); - - _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); - + _popped_tx.insert( _popped_tx.begin(), fork_db_head->data.transactions.begin(), fork_db_head->data.transactions.end() ); } FC_CAPTURE_AND_RETHROW() } void database::clear_pending() diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index a9b4a95fad..71da27629b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -41,7 +41,7 @@ void fork_database::pop_block() FC_ASSERT( _head, "no block to pop" ); auto prev = _head->prev.lock(); FC_ASSERT( prev, "popping block would leave head block null" ); - _head = prev; + _head = prev; } void fork_database::start_block(signed_block b) From 73d8b08504f53f71b80b4e4b676d4fa3459f6406 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 7 Oct 2018 14:31:50 +0200 Subject: [PATCH 061/132] Added --revalidate-blockchain --- libraries/app/application.cpp | 10 +++++++--- libraries/chain/db_management.cpp | 2 ++ libraries/chain/include/graphene/chain/database.hpp | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index b77da4b711..d1dd2d903e 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -394,9 +394,12 @@ void application_impl::startup() _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as() ); } - if( _options->count("replay-blockchain") ) + if( _options->count("replay-blockchain") || _options->count("revalidate-blockchain") ) _chain_db->wipe( _data_dir / "blockchain", false ); + if( _options->count("revalidate-blockchain") ) + _chain_db->_replay_with_validation = true; + try { _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); @@ -961,9 +964,10 @@ void application::set_program_options(boost::program_options::options_descriptio "Path to create a Genesis State at. If a well-formed JSON file exists at the path, it will be parsed and any " "missing fields in a Genesis State will be added, and any unknown fields will be removed. If no file or an " "invalid file is found, it will be replaced with an example Genesis State.") - ("replay-blockchain", "Rebuild object graph by replaying all blocks") + ("replay-blockchain", "Rebuild object graph by replaying all blocks without validatino") + ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation") ("resync-blockchain", "Delete all blocks and re-sync with network from scratch") - ("force-validate", "Force validation of all transactions") + ("force-validate", "Force validation of all transactions during normal operation") ("genesis-timestamp", bpo::value(), "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)") ; diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 6f627adb55..13dca0fdb7 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -82,6 +82,8 @@ void database::reindex( fc::path data_dir ) skip_tapos_check | skip_witness_schedule_check | skip_authority_check; + if( _replay_with_validation ) + skip = 0; size_t total_processed_block_size; size_t total_block_size = _block_id_to_block.total_block_size(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..c73a0756a9 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -437,6 +437,10 @@ namespace graphene { namespace chain { void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing ); processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op ); + + /** Set to true before open() to force full revalidation during replay */ + bool _replay_with_validation = false; + private: void _apply_block( const signed_block& next_block ); processed_transaction _apply_transaction( const signed_transaction& trx ); From 34baaed45a4c3535fd5e23b7aaa8bbf3ada3ef0c Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 17:56:26 +0200 Subject: [PATCH 062/132] Use externally provided skip flags for replay --- libraries/app/application.cpp | 19 +++++++++++++++---- libraries/chain/db_management.cpp | 11 +---------- .../chain/include/graphene/chain/database.hpp | 3 --- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index d1dd2d903e..0c27120efd 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -397,12 +398,22 @@ void application_impl::startup() if( _options->count("replay-blockchain") || _options->count("revalidate-blockchain") ) _chain_db->wipe( _data_dir / "blockchain", false ); - if( _options->count("revalidate-blockchain") ) - _chain_db->_replay_with_validation = true; - try { - _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); + // these flags are used in open() only, i. e. during replay + uint32_t skip = graphene::chain::database::skip_witness_signature | + graphene::chain::database::skip_block_size_check | + graphene::chain::database::skip_merkle_check | + graphene::chain::database::skip_transaction_signatures | + graphene::chain::database::skip_transaction_dupe_check | + graphene::chain::database::skip_tapos_check | + graphene::chain::database::skip_witness_schedule_check | + graphene::chain::database::skip_authority_check; + if( _options->count("revalidate-blockchain") ) // see also handle_block() + skip = _options->count("force-validate") ? 0 : graphene::chain::database::skip_transaction_signatures; + graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () { + _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); + }); } catch( const fc::exception& e ) { diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 13dca0fdb7..a86978dc4b 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -74,16 +74,7 @@ void database::reindex( fc::path data_dir ) else _undo_db.disable(); - uint32_t skip = skip_witness_signature | - skip_block_size_check | - skip_merkle_check | - skip_transaction_signatures | - skip_transaction_dupe_check | - skip_tapos_check | - skip_witness_schedule_check | - skip_authority_check; - if( _replay_with_validation ) - skip = 0; + uint32_t skip = node_properties().skip_flags; size_t total_processed_block_size; size_t total_block_size = _block_id_to_block.total_block_size(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c73a0756a9..4fdc984ef0 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -438,9 +438,6 @@ namespace graphene { namespace chain { processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op ); - /** Set to true before open() to force full revalidation during replay */ - bool _replay_with_validation = false; - private: void _apply_block( const signed_block& next_block ); processed_transaction _apply_transaction( const signed_transaction& trx ); From bd8ed044a53c69c2b3326c683b8681806f035059 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 21:18:34 +0200 Subject: [PATCH 063/132] Removed skip_authority flag because its used interchangably with skip_transaction_signatures --- libraries/app/application.cpp | 3 +-- libraries/chain/db_block.cpp | 2 +- libraries/chain/db_init.cpp | 2 +- .../chain/include/graphene/chain/database.hpp | 2 +- tests/tests/authority_tests.cpp | 8 ++++---- tests/tests/bitasset_tests.cpp | 2 -- tests/tests/block_tests.cpp | 19 ++++++++----------- tests/tests/fee_tests.cpp | 5 ----- tests/tests/operation_tests.cpp | 6 +++--- tests/tests/operation_tests2.cpp | 6 +----- tests/tests/uia_tests.cpp | 4 ++-- 11 files changed, 22 insertions(+), 37 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 0c27120efd..eead708ba2 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -407,8 +407,7 @@ void application_impl::startup() graphene::chain::database::skip_transaction_signatures | graphene::chain::database::skip_transaction_dupe_check | graphene::chain::database::skip_tapos_check | - graphene::chain::database::skip_witness_schedule_check | - graphene::chain::database::skip_authority_check; + graphene::chain::database::skip_witness_schedule_check; if( _options->count("revalidate-blockchain") ) // see also handle_block() skip = _options->count("force-validate") ? 0 : graphene::chain::database::skip_transaction_signatures; graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () { diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index d6d083a967..8c2395ef20 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -638,7 +638,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx const chain_parameters& chain_parameters = get_global_properties().parameters; eval_state._trx = &trx; - if( !(skip & (skip_transaction_signatures | skip_authority_check) ) ) + if( !(skip & skip_transaction_signatures) ) { auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 34d16f4b82..4ca2231571 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -235,7 +235,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) _undo_db.disable(); struct auth_inhibitor { auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) - { db.node_properties().skip_flags |= skip_authority_check; } + { db.node_properties().skip_flags |= skip_transaction_signatures; } ~auth_inhibitor() { db.node_properties().skip_flags = old_flags; } private: diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 4fdc984ef0..3b50f1fdb1 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -69,7 +69,7 @@ namespace graphene { namespace chain { skip_fork_db = 1 << 3, ///< used while reindexing skip_block_size_check = 1 << 4, ///< used when applying locally generated transactions skip_tapos_check = 1 << 5, ///< used while reindexing -- note this skips expiration check as well - skip_authority_check = 1 << 6, ///< used while reindexing -- disables any checking of authority on transactions + // skip_authority_check = 1 << 6, ///< removed because effectively identical to skip_transaction_signatures skip_merkle_check = 1 << 7, ///< used while reindexing skip_assert_evaluation = 1 << 8, ///< used while reindexing skip_undo_history_check = 1 << 9, ///< used while reindexing diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 813cff42d4..0e160c2fd6 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1169,7 +1169,7 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) op.owner = auth; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( @@ -1283,7 +1283,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) op.owner = auth; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( @@ -1370,7 +1370,7 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) op.owner = owner; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto set_auth = [&]( @@ -1462,7 +1462,7 @@ BOOST_FIXTURE_TEST_CASE( missing_owner_auth_test, database_fixture ) op.owner = owner; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 9e9a2814ed..4fc6097650 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -448,7 +448,6 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_blocks(HARDFORK_615_TIME, true, skip); // get around Graphene issue #615 feed expiration bug @@ -918,7 +917,6 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_blocks( HARDFORK_615_TIME, true, skip ); // get around Graphene issue #615 feed expiration bug diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index e71642726b..14e0295ca6 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -687,7 +687,7 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions ) db2.open(dir2.path(), make_genesis, "TEST"); BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() ); - auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check; + auto skip_sigs = database::skip_transaction_signatures; auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); public_key_type init_account_pub_key = init_account_priv_key.get_public_key(); @@ -766,11 +766,11 @@ BOOST_AUTO_TEST_CASE( tapos ) trx.operations.push_back(t); trx.sign( init_account_priv_key, db1.get_chain_id() ); //relative_expiration is 1, but ref block is 2 blocks old, so this should fail. - GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures ), fc::exception); set_expiration( db1, trx ); trx.clear_signatures(); trx.sign( init_account_priv_key, db1.get_chain_id() ); - db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check); + PUSH_TX( db1, trx, database::skip_transaction_signatures ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; @@ -1056,7 +1056,6 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) uint32_t skip_flags = ( database::skip_witness_signature | database::skip_transaction_signatures - | database::skip_authority_check ); const asset_object& core = asset_id_type()(db); @@ -1231,8 +1230,8 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) }; // tx's created by ACTORS() have bogus authority, so we need to - // skip_authority_check in the block where they're included - signed_block b1 = generate_block(db, database::skip_authority_check); + // skip_transaction_signatures in the block where they're included + signed_block b1 = generate_block(db, database::skip_transaction_signatures); fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); @@ -1244,7 +1243,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) { optional< signed_block > b = db.fetch_block_by_number( db2.head_block_num()+1 ); db2.push_block(*b, database::skip_witness_signature - |database::skip_authority_check ); + |database::skip_transaction_signatures ); } BOOST_CHECK( db2.get( alice_id ).name == "alice" ); BOOST_CHECK( db2.get( bob_id ).name == "bob" ); @@ -1253,7 +1252,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) transfer( account_id_type(), alice_id, asset( 1000 ) ); transfer( account_id_type(), bob_id, asset( 1000 ) ); // need to skip authority check here as well for same reason as above - db2.push_block(generate_block(db, database::skip_authority_check), database::skip_authority_check); + db2.push_block(generate_block(db, database::skip_transaction_signatures), database::skip_transaction_signatures); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000); @@ -1478,7 +1477,6 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) database::skip_transaction_dupe_check | database::skip_witness_signature | database::skip_transaction_signatures - | database::skip_authority_check ; // Sam is the creator of accounts @@ -1606,8 +1604,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) processed_transaction ptx_create = db.push_transaction( trx, database::skip_transaction_dupe_check | - database::skip_transaction_signatures | - database::skip_authority_check + database::skip_transaction_signatures ); account_id_type alice_account_id = ptx_create.operation_results[0] diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 503c9ebc6e..587814815c 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -759,7 +759,6 @@ BOOST_AUTO_TEST_CASE( fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -897,7 +896,6 @@ BOOST_AUTO_TEST_CASE( non_core_fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1284,7 +1282,6 @@ BOOST_AUTO_TEST_CASE( hf445_fee_refund_cross_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1791,7 +1788,6 @@ BOOST_AUTO_TEST_CASE( bsip26_fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -2349,7 +2345,6 @@ BOOST_AUTO_TEST_CASE( bsip26_fee_refund_cross_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 5e4168562d..b00745ff06 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1940,7 +1940,7 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; auto _issue_uia = [&]( const account_object& recipient, asset amount ) @@ -1952,7 +1952,7 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; int64_t init_balance = 10000; @@ -2043,7 +2043,7 @@ BOOST_AUTO_TEST_CASE( cover_with_collateral_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; // margin call requirement: 1.75x diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 5a876d9418..e0d027da1c 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -565,7 +565,6 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_whitelist_asset_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1052,7 +1051,6 @@ BOOST_AUTO_TEST_CASE( witness_create ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_block(skip); @@ -1238,7 +1236,6 @@ BOOST_AUTO_TEST_CASE( global_settle_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1616,7 +1613,6 @@ BOOST_AUTO_TEST_CASE( force_settle_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -2058,7 +2054,7 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } enable_fees(); upgrade_to_lifetime_member(alice_id); diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 7b44ef4c04..56b2116a44 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -377,7 +377,7 @@ BOOST_AUTO_TEST_CASE( transfer_restricted_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; const asset_object& uia = create_user_issued_asset( "TXRX", sam, transfer_restricted ); @@ -396,7 +396,7 @@ BOOST_AUTO_TEST_CASE( transfer_restricted_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; BOOST_TEST_MESSAGE( "Enable transfer_restricted, send fails" ); From 56dfa9c35abf85f97caf4d71b80a336c69ff6c20 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 21:20:47 +0200 Subject: [PATCH 064/132] Removed unused skip_validate flag introduced in #dca5c95 2016-01-04 --- libraries/chain/db_block.cpp | 3 +-- libraries/chain/include/graphene/chain/database.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 8c2395ef20..9b85dad148 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -623,8 +623,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx { try { uint32_t skip = get_node_properties().skip_flags; - if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */ - trx.validate(); + trx.validate(); auto& trx_idx = get_mutable_index_type(); const chain_id_type& chain_id = get_chain_id(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 3b50f1fdb1..c7d35cc13f 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -73,8 +73,7 @@ namespace graphene { namespace chain { skip_merkle_check = 1 << 7, ///< used while reindexing skip_assert_evaluation = 1 << 8, ///< used while reindexing skip_undo_history_check = 1 << 9, ///< used while reindexing - skip_witness_schedule_check = 1 << 10, ///< used while reindexing - skip_validate = 1 << 11 ///< used prior to checkpoint, skips validate() call on transaction + skip_witness_schedule_check = 1 << 10 ///< used while reindexing }; /** From 1a78e1b2be2982b5138df6b18965a79c747dd10b Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 22:07:43 +0200 Subject: [PATCH 065/132] Add warning when revalidating with checkpoints --- libraries/app/application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index eead708ba2..057e6368de 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -409,7 +409,11 @@ void application_impl::startup() graphene::chain::database::skip_tapos_check | graphene::chain::database::skip_witness_schedule_check; if( _options->count("revalidate-blockchain") ) // see also handle_block() + { + if( !loaded_checkpoints.empty() ) + wlog( "Warning - revalidate will not validate before last checkpoint" ); skip = _options->count("force-validate") ? 0 : graphene::chain::database::skip_transaction_signatures; + } graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () { _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); }); From cf2d6e7b29977e29988f01e6dfcd49fd412a22d1 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 12 Oct 2018 18:00:43 +0200 Subject: [PATCH 066/132] Make distinction between skip_flag cases clearer --- libraries/app/application.cpp | 22 ++++++++++++------- .../graphene/debug_witness/debug_witness.hpp | 2 +- .../include/graphene/witness/witness.hpp | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 057e6368de..3858c1871c 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -401,19 +401,25 @@ void application_impl::startup() try { // these flags are used in open() only, i. e. during replay - uint32_t skip = graphene::chain::database::skip_witness_signature | - graphene::chain::database::skip_block_size_check | - graphene::chain::database::skip_merkle_check | - graphene::chain::database::skip_transaction_signatures | - graphene::chain::database::skip_transaction_dupe_check | - graphene::chain::database::skip_tapos_check | - graphene::chain::database::skip_witness_schedule_check; + uint32_t skip; if( _options->count("revalidate-blockchain") ) // see also handle_block() { if( !loaded_checkpoints.empty() ) wlog( "Warning - revalidate will not validate before last checkpoint" ); - skip = _options->count("force-validate") ? 0 : graphene::chain::database::skip_transaction_signatures; + if( _options->count("force-validate") ) + skip = graphene::chain::database::skip_nothing; + else + skip = graphene::chain::database::skip_transaction_signatures; } + else // no revalidate, skip most checks + skip = graphene::chain::database::skip_witness_signature | + graphene::chain::database::skip_block_size_check | + graphene::chain::database::skip_merkle_check | + graphene::chain::database::skip_transaction_signatures | + graphene::chain::database::skip_transaction_dupe_check | + graphene::chain::database::skip_tapos_check | + graphene::chain::database::skip_witness_schedule_check; + graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () { _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); }); diff --git a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp index 907d26ae9a..22c71236b5 100644 --- a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp +++ b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp @@ -58,7 +58,7 @@ class debug_witness_plugin : public graphene::app::plugin { boost::program_options::variables_map _options; - std::map _private_keys; + std::map _private_keys; std::shared_ptr< std::ofstream > _json_object_stream; boost::signals2::scoped_connection _applied_block_conn; diff --git a/libraries/plugins/witness/include/graphene/witness/witness.hpp b/libraries/plugins/witness/include/graphene/witness/witness.hpp index 9292b55e9f..0d2eab27ac 100644 --- a/libraries/plugins/witness/include/graphene/witness/witness.hpp +++ b/libraries/plugins/witness/include/graphene/witness/witness.hpp @@ -81,7 +81,7 @@ class witness_plugin : public graphene::app::plugin { uint32_t _required_witness_participation = 33 * GRAPHENE_1_PERCENT; uint32_t _production_skip_flags = graphene::chain::database::skip_nothing; - std::map _private_keys; + std::map _private_keys; std::set _witnesses; fc::future _block_production_task; From d644f200af36b33a3aa95acc4211acb2acc4861b Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 26 Oct 2018 15:56:13 +0200 Subject: [PATCH 067/132] Prove that irrelevant signature detection depends on sorting order --- tests/tests/authority_tests.cpp | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 0e160c2fd6..310a992060 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1677,4 +1677,42 @@ BOOST_AUTO_TEST_CASE( issue_214 ) BOOST_CHECK_EQUAL( top.amount.amount.value, get_balance( bob_id, top.amount.asset_id ) ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( irrelevant_signatures ) +{ try { + ACTORS( (alice)(bob) ); + fund( alice ); + + // PK: BTS4vsFgTXJcGQMKCFayF2hrNRfYcKjNZ6Mzk8aw9M4zuWfscPhzE, A: BTSGfxPKKLj6tdTUB7i3mHsd2m7QvPLPy2YA + const fc::ecc::private_key test2 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( "test-2" ) ) ); + const public_key_type test2_pub( test2.get_public_key() ); + + // PK: BTS7FXC7S9UH7HEH8QiuJ8Xv1NRJJZd1GomALLm9ffjtH95Tb2ZQB, A: BTSBajRqmdrXqmDpZhJ8sgkGagdeXneHFVeM + const fc::ecc::private_key test3 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( "test-3" ) ) ); + const public_key_type test3_pub( test3.get_public_key() ); + + BOOST_REQUIRE( test2_pub.key_data < test3_pub.key_data ); + BOOST_REQUIRE( address( test3_pub ) < address( test2_pub ) ); + + account_update_operation auo; + auo.account = alice_id; + auo.active = authority( 2, test2_pub, 2, test3_pub, 1 ); + + trx.clear(); + set_expiration( db, trx ); + trx.operations.push_back( auo ); + sign( trx, alice_private_key ); + PUSH_TX( db, trx ); + trx.clear(); + + transfer_operation to; + to.amount = asset( 1 ); + to.from = alice_id; + to.to = bob_id; + trx.operations.push_back( to ); + sign( trx, test2 ); + sign( trx, test3 ); + PUSH_TX( db, trx ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From 0c4c13364bb6a29fd06a254c9f9e9d1071412f07 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 25 Oct 2018 17:43:49 +0200 Subject: [PATCH 068/132] Fixed typo --- libraries/app/application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 3858c1871c..882f8f7a8f 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -984,7 +984,7 @@ void application::set_program_options(boost::program_options::options_descriptio "Path to create a Genesis State at. If a well-formed JSON file exists at the path, it will be parsed and any " "missing fields in a Genesis State will be added, and any unknown fields will be removed. If no file or an " "invalid file is found, it will be replaced with an example Genesis State.") - ("replay-blockchain", "Rebuild object graph by replaying all blocks without validatino") + ("replay-blockchain", "Rebuild object graph by replaying all blocks without validation") ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation") ("resync-blockchain", "Delete all blocks and re-sync with network from scratch") ("force-validate", "Force validation of all transactions during normal operation") From e5ba271c264c2c4173e12f37db9d0def1e068ab8 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 27 Sep 2018 20:24:30 +0200 Subject: [PATCH 069/132] Parallelize loading/saving object_database --- libraries/db/object_database.cpp | 17 +++++++++++++++-- libraries/fc | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index fdde0fed6b..5b026c08cf 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -25,6 +25,7 @@ #include #include +#include #include namespace graphene { namespace db { @@ -72,14 +73,20 @@ void object_database::flush() { // ilog("Save object_database in ${d}", ("d", _data_dir)); fc::create_directories( _data_dir / "object_database.tmp" / "lock" ); + std::vector> tasks; + tasks.reserve(200); for( uint32_t space = 0; space < _index.size(); ++space ) { fc::create_directories( _data_dir / "object_database.tmp" / fc::to_string(space) ); const auto types = _index[space].size(); for( uint32_t type = 0; type < types; ++type ) if( _index[space][type] ) - _index[space][type]->save( _data_dir / "object_database.tmp" / fc::to_string(space)/fc::to_string(type) ); + tasks.push_back( fc::do_parallel( [this,space,type] () { + _index[space][type]->save( _data_dir / "object_database.tmp" / fc::to_string(space)/fc::to_string(type) ); + } ) ); } + for( auto& task : tasks ) + task.wait(); fc::remove_all( _data_dir / "object_database.tmp" / "lock" ); if( fc::exists( _data_dir / "object_database" ) ) fc::rename( _data_dir / "object_database", _data_dir / "object_database.old" ); @@ -103,11 +110,17 @@ void object_database::open(const fc::path& data_dir) wlog("Ignoring locked object_database"); return; } + std::vector> tasks; + tasks.reserve(200); ilog("Opening object database from ${d} ...", ("d", data_dir)); for( uint32_t space = 0; space < _index.size(); ++space ) for( uint32_t type = 0; type < _index[space].size(); ++type ) if( _index[space][type] ) - _index[space][type]->open( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + tasks.push_back( fc::do_parallel( [this,space,type] () { + _index[space][type]->open( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + } ) ); + for( auto& task : tasks ) + task.wait(); ilog( "Done opening object database." ); } FC_CAPTURE_AND_RETHROW( (data_dir) ) } diff --git a/libraries/fc b/libraries/fc index 9cce60c917..0468884ea6 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 9cce60c91773ad99cfdfa42c5e86ba6ceb3f3ee9 +Subproject commit 0468884ea675afe3a16ffc61371672fecf6e7dde From 033ddea8cdab350cbfed6f87de86dfb59dd4d959 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 4 Oct 2018 16:43:26 +0200 Subject: [PATCH 070/132] Introduce precomputable_transaction, and clearable_block in tests --- .../graphene/chain/protocol/transaction.hpp | 47 ++++++++++++++----- libraries/chain/protocol/transaction.cpp | 46 ++++++++++++------ tests/common/database_fixture.cpp | 7 +++ tests/common/database_fixture.hpp | 5 ++ tests/tests/authority_tests.cpp | 1 - tests/tests/basic_tests.cpp | 11 ++++- tests/tests/block_tests.cpp | 4 +- 7 files changed, 91 insertions(+), 30 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 95c3996135..2f67d5dd60 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -85,9 +85,9 @@ namespace graphene { namespace chain { extensions_type extensions; /// Calculate the digest for a transaction - digest_type digest()const; - transaction_id_type id()const; - void validate() const; + digest_type digest()const; + virtual const transaction_id_type& id()const; + virtual void validate() const; /// Calculate the digest used for signature validation digest_type sig_digest( const chain_id_type& chain_id )const; @@ -113,6 +113,10 @@ namespace graphene { namespace chain { } void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; + + protected: + digest_type sig_digest( const chain_id_type& chain_id )const; + mutable transaction_id_type _tx_id_buffer; }; /** @@ -176,7 +180,7 @@ namespace graphene { namespace chain { * otherwise, the @ref chain_id parameter will be ignored, and * @ref signees will be returned directly. */ - const flat_set& get_signature_keys( const chain_id_type& chain_id )const; + virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const; /** Signatures */ vector signatures; @@ -184,11 +188,31 @@ namespace graphene { namespace chain { /** Public keys extracted from signatures */ mutable flat_set signees; - /// Removes all operations, signatures and signees - void clear() { operations.clear(); signatures.clear(); signees.clear(); } + /// Removes all operations and signatures + void clear() { operations.clear(); signatures.clear(); } + + /// Removes all signatures + void clear_signatures() { signatures.clear(); } + protected: + /** Public keys extracted from signatures */ + mutable flat_set _signees; + }; + + /** This represents a signed transaction that will never have its operations, + * signatures etc. modified again, after initial creation. It is therefore + * safe to cache results from various calls. + */ + class precomputable_transaction : public signed_transaction { + public: + precomputable_transaction() {} + precomputable_transaction( const signed_transaction& tx ) : signed_transaction(tx) {}; + precomputable_transaction( signed_transaction&& tx ) : signed_transaction( std::move(tx) ) {}; - /// Removes all signatures and signees - void clear_signatures() { signatures.clear(); signees.clear(); } + virtual const transaction_id_type& id()const; + virtual void validate() const; + virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const; + protected: + mutable bool _validated = false; }; void verify_authority( const vector& ops, const flat_set& sigs, @@ -212,10 +236,10 @@ namespace graphene { namespace chain { * If an operation did not create any new object IDs then 0 * should be returned. */ - struct processed_transaction : public signed_transaction + struct processed_transaction : public precomputable_transaction { processed_transaction( const signed_transaction& trx = signed_transaction() ) - : signed_transaction(trx){} + : precomputable_transaction(trx){} vector operation_results; @@ -229,4 +253,5 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expiration)(operations)(extensions) ) // Note: not reflecting signees field for backward compatibility; in addition, it should not be in p2p messages FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) ) -FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) ) +FC_REFLECT_DERIVED( graphene::chain::precomputable_transaction, (graphene::chain::signed_transaction), ) +FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::precomputable_transaction), (operation_results) ) diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 4bb7ce8378..04e3cb4914 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -60,19 +60,17 @@ void transaction::validate() const operation_validate(op); } -graphene::chain::transaction_id_type graphene::chain::transaction::id() const +const graphene::chain::transaction_id_type& graphene::chain::transaction::id() const { auto h = digest(); - transaction_id_type result; - memcpy(result._hash, h._hash, std::min(sizeof(result), sizeof(h))); - return result; + memcpy(_tx_id_buffer._hash, h._hash, std::min(sizeof(_tx_id_buffer), sizeof(h))); + return _tx_id_buffer; } const signature_type& graphene::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) { digest_type h = sig_digest( chain_id ); signatures.push_back(key.sign_compact(h)); - signees.clear(); // Clear signees since it may be inconsistent after added a new signature return signatures.back(); } @@ -307,20 +305,17 @@ const flat_set& signed_transaction::get_signature_keys( const c { try { // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. // However, we don't pass in another chain ID so far, for better performance, we skip the check. - if( signees.empty() && !signatures.empty() ) + auto d = sig_digest( chain_id ); + flat_set result; + for( const auto& sig : signatures ) { - auto d = sig_digest( chain_id ); - flat_set result; - for( const auto& sig : signatures ) - { - GRAPHENE_ASSERT( - result.insert( fc::ecc::public_key(sig,d) ).second, + GRAPHENE_ASSERT( + result.insert( fc::ecc::public_key(sig,d) ).second, tx_duplicate_sig, "Duplicate Signature detected" ); - } - signees = std::move( result ); } - return signees; + _signees = std::move( result ); + return _signees; } FC_CAPTURE_AND_RETHROW() } @@ -386,6 +381,27 @@ set signed_transaction::minimize_required_signatures( return set( result.begin(), result.end() ); } +const transaction_id_type& precomputable_transaction::id()const +{ + if( !_tx_id_buffer._hash[0] ) + transaction::id(); + return _tx_id_buffer; +} + +void precomputable_transaction::validate() const +{ + if( _validated ) return; + transaction::validate(); + _validated = true; +} + +const flat_set& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const +{ + if( _signees.empty() ) + signed_transaction::get_signature_keys( chain_id ); + return _signees; +} + void signed_transaction::verify_authority( const chain_id_type& chain_id, const std::function& get_active, diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 534c03de33..2e05f71017 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -55,6 +55,13 @@ namespace graphene { namespace chain { using std::cout; using std::cerr; +void clearable_block::clear() +{ + _calculated_merkle_root = checksum_type(); + _signee = fc::ecc::public_key(); + _block_id = block_id_type(); +} + database_fixture::database_fixture() : app(), db( *app.chain_database() ) { diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 78f7b65d19..72744ff7e5 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -170,6 +170,11 @@ extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; namespace graphene { namespace chain { +class clearable_block : public signed_block { +public: + void clear(); +}; + struct database_fixture { // the reason we use an app is to exercise the indexes of built-in // plugins diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 310a992060..573d609edf 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1057,7 +1057,6 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) PUSH_TX( db, trx, skip ); trx.operations.push_back( xfer_op ); - trx.signees.clear(); // signees should be invalidated BOOST_TEST_MESSAGE( "Invalidating Alices Signature" ); // Alice's signature is now invalid GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index 2157dde75a..97d03ed41b 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -408,7 +408,7 @@ BOOST_AUTO_TEST_CASE( scaled_precision ) BOOST_AUTO_TEST_CASE( merkle_root ) { - signed_block block; + clearable_block block; vector tx; vector t; const uint32_t num_tx = 10; @@ -444,6 +444,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dA = d(t[0], t[1]); block.transactions.push_back( tx[1] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dA) ); /* @@ -458,6 +459,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dI = d(dA, dB); block.transactions.push_back( tx[2] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dI) ); /* @@ -472,6 +474,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dI = d(dA, dB); block.transactions.push_back( tx[3] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dI) ); /* @@ -489,6 +492,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[4] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -506,6 +510,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[5] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -523,6 +528,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[6] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -540,6 +546,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[7] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -560,6 +567,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dO = d(dM, dN); block.transactions.push_back( tx[8] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dO) ); /* @@ -580,6 +588,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dO = d(dM, dN); block.transactions.push_back( tx[9] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dO) ); } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 14e0295ca6..1890dc38b8 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -80,11 +80,12 @@ BOOST_AUTO_TEST_CASE( block_database_test ) FC_ASSERT( !bdb.is_open() ); bdb.open( data_dir.path() ); - signed_block b; + clearable_block b; for( uint32_t i = 0; i < 5; ++i ) { if( i > 0 ) b.previous = b.id(); b.witness = witness_id_type(i+1); + b.clear(); bdb.store( b.id(), b ); auto fetch = bdb.fetch_by_number( b.block_num() ); @@ -976,7 +977,6 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" ); trx.signatures.pop_back(); - trx.signees.clear(); // signees should be invalidated db.push_transaction(trx, 0); } FC_LOG_AND_RETHROW() } From d2d8b29c097071b15ee311bf675512d589651b1b Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 27 Sep 2018 22:09:44 +0200 Subject: [PATCH 071/132] Preprocess blocks + transactions in parallel, working version --- libraries/app/api.cpp | 13 ++- libraries/app/application.cpp | 19 ++-- libraries/app/application_impl.hxx | 4 + libraries/app/include/graphene/app/api.hpp | 6 +- libraries/chain/db_block.cpp | 71 ++++++++++++ libraries/chain/db_management.cpp | 107 +++++++++++------- .../chain/include/graphene/chain/database.hpp | 24 ++++ .../include/graphene/chain/protocol/base.hpp | 3 + .../include/graphene/chain/protocol/block.hpp | 20 +++- .../graphene/chain/protocol/transaction.hpp | 14 +-- libraries/chain/protocol/block.cpp | 64 ++++++----- libraries/chain/protocol/operations.cpp | 6 + libraries/chain/protocol/transaction.cpp | 2 +- .../include/graphene/net/core_messages.hpp | 4 +- .../delayed_node/delayed_node_plugin.cpp | 1 + 15 files changed, 253 insertions(+), 105 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index dbe6043e50..f3cffa70ff 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -159,18 +159,18 @@ namespace graphene { namespace app { } } - void network_broadcast_api::broadcast_transaction(const signed_transaction& trx) + void network_broadcast_api::broadcast_transaction(const precomputable_transaction& trx) { - trx.validate(); + _app.chain_database()->precompute_parallel( trx ).wait(); _app.chain_database()->push_transaction(trx); if( _app.p2p_node() != nullptr ) _app.p2p_node()->broadcast_transaction(trx); } - fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx) + fc::variant network_broadcast_api::broadcast_transaction_synchronous(const precomputable_transaction& trx) { fc::promise::ptr prom( new fc::promise() ); - broadcast_transaction_with_callback( [=]( const fc::variant& v ){ + broadcast_transaction_with_callback( [prom]( const fc::variant& v ){ prom->set_value(v); }, trx ); @@ -179,14 +179,15 @@ namespace graphene { namespace app { void network_broadcast_api::broadcast_block( const signed_block& b ) { + _app.chain_database()->precompute_parallel( b ).wait(); _app.chain_database()->push_block(b); if( _app.p2p_node() != nullptr ) _app.p2p_node()->broadcast( net::block_message( b )); } - void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction& trx) + void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const precomputable_transaction& trx) { - trx.validate(); + _app.chain_database()->precompute_parallel( trx ).wait(); _callbacks[trx.id()] = cb; _app.chain_database()->push_transaction(trx); if( _app.p2p_node() != nullptr ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 882f8f7a8f..cc7828034f 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -540,13 +540,17 @@ bool application_impl::handle_block(const graphene::net::block_message& blk_msg, FC_ASSERT( (latency.count()/1000) > -5000, "Rejecting block with timestamp in the future" ); try { - // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, - // you can help the network code out by throwing a block_older_than_undo_history exception. - // when the net code sees that, it will stop trying to push blocks from that chain, but - // leave that peer connected so that they can get sync blocks from us - bool result = _chain_db->push_block( blk_msg.block, - (_is_block_producer | _force_validate) ? - database::skip_nothing : database::skip_transaction_signatures ); + const uint32_t skip = (_is_block_producer | _force_validate) ? + database::skip_nothing : database::skip_transaction_signatures; + bool result = valve.do_serial( [this,&blk_msg,skip] () { + _chain_db->precompute_parallel( blk_msg.block, skip ).wait(); + }, [this,&blk_msg,skip] () { + // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, + // you can help the network code out by throwing a block_older_than_undo_history exception. + // when the net code sees that, it will stop trying to push blocks from that chain, but + // leave that peer connected so that they can get sync blocks from us + return _chain_db->push_block( blk_msg.block, skip ); + }); // the block was accepted, so we now know all of the transactions contained in the block if (!sync_mode) @@ -596,6 +600,7 @@ void application_impl::handle_transaction(const graphene::net::trx_message& tran trx_count = 0; } + _chain_db->precompute_parallel( transaction_message.trx ).wait(); _chain_db->push_transaction( transaction_message.trx ); } FC_CAPTURE_AND_RETHROW( (transaction_message) ) } diff --git a/libraries/app/application_impl.hxx b/libraries/app/application_impl.hxx index 5b0c543728..9f601bce79 100644 --- a/libraries/app/application_impl.hxx +++ b/libraries/app/application_impl.hxx @@ -1,6 +1,8 @@ #pragma once #include +#include + #include #include #include @@ -194,6 +196,8 @@ class application_impl : public net::node_delegate std::map> _available_plugins; bool _is_finished_syncing = false; + private: + fc::serial_valve valve; }; }}} // namespace graphene namespace app namespace detail diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index aea35be08b..af34bbaf73 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -266,19 +266,19 @@ namespace graphene { namespace app { * The transaction will be checked for validity in the local database prior to broadcasting. If it fails to * apply locally, an error will be thrown and the transaction will not be broadcast. */ - void broadcast_transaction(const signed_transaction& trx); + void broadcast_transaction(const precomputable_transaction& trx); /** this version of broadcast transaction registers a callback method that will be called when the transaction is * included into a block. The callback method includes the transaction id, block number, and transaction number in the * block. */ - void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx); + void broadcast_transaction_with_callback( confirmation_callback cb, const precomputable_transaction& trx); /** this version of broadcast transaction registers a callback method that will be called when the transaction is * included into a block. The callback method includes the transaction id, block number, and transaction number in the * block. */ - fc::variant broadcast_transaction_synchronous(const signed_transaction& trx); + fc::variant broadcast_transaction_synchronous(const precomputable_transaction& trx); /** * @brief Broadcast a signed block to the network diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 9b85dad148..296d88e649 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -37,6 +37,7 @@ #include #include +#include #include namespace graphene { namespace chain { @@ -751,4 +752,74 @@ bool database::before_last_checkpoint()const return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num()); } + +static const uint32_t skip_expensive = database::skip_transaction_signatures | database::skip_witness_signature + | database::skip_merkle_check | database::skip_transaction_dupe_check; + +template +void database::_precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const +{ + for( size_t i = 0; i < count; ++i, ++trx ) + { + trx->validate(); // TODO - parallelize wrt confidential operations + if( !(skip&skip_transaction_dupe_check) ) + trx->id(); + if( !(skip&skip_transaction_signatures) ) + trx->get_signature_keys( get_chain_id() ); + } +} + +void database::_precompute_parallel( const signed_block& block, const uint32_t skip )const +{ try { + const bool cheap = (skip & skip_expensive) == skip_expensive; + std::vector> workers; + if( !block.transactions.empty() ) + { + if( cheap ) + _precompute_parallel( &block.transactions[0], block.transactions.size(), skip ); + else + { + uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads(); + if( !chunks ) return; + uint32_t chunk_size = block.transactions.size() / chunks; + if( chunks * chunk_size < block.transactions.size() ) + chunk_size++; + workers.reserve( chunks + 1 ); + for( size_t base = 0; base < block.transactions.size(); base += chunk_size ) + workers.push_back( fc::do_parallel( [this,&block,base,chunk_size,skip] () { + _precompute_parallel( &block.transactions[base], + base + chunk_size < block.transactions.size() ? chunk_size : block.transactions.size() - base, + skip ); + }) ); + } + } + + if( !(skip&skip_witness_signature) ) + workers.push_back( fc::do_parallel( [&block] () { block.signee(); } ) ); + if( !(skip&skip_merkle_check) ) + block.calculate_merkle_root(); + block.id(); + for( auto& worker : workers ) + worker.wait(); +} FC_LOG_AND_RETHROW() } + +fc::future database::precompute_parallel( const signed_block& block, const uint32_t skip )const +{ + if( block.transactions.empty() || (skip & skip_expensive) == skip_expensive ) + { + _precompute_parallel( block, skip ); + return fc::future< void >( fc::promise< void >::ptr( new fc::promise< void >( true ) ) ); + } + return fc::do_parallel([this,&block,skip] () { + _precompute_parallel( block, skip ); + }); +} + +fc::future database::precompute_parallel( const precomputable_transaction& trx )const +{ + return fc::do_parallel([this,&trx] () { + _precompute_parallel( &trx, 1, skip_nothing ); + }); +} + } } diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index a86978dc4b..9167768f48 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { @@ -76,59 +78,76 @@ void database::reindex( fc::path data_dir ) uint32_t skip = node_properties().skip_flags; - size_t total_processed_block_size; size_t total_block_size = _block_id_to_block.total_block_size(); const auto& gpo = get_global_properties(); - for( uint32_t i = head_block_num() + 1; i <= last_block_num; ++i ) + std::queue< std::tuple< size_t, signed_block, fc::future< void > > > blocks; + uint32_t next_block_num = head_block_num() + 1; + uint32_t i = next_block_num; + while( next_block_num <= last_block_num || !blocks.empty() ) { - if( i % 10000 == 0 ) + if( next_block_num <= last_block_num && blocks.size() < 20 ) { - total_processed_block_size = _block_id_to_block.blocks_current_position(); - - ilog( - " [by size: ${size}% ${processed} of ${total}] [by num: ${num}% ${i} of ${last}]", - ("size", double(total_processed_block_size) / total_block_size * 100) - ("processed", total_processed_block_size) - ("total", total_block_size) - ("num", double(i*100)/last_block_num) - ("i", i) - ("last", last_block_num) - ); - } - if( i == flush_point ) - { - ilog( "Writing database to disk at block ${i}", ("i",i) ); - flush(); - ilog( "Done" ); - } - if( head_block_time() >= last_block->timestamp - gpo.parameters.maximum_time_until_expiration ) - skip &= ~skip_transaction_dupe_check; - fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i); - if( !block.valid() ) - { - wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); - uint32_t dropped_count = 0; - while( true ) + const size_t processed_block_size = _block_id_to_block.blocks_current_position(); + fc::optional< signed_block > block = _block_id_to_block.fetch_by_number( next_block_num++ ); + if( block.valid() ) { - fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); - // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 - if( !last_id.valid() ) - break; - // we've caught up to the gap - if( block_header::num_from_id( *last_id ) <= i ) - break; - _block_id_to_block.remove( *last_id ); - dropped_count++; + if( block->timestamp >= last_block->timestamp - gpo.parameters.maximum_time_until_expiration ) + skip &= ~skip_transaction_dupe_check; + blocks.emplace( processed_block_size, std::move(*block), fc::future() ); + std::get<2>(blocks.back()) = precompute_parallel( std::get<1>(blocks.back()), skip ); + } + else + { + wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); + uint32_t dropped_count = 0; + while( true ) + { + fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); + // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 + if( !last_id.valid() ) + break; + // we've caught up to the gap + if( block_header::num_from_id( *last_id ) <= i ) + break; + _block_id_to_block.remove( *last_id ); + dropped_count++; + } + wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); + next_block_num = last_block_num + 1; // don't load more blocks } - wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); - break; } - if( i < undo_point ) - apply_block( *block, skip ); else { - _undo_db.enable(); - push_block( *block, skip ); + std::get<2>(blocks.front()).wait(); + const signed_block& block = std::get<1>(blocks.front()); + + if( i % 10000 == 0 ) + { + ilog( + " [by size: ${size}% ${processed} of ${total}] [by num: ${num}% ${i} of ${last}]", + ("size", double(std::get<0>(blocks.front())) / total_block_size * 100) + ("processed", std::get<0>(blocks.front())) + ("total", total_block_size) + ("num", double(i*100)/last_block_num) + ("i", i) + ("last", last_block_num) + ); + } + if( i == flush_point ) + { + ilog( "Writing database to disk at block ${i}", ("i",i) ); + flush(); + ilog( "Done" ); + } + if( i < undo_point ) + apply_block( block, skip ); + else + { + _undo_db.enable(); + push_block( block, skip ); + } + blocks.pop(); + i++; } } _undo_db.enable(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c7d35cc13f..db2f38e5de 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -415,6 +415,30 @@ namespace graphene { namespace chain { /// Enable or disable tracking of votes of standby witnesses and committee members inline void enable_standby_votes_tracking(bool enable) { _track_standby_votes = enable; } + /** Precomputes digests, signatures and operation validations depending + * on skip flags. "Expensive" computations may be done in a parallel + * thread. + * + * @param block the block to preprocess + * @param skip indicates which computations can be skipped + * @return a future that will resolve to the input block with + * precomputations applied + */ + fc::future precompute_parallel( const signed_block& block, const uint32_t skip = skip_nothing )const; + + /** Precomputes digests, signatures and operation validations. + * "Expensive" computations may be done in a parallel thread. + * + * @param trx the transaction to preprocess + * @return a future that will resolve to the input transaction with + * precomputations applied + */ + fc::future precompute_parallel( const precomputable_transaction& trx )const; + private: + template + void _precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const; + void _precompute_parallel( const signed_block& block, const uint32_t skip )const; + protected: //Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead void pop_undo() { object_database::pop_undo(); } diff --git a/libraries/chain/include/graphene/chain/protocol/base.hpp b/libraries/chain/include/graphene/chain/protocol/base.hpp index 52240b934a..73209a1861 100644 --- a/libraries/chain/include/graphene/chain/protocol/base.hpp +++ b/libraries/chain/include/graphene/chain/protocol/base.hpp @@ -27,6 +27,8 @@ #include #include +#include + namespace graphene { namespace chain { /** @@ -94,6 +96,7 @@ namespace graphene { namespace chain { void get_required_active_authorities( flat_set& )const{} void get_required_owner_authorities( flat_set& )const{} void validate()const{} + fc::optional< fc::future > validate_parallel( uint32_t skip )const; static uint64_t calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte ); }; diff --git a/libraries/chain/include/graphene/chain/protocol/block.hpp b/libraries/chain/include/graphene/chain/protocol/block.hpp index 98e627c928..aa8c46052f 100644 --- a/libraries/chain/include/graphene/chain/protocol/block.hpp +++ b/libraries/chain/include/graphene/chain/protocol/block.hpp @@ -26,8 +26,9 @@ namespace graphene { namespace chain { - struct block_header + class block_header { + public: digest_type digest()const; block_id_type previous; uint32_t block_num()const { return num_from_id(previous) + 1; } @@ -41,20 +42,27 @@ namespace graphene { namespace chain { static uint32_t num_from_id(const block_id_type& id); }; - struct signed_block_header : public block_header + class signed_block_header : public block_header { - block_id_type id()const; - fc::ecc::public_key signee()const; + public: + const block_id_type& id()const; + const fc::ecc::public_key& signee()const; void sign( const fc::ecc::private_key& signer ); bool validate_signee( const fc::ecc::public_key& expected_signee )const; signature_type witness_signature; + protected: + mutable fc::ecc::public_key _signee; + mutable block_id_type _block_id; }; - struct signed_block : public signed_block_header + class signed_block : public signed_block_header { - checksum_type calculate_merkle_root()const; + public: + const checksum_type& calculate_merkle_root()const; vector transactions; + protected: + mutable checksum_type _calculated_merkle_root; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 2f67d5dd60..02f00799c6 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -62,8 +62,9 @@ namespace graphene { namespace chain { /** * @brief groups operations that should be applied atomically */ - struct transaction + class transaction { + public: /** * Least significant 16 bits from the reference block number. If @ref relative_expiration is zero, this field * must be zero as well. @@ -89,7 +90,6 @@ namespace graphene { namespace chain { virtual const transaction_id_type& id()const; virtual void validate() const; /// Calculate the digest used for signature validation - digest_type sig_digest( const chain_id_type& chain_id )const; void set_expiration( fc::time_point_sec expiration_time ); void set_reference_block( const block_id_type& reference_block ); @@ -122,8 +122,9 @@ namespace graphene { namespace chain { /** * @brief adds a signature to a transaction */ - struct signed_transaction : public transaction + class signed_transaction : public transaction { + public: signed_transaction( const transaction& trx = transaction() ) : transaction(trx){} @@ -185,13 +186,10 @@ namespace graphene { namespace chain { /** Signatures */ vector signatures; - /** Public keys extracted from signatures */ - mutable flat_set signees; - - /// Removes all operations and signatures + /** Removes all operations and signatures */ void clear() { operations.clear(); signatures.clear(); } - /// Removes all signatures + /** Removes all signatures */ void clear_signatures() { signatures.clear(); } protected: /** Public keys extracted from signatures */ diff --git a/libraries/chain/protocol/block.cpp b/libraries/chain/protocol/block.cpp index d32365dd08..9fdf4707eb 100644 --- a/libraries/chain/protocol/block.cpp +++ b/libraries/chain/protocol/block.cpp @@ -37,19 +37,23 @@ namespace graphene { namespace chain { return fc::endian_reverse_u32(id._hash[0]); } - block_id_type signed_block_header::id()const + const block_id_type& signed_block_header::id()const { - auto tmp = fc::sha224::hash( *this ); - tmp._hash[0] = fc::endian_reverse_u32(block_num()); // store the block num in the ID, 160 bits is plenty for the hash - static_assert( sizeof(tmp._hash[0]) == 4, "should be 4 bytes" ); - block_id_type result; - memcpy(result._hash, tmp._hash, std::min(sizeof(result), sizeof(tmp))); - return result; + if( !_block_id._hash[0] ) + { + auto tmp = fc::sha224::hash( *this ); + tmp._hash[0] = fc::endian_reverse_u32(block_num()); // store the block num in the ID, 160 bits is plenty for the hash + static_assert( sizeof(tmp._hash[0]) == 4, "should be 4 bytes" ); + memcpy(_block_id._hash, tmp._hash, std::min(sizeof(_block_id), sizeof(tmp))); + } + return _block_id; } - fc::ecc::public_key signed_block_header::signee()const + const fc::ecc::public_key& signed_block_header::signee()const { - return fc::ecc::public_key( witness_signature, digest(), true/*enforce canonical*/ ); + if( !_signee.valid() ) + _signee = fc::ecc::public_key( witness_signature, digest(), true/*enforce canonical*/ ); + return _signee; } void signed_block_header::sign( const fc::ecc::private_key& signer ) @@ -62,31 +66,35 @@ namespace graphene { namespace chain { return signee() == expected_signee; } - checksum_type signed_block::calculate_merkle_root()const + const checksum_type& signed_block::calculate_merkle_root()const { + static const checksum_type empty_checksum; if( transactions.size() == 0 ) - return checksum_type(); - - vector ids; - ids.resize( transactions.size() ); - for( uint32_t i = 0; i < transactions.size(); ++i ) - ids[i] = transactions[i].merkle_digest(); + return empty_checksum; - vector::size_type current_number_of_hashes = ids.size(); - while( current_number_of_hashes > 1 ) + if( !_calculated_merkle_root._hash[0] ) { - // hash ID's in pairs - uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1); - uint32_t k = 0; + vector ids; + ids.resize( transactions.size() ); + for( uint32_t i = 0; i < transactions.size(); ++i ) + ids[i] = transactions[i].merkle_digest(); - for( uint32_t i = 0; i < i_max; i += 2 ) - ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) ); + vector::size_type current_number_of_hashes = ids.size(); + while( current_number_of_hashes > 1 ) + { + // hash ID's in pairs + uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1); + uint32_t k = 0; - if( current_number_of_hashes&1 ) - ids[k++] = ids[i_max]; - current_number_of_hashes = k; + for( uint32_t i = 0; i < i_max; i += 2 ) + ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) ); + + if( current_number_of_hashes&1 ) + ids[k++] = ids[i_max]; + current_number_of_hashes = k; + } + _calculated_merkle_root = checksum_type::hash( ids[0] ); } - return checksum_type::hash( ids[0] ); + return _calculated_merkle_root; } - } } diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 40a37eba3a..48a65f6fed 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -32,6 +32,12 @@ uint64_t base_operation::calculate_data_fee( uint64_t bytes, uint64_t price_per_ return result.to_uint64(); } +fc::optional< fc::future > base_operation::validate_parallel( uint32_t skip )const +{ + validate(); + return fc::optional< fc::future >(); +} + void balance_claim_operation::validate()const { FC_ASSERT( fee == asset() ); diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 04e3cb4914..c1dd817693 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -60,7 +60,7 @@ void transaction::validate() const operation_validate(op); } -const graphene::chain::transaction_id_type& graphene::chain::transaction::id() const +const transaction_id_type& transaction::id() const { auto h = digest(); memcpy(_tx_id_buffer._hash, h._hash, std::min(sizeof(_tx_id_buffer), sizeof(h))); diff --git a/libraries/net/include/graphene/net/core_messages.hpp b/libraries/net/include/graphene/net/core_messages.hpp index 8af0c3443c..76f74bd253 100644 --- a/libraries/net/include/graphene/net/core_messages.hpp +++ b/libraries/net/include/graphene/net/core_messages.hpp @@ -95,9 +95,9 @@ namespace graphene { namespace net { { static const core_message_type_enum type; - signed_transaction trx; + graphene::chain::precomputable_transaction trx; trx_message() {} - trx_message(signed_transaction transaction) : + trx_message(graphene::chain::signed_transaction transaction) : trx(std::move(transaction)) {} }; diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 24a46cc066..dd1d4705e6 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -105,6 +105,7 @@ void delayed_node_plugin::sync_with_trusted_node() fc::optional block = my->database_api->get_block( db.head_block_num()+1 ); FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); ilog("Pushing block #${n}", ("n", block->block_num())); + db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait(); db.push_block(*block); synced_blocks++; } From 9946bdefd093499684deb77ba01992e261c661da Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 5 Oct 2018 17:53:40 +0200 Subject: [PATCH 072/132] Get rid of possibly uninitialized local variable --- libraries/chain/db_block.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 296d88e649..4873c65ed5 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -628,12 +628,8 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx auto& trx_idx = get_mutable_index_type(); const chain_id_type& chain_id = get_chain_id(); - transaction_id_type trx_id; if( !(skip & skip_transaction_dupe_check) ) - { - trx_id = trx.id(); - FC_ASSERT( trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end() ); - } + FC_ASSERT( trx_idx.indices().get().find(trx.id()) == trx_idx.indices().get().end() ); transaction_evaluation_state eval_state(this); const chain_parameters& chain_parameters = get_global_properties().parameters; eval_state._trx = &trx; @@ -667,8 +663,8 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx //Insert transaction into unique transactions database. if( !(skip & skip_transaction_dupe_check) ) { - create([&trx_id,&trx](transaction_object& transaction) { - transaction.trx_id = trx_id; + create([&trx](transaction_object& transaction) { + transaction.trx_id = trx.id(); transaction.trx = trx; }); } From cb2244f3db24f23770e888657b8cb084da2d99c2 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Mon, 8 Oct 2018 16:36:04 +0200 Subject: [PATCH 073/132] Changed push_transaction to accept precomputable_transaction instead of signed_transaction --- libraries/chain/db_block.cpp | 4 +- .../chain/include/graphene/chain/database.hpp | 6 +- .../chain/include/graphene/chain/db_with.hpp | 14 +---- tests/app/main.cpp | 2 +- tests/common/database_fixture.cpp | 57 ++++++++++--------- tests/tests/authority_tests.cpp | 2 +- tests/tests/block_tests.cpp | 26 ++++----- tests/tests/confidential_tests.cpp | 8 +-- tests/tests/operation_tests.cpp | 6 +- tests/tests/operation_tests2.cpp | 36 ++++++------ 10 files changed, 78 insertions(+), 83 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 4873c65ed5..ada5167ba2 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -228,7 +228,7 @@ bool database::_push_block(const signed_block& new_block) * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending * queues. */ -processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) +processed_transaction database::push_transaction( const precomputable_transaction& trx, uint32_t skip ) { try { processed_transaction result; detail::with_skip_flags( *this, skip, [&]() @@ -238,7 +238,7 @@ processed_transaction database::push_transaction( const signed_transaction& trx, return result; } FC_CAPTURE_AND_RETHROW( (trx) ) } -processed_transaction database::_push_transaction( const signed_transaction& trx ) +processed_transaction database::_push_transaction( const precomputable_transaction& trx ) { // If this is the first transaction pushed after applying a block, start a new undo session. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index db2f38e5de..bf2795ea1f 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -135,9 +135,9 @@ namespace graphene { namespace chain { bool before_last_checkpoint()const; bool push_block( const signed_block& b, uint32_t skip = skip_nothing ); - processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + processed_transaction push_transaction( const precomputable_transaction& trx, uint32_t skip = skip_nothing ); bool _push_block( const signed_block& b ); - processed_transaction _push_transaction( const signed_transaction& trx ); + processed_transaction _push_transaction( const precomputable_transaction& trx ); ///@throws fc::exception if the proposed transaction fails to apply. processed_transaction push_proposal( const proposal_object& proposal ); @@ -406,7 +406,7 @@ namespace graphene { namespace chain { /** when popping a block, the transactions that were removed get cached here so they * can be reapplied at the proper time */ - std::deque< signed_transaction > _popped_tx; + std::deque< precomputable_transaction > _popped_tx; /** * @} diff --git a/libraries/chain/include/graphene/chain/db_with.hpp b/libraries/chain/include/graphene/chain/db_with.hpp index de93bb15f5..7ae189216f 100644 --- a/libraries/chain/include/graphene/chain/db_with.hpp +++ b/libraries/chain/include/graphene/chain/db_with.hpp @@ -80,11 +80,9 @@ struct pending_transactions_restorer { try { if( !_db.is_known_transaction( tx.id() ) ) { - // since push_transaction() takes a signed_transaction, - // the operation_results field will be ignored. _db._push_transaction( tx ); } - } catch ( const fc::exception& ) { + } catch ( const fc::exception& ) { // ignore invalid transactions } } _db._popped_tx.clear(); @@ -93,17 +91,11 @@ struct pending_transactions_restorer try { if( !_db.is_known_transaction( tx.id() ) ) { - // since push_transaction() takes a signed_transaction, - // the operation_results field will be ignored. _db._push_transaction( tx ); } } - catch( const fc::exception& e ) - { - /* - wlog( "Pending transaction became invalid after switching to block ${b} ${t}", ("b", _db.head_block_id())("t",_db.head_block_time()) ); - wlog( "The invalid pending transaction caused exception ${e}", ("e", e.to_detail_string() ) ); - */ + catch( const fc::exception& ) + { // ignore invalid transactions } } } diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 848d60d664..b68cdf78a0 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) BOOST_CHECK_EQUAL( db2->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 0 ); BOOST_TEST_MESSAGE( "Creating transfer tx" ); - graphene::chain::signed_transaction trx; + graphene::chain::precomputable_transaction trx; { account_id_type nathan_id = db2->get_index_type().indices().get().find( "nathan" )->id; fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 2e05f71017..874e2f8241 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -234,7 +235,7 @@ void database_fixture::verify_asset_supplies( const database& db ) BOOST_CHECK(core_asset_data.fee_pool == 0); const auto& statistics_index = db.get_index_type().indices(); - const auto& balance_index = db.get_index_type().indices(); + const auto& acct_balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); const auto& bids = db.get_index_type().indices(); map total_balances; @@ -242,7 +243,7 @@ void database_fixture::verify_asset_supplies( const database& db ) share_type core_in_orders; share_type reported_core_in_orders; - for( const account_balance_object& b : balance_index ) + for( const account_balance_object& b : acct_balance_index ) total_balances[b.asset_type] += b.balance; for( const force_settlement_object& s : settle_index ) total_balances[s.balance.asset_id] += s.balance.amount; @@ -284,6 +285,8 @@ void database_fixture::verify_asset_supplies( const database& db ) total_balances[ vbo.balance.asset_id ] += vbo.balance.amount; for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() ) total_balances[ asset_id_type() ] += fba.accumulated_fba_fees; + for( const balance_object& bo : db.get_index_type< balance_index >().indices() ) + total_balances[ bo.balance.asset_id ] += bo.balance.amount; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; @@ -460,7 +463,7 @@ const asset_object& database_fixture::create_bitasset( creator.bitasset_opts->short_backing_asset = backing_asset; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW( (name)(flags) ) } @@ -491,7 +494,7 @@ const asset_object& database_fixture::create_prediction_market( creator.is_prediction_market = true; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW( (name)(flags) ) } @@ -511,7 +514,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.common_options.issuer_permissions = charge_market_fee; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -533,7 +536,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na trx.operations.push_back(std::move(creator)); set_expiration( db, trx ); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -546,7 +549,7 @@ void database_fixture::issue_uia( const account_object& recipient, asset amount op.asset_to_issue = amount; op.issue_to_account = recipient.id; trx.operations.push_back(op); - db.push_transaction( trx, ~0 ); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } @@ -592,7 +595,7 @@ const account_object& database_fixture::create_account( { trx.operations.push_back(make_account(name, key)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); auto& result = db.get(ptx.operation_results[0].get()); trx.operations.clear(); return result; @@ -611,7 +614,7 @@ const account_object& database_fixture::create_account( trx.operations.resize(1); trx.operations.back() = (make_account(name, registrar, referrer, referrer_percent, key)); trx.validate(); - auto r = db.push_transaction(trx, ~0); + auto r = PUSH_TX(db, trx, ~0); const auto& result = db.get(r.operation_results[0].get()); trx.operations.clear(); return result; @@ -643,7 +646,7 @@ const account_object& database_fixture::create_account( trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); const account_object& result = db.get(ptx.operation_results[0].get()); trx.operations.clear(); return result; @@ -657,7 +660,7 @@ const committee_member_object& database_fixture::create_committee_member( const op.committee_member_account = owner.id; trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -678,7 +681,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow op.block_signing_key = signing_private_key.get_public_key(); trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, skip_flags ); + processed_transaction ptx = PUSH_TX(db, trx, skip_flags ); trx.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW() } @@ -693,7 +696,7 @@ const worker_object& database_fixture::create_worker( const account_id_type owne op.work_end_date = op.work_begin_date + duration; trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW() } @@ -738,7 +741,7 @@ const limit_order_object* database_fixture::create_sell_order( const account_obj trx.operations.push_back(buy_order); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op, fee_core_exchange_rate); trx.validate(); - auto processed = db.push_transaction(trx, ~0); + auto processed = PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); return db.find( processed.operation_results[0].get() ); @@ -752,7 +755,7 @@ asset database_fixture::cancel_limit_order( const limit_order_object& order ) trx.operations.push_back(cancel_order); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - auto processed = db.push_transaction(trx, ~0); + auto processed = PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); return processed.operation_results[0].get(); @@ -788,7 +791,7 @@ void database_fixture::transfer( for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); } trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); verify_asset_supplies(db); trx.operations.clear(); } FC_CAPTURE_AND_RETHROW( (from.id)(to.id)(amount)(fee) ) @@ -806,7 +809,7 @@ void database_fixture::update_feed_producers( const asset_object& mia, flat_set< for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (mia)(producers) ) } @@ -826,7 +829,7 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obje for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } @@ -874,7 +877,7 @@ void database_fixture::force_global_settle( const asset_object& what, const pric trx.operations.push_back(sop); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (what)(p) ) } @@ -889,7 +892,7 @@ operation_result database_fixture::force_settle( const account_object& who, asse trx.operations.push_back(sop); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); const operation_result& op_result = ptx.operation_results.front(); trx.operations.clear(); verify_asset_supplies(db); @@ -909,7 +912,7 @@ const call_order_object* database_fixture::borrow( const account_object& who, as trx.operations.push_back(update); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); @@ -934,7 +937,7 @@ void database_fixture::cover(const account_object& who, asset what, asset collat trx.operations.push_back(update); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral)(target_cr) ) } @@ -950,7 +953,7 @@ void database_fixture::bid_collateral(const account_object& who, const asset& to trx.operations.push_back(bid); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (who.name)(to_bid)(to_cover) ) } @@ -966,7 +969,7 @@ void database_fixture::fund_fee_pool( const account_object& from, const asset_ob for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); set_expiration( db, trx ); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } @@ -993,7 +996,7 @@ void database_fixture::upgrade_to_lifetime_member( const account_object& account op.upgrade_to_lifetime_member = true; op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); trx.operations = {op}; - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() ); trx.clear(); verify_asset_supplies(db); @@ -1013,7 +1016,7 @@ void database_fixture::upgrade_to_annual_member(const account_object& account) op.account_to_upgrade = account.get_id(); op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); trx.operations = {op}; - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); FC_ASSERT( op.account_to_upgrade(db).is_member(db.head_block_time()) ); trx.clear(); verify_asset_supplies(db); @@ -1180,7 +1183,7 @@ bool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ ) { try { - auto pt = db.push_transaction( tx, skip_flags ); + auto pt = db.push_transaction( precomputable_transaction(tx), skip_flags ); database_fixture::verify_asset_supplies(db); return pt; } FC_CAPTURE_AND_RETHROW((tx)) } diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 573d609edf..37bfb8f7cf 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -498,7 +498,7 @@ BOOST_AUTO_TEST_CASE( committee_authority ) */ trx.operations.push_back(uop); sign( trx, committee_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0); BOOST_CHECK(db.get(prop.id).is_authorized_to_execute(db)); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 1890dc38b8..837a1d9bb2 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -602,11 +602,11 @@ BOOST_AUTO_TEST_CASE( undo_pending ) t.to = nathan_id; t.amount = asset(5000); trx.operations.push_back(t); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.clear(); set_expiration( db, trx ); trx.operations.push_back(t); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000); db.clear_pending(); @@ -757,7 +757,7 @@ BOOST_AUTO_TEST_CASE( tapos ) cop.active = cop.owner; trx.operations.push_back(cop); trx.sign( init_account_priv_key, db1.get_chain_id() ); - db1.push_transaction(trx); + PUSH_TX(db1, trx); b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); trx.clear(); @@ -952,7 +952,7 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); t.from = bob.id; @@ -963,21 +963,21 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) trx.validate(); BOOST_TEST_MESSAGE( "Verify that not-signing causes an exception" ); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), fc::exception ); BOOST_TEST_MESSAGE( "Verify that double-signing causes an exception" ); sign( trx, bob_private_key ); sign( trx, bob_private_key ); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_duplicate_sig ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_duplicate_sig ); BOOST_TEST_MESSAGE( "Verify that signing with an extra, unused key fails" ); trx.signatures.pop_back(); sign( trx, generate_private_key("bogus" )); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_irrelevant_sig ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_irrelevant_sig ); BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" ); trx.signatures.pop_back(); - db.push_transaction(trx, 0); + PUSH_TX(db, trx, 0); } FC_LOG_AND_RETHROW() } @@ -998,7 +998,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) uop.new_parameters.block_interval = 1; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - db.push_transaction(trx); + PUSH_TX(db, trx); } BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { @@ -1019,7 +1019,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) sign( trx, get_account("init6" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init7" ).active.get_keys().front(),init_account_priv_key); */ - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); } BOOST_TEST_MESSAGE( "Verifying that the interval didn't change immediately" ); @@ -1602,7 +1602,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) trx.operations.push_back( create_op ); // trx.sign( sam_key ); - processed_transaction ptx_create = db.push_transaction( trx, + processed_transaction ptx_create = PUSH_TX( db, trx, database::skip_transaction_dupe_check | database::skip_transaction_signatures ); @@ -1639,11 +1639,11 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) sign( trx, *owner_privkey[i] ); if( i < int(create_op.owner.weight_threshold-1) ) { - GRAPHENE_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + GRAPHENE_REQUIRE_THROW(PUSH_TX(db, trx), fc::exception); } else { - db.push_transaction( trx, + PUSH_TX( db, trx, database::skip_transaction_dupe_check | database::skip_transaction_signatures ); } diff --git a/tests/tests/confidential_tests.cpp b/tests/tests/confidential_tests.cpp index bef651db5c..b5046dcdfe 100644 --- a/tests/tests/confidential_tests.cpp +++ b/tests/tests/confidential_tests.cpp @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) trx.operations = {to_blind}; sign( trx, dan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); trx.clear_signatures(); BOOST_TEST_MESSAGE( "Transfering from blind to blind with change address" ); @@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) blind_tr.validate(); trx.operations = {blind_tr}; sign( trx, owner2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE( "Attempting to double spend the same commitments" ); blind_tr.fee = core.amount(11); @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-11 ); blind_tr.outputs = {out4,out3}; trx.operations = {blind_tr}; - BOOST_REQUIRE_THROW( db.push_transaction(trx, ~0), graphene::chain::blind_transfer_unknown_commitment ); + BOOST_REQUIRE_THROW( PUSH_TX(db, trx, ~0), graphene::chain::blind_transfer_unknown_commitment ); BOOST_TEST_MESSAGE( "Transfering from blind to nathan public" ); @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) from_blind.inputs.push_back( {out4.commitment, out4.owner} ); trx.operations = {from_blind}; trx.clear_signatures(); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 ); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index b00745ff06..398eb42b11 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE( asset_settle_cancel_operation_test_after_hf588 ) pcop.proposed_ops.emplace_back(ascop); trx.operations.push_back(pcop); - BOOST_CHECK_EXCEPTION(db.push_transaction(trx), fc::assert_exception, + BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception, [](fc::assert_exception const &e) -> bool { std::cout << e.to_string() << std::endl; if (e.to_string().find("Virtual operation") != std::string::npos) @@ -333,7 +333,7 @@ BOOST_AUTO_TEST_CASE( asset_settle_cancel_operation_test_after_hf588 ) trx.operations.push_back(pcop); - BOOST_CHECK_EXCEPTION(db.push_transaction(trx), fc::assert_exception, + BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception, [](fc::assert_exception const &e) -> bool { std::cout << e.to_string() << std::endl; if (e.to_string().find("Virtual operation") != std::string::npos) @@ -656,7 +656,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_target_cr_hardfork_time_test ) tx.operations.push_back( prop ); db.current_fee_schedule().set_fee( tx.operations.back() ); set_expiration( db, tx ); - db.push_transaction( tx, ~0 ); + PUSH_TX( db, tx, ~0 ); }; BOOST_TEST_MESSAGE( "bob tries to propose a proposal with target_cr set, " diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index e0d027da1c..15df1b2ece 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create_before_hardfork_23 ) trx.operations.back() = op; } sign( trx, nathan_private_key ); - db.push_transaction( trx ); + PUSH_TX( db, trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create_after_hardfork_23 ) trx.operations.back() = op; } sign( trx, nathan_private_key ); - db.push_transaction( trx ); + PUSH_TX( db, trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -1027,7 +1027,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_test ) op.issuer = bit_usd.issuer; trx.operations = {op}; sign( trx, nathan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE("Checking current_feed is null"); BOOST_CHECK(bitasset.current_feed.settlement_price.is_null()); @@ -1037,7 +1037,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_test ) trx.clear(); trx.operations = {op}; sign( trx, nathan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE("Checking current_feed is not null"); BOOST_CHECK(!bitasset.current_feed.settlement_price.is_null()); @@ -1907,13 +1907,13 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.operations = {op}; _sign( trx, n_key ); // Fail because I'm claiming from an address which hasn't signed - GRAPHENE_CHECK_THROW(db.push_transaction(trx), tx_missing_other_auth); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), tx_missing_other_auth); trx.clear(); op.balance_to_claim = balance_id_type(); op.balance_owner_key = n_key.get_public_key(); trx.operations = {op}; _sign( trx, n_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); // Not using fixture's get_balance() here because it uses fixture's db, not my override BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 1); @@ -1941,7 +1941,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v1_key ); // Attempting to claim 1 from a balance with 0 available - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount); op.balance_to_claim = vesting_balance_2.id; op.total_claimed.amount = 151; @@ -1951,7 +1951,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim 151 from a balance with 150 available - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount); op.balance_to_claim = vesting_balance_2.id; op.total_claimed.amount = 100; @@ -1960,7 +1960,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 101); BOOST_CHECK_EQUAL(vesting_balance_2.balance.amount.value, 300); @@ -1970,7 +1970,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim twice within a day - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often); db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags); slot = db.get_slot_at_time(vesting_balance_1.vesting_policy->begin_timestamp + 60); @@ -1984,7 +1984,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v1_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 601); @@ -1996,7 +1996,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim twice within a day - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often); db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags); slot = db.get_slot_at_time(db.head_block_time() + fc::days(1)); @@ -2008,7 +2008,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 901); } FC_LOG_AND_RETHROW() } @@ -2028,7 +2028,7 @@ BOOST_AUTO_TEST_CASE(transfer_with_memo) { op.memo->set_message(alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); trx.operations = {op}; trx.sign(alice_private_key, db.get_chain_id()); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 500); BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 500); @@ -2429,18 +2429,18 @@ BOOST_AUTO_TEST_CASE( buyback ) sign( tx, philbin_private_key ); // Alice and Philbin signed, but asset issuer is invalid - GRAPHENE_CHECK_THROW( db.push_transaction(tx), account_create_buyback_incorrect_issuer ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_incorrect_issuer ); tx.clear_signatures(); tx.operations.back().get< account_create_operation >().extensions.value.buyback_options->asset_to_buy_issuer = izzy_id; sign( tx, philbin_private_key ); // Izzy didn't sign - GRAPHENE_CHECK_THROW( db.push_transaction(tx), tx_missing_active_auth ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), tx_missing_active_auth ); sign( tx, izzy_private_key ); // OK - processed_transaction ptx = db.push_transaction( tx ); + processed_transaction ptx = PUSH_TX( db, tx ); rex_id = ptx.operation_results.back().get< object_id_type >(); // Try to create another account rex2 which is bbo on same asset @@ -2448,7 +2448,7 @@ BOOST_AUTO_TEST_CASE( buyback ) tx.operations.back().get< account_create_operation >().name = "rex2"; sign( tx, izzy_private_key ); sign( tx, philbin_private_key ); - GRAPHENE_CHECK_THROW( db.push_transaction(tx), account_create_buyback_already_exists ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_already_exists ); } // issue some BUYME to Alice From 883ec6a7f3ffb01fcba3130ec0f21a1d39977749 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 17:57:22 +0200 Subject: [PATCH 074/132] Avoid one level of indirection on precompute block --- libraries/chain/db_block.cpp | 27 +++++++------------ .../chain/include/graphene/chain/database.hpp | 1 - 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index ada5167ba2..b26f2380cf 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -765,18 +765,16 @@ void database::_precompute_parallel( const Trx* trx, const size_t count, const u } } -void database::_precompute_parallel( const signed_block& block, const uint32_t skip )const +fc::future database::precompute_parallel( const signed_block& block, const uint32_t skip )const { try { - const bool cheap = (skip & skip_expensive) == skip_expensive; std::vector> workers; if( !block.transactions.empty() ) { - if( cheap ) + if( (skip & skip_expensive) == skip_expensive ) _precompute_parallel( &block.transactions[0], block.transactions.size(), skip ); else { uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads(); - if( !chunks ) return; uint32_t chunk_size = block.transactions.size() / chunks; if( chunks * chunk_size < block.transactions.size() ) chunk_size++; @@ -795,21 +793,16 @@ void database::_precompute_parallel( const signed_block& block, const uint32_t s if( !(skip&skip_merkle_check) ) block.calculate_merkle_root(); block.id(); - for( auto& worker : workers ) - worker.wait(); -} FC_LOG_AND_RETHROW() } -fc::future database::precompute_parallel( const signed_block& block, const uint32_t skip )const -{ - if( block.transactions.empty() || (skip & skip_expensive) == skip_expensive ) - { - _precompute_parallel( block, skip ); + if( workers.empty() ) return fc::future< void >( fc::promise< void >::ptr( new fc::promise< void >( true ) ) ); - } - return fc::do_parallel([this,&block,skip] () { - _precompute_parallel( block, skip ); - }); -} + + auto first = workers.begin(); + auto worker = first; + while( ++worker != workers.end() ) + worker->wait(); + return *first; +} FC_LOG_AND_RETHROW() } fc::future database::precompute_parallel( const precomputable_transaction& trx )const { diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index bf2795ea1f..84e4ad4190 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -437,7 +437,6 @@ namespace graphene { namespace chain { private: template void _precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const; - void _precompute_parallel( const signed_block& block, const uint32_t skip )const; protected: //Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead From d032890de9044fc5e2165e6185e721db5be07f68 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 11 Oct 2018 23:04:39 +0200 Subject: [PATCH 075/132] Make pubkey comparator generally available --- libraries/chain/account_object.cpp | 6 ++--- .../include/graphene/chain/account_object.hpp | 26 +++++++------------ .../include/graphene/chain/protocol/types.hpp | 8 ++++++ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 952a4c7c2c..7bdf51a2de 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -121,9 +121,9 @@ set account_member_index::get_account_members(const account_obj result.insert(auth.first); return result; } -set account_member_index::get_key_members(const account_object& a)const +set account_member_index::get_key_members(const account_object& a)const { - set result; + set result; for( auto auth : a.owner.key_auths ) result.insert(auth.first); for( auto auth : a.active.key_auths ) @@ -215,7 +215,7 @@ void account_member_index::object_modified(const object& after) { - set after_key_members = get_key_members(a); + set after_key_members = get_key_members(a); vector removed; removed.reserve(before_key_members.size()); std::set_difference(before_key_members.begin(), before_key_members.end(), diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index cae9d35984..c3fdcee01f 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -292,14 +292,6 @@ namespace graphene { namespace chain { */ class account_member_index : public secondary_index { - class key_compare { - public: - inline bool operator()( const public_key_type& a, const public_key_type& b )const - { - return a.key_data < b.key_data; - } - }; - public: virtual void object_inserted( const object& obj ) override; virtual void object_removed( const object& obj ) override; @@ -308,20 +300,20 @@ namespace graphene { namespace chain { /** given an account or key, map it to the set of accounts that reference it in an active or owner authority */ - map< account_id_type, set > account_to_account_memberships; - map< public_key_type, set, key_compare > account_to_key_memberships; + map< account_id_type, set > account_to_account_memberships; + map< public_key_type, set, pubkey_comparator > account_to_key_memberships; /** some accounts use address authorities in the genesis block */ - map< address, set > account_to_address_memberships; + map< address, set > account_to_address_memberships; protected: - set get_account_members( const account_object& a )const; - set get_key_members( const account_object& a )const; - set
get_address_members( const account_object& a )const; + set get_account_members( const account_object& a )const; + set get_key_members( const account_object& a )const; + set
get_address_members( const account_object& a )const; - set before_account_members; - set before_key_members; - set
before_address_members; + set before_account_members; + set before_key_members; + set
before_address_members; }; diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index bd04c071d3..4456d6d3a5 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -266,6 +266,14 @@ namespace graphene { namespace chain { friend bool operator != ( const public_key_type& p1, const public_key_type& p2); }; + class pubkey_comparator { + public: + inline bool operator()( const public_key_type& a, const public_key_type& b )const + { + return a.key_data < b.key_data; + } + }; + struct extended_public_key_type { struct binary_key From 2c01109c6479713cd8b0f0b01346c4ce2bb91e9a Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 7 Nov 2018 17:27:47 +0100 Subject: [PATCH 076/132] Address some review comments --- libraries/chain/db_block.cpp | 4 +--- .../chain/include/graphene/chain/protocol/transaction.hpp | 8 ++++---- libraries/chain/protocol/transaction.cpp | 4 ++-- libraries/plugins/delayed_node/delayed_node_plugin.cpp | 1 + tests/common/database_fixture.hpp | 1 + 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index b26f2380cf..9e0ff2c95b 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -775,9 +775,7 @@ fc::future database::precompute_parallel( const signed_block& block, const else { uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads(); - uint32_t chunk_size = block.transactions.size() / chunks; - if( chunks * chunk_size < block.transactions.size() ) - chunk_size++; + uint32_t chunk_size = ( block.transactions.size() + chunks - 1 ) / chunks; workers.reserve( chunks + 1 ); for( size_t base = 0; base < block.transactions.size(); base += chunk_size ) workers.push_back( fc::do_parallel( [this,&block,base,chunk_size,skip] () { diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 02f00799c6..84234afb9e 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -89,7 +89,6 @@ namespace graphene { namespace chain { digest_type digest()const; virtual const transaction_id_type& id()const; virtual void validate() const; - /// Calculate the digest used for signature validation void set_expiration( fc::time_point_sec expiration_time ); void set_reference_block( const block_id_type& reference_block ); @@ -115,6 +114,7 @@ namespace graphene { namespace chain { void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; protected: + // Calculate the digest used for signature validation digest_type sig_digest( const chain_id_type& chain_id )const; mutable transaction_id_type _tx_id_buffer; }; @@ -206,9 +206,9 @@ namespace graphene { namespace chain { precomputable_transaction( const signed_transaction& tx ) : signed_transaction(tx) {}; precomputable_transaction( signed_transaction&& tx ) : signed_transaction( std::move(tx) ) {}; - virtual const transaction_id_type& id()const; - virtual void validate() const; - virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const; + virtual const transaction_id_type& id()const override; + virtual void validate()const override; + virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const override; protected: mutable bool _validated = false; }; diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index c1dd817693..1a1293ca76 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -303,8 +303,6 @@ void verify_authority( const vector& ops, const flat_set& signed_transaction::get_signature_keys( const chain_id_type& chain_id )const { try { - // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. - // However, we don't pass in another chain ID so far, for better performance, we skip the check. auto d = sig_digest( chain_id ); flat_set result; for( const auto& sig : signatures ) @@ -397,6 +395,8 @@ void precomputable_transaction::validate() const const flat_set& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const { + // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. + // However, we don't pass in another chain ID so far, for better performance, we skip the check. if( _signees.empty() ) signed_transaction::get_signature_keys( chain_id ); return _signees; diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index dd1d4705e6..f5e3f88cd9 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -103,6 +103,7 @@ void delayed_node_plugin::sync_with_trusted_node() while( remote_dpo.last_irreversible_block_num > db.head_block_num() ) { fc::optional block = my->database_api->get_block( db.head_block_num()+1 ); + // TODO: during sync, decouple requesting blocks from preprocessing + applying them FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); ilog("Pushing block #${n}", ("n", block->block_num())); db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait(); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 72744ff7e5..eceba6876b 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -172,6 +172,7 @@ namespace graphene { namespace chain { class clearable_block : public signed_block { public: + /** @brief Clears internal cached values like ID, signing key, Merkle root etc. */ void clear(); }; From c4b584c089d515ae81d00d3fed050c5daab24520 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 9 Dec 2018 13:43:17 +0100 Subject: [PATCH 077/132] direct_index: Added clarifying comment, ensure that object ID is not modified --- libraries/db/include/graphene/db/index.hpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index fd2923f599..ddee3833ed 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -194,6 +194,10 @@ namespace graphene { namespace db { * @brief A secondary index that tracks objects in vectors indexed by object * id. It is meant for fully (or almost fully) populated indexes only (will * fail when loading an object_database with large gaps). + * + * WARNING! If any of the methods called on insertion, removal or + * modification throws, subsequent behaviour is undefined! Such exceptions + * indicate that this index type is not appropriate for the use-case. */ template class direct_index : public secondary_index @@ -205,6 +209,7 @@ namespace graphene { namespace db { static const size_t _mask = ((1 << chunkbits) - 1); size_t next = 0; vector< vector< const Object* > > content; + object_id_type id_being_modified; public: direct_index() { @@ -254,13 +259,23 @@ namespace graphene { namespace db { content[instance >> chunkbits][instance & _mask] = nullptr; } + virtual void about_to_modify( const object& before ) + { + id_being_modified = before.id; + } + + virtual void object_modified( const object& after ) + { + FC_ASSERT( id_being_modified == after.id, "Modification of ID is not supported!"); + } + template< typename object_id > const Object* find( const object_id& id )const { static_assert( object_id::space_id == Object::space_id, "Space ID mismatch!" ); static_assert( object_id::type_id == Object::type_id, "Type_ID mismatch!" ); if( id.instance >= next ) return nullptr; - return content[id.instance >> chunkbits][id.instance & _mask]; + return content[id.instance.value >> chunkbits][id.instance.value & _mask]; }; template< typename object_id > From 9117c85dba00b6a507b9fad91cf16594423db2d5 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 9 Dec 2018 13:43:30 +0100 Subject: [PATCH 078/132] Added unit test for direct_index --- tests/tests/database_tests.cpp | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/tests/database_tests.cpp b/tests/tests/database_tests.cpp index 6b5ea8b96b..e8c1d24fe2 100644 --- a/tests/tests/database_tests.cpp +++ b/tests/tests/database_tests.cpp @@ -137,4 +137,79 @@ BOOST_AUTO_TEST_CASE( merge_test ) } } +BOOST_AUTO_TEST_CASE( direct_index_test ) +{ try { + try { + const graphene::db::primary_index< account_index, 6 > small_chunkbits( db ); + BOOST_FAIL( "Expected assertion failure!" ); + } catch( const fc::assert_exception& expected ) {} + + graphene::db::primary_index< account_index, 8 > my_accounts( db ); + const auto& direct = my_accounts.get_secondary_index>(); + BOOST_CHECK_EQUAL( 0, my_accounts.indices().size() ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 1 ) ) ); + // BOOST_CHECK_THROW( direct.find( asset_id_type( 1 ) ), fc::assert_exception ); // compile-time error + BOOST_CHECK_THROW( direct.find( object_id_type( asset_id_type( 1 ) ) ), fc::assert_exception ); + BOOST_CHECK_THROW( direct.get( account_id_type( 1 ) ), fc::assert_exception ); + + account_object test_account; + test_account.id = account_id_type(1); + test_account.name = "account1"; + + my_accounts.load( fc::raw::pack( test_account ) ); + + BOOST_CHECK_EQUAL( 1, my_accounts.indices().size() ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 0 ) ) ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 2 ) ) ); + BOOST_CHECK( nullptr != direct.find( account_id_type( 1 ) ) ); + BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name ); + + // The following assumes that MAX_HOLE = 100 + test_account.id = account_id_type(102); + test_account.name = "account102"; + // highest insert was 1, direct.next is 2 => 102 is highest allowed instance + my_accounts.load( fc::raw::pack( test_account ) ); + BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name ); + + // direct.next is now 103, but index sequence counter is 0 + my_accounts.create( [] ( object& o ) { + account_object& acct = dynamic_cast< account_object& >( o ); + BOOST_CHECK_EQUAL( 0, acct.id.instance() ); + acct.name = "account0"; + } ); + + test_account.id = account_id_type(50); + test_account.name = "account50"; + my_accounts.load( fc::raw::pack( test_account ) ); + + // direct.next is still 103, so 204 is not allowed + test_account.id = account_id_type(204); + test_account.name = "account204"; + GRAPHENE_REQUIRE_THROW( my_accounts.load( fc::raw::pack( test_account ) ), fc::assert_exception ); + // This is actually undefined behaviour. The object has been inserted into + // the primary index, but the secondary has refused to insert it! + BOOST_CHECK_EQUAL( 5, my_accounts.indices().size() ); + + uint32_t count = 0; + for( uint32_t i = 0; i < 250; i++ ) + { + const account_object* aptr = dynamic_cast< const account_object* >( my_accounts.find( account_id_type( i ) ) ); + if( aptr ) + { + count++; + BOOST_CHECK( aptr->id.instance() == 0 || aptr->id.instance() == 1 + || aptr->id.instance() == 50 || aptr->id.instance() == 102 ); + BOOST_CHECK_EQUAL( i, aptr->id.instance() ); + BOOST_CHECK_EQUAL( "account" + std::to_string( i ), aptr->name ); + } + } + BOOST_CHECK_EQUAL( count, my_accounts.indices().size() - 1 ); + + GRAPHENE_REQUIRE_THROW( my_accounts.modify( direct.get( account_id_type( 1 ) ), [] ( object& acct ) { + acct.id = account_id_type(2); + }), fc::assert_exception ); + // This is actually undefined behaviour. The object has been modified, but + // but the secondary has not updated its representation +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From 68982ca711bbfba96dc671470520b0d0787d5e92 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 11 Dec 2018 09:48:25 -0500 Subject: [PATCH 079/132] Added base16 to htlc hash for cli wallet --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 15 ++++++++++++-- tests/cli/main.cpp | 20 +++++++++++++------ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index d8cf519d71..2613e177cf 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1469,7 +1469,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ); /**** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0b709add69..ea10dcc7c9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -3104,10 +3105,20 @@ uint64_t wallet_api::get_asset_count()const } signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast) { - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + // convert string back into binary + char s[3]; + s[2] = 0; + std::vector uns; + for(int i = 0; i < preimage_hash.length(); i+= 2) + { + s[0] = preimage_hash[i]; + s[1] = preimage_hash[i+1]; + uns.push_back( (int)strtol(&s[0], nullptr, 16)); + } + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, uns, preimage_size, seconds_in_force, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 6af62e98fe..3fdc689b82 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef _WIN32 #ifndef _WIN32_WINNT @@ -678,14 +679,21 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC"); // create an HTLC std::string preimage_string = "My Secret"; - fc::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); - std::vector hash(preimage_md.data_size()); - for(size_t i = 0; i < preimage_md.data_size(); ++i) - hash[i] = preimage_md.data()[i]; + fc::sha256 preimage_md = fc::sha256::hash(preimage_string); + std::stringstream ss; + int data_size = preimage_md.data_size(); + for(int i = 0; i < preimage_md.data_size(); i++) + { + char d = preimage_md.data()[i]; + unsigned char uc = static_cast(d); + ss << std::setfill('0') << std::setw(2) << std::hex << (int)uc; + } + std::string hash_str = ss.str(); + BOOST_TEST_MESSAGE("Secret is " + preimage_string + " and hash is " + hash_str); uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->htlc_prepare("alice", "bob", - "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "1.3.0", "3", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string alice_htlc_id_as_string; @@ -707,7 +715,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC con.wallet_api_ptr->htlc_prepare("bob", "alice", - "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "BOBCOIN", "3", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string bob_htlc_id_as_string; From 44d4134501d2413f0e59355a8e531f588b52fb03 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 11 Dec 2018 11:03:24 -0500 Subject: [PATCH 080/132] fix for redeem --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 29 +++++++++++-------- tests/cli/main.cpp | 6 ++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 2613e177cf..7cc545c014 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1479,7 +1479,7 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage, bool broadcast = false ); /***** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ea10dcc7c9..9412207396 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3104,21 +3104,25 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast) +std::vector string_to_vec(std::string incoming) { - // convert string back into binary char s[3]; s[2] = 0; - std::vector uns; - for(int i = 0; i < preimage_hash.length(); i+= 2) + std::vector vec; + for(int i = 0; i < incoming.length(); i+= 2) { - s[0] = preimage_hash[i]; - s[1] = preimage_hash[i+1]; - uns.push_back( (int)strtol(&s[0], nullptr, 16)); + s[0] = incoming[i]; + s[1] = incoming[i+1]; + vec.push_back( (int)strtol(&s[0], nullptr, 16)); } - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, uns, preimage_size, + return vec; +} + +signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + const uint32_t seconds_in_force, bool broadcast) +{ + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, string_to_vec(preimage_hash), preimage_size, seconds_in_force, broadcast); } @@ -3127,10 +3131,11 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } -signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::vector& preimage, +signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, bool broadcast) { - return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); + + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3fdc689b82..f762511380 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -739,8 +739,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) { BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; - vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } @@ -750,8 +749,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) { BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; - vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } From 13926f53f4ca84ce4fba8db51372c27163413be6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 11:14:00 -0500 Subject: [PATCH 081/132] Added details to history for HTLC create and redeem --- libraries/wallet/wallet.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9412207396..34c4337a6c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -135,6 +135,8 @@ struct operation_printer std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; std::string operator()(const asset_create_operation& op)const; + std::string operator()(const htlc_create_operation& op)const; + std::string operator()(const htlc_redeem_operation& op)const; }; template @@ -2974,6 +2976,36 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons return fee(op.fee); } +std::string operation_printer::operator()(const htlc_redeem_operation& op) const +{ + auto from = wallet.get_account( op.source ); + + out << "Redeem HTLC from " << from.name + << " with database id " << std::to_string(op.htlc_id.space_id) + << "." << std::to_string(op.htlc_id.type_id) + << "." << std::to_string((uint64_t)op.htlc_id.instance) + << " with preimage \""; + for (unsigned char c : op.preimage) + out << c; + out << "\""; + return fee(op.fee); +} + +std::string operation_printer::operator()(const htlc_create_operation& op) const +{ + auto a = wallet.get_asset( op.fee.asset_id ); + auto payer = wallet.get_account( op.fee_payer() ); + auto to = wallet.get_account( op.destination ); + operation_result_printer rprinter(wallet); + std::string database_id = result.visit(rprinter); + + out << "Create HTLC to " << to.name + << " with database id " << database_id + << " Fee paid by " << payer.name + << " (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + return ""; +} + std::string operation_result_printer::operator()(const void_result& x) const { return ""; From 5103f7e98a39b93ec7dc60e96273bc55c50df4b3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 11:19:23 -0500 Subject: [PATCH 082/132] clearer text in account history --- libraries/wallet/wallet.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 34c4337a6c..0d31ca8834 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2978,10 +2978,8 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons std::string operation_printer::operator()(const htlc_redeem_operation& op) const { - auto from = wallet.get_account( op.source ); - - out << "Redeem HTLC from " << from.name - << " with database id " << std::to_string(op.htlc_id.space_id) + out << "Redeem HTLC with database id " + << std::to_string(op.htlc_id.space_id) << "." << std::to_string(op.htlc_id.type_id) << "." << std::to_string((uint64_t)op.htlc_id.instance) << " with preimage \""; From 2e5d74f722c693ffcc24a18747d8b46f162577ef Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 20:40:36 -0500 Subject: [PATCH 083/132] Adjust history lines --- libraries/wallet/wallet.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0d31ca8834..32585f3f82 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2999,8 +2999,12 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with database id " << database_id - << " Fee paid by " << payer.name - << " (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + << " preimage hash: ["; + for(unsigned char c : op.key_hash) + { + out << setfill('0') << std::setw(2) << std::hex << (int)c; + } + out << "] (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; return ""; } From d2dfa69f97432ac64068f50164eb8c1bbd073f9a Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 04:47:02 -0500 Subject: [PATCH 084/132] Modified wallet get_htlc to return pretty format --- .../include/graphene/chain/htlc_object.hpp | 2 +- .../include/graphene/chain/protocol/htlc.hpp | 15 ++++++ libraries/chain/protocol/htlc.cpp | 50 +++++++++++++++---- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 32 +++++++++--- tests/cli/main.cpp | 5 +- 6 files changed, 82 insertions(+), 24 deletions(-) diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 324dbc706e..89366791e4 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -38,7 +38,7 @@ namespace graphene { namespace chain { sha256 = 0x02, sha1 = 0x03 }; - + /** * @brief database object to store HTLCs * diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 503398ba27..6a0f9ab88d 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,6 +31,21 @@ namespace graphene { namespace chain { + /** + * Convert the hash algorithm to a string + * @param algo the enum to convert + * @returns a string (lower case) + */ + std::string hash_algorithm_to_string(fc::enum_type algo); + + /** + * Convert a string to the enum that matches the hash algorithm + * @param incoing the string (case insensitive) + * @returns the matching enum + */ + fc::enum_type string_to_hash_algorithm(std::string incoming); + + struct htlc_create_operation : public base_operation { struct fee_parameters_type { diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 7d21d7ace4..0f3e61bd95 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -25,17 +25,45 @@ namespace graphene { namespace chain { - void htlc_create_operation::validate()const { - FC_ASSERT( source != destination ); - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( amount.amount > 0 ); - } + void htlc_create_operation::validate()const { + FC_ASSERT( source != destination ); + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount.amount > 0 ); + } - void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( preimage.size() > 0 ); - } - void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + void htlc_redeem_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( preimage.size() > 0 ); + } + void htlc_extend_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + } + + std::string hash_algorithm_to_string(fc::enum_type algo) + { + switch(algo) + { + case (hash_algorithm::ripemd160): + return "ripemd160"; + case (hash_algorithm::sha256): + return "sha256"; + case (hash_algorithm::sha1): + return "sha1"; + default: + break; } + return "unknown"; + } + + fc::enum_type string_to_hash_algorithm(std::string incoming) + { + std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); + if (incoming == "RIPEMD160") + return hash_algorithm::ripemd160; + if (incoming == "SHA256") + return hash_algorithm::sha256; + if (incoming == "SHA1") + return hash_algorithm::sha1; + return hash_algorithm::unknown; + } } } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7cc545c014..462245a6d7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -495,7 +495,7 @@ class wallet_api * @param htlc_id the id of the HTLC object. * @returns the information about the HTLC object */ - htlc_object get_htlc(string htlc_id) const; + variant get_htlc(string htlc_id) const; /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 32585f3f82..6879527bd9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1762,12 +1762,7 @@ class wallet_api_impl create_op.seconds_in_force = seconds_in_force; create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; - if ( "SHA256" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::sha256; - if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; - if ( "SHA1" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::sha1; + create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); @@ -3160,9 +3155,30 @@ signed_transaction wallet_api::htlc_prepare( string source, string destination, seconds_in_force, broadcast); } -graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const +std::string object_id_to_string(const graphene::db::object_id_type& obj_id) { - return my->get_htlc(htlc_id); + return std::to_string(obj_id.space()) + "." + std::to_string(obj_id.type()) + "." + std::to_string(obj_id.instance()); +} + +variant wallet_api::get_htlc(std::string htlc_id) const +{ + graphene::chain::htlc_object obj = my->get_htlc(htlc_id); + fc::mutable_variant_object ret_val; + ret_val["database_id"] = (std::string)obj.id; + ret_val["from"] = object_id_to_string(obj.from); + ret_val["to"] = object_id_to_string(obj.to); + ret_val["amount"] = obj.amount.amount.value; + ret_val["asset"] = object_id_to_string(obj.amount.asset_id); + ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); + ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; + ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); + std::stringstream hash_string; + for(unsigned char c : obj.preimage_hash) + hash_string << std::setfill('0') << std::setw(2) << std::hex << c; + ret_val["preimage_hash"] = hash_string.str(); + ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); + ret_val["preimage_size"] = obj.preimage_size; + return ret_val; } signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index f762511380..e6511aad70 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -681,7 +681,6 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::string preimage_string = "My Secret"; fc::sha256 preimage_md = fc::sha256::hash(preimage_string); std::stringstream ss; - int data_size = preimage_md.data_size(); for(int i = 0; i < preimage_md.data_size(); i++) { char d = preimage_md.data()[i]; @@ -710,7 +709,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // Bob can now look over Alice's HTLC, to see if it is what was agreed to. BOOST_TEST_MESSAGE("Bob retrieves the HTLC Object by ID to examine it."); - graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string); + auto alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string); BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC @@ -732,7 +731,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // Alice can now look over Bob's HTLC, to see if it is what was agreed to: BOOST_TEST_MESSAGE("Alice retrieves the HTLC Object by ID to examine it."); - graphene::chain::htlc_object bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string); + auto bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string); BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(bob_htlc)); // Alice likes what she sees, so uses her preimage to get her BOBCOIN From 4af8e6cbb1a9c54125121a5e122af7cb1059fcc2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 05:17:24 -0500 Subject: [PATCH 085/132] hash format fix --- libraries/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6879527bd9..fac63d3cd2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3174,7 +3174,7 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) - hash_string << std::setfill('0') << std::setw(2) << std::hex << c; + hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; ret_val["preimage_hash"] = hash_string.str(); ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); ret_val["preimage_size"] = obj.preimage_size; From 5492797da08964d9c227970f793f086dcc8e0d68 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 13:17:10 -0500 Subject: [PATCH 086/132] Add pending info to HTLC in account history --- libraries/wallet/wallet.cpp | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fac63d3cd2..fdd2382cc5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -115,14 +115,16 @@ struct operation_printer ostream& out; const wallet_api_impl& wallet; operation_result result; + operation_history_object hist; std::string fee(const asset& a) const; public: - operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) + operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_history_object& obj ) : out(out), wallet(wallet), - result(r) + result(obj.result), + hist(obj) {} typedef std::string result_type; @@ -2314,7 +2316,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2331,7 +2333,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2352,7 +2354,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " transaction_id : "; ss << d.transaction_id.str(); ss << " \n"; @@ -2394,7 +2396,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2407,7 +2409,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2986,20 +2988,24 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const std::string operation_printer::operator()(const htlc_create_operation& op) const { - auto a = wallet.get_asset( op.fee.asset_id ); - auto payer = wallet.get_account( op.fee_payer() ); + auto fee_asset = wallet.get_asset( op.fee.asset_id ); auto to = wallet.get_account( op.destination ); operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); out << "Create HTLC to " << to.name - << " with database id " << database_id + << " with id " << database_id << " preimage hash: ["; for(unsigned char c : op.key_hash) { out << setfill('0') << std::setw(2) << std::hex << (int)c; } - out << "] (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + out << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; + // determine if the block that the HTLC is in is before or after LIB + int32_t pending_blocks = hist.block_num - wallet.get_dynamic_global_properties().last_irreversible_block_num; + if (pending_blocks > 0) + out << " (pending " << std::to_string(pending_blocks) << " blocks)"; + return ""; } @@ -3155,23 +3161,18 @@ signed_transaction wallet_api::htlc_prepare( string source, string destination, seconds_in_force, broadcast); } -std::string object_id_to_string(const graphene::db::object_id_type& obj_id) -{ - return std::to_string(obj_id.space()) + "." + std::to_string(obj_id.type()) + "." + std::to_string(obj_id.instance()); -} - variant wallet_api::get_htlc(std::string htlc_id) const { graphene::chain::htlc_object obj = my->get_htlc(htlc_id); fc::mutable_variant_object ret_val; ret_val["database_id"] = (std::string)obj.id; - ret_val["from"] = object_id_to_string(obj.from); - ret_val["to"] = object_id_to_string(obj.to); + ret_val["from"] = (std::string)((graphene::db::object_id_type)obj.from); + ret_val["to"] = (std::string)((graphene::db::object_id_type)obj.to); ret_val["amount"] = obj.amount.amount.value; - ret_val["asset"] = object_id_to_string(obj.amount.asset_id); + ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; - ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); + ret_val["pending_fee_asset"] = (std::string)((graphene::db::object_id_type)obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; @@ -3242,7 +3243,7 @@ vector wallet_api::get_account_history(string name, int limit) } } std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back( operation_detail{ memo, ss.str(), o } ); } @@ -3290,7 +3291,7 @@ vector wallet_api::get_relative_account_history( start); for (auto &o : current) { std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back(operation_detail{memo, ss.str(), o}); } if (current.size() < std::min(100, limit)) @@ -3334,7 +3335,7 @@ account_history_operation_detail wallet_api::get_account_history_by_operations( auto current = my->_remote_hist->get_account_history_by_operations(always_id, operation_types, start, min_limit); for (auto& obj : current.operation_history_objs) { std::stringstream ss; - auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj.result)); + auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj)); transaction_id_type transaction_id; auto block = get_block(obj.block_num); From 742f0257dc76609c9db3c3b8dbbe3fae3a438b62 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 13:30:01 -0500 Subject: [PATCH 087/132] Fix htlc_prepare method signature --- libraries/wallet/include/graphene/wallet/wallet.hpp | 4 ++-- libraries/wallet/wallet.cpp | 6 +++--- tests/cli/main.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 462245a6d7..7b087dab61 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1460,15 +1460,15 @@ class wallet_api * * @param source The account that will reserve the funds (and pay the fee) * @param destination The account that will receive the funds if the preimage is presented - * @param asset_symbol The asset that is to be traded * @param amount the amount of the asset that is to be traded + * @param asset_symbol The asset that is to be traded * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes * @param seconds_in_force when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fdd2382cc5..185b11f08e 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1747,7 +1747,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ) { @@ -3153,11 +3153,11 @@ std::vector string_to_vec(std::string incoming) return vec; } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, +signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast) { - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, seconds_in_force, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index e6511aad70..3ef22603cb 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -692,7 +692,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->htlc_prepare("alice", "bob", - "1.3.0", "3", "SHA256", hash_str, preimage_string.size(), timelock, true); + "3", "1.3.0", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string alice_htlc_id_as_string; @@ -714,7 +714,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC con.wallet_api_ptr->htlc_prepare("bob", "alice", - "BOBCOIN", "3", "SHA256", hash_str, preimage_string.size(), timelock, true); + "3", "BOBCOIN", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string bob_htlc_id_as_string; From b5b8dfc95dbb7adc5c010fb89c4f2d253f3f8a75 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 14:47:21 -0500 Subject: [PATCH 088/132] Add virtual op for refund --- libraries/chain/db_notify.cpp | 4 ++++ libraries/chain/db_update.cpp | 7 +++++++ .../include/graphene/chain/protocol/htlc.hpp | 19 +++++++++++++++++++ .../graphene/chain/protocol/operations.hpp | 3 ++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index ba8ff5f1ac..f379116c9e 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -268,6 +268,10 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); } + void operator()( const htlc_refund_operation& op ) + { + _impacted.insert( op.fee_payer() ); // bidder + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 7552bc5528..df4d57b161 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -565,8 +565,15 @@ void database::clear_expired_htlcs() && htlc_idx.begin()->expiration <= head_block_time() ) { adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + // virtual op + htlc_refund_operation vop; + vop.htlc_id = htlc_idx.begin()->id; + push_applied_operation(vop); + + // remove the db object remove( *htlc_idx.begin()); } + } } } diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 6a0f9ab88d..5a1c6ff165 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -190,14 +190,33 @@ namespace graphene { return fee_params.fee + (fee_params.fee_per_day * days); } }; + + struct htlc_refund_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_refund_operation(){} + htlc_refund_operation( const htlc_id_type& htlc_id ) : htlc_id(htlc_id) {} + + account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + /// This is a virtual operation; there is no fee + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + asset fee; + }; } } FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) +FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) +FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 2eded4a707..3f5c27d998 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -99,7 +99,8 @@ namespace graphene { namespace chain { asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, - htlc_extend_operation + htlc_extend_operation, + htlc_refund_operation // VIRTUAL > operation; /// @} // operations group From fe24161fa4544358f89b102bb28394f894d6025a Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 15:28:19 -0500 Subject: [PATCH 089/132] Make refund appear in proper account --- libraries/chain/db_update.cpp | 5 +++-- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index df4d57b161..1acfc2f9d8 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -564,9 +564,10 @@ void database::clear_expired_htlcs() while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() && htlc_idx.begin()->expiration <= head_block_time() ) { - adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + const htlc_object& obj = *htlc_idx.begin(); + adjust_balance( obj.from, obj.amount ); // virtual op - htlc_refund_operation vop; + htlc_refund_operation vop(obj.id, obj.from); vop.htlc_id = htlc_idx.begin()->id; push_applied_operation(vop); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 5a1c6ff165..d0f5258066 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -196,15 +196,17 @@ namespace graphene { struct fee_parameters_type {}; htlc_refund_operation(){} - htlc_refund_operation( const htlc_id_type& htlc_id ) : htlc_id(htlc_id) {} + htlc_refund_operation( const htlc_id_type& htlc_id, const account_id_type& to ) + : htlc_id(htlc_id), to(to) {} - account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + account_id_type fee_payer()const { return to; } void validate()const { FC_ASSERT( !"virtual operation" ); } /// This is a virtual operation; there is no fee share_type calculate_fee(const fee_parameters_type& k)const { return 0; } htlc_id_type htlc_id; + account_id_type to; asset fee; }; } @@ -219,4 +221,4 @@ FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) -FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)) +FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) From 956f1c9c42869ec67ed3a1d98ad84689e4acc0d5 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 14 Dec 2018 10:24:57 -0300 Subject: [PATCH 090/132] bump database --- libraries/chain/include/graphene/chain/config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 1ef28a0c67..3ab5f47f19 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.18" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.19" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From 115ed51b787ce96c2743c988897cfd2f1a6d75df Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 14 Dec 2018 11:49:10 -0300 Subject: [PATCH 091/132] clarify proxy vote test times --- tests/tests/voting_tests.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index b4af210637..141c19e0f3 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) // witness to vote for auto witness1 = witness_id_type(1)(db); - // alice changes proxy, this is voting activity + // round1: alice changes proxy, this is voting activity { graphene::chain::account_update_operation op; op.account = alice_id; @@ -472,12 +472,12 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) } // alice last_vote_time is updated auto alice_stats_obj = db.get_account_stats_by_owner(alice_id); - auto now = db.head_block_time().sec_since_epoch(); - BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), now); + auto round1 = db.head_block_time().sec_since_epoch(); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); generate_block(); - // alice update account but no proxy or voting changes are done + // round 2: alice update account but no proxy or voting changes are done { graphene::chain::account_update_operation op; op.account = alice_id; @@ -488,13 +488,13 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) PUSH_TX( db, trx, ~0 ); } // last_vote_time is not updated - now = db.head_block_time().sec_since_epoch(); + auto round2 = db.head_block_time().sec_since_epoch(); alice_stats_obj = db.get_account_stats_by_owner(alice_id); - BOOST_CHECK(alice_stats_obj.last_vote_time.sec_since_epoch() != now); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); generate_block(); - // bob votes + // round 3: bob votes { graphene::chain::account_update_operation op; op.account = bob_id; @@ -507,13 +507,13 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) } // last_vote_time for bob is updated as he voted - now = db.head_block_time().sec_since_epoch(); + auto round3 = db.head_block_time().sec_since_epoch(); auto bob_stats_obj = db.get_account_stats_by_owner(bob_id); - BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), now); + BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3); generate_block(); - // proxy votes + // round 4: proxy votes { graphene::chain::account_update_operation op; op.account = proxy_id; @@ -525,17 +525,17 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) } // proxy just voted so the last_vote_time is updated - now = db.head_block_time().sec_since_epoch(); + auto round4 = db.head_block_time().sec_since_epoch(); auto proxy_stats_obj = db.get_account_stats_by_owner(proxy_id); - BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), now); + BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), round4); // alice haves proxy, proxy votes but last_vote_time is not updated for alice alice_stats_obj = db.get_account_stats_by_owner(alice_id); - BOOST_CHECK(alice_stats_obj.last_vote_time.sec_since_epoch() != now); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); // bob haves nothing to do with proxy so last_vote_time is not updated bob_stats_obj = db.get_account_stats_by_owner(bob_id); - BOOST_CHECK(bob_stats_obj.last_vote_time.sec_since_epoch() != now); + BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3); } FC_LOG_AND_RETHROW() } From 4a3f45b1766067d1a1f356474166e04df329d624 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 14 Dec 2018 18:15:14 +0100 Subject: [PATCH 092/132] Fixed code smell --- libraries/db/include/graphene/db/index.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index ddee3833ed..babfbda91f 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -204,7 +204,7 @@ namespace graphene { namespace db { { static_assert( chunkbits < 64, "Do you really want arrays with more than 2^63 elements???" ); - private: + // private static const size_t MAX_HOLE = 100; static const size_t _mask = ((1 << chunkbits) - 1); size_t next = 0; From 27e2e75e322200c0716d4794b7b04d4b4d5f6b37 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 13:58:14 -0500 Subject: [PATCH 093/132] Code cleanup pmconrad suggestions --- libraries/app/database_api.cpp | 45 -------- .../app/include/graphene/app/database_api.hpp | 10 -- .../chain/committee_member_evaluator.cpp | 2 +- libraries/chain/db_notify.cpp | 10 +- libraries/chain/db_update.cpp | 9 +- libraries/chain/hardfork.d/HTLC.hf | 4 - libraries/chain/hardfork.d/core-1468.hf | 4 + libraries/chain/htlc_evaluator.cpp | 102 +++++++----------- .../include/graphene/chain/htlc_object.hpp | 17 +-- .../chain/protocol/chain_parameters.hpp | 4 +- libraries/chain/proposal_evaluator.cpp | 2 +- libraries/wallet/wallet.cpp | 10 +- tests/cli/main.cpp | 4 +- tests/common/database_fixture.cpp | 1 - tests/tests/htlc_tests.cpp | 6 +- 15 files changed, 75 insertions(+), 155 deletions(-) delete mode 100644 libraries/chain/hardfork.d/HTLC.hf create mode 100644 libraries/chain/hardfork.d/core-1468.hf diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6e3115b674..c89971a728 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -168,9 +168,6 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - // Escrow / HTLC - fc::optional get_htlc(const std::string htlc_id) const; - //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); @@ -420,10 +417,6 @@ void database_api_impl::cancel_all_subscriptions( bool reset_callback, bool rese // // ////////////////////////////////////////////////////////////////////// -optional database_api::get_htlc(const std::string& htlc_id) const -{ - return my->get_htlc( htlc_id ); -} optional database_api::get_block_header(uint32_t block_num)const { return my->get_block_header( block_num ); @@ -2296,44 +2289,6 @@ vector database_api_impl::get_withdraw_permissions_b return result; } -////////////////////////////////////////////////////////////////////// -// // -// Escrow / HTLC // -// // -////////////////////////////////////////////////////////////////////// -htlc_id_type htlc_string_to_id(std::string incoming) -{ - boost::replace_all(incoming, "\"", ""); - boost::replace_all(incoming, ".", ","); - std::stringstream ss(incoming); - htlc_id_type id; - int i; - int pos = 0; - while(ss >> i) - { - if (pos == 0 && i != id.space_id) - FC_ASSERT("Invalid HTLC ID"); - if (pos == 1 && i != id.type_id) - FC_ASSERT("Invalid HTLC ID"); - if (pos == 2) - id.instance = i; - pos++; - if (ss.peek() == ',') - ss.ignore(); - } - return id; -} - -fc::optional database_api_impl::get_htlc(const std::string htlc_id) const -{ - htlc_id_type id = htlc_string_to_id(htlc_id); - const auto& htlc_idx = _db.get_index_type().indices().get(); - auto itr = htlc_idx.find(id); - if (itr == htlc_idx.end()) - return {}; - return (*itr); -} - ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 2bfd27c6d7..506b427597 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -267,13 +267,6 @@ class database_api */ account_id_type get_account_id_from_string(const std::string& name_or_id) const; - /** - * @brief Get HTLC object from an ID - * @param htlc_id the id of the HTLC - * @return the HTLC object - */ - fc::optional get_htlc( const std::string& htlc_id ) const; - /** * @brief Get a list of accounts by ID * @param account_names_or_ids IDs or names of the accounts to retrieve @@ -798,9 +791,6 @@ FC_API(graphene::app::database_api, (lookup_asset_symbols) (get_asset_count) - // HTLC / Escrow - (get_htlc) - // Markets / feeds (get_order_book) (get_limit_orders) diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 62e658303d..9a258e6543 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -78,7 +78,7 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con { try { FC_ASSERT(trx_state->_is_proposed_trx); - FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), + FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC parameters until hardfork." ); return void_result(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index f379116c9e..3be760bba4 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -422,9 +422,13 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } case impl_htlc_object_type: - break; - + } case impl_htlc_object_type: { + const auto& htlc_obj = dynamic_cast(obj); + FC_ASSERT( htlc_obj != nullptr); + accounts.insert( htlc_obj->from ); + accounts.insert( htlc_obj->to ); + break; + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 1acfc2f9d8..b5f55fbb66 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -561,20 +561,19 @@ void database::update_withdraw_permissions() void database::clear_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); - while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() + while ( htlc_idx.begin() != htlc_idx.end() && htlc_idx.begin()->expiration <= head_block_time() ) { const htlc_object& obj = *htlc_idx.begin(); adjust_balance( obj.from, obj.amount ); // virtual op - htlc_refund_operation vop(obj.id, obj.from); + htlc_refund_operation vop( obj.id, obj.from ); vop.htlc_id = htlc_idx.begin()->id; - push_applied_operation(vop); + push_applied_operation( vop ); // remove the db object - remove( *htlc_idx.begin()); + remove( *htlc_idx.begin() ); } - } } } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf deleted file mode 100644 index 2ab8857d78..0000000000 --- a/libraries/chain/hardfork.d/HTLC.hf +++ /dev/null @@ -1,4 +0,0 @@ -// HTLC implementation -#ifndef HARDFORK_HTLC_TIME -#define HARDFORK_HTLC_TIME (fc::time_point_sec( 1600000000 ) ) -#endif diff --git a/libraries/chain/hardfork.d/core-1468.hf b/libraries/chain/hardfork.d/core-1468.hf new file mode 100644 index 0000000000..a669d57c84 --- /dev/null +++ b/libraries/chain/hardfork.d/core-1468.hf @@ -0,0 +1,4 @@ +// HTLC implementation +#ifndef HARDFORK_CORE_1468_TIME +#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1600000000 ) ) +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 15eb029bd4..62abffa339 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -37,41 +37,35 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, - // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME + //FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME, + // "Operation not allowed before HARDFORK_CORE_1468_TIME."); // remove after HARDFORK_ESCROW_TIME optional htlc_options = get_committee_htlc_options(db()); FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.seconds_in_force < htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.seconds_in_force <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.key_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); - // make sure sender has the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); - // make sure sender has the funds for the fee - FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee, "Insufficient funds" ); return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) { try { - //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, - // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + graphene::chain::database& dbase = db(); + dbase.adjust_balance( o.source, -o.amount ); - db().adjust_balance( o.source, -o.amount ); - - const htlc_object& esc = db().create([&]( htlc_object& esc ) { + const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { esc.from = o.source; esc.to = o.destination; esc.amount = o.amount; esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; - esc.expiration = db().head_block_time() + o.seconds_in_force; + esc.expiration = dbase.head_block_time() + o.seconds_in_force; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -82,86 +76,62 @@ namespace graphene { template bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - // convert incoming_preimage to an array - unsigned char incoming_array[incoming_preimage.size()]; - for(unsigned i = 0; i < incoming_preimage.size(); ++i) - incoming_array[i] = incoming_preimage[i]; - T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); + T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) return false; - char* data = attempted_hash.data(); - for(size_t i = 0; i < attempted_hash.data_size(); ++i) - { - if ( ((unsigned char)data[i]) != valid_hash[i]) - return false; - } - return true; + return memcmp(attempted_hash.data(), valid_hash.data(), attempted_hash.data_size()) == 0; } void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { htlc_obj = &db().get(o.htlc_id); - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); // see if the preimages match bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - - FC_ASSERT(match, "Provided preimage does not generate correct hash."); - return void_result(); + switch(htlc_obj->preimage_hash_algorithm) + { + case (graphene::chain::hash_algorithm::sha256): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + case (graphene::chain::hash_algorithm::ripemd160): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + case (graphene::chain::hash_algorithm::sha1): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + default: + break; + } + + FC_ASSERT(match, "Provided preimage does not generate correct hash."); + return void_result(); } void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - return void_result(); + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); + return void_result(); } void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) { - htlc_obj = &db().get(o.htlc_id); - - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); - - return void_result(); + htlc_obj = &db().get(o.htlc_id); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); + return void_result(); } void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) { db().modify(*htlc_obj, [&o](htlc_object& db_obj) { - db_obj.expiration = fc::time_point_sec(db_obj.expiration.sec_since_epoch() + o.seconds_to_add); + db_obj.expiration += o.seconds_to_add; }); return void_result(); } - - - - - - - - - - - - - - - - - - - - } // namespace chain } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 89366791e4..6b8b0389c3 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -55,7 +55,6 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - asset pending_fee; vector preimage_hash; fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; @@ -68,13 +67,15 @@ namespace graphene { namespace chain { indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag< by_expiration >, member< htlc_object, - fc::time_point_sec, &htlc_object::expiration > >, + ordered_unique< tag< by_expiration >, + composite_key< htlc_object, + member< htlc_object, fc::time_point_sec, &htlc_object::expiration >, + member< object, object_id_type, &object::id > > >, - ordered_non_unique< tag< by_from_id >, - composite_key< htlc_object, member< htlc_object, account_id_type, &htlc_object::from > - > - > + ordered_unique< tag< by_from_id >, + composite_key< htlc_object, + member< htlc_object, account_id_type, &htlc_object::from >, + member< object, object_id_type, &object::id > > > > > htlc_object_index_type; @@ -98,5 +99,5 @@ namespace fc FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), - (from)(to)(amount)(expiration)(pending_fee) + (from)(to)(amount)(expiration) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 4d0863f8b5..dba8281305 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -88,7 +88,9 @@ FC_REFLECT( graphene::chain::htlc_options, (max_preimage_size) ) -FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) +FC_REFLECT( graphene::chain::chain_parameters::ext, + (updatable_htlc_options) +) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 23c83199e2..f60eb0e740 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -146,7 +146,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati operation_get_required_authorities(op.op, auths, auths, other); // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. FC_ASSERT( op.op.which() != operation::tag::value - || d.head_block_time() >= HARDFORK_HTLC_TIME + || d.head_block_time() >= HARDFORK_CORE_1468_TIME || !op.op.get().new_parameters.extensions .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 185b11f08e..9d9ecb65b9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -652,9 +652,11 @@ class wallet_api_impl htlc_object get_htlc(string htlc_id) const { - auto obj = _remote_db->get_htlc(htlc_id); - FC_ASSERT(obj, "HTLC not found"); - return *obj; + htlc_id_type id; + fc::from_variant(htlc_id, id); + auto obj = _remote_db->get_objects( { id }).front(); + htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); + return htlc; } asset_id_type get_asset_id(string asset_symbol_or_id) const @@ -3171,8 +3173,6 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["amount"] = obj.amount.amount.value; ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); - ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; - ret_val["pending_fee_asset"] = (std::string)((graphene::db::object_id_type)obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3ef22603cb..a858c5827a 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -703,7 +703,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // get the ID: htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); - alice_htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + alice_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Alice shares the HTLC ID with Bob. The HTLC ID is: " + alice_htlc_id_as_string); } @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // get the ID: htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); - bob_htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 9bbb22ab51..1214d8be9a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -290,7 +290,6 @@ void database_fixture::verify_asset_supplies( const database& db ) for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) { total_balances[itr->amount.asset_id] += itr->amount.amount; - total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; } for( const asset_object& asset_obj : db.get_index_type().indices() ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 9414ef1ce3..1b0ff56f59 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -95,7 +95,7 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_HTLC_TIME); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -191,8 +191,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); - optional htlc = db_api.get_htlc(fc::json::to_pretty_string(alice_htlc_id)); - BOOST_CHECK(htlc); + auto obj = db_api.get_objects( {alice_htlc_id }).front(); + graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) generate_blocks(fc::time_point_sec(120) ); From dd6c7fc4e7bf09b63b67c7cad095faaf9a701ba6 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sat, 15 Dec 2018 21:24:45 +0100 Subject: [PATCH 094/132] Properly initialize inner vectors, support nested modifications --- libraries/db/include/graphene/db/index.hpp | 14 +++++++++----- tests/tests/database_tests.cpp | 10 ++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index babfbda91f..3687b5d803 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -23,11 +23,14 @@ */ #pragma once #include + #include #include #include #include + #include +#include namespace graphene { namespace db { class object_database; @@ -209,7 +212,7 @@ namespace graphene { namespace db { static const size_t _mask = ((1 << chunkbits) - 1); size_t next = 0; vector< vector< const Object* > > content; - object_id_type id_being_modified; + std::stack< object_id_type > ids_being_modified; public: direct_index() { @@ -226,7 +229,7 @@ namespace graphene { namespace db { if( !(next & _mask) ) { content.resize((next >> chunkbits) + 1); - content[next >> chunkbits].reserve( 1 << chunkbits ); + content[next >> chunkbits].resize( 1 << chunkbits, nullptr ); } next++; } @@ -238,7 +241,7 @@ namespace graphene { namespace db { if( !(next & _mask) || (next & (~_mask)) != (instance & (~_mask)) ) { content.resize((instance >> chunkbits) + 1); - content[instance >> chunkbits].reserve( 1 << chunkbits ); + content[instance >> chunkbits].resize( 1 << chunkbits, nullptr ); } while( next <= instance ) { @@ -261,12 +264,13 @@ namespace graphene { namespace db { virtual void about_to_modify( const object& before ) { - id_being_modified = before.id; + ids_being_modified.emplace( before.id ); } virtual void object_modified( const object& after ) { - FC_ASSERT( id_being_modified == after.id, "Modification of ID is not supported!"); + FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!"); + ids_being_modified.pop(); } template< typename object_id > diff --git a/tests/tests/database_tests.cpp b/tests/tests/database_tests.cpp index e8c1d24fe2..6158c2eb05 100644 --- a/tests/tests/database_tests.cpp +++ b/tests/tests/database_tests.cpp @@ -182,6 +182,16 @@ BOOST_AUTO_TEST_CASE( direct_index_test ) test_account.name = "account50"; my_accounts.load( fc::raw::pack( test_account ) ); + // can handle nested modification + my_accounts.modify( direct.get( account_id_type(0) ), [&direct,&my_accounts] ( object& outer ) { + account_object& _outer = dynamic_cast< account_object& >( outer ); + my_accounts.modify( direct.get( account_id_type(50) ), [] ( object& inner ) { + account_object& _inner = dynamic_cast< account_object& >( inner ); + _inner.referrer = account_id_type(102); + }); + _outer.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + }); + // direct.next is still 103, so 204 is not allowed test_account.id = account_id_type(204); test_account.name = "account204"; From 69c4011cf483819f0fc4cd31c34dc21bbb68c66b Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sat, 15 Dec 2018 21:26:23 +0100 Subject: [PATCH 095/132] Removed obsolete hardfork code to avoid problems with next commit --- libraries/chain/db_block.cpp | 8 -------- tests/tests/block_tests.cpp | 5 ----- 2 files changed, 13 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..629c9b8605 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -683,14 +683,6 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx } ptrx.operation_results = std::move(eval_state.operation_results); - if( head_block_time() < HARDFORK_CORE_1040_TIME ) // TODO totally remove this code block after hard fork - { - //Make sure the temp account has no non-zero balances - const auto& index = get_index_type().indices().get(); - auto range = index.equal_range( boost::make_tuple( GRAPHENE_TEMP_ACCOUNT ) ); - std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); }); - } - return ptrx; } FC_CAPTURE_AND_RETHROW( (trx) ) } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index e71642726b..8af8e8777c 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -1830,11 +1830,6 @@ BOOST_FIXTURE_TEST_CASE( temp_account_balance, database_fixture ) top.to = GRAPHENE_COMMITTEE_ACCOUNT; trx.operations.push_back( top ); - sign( trx, alice_private_key ); - BOOST_CHECK_THROW( PUSH_TX( db, trx ), fc::assert_exception ); - - generate_blocks( HARDFORK_CORE_1040_TIME ); - set_expiration( db, trx ); trx.clear_signatures(); sign( trx, alice_private_key ); From 2409104bb075a9a49e261185fb3858243fb7b237 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sat, 15 Dec 2018 21:28:05 +0100 Subject: [PATCH 096/132] Added direct_index-like replacement for by_account_asset subindex --- libraries/app/database_api.cpp | 16 +++--- libraries/chain/account_object.cpp | 50 +++++++++++++++++++ libraries/chain/db_balance.cpp | 19 +++---- libraries/chain/db_init.cpp | 5 +- libraries/chain/db_maint.cpp | 12 ++--- .../include/graphene/chain/account_object.hpp | 32 +++++++++--- 6 files changed, 98 insertions(+), 36 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 9316cfa4b6..9a86afba4a 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -799,11 +799,9 @@ std::map database_api_impl::get_full_accounts( const // Add the account's balances - auto balance_range = _db.get_index_type().indices().get().equal_range(boost::make_tuple(account->id)); - std::for_each(balance_range.first, balance_range.second, - [&acnt](const account_balance_object& balance) { - acnt.balances.emplace_back(balance); - }); + const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >().get_account_balances( account->id ); + for( const auto balance : balances ) + acnt.balances.emplace_back( *balance.second ); // Add the account's vesting balances auto vesting_range = _db.get_index_type().indices().get().equal_range(account->id); @@ -955,10 +953,10 @@ vector database_api_impl::get_account_balances(const std::string& account if (assets.empty()) { // if the caller passes in an empty list of assets, return balances for all assets the account owns - const account_balance_index& balance_index = _db.get_index_type(); - auto range = balance_index.indices().get().equal_range(boost::make_tuple(acnt)); - for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second)) - result.push_back(asset(balance.get_balance())); + const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >(); + const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( acnt ); + for( const auto balance : balances ) + result.push_back( balance.second->get_balance() ); } else { diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 952a4c7c2c..466f7a6fc9 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -269,4 +269,54 @@ void account_referrer_index::object_modified( const object& after ) { } +const uint8_t balances_by_account_index::bits = 20; +const uint64_t balances_by_account_index::mask = (1ULL << balances_by_account_index::bits) - 1; + +void balances_by_account_index::object_inserted( const object& obj ) +{ + const auto& abo = dynamic_cast< const account_balance_object& >( obj ); + while( balances.size() < (abo.owner.instance.value >> bits) + 1 ) + { + balances.reserve( (abo.owner.instance.value >> bits) + 1 ); + balances.resize( balances.size() + 1 ); + balances.back().resize( 1ULL << bits ); + } + balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask][abo.asset_type] = &abo; +} + +void balances_by_account_index::object_removed( const object& obj ) +{ + const auto& abo = dynamic_cast< const account_balance_object& >( obj ); + if( balances.size() < (abo.owner.instance.value >> bits) + 1 ) return; + balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask].erase( abo.asset_type ); +} + +void balances_by_account_index::about_to_modify( const object& before ) +{ + ids_being_modified.emplace( before.id ); +} + +void balances_by_account_index::object_modified( const object& after ) +{ + FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!"); + ids_being_modified.pop(); +} + +const map< asset_id_type, const account_balance_object* >& balances_by_account_index::get_account_balances( const account_id_type& acct )const +{ + static const map< asset_id_type, const account_balance_object* > _empty; + + if( balances.size() < (acct.instance.value >> bits) + 1 ) return _empty; + return balances[acct.instance.value >> bits][acct.instance.value & mask]; +} + +const account_balance_object* balances_by_account_index::get_account_balance( const account_id_type& acct, const asset_id_type& asset )const +{ + if( balances.size() < (acct.instance.value >> bits) + 1 ) return nullptr; + const auto& mine = balances[acct.instance.value >> bits][acct.instance.value & mask]; + const auto itr = mine.find( asset ); + if( mine.end() == itr ) return nullptr; + return itr->second; +} + } } // graphene::chain diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index cf58cb432d..caa1eff63b 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -33,11 +33,11 @@ namespace graphene { namespace chain { asset database::get_balance(account_id_type owner, asset_id_type asset_id) const { - auto& index = get_index_type().indices().get(); - auto itr = index.find(boost::make_tuple(owner, asset_id)); - if( itr == index.end() ) + auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index(); + auto abo = index.get_account_balance( owner, asset_id ); + if( !abo ) return asset(0, asset_id); - return itr->get_balance(); + return abo->get_balance(); } asset database::get_balance(const account_object& owner, const asset_object& asset_obj) const @@ -55,9 +55,9 @@ void database::adjust_balance(account_id_type account, asset delta ) if( delta.amount == 0 ) return; - auto& index = get_index_type().indices().get(); - auto itr = index.find(boost::make_tuple(account, delta.asset_id)); - if(itr == index.end()) + auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index(); + auto abo = index.get_account_balance( account, delta.asset_id ); + if( !abo ) { FC_ASSERT( delta.amount > 0, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name) @@ -72,8 +72,9 @@ void database::adjust_balance(account_id_type account, asset delta ) }); } else { if( delta.amount < 0 ) - FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta))); - modify(*itr, [delta](account_balance_object& b) { + FC_ASSERT( abo->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", + ("a",account(*this).name)("b",to_pretty_string(abo->get_balance()))("r",to_pretty_string(-delta))); + modify(*abo, [delta](account_balance_object& b) { b.adjust_balance(delta); }); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c87d9ec6a6..4ecd578fba 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -205,7 +205,10 @@ void database::initialize_indexes() //Implementation object indexes add_index< primary_index >(); - add_index< primary_index >(); + + auto bal_idx = add_index< primary_index >(); + bal_idx->add_secondary_index(); + add_index< primary_index >(); // 8192 add_index< primary_index> >(); add_index< primary_index> >(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index d011740a6c..4ee7cb2825 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -693,7 +693,7 @@ void distribute_fba_balances( database& db ) void create_buyback_orders( database& db ) { const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get(); - const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_account_asset >(); + const auto& bal_idx = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >(); for( const buyback_object& bbo : bbo_idx ) { @@ -701,7 +701,6 @@ void create_buyback_orders( database& db ) assert( asset_to_buy.buyback_account.valid() ); const account_object& buyback_account = (*(asset_to_buy.buyback_account))(db); - asset_id_type next_asset = asset_id_type(); if( !buyback_account.allowed_assets.valid() ) { @@ -709,16 +708,11 @@ void create_buyback_orders( database& db ) continue; } - while( true ) + for( const auto& entry : bal_idx.get_account_balances( buyback_account.id ) ) { - auto it = bal_idx.lower_bound( boost::make_tuple( buyback_account.id, next_asset ) ); - if( it == bal_idx.end() ) - break; - if( it->owner != buyback_account.id ) - break; + const auto* it = entry.second; asset_id_type asset_to_sell = it->asset_type; share_type amount_to_sell = it->balance; - next_asset = asset_to_sell + 1; if( asset_to_sell == asset_to_buy.id ) continue; if( amount_to_sell == 0 ) diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index cae9d35984..1c4526fde0 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -341,7 +341,30 @@ namespace graphene { namespace chain { map< account_id_type, set > referred_by; }; - struct by_account_asset; + /** + * @brief This secondary index will allow fast access to the balance objects + * that belonging to an account. + */ + class balances_by_account_index : public secondary_index + { + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + const map< asset_id_type, const account_balance_object* >& get_account_balances( const account_id_type& acct )const; + const account_balance_object* get_account_balance( const account_id_type& acct, const asset_id_type& asset )const; + + private: + static const uint8_t bits; + static const uint64_t mask; + + /** Maps each account to its balance objects */ + vector< vector< map< asset_id_type, const account_balance_object* > > > balances; + std::stack< object_id_type > ids_being_modified; + }; + struct by_asset_balance; struct by_maintenance_flag; /** @@ -353,13 +376,6 @@ namespace graphene { namespace chain { ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_non_unique< tag, member< account_balance_object, bool, &account_balance_object::maintenance_flag > >, - ordered_unique< tag, - composite_key< - account_balance_object, - member, - member - > - >, ordered_unique< tag, composite_key< account_balance_object, From e56389949a7247f86e869097f5774e78ca978be8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 16:17:09 -0500 Subject: [PATCH 097/132] cleanup, consistent variable naming --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/htlc_evaluator.cpp | 14 ++-- libraries/chain/htlc_object.cpp | 45 ++++++++++ .../include/graphene/chain/htlc_evaluator.hpp | 2 +- .../include/graphene/chain/htlc_object.hpp | 15 +++- .../graphene/chain/protocol/fee_schedule.hpp | 2 +- .../include/graphene/chain/protocol/htlc.hpp | 65 +++------------ libraries/chain/protocol/htlc.cpp | 27 ------ .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 32 +++---- tests/tests/htlc_tests.cpp | 83 +++++++++---------- 11 files changed, 137 insertions(+), 153 deletions(-) create mode 100644 libraries/chain/htlc_object.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 71d2fd2d2e..e6551c1189 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -78,6 +78,7 @@ add_library( graphene_chain withdraw_permission_evaluator.cpp worker_evaluator.cpp htlc_evaluator.cpp + htlc_object.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 62abffa339..ef94c644d0 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -45,11 +45,13 @@ namespace graphene { FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.seconds_in_force <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); + // make sure the sender has the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); } @@ -63,9 +65,9 @@ namespace graphene { esc.from = o.source; esc.to = o.destination; esc.amount = o.amount; - esc.preimage_hash = o.key_hash; - esc.preimage_size = o.key_size; - esc.expiration = dbase.head_block_time() + o.seconds_in_force; + esc.preimage_hash = o.preimage_hash; + esc.preimage_size = o.preimage_size; + esc.expiration = dbase.head_block_time() + o.claim_period_seconds; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -74,7 +76,7 @@ namespace graphene { } template - bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp new file mode 100644 index 0000000000..f7f4c93b21 --- /dev/null +++ b/libraries/chain/htlc_object.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Bitshares Foundation, and contributors. + * + * 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 + +namespace graphene { namespace chain { + + /**** + * Convert string to hash algorithm enum + * @param incoming the string to convert + * @returns one of the valid algorithms or the enum value "unknown" + */ + fc::enum_type string_to_hash_algorithm(std::string incoming) + { + std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); + if (incoming == "RIPEMD160") + return hash_algorithm::ripemd160; + if (incoming == "SHA256") + return hash_algorithm::sha256; + if (incoming == "SHA1") + return hash_algorithm::sha1; + return hash_algorithm::unknown; + } + +} } diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 7c6b641db5..c93b579742 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -46,7 +46,7 @@ namespace graphene { const htlc_object* htlc_obj = nullptr; private: template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; class htlc_extend_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 6b8b0389c3..01a5510467 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -39,6 +39,13 @@ namespace graphene { namespace chain { sha1 = 0x03 }; + /**** + * Convert string to hash algorithm enum + * @param incoming the string to convert + * @returns one of the valid algorithms or the enum value "unknown" + */ + fc::enum_type string_to_hash_algorithm(std::string incoming); + /** * @brief database object to store HTLCs * @@ -55,7 +62,7 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - vector preimage_hash; + vector preimage_hash; fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; }; @@ -87,11 +94,11 @@ namespace graphene { namespace chain { namespace fc { template<> - struct get_typename> + struct get_typename> { static const char* name() { - return "fc::enum_type"; + return "fc::enum_type"; } }; } @@ -100,4 +107,4 @@ FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(s FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) - (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file + (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 3a054cf2ec..0b7e8ab457 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,7 +43,7 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } - typename Operation::fee_parameters_type& get(flat_set& parameters) + typename Operation::fee_parameters_type& get(flat_set& parameters)const { auto itr = parameters.find( typename Operation::fee_parameters_type() ); FC_ASSERT( itr != parameters.end() ); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index d0f5258066..f8ca2c393d 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,21 +31,6 @@ namespace graphene { namespace chain { - /** - * Convert the hash algorithm to a string - * @param algo the enum to convert - * @returns a string (lower case) - */ - std::string hash_algorithm_to_string(fc::enum_type algo); - - /** - * Convert a string to the enum that matches the hash algorithm - * @param incoing the string (case insensitive) - * @returns the matching enum - */ - fc::enum_type string_to_hash_algorithm(std::string incoming); - - struct htlc_create_operation : public base_operation { struct fee_parameters_type { @@ -60,15 +45,15 @@ namespace graphene { account_id_type destination; // the amount to hold asset amount; - // hash algorithm used to create key_hash + // hash algorithm used to create preimage_hash fc::enum_type hash_type = graphene::chain::hash_algorithm::unknown; // the hash of the preimage - std::vector key_hash; + std::vector preimage_hash; // the size of the preimage - uint16_t key_size; + uint16_t preimage_size; // The time the funds will be returned to the source if not claimed - uint32_t seconds_in_force; + uint32_t claim_period_seconds; // for future expansion extensions_type extensions; @@ -77,14 +62,6 @@ namespace graphene { */ void validate()const; - /** - * @brief Determines who is required to sign - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(source); - } - /** * @brief who will pay the fee */ @@ -95,7 +72,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = seconds_in_force / (60 * 60 * 24); + uint32_t days = claim_period_seconds / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -104,7 +81,7 @@ namespace graphene { struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network @@ -112,9 +89,9 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type update_issuer; + account_id_type redeemer; // the preimage (not used if after epoch timeout) - std::vector preimage; + std::vector preimage; // for future expansion extensions_type extensions; @@ -123,25 +100,17 @@ namespace graphene { */ void validate()const; - /*** - * @determines who should have signed this object - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(update_issuer); - } - /** * @brief Who is to pay the fee */ - account_id_type fee_payer()const { return update_issuer; } + account_id_type fee_payer()const { return redeemer; } /**** * @brief calculates the fee to be paid for this operation */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - return fee_params.fee; + return preimage.size() / 1024 * fee_params.fee_per_kb; } }; @@ -168,14 +137,6 @@ namespace graphene { */ void validate()const; - /*** - * @determines who should have signed this object - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(update_issuer); - } - /** * @brief Who is to pay the fee */ @@ -213,12 +174,12 @@ namespace graphene { } FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) -FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee_per_kb) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) -FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) + (fee)(source)(destination)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 0f3e61bd95..a83f42302f 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -39,31 +39,4 @@ namespace graphene { namespace chain { FC_ASSERT( fee.amount >= 0 ); } - std::string hash_algorithm_to_string(fc::enum_type algo) - { - switch(algo) - { - case (hash_algorithm::ripemd160): - return "ripemd160"; - case (hash_algorithm::sha256): - return "sha256"; - case (hash_algorithm::sha1): - return "sha1"; - default: - break; - } - return "unknown"; - } - - fc::enum_type string_to_hash_algorithm(std::string incoming) - { - std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); - if (incoming == "RIPEMD160") - return hash_algorithm::ripemd160; - if (incoming == "SHA256") - return hash_algorithm::sha256; - if (incoming == "SHA1") - return hash_algorithm::sha1; - return hash_algorithm::unknown; - } } } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7b087dab61..a934d19644 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1465,12 +1465,12 @@ class wallet_api * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param seconds_in_force when the time lock expires + * @param claim_period_seconds when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast = false ); + const uint32_t claim_period_seconds, bool broadcast = false ); /**** * Update a hashed time lock contract diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9d9ecb65b9..4477bc044f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1750,8 +1750,8 @@ class wallet_api_impl } signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast = false ) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const uint32_t claim_period_seconds, bool broadcast = false ) { try { @@ -1763,9 +1763,9 @@ class wallet_api_impl create_op.source = get_account(source).id; create_op.destination = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); - create_op.seconds_in_force = seconds_in_force; - create_op.key_hash = preimage_hash; - create_op.key_size = preimage_size; + create_op.claim_period_seconds = claim_period_seconds; + create_op.preimage_hash = preimage_hash; + create_op.preimage_size = preimage_size; create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); @@ -1777,10 +1777,10 @@ class wallet_api_impl return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) - (preimage_hash)(preimage_size)(seconds_in_force)(broadcast) ) + (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) ) } - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -1792,7 +1792,7 @@ class wallet_api_impl htlc_redeem_operation update_op; update_op.htlc_id = htlc_obj->id; - update_op.update_issuer = issuer_obj.id; + update_op.redeemer = issuer_obj.id; update_op.preimage = preimage; signed_transaction tx; @@ -2998,7 +2998,7 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with id " << database_id << " preimage hash: ["; - for(unsigned char c : op.key_hash) + for(uint8_t c : op.preimage_hash) { out << setfill('0') << std::setw(2) << std::hex << (int)c; } @@ -3141,11 +3141,11 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -std::vector string_to_vec(std::string incoming) +std::vector string_to_vec(std::string incoming) { char s[3]; s[2] = 0; - std::vector vec; + std::vector vec; for(int i = 0; i < incoming.length(); i+= 2) { s[0] = incoming[i]; @@ -3157,10 +3157,10 @@ std::vector string_to_vec(std::string incoming) signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast) + const uint32_t claim_period_seconds, bool broadcast) { return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, - seconds_in_force, broadcast); + claim_period_seconds, broadcast); } variant wallet_api::get_htlc(std::string htlc_id) const @@ -3174,10 +3174,10 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); std::stringstream hash_string; - for(unsigned char c : obj.preimage_hash) + for(uint8_t c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; ret_val["preimage_hash"] = hash_string.str(); - ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); + ret_val["preimage_algorithm"] = (std::string)obj.preimage_hash_algorithm; ret_val["preimage_size"] = obj.preimage_size; return ret_val; } @@ -3186,7 +3186,7 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss bool broadcast) { - return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 1b0ff56f59..c5b09e6a92 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -55,27 +55,22 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) -void generate_random_preimage(uint16_t key_size, std::vector& vec) +void generate_random_preimage(uint16_t key_size, std::vector& vec) { - std::independent_bits_engine rbe; + std::independent_bits_engine rbe; std::generate(begin(vec), end(vec), std::ref(rbe)); return; } -std::vector hash_it(std::vector preimage) +/**** + * Hash the preimage and put it in a vector + * @param preimage the preimage + * @returns a vector that cointains the sha256 hash of the preimage + */ +std::vector hash_it(std::vector preimage) { - // convert the preimage to a char array - unsigned char char_array[preimage.size()]; - for(unsigned int i = 0; i < preimage.size(); ++i) - char_array[i] = preimage[i]; - fc::sha256 hash = fc::sha256::hash((char*)char_array, preimage.size()); - std::vector ret_val(hash.data_size()); - char* data = hash.data(); - for(size_t i = 0; i < hash.data_size(); i++) - { - ret_val[i] = data[i]; - } - return ret_val; + fc::sha256 hash = fc::sha256::hash((char*)preimage.data(), preimage.size()); + return std::vector(hash.data(), hash.data() + hash.data_size()); } void set_committee_parameters(database_fixture* db_fixture) @@ -108,10 +103,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -123,10 +118,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 60; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); @@ -155,10 +150,10 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -170,10 +165,10 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 60; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); @@ -212,10 +207,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; - std::vector pre_image(key_size); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + uint16_t preimage_size = 256; + std::vector pre_image(preimage_size); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -228,10 +223,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 86400; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 86400; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); @@ -252,7 +247,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // send an update operation to claim the funds { graphene::chain::htlc_redeem_operation update_operation; - update_operation.update_issuer = bob_id; + update_operation.redeemer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); @@ -284,10 +279,10 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -298,9 +293,9 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 3; - create_operation.key_hash = key_hash; - create_operation.key_size = key_size; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = preimage_hash; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); @@ -312,10 +307,10 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 3; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); From 62395643e0d7b446dba5ec9da68e56c91345596e Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 17:27:18 -0500 Subject: [PATCH 098/132] More consistent naming --- libraries/chain/htlc_evaluator.cpp | 43 +++++++++---------- .../include/graphene/chain/protocol/htlc.hpp | 8 ++-- libraries/chain/protocol/htlc.cpp | 1 - .../wallet/include/graphene/wallet/wallet.hpp | 12 +++--- libraries/wallet/wallet.cpp | 27 +++++------- tests/tests/htlc_tests.cpp | 24 +++++------ 6 files changed, 53 insertions(+), 62 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index ef94c644d0..6eb4a33a0a 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -51,28 +51,28 @@ namespace graphene { // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) { - try { - graphene::chain::database& dbase = db(); - dbase.adjust_balance( o.source, -o.amount ); - - const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { - esc.from = o.source; - esc.to = o.destination; - esc.amount = o.amount; - esc.preimage_hash = o.preimage_hash; - esc.preimage_size = o.preimage_size; - esc.expiration = dbase.head_block_time() + o.claim_period_seconds; - esc.preimage_hash_algorithm = o.hash_type; - }); - return esc.id; - - } FC_CAPTURE_AND_RETHROW( (o) ) + try { + graphene::chain::database& dbase = db(); + dbase.adjust_balance( o.from, -o.amount ); + + const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { + esc.from = o.from; + esc.to = o.to; + esc.amount = o.amount; + esc.preimage_hash = o.preimage_hash; + esc.preimage_size = o.preimage_size; + esc.expiration = dbase.head_block_time() + o.claim_period_seconds; + esc.preimage_hash_algorithm = o.hash_type; + }); + return esc.id; + + } FC_CAPTURE_AND_RETHROW( (o) ) } template @@ -86,12 +86,12 @@ namespace graphene { void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { - htlc_obj = &db().get(o.htlc_id); + htlc_obj = &db().get(o.htlc_id); FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); + FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); - // see if the preimages match + // see if the preimages match bool match = false; switch(htlc_obj->preimage_hash_algorithm) { @@ -122,7 +122,6 @@ namespace graphene { void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) { htlc_obj = &db().get(o.htlc_id); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); return void_result(); } @@ -132,7 +131,7 @@ namespace graphene { db_obj.expiration += o.seconds_to_add; }); - return void_result(); + return void_result(); } } // namespace chain diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f8ca2c393d..7a7469eb2e 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -40,9 +40,9 @@ namespace graphene { // paid to network asset fee; // where the held monies are to come from - account_id_type source; + account_id_type from; // where the held monies will go if the preimage is provided - account_id_type destination; + account_id_type to; // the amount to hold asset amount; // hash algorithm used to create preimage_hash @@ -65,7 +65,7 @@ namespace graphene { /** * @brief who will pay the fee */ - account_id_type fee_payer()const { return source; } + account_id_type fee_payer()const { return from; } /**** * @brief calculates the fee to be paid for this operation @@ -179,7 +179,7 @@ FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) ( FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) + (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index a83f42302f..56e4cb8d06 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -26,7 +26,6 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( source != destination ); FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index a934d19644..d0b80cceca 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1462,10 +1462,10 @@ class wallet_api * @param destination The account that will receive the funds if the preimage is presented * @param amount the amount of the asset that is to be traded * @param asset_symbol The asset that is to be traded - * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. + * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160, SHA1 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param claim_period_seconds when the time lock expires + * @param claim_period_seconds how long after creation until the lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, @@ -1483,14 +1483,14 @@ class wallet_api bool broadcast = false ); /***** - * Set a new timelock on an existing HTLC + * Increase the timelock on an existing HTLC * * @param htlc_id The object identifier of the HTLC on the blockchain * @param issuer Who is performing this operation (and paying the fee) - * @param timelock the new time of expiry + * @param seconds_to_add how many seconds to add to the existing timelock * @param broadcast true to broadcast to the network */ - signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const uint32_t seconds_to_add, + signed_transaction htlc_extend(string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast = false); /** @@ -1846,7 +1846,7 @@ FC_API( graphene::wallet::wallet_api, (update_worker_votes) (htlc_prepare) (htlc_redeem) - (htlc_extend_expiry) + (htlc_extend) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 4477bc044f..0e9d1d1997 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1760,8 +1760,8 @@ class wallet_api_impl FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); htlc_create_operation create_op; - create_op.source = get_account(source).id; - create_op.destination = get_account(destination).id; + create_op.from = get_account(source).id; + create_op.to = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); create_op.claim_period_seconds = claim_period_seconds; create_op.preimage_hash = preimage_hash; @@ -1804,7 +1804,7 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } - signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) + signed_transaction htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) { try { @@ -2991,7 +2991,7 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const std::string operation_printer::operator()(const htlc_create_operation& op) const { auto fee_asset = wallet.get_asset( op.fee.asset_id ); - auto to = wallet.get_account( op.destination ); + auto to = wallet.get_account( op.to ); operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); @@ -3143,16 +3143,11 @@ uint64_t wallet_api::get_asset_count()const std::vector string_to_vec(std::string incoming) { - char s[3]; - s[2] = 0; - std::vector vec; - for(int i = 0; i < incoming.length(); i+= 2) - { - s[0] = incoming[i]; - s[1] = incoming[i+1]; - vec.push_back( (int)strtol(&s[0], nullptr, 16)); - } - return vec; + FC_ASSERT(incoming.size() % 2 == 0, "Invalid incoming hash."); + std::size_t outgoing_size = incoming.size() / 2; + std::vector outgoing(outgoing_size); + fc::from_hex(incoming, (char*)outgoing.data(), outgoing_size); + return outgoing; } signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, @@ -3189,10 +3184,10 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } -signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, +signed_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, bool broadcast) { - return my->htlc_extend_expiry(htlc_id, issuer, seconds_to_add, broadcast); + return my->htlc_extend(htlc_id, issuer, seconds_to_add, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index c5b09e6a92..da4ec86224 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,12 +117,12 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); @@ -164,18 +164,17 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); - // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); generate_block(); @@ -222,19 +221,18 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 100000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); - // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); } @@ -265,7 +263,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1100000 ); BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); } @@ -292,11 +290,11 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); @@ -306,12 +304,12 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, database::skip_nothing); From e914a372ebda10c55328c8c631f24ac33f7d60b4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 17:52:38 -0500 Subject: [PATCH 099/132] Switch htlc object from implementation to protocol --- libraries/chain/db_notify.cpp | 14 +++++++------- .../chain/include/graphene/chain/htlc_object.hpp | 4 ++-- .../include/graphene/chain/protocol/types.hpp | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 3be760bba4..354505b485 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -360,6 +360,12 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; + } case htlc_object_type: { + const auto& htlc_obj = dynamic_cast(obj); + FC_ASSERT( htlc_obj != nullptr); + accounts.insert( htlc_obj->from ); + accounts.insert( htlc_obj->to ); + break; } } } @@ -422,13 +428,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } case impl_htlc_object_type: { - const auto& htlc_obj = dynamic_cast(obj); - FC_ASSERT( htlc_obj != nullptr); - accounts.insert( htlc_obj->from ); - accounts.insert( htlc_obj->to ); - break; - } + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 01a5510467..7c45d4d636 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -55,8 +55,8 @@ namespace graphene { namespace chain { class htlc_object : public graphene::db::abstract_object { public: // uniquely identify this object in the database - static const uint8_t space_id = implementation_ids; - static const uint8_t type_id = impl_htlc_object_type; + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = htlc_object_type; account_id_type from; account_id_type to; diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 4d04426527..53b0378c65 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -140,6 +140,7 @@ namespace graphene { namespace chain { vesting_balance_object_type, worker_object_type, balance_object_type, + htlc_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -162,8 +163,7 @@ namespace graphene { namespace chain { impl_special_authority_object_type, impl_buyback_object_type, impl_fba_accumulator_object_type, - impl_collateral_bid_object_type, - impl_htlc_object_type, + impl_collateral_bid_object_type }; //typedef fc::unsigned_int object_id_type; @@ -183,6 +183,7 @@ namespace graphene { namespace chain { class worker_object; class balance_object; class blinded_balance_object; + class htlc_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -198,6 +199,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type; + typedef object_id< protocol_ids, htlc_object_type, htlc_object> htlc_id_type; // implementation types class global_property_object; @@ -215,7 +217,6 @@ namespace graphene { namespace chain { class buyback_object; class fba_accumulator_object; class collateral_bid_object; - class htlc_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -236,7 +237,6 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; - typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object> htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; @@ -346,6 +346,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (vesting_balance_object_type) (worker_object_type) (balance_object_type) + (htlc_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -367,7 +368,6 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_buyback_object_type) (impl_fba_accumulator_object_type) (impl_collateral_bid_object_type) - (impl_htlc_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) From c808e61170340fcba6b0b917db14affd0ddfeb4d Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 18:37:08 -0500 Subject: [PATCH 100/132] Move committee parameter hardfork validation to visitor --- libraries/chain/proposal_evaluator.cpp | 8 +++----- libraries/chain/protocol/htlc.cpp | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index f60eb0e740..efab4be33a 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -94,6 +94,9 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!"Virtual operation"); } } + void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { + FC_ASSERT(block_time > HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468"); + } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) @@ -144,11 +147,6 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for( auto& op : o.proposed_ops ) { operation_get_required_authorities(op.op, auths, auths, other); - // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. - FC_ASSERT( op.op.which() != operation::tag::value - || d.head_block_time() >= HARDFORK_CORE_1468_TIME - || !op.op.get().new_parameters.extensions - .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 56e4cb8d06..be444735f6 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,11 +28,13 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); + FC_ASSERT( hash_type == hash_algorithm::ripemd160 + || hash_type == graphene::chain::hash_algorithm::sha1 + || hash_type == hash_algorithm::sha256, "Unknown Hash Algorithm"); } void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( preimage.size() > 0 ); } void htlc_extend_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); From fe4c4e9eefa858ec7c4e784a05ee5b686d50a44c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 Dec 2018 14:27:33 -0300 Subject: [PATCH 101/132] add link to hackthedex.io to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a516790596..e5147f0112 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Visit [BitShares.org](https://bitshares.org/) to learn about BitShares and join Information for developers can be found in the [Bitshares Developer Portal](https://dev.bitshares.works/). Users interested in how bitshares works can go to the [BitShares Documentation](https://how.bitshares.works/) site. +For security issues and bug bounty program please visit [Hack the DEX](https://hackthedex.io). + Getting Started --------------- Build instructions and additional documentation are available in the From fea41d77006512637da6fe7e5796942de3f0b9ed Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Dec 2018 17:13:11 -0500 Subject: [PATCH 102/132] htlc_prepare is now htlc_create, plus start of fee testing (wip) --- libraries/chain/htlc_evaluator.cpp | 3 - .../graphene/chain/protocol/fee_schedule.hpp | 22 +++++++ .../include/graphene/chain/protocol/htlc.hpp | 3 +- libraries/chain/proposal_evaluator.cpp | 5 +- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 6 +- tests/cli/main.cpp | 4 +- tests/tests/htlc_tests.cpp | 58 ++++++++++++++++++- 8 files changed, 92 insertions(+), 13 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 6eb4a33a0a..6087442cb2 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -37,9 +37,6 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME, - // "Operation not allowed before HARDFORK_CORE_1468_TIME."); // remove after HARDFORK_ESCROW_TIME - optional htlc_options = get_committee_htlc_options(db()); FC_ASSERT(htlc_options, "HTLC Committee options are not set."); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 0b7e8ab457..39fa0a390f 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -113,6 +113,28 @@ namespace graphene { namespace chain { } }; + /* + template<> + class fee_helper { + public: + const htlc_create_operation::fee_parameters_type& cget(const flat_set& parameters)const + { + auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + + static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy; + return htlc_create_operation_fee_dummy; + } + typename htlc_create_operation::fee_parameters_type& get(flat_set& parameters)const + { + auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->get(); + } + }; + */ + /** * @brief contains all of the parameters necessary to calculate the fee for any operation */ diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7a7469eb2e..a9428f492c 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -27,6 +27,7 @@ #include #include #include +#include // std::min namespace graphene { namespace chain { @@ -72,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = claim_period_seconds / (60 * 60 * 24); + uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index efab4be33a..ed335dcff5 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -95,7 +95,10 @@ struct proposal_operation_hardfork_visitor } } void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { - FC_ASSERT(block_time > HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468"); + if (block_time < HARDFORK_CORE_1468_TIME) { + FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); + // TODO: Do not allow changing of fees before hardfork + } } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index d0b80cceca..e069fbcdc3 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1468,7 +1468,7 @@ class wallet_api * @param claim_period_seconds how long after creation until the lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ); @@ -1844,7 +1844,7 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) - (htlc_prepare) + (htlc_create) (htlc_redeem) (htlc_extend) (get_vesting_balances) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0e9d1d1997..99445c13e7 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1749,7 +1749,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ) { @@ -3150,11 +3150,11 @@ std::vector string_to_vec(std::string incoming) return outgoing; } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, +signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast) { - return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, claim_period_seconds, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index a858c5827a..7968f75349 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -691,7 +691,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Secret is " + preimage_string + " and hash is " + hash_str); uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx - = con.wallet_api_ptr->htlc_prepare("alice", "bob", + = con.wallet_api_ptr->htlc_create("alice", "bob", "3", "1.3.0", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: @@ -713,7 +713,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC - con.wallet_api_ptr->htlc_prepare("bob", "alice", + con.wallet_api_ptr->htlc_create("bob", "alice", "3", "BOBCOIN", "SHA256", hash_str, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index da4ec86224..81567283b7 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -90,7 +90,7 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -319,6 +319,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) BOOST_AUTO_TEST_CASE( set_htlc_params ) { +try { { // try to set committee parameters before hardfork proposal_create_operation cop = proposal_create_operation::committee_proposal( @@ -348,7 +349,54 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) trx.clear(); } + /* + { + BOOST_TEST_MESSAGE("Attempting to set HTLC fees before hard fork."); + // get existing fees + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // build a map of existing fees + flat_map< int, fee_parameters > fee_map; + fee_map.reserve( existing_fee_schedule.parameters.size() ); + for( const fee_parameters& op_fee : existing_fee_schedule.parameters ) + fee_map[ op_fee.which() ] = op_fee; + // add the ability to look up the fee shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the htlc_create_operation fee structure with the new one + for(auto param : existing_fee_schedule.parameters) + { + if (param.which() != ((operation)htlc_create_operation()).which()) // we want to keep it as is... + new_fee_schedule.parameters.insert(param); + else + { + // we want to change this one + htlc_create_operation::fee_parameters_type htlc_param; + htlc_param.fee_per_day = 2; + htlc_param.fee = 2; + new_fee_schedule.parameters.insert(htlc_param); + } + } + // send a fee change proposal + chain_parameters new_parameters = existing_params; + new_parameters.current_fees = new_fee_schedule; + committee_member_update_global_parameters_operation cmuop; + cmuop.new_parameters = new_parameters; + proposal_create_operation cop = proposal_create_operation::committee_proposal( + existing_params, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + cop.proposed_ops.emplace_back( cmuop ); + cop.expiration_time = db.head_block_time() + fc::days(1); + cop.fee = asset( 100000 ); + trx.operations.push_back( cop ); + BOOST_TEST_MESSAGE("About to push fees before their time."); + db.push_transaction( trx ); + BOOST_TEST_MESSAGE("Attempted to push fees before their time."); + } + */ // now things should start working... + BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); advance_past_hardfork(this); proposal_id_type good_proposal_id; @@ -367,6 +415,7 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); good_proposal_id = proc_trx.operation_results[0].get(); } + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { proposal_update_operation uop; @@ -396,6 +445,13 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); +} catch (fc::exception &fcx) { + BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); +} catch (std::exception &ex) { + BOOST_FAIL("Exception: " << ex.what()); +} catch (...) { + BOOST_FAIL("Uncaught exception."); +} } BOOST_AUTO_TEST_SUITE_END() From e2e571ae018c2aeff4c72168211b9e239902c244 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Dec 2018 18:21:25 -0500 Subject: [PATCH 103/132] 1 day and 1kb mins on fees --- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index a9428f492c..526aa31037 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -73,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); + uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -111,7 +111,9 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - return preimage.size() / 1024 * fee_params.fee_per_kb; + if (fee_params.fee_per_kb > 0) + return std::min(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return 0; } }; @@ -148,7 +150,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = seconds_to_add / (60 * 60 * 24); + uint32_t days = std::min(1, seconds_to_add / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } }; From 5c7a9e89d8e5efad6b0fa1c2851906bb9ee833d5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 00:24:11 -0500 Subject: [PATCH 104/132] still trying to get fees working --- .../chain/include/graphene/chain/protocol/htlc.hpp | 8 ++++---- tests/tests/htlc_tests.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 526aa31037..f7b583c232 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -27,7 +27,7 @@ #include #include #include -#include // std::min +#include // std::max namespace graphene { namespace chain { @@ -73,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); + uint64_t days = std::max(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -112,7 +112,7 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::min(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return std::max(1, preimage.size() / (1024 * fee_params.fee_per_kb)); return 0; } }; @@ -150,7 +150,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, seconds_to_add / (60 * 60 * 24)); + uint32_t days = std::max(1, seconds_to_add / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } }; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 81567283b7..beef8276e4 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); @@ -162,8 +162,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 2 coins to Bob"); + create_operation.amount = graphene::chain::asset( 2 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; @@ -180,8 +180,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) generate_block(); } - // verify funds on hold (TODO: make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + // verify funds on hold... 100 - 2 = 98, minus the 1 coin fee = 9 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 97 * GRAPHENE_BLOCKCHAIN_PRECISION ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -189,9 +189,9 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) - generate_blocks(fc::time_point_sec(120) ); + generate_blocks( fc::time_point::now() + fc::seconds(120) ); // verify funds return (minus the fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 99 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify Bob cannot execute the contract after the fact } From 1e8ccab7b411de624b418be2ad4cbd68d0cf442f Mon Sep 17 00:00:00 2001 From: Alfredo Date: Tue, 18 Dec 2018 15:22:56 -0300 Subject: [PATCH 105/132] change adaptor response to be mutable_variant_object --- libraries/plugins/es_objects/es_objects.cpp | 9 +++------ .../include/graphene/es_objects/es_objects.hpp | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 01ec4722a6..5695064325 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -198,14 +198,11 @@ void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_n bulk_header["_id"] = string(blockchain_object.id); } - auto blockchain_object_string = fc::json::to_string(blockchain_object, fc::json::legacy_generator); - variant blockchain_object_variant = fc::json::from_string(blockchain_object_string); - fc::mutable_variant_object o; - adaptor_struct adaptor; - auto adapted = adaptor.adapt(blockchain_object_variant.get_object()); + fc::variant blockchain_object_variant; + fc::to_variant( blockchain_object, blockchain_object_variant, GRAPHENE_NET_MAX_NESTED_OBJECTS ); + fc::mutable_variant_object o = adaptor.adapt(blockchain_object_variant.get_object()); - fc::from_variant(adapted, o, FC_PACK_MAX_DEPTH); o["object_id"] = string(blockchain_object.id); o["block_time"] = block_time; o["block_number"] = block_number; diff --git a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp index a66e299551..fa91e3bde4 100644 --- a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp +++ b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp @@ -54,7 +54,7 @@ class es_objects_plugin : public graphene::app::plugin }; struct adaptor_struct { - variant adapt(const variant_object &obj) { + fc::mutable_variant_object adapt(const variant_object &obj) { fc::mutable_variant_object o(obj); vector keys_to_rename; for (auto i = o.begin(); i != o.end(); ++i) { @@ -94,10 +94,8 @@ struct adaptor_struct { { o["operations"] = fc::json::to_string(o["operations"]); } - - variant v; - fc::to_variant(o, v, FC_PACK_MAX_DEPTH); - return v; + + return o; } void adapt(fc::variants &v) { From a2192ec21e9d95372dc5e3d790bbc698bfb32a0c Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 20:23:17 -0500 Subject: [PATCH 106/132] fix htlc fee tests --- .../graphene/chain/protocol/fee_schedule.hpp | 36 +- libraries/chain/proposal_evaluator.cpp | 4 +- tests/tests/htlc_tests.cpp | 366 +++++++++++------- 3 files changed, 255 insertions(+), 151 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 39fa0a390f..eddc7e7094 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -113,7 +113,6 @@ namespace graphene { namespace chain { } }; - /* template<> class fee_helper { public: @@ -126,15 +125,34 @@ namespace graphene { namespace chain { static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy; return htlc_create_operation_fee_dummy; } - typename htlc_create_operation::fee_parameters_type& get(flat_set& parameters)const + }; + + template<> + class fee_helper { + public: + const htlc_redeem_operation::fee_parameters_type& cget(const flat_set& parameters)const { - auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->get(); + auto itr = parameters.find( htlc_redeem_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + + static htlc_redeem_operation::fee_parameters_type htlc_redeem_operation_fee_dummy; + return htlc_redeem_operation_fee_dummy; } }; - */ + template<> + class fee_helper { + public: + const htlc_extend_operation::fee_parameters_type& cget(const flat_set& parameters)const + { + auto itr = parameters.find( htlc_extend_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + static htlc_extend_operation::fee_parameters_type htlc_extend_operation_fee_dummy; + return htlc_extend_operation_fee_dummy; + } + }; /** * @brief contains all of the parameters necessary to calculate the fee for any operation */ @@ -168,6 +186,12 @@ namespace graphene { namespace chain { { return fee_helper().get(parameters); } + template + const bool exists()const + { + auto itr = parameters.find(typename Operation::fee_parameters_type()); + return itr != parameters.end(); + } /** * @note must be sorted by fee_parameters.which() and have no duplicates diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ed335dcff5..ff295c00f8 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -97,7 +97,9 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { if (block_time < HARDFORK_CORE_1468_TIME) { FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); - // TODO: Do not allow changing of fees before hardfork + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); } } // loop and self visit in proposals diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index beef8276e4..2918ff6c52 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -73,24 +73,92 @@ std::vector hash_it(std::vector preimage) return std::vector(hash.data(), hash.data() + hash.data_size()); } +flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() +{ + flat_map ret_val; + + htlc_create_operation::fee_parameters_type create_param; + create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_create_operation()).which()] = create_param; + + htlc_redeem_operation::fee_parameters_type redeem_param; + redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param; + + htlc_extend_operation::fee_parameters_type extend_param; + extend_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + extend_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_extend_operation()).which()] = extend_param; + + return ret_val; +} + +/**** + * @brief push through a proposal that sets htlc parameters and fees + * @param db_fixture the database connection + */ void set_committee_parameters(database_fixture* db_fixture) { - // set the committee parameters - db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { - // htlc options - graphene::chain::htlc_options params; - params.max_preimage_size = 1024; - params.max_timeout_secs = 60 * 60 * 24 * 28; - p.parameters.extensions.value.updatable_htlc_options = params; - // htlc operation fees - p.parameters.current_fees->get().fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - p.parameters.current_fees->get().fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - }); + // htlc fees + // get existing fee_schedule + const chain_parameters& existing_params = db_fixture->db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + for(auto param : existing_fee_schedule.parameters) + { + auto itr = params_map.find(param.which()); + if (itr == params_map.end()) + new_fee_schedule.parameters.insert(param); + else + { + new_fee_schedule.parameters.insert( (*itr).second); + } + } + // htlc parameters + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db_fixture->db.get_global_properties().parameters, db_fixture->db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db_fixture->db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 19200; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + + db_fixture->trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db_fixture->db.push_transaction(db_fixture->trx); + proposal_id_type good_proposal_id = proc_trx.operation_results[0].get(); + + proposal_update_operation puo; + puo.proposal = good_proposal_id; + puo.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + puo.active_approvals_to_add = { + db_fixture->get_account("init0").get_id(), db_fixture->get_account("init1").get_id(), + db_fixture->get_account("init2").get_id(), db_fixture->get_account("init3").get_id(), + db_fixture->get_account("init4").get_id(), db_fixture->get_account("init5").get_id(), + db_fixture->get_account("init6").get_id(), db_fixture->get_account("init7").get_id()}; + db_fixture->trx.operations.push_back(puo); + db_fixture->sign( db_fixture->trx, db_fixture->init_account_priv_key ); + db_fixture->db.push_transaction(db_fixture->trx); + db_fixture->trx.clear(); + + db_fixture->generate_blocks( good_proposal_id( db_fixture->db ).expiration_time + 5 ); + db_fixture->generate_blocks( db_fixture->db.get_dynamic_global_properties().next_maintenance_time ); + db_fixture->generate_block(); // get the maintenance skip slots out of the way + } void advance_past_hardfork(database_fixture* db_fixture) { db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); + set_expiration(db_fixture->db, db_fixture->trx); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -201,7 +269,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) ACTORS((alice)(bob)); - int64_t init_balance(1000000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); @@ -220,7 +288,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 100000 ); + create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; @@ -237,8 +305,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) alice_htlc_id = alice_trx.operation_results[0].get(); } - // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); + // verify funds on hold (make sure this can cover fees) (100 - 20 - fee) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7800000 ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -262,9 +330,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1100000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); + // verify funds end up in Bob's account (100 + 20 - 0.00002(fee)) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 11999998 ); + // 100 - 20 - 0.00002 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7999998 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) @@ -317,141 +386,150 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) } } -BOOST_AUTO_TEST_CASE( set_htlc_params ) +BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) { -try { - { - // try to set committee parameters before hardfork - proposal_create_operation cop = proposal_create_operation::committee_proposal( - db.get_global_properties().parameters, db.head_block_time()); - cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - committee_member_update_global_parameters_operation cmuop; - graphene::chain::htlc_options new_params; - new_params.max_preimage_size = 2048; - new_params.max_timeout_secs = 60 * 60 * 24 * 28; - cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; - cop.proposed_ops.emplace_back(cmuop); - trx.operations.push_back(cop); - // update with signatures - proposal_update_operation uop; - uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), - get_account("init2").get_id(), get_account("init3").get_id(), - get_account("init4").get_id(), get_account("init5").get_id(), - get_account("init6").get_id(), get_account("init7").get_id()}; - trx.operations.push_back(uop); - sign( trx, init_account_priv_key ); - BOOST_TEST_MESSAGE("Sending proposal."); - GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); - BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); - BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); - trx.clear(); - } + try { + { + // try to set committee parameters before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + // update with signatures + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + BOOST_TEST_MESSAGE("Sending proposal."); + GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); + BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); + trx.clear(); + } - /* - { - BOOST_TEST_MESSAGE("Attempting to set HTLC fees before hard fork."); - // get existing fees - const chain_parameters& existing_params = db.get_global_properties().parameters; - const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); - // build a map of existing fees - flat_map< int, fee_parameters > fee_map; - fee_map.reserve( existing_fee_schedule.parameters.size() ); - for( const fee_parameters& op_fee : existing_fee_schedule.parameters ) - fee_map[ op_fee.which() ] = op_fee; - // add the ability to look up the fee shedule - fee_schedule_type new_fee_schedule; - new_fee_schedule.scale = existing_fee_schedule.scale; - // replace the htlc_create_operation fee structure with the new one - for(auto param : existing_fee_schedule.parameters) { - if (param.which() != ((operation)htlc_create_operation()).which()) // we want to keep it as is... - new_fee_schedule.parameters.insert(param); - else + BOOST_TEST_MESSAGE("Attempting to set HTLC fees before hard fork."); + + // get existing fee_schedule + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + for(auto param : existing_fee_schedule.parameters) { - // we want to change this one - htlc_create_operation::fee_parameters_type htlc_param; - htlc_param.fee_per_day = 2; - htlc_param.fee = 2; - new_fee_schedule.parameters.insert(htlc_param); + auto itr = params_map.find(param.which()); + if (itr == params_map.end()) + new_fee_schedule.parameters.insert(param); + else + { + new_fee_schedule.parameters.insert( (*itr).second); + } } + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + cop.fee = asset( 100000 ); + trx.operations.push_back( cop ); + GRAPHENE_CHECK_THROW(db.push_transaction( trx ), fc::exception); + trx.clear(); } - // send a fee change proposal - chain_parameters new_parameters = existing_params; - new_parameters.current_fees = new_fee_schedule; - committee_member_update_global_parameters_operation cmuop; - cmuop.new_parameters = new_parameters; - proposal_create_operation cop = proposal_create_operation::committee_proposal( - existing_params, db.head_block_time()); - cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - cop.proposed_ops.emplace_back( cmuop ); - cop.expiration_time = db.head_block_time() + fc::days(1); - cop.fee = asset( 100000 ); - trx.operations.push_back( cop ); - BOOST_TEST_MESSAGE("About to push fees before their time."); - db.push_transaction( trx ); - BOOST_TEST_MESSAGE("Attempted to push fees before their time."); - } - */ - // now things should start working... - BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); - advance_past_hardfork(this); - proposal_id_type good_proposal_id; - BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); - { - proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); - cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - committee_member_update_global_parameters_operation uop; - graphene::chain::htlc_options new_params; - new_params.max_preimage_size = 2048; - new_params.max_timeout_secs = 60 * 60 * 24 * 28; - uop.new_parameters.extensions.value.updatable_htlc_options = new_params; - cop.proposed_ops.emplace_back(uop); - trx.operations.push_back(cop); - graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); - good_proposal_id = proc_trx.operation_results[0].get(); - } + // now things should start working... + BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); + advance_past_hardfork(this); - BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); - { - proposal_update_operation uop; - uop.proposal = good_proposal_id; - uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), - get_account("init2").get_id(), get_account("init3").get_id(), - get_account("init4").get_id(), get_account("init5").get_id(), - get_account("init6").get_id(), get_account("init7").get_id()}; - trx.operations.push_back(uop); - sign( trx, init_account_priv_key ); - db.push_transaction(trx); - BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); + proposal_id_type good_proposal_id; + BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048 and set higher fees" ); + { + // get existing fee_schedule + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + for(auto param : existing_fee_schedule.parameters) + { + auto itr = params_map.find(param.which()); + if (itr == params_map.end()) + new_fee_schedule.parameters.insert(param); + else + { + new_fee_schedule.parameters.insert( (*itr).second); + } + } + proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); + good_proposal_id = proc_trx.operation_results[0].get(); + } + + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.proposal = good_proposal_id; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); + } + BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(good_proposal_id(db).expiration_time + 5); + BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); + + } catch (fc::exception &fcx) { + BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); + } catch (std::exception &ex) { + BOOST_FAIL("Exception: " << ex.what()); + } catch (...) { + BOOST_FAIL("Uncaught exception."); } - BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); - - BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); - generate_blocks(good_proposal_id(db).expiration_time + 5); - BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); - - BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); // get the maintenance skip slots out of the way - - BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); -} catch (fc::exception &fcx) { - BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); -} catch (std::exception &ex) { - BOOST_FAIL("Exception: " << ex.what()); -} catch (...) { - BOOST_FAIL("Uncaught exception."); -} } BOOST_AUTO_TEST_SUITE_END() From f7d7f5ce41910edf2646a2b51f09846136e2f348 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 22:49:52 -0500 Subject: [PATCH 107/132] Making fees work with htlc tests --- .../include/graphene/chain/protocol/htlc.hpp | 2 +- tests/tests/htlc_tests.cpp | 182 +++++++++++------- 2 files changed, 112 insertions(+), 72 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f7b583c232..d2e4a15aa5 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -112,7 +112,7 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::max(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return std::max(fee_params.fee_per_kb, preimage.size() / 1024 * fee_params.fee_per_kb); return 0; } }; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 2918ff6c52..149a479aed 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -106,7 +106,7 @@ void set_committee_parameters(database_fixture* db_fixture) const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); // create a new fee_shedule fee_schedule_type new_fee_schedule; - new_fee_schedule.scale = existing_fee_schedule.scale; + new_fee_schedule.scale = GRAPHENE_100_PERCENT; // replace the old with the new flat_map params_map = get_htlc_fee_parameters(); for(auto param : existing_fee_schedule.parameters) @@ -157,67 +157,23 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); set_expiration(db_fixture->db, db_fixture->trx); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } -BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) -{ - ACTORS((alice)(bob)); - - int64_t init_balance(100000); - - transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - - uint16_t preimage_size = 256; - std::vector pre_image(256); - generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); - - graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out - generate_block(); - trx.clear(); - // Alice tries to put a contract on the blockchain - { - graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.preimage_size = preimage_size; - create_operation.from = alice_id; - trx.operations.push_back(create_operation); - sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); - } - // attempt to create a proposal that contains htlc stuff - { - - graphene::chain::committee_member_update_global_parameters_operation param_op; - - graphene::chain::proposal_create_operation create_operation; - - create_operation.fee_paying_account = committee_account; - create_operation.review_period_seconds = 60 * 60 * 48; - create_operation.proposed_ops.emplace_back(param_op); - } -} - BOOST_AUTO_TEST_CASE( htlc_expires ) { - advance_past_hardfork(this); - +try { ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + advance_past_hardfork(this); + uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); @@ -231,13 +187,14 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) { graphene::chain::htlc_create_operation create_operation; BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 2 coins to Bob"); - create_operation.amount = graphene::chain::asset( 2 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -248,8 +205,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) generate_block(); } - // verify funds on hold... 100 - 2 = 98, minus the 1 coin fee = 9 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 97 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds on hold... 100 - 3 = 97, minus the 4 coin fee = 93 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -257,16 +214,15 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) - generate_blocks( fc::time_point::now() + fc::seconds(120) ); + generate_blocks( db.head_block_time() + fc::seconds(120) ); // verify funds return (minus the fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 99 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 96 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify Bob cannot execute the contract after the fact +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { - advance_past_hardfork(this); - ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); @@ -274,16 +230,17 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); + advance_past_hardfork(this); + uint16_t preimage_size = 256; std::vector pre_image(preimage_size); generate_random_preimage(preimage_size, pre_image); std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out + // clear everything out generate_block(); trx.clear(); - enable_fees(); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -296,7 +253,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); - trx.operations.push_back(create_operation); + trx.operations.push_back( create_operation ); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); @@ -305,8 +262,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) alice_htlc_id = alice_trx.operation_results[0].get(); } - // verify funds on hold (make sure this can cover fees) (100 - 20 - fee) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7800000 ); + // make sure Alice's money gets put on hold (100 - 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -330,19 +287,20 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account (100 + 20 - 0.00002(fee)) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 11999998 ); - // 100 - 20 - 0.00002 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7999998 ); + // verify funds end up in Bob's account (100 + 20 - 2(fee) ) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 118 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { +try { advance_past_hardfork(this); ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION ); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); @@ -358,35 +316,38 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) // Bob attempts to put a contract on the blockchain using Alice's funds { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, bob_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception); trx.clear(); } // now try the same but with Alice's signature (should work) { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - PUSH_TX(db, trx, database::skip_nothing); + PUSH_TX( db, trx ); trx.clear(); } +} FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) +BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) { try { { @@ -532,4 +493,83 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) } } +BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t preimage_size = 256; + std::vector pre_image(256); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice tries to put a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + } +} + +BOOST_AUTO_TEST_CASE( fee_calculations ) +{ + // create + { + htlc_create_operation::fee_parameters_type create_fee; + create_fee.fee = 2; + create_fee.fee_per_day = 2; + htlc_create_operation create; + create.claim_period_seconds = 0; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + // almost 2 days + create.claim_period_seconds = 2 * 60 * 60 * 24 - 1; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + // 2 days + create.claim_period_seconds++; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); + } + // redeem + { + htlc_redeem_operation::fee_parameters_type redeem_fee; + redeem_fee.fee_per_kb = 2; + htlc_redeem_operation redeem; + redeem.preimage = std::vector(); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; + std::string test(2048, 'a'); + redeem.preimage = std::vector( test.begin(), test.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + } + // extend + { + htlc_extend_operation::fee_parameters_type extend_fee; + extend_fee.fee = 2; + extend_fee.fee_per_day = 2; + htlc_extend_operation extend; + extend.seconds_to_add = 0; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + // almost 2 days + extend.seconds_to_add = 2 * 60 * 60 * 24 - 1; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + // 2 days + extend.seconds_to_add++; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 ); + } +} + BOOST_AUTO_TEST_SUITE_END() From 96ec5f36df7c85b8df7892c88eb9f9dfcb9eb484 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 19 Dec 2018 00:22:50 -0500 Subject: [PATCH 108/132] move hash_algoritm enum plus misc --- libraries/app/database_api.cpp | 1 - libraries/chain/db_notify.cpp | 6 +-- libraries/chain/htlc_evaluator.cpp | 10 ++--- libraries/chain/htlc_object.cpp | 10 ++--- .../include/graphene/chain/htlc_object.hpp | 16 ++------ .../graphene/chain/protocol/fee_schedule.hpp | 6 --- .../include/graphene/chain/protocol/htlc.hpp | 27 +++++++++---- libraries/chain/protocol/htlc.cpp | 6 +-- libraries/wallet/wallet.cpp | 3 +- programs/js_operation_serializer/main.cpp | 1 + tests/common/database_fixture.cpp | 1 + tests/tests/htlc_tests.cpp | 39 ++++++++++++------- 12 files changed, 68 insertions(+), 58 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c89971a728..acb90c8af5 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 354505b485..0862dd28ac 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -270,7 +270,7 @@ struct get_impacted_account_visitor } void operator()( const htlc_refund_operation& op ) { - _impacted.insert( op.fee_payer() ); // bidder + _impacted.insert( op.fee_payer() ); } }; @@ -360,7 +360,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; - } case htlc_object_type: { + } case htlc_object_type:{ const auto& htlc_obj = dynamic_cast(obj); FC_ASSERT( htlc_obj != nullptr); accounts.insert( htlc_obj->from ); @@ -428,7 +428,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 6087442cb2..fba098025d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -46,7 +46,7 @@ namespace graphene { // make sure the preimage length is reasonable FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set - FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); + FC_ASSERT( o.hash_type != htlc_hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); @@ -58,7 +58,7 @@ namespace graphene { graphene::chain::database& dbase = db(); dbase.adjust_balance( o.from, -o.amount ); - const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { + const htlc_object& esc = db().create([&dbase,&o]( htlc_object& esc ) { esc.from = o.from; esc.to = o.to; esc.amount = o.amount; @@ -92,13 +92,13 @@ namespace graphene { bool match = false; switch(htlc_obj->preimage_hash_algorithm) { - case (graphene::chain::hash_algorithm::sha256): + case (htlc_hash_algorithm::sha256): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; - case (graphene::chain::hash_algorithm::ripemd160): + case (htlc_hash_algorithm::ripemd160): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; - case (graphene::chain::hash_algorithm::sha1): + case (htlc_hash_algorithm::sha1): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; default: diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp index f7f4c93b21..e460619212 100644 --- a/libraries/chain/htlc_object.cpp +++ b/libraries/chain/htlc_object.cpp @@ -30,16 +30,16 @@ namespace graphene { namespace chain { * @param incoming the string to convert * @returns one of the valid algorithms or the enum value "unknown" */ - fc::enum_type string_to_hash_algorithm(std::string incoming) + fc::enum_type string_to_hash_algorithm(std::string incoming) { std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); if (incoming == "RIPEMD160") - return hash_algorithm::ripemd160; + return htlc_hash_algorithm::ripemd160; if (incoming == "SHA256") - return hash_algorithm::sha256; + return htlc_hash_algorithm::sha256; if (incoming == "SHA1") - return hash_algorithm::sha1; - return hash_algorithm::unknown; + return htlc_hash_algorithm::sha1; + return htlc_hash_algorithm::unknown; } } } diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 7c45d4d636..57dab81416 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -26,25 +26,19 @@ #include #include #include +#include #include #include #include namespace graphene { namespace chain { - enum hash_algorithm { - unknown = 0x00, - ripemd160 = 0x01, - sha256 = 0x02, - sha1 = 0x03 - }; - /**** * Convert string to hash algorithm enum * @param incoming the string to convert * @returns one of the valid algorithms or the enum value "unknown" */ - fc::enum_type string_to_hash_algorithm(std::string incoming); + fc::enum_type string_to_hash_algorithm(std::string incoming); /** * @brief database object to store HTLCs @@ -63,7 +57,7 @@ namespace graphene { namespace chain { asset amount; fc::time_point_sec expiration; vector preimage_hash; - fc::enum_type preimage_hash_algorithm; + fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; }; @@ -94,7 +88,7 @@ namespace graphene { namespace chain { namespace fc { template<> - struct get_typename> + struct get_typename> { static const char* name() { @@ -103,8 +97,6 @@ namespace fc }; } -FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); - FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index eddc7e7094..e578f1d9f4 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,12 +43,6 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } - typename Operation::fee_parameters_type& get(flat_set& parameters)const - { - auto itr = parameters.find( typename Operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->template get(); - } }; template<> diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index d2e4a15aa5..2c6ba84f8e 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -24,7 +24,6 @@ #pragma once #include #include -#include #include #include #include // std::max @@ -32,6 +31,13 @@ namespace graphene { namespace chain { + enum htlc_hash_algorithm { + unknown = 0x00, + ripemd160 = 0x01, + sha256 = 0x02, + sha1 = 0x03 + }; + struct htlc_create_operation : public base_operation { struct fee_parameters_type { @@ -47,8 +53,8 @@ namespace graphene { // the amount to hold asset amount; // hash algorithm used to create preimage_hash - fc::enum_type hash_type - = graphene::chain::hash_algorithm::unknown; + fc::enum_type hash_type + = htlc_hash_algorithm::unknown; // the hash of the preimage std::vector preimage_hash; // the size of the preimage @@ -73,7 +79,8 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint64_t days = std::max(1, claim_period_seconds / (60 * 60 * 24)); + + uint64_t days = std::max( 1, std::ceil( claim_period_seconds / (double)(60 * 60 * 24) ) ); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -82,6 +89,7 @@ namespace graphene { struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; @@ -112,8 +120,9 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::max(fee_params.fee_per_kb, preimage.size() / 1024 * fee_params.fee_per_kb); - return 0; + return fee_params.fee + std::max(fee_params.fee_per_kb, + std::ceil( preimage.size() / (double)1024 ) * fee_params.fee_per_kb ); + return fee_params.fee; } }; @@ -150,7 +159,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::max(1, seconds_to_add / (60 * 60 * 24)); + uint32_t days = std::max(1, std::ceil( seconds_to_add / (double)(60 * 60 * 24) ) ); return fee_params.fee + (fee_params.fee_per_day * days); } }; @@ -176,8 +185,10 @@ namespace graphene { } } +FC_REFLECT_ENUM( graphene::chain::htlc_hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); + FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) -FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee_per_kb) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index be444735f6..04ef3f0bd6 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,9 +28,9 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); - FC_ASSERT( hash_type == hash_algorithm::ripemd160 - || hash_type == graphene::chain::hash_algorithm::sha1 - || hash_type == hash_algorithm::sha256, "Unknown Hash Algorithm"); + FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 + || hash_type == htlc_hash_algorithm::sha1 + || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); } void htlc_redeem_operation::validate()const { diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 99445c13e7..cc5d8a4062 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -1767,7 +1768,7 @@ class wallet_api_impl create_op.preimage_hash = preimage_hash; create_op.preimage_size = preimage_size; create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); - FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, + FC_ASSERT(create_op.hash_type != htlc_hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index e955f8d7fa..24a4065e64 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 1214d8be9a..d1e62ea197 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 149a479aed..6faff73d40 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -45,6 +45,7 @@ #include #include +#include #include @@ -83,6 +84,7 @@ flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() ret_val[((operation)htlc_create_operation()).which()] = create_param; htlc_redeem_operation::fee_parameters_type redeem_param; + redeem_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param; @@ -191,7 +193,7 @@ try { create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); @@ -249,7 +251,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -287,8 +289,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account (100 + 20 - 2(fee) ) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 118 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds end up in Bob's account (100 + 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 116 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); } @@ -335,7 +337,7 @@ try { create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -518,7 +520,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; trx.operations.push_back(create_operation); @@ -535,12 +537,13 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) create_fee.fee = 2; create_fee.fee_per_day = 2; htlc_create_operation create; + // no days create.claim_period_seconds = 0; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); - // almost 2 days - create.claim_period_seconds = 2 * 60 * 60 * 24 - 1; + // exactly 1 day + create.claim_period_seconds = 60 * 60 * 24; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); - // 2 days + // tad over a day create.claim_period_seconds++; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); } @@ -548,12 +551,19 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) { htlc_redeem_operation::fee_parameters_type redeem_fee; redeem_fee.fee_per_kb = 2; + redeem_fee.fee = 2; htlc_redeem_operation redeem; + // no preimage redeem.preimage = std::vector(); - BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; - std::string test(2048, 'a'); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + // exactly 1KB + std::string test(1024, 'a'); redeem.preimage = std::vector( test.begin(), test.end() ); BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + // just 1 byte over 1KB + std::string larger(1025, 'a'); + redeem.preimage = std::vector( larger.begin(), larger.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 6 ) ; } // extend { @@ -561,12 +571,13 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) extend_fee.fee = 2; extend_fee.fee_per_day = 2; htlc_extend_operation extend; + // no days extend.seconds_to_add = 0; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); - // almost 2 days - extend.seconds_to_add = 2 * 60 * 60 * 24 - 1; + // exactly 1 day + extend.seconds_to_add = 60 * 60 * 24; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); - // 2 days + // 1 day and 1 second extend.seconds_to_add++; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 ); } From 0244ff5e51867c89dc8497728f163c6ea3546c8f Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:02:23 +0100 Subject: [PATCH 109/132] Added missing (?) includes --- libraries/chain/db_notify.cpp | 1 + libraries/chain/db_update.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 0862dd28ac..e044bd843d 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index b5f55fbb66..05aae9d058 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include From 5ffa2a0b2332daa66ab8a68cfe0fb2756df215f4 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 17:03:41 +0100 Subject: [PATCH 110/132] Get rid of floating point calculations --- .../include/graphene/chain/protocol/htlc.hpp | 26 ++++--------------- libraries/chain/protocol/htlc.cpp | 20 ++++++++++++++ tests/tests/htlc_tests.cpp | 6 ++--- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 2c6ba84f8e..f7d3b88d68 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -77,13 +77,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - - uint64_t days = std::max( 1, std::ceil( claim_period_seconds / (double)(60 * 60 * 24) ) ); - return fee_params.fee + (fee_params.fee_per_day * days); - } - + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_redeem_operation : public base_operation @@ -117,13 +111,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - if (fee_params.fee_per_kb > 0) - return fee_params.fee + std::max(fee_params.fee_per_kb, - std::ceil( preimage.size() / (double)1024 ) * fee_params.fee_per_kb ); - return fee_params.fee; - } + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_extend_operation : public base_operation @@ -138,7 +126,7 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type update_issuer; + account_id_type update_issuer; // how much to add uint32_t seconds_to_add; // for future expansion @@ -147,7 +135,7 @@ namespace graphene { /*** * @brief Perform obvious checks to validate this object */ - void validate()const; + void validate()const; /** * @brief Who is to pay the fee @@ -157,11 +145,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - uint32_t days = std::max(1, std::ceil( seconds_to_add / (double)(60 * 60 * 24) ) ); - return fee_params.fee + (fee_params.fee_per_day * days); - } + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_refund_operation : public base_operation diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 04ef3f0bd6..dd8f12e40f 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,6 +23,8 @@ */ #include +#define SECONDS_PER_DAY (60 * 60 * 24) + namespace graphene { namespace chain { void htlc_create_operation::validate()const { @@ -33,11 +35,29 @@ namespace graphene { namespace chain { || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); } + share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + uint64_t days = (claim_period_seconds + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; + return fee_params.fee + (fee_params.fee_per_day * days); + } + void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } + + share_type htlc_redeem_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + return fee_params.fee + + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; + } + void htlc_extend_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } + share_type htlc_extend_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + uint32_t days = (seconds_to_add + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; + return fee_params.fee + (fee_params.fee_per_day * days); + } } } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 6faff73d40..466b72b9c3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -539,7 +539,7 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_create_operation create; // no days create.claim_period_seconds = 0; - BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 2 ); // exactly 1 day create.claim_period_seconds = 60 * 60 * 24; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); @@ -555,7 +555,7 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_redeem_operation redeem; // no preimage redeem.preimage = std::vector(); - BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; // exactly 1KB std::string test(1024, 'a'); redeem.preimage = std::vector( test.begin(), test.end() ); @@ -573,7 +573,7 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_extend_operation extend; // no days extend.seconds_to_add = 0; - BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 2 ); // exactly 1 day extend.seconds_to_add = 60 * 60 * 24; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); From 145d79066b4d23087abd6429269ead9e0881cdd8 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:36:54 +0100 Subject: [PATCH 111/132] Added hf protection for htlc_operations in proposals --- libraries/chain/proposal_evaluator.cpp | 9 ++++ tests/tests/htlc_tests.cpp | 73 ++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ff295c00f8..f943cc5ad9 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -102,6 +102,15 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists()); } } + void operator()(const graphene::chain::htlc_create_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_redeem_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_extend_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 466b72b9c3..2b26721553 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -486,17 +486,10 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); - } catch (fc::exception &fcx) { - BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); - } catch (std::exception &ex) { - BOOST_FAIL("Exception: " << ex.what()); - } catch (...) { - BOOST_FAIL("Uncaught exception."); - } -} +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) -{ +{ try { ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -509,9 +502,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out + // clear everything out generate_block(); trx.clear(); + // Alice tries to put a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -526,8 +520,65 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) trx.operations.push_back(create_operation); sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); } -} + + // Propose htlc_create + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; + create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + + pco.proposed_ops.emplace_back( create_operation ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_redeem + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_redeem_operation rop; + rop.redeemer = bob_id; + rop.htlc_id = alice_htlc_id; + string preimage_str = "Arglebargle"; + rop.preimage.insert( rop.preimage.begin(), preimage_str.begin(), preimage_str.end() ); + + pco.proposed_ops.emplace_back( rop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_extend + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_extend_operation xop; + xop.htlc_id = alice_htlc_id; + xop.seconds_to_add = 100; + xop.update_issuer = alice_id; + + pco.proposed_ops.emplace_back( xop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( fee_calculations ) { From e255f39bed52c867f8c047300ba8d1ac0343eca5 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:55:34 +0100 Subject: [PATCH 112/132] Validate that preimage hash length matches algorithm hash length --- libraries/chain/protocol/htlc.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index dd8f12e40f..a1e1d14f91 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,16 +23,30 @@ */ #include +#include +#include +#include + #define SECONDS_PER_DAY (60 * 60 * 24) namespace graphene { namespace chain { void htlc_create_operation::validate()const { + static const fc::ripemd160 ripemd160_instance; + static const fc::sha1 sha1_instance; + static const fc::sha256 sha256_instance; + FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 || hash_type == htlc_hash_algorithm::sha1 || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); + FC_ASSERT( ( hash_type == htlc_hash_algorithm::ripemd160 + && preimage_hash.size() == ripemd160_instance.data_size() ) + || ( hash_type == htlc_hash_algorithm::sha1 + && preimage_hash.size() == sha1_instance.data_size() ) + || ( hash_type == htlc_hash_algorithm::sha256 + && preimage_hash.size() == sha256_instance.data_size() ), "Hash length mismatch"); } share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const From da6fbef6cfeb5a38df7c0d09133ff76a4943a527 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 23:25:34 +0100 Subject: [PATCH 113/132] Refactoring: use a static_variant instead of separate enum + hash vector --- libraries/chain/CMakeLists.txt | 1 - libraries/chain/htlc_evaluator.cpp | 45 +++++------- libraries/chain/htlc_object.cpp | 45 ------------ .../include/graphene/chain/htlc_evaluator.hpp | 3 - .../include/graphene/chain/htlc_object.hpp | 24 +------ .../include/graphene/chain/protocol/htlc.hpp | 48 +++++++------ .../include/graphene/chain/protocol/types.hpp | 17 +++++ libraries/chain/protocol/htlc.cpp | 17 ----- libraries/wallet/wallet.cpp | 71 ++++++++++++------- tests/tests/htlc_tests.cpp | 27 +++---- 10 files changed, 116 insertions(+), 182 deletions(-) delete mode 100644 libraries/chain/htlc_object.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index e6551c1189..71d2fd2d2e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -78,7 +78,6 @@ add_library( graphene_chain withdraw_permission_evaluator.cpp worker_evaluator.cpp htlc_evaluator.cpp - htlc_object.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index fba098025d..37512a0d1f 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include #include #include @@ -45,8 +44,6 @@ namespace graphene { FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); - // make sure we have a hash algorithm set - FC_ASSERT( o.hash_type != htlc_hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); @@ -65,21 +62,28 @@ namespace graphene { esc.preimage_hash = o.preimage_hash; esc.preimage_size = o.preimage_size; esc.expiration = dbase.head_block_time() + o.claim_period_seconds; - esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; } FC_CAPTURE_AND_RETHROW( (o) ) } - template - bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + class htlc_redeem_visitor { - T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); - if (attempted_hash.data_size() != valid_hash.size()) - return false; - return memcmp(attempted_hash.data(), valid_hash.data(), attempted_hash.data_size()) == 0; - } + //private: + const std::vector& data; + public: + typedef bool result_type; + + htlc_redeem_visitor( const std::vector& preimage ) + : data( preimage ) {} + + template + bool operator()( const T& preimage_hash )const + { + return T::hash( (const char*)data.data(), (uint32_t) data.size() ) == preimage_hash; + } + }; void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { @@ -88,24 +92,9 @@ namespace graphene { FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); - // see if the preimages match - bool match = false; - switch(htlc_obj->preimage_hash_algorithm) - { - case (htlc_hash_algorithm::sha256): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - case (htlc_hash_algorithm::ripemd160): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - case (htlc_hash_algorithm::sha1): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - default: - break; - } + const htlc_redeem_visitor vtor( o.preimage ); + FC_ASSERT( htlc_obj->preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); - FC_ASSERT(match, "Provided preimage does not generate correct hash."); return void_result(); } diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp deleted file mode 100644 index e460619212..0000000000 --- a/libraries/chain/htlc_object.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018 Bitshares Foundation, and contributors. - * - * 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 - -namespace graphene { namespace chain { - - /**** - * Convert string to hash algorithm enum - * @param incoming the string to convert - * @returns one of the valid algorithms or the enum value "unknown" - */ - fc::enum_type string_to_hash_algorithm(std::string incoming) - { - std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); - if (incoming == "RIPEMD160") - return htlc_hash_algorithm::ripemd160; - if (incoming == "SHA256") - return htlc_hash_algorithm::sha256; - if (incoming == "SHA1") - return htlc_hash_algorithm::sha1; - return htlc_hash_algorithm::unknown; - } - -} } diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index c93b579742..08a24b2bdc 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -44,9 +44,6 @@ namespace graphene { void_result do_evaluate( const htlc_redeem_operation& o); void_result do_apply( const htlc_redeem_operation& o); const htlc_object* htlc_obj = nullptr; - private: - template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; class htlc_extend_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 57dab81416..e9c4515042 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -33,13 +33,6 @@ namespace graphene { namespace chain { - /**** - * Convert string to hash algorithm enum - * @param incoming the string to convert - * @returns one of the valid algorithms or the enum value "unknown" - */ - fc::enum_type string_to_hash_algorithm(std::string incoming); - /** * @brief database object to store HTLCs * @@ -56,8 +49,7 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - vector preimage_hash; - fc::enum_type preimage_hash_algorithm; + htlc_hash preimage_hash; uint16_t preimage_size; }; @@ -85,18 +77,6 @@ namespace graphene { namespace chain { } } // namespace graphene::chain -namespace fc -{ - template<> - struct get_typename> - { - static const char* name() - { - return "fc::enum_type"; - } - }; -} - FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) - (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); + (preimage_hash)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f7d3b88d68..310282da12 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -22,6 +22,10 @@ * THE SOFTWARE. */ #pragma once + +#include +#include +#include #include #include #include @@ -31,12 +35,15 @@ namespace graphene { namespace chain { - enum htlc_hash_algorithm { - unknown = 0x00, - ripemd160 = 0x01, - sha256 = 0x02, - sha1 = 0x03 - }; + typedef fc::ripemd160 htlc_algo_ripemd160; + typedef fc::sha1 htlc_algo_sha1; + typedef fc::sha256 htlc_algo_sha256; + + typedef fc::static_variant< + htlc_algo_ripemd160, + htlc_algo_sha1, + htlc_algo_sha256 + > htlc_hash; struct htlc_create_operation : public base_operation { @@ -45,29 +52,26 @@ namespace graphene { uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network - asset fee; + asset fee; // where the held monies are to come from account_id_type from; // where the held monies will go if the preimage is provided - account_id_type to; + account_id_type to; // the amount to hold - asset amount; - // hash algorithm used to create preimage_hash - fc::enum_type hash_type - = htlc_hash_algorithm::unknown; - // the hash of the preimage - std::vector preimage_hash; + asset amount; + // the (typed) hash of the preimage + htlc_hash preimage_hash; // the size of the preimage - uint16_t preimage_size; + uint16_t preimage_size; // The time the funds will be returned to the source if not claimed - uint32_t claim_period_seconds; + uint32_t claim_period_seconds; // for future expansion - extensions_type extensions; + extensions_type extensions; /*** * @brief Does simple validation of this object */ - void validate()const; + void validate()const; /** * @brief who will pay the fee @@ -92,9 +96,9 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type redeemer; + account_id_type redeemer; // the preimage (not used if after epoch timeout) - std::vector preimage; + std::vector preimage; // for future expansion extensions_type extensions; @@ -169,7 +173,7 @@ namespace graphene { } } -FC_REFLECT_ENUM( graphene::chain::htlc_hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); +FC_REFLECT_TYPENAME( graphene::chain::htlc_hash ) FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) @@ -177,7 +181,7 @@ FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) ( FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) + (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 53b0378c65..a9202c32a8 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -36,6 +36,23 @@ #include +// TODO: move this to fc +#include +FC_REFLECT_TYPENAME( fc::sha1 ) +namespace fc { namespace raw { + template + inline void pack( T& ds, const fc::sha1& ep, uint32_t _max_depth = 1 ) { + ds << ep; + } + + template + inline void unpack( T& ds, sha1& ep, uint32_t _max_depth = 1 ) { + ds >> ep; + } + +} } +// /TODO: move to fc + #include #include #include diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index a1e1d14f91..b176c6e85d 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,30 +23,13 @@ */ #include -#include -#include -#include - #define SECONDS_PER_DAY (60 * 60 * 24) namespace graphene { namespace chain { void htlc_create_operation::validate()const { - static const fc::ripemd160 ripemd160_instance; - static const fc::sha1 sha1_instance; - static const fc::sha256 sha256_instance; - FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); - FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 - || hash_type == htlc_hash_algorithm::sha1 - || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); - FC_ASSERT( ( hash_type == htlc_hash_algorithm::ripemd160 - && preimage_hash.size() == ripemd160_instance.data_size() ) - || ( hash_type == htlc_hash_algorithm::sha1 - && preimage_hash.size() == sha1_instance.data_size() ) - || ( hash_type == htlc_hash_algorithm::sha256 - && preimage_hash.size() == sha256_instance.data_size() ), "Hash length mismatch"); } share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index cc5d8a4062..0eabbe35d1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -265,6 +265,27 @@ struct op_prototype_visitor } }; +class htlc_hash_to_string_visitor +{ +public: + typedef string result_type; + + result_type operator()( const fc::ripemd160& hash )const + { + return "RIPEMD160 " + hash.str(); + } + + result_type operator()( const fc::sha1& hash )const + { + return "SHA1 " + hash.str(); + } + + result_type operator()( const fc::sha256& hash )const + { + return "SHA256 " + hash.str(); + } +}; + class wallet_api_impl { public: @@ -1750,8 +1771,21 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + static htlc_hash do_hash( const string& algorithm, const std::string& hash ) + { + string name_upper; + std::transform( algorithm.begin(), algorithm.end(), std::back_inserter(name_upper), ::toupper); + if( name_upper == "RIPEMD160" ) + return fc::ripemd160( hash ); + if( name_upper == "SHA256" ) + return fc::sha256( hash ); + if( name_upper == "SHA1" ) + return fc::sha1( hash ); + FC_THROW_EXCEPTION( fc::invalid_arg_exception, "Unknown algorithm '${a}'", ("a",algorithm) ); + } + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ) { try @@ -1765,11 +1799,8 @@ class wallet_api_impl create_op.to = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); create_op.claim_period_seconds = claim_period_seconds; - create_op.preimage_hash = preimage_hash; + create_op.preimage_hash = do_hash( hash_algorithm, preimage_hash ); create_op.preimage_size = preimage_size; - create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); - FC_ASSERT(create_op.hash_type != htlc_hash_algorithm::unknown, - "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; tx.operations.push_back(create_op); @@ -2991,6 +3022,8 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const std::string operation_printer::operator()(const htlc_create_operation& op) const { + static htlc_hash_to_string_visitor vtor; + auto fee_asset = wallet.get_asset( op.fee.asset_id ); auto to = wallet.get_account( op.to ); operation_result_printer rprinter(wallet); @@ -2998,12 +3031,9 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with id " << database_id - << " preimage hash: ["; - for(uint8_t c : op.preimage_hash) - { - out << setfill('0') << std::setw(2) << std::hex << (int)c; - } - out << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; + << " preimage hash: [" + << op.preimage_hash.visit( vtor ) + << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; // determine if the block that the HTLC is in is before or after LIB int32_t pending_blocks = hist.block_num - wallet.get_dynamic_global_properties().last_irreversible_block_num; if (pending_blocks > 0) @@ -3142,25 +3172,18 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -std::vector string_to_vec(std::string incoming) -{ - FC_ASSERT(incoming.size() % 2 == 0, "Invalid incoming hash."); - std::size_t outgoing_size = incoming.size() / 2; - std::vector outgoing(outgoing_size); - fc::from_hex(incoming, (char*)outgoing.data(), outgoing_size); - return outgoing; -} - signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast) { - return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size, claim_period_seconds, broadcast); } variant wallet_api::get_htlc(std::string htlc_id) const { + static detail::htlc_hash_to_string_visitor vtor; + graphene::chain::htlc_object obj = my->get_htlc(htlc_id); fc::mutable_variant_object ret_val; ret_val["database_id"] = (std::string)obj.id; @@ -3169,11 +3192,7 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["amount"] = obj.amount.amount.value; ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); - std::stringstream hash_string; - for(uint8_t c : obj.preimage_hash) - hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; - ret_val["preimage_hash"] = hash_string.str(); - ret_val["preimage_algorithm"] = (std::string)obj.preimage_hash_algorithm; + ret_val["preimage_hash"] = obj.preimage_hash.visit( vtor ); ret_val["preimage_size"] = obj.preimage_size; return ret_val; } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 2b26721553..1f659ea89e 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -68,10 +68,10 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec) * @param preimage the preimage * @returns a vector that cointains the sha256 hash of the preimage */ -std::vector hash_it(std::vector preimage) +template +H hash_it(std::vector preimage) { - fc::sha256 hash = fc::sha256::hash((char*)preimage.data(), preimage.size()); - return std::vector(hash.data(), hash.data() + hash.data_size()); + return H::hash( (char*)preimage.data(), preimage.size() ); } flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() @@ -179,7 +179,6 @@ try { uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -192,8 +191,7 @@ try { create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); @@ -237,7 +235,6 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) uint16_t preimage_size = 256; std::vector pre_image(preimage_size); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // clear everything out @@ -250,8 +247,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -309,7 +305,6 @@ try { uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -321,7 +316,7 @@ try { create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; - create_operation.preimage_hash = preimage_hash; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -336,8 +331,7 @@ try { create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -499,7 +493,6 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // clear everything out @@ -513,8 +506,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; trx.operations.push_back(create_operation); @@ -533,8 +525,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; From f4dcd9b2f43ec552725c63d37413b9f41601762d Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 20 Dec 2018 16:46:09 +0100 Subject: [PATCH 114/132] Fix for node crash #1479 --- libraries/chain/db_block.cpp | 14 ++++++-- libraries/chain/hardfork.d/CORE_1479.hf | 4 +++ .../chain/include/graphene/chain/database.hpp | 1 + tests/tests/authority_tests.cpp | 33 +++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_1479.hf diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..e98e1616d6 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -270,20 +270,27 @@ processed_transaction database::validate_transaction( const signed_transaction& class push_proposal_nesting_guard { public: - push_proposal_nesting_guard( uint32_t& nesting_counter, const database& db ) - : orig_value(nesting_counter), counter(nesting_counter) + push_proposal_nesting_guard( uint32_t& nesting_counter, uint64_t& prop_id, + const database& db, const proposal_object& proposal ) + : orig_value(nesting_counter), counter(nesting_counter), orig_id(prop_id), current_id(prop_id) { FC_ASSERT( counter < db.get_global_properties().active_witnesses.size() * 2, "Max proposal nesting depth exceeded!" ); + if( db.head_block_time() >= HARDFORK_CORE_1479_TIME ) + FC_ASSERT( proposal.id.instance() < current_id, "Cannot approve proposals that didn't exist at time of creation!" ); counter++; + current_id = proposal.id.instance(); } ~push_proposal_nesting_guard() { if( --counter != orig_value ) elog( "Unexpected proposal nesting count value: ${n} != ${o}", ("n",counter)("o",orig_value) ); + current_id = orig_id; } private: const uint32_t orig_value; uint32_t& counter; + const uint64_t orig_id; + uint64_t& current_id; }; processed_transaction database::push_proposal(const proposal_object& proposal) @@ -297,7 +304,8 @@ processed_transaction database::push_proposal(const proposal_object& proposal) size_t old_applied_ops_size = _applied_ops.size(); try { - push_proposal_nesting_guard guard( _push_proposal_nesting_depth, *this ); + push_proposal_nesting_guard guard( _push_proposal_nesting_depth, _push_proposal_current_id, + *this, proposal ); if( _undo_db.size() >= _undo_db.max_size() ) _undo_db.set_max_size( _undo_db.size() + 1 ); auto session = _undo_db.start_undo_session(true); diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf new file mode 100644 index 0000000000..5230120e4c --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #1479 nodes crashing on self-approving proposal +#ifndef HARDFORK_CORE_1479_TIME +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545302820 )) // 2018-12-20T10:47:00Z +#endif diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..0643a59d0a 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -538,6 +538,7 @@ namespace graphene { namespace chain { // Counts nested proposal updates uint32_t _push_proposal_nesting_depth = 0; + uint64_t _push_proposal_current_id = UINT64_MAX; /// Tracks assets affected by bitshares-core issue #453 before hard fork #615 in one block flat_set _issue_453_affected_assets; diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 813cff42d4..4ce77a23a5 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1677,4 +1677,37 @@ BOOST_AUTO_TEST_CASE( issue_214 ) BOOST_CHECK_EQUAL( top.amount.amount.value, get_balance( bob_id, top.amount.asset_id ) ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( self_approving_proposal ) +{ try { + ACTORS( (alice) ); + fund( alice ); + + generate_blocks( HARDFORK_CORE_1479_TIME ); + trx.clear(); + set_expiration( db, trx ); + + proposal_update_operation pup; + pup.fee_paying_account = alice_id; + pup.proposal = proposal_id_type(0); + pup.active_approvals_to_add.insert( alice_id ); + + proposal_create_operation pop; + pop.proposed_ops.emplace_back(pup); + pop.fee_paying_account = alice_id; + pop.expiration_time = db.head_block_time() + fc::days(1); + trx.operations.push_back(pop); + const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get(); + trx.clear(); + BOOST_REQUIRE_EQUAL( 0, pid1.instance.value ); + db.get(pid1); + + trx.operations.push_back(pup); + PUSH_TX( db, trx, ~0 ); + + // The inner push_proposal fails, but that doesn't make the inner + // proposal_update fail. Therefore, the outer push_proposal succeeds and the + // proposal is removed. + BOOST_CHECK_THROW( db.get(pid1), fc::assert_exception ); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From d3cab53a0689f404e1ad037feaf313326e77a508 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 20 Dec 2018 17:49:21 +0100 Subject: [PATCH 115/132] Fixed hf date --- libraries/chain/hardfork.d/CORE_1479.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf index 5230120e4c..f26fe237a4 100644 --- a/libraries/chain/hardfork.d/CORE_1479.hf +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -1,4 +1,4 @@ // bitshares-core issue #1479 nodes crashing on self-approving proposal #ifndef HARDFORK_CORE_1479_TIME -#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545302820 )) // 2018-12-20T10:47:00Z +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545299220 )) // 2018-12-20T09:47:00Z #endif From 80e9664318045fdcd9794c4c4694d1975db24d4c Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 20 Dec 2018 20:42:27 +0100 Subject: [PATCH 116/132] Revert fix attempt --- libraries/chain/db_block.cpp | 14 +++----------- .../chain/include/graphene/chain/database.hpp | 1 - 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e98e1616d6..efc5562a89 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -270,27 +270,20 @@ processed_transaction database::validate_transaction( const signed_transaction& class push_proposal_nesting_guard { public: - push_proposal_nesting_guard( uint32_t& nesting_counter, uint64_t& prop_id, - const database& db, const proposal_object& proposal ) - : orig_value(nesting_counter), counter(nesting_counter), orig_id(prop_id), current_id(prop_id) + push_proposal_nesting_guard( uint32_t& nesting_counter, const database& db ) + : orig_value(nesting_counter), counter(nesting_counter) { FC_ASSERT( counter < db.get_global_properties().active_witnesses.size() * 2, "Max proposal nesting depth exceeded!" ); - if( db.head_block_time() >= HARDFORK_CORE_1479_TIME ) - FC_ASSERT( proposal.id.instance() < current_id, "Cannot approve proposals that didn't exist at time of creation!" ); counter++; - current_id = proposal.id.instance(); } ~push_proposal_nesting_guard() { if( --counter != orig_value ) elog( "Unexpected proposal nesting count value: ${n} != ${o}", ("n",counter)("o",orig_value) ); - current_id = orig_id; } private: const uint32_t orig_value; uint32_t& counter; - const uint64_t orig_id; - uint64_t& current_id; }; processed_transaction database::push_proposal(const proposal_object& proposal) @@ -304,8 +297,7 @@ processed_transaction database::push_proposal(const proposal_object& proposal) size_t old_applied_ops_size = _applied_ops.size(); try { - push_proposal_nesting_guard guard( _push_proposal_nesting_depth, _push_proposal_current_id, - *this, proposal ); + push_proposal_nesting_guard guard( _push_proposal_nesting_depth, *this ); if( _undo_db.size() >= _undo_db.max_size() ) _undo_db.set_max_size( _undo_db.size() + 1 ); auto session = _undo_db.start_undo_session(true); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 0643a59d0a..f0fb8e11c6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -538,7 +538,6 @@ namespace graphene { namespace chain { // Counts nested proposal updates uint32_t _push_proposal_nesting_depth = 0; - uint64_t _push_proposal_current_id = UINT64_MAX; /// Tracks assets affected by bitshares-core issue #453 before hard fork #615 in one block flat_set _issue_453_affected_assets; From 793220eed2ff3bbd64a489845307d25e451fb965 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:02:28 -0500 Subject: [PATCH 117/132] added extend test + smaller misc --- libraries/chain/db_notify.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 1 - tests/tests/htlc_tests.cpp | 32 +++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index e044bd843d..3def684732 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -363,7 +363,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun break; } case htlc_object_type:{ const auto& htlc_obj = dynamic_cast(obj); - FC_ASSERT( htlc_obj != nullptr); + FC_ASSERT( htlc_obj != nullptr ); accounts.insert( htlc_obj->from ); accounts.insert( htlc_obj->to ); break; diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 37512a0d1f..9306e2b2eb 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -90,7 +90,6 @@ namespace graphene { htlc_obj = &db().get(o.htlc_id); FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); const htlc_redeem_visitor vtor( o.preimage ); FC_ASSERT( htlc_obj->preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 1f659ea89e..0a25a6ee3f 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -223,6 +223,7 @@ try { BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { +try { ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); @@ -263,24 +264,40 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // make sure Alice's money gets put on hold (100 - 20 - 4(fee) ) BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // TODO: make sure Bob (or anyone) can see the details of the transaction + // extend the timeout so that Bob has more time + { + graphene::chain::htlc_extend_operation extend_operation; + extend_operation.htlc_id = alice_htlc_id; + extend_operation.seconds_to_add = 86400; + extend_operation.update_issuer = alice_id; + extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation ); + trx.operations.push_back( extend_operation ); + sign( trx, alice_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_blocks( db.head_block_time() + fc::seconds(87000) ); + set_expiration( db, trx ); + } + + // make sure Alice's money is still on hold, and account for extra fee + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // send an update operation to claim the funds + // send a redeem operation to claim the funds { graphene::chain::htlc_redeem_operation update_operation; update_operation.redeemer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); - trx.operations.push_back(update_operation); + trx.operations.push_back( update_operation ); sign(trx, bob_private_key); try { - PUSH_TX(db, trx, ~0); + PUSH_TX( db, trx, ~0 ); } - catch (fc::exception& ex) + catch ( fc::exception& ex ) { - BOOST_FAIL(ex.what()); + BOOST_FAIL( ex.to_detail_string() ); } generate_block(); trx.clear(); @@ -288,7 +305,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // verify funds end up in Bob's account (100 + 20 - 4(fee) ) BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 116 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( other_peoples_money ) From 57248daa1b3cca8965135df2bddc93f23a88975b Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:14:42 -0500 Subject: [PATCH 118/132] handle fee multiplication overflow --- libraries/chain/protocol/htlc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index b176c6e85d..11f046e2cf 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -32,17 +32,20 @@ namespace graphene { namespace chain { FC_ASSERT( amount.amount > 0 ); } - share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const + share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params )const { - uint64_t days = (claim_period_seconds + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; - return fee_params.fee + (fee_params.fee_per_day * days); + uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; + // multiply with overflow check + uint64_t per_day_fee = fee_params.fee_per_day * days; + FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + return fee_params.fee + per_day_fee; } void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } - share_type htlc_redeem_operation::calculate_fee(const fee_parameters_type& fee_params)const + share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { return fee_params.fee + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; @@ -52,9 +55,11 @@ namespace graphene { namespace chain { FC_ASSERT( fee.amount >= 0 ); } - share_type htlc_extend_operation::calculate_fee(const fee_parameters_type& fee_params)const + share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const { - uint32_t days = (seconds_to_add + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; - return fee_params.fee + (fee_params.fee_per_day * days); + uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; + uint64_t per_day_fee = fee_params.fee_per_day * days; + FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + return fee_params.fee + per_day_fee; } } } From e8172e92ee0d83d661a99988a904dbcfb3aa6f9d Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:20:43 -0500 Subject: [PATCH 119/132] added messages to asserts --- libraries/chain/protocol/htlc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 11f046e2cf..e9be01b5e5 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,8 +28,8 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( amount.amount > 0 ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); + FC_ASSERT( amount.amount > 0, "HTLC amount should not be zero" ); } share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params )const @@ -37,12 +37,12 @@ namespace graphene { namespace chain { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check uint64_t per_day_fee = fee_params.fee_per_day * days; - FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); return fee_params.fee + per_day_fee; } void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); } share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const @@ -52,14 +52,14 @@ namespace graphene { namespace chain { } void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( fee.amount >= 0 , "Fee amount should not be zero"); } share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const { uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; uint64_t per_day_fee = fee_params.fee_per_day * days; - FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); return fee_params.fee + per_day_fee; } } } From 9c7f69329822a665c7bc58eddbe7d8571ac6678f Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Thu, 20 Dec 2018 21:57:33 +0100 Subject: [PATCH 120/132] Different approach - detect nested proposal_update with invalid id and prevent execution before hf / forbid after hf --- libraries/chain/hardfork.d/CORE_1479.hf | 2 +- .../graphene/chain/proposal_evaluator.hpp | 19 +++++++++++++ libraries/chain/proposal_evaluator.cpp | 28 +++++++++++++++++++ tests/tests/authority_tests.cpp | 6 ++-- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf index f26fe237a4..5347c8e88a 100644 --- a/libraries/chain/hardfork.d/CORE_1479.hf +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -1,4 +1,4 @@ // bitshares-core issue #1479 nodes crashing on self-approving proposal #ifndef HARDFORK_CORE_1479_TIME -#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545299220 )) // 2018-12-20T09:47:00Z +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545350400 )) // 2018-12-21T00:00:00Z #endif diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index 36f99ccfbe..b8cfe9cfed 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -28,6 +28,23 @@ namespace graphene { namespace chain { + class hardfork_visitor_1479 + { + public: + typedef void result_type; + + uint64_t max_update_instance = 0; + uint64_t nested_update_count = 0; + + template + void operator()(const T &v) const {} + + void operator()(const proposal_update_operation &v); + + // loop and self visit in proposals + void operator()(const graphene::chain::proposal_create_operation &v); + }; + class proposal_create_evaluator : public evaluator { public: @@ -37,6 +54,8 @@ namespace graphene { namespace chain { object_id_type do_apply( const proposal_create_operation& o ); transaction _proposed_trx; + + hardfork_visitor_1479 vtor_1479; }; class proposal_update_evaluator : public evaluator diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 8124cc4fda..8a12fc13f3 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -113,6 +113,20 @@ struct hardfork_visitor_214 // non-recursive proposal visitor } }; +void hardfork_visitor_1479::operator()(const proposal_update_operation &v) +{ + if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance ) + max_update_instance = v.proposal.instance.value; + nested_update_count++; +} + +// loop and self visit in proposals +void hardfork_visitor_1479::operator()(const graphene::chain::proposal_create_operation &v) +{ + for (const op_wrapper &op : v.proposed_ops) + op.op.visit(*this); +} + void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) { try { const database& d = db(); @@ -128,6 +142,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for (const op_wrapper &op : o.proposed_ops) op.op.visit( hf214 ); } + vtor_1479( o ); const auto& global_parameters = d.get_global_properties().parameters; @@ -199,6 +214,19 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati std::set_difference(required_active.begin(), required_active.end(), proposal.required_owner_approvals.begin(), proposal.required_owner_approvals.end(), std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin())); + + if( d.head_block_time() > HARDFORK_CORE_1479_TIME ) + FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance, "Cannot update a proposal with a future id!" ); + else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance ) + { + // prevent approval + transfer_operation top; + top.from = GRAPHENE_COMMITTEE_ACCOUNT; + top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; + top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY ); + proposal.proposed_transaction.operations.emplace_back( top ); + ilog( "Issue 1479: ${p}", ("p",proposal) ); + } }); return proposal.id; diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 4ce77a23a5..af0f7f347e 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1704,10 +1704,8 @@ BOOST_AUTO_TEST_CASE( self_approving_proposal ) trx.operations.push_back(pup); PUSH_TX( db, trx, ~0 ); - // The inner push_proposal fails, but that doesn't make the inner - // proposal_update fail. Therefore, the outer push_proposal succeeds and the - // proposal is removed. - BOOST_CHECK_THROW( db.get(pid1), fc::assert_exception ); + // Proposal failed and still exists + db.get(pid1); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From 2f08968c5d4044220663cd4cd0ccaa49d41d68a5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 19:59:46 -0500 Subject: [PATCH 121/132] virtual op for notifications --- libraries/chain/db_notify.cpp | 7 +++++- libraries/chain/htlc_evaluator.cpp | 3 +++ .../include/graphene/chain/protocol/htlc.hpp | 24 +++++++++++++++++++ .../graphene/chain/protocol/operations.hpp | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 3def684732..17e71e044f 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -259,12 +259,17 @@ struct get_impacted_account_visitor } void operator()( const htlc_create_operation& op ) { - _impacted.insert( op.fee_payer() ); + _impacted.insert( op.fee_payer() ); + _impacted.insert( op.to ); } void operator()( const htlc_redeem_operation& op ) { _impacted.insert( op.fee_payer() ); } + void operator()( const htlc_redeemed_operation& op ) + { + _impacted.insert( op.from ); + } void operator()( const htlc_extend_operation& op ) { _impacted.insert( op.fee_payer() ); diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 9306e2b2eb..218558a98d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -100,6 +100,9 @@ namespace graphene { void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { db().adjust_balance(htlc_obj->to, htlc_obj->amount); + // notify related parties + htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->from, htlc_obj->to, htlc_obj->amount ); + db().push_applied_operation( virt_op ); db().remove(*htlc_obj); return void_result(); } diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 310282da12..ef5f2b1469 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -118,6 +118,28 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const; }; + /** + * virtual op to assist with notifying related parties + */ + struct htlc_redeemed_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_redeemed_operation() {} + htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, asset amount) : + htlc_id(htlc_id), from(from), to(to), amount(amount) {} + + account_id_type fee_payer()const { return to; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + account_id_type from, to; + asset amount; + asset fee; + }; + struct htlc_extend_operation : public base_operation { struct fee_parameters_type { @@ -177,11 +199,13 @@ FC_REFLECT_TYPENAME( graphene::chain::htlc_hash ) FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) +FC_REFLECT( graphene::chain::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_redeemed_operation, (fee)(htlc_id)(from)(to)(amount) ) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 3f5c27d998..9b5ffabb37 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -99,6 +99,7 @@ namespace graphene { namespace chain { asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, + htlc_redeemed_operation, // VIRTUAL htlc_extend_operation, htlc_refund_operation // VIRTUAL > operation; From 062613c4e936a7fd88ac85d718ad0ecccbdef5dd Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 10:42:29 +0100 Subject: [PATCH 122/132] Catch nested delete as well --- .../include/graphene/chain/proposal_evaluator.hpp | 2 ++ libraries/chain/proposal_evaluator.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index b8cfe9cfed..04b5c62d22 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -41,6 +41,8 @@ namespace graphene { namespace chain { void operator()(const proposal_update_operation &v); + void operator()(const proposal_delete_operation &v); + // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v); }; diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 8a12fc13f3..42f4f0779f 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -120,6 +120,13 @@ void hardfork_visitor_1479::operator()(const proposal_update_operation &v) nested_update_count++; } +void hardfork_visitor_1479::operator()(const proposal_delete_operation &v) +{ + if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance ) + max_update_instance = v.proposal.instance.value; + nested_update_count++; +} + // loop and self visit in proposals void hardfork_visitor_1479::operator()(const graphene::chain::proposal_create_operation &v) { @@ -216,7 +223,8 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin())); if( d.head_block_time() > HARDFORK_CORE_1479_TIME ) - FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance, "Cannot update a proposal with a future id!" ); + FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance, + "Cannot update/delete a proposal with a future id!" ); else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance ) { // prevent approval From 46daa678feab3d6c812bcfe3464c06f4237aca01 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 10:43:43 +0100 Subject: [PATCH 123/132] Bump db version --- libraries/chain/include/graphene/chain/config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 32eeb898cc..e97bacc032 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.181127" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.181221" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From 36cb35cffd4caf6ac1076178b7e3d22446f45108 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 10:44:41 +0100 Subject: [PATCH 124/132] Switched impossible authority to null account --- libraries/chain/proposal_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 42f4f0779f..a0e0228763 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -229,7 +229,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati { // prevent approval transfer_operation top; - top.from = GRAPHENE_COMMITTEE_ACCOUNT; + top.from = GRAPHENE_NULL_ACCOUNT; top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY ); proposal.proposed_transaction.operations.emplace_back( top ); From 5cb908e87f1be3a7621ac6e018e6d4c1e0bb2cf6 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 10:46:55 +0100 Subject: [PATCH 125/132] Bumped hf date by 24 hours --- libraries/chain/hardfork.d/CORE_1479.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf index 5347c8e88a..2f8ee807e3 100644 --- a/libraries/chain/hardfork.d/CORE_1479.hf +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -1,4 +1,4 @@ // bitshares-core issue #1479 nodes crashing on self-approving proposal #ifndef HARDFORK_CORE_1479_TIME -#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545350400 )) // 2018-12-21T00:00:00Z +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545436800 )) // 2018-12-22T00:00:00Z #endif From b0ada12b01648e8136b9029a95991c5278f6501b Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 11:17:39 +0100 Subject: [PATCH 126/132] Log as warning not info --- libraries/chain/proposal_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index a0e0228763..fce7fd2a35 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -233,7 +233,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY ); proposal.proposed_transaction.operations.emplace_back( top ); - ilog( "Issue 1479: ${p}", ("p",proposal) ); + wlog( "Issue 1479: ${p}", ("p",proposal) ); } }); From 35bc344697b08daaaa6aaf457648e0eecb796da6 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 11:17:54 +0100 Subject: [PATCH 127/132] Added self-deletion test case --- tests/tests/authority_tests.cpp | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index af0f7f347e..5a2528acb0 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1708,4 +1708,39 @@ BOOST_AUTO_TEST_CASE( self_approving_proposal ) db.get(pid1); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( self_deleting_proposal ) +{ try { + ACTORS( (alice) ); + fund( alice ); + + generate_blocks( HARDFORK_CORE_1479_TIME ); + trx.clear(); + set_expiration( db, trx ); + + proposal_delete_operation pdo; + pdo.fee_paying_account = alice_id; + pdo.proposal = proposal_id_type(0); + pdo.using_owner_authority = false; + + proposal_create_operation pop; + pop.proposed_ops.emplace_back( pdo ); + pop.fee_paying_account = alice_id; + pop.expiration_time = db.head_block_time() + fc::days(1); + trx.operations.push_back( pop ); + const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get(); + trx.clear(); + BOOST_REQUIRE_EQUAL( 0, pid1.instance.value ); + db.get(pid1); + + proposal_update_operation pup; + pup.fee_paying_account = alice_id; + pup.proposal = proposal_id_type(0); + pup.active_approvals_to_add.insert( alice_id ); + trx.operations.push_back(pup); + PUSH_TX( db, trx, ~0 ); + + // Proposal failed and still exists + db.get(pid1); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From 22ea563f2856327290dd57dd00ddbd215415ee3b Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 21 Dec 2018 08:40:47 -0500 Subject: [PATCH 128/132] More overflow protection + wording changes --- libraries/chain/protocol/htlc.cpp | 13 +++++++++---- tests/tests/htlc_tests.cpp | 9 +-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index e9be01b5e5..9f60e7dec8 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,8 +28,8 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); - FC_ASSERT( amount.amount > 0, "HTLC amount should not be zero" ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); + FC_ASSERT( amount.amount > 0, "HTLC amount should be greater than zero" ); } share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params )const @@ -42,17 +42,22 @@ namespace graphene { namespace chain { } void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); } share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { + if (fee_params.fee_per_kb == 0) + return fee_params.fee; + uint64_t product = 1024 * fee_params.fee_per_kb; + FC_ASSERT( product / 1024 == fee_params.fee_per_kb, "Fee calculation overflow"); return fee_params.fee + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; + } void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 , "Fee amount should not be zero"); + FC_ASSERT( fee.amount >= 0 , "Fee amount should not be negative"); } share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 0a25a6ee3f..98518f6484 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -291,14 +291,7 @@ try { update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); trx.operations.push_back( update_operation ); sign(trx, bob_private_key); - try - { - PUSH_TX( db, trx, ~0 ); - } - catch ( fc::exception& ex ) - { - BOOST_FAIL( ex.to_detail_string() ); - } + PUSH_TX( db, trx, ~0 ); generate_block(); trx.clear(); } From 8e77fff86cb847dc4b6b5b5b43c020312d4a0a0c Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 21 Dec 2018 09:37:45 -0500 Subject: [PATCH 129/132] fix bug in fee calculation --- libraries/chain/protocol/htlc.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 9f60e7dec8..645feb6dbf 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -47,13 +47,10 @@ namespace graphene { namespace chain { share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { - if (fee_params.fee_per_kb == 0) - return fee_params.fee; - uint64_t product = 1024 * fee_params.fee_per_kb; - FC_ASSERT( product / 1024 == fee_params.fee_per_kb, "Fee calculation overflow"); - return fee_params.fee - + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; - + uint64_t kb = ( preimage.size() + 1023 ) / 1024; + uint64_t product = kb * fee_params.fee_per_kb; + FC_ASSERT( kb == 0 || product / kb == fee_params.fee_per_kb, "Fee calculation overflow"); + return fee_params.fee + product; } void htlc_extend_operation::validate()const { From 19375f7b6aa1c8c95d6213c2b53ff285f707a2f7 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 17:12:44 +0100 Subject: [PATCH 130/132] Fixed build error --- libraries/chain/include/graphene/chain/protocol/types.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 170baa6b19..87536f42ee 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -38,7 +38,6 @@ // TODO: move this to fc #include -FC_REFLECT_TYPENAME( fc::sha1 ) namespace fc { namespace raw { template inline void pack( T& ds, const fc::sha1& ep, uint32_t _max_depth = 1 ) { From c4c8e412f6950a999a29d3987c6619e02459f4e1 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 18:43:46 +0100 Subject: [PATCH 131/132] Set testnet hardfork date to 2018-12-21T20:00:00Z --- libraries/chain/hardfork.d/CORE_1479.hf | 2 +- libraries/chain/hardfork.d/core-1468.hf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf index 2f8ee807e3..69157dc3d1 100644 --- a/libraries/chain/hardfork.d/CORE_1479.hf +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -1,4 +1,4 @@ // bitshares-core issue #1479 nodes crashing on self-approving proposal #ifndef HARDFORK_CORE_1479_TIME -#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545436800 )) // 2018-12-22T00:00:00Z +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545422400 )) // 2018-12-21T20:00:00Z // testnet #endif diff --git a/libraries/chain/hardfork.d/core-1468.hf b/libraries/chain/hardfork.d/core-1468.hf index a669d57c84..005e1902b4 100644 --- a/libraries/chain/hardfork.d/core-1468.hf +++ b/libraries/chain/hardfork.d/core-1468.hf @@ -1,4 +1,4 @@ // HTLC implementation #ifndef HARDFORK_CORE_1468_TIME -#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1600000000 ) ) +#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1545422400 ) ) // 2018-12-21T20:00:00Z // testnet #endif From c5fa14d24a6944c7d633812043b615d7a634818a Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 18:57:59 +0100 Subject: [PATCH 132/132] Generalized testcase for testnet --- tests/tests/database_api_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 1eeb177b42..7a37b2a318 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -845,7 +845,7 @@ BOOST_AUTO_TEST_CASE(verify_account_authority) // bad keys flat_set bad_public_keys; - bad_public_keys.emplace(public_key_type("BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); + bad_public_keys.emplace(public_key_type(GRAPHENE_ADDRESS_PREFIX "6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); BOOST_CHECK(!db_api.verify_account_authority( "nathan", bad_public_keys)); } FC_LOG_AND_RETHROW()