From a0e4ac59ff5d6b9874f94bab63265d70038f8ea5 Mon Sep 17 00:00:00 2001 From: gladcow Date: Fri, 1 Nov 2019 16:43:34 +0300 Subject: [PATCH 01/64] [SON-160] Fix create_vesting wallet_api call (#206) * Fix create_vesting wallet_api call * change type for vesting_type in create_vesting_balance --- .../wallet/include/graphene/wallet/wallet.hpp | 12 +++++---- libraries/wallet/wallet.cpp | 27 ++++++++----------- tests/cli/son.cpp | 5 ++-- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 9b3282c4..349ccea8 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1429,15 +1429,17 @@ class wallet_api /** Creates a vesting deposit owned by the given account. * - * @param owner_account the name or id of the account - * @param amount the amount to deposit + * @param owner_account vesting balance owner and creator (the name or id) + * @param amount amount to vest + * @param asset_symbol the symbol of the asset to vest * @param vesting_type "normal", "gpos" or "son" * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction registering a vesting object */ - signed_transaction create_vesting(string owner_account, + signed_transaction create_vesting_balance(string owner_account, string amount, - string vesting_type, + string asset_symbol, + vesting_balance_type vesting_type, bool broadcast = false); /** @@ -2118,7 +2120,7 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) - (create_vesting) + (create_vesting_balance) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 4244b7ef..edc59a65 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2107,27 +2107,21 @@ public: return sign_transaction( tx, broadcast ); } - signed_transaction create_vesting(string owner_account, + signed_transaction create_vesting_balance(string owner_account, string amount, - string vesting_type, + string asset_symbol, + vesting_balance_type vesting_type, bool broadcast /* = false */) { try { account_object son_account = get_account(owner_account); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Invalid asset symbol {asst}", ("asst", asset_symbol)); vesting_balance_create_operation op; op.creator = son_account.get_id(); op.owner = son_account.get_id(); - op.amount = asset_object().amount_from_string(amount); - if (vesting_type == "normal") - op.balance_type = vesting_balance_type::normal; - else if (vesting_type == "gpos") - op.balance_type = vesting_balance_type::gpos; - else if (vesting_type == "son") - op.balance_type = vesting_balance_type::son; - else - { - FC_ASSERT( false, "unknown vesting type value ${vt}", ("vt", vesting_type) ); - } + op.amount = asset_obj->amount_from_string(amount); + op.balance_type = vesting_type; if (op.balance_type == vesting_balance_type::son) op.policy = dormant_vesting_policy_initializer {}; @@ -4271,12 +4265,13 @@ committee_member_object wallet_api::get_committee_member(string owner_account) return my->get_committee_member(owner_account); } -signed_transaction wallet_api::create_vesting(string owner_account, +signed_transaction wallet_api::create_vesting_balance(string owner_account, string amount, - string vesting_type, + string asset_symbol, + vesting_balance_type vesting_type, bool broadcast /* = false */) { - return my->create_vesting(owner_account, amount, vesting_type, broadcast); + return my->create_vesting_balance(owner_account, amount, asset_symbol, vesting_type, broadcast); } signed_transaction wallet_api::create_son(string owner_account, diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 1dfd8381..49779dfd 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -78,11 +78,12 @@ public: BOOST_CHECK(fixture_.generate_block()); // create deposit vesting - fixture_.con.wallet_api_ptr->create_vesting(account_name, "50000000", "son", true); + fixture_.con.wallet_api_ptr->create_vesting_balance(account_name, + "50", "1.3.0", vesting_balance_type::son, true); BOOST_CHECK(fixture_.generate_block()); // create pay_vb vesting - fixture_.con.wallet_api_ptr->create_vesting(account_name, "1000000", "normal", true); + fixture_.con.wallet_api_ptr->create_vesting_balance(account_name, "1", "1.3.0", vesting_balance_type::normal, true); BOOST_CHECK(fixture_.generate_block()); // check deposits are here From e4eb3e6ce3c519729dcb1876486827f1af9bbb6c Mon Sep 17 00:00:00 2001 From: gladcow Date: Fri, 1 Nov 2019 19:56:00 +0300 Subject: [PATCH 02/64] [SON-113] Fix several issues in update_son_votes call in wallet_api (#208) * do not allow update votes with both empty lists * fix error messages * check number of sons against votes number in account_object * Update error message --- libraries/chain/protocol/account.cpp | 11 +++++++++-- libraries/wallet/wallet.cpp | 9 +++++---- tests/cli/son.cpp | 27 ++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index cf592d5c..6721bb07 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -171,15 +171,22 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; + auto needed_sons = num_son; for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) --needed_witnesses; else if ( id.type() == vote_id_type::committee && needed_committee ) --needed_committee; + else if ( id.type() == vote_id_type::son && needed_sons ) + --needed_sons; - FC_ASSERT( needed_witnesses == 0 && needed_committee == 0, - "May not specify fewer witnesses or committee members than the number voted for."); + FC_ASSERT( needed_witnesses == 0, + "May not specify fewer witnesses than the number voted for."); + FC_ASSERT( needed_committee == 0, + "May not specify fewer committee members than the number voted for."); + FC_ASSERT( needed_sons == 0, + "May not specify fewer SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index edc59a65..69f3cb7a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2266,26 +2266,27 @@ public: uint16_t desired_number_of_sons, bool broadcast /* = false */) { try { + FC_ASSERT(sons_to_approve.size() || sons_to_reject.size(), "Both accepted and rejected lists can't be empty simultaneously"); account_object voting_account_object = get_account(voting_account); for (const std::string& son : sons_to_approve) { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); if (!son_obj) - FC_THROW("Account ${son} is not registered as a witness", ("son", son)); + FC_THROW("Account ${son} is not registered as a SON", ("son", son)); auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); if (!insert_result.second) - FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); } for (const std::string& son : sons_to_reject) { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); if (!son_obj) - FC_THROW("Account ${son} is not registered as a son", ("son", son)); + FC_THROW("Account ${son} is not registered as a SON", ("son", son)); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); if (!votes_removed) - FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } voting_account_object.options.num_son = desired_number_of_sons; diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 49779dfd..b6c7b887 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) accepted.push_back("son1account"); accepted.push_back("son2account"); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 15, true); + rejected, 2, true); BOOST_CHECK(generate_block()); BOOST_CHECK(generate_maintenance_block()); @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); rejected.push_back("son1account"); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 15, true); + rejected, 1, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes @@ -432,7 +432,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); rejected.push_back("son1accnt"); BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 15, true), fc::exception); + rejected, 1, true), fc::exception); BOOST_CHECK(generate_block()); // Verify the votes @@ -450,7 +450,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); rejected.push_back("son2account"); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 15, true); + rejected, 0, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes @@ -469,7 +469,24 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.push_back("son1accnt"); accepted.push_back("son1accnt"); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 15, true), fc::exception); + rejected, 1, true), fc::exception); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes == son2_start_votes); + son2_start_votes = son2_end_votes; + + // Try to accept and reject empty lists + accepted.clear(); + rejected.clear(); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes From 22f76a04c01c0fe68ef8346bacae3c6d1e95ec90 Mon Sep 17 00:00:00 2001 From: gladcow Date: Wed, 6 Nov 2019 17:58:32 +0300 Subject: [PATCH 03/64] list_active_sons api call implementation --- .../wallet/include/graphene/wallet/wallet.hpp | 7 +++++ libraries/wallet/wallet.cpp | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 349ccea8..2b511ba7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1363,6 +1363,13 @@ class wallet_api */ map list_sons(const string& lowerbound, uint32_t limit); + /** Lists active at the moment SONs. + * This returns a list of all account names that own active SON, and the associated SON id, + * sorted by name. + * @returns a list of active SONs mapping SON names to SON ids + */ + map list_active_sons(); + /** Creates a witness object owned by the given account. * * An account can have at most one witness object. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 69f3cb7a..c427f373 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1932,6 +1932,32 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + map list_active_sons() + { try { + global_property_object gpo = get_global_properties(); + std::vector> son_objects = + _remote_db->get_sons(gpo.active_sons); + vector owners; + owners.resize(son_objects.size()); + std::transform(son_objects.begin(), son_objects.end(), owners.begin(), + [](const fc::optional& obj) { + if(!obj) + FC_THROW("Invalid active SONs list in global properties."); + return obj->son_account; + }); + vector> accs = _remote_db->get_accounts(owners); + map result; + std::transform(accs.begin(), accs.end(), gpo.active_sons.begin(), + std::inserter(result, result.end()), + [](fc::optional& acct, + son_id_type& sid) { + if(!acct) + FC_THROW("Invalid active SONs list in global properties."); + return std::make_pair(string(acct->name), std::move(sid)); + }); + return result; + } FC_CAPTURE_AND_RETHROW() } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -4303,6 +4329,11 @@ map wallet_api::list_sons(const string& lowerbound, uint32_ return my->_remote_db->lookup_son_accounts(lowerbound, limit); } +map wallet_api::list_active_sons() +{ + return my->list_active_sons(); +} + signed_transaction wallet_api::create_witness(string owner_account, string url, bool broadcast /* = false */) From 8c4eb579a724ec52265b3e0a8553637c5964e006 Mon Sep 17 00:00:00 2001 From: gladcow Date: Thu, 7 Nov 2019 13:03:32 +0300 Subject: [PATCH 04/64] unit test for list_active_sons --- tests/cli/son.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index b6c7b887..b72bf567 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -530,6 +530,62 @@ BOOST_AUTO_TEST_CASE( related_functions ) BOOST_TEST_MESSAGE("SON-related functions cli wallet tests end"); } +BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons begin"); + try + { + son_test_helper sth(*this); + + signed_transaction vote_tx; + global_property_object gpo; + + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count; + + // create son accounts + for(unsigned int i = 0; i < son_number + 1; i++) + { + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), false); + } + BOOST_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number + 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + + for(unsigned int i = 1; i < son_number; i++) + { + std::string name1 = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + + BOOST_CHECK(gpo.active_sons.size() == son_number); + + map active_sons = con.wallet_api_ptr->list_active_sons(); + BOOST_CHECK(active_sons.size() == son_number); + for(unsigned int i = 1; i < son_number + 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + BOOST_CHECK(active_sons.find(name) != active_sons.end()); + } + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons end"); +} BOOST_AUTO_TEST_SUITE_END() From 62f973ca80ad3a2b8359300f6196a488b9eb20ed Mon Sep 17 00:00:00 2001 From: gladcow Date: Mon, 11 Nov 2019 12:54:54 +0300 Subject: [PATCH 05/64] fix code style --- libraries/wallet/wallet.cpp | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index c427f373..f769fd12 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1934,28 +1934,26 @@ public: map list_active_sons() { try { - global_property_object gpo = get_global_properties(); - std::vector> son_objects = - _remote_db->get_sons(gpo.active_sons); - vector owners; - owners.resize(son_objects.size()); - std::transform(son_objects.begin(), son_objects.end(), owners.begin(), - [](const fc::optional& obj) { - if(!obj) - FC_THROW("Invalid active SONs list in global properties."); - return obj->son_account; - }); - vector> accs = _remote_db->get_accounts(owners); - map result; - std::transform(accs.begin(), accs.end(), gpo.active_sons.begin(), - std::inserter(result, result.end()), - [](fc::optional& acct, - son_id_type& sid) { - if(!acct) - FC_THROW("Invalid active SONs list in global properties."); - return std::make_pair(string(acct->name), std::move(sid)); - }); - return result; + global_property_object gpo = get_global_properties(); + std::vector> son_objects = _remote_db->get_sons(gpo.active_sons); + vector owners; + owners.resize(son_objects.size()); + std::transform(son_objects.begin(), son_objects.end(), owners.begin(), + [](const fc::optional& obj) { + if(!obj) + FC_THROW("Invalid active SONs list in global properties."); + return obj->son_account; + }); + vector> accs = _remote_db->get_accounts(owners); + map result; + std::transform(accs.begin(), accs.end(), gpo.active_sons.begin(), + std::inserter(result, result.end()), + [](fc::optional& acct, son_id_type& sid) { + if(!acct) + FC_THROW("Invalid active SONs list in global properties."); + return std::make_pair(string(acct->name), std::move(sid)); + }); + return result; } FC_CAPTURE_AND_RETHROW() } signed_transaction create_witness(string owner_account, From dbf6be02c5a4def3d4c2e4405bc9ec517b9a8386 Mon Sep 17 00:00:00 2001 From: gladcow Date: Mon, 11 Nov 2019 13:13:57 +0300 Subject: [PATCH 06/64] use assert instead of checking condition with low possibility --- 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 f769fd12..f7cc2a51 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1940,8 +1940,7 @@ public: owners.resize(son_objects.size()); std::transform(son_objects.begin(), son_objects.end(), owners.begin(), [](const fc::optional& obj) { - if(!obj) - FC_THROW("Invalid active SONs list in global properties."); + FC_ASSERT(obj, "Invalid active SONs list in global properties."); return obj->son_account; }); vector> accs = _remote_db->get_accounts(owners); @@ -1949,8 +1948,7 @@ public: std::transform(accs.begin(), accs.end(), gpo.active_sons.begin(), std::inserter(result, result.end()), [](fc::optional& acct, son_id_type& sid) { - if(!acct) - FC_THROW("Invalid active SONs list in global properties."); + FC_ASSERT(acct, "Invalid active SONs list in global properties."); return std::make_pair(string(acct->name), std::move(sid)); }); return result; From ae781e48afb2f080aa8e60e562a6945ce9bf235f Mon Sep 17 00:00:00 2001 From: Sandip Patel Date: Tue, 12 Nov 2019 00:23:14 +0530 Subject: [PATCH 07/64] Fixed betting tests (#217) * Fixed betting tests * Removed comments --- libraries/chain/db_init.cpp | 2 +- libraries/db/include/graphene/db/index.hpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 7dc986a4..72841afe 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -263,7 +263,7 @@ void database::initialize_indexes() acnt_index->add_secondary_index(); add_index< primary_index >(); // 256 members per chunk - add_index< primary_index >(); // 256 sons per chunk + add_index< primary_index >(); add_index< primary_index >(); // 1024 witnesses per chunk add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index e8d4fa11..1bc593f4 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -402,15 +402,6 @@ namespace graphene { namespace db { DerivedIndex::remove(obj); } - virtual const object& insert( object&& obj )override - { - const auto& res = DerivedIndex::insert(std::move(obj)); - for( const auto& item : _sindex ) - item->object_inserted( res ); - on_add(res); - return res; - } - virtual void modify( const object& obj, const std::function& m )override { save_undo( obj ); From 76b95729e1c0208715b1e4e18ea43835f4c1eab4 Mon Sep 17 00:00:00 2001 From: Sandip Patel Date: Thu, 21 Nov 2019 13:34:29 +0530 Subject: [PATCH 08/64] removed unrelated parameter description from delete_son --- libraries/wallet/include/graphene/wallet/wallet.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 2b511ba7..6a78d8d3 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1340,8 +1340,6 @@ class wallet_api * An account can have at most one witness object. * * @param owner_account the name or id of the account which is creating the witness - * @param url a URL to include in the witness record in the blockchain. Clients may - * display this when showing a list of witnesses. May be blank. * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction registering a witness */ From 749fc2f330f379c4c8af047f48b443cfb4a8db04 Mon Sep 17 00:00:00 2001 From: obucinac Date: Wed, 4 Dec 2019 18:52:00 +0100 Subject: [PATCH 09/64] Add Bitcoin network listener to a SON plugin (#196) * Add Bitcoin network listener to a SON plugin * Add default parameters for Peerplays Bitcoin test node * Add Bitcoin block processing * Update source code to last designs * Set default parameters for peerplays_sidechain plugin to Bitcoin test server * WIP: Some Bitcoin transaction processing --- Dockerfile | 1 + README.md | 7 +- .../peerplays_sidechain/CMakeLists.txt | 5 +- .../plugins/peerplays_sidechain/addresses.txt | 391 +++++++++++++++++ .../graphene/peerplays_sidechain/defs.hpp | 70 +++ .../peerplays_sidechain_plugin.hpp | 7 +- .../sidechain_net_handler.hpp | 33 ++ .../sidechain_net_handler_bitcoin.hpp | 101 +++++ .../sidechain_net_manager.hpp | 25 ++ .../peerplays_sidechain_plugin.cpp | 43 +- .../sidechain_net_handler.cpp | 35 ++ .../sidechain_net_handler_bitcoin.cpp | 403 ++++++++++++++++++ .../sidechain_net_manager.cpp | 35 ++ 13 files changed, 1137 insertions(+), 19 deletions(-) create mode 100644 libraries/plugins/peerplays_sidechain/addresses.txt create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp diff --git a/Dockerfile b/Dockerfile index 8a970e39..dc4caae4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ RUN \ libreadline-dev \ libssl-dev \ libtool \ + libzmq3-dev \ locales \ ntp \ pkg-config \ diff --git a/README.md b/README.md index 8207bb29..941afa68 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ This is a quick introduction to get new developers and witnesses up to speed on The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS: ``` - sudo apt-get install gcc-5 g++-5 cmake make libbz2-dev\ - libdb++-dev libdb-dev libssl-dev openssl libreadline-dev\ - autoconf libtool git + sudo apt-get install autoconf bash build-essential ca-certificates cmake \ + doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ + libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \ + wget ``` ## Build Boost 1.67.0 diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 931d4f45..4941ce51 100644 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -2,9 +2,12 @@ file(GLOB HEADERS "include/graphene/peerplays_sidechain/*.hpp") add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp + sidechain_net_manager.cpp + sidechain_net_handler.cpp + sidechain_net_handler_bitcoin.cpp ) -target_link_libraries( peerplays_sidechain graphene_chain graphene_app ) +target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/addresses.txt b/libraries/plugins/peerplays_sidechain/addresses.txt new file mode 100644 index 00000000..df57167d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/addresses.txt @@ -0,0 +1,391 @@ +2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J +{ + "address": "2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J", + "scriptPubKey": "a914873aad1ecf7510c80b83d8ca94d21432dc71b88787", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/2']0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd))#7s0qfnvz", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00141d30ac0c47f7b32460265daf49c5925236f5882d", + "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "1d30ac0c47f7b32460265daf49c5925236f5882d", + "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", + "address": "bcrt1qr5c2crz877ejgcpxtkh5n3vj2gm0tzpdqrw3n0", + "scriptPubKey": "00141d30ac0c47f7b32460265daf49c5925236f5882d" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/2'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2MxAnE469fhhdvUqUB7daU997VSearb2mn7 +{ + "address": "2MxAnE469fhhdvUqUB7daU997VSearb2mn7", + "scriptPubKey": "a914360175a50918495a20573aed68d506a790420fe587", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/3']02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d))#p3st4e4e", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00146c1c0571f3132eb0702f487123d2026495592830", + "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "6c1c0571f3132eb0702f487123d2026495592830", + "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", + "address": "bcrt1qdswq2u0nzvhtqup0fpcj85szvj24j2psgfwy6w", + "scriptPubKey": "00146c1c0571f3132eb0702f487123d2026495592830" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/3'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL +{ + "address": "2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL", + "scriptPubKey": "a914bdce56e7f2fc04614c0f6f4d1d59fff63b0b73f187", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/4']020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462))#3r63c8fu", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00146abd0d5f055df80b41bc6449328eda09f61a2be3", + "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "6abd0d5f055df80b41bc6449328eda09f61a2be3", + "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", + "address": "bcrt1qd27s6hc9thuqksduv3yn9rk6p8mp52lr2e846e", + "scriptPubKey": "00146abd0d5f055df80b41bc6449328eda09f61a2be3" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/4'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR +{ + "address": "2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR", + "scriptPubKey": "a914b7abe6d957106da3a21782eea1164f4964b521ba87", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/5']03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce))#e3sfze3l", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8", + "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "7a4fd72ff8e192004c70d8139b4ca53e1467d8c8", + "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", + "address": "bcrt1q0f8awtlcuxfqqnrsmqfekn998c2x0kxgf4t7dm", + "scriptPubKey": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/5'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL +{ + "address": "2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL", + "scriptPubKey": "a914dcb019e8330b4fffc50ba22bbf90215922a1379787", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/6']028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013))#3326m2za", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652", + "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "ccd3dee026a1d641352f5b8c7d72805d75fd0652", + "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", + "address": "bcrt1qenfaacpx58tyzdf0twx86u5qt46l6pjjef5y7u", + "scriptPubKey": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/6'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG +{ + "address": "2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG", + "scriptPubKey": "a9144cb2b8f97d8e7ad5bfb81afd611394387f374ab887", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/7']02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b))#7gvhakzu", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", + "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", + "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", + "address": "bcrt1quds85rm5tchm3vz0u8a87puvx5qf5a7pqw8u7l", + "scriptPubKey": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/7'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg +{ + "address": "2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg", + "scriptPubKey": "a914dae51c6601ef4e05817f67d57d3ac0c8cb64948e87", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/8']03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b))#ckuy6v83", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7", + "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "60c1c7776b1f92cd36fe6138b99212ebe6381fe7", + "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", + "address": "bcrt1qvrquwamtr7fv6dh7vyutnysja0nrs8l8vzcnwj", + "scriptPubKey": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/8'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX +{ + "address": "2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX", + "scriptPubKey": "a91413930c6d40f5f01b169f2fc7884c2b3984ff8a3087", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/9']022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac))#f8cdpnn9", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4", + "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "35b75b7c34a7f908955b8b93aaa677aa47fab2c4", + "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", + "address": "bcrt1qxkm4klp55lus392m3wf64fnh4frl4vkyng4ffg", + "scriptPubKey": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/9'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM +{ + "address": "2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM", + "scriptPubKey": "a9145e91543069ae37d8bace2f59aade945f5916dc3287", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/10']03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af))#x7xpvc0y", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", + "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", + "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", + "address": "bcrt1qe6e4hqvckkx8dlx83fcdr5aza5w5xf0jd22xet", + "scriptPubKey": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/10'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy +{ + "address": "2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy", + "scriptPubKey": "a914e132c578bd294a01a472d42b376b29e5ef6678c987", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/11']02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b))#2janhauz", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762", + "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "831d63f8d99be71f9b385f2ccc92ab2783da3762", + "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", + "address": "bcrt1qsvwk87xen0n3lxectukvey4ty7pa5dmz0yanuv", + "scriptPubKey": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/11'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp new file mode 100644 index 00000000..1b6a9099 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +enum network { + bitcoin, + //ethereum +}; + +using bytes = std::vector; + +struct prev_out +{ + bool operator!=( const prev_out& obj ) const + { + if( this->hash_tx != obj.hash_tx || + this->n_vout != obj.n_vout || + this->amount != obj.amount ) + { + return true; + } + return false; + } + + std::string hash_tx; + uint32_t n_vout; + uint64_t amount; +}; + +struct info_for_vin +{ + info_for_vin() = default; + + info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes(), bool _resend = false ); + + bool operator!=( const info_for_vin& obj ) const; + + struct comparer { + bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const; + }; + + static uint64_t count_id_info_for_vin; + uint64_t id; + + fc::sha256 identifier; + + prev_out out; + std::string address; + bytes script; + + bool used = false; + bool resend = false; +}; + +struct sidechain_event_data { + network sidechain; + std::string transaction_id; + std::string from; + std::string to; + int64_t amount; +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index d32fb09d..45628223 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -1,10 +1,9 @@ #pragma once #include -#include -#include -#include +#include +#include namespace graphene { namespace peerplays_sidechain { using namespace chain; @@ -30,5 +29,5 @@ class peerplays_sidechain_plugin : public graphene::app::plugin std::unique_ptr my; }; -} } //graphene::peerplays_sidechain_plugin +} } //graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp new file mode 100644 index 00000000..fa4f0b50 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler { +public: + sidechain_net_handler(const boost::program_options::variables_map& options); + virtual ~sidechain_net_handler(); + + std::vector get_user_sidechain_address_mapping(); + +protected: + graphene::peerplays_sidechain::network network; + + virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; + virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; + virtual std::string sign_transaction( const std::string& transaction ) = 0; + virtual std::string send_transaction( const std::string& transaction ) = 0; + + virtual void handle_event( const std::string& event_data ) = 0; + +private: + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp new file mode 100644 index 00000000..792aaf45 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class bitcoin_rpc_client { +public: + bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ; + std::string receive_full_block( const std::string& block_hash ); + int32_t receive_confirmations_tx( const std::string& tx_hash ); + bool receive_mempool_entry_tx( const std::string& tx_hash ); + uint64_t receive_estimated_fee(); + void send_btc_tx( const std::string& tx_hex ); + bool connection_is_not_defined() const; + +private: + + fc::http::reply send_post_request( std::string body ); + + std::string ip; + uint32_t rpc_port; + std::string user; + std::string password; + + fc::http::header authorization; +}; + +// ============================================================================= + +class zmq_listener { +public: + zmq_listener( std::string _ip, uint32_t _zmq ); + bool connection_is_not_defined() const { return zmq_port == 0; } + + fc::signal event_received; +private: + void handle_zmq(); + std::vector receive_multipart(); + + std::string ip; + uint32_t zmq_port; + + zmq::context_t ctx; + zmq::socket_t socket; +}; + +// ============================================================================= + +class sidechain_net_handler_bitcoin : public sidechain_net_handler { +public: + sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options); + virtual ~sidechain_net_handler_bitcoin(); + + void update_tx_infos( const std::string& block_hash ); + + //void update_tx_approvals(); + + //void update_estimated_fee(); + + //void send_btc_tx( const sidechain::bitcoin_transaction& trx ); + + bool connection_is_not_defined() const; + + std::string create_multisignature_wallet( const std::vector public_keys ); + std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); + std::string sign_transaction( const std::string& transaction ); + std::string send_transaction( const std::string& transaction ); + +private: + std::string ip; + uint32_t zmq_port; + uint32_t rpc_port; + std::string rpc_user; + std::string rpc_password; + + std::unique_ptr listener; + std::unique_ptr bitcoin_client; + graphene::chain::database* db; + + void handle_event( const std::string& event_data); + + std::vector extract_info_from_block( const std::string& _block ); + + void update_transaction_status( std::vector trx_for_check ); + + std::set get_valid_vins( const std::string tx_hash ); + + inline uint64_t parse_amount(std::string raw); + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp new file mode 100644 index 00000000..49073314 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_manager { +public: + sidechain_net_manager(); + virtual ~sidechain_net_manager(); + + bool create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options); +private: + + std::vector> net_handlers; + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 36d0b713..9b993614 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,11 +1,15 @@ #include +#include +#include + +namespace bpo = boost::program_options; + namespace graphene { namespace peerplays_sidechain { namespace detail { - class peerplays_sidechain_plugin_impl { public: @@ -16,8 +20,8 @@ class peerplays_sidechain_plugin_impl peerplays_sidechain_plugin& _self; - uint32_t parameter; - uint32_t optional_parameter; + peerplays_sidechain::sidechain_net_manager _net_manager; + }; peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() @@ -48,8 +52,22 @@ void peerplays_sidechain_plugin::plugin_set_program_options( ) { cli.add_options() - ("parameter", boost::program_options::value(), "Parameter") - ("optional-parameter", boost::program_options::value(), "Optional parameter") + //("bitcoin-node-ip", bpo::value()->implicit_value("127.0.0.1"), "IP address of Bitcoin node") + //("bitcoin-node-zmq-port", bpo::value()->implicit_value(28332), "ZMQ port of Bitcoin node") + //("bitcoin-node-rpc-port", bpo::value()->implicit_value(18332), "RPC port of Bitcoin node") + //("bitcoin-node-rpc-user", bpo::value(), "Bitcoin RPC user") + //("bitcoin-node-rpc-password", bpo::value(), "Bitcoin RPC password") + //("bitcoin-address", bpo::value(), "Bitcoin address") + //("bitcoin-public-key", bpo::value(), "Bitcoin public key") + //("bitcoin-private-key", bpo::value(), "Bitcoin private key") + ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") + ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") + ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") + ("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user") + ("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password") + ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") + ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") + ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") ; cfg.add(cli); } @@ -58,11 +76,13 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options: { ilog("peerplays sidechain plugin: plugin_initialize()"); - if (options.count("parameter")) { - my->parameter = options["optional-parameter"].as(); - } - if (options.count("optional-parameter")) { - my->optional_parameter = options["optional-parameter"].as(); + if( options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) + && options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) + && options.count( "bitcoin-address" ) && options.count( "bitcoin-public-key" ) && options.count( "bitcoin-private-key" ) ) + { + my->_net_manager.create_handler(network::bitcoin, options); + } else { + wlog("Haven't set up bitcoin sidechain parameters"); } } @@ -71,4 +91,5 @@ void peerplays_sidechain_plugin::plugin_startup() ilog("peerplays sidechain plugin: plugin_startup()"); } -} } +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp new file mode 100644 index 00000000..fefeacc1 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -0,0 +1,35 @@ +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler::sidechain_net_handler(const boost::program_options::variables_map& options) { +} + +sidechain_net_handler::~sidechain_net_handler() { +} + +std::vector sidechain_net_handler::get_user_sidechain_address_mapping() { + std::vector result; + + switch (network) { + case network::bitcoin: + result.push_back("2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J"); + result.push_back("2MxAnE469fhhdvUqUB7daU997VSearb2mn7"); + result.push_back("2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL"); + result.push_back("2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR"); + result.push_back("2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL"); + //result.push_back("2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG"); + //result.push_back("2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg"); + //result.push_back("2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX"); + //result.push_back("2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM"); + //result.push_back("2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy"); + + default: + assert(false); + } + + return result; +} + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp new file mode 100644 index 00000000..1fce21ea --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -0,0 +1,403 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "graphene/peerplays_sidechain/sidechain_net_manager.hpp" + +namespace graphene { namespace peerplays_sidechain { + +// ============================================================================= + +bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password ): + ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password ) +{ + authorization.key = "Authorization"; + authorization.val = "Basic " + fc::base64_encode( user + ":" + password ); +} + +std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash ) +{ + fc::http::connection conn; + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + + const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json"; + + const auto reply = conn.request( "GET", url ); + if ( reply.status != 200 ) + return ""; + + ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) ); + return std::string( reply.body.begin(), reply.body.end() ); +} + +//int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash ) +//{ +// const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") + +// std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }"); +// +// const auto reply = send_post_request( body ); +// +// if ( reply.status != 200 ) +// return 0; +// +// const auto result = std::string( reply.body.begin(), reply.body.end() ); +// +// std::stringstream ss( result ); +// boost::property_tree::ptree tx; +// boost::property_tree::read_json( ss, tx ); +// +// if( tx.count( "result" ) ) { +// if( tx.get_child( "result" ).count( "confirmations" ) ) { +// return tx.get_child( "result" ).get_child( "confirmations" ).get_value(); +// } +// } +// return 0; +//} + +bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash ) +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") + + std::string("\"") + tx_hash + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request( body ); + + if ( reply.status != 200 ) + return false; + + return true; +} + +uint64_t bitcoin_rpc_client::receive_estimated_fee() +{ + static const auto confirmation_target_blocks = 6; + + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") + + std::to_string(confirmation_target_blocks) + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.status != 200 ) + return 0; + + std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) ); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( json.count( "result" ) ) + if ( json.get_child( "result" ).count( "feerate" ) ) { + auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value(); + feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() ); + return std::stoll( feerate_str ); + } + return 0; +} + +void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") + + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + return; + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump(( tx_hex )); + return; + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value(); + if( error_code == -27 ) // transaction already in block chain + return; + + wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) ); + } +} + +bool bitcoin_rpc_client::connection_is_not_defined() const +{ + return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); +} + +fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) +{ + fc::http::connection conn; + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + + const auto url = "http://" + ip + ":" + std::to_string( rpc_port ); + + return conn.request( "POST", url, body, fc::http::headers{authorization} ); +} + +// ============================================================================= + +zmq_listener::zmq_listener( std::string _ip, uint32_t _zmq ): ip( _ip ), zmq_port( _zmq ), ctx( 1 ), socket( ctx, ZMQ_SUB ) { + std::thread( &zmq_listener::handle_zmq, this ).detach(); +} + +std::vector zmq_listener::receive_multipart() { + std::vector msgs; + + int32_t more; + size_t more_size = sizeof( more ); + while ( true ) { + zmq::message_t msg; + socket.recv( &msg, 0 ); + socket.getsockopt( ZMQ_RCVMORE, &more, &more_size ); + + if ( !more ) + break; + msgs.push_back( std::move(msg) ); + } + + return msgs; +} + +void zmq_listener::handle_zmq() { + socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 9 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); + socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) ); + + while ( true ) { + auto msg = receive_multipart(); + const auto header = std::string( static_cast( msg[0].data() ), msg[0].size() ); + const auto hash = boost::algorithm::hex( std::string( static_cast( msg[1].data() ), msg[1].size() ) ); + + event_received( hash ); + } +} + +// ============================================================================= + +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options) : + sidechain_net_handler(options) { + network = peerplays_sidechain::network::bitcoin; + + ip = options.at("bitcoin-node-ip").as(); + zmq_port = options.at("bitcoin-node-zmq-port").as(); + rpc_port = options.at("bitcoin-node-rpc-port").as(); + rpc_user = options.at("bitcoin-node-rpc-user").as(); + rpc_password = options.at("bitcoin-node-rpc-password").as(); + + fc::http::connection conn; + try { + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + } catch ( fc::exception e ) { + elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip) ("port", rpc_port) ); + FC_ASSERT( false ); + } + + listener = std::unique_ptr( new zmq_listener( ip, zmq_port ) ); + bitcoin_client = std::unique_ptr( new bitcoin_rpc_client( ip, rpc_port, rpc_user, rpc_password ) ); + //db = _db; + + listener->event_received.connect([this]( const std::string& event_data ) { + std::thread( &sidechain_net_handler_bitcoin::handle_event, this, event_data ).detach(); + } ); + + //db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) { + // std::thread( &sidechain_net_handler_bitcoin::send_btc_tx, this, trx ).detach(); + //} ); +} + +sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { +} + +void sidechain_net_handler_bitcoin::update_tx_infos( const std::string& block_hash ) +{ + std::string block = bitcoin_client->receive_full_block( block_hash ); + if( block != "" ) { + const auto& vins = extract_info_from_block( block ); +// const auto& addr_idx = db->get_index_type().indices().get(); +// for( const auto& v : vins ) { +// const auto& addr_itr = addr_idx.find( v.address ); +// FC_ASSERT( addr_itr != addr_idx.end() ); +// db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() ); +// } + } +} + +//void sidechain_net_handler_bitcoin::update_tx_approvals() +//{ +// std::vector trx_for_check; +// const auto& confirmations_num = db->get_sidechain_params().confirmations_num; +// +// db->bitcoin_confirmations.safe_for([&]( btc_tx_confirmations_index::iterator itr_b, btc_tx_confirmations_index::iterator itr_e ){ +// for(auto iter = itr_b; iter != itr_e; iter++) { +// db->bitcoin_confirmations.modify( iter->transaction_id, [&]( bitcoin_transaction_confirmations& obj ) { +// obj.count_block++; +// }); +// +// if( iter->count_block == confirmations_num ) { +// trx_for_check.push_back( iter->transaction_id ); +// } +// } +// }); +// +// update_transaction_status( trx_for_check ); +// +//} + +//void sidechain_net_handler_bitcoin::update_estimated_fee() +//{ +// db->estimated_feerate = bitcoin_client->receive_estimated_fee(); +//} + +//void sidechain_net_handler_bitcoin::send_btc_tx( const sidechain::bitcoin_transaction& trx ) +//{ +// std::set valid_vins; +// for( const auto& v : trx.vin ) { +// valid_vins.insert( v.prevout.hash ); +// } +// db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid(), valid_vins ) ); +// +// FC_ASSERT( !bitcoin_client->connection_is_not_defined() ); +// const auto tx_hex = fc::to_hex( pack( trx ) ); +// +// bitcoin_client->send_btc_tx( tx_hex ); +//} + +bool sidechain_net_handler_bitcoin::connection_is_not_defined() const +{ + return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); +} + +std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction( const std::string& transaction ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& transaction ) +{ + return ""; +} + +void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { + ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); + ilog(" event_data: ${event_data}", ("event_data", event_data)); + //update_tx_approvals(); + //update_estimated_fee(); + //update_tx_infos( block_hash ); +} + +std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) +{ + std::stringstream ss( _block ); + boost::property_tree::ptree block; + boost::property_tree::read_json( ss, block ); + + std::vector result; + + const auto& addr_idx = get_user_sidechain_address_mapping();// db->get_index_type().indices().get(); + + for (const auto& tx_child : block.get_child("tx")) { + const auto& tx = tx_child.second; + + for ( const auto& o : tx.get_child("vout") ) { + const auto script = o.second.get_child("scriptPubKey"); + + if( !script.count("addresses") ) continue; + + for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses? + const auto address_base58 = addr.second.get_value(); + + auto it = find(addr_idx.begin(), addr_idx.end(), address_base58); + if (it == addr_idx.end()) continue; + + info_for_vin vin; + vin.out.hash_tx = tx.get_child("txid").get_value(); + vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value() ); + vin.out.n_vout = o.second.get_child( "n" ).get_value(); + vin.address = address_base58; + result.push_back( vin ); + } + } + } + + return result; +} + +//void sidechain_net_handler_bitcoin::update_transaction_status( std::vector trx_for_check ) +//{ +// const auto& confirmations_num = db->get_sidechain_params().confirmations_num; +// +// for( const auto& trx : trx_for_check ) { +// auto confirmations = bitcoin_client->receive_confirmations_tx( trx.str() ); +// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { +// obj.count_block = confirmations; +// }); +// +// if( confirmations >= confirmations_num ) { +// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { +// obj.confirmed = true; +// }); +// +// } else if( confirmations == 0 ) { +// auto is_in_mempool = bitcoin_client->receive_mempool_entry_tx( trx.str() ); +// +// std::set valid_vins; +// if( !is_in_mempool ) { +// valid_vins = get_valid_vins( trx.str() ); +// } +// +// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { +// obj.missing = !is_in_mempool; +// obj.valid_vins = valid_vins; +// }); +// } +// } +//} + +//std::set sidechain_net_handler_bitcoin::get_valid_vins( const std::string tx_hash ) +//{ +// const auto& confirmations_obj = db->bitcoin_confirmations.find( fc::sha256( tx_hash ) ); +// FC_ASSERT( confirmations_obj.valid() ); +// +// std::set valid_vins; +// for( const auto& v : confirmations_obj->valid_vins ) { +// auto confirmations = bitcoin_client->receive_confirmations_tx( v.str() ); +// if( confirmations == 0 ) { +// continue; +// } +// valid_vins.insert( v ); +// } +// return valid_vins; +//} + +// Removes dot from amount output: "50.00000000" +inline uint64_t sidechain_net_handler_bitcoin::parse_amount(std::string raw) { + raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end()); + return std::stoll(raw); +} + +// ============================================================================= + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp new file mode 100644 index 00000000..e1c0bce6 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_manager::sidechain_net_manager() { + ilog(__FUNCTION__); +} + +sidechain_net_manager::~sidechain_net_manager() { + ilog(__FUNCTION__); +} + +bool sidechain_net_manager::create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options) { + ilog(__FUNCTION__); + + bool ret_val = false; + + switch (network) { + case network::bitcoin: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + } + default: + assert(false); + } + + return ret_val; +} + +} } // graphene::peerplays_sidechain + From d03bfa81f70da861cab3cc61415d2cefb23d90c1 Mon Sep 17 00:00:00 2001 From: gladcow Date: Sun, 8 Dec 2019 09:43:05 +0300 Subject: [PATCH 10/64] [SON-199] Fix unit tests (#233) * fix app_test * fix son_delete_test --- libraries/chain/son_evaluator.cpp | 1 + tests/CMakeLists.txt | 2 +- tests/app/main.cpp | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index c54d1391..ad581348 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -83,6 +83,7 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op) linear_vesting_policy new_vesting_policy; new_vesting_policy.begin_timestamp = db().head_block_time(); new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period(); + new_vesting_policy.begin_balance = deposit.balance.amount; db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) { vbo.policy = new_vesting_policy; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 472da676..cf633dfd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,7 @@ target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_ file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) -target_link_libraries( app_test graphene_app graphene_account_history graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB INTENSE_SOURCES "intense/*.cpp") add_executable( intense_test ${INTENSE_SOURCES} ${COMMON_SOURCES} ) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 98f19c19..b3775b1f 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -29,6 +29,8 @@ #include #include +#include +#include #include #include @@ -57,6 +59,8 @@ BOOST_AUTO_TEST_CASE( two_node_network ) graphene::app::application app1; app1.register_plugin(); + app1.register_plugin(); + app1.register_plugin(); boost::program_options::variables_map cfg; cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); app1.initialize(app_dir.path(), cfg); @@ -72,6 +76,8 @@ BOOST_AUTO_TEST_CASE( two_node_network ) graphene::app::application app2; app2.register_plugin(); + app2.register_plugin(); + app2.register_plugin(); cfg2.erase("p2p-endpoint"); cfg2.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); cfg2.emplace("seed-node", boost::program_options::variable_value(vector{endpoint1}, false)); From 876bbc8c6d87b62fdeeaa7de864b77227494639a Mon Sep 17 00:00:00 2001 From: obucinac Date: Wed, 11 Dec 2019 14:58:47 +0100 Subject: [PATCH 11/64] Add peerplays account for a SON in a config/command line options (#231) --- .../peerplays_sidechain_plugin.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 9b993614..50739bef 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace bpo = boost::program_options; @@ -51,15 +52,15 @@ void peerplays_sidechain_plugin::plugin_set_program_options( boost::program_options::options_description& cfg ) { + auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); + string son_id_example = fc::json::to_string(chain::son_id_type(5)); + cli.add_options() - //("bitcoin-node-ip", bpo::value()->implicit_value("127.0.0.1"), "IP address of Bitcoin node") - //("bitcoin-node-zmq-port", bpo::value()->implicit_value(28332), "ZMQ port of Bitcoin node") - //("bitcoin-node-rpc-port", bpo::value()->implicit_value(18332), "RPC port of Bitcoin node") - //("bitcoin-node-rpc-user", bpo::value(), "Bitcoin RPC user") - //("bitcoin-node-rpc-password", bpo::value(), "Bitcoin RPC password") - //("bitcoin-address", bpo::value(), "Bitcoin address") - //("bitcoin-public-key", bpo::value(), "Bitcoin public key") - //("bitcoin-private-key", bpo::value(), "Bitcoin private key") + ("son-id,w", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) + ("peerplays-private-key", bpo::value>()->composing()->multitoken()-> + DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), + "Tuple of [PublicKey, WIF private key]") + ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") From 1d5878db28eafca237e37cfbeb6923b7be652e1c Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 13 Dec 2019 00:06:38 +1100 Subject: [PATCH 12/64] SON193-SON200- SON Heartbeats and maintenance mode changes (#241) * SON193-SON200- SON Heartbeats and maintenance mode changes * SON193-SON200- SON Heartbeats and maintenance tests --- libraries/app/impacted.cpp | 3 + libraries/chain/db_init.cpp | 1 + libraries/chain/db_notify.cpp | 3 + .../graphene/chain/protocol/operations.hpp | 3 +- .../include/graphene/chain/protocol/son.hpp | 16 ++++ .../include/graphene/chain/son_evaluator.hpp | 9 ++ .../include/graphene/chain/son_object.hpp | 9 +- libraries/chain/proposal_evaluator.cpp | 4 + libraries/chain/son_evaluator.cpp | 47 +++++++++++ tests/tests/son_operations_tests.cpp | 84 +++++++++++++++++++ 10 files changed, 176 insertions(+), 3 deletions(-) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 997a3a38..66dc2939 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -307,6 +307,9 @@ struct get_impacted_account_visitor void operator()( const son_delete_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_heartbeat_operation& op ){ + _impacted.insert( op.owner_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 72841afe..956e34a7 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -247,6 +247,7 @@ void database::initialize_evaluators() 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 63b0fce8..c71b2930 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -294,6 +294,9 @@ struct get_impacted_account_visitor void operator()( const son_delete_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_heartbeat_operation& op ) { + _impacted.insert( op.owner_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index cbad1e05..a0e72094 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -139,7 +139,8 @@ namespace graphene { namespace chain { sweeps_vesting_claim_operation, son_create_operation, son_update_operation, - son_delete_operation + son_delete_operation, + son_heartbeat_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 93b8a0a4..08e74a2d 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -47,6 +47,19 @@ namespace graphene { namespace chain { share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; + struct son_heartbeat_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type owner_account; + time_point_sec ts; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + } } // namespace graphene::chain FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) ) @@ -59,3 +72,6 @@ FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(n FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(payer)(owner_account) ) + +FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/son_evaluator.hpp b/libraries/chain/include/graphene/chain/son_evaluator.hpp index bb6a1820..6b82f5e5 100644 --- a/libraries/chain/include/graphene/chain/son_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/son_evaluator.hpp @@ -31,4 +31,13 @@ public: void_result do_apply(const son_delete_operation& o); }; +class son_heartbeat_evaluator : public evaluator +{ +public: + typedef son_heartbeat_operation operation_type; + + void_result do_evaluate(const son_heartbeat_operation& o); + object_id_type do_apply(const son_heartbeat_operation& o); +}; + } } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 77316a4d..4cbff5ed 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -10,7 +10,8 @@ namespace graphene { namespace chain { { inactive, active, - in_maintenance + in_maintenance, + deregistered }; /** * @class son_statistics_object @@ -31,8 +32,12 @@ namespace graphene { namespace chain { uint64_t txs_signed = 0; // Total Downtime barring the current down time in seconds, used for stats to present to user uint64_t total_downtime = 0; + // Current Interval Downtime since last maintenance + uint64_t current_interval_downtime = 0; // Down timestamp, if son status is in_maintenance use this fc::time_point_sec last_down_timestamp; + // Last Active heartbeat timestamp + fc::time_point_sec last_active_timestamp; }; /** @@ -87,7 +92,7 @@ namespace graphene { namespace chain { using son_stats_index = generic_index; } } // graphene::chain -FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance) ) +FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance)(deregistered) ) FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) ) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index d377e0d8..b50d4b82 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -148,6 +148,10 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_delete_operation not allowed yet!" ); } + void operator()(const son_heartbeat_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" ); + } + // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index ad581348..4300bdbb 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -94,4 +94,51 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op) return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } +void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_id) != idx.end() ); + auto itr = idx.find(op.son_id); + auto stats = itr->statistics( db() ); + // Inactive SONs need not send heartbeats + FC_ASSERT(itr->status == son_status::active || itr->status == son_status::in_maintenance, "Inactive SONs need not send heartbeats"); + // Account for network delays + fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); + // Account for server ntp sync difference + fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(2 * db().block_interval()); + FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); + FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); + FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); + FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + if(itr->status == son_status::in_maintenance) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); + sso.last_active_timestamp = op.ts; + } ); + + db().modify(*itr, [&op](son_object &so) { + so.status = son_status::active; + }); + } else if (itr->status == son_status::active) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.last_active_timestamp = op.ts; + } ); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + } } // namespace graphene::chain diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index a458b45e..3740335c 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -652,4 +652,88 @@ BOOST_AUTO_TEST_CASE( son_witness_proposal_test ) generate_block(); } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { + + try + { + INVOKE(create_son_test); + GET_ACTOR(alice); + + { + // Send Heartbeat for an inactive SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = fc::time_point::now(); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + // Modify SON's status to in_maintenance + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + db.modify( *son_stats_obj, [&]( son_statistics_object& _s) + { + _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(1)); + }); + + uint64_t downtime = 0; + + { + generate_block(); + // Send Heartbeat for an in_maintenance SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); + downtime = op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); + BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + + { + generate_block(); + // Send Heartbeat for an active SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime); + BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From 6d5b86a8e597491b66951cab9671343f4c5f9961 Mon Sep 17 00:00:00 2001 From: obucinac Date: Wed, 18 Dec 2019 19:30:38 +0100 Subject: [PATCH 13/64] User sidechain address mappings (#240) * WIP: Sidechain objects * Revert "WIP: Sidechain objects" This reverts commit 8676940a281604688771e96ceb1e65a35d98e8e5. * WIP: User sidechain address mappings * Fix reflection problem * Reflect missing members of sidechain_address_update_operation * Add sidechain address operation tests * Enable RPC calls * Fix build errors due to merge conflict * Fix RPC, add CLI wallet commands for sidechain addresses * Improved peerplays_sidechain_plugin_impl * Remove short param for son-id * Fix crashing errors on bitcoin event received * Code review changes --- libraries/app/CMakeLists.txt | 2 +- libraries/app/api.cpp | 5 + libraries/app/database_api.cpp | 110 ++++++++++-- libraries/app/impacted.cpp | 9 + .../app/include/graphene/app/database_api.hpp | 48 ++++++ libraries/chain/CMakeLists.txt | 4 +- libraries/chain/db_init.cpp | 7 + libraries/chain/db_notify.cpp | 14 ++ .../graphene/chain/protocol/operations.hpp | 6 +- .../chain/protocol/sidechain_address.hpp | 66 ++++++++ .../include/graphene/chain/protocol/types.hpp | 5 + .../chain/sidechain_address_evaluator.hpp | 34 ++++ .../chain/sidechain_address_object.hpp | 70 ++++++++ .../chain/sidechain_address_evaluator.cpp | 70 ++++++++ .../graphene/peerplays_sidechain/defs.hpp | 8 +- .../peerplays_sidechain_plugin.hpp | 2 + .../sidechain_net_handler.hpp | 10 +- .../sidechain_net_handler_bitcoin.hpp | 19 +-- .../sidechain_net_manager.hpp | 7 +- .../peerplays_sidechain_plugin.cpp | 139 +++++++++++---- .../sidechain_net_handler.cpp | 45 +++-- .../sidechain_net_handler_bitcoin.cpp | 158 ++++-------------- .../sidechain_net_manager.cpp | 13 +- libraries/wallet/CMakeLists.txt | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 84 ++++++++++ libraries/wallet/wallet.cpp | 117 +++++++++++++ programs/js_operation_serializer/main.cpp | 1 + tests/CMakeLists.txt | 2 +- tests/tests/sidechain_addresses_test.cpp | 143 ++++++++++++++++ 29 files changed, 972 insertions(+), 228 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_address_object.hpp create mode 100644 libraries/chain/sidechain_address_evaluator.cpp create mode 100644 tests/tests/sidechain_addresses_test.cpp diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 93e540f9..e6f8940c 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -14,7 +14,7 @@ add_library( graphene_app # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie ) +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 833069f8..d31abe19 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -443,6 +443,11 @@ namespace graphene { namespace app { assert( aobj != nullptr ); accounts.insert( aobj->son_account ); break; + } case sidechain_address_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->sidechain_address_account ); + break; } case sport_object_type: case event_group_object_type: diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b9ae31b6..c4566d24 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -103,7 +103,7 @@ class database_api_impl : public std::enable_shared_from_this uint64_t get_asset_count()const; // Peerplays - vector list_sports() const; + vector list_sports() const; vector list_event_groups(sport_id_type sport_id) const; vector list_events_in_group(event_group_id_type event_group_id) const; vector list_betting_market_groups(event_id_type) const; @@ -115,14 +115,14 @@ class database_api_impl : public std::enable_shared_from_this vector get_lotteries( asset_id_type stop = asset_id_type(), unsigned limit = 100, asset_id_type start = asset_id_type() )const; - vector get_account_lotteries( account_id_type issuer, + vector get_account_lotteries( account_id_type issuer, asset_id_type stop, unsigned limit, asset_id_type start )const; asset get_lottery_balance( asset_id_type lottery_id )const; sweeps_vesting_balance_object get_sweeps_vesting_balance_object( account_id_type account )const; asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const; - + // Markets / feeds vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; vector get_call_orders(asset_id_type a, uint32_t limit)const; @@ -152,6 +152,13 @@ class database_api_impl : public std::enable_shared_from_this map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_son_count()const; + // Sidechain addresses + vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; + vector> get_sidechain_addresses_by_account(account_id_type account)const; + vector> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const; + fc::optional get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const; + uint64_t get_sidechain_addresses_count()const; + // Votes vector lookup_vote_ids( const vector& votes )const; @@ -528,11 +535,11 @@ vector> database_api::get_key_references( vector> database_api_impl::get_key_references( vector keys )const { wdump( (keys) ); - + 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()); @@ -648,7 +655,7 @@ std::map database_api_impl::get_full_accounts( const 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) @@ -738,7 +745,7 @@ std::map database_api_impl::get_full_accounts( const acnt.withdraws.emplace_back(withdraw); }); - auto pending_payouts_range = + auto pending_payouts_range = _db.get_index_type().indices().get().equal_range(boost::make_tuple(account->id)); std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments)); @@ -1058,7 +1065,7 @@ vector database_api_impl::get_lotteries( asset_id_type stop, return result; } -vector database_api::get_account_lotteries( account_id_type issuer, +vector database_api::get_account_lotteries( account_id_type issuer, asset_id_type stop, unsigned limit, asset_id_type start )const @@ -1066,7 +1073,7 @@ vector database_api::get_account_lotteries( account_id_type issuer return my->get_account_lotteries( issuer, stop, limit, start ); } -vector database_api_impl::get_account_lotteries( account_id_type issuer, +vector database_api_impl::get_account_lotteries( account_id_type issuer, asset_id_type stop, unsigned limit, asset_id_type start )const @@ -1763,6 +1770,85 @@ uint64_t database_api_impl::get_son_count()const return _db.get_index_type().indices().size(); } +////////////////////////////////////////////////////////////////////// +// // +// Sidechain Accounts // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_sidechain_addresses(const vector& sidechain_address_ids)const +{ + return my->get_sidechain_addresses( sidechain_address_ids ); +} + +vector> database_api_impl::get_sidechain_addresses(const vector& sidechain_address_ids)const +{ + vector> result; result.reserve(sidechain_address_ids.size()); + std::transform(sidechain_address_ids.begin(), sidechain_address_ids.end(), std::back_inserter(result), + [this](sidechain_address_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +vector> database_api::get_sidechain_addresses_by_account(account_id_type account)const +{ + return my->get_sidechain_addresses_by_account( account ); +} + +vector> database_api_impl::get_sidechain_addresses_by_account(account_id_type account)const +{ + vector> result; + const auto& sidechain_addresses_range = _db.get_index_type().indices().get().equal_range(account); + std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second, + [&result] (const sidechain_address_object& sao) { + result.push_back(sao); + }); + return result; +} + +vector> database_api::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const +{ + return my->get_sidechain_addresses_by_sidechain( sidechain ); +} + +vector> database_api_impl::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const +{ + vector> result; + const auto& sidechain_addresses_range = _db.get_index_type().indices().get().equal_range(sidechain); + std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second, + [&result] (const sidechain_address_object& sao) { + result.push_back(sao); + }); + return result; +} + +fc::optional database_api::get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const +{ + return my->get_sidechain_address_by_account_and_sidechain( account, sidechain ); +} + +fc::optional database_api_impl::get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find( boost::make_tuple( account, sidechain ) ); + if( itr != idx.end() ) + return *itr; + return {}; +} + +uint64_t database_api::get_sidechain_addresses_count()const +{ + return my->get_sidechain_addresses_count(); +} + +uint64_t database_api_impl::get_sidechain_addresses_count()const +{ + return _db.get_index_type().indices().size(); +} + ////////////////////////////////////////////////////////////////////// // // // Votes // @@ -2164,7 +2250,7 @@ vector database_api::get_tournaments(tournament_id_type stop, vector database_api_impl::get_tournaments(tournament_id_type stop, unsigned limit, - tournament_id_type start) + tournament_id_type start) { vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); @@ -2191,7 +2277,7 @@ vector database_api_impl::get_tournaments_by_state(tournament unsigned limit, tournament_id_type start, tournament_state state) -{ +{ vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); for (auto elem: tournament_idx) { @@ -2320,7 +2406,7 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec /// if a connection hangs then this could get backed up and result in /// a failure to exit cleanly. //fc::async([capture_this,this,updates,market_broadcast_queue](){ - //if( _subscribe_callback ) + //if( _subscribe_callback ) // _subscribe_callback( updates ); for(auto id : ids) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 66dc2939..5b6f5411 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -310,6 +310,15 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const sidechain_address_add_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_update_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_delete_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index d597110e..e3252ec6 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -602,6 +603,46 @@ class database_api */ uint64_t get_son_count()const; + ///////////////////////// + // Sidechain Addresses // + ///////////////////////// + + /** + * @brief Get a list of sidechain addresses + * @param sidechain_address_ids IDs of the sidechain addresses to retrieve + * @return The sidechain accounts corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; + + /** + * @brief Get the sidechain addresses for a given account + * @param account The ID of the account whose sidechain addresses should be retrieved + * @return The sidechain addresses objects, or null if the account does not have a sidechain addresses + */ + vector> get_sidechain_addresses_by_account(account_id_type account)const; + + /** + * @brief Get the sidechain addresses for a given sidechain + * @param sidechain Sidechain for which addresses should be retrieved + * @return The sidechain addresses objects, or null if the sidechain does not have any addresses + */ + vector> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain)const; + + /** + * @brief Get the sidechain addresses for a given account and sidechain + * @param account The ID of the account whose sidechain addresses should be retrieved + * @param sidechain Sidechain for which address should be retrieved + * @return The sidechain addresses objects, or null if the account does not have a sidechain addresses for a given sidechain + */ + fc::optional get_sidechain_address_by_account_and_sidechain(account_id_type account, peerplays_sidechain::sidechain_type sidechain)const; + + /** + * @brief Get the total number of sidechain addresses registered with the blockchain + */ + uint64_t get_sidechain_addresses_count()const; + /// WORKERS /** @@ -814,6 +855,13 @@ FC_API(graphene::app::database_api, (lookup_son_accounts) (get_son_count) + // Sidechain addresses + (get_sidechain_addresses) + (get_sidechain_addresses_by_account) + (get_sidechain_addresses_by_sidechain) + (get_sidechain_address_by_account_and_sidechain) + (get_sidechain_addresses_count) + // workers (get_workers_by_account) // Votes diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 5688eabf..4e3d4625 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -116,13 +116,15 @@ add_library( graphene_chain son_evaluator.cpp son_object.cpp + sidechain_address_evaluator.cpp + ${HEADERS} ${PROTOCOL_HEADERS} "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" ) add_dependencies( graphene_chain build_hardfork_hpp ) -target_link_libraries( graphene_chain fc graphene_db ) +target_link_libraries( graphene_chain fc graphene_db peerplays_sidechain ) target_include_directories( graphene_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 956e34a7..c4ddad18 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -78,6 +79,7 @@ #include #include #include +#include #include @@ -248,6 +250,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -292,6 +297,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index c71b2930..83a58c45 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -297,6 +297,15 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const sidechain_address_add_operation& op ) { + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_update_operation& op ) { + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_delete_operation& op ) { + _impacted.insert( op.sidechain_address_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) @@ -390,6 +399,11 @@ void get_relevant_accounts( const object* obj, flat_set& accoun assert( aobj != nullptr ); accounts.insert( aobj->son_account ); break; + } case sidechain_address_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->sidechain_address_account ); + break; } } } diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index a0e72094..e1cc5225 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -46,6 +46,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -140,7 +141,10 @@ namespace graphene { namespace chain { son_create_operation, son_update_operation, son_delete_operation, - son_heartbeat_operation + son_heartbeat_operation, + sidechain_address_add_operation, + sidechain_address_update_operation, + sidechain_address_delete_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp new file mode 100644 index 00000000..d0e658b4 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp @@ -0,0 +1,66 @@ +#pragma once +#include + +#include + +namespace graphene { namespace chain { + + struct sidechain_address_add_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type sidechain_address_account; + graphene::peerplays_sidechain::sidechain_type sidechain; + string address; + string private_key; + string public_key; + + account_id_type fee_payer()const { return sidechain_address_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct sidechain_address_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + sidechain_address_id_type sidechain_address_id; + account_id_type sidechain_address_account; + graphene::peerplays_sidechain::sidechain_type sidechain; + optional address; + optional private_key; + optional public_key; + + account_id_type fee_payer()const { return sidechain_address_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct sidechain_address_delete_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + sidechain_address_id_type sidechain_address_id; + account_id_type sidechain_address_account; + graphene::peerplays_sidechain::sidechain_type sidechain; + + account_id_type fee_payer()const { return sidechain_address_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee) + (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) + +FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee) + (sidechain_address_id) + (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) + +FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee) + (sidechain_address_id) + (sidechain_address_account)(sidechain) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index bcdd1a83..463c862f 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -147,6 +147,7 @@ namespace graphene { namespace chain { bet_object_type, son_object_type, son_proposal_object_type, + sidechain_address_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -209,6 +210,7 @@ namespace graphene { namespace chain { class bet_object; class son_object; class son_proposal_object; + class sidechain_address_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; @@ -237,6 +239,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type; typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; + typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; // implementation types class global_property_object; @@ -421,6 +424,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (bet_object_type) (son_object_type) (son_proposal_object_type) + (sidechain_address_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -493,6 +497,7 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp new file mode 100644 index 00000000..82bfcf1a --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class add_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_add_operation operation_type; + + void_result do_evaluate(const sidechain_address_add_operation& o); + object_id_type do_apply(const sidechain_address_add_operation& o); +}; + +class update_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_update_operation operation_type; + + void_result do_evaluate(const sidechain_address_update_operation& o); + object_id_type do_apply(const sidechain_address_update_operation& o); +}; + +class delete_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_delete_operation operation_type; + + void_result do_evaluate(const sidechain_address_delete_operation& o); + void_result do_apply(const sidechain_address_delete_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp new file mode 100644 index 00000000..8c77fad2 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include + +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class sidechain_address_object + * @brief tracks information about a sidechain addresses for user accounts. + * @ingroup object + */ + class sidechain_address_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = sidechain_address_object_type; + + account_id_type sidechain_address_account; + graphene::peerplays_sidechain::sidechain_type sidechain; + string address; + string private_key; + string public_key; + + sidechain_address_object() : + sidechain(graphene::peerplays_sidechain::sidechain_type::bitcoin), + address(""), + private_key(""), + public_key("") {} + }; + + struct by_account; + struct by_sidechain; + struct by_account_and_sidechain; + struct by_sidechain_and_address; + using sidechain_address_multi_index_type = multi_index_container< + sidechain_address_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_unique< tag, + composite_key, + member + > + >, + ordered_unique< tag, + composite_key, + member + > + > + > + >; + using sidechain_address_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object), + (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp new file mode 100644 index 00000000..d79d4cb1 --- /dev/null +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -0,0 +1,70 @@ +#include + +#include +#include +#include + +namespace graphene { namespace chain { + +void_result add_sidechain_address_evaluator::do_evaluate(const sidechain_address_add_operation& op) +{ try{ + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(boost::make_tuple(op.sidechain_address_account, op.sidechain)) == idx.end(), "Duplicated item" ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address_add_operation& op) +{ try { + const auto& new_sidechain_address_object = db().create( [&]( sidechain_address_object& obj ){ + obj.sidechain_address_account = op.sidechain_address_account; + obj.sidechain = op.sidechain; + obj.address = op.address; + obj.private_key = op.private_key; + obj.public_key = op.public_key; + }); + return new_sidechain_address_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_address_update_operation& op) +{ try { + FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_address_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.sidechain_address_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](sidechain_address_object &sao) { + if(op.address.valid()) sao.address = *op.address; + if(op.private_key.valid()) sao.private_key = *op.private_key; + if(op.public_key.valid()) sao.public_key = *op.public_key; + }); + } + return op.sidechain_address_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_sidechain_address_evaluator::do_evaluate(const sidechain_address_delete_operation& op) +{ try { + FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address_delete_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto sidechain_address = idx.find(op.sidechain_address_id); + if(sidechain_address != idx.end()) { + db().remove(*sidechain_address); + } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 1b6a9099..498784de 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -8,9 +8,10 @@ namespace graphene { namespace peerplays_sidechain { -enum network { +enum class sidechain_type { bitcoin, - //ethereum + //ethereum, + //eos }; using bytes = std::vector; @@ -59,7 +60,7 @@ struct info_for_vin }; struct sidechain_event_data { - network sidechain; + sidechain_type sidechain; std::string transaction_id; std::string from; std::string to; @@ -68,3 +69,4 @@ struct sidechain_event_data { } } // graphene::peerplays_sidechain +FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin) ) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 45628223..2f72ae06 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace graphene { namespace peerplays_sidechain { using namespace chain; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index fa4f0b50..e841a639 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -10,13 +11,16 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { public: - sidechain_net_handler(const boost::program_options::variables_map& options); + sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler(); - std::vector get_user_sidechain_address_mapping(); + std::vector get_sidechain_addresses(); protected: - graphene::peerplays_sidechain::network network; + std::shared_ptr database; + graphene::peerplays_sidechain::sidechain_type sidechain; + + void sidechain_event_data_received(const sidechain_event_data& sed); virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 792aaf45..4c37c530 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -56,17 +56,9 @@ private: class sidechain_net_handler_bitcoin : public sidechain_net_handler { public: - sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options); + sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler_bitcoin(); - void update_tx_infos( const std::string& block_hash ); - - //void update_tx_approvals(); - - //void update_estimated_fee(); - - //void send_btc_tx( const sidechain::bitcoin_transaction& trx ); - bool connection_is_not_defined() const; std::string create_multisignature_wallet( const std::vector public_keys ); @@ -83,18 +75,9 @@ private: std::unique_ptr listener; std::unique_ptr bitcoin_client; - graphene::chain::database* db; void handle_event( const std::string& event_data); - std::vector extract_info_from_block( const std::string& _block ); - - void update_transaction_status( std::vector trx_for_check ); - - std::set get_valid_vins( const std::string tx_hash ); - - inline uint64_t parse_amount(std::string raw); - }; } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index 49073314..c60aa73b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -11,12 +12,12 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_manager { public: - sidechain_net_manager(); + sidechain_net_manager(std::shared_ptr db); virtual ~sidechain_net_manager(); - bool create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options); + bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options); private: - + std::shared_ptr database; std::vector> net_handlers; }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 50739bef..a8741cff 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -14,20 +15,111 @@ namespace detail class peerplays_sidechain_plugin_impl { public: - peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) - : _self( _plugin ) - { } + peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin); virtual ~peerplays_sidechain_plugin_impl(); - peerplays_sidechain_plugin& _self; + void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg); + void plugin_initialize(const boost::program_options::variables_map& options); + void plugin_startup(); - peerplays_sidechain::sidechain_net_manager _net_manager; +private: + peerplays_sidechain_plugin& plugin; + bool config_ready_son; + bool config_ready_bitcoin; + + std::unique_ptr net_manager; }; +peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : + plugin( _plugin ), + config_ready_son(false), + config_ready_bitcoin(false), + net_manager(nullptr) +{ +} + peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { - return; +} + +void peerplays_sidechain_plugin_impl::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) +{ + auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); + string son_id_example = fc::json::to_string(chain::son_id_type(5)); + + cli.add_options() + ("son-id", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) + ("peerplays-private-key", bpo::value>()->composing()->multitoken()-> + DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), + "Tuple of [PublicKey, WIF private key]") + + ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") + ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") + ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") + ("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user") + ("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password") + ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") + ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") + ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") + ; + cfg.add(cli); +} + +void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map& options) +{ + config_ready_son = options.count( "son-id" ) && options.count( "peerplays-private-key" ); + if (config_ready_son) { + } else { + wlog("Haven't set up SON parameters"); + throw; + } + + net_manager = std::unique_ptr(new sidechain_net_manager(plugin.app().chain_database())); + + config_ready_bitcoin = options.count( "bitcoin-node-ip" ) && + options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) && + options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) && + options.count( "bitcoin-address" ) && options.count( "bitcoin-public-key" ) && options.count( "bitcoin-private-key" ); + if (config_ready_bitcoin) { + net_manager->create_handler(sidechain_type::bitcoin, options); + ilog("Bitcoin sidechain handler created"); + } else { + wlog("Haven't set up Bitcoin sidechain parameters"); + } + + //config_ready_ethereum = options.count( "ethereum-node-ip" ) && + // options.count( "ethereum-address" ) && options.count( "ethereum-public-key" ) && options.count( "ethereum-private-key" ); + //if (config_ready_ethereum) { + // net_manager->create_handler(sidechain_type::ethereum, options); + // ilog("Ethereum sidechain handler created"); + //} else { + // wlog("Haven't set up Ethereum sidechain parameters"); + //} + + if (!(config_ready_bitcoin /*&& config_ready_ethereum*/)) { + wlog("Haven't set up any sidechain parameters"); + throw; + } +} + +void peerplays_sidechain_plugin_impl::plugin_startup() +{ + if (config_ready_son) { + ilog("SON running"); + } + + if (config_ready_bitcoin) { + ilog("Bitcoin sidechain handler running"); + } + + //if (config_ready_ethereum) { + // ilog("Ethereum sidechain handler running"); + //} } } // end namespace detail @@ -49,47 +141,22 @@ std::string peerplays_sidechain_plugin::plugin_name()const void peerplays_sidechain_plugin::plugin_set_program_options( boost::program_options::options_description& cli, - boost::program_options::options_description& cfg - ) + boost::program_options::options_description& cfg) { - auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); - string son_id_example = fc::json::to_string(chain::son_id_type(5)); - - cli.add_options() - ("son-id,w", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) - ("peerplays-private-key", bpo::value>()->composing()->multitoken()-> - DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), - "Tuple of [PublicKey, WIF private key]") - - ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") - ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") - ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") - ("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user") - ("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password") - ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") - ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") - ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") - ; - cfg.add(cli); + ilog("peerplays sidechain plugin: plugin_set_program_options()"); + my->plugin_set_program_options(cli, cfg); } void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map& options) { ilog("peerplays sidechain plugin: plugin_initialize()"); - - if( options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) - && options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) - && options.count( "bitcoin-address" ) && options.count( "bitcoin-public-key" ) && options.count( "bitcoin-private-key" ) ) - { - my->_net_manager.create_handler(network::bitcoin, options); - } else { - wlog("Haven't set up bitcoin sidechain parameters"); - } + my->plugin_initialize(options); } void peerplays_sidechain_plugin::plugin_startup() { ilog("peerplays sidechain plugin: plugin_startup()"); + my->plugin_startup(); } } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index fefeacc1..85196685 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -1,29 +1,34 @@ #include +#include + +#include + namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(const boost::program_options::variables_map& options) { +sidechain_net_handler::sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options) : + database(db) +{ } sidechain_net_handler::~sidechain_net_handler() { } -std::vector sidechain_net_handler::get_user_sidechain_address_mapping() { +std::vector sidechain_net_handler::get_sidechain_addresses() { std::vector result; - switch (network) { - case network::bitcoin: - result.push_back("2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J"); - result.push_back("2MxAnE469fhhdvUqUB7daU997VSearb2mn7"); - result.push_back("2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL"); - result.push_back("2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR"); - result.push_back("2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL"); - //result.push_back("2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG"); - //result.push_back("2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg"); - //result.push_back("2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX"); - //result.push_back("2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM"); - //result.push_back("2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy"); - + switch (sidechain) { + case sidechain_type::bitcoin: + { + const auto& sidechain_addresses_idx = database->get_index_type(); + const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&result] (const sidechain_address_object& sao) { + result.push_back(sao.address); + }); + break; + } default: assert(false); } @@ -31,5 +36,15 @@ std::vector sidechain_net_handler::get_user_sidechain_address_mappi return result; } +void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) { + ilog( __FUNCTION__ ); + ilog( "sidechain_event_data:" ); + ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); + ilog( " transaction_id: ${transaction_id}", ( "transaction_id", sed.transaction_id ) ); + ilog( " from: ${from}", ( "from", sed.from ) ); + ilog( " to: ${to}", ( "to", sed.to ) ); + ilog( " amount: ${amount}", ( "amount", sed.amount ) ); +} + } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 1fce21ea..a2bd8d94 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -11,7 +11,7 @@ #include #include -#include "graphene/peerplays_sidechain/sidechain_net_manager.hpp" +#include namespace graphene { namespace peerplays_sidechain { @@ -178,17 +178,17 @@ void zmq_listener::handle_zmq() { while ( true ) { auto msg = receive_multipart(); const auto header = std::string( static_cast( msg[0].data() ), msg[0].size() ); - const auto hash = boost::algorithm::hex( std::string( static_cast( msg[1].data() ), msg[1].size() ) ); + const auto block_hash = boost::algorithm::hex( std::string( static_cast( msg[1].data() ), msg[1].size() ) ); - event_received( hash ); + event_received( block_hash ); } } // ============================================================================= -sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options) : - sidechain_net_handler(options) { - network = peerplays_sidechain::network::bitcoin; +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options) : + sidechain_net_handler(db, options) { + sidechain = sidechain_type::bitcoin; ip = options.at("bitcoin-node-ip").as(); zmq_port = options.at("bitcoin-node-zmq-port").as(); @@ -206,74 +206,15 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(const boost::progra listener = std::unique_ptr( new zmq_listener( ip, zmq_port ) ); bitcoin_client = std::unique_ptr( new bitcoin_rpc_client( ip, rpc_port, rpc_user, rpc_password ) ); - //db = _db; listener->event_received.connect([this]( const std::string& event_data ) { std::thread( &sidechain_net_handler_bitcoin::handle_event, this, event_data ).detach(); } ); - - //db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) { - // std::thread( &sidechain_net_handler_bitcoin::send_btc_tx, this, trx ).detach(); - //} ); } sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { } -void sidechain_net_handler_bitcoin::update_tx_infos( const std::string& block_hash ) -{ - std::string block = bitcoin_client->receive_full_block( block_hash ); - if( block != "" ) { - const auto& vins = extract_info_from_block( block ); -// const auto& addr_idx = db->get_index_type().indices().get(); -// for( const auto& v : vins ) { -// const auto& addr_itr = addr_idx.find( v.address ); -// FC_ASSERT( addr_itr != addr_idx.end() ); -// db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() ); -// } - } -} - -//void sidechain_net_handler_bitcoin::update_tx_approvals() -//{ -// std::vector trx_for_check; -// const auto& confirmations_num = db->get_sidechain_params().confirmations_num; -// -// db->bitcoin_confirmations.safe_for([&]( btc_tx_confirmations_index::iterator itr_b, btc_tx_confirmations_index::iterator itr_e ){ -// for(auto iter = itr_b; iter != itr_e; iter++) { -// db->bitcoin_confirmations.modify( iter->transaction_id, [&]( bitcoin_transaction_confirmations& obj ) { -// obj.count_block++; -// }); -// -// if( iter->count_block == confirmations_num ) { -// trx_for_check.push_back( iter->transaction_id ); -// } -// } -// }); -// -// update_transaction_status( trx_for_check ); -// -//} - -//void sidechain_net_handler_bitcoin::update_estimated_fee() -//{ -// db->estimated_feerate = bitcoin_client->receive_estimated_fee(); -//} - -//void sidechain_net_handler_bitcoin::send_btc_tx( const sidechain::bitcoin_transaction& trx ) -//{ -// std::set valid_vins; -// for( const auto& v : trx.vin ) { -// valid_vins.insert( v.prevout.hash ); -// } -// db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid(), valid_vins ) ); -// -// FC_ASSERT( !bitcoin_client->connection_is_not_defined() ); -// const auto tx_hex = fc::to_hex( pack( trx ) ); -// -// bitcoin_client->send_btc_tx( tx_hex ); -//} - bool sidechain_net_handler_bitcoin::connection_is_not_defined() const { return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); @@ -302,9 +243,27 @@ std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); ilog(" event_data: ${event_data}", ("event_data", event_data)); - //update_tx_approvals(); - //update_estimated_fee(); - //update_tx_infos( block_hash ); + + std::string block = bitcoin_client->receive_full_block( event_data ); + if( block != "" ) { + const auto& vins = extract_info_from_block( block ); + + const auto& sidechain_addresses_idx = database->get_index_type().indices().get(); + + for( const auto& v : vins ) { + const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain_type::bitcoin, v.address)); + if ( addr_itr == sidechain_addresses_idx.end() ) + continue; + + sidechain_event_data sed; + sed.sidechain = addr_itr->sidechain; + sed.transaction_id = v.out.hash_tx; + sed.from = ""; + sed.to = v.address; + sed.amount = v.out.amount; + sidechain_event_data_received(sed); + } + } } std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) @@ -315,8 +274,6 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block std::vector result; - const auto& addr_idx = get_user_sidechain_address_mapping();// db->get_index_type().indices().get(); - for (const auto& tx_child : block.get_child("tx")) { const auto& tx = tx_child.second; @@ -327,13 +284,11 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses? const auto address_base58 = addr.second.get_value(); - - auto it = find(addr_idx.begin(), addr_idx.end(), address_base58); - if (it == addr_idx.end()) continue; - info_for_vin vin; vin.out.hash_tx = tx.get_child("txid").get_value(); - vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value() ); + string amount = o.second.get_child( "value" ).get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + vin.out.amount = std::stoll(amount); vin.out.n_vout = o.second.get_child( "n" ).get_value(); vin.address = address_base58; result.push_back( vin ); @@ -344,59 +299,6 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block return result; } -//void sidechain_net_handler_bitcoin::update_transaction_status( std::vector trx_for_check ) -//{ -// const auto& confirmations_num = db->get_sidechain_params().confirmations_num; -// -// for( const auto& trx : trx_for_check ) { -// auto confirmations = bitcoin_client->receive_confirmations_tx( trx.str() ); -// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { -// obj.count_block = confirmations; -// }); -// -// if( confirmations >= confirmations_num ) { -// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { -// obj.confirmed = true; -// }); -// -// } else if( confirmations == 0 ) { -// auto is_in_mempool = bitcoin_client->receive_mempool_entry_tx( trx.str() ); -// -// std::set valid_vins; -// if( !is_in_mempool ) { -// valid_vins = get_valid_vins( trx.str() ); -// } -// -// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { -// obj.missing = !is_in_mempool; -// obj.valid_vins = valid_vins; -// }); -// } -// } -//} - -//std::set sidechain_net_handler_bitcoin::get_valid_vins( const std::string tx_hash ) -//{ -// const auto& confirmations_obj = db->bitcoin_confirmations.find( fc::sha256( tx_hash ) ); -// FC_ASSERT( confirmations_obj.valid() ); -// -// std::set valid_vins; -// for( const auto& v : confirmations_obj->valid_vins ) { -// auto confirmations = bitcoin_client->receive_confirmations_tx( v.str() ); -// if( confirmations == 0 ) { -// continue; -// } -// valid_vins.insert( v ); -// } -// return valid_vins; -//} - -// Removes dot from amount output: "50.00000000" -inline uint64_t sidechain_net_handler_bitcoin::parse_amount(std::string raw) { - raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end()); - return std::stoll(raw); -} - // ============================================================================= } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index e1c0bce6..7c39fd81 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -5,7 +5,9 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_manager::sidechain_net_manager() { +sidechain_net_manager::sidechain_net_manager(std::shared_ptr db) : + database(db) +{ ilog(__FUNCTION__); } @@ -13,16 +15,17 @@ sidechain_net_manager::~sidechain_net_manager() { ilog(__FUNCTION__); } -bool sidechain_net_manager::create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options) { +bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options) { ilog(__FUNCTION__); bool ret_val = false; - switch (network) { - case network::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(options)); + switch (sidechain) { + case sidechain_type::bitcoin: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(database, options)); net_handlers.push_back(std::move(h)); ret_val = true; + break; } default: assert(false); diff --git a/libraries/wallet/CMakeLists.txt b/libraries/wallet/CMakeLists.txt index 8c9f8790..382adda1 100644 --- a/libraries/wallet/CMakeLists.txt +++ b/libraries/wallet/CMakeLists.txt @@ -23,7 +23,7 @@ else() endif() add_library( graphene_wallet wallet.cpp ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp ${HEADERS} ) -target_link_libraries( graphene_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( graphene_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) target_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) if(MSVC) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 6a78d8d3..4c95b5f7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1368,6 +1368,83 @@ class wallet_api */ map list_active_sons(); + /** Adds sidechain address owned by the given account for a given sidechain. + * + * An account can have at most one sidechain address for one sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain a sidechain to whom address belongs + * @param address sidechain address + * @param private_key private key for sidechain address + * @param public_key public key for sidechain address + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction adding sidechain address + */ + signed_transaction add_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast = false); + + /** Updates existing sidechain address owned by the given account for a given sidechain. + * + * Only address, private key and public key might be updated. + * + * @param account the name or id of the account who owns the address + * @param sidechain a sidechain to whom address belongs + * @param address sidechain address + * @param private_key private key for sidechain address + * @param public_key public key for sidechain address + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction updating sidechain address + */ + signed_transaction update_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast = false); + + /** Deletes existing sidechain address owned by the given account for a given sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain a sidechain to whom address belongs + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction updating sidechain address + */ + signed_transaction delete_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + bool broadcast = false); + + /** Retrieves all sidechain addresses owned by given account. + * + * @param account the name or id of the account who owns the address + * @returns the list of all sidechain addresses owned by given account. + */ + vector> get_sidechain_addresses_by_account(string account); + + /** Retrieves all sidechain addresses registered for a given sidechain. + * + * @param sidechain the name of the sidechain + * @returns the list of all sidechain addresses registered for a given sidechain. + */ + vector> get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain); + + /** Retrieves sidechain address owned by given account for a given sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain the name of the sidechain + * @returns the sidechain address owned by given account for a given sidechain. + */ + fc::optional get_sidechain_address_by_account_and_sidechain(string account, peerplays_sidechain::sidechain_type sidechain); + + /** Retrieves the total number of sidechain addresses registered in the system. + * + * @returns the total number of sidechain addresses registered in the system. + */ + uint64_t get_sidechain_addresses_count(); + /** Creates a witness object owned by the given account. * * An account can have at most one witness object. @@ -2121,6 +2198,13 @@ FC_API( graphene::wallet::wallet_api, (update_son) (delete_son) (list_sons) + (add_sidechain_address) + (update_sidechain_address) + (delete_sidechain_address) + (get_sidechain_addresses_by_account) + (get_sidechain_addresses_by_sidechain) + (get_sidechain_address_by_account_and_sidechain) + (get_sidechain_addresses_count) (create_witness) (update_witness) (create_worker) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index f7cc2a51..6946a2cc 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -73,6 +73,7 @@ #include #include +#include #include #include @@ -1954,6 +1955,73 @@ public: return result; } FC_CAPTURE_AND_RETHROW() } + signed_transaction add_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast /* = false */) + { try { + account_id_type sidechain_address_account_id = get_account_id(account); + + sidechain_address_add_operation op; + op.sidechain_address_account = sidechain_address_account_id; + op.sidechain = sidechain; + op.address = address; + op.private_key = private_key; + op.public_key = public_key; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW() } + + signed_transaction update_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast /* = false */) + { try { + account_id_type sidechain_address_account_id = get_account_id(account); + + sidechain_address_update_operation op; + op.sidechain_address_account = sidechain_address_account_id; + op.sidechain = sidechain; + op.address = address; + op.private_key = private_key; + op.public_key = public_key; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW() } + + signed_transaction delete_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + bool broadcast /* = false */) + { try { + account_id_type sidechain_address_account_id = get_account_id(account); + + sidechain_address_delete_operation op; + op.sidechain_address_account = sidechain_address_account_id; + op.sidechain = sidechain; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + + } FC_CAPTURE_AND_RETHROW() } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -4330,6 +4398,55 @@ map wallet_api::list_active_sons() return my->list_active_sons(); } +signed_transaction wallet_api::add_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast /* = false */) +{ + return my->add_sidechain_address(account, sidechain, address, private_key, public_key, broadcast); +} + +signed_transaction wallet_api::update_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + string address, + string private_key, + string public_key, + bool broadcast /* = false */) +{ + return my->update_sidechain_address(account, sidechain, address, private_key, public_key, broadcast); +} + +signed_transaction wallet_api::delete_sidechain_address(string account, + peerplays_sidechain::sidechain_type sidechain, + bool broadcast /* = false */) +{ + return my->delete_sidechain_address(account, sidechain, broadcast); +} + +vector> wallet_api::get_sidechain_addresses_by_account(string account) +{ + account_id_type account_id = get_account_id(account); + return my->_remote_db->get_sidechain_addresses_by_account(account_id); +} + +vector> wallet_api::get_sidechain_addresses_by_sidechain(peerplays_sidechain::sidechain_type sidechain) +{ + return my->_remote_db->get_sidechain_addresses_by_sidechain(sidechain); +} + +fc::optional wallet_api::get_sidechain_address_by_account_and_sidechain(string account, peerplays_sidechain::sidechain_type sidechain) +{ + account_id_type account_id = get_account_id(account); + return my->_remote_db->get_sidechain_address_by_account_and_sidechain(account_id, sidechain); +} + +uint64_t wallet_api::get_sidechain_addresses_count() +{ + return my->_remote_db->get_sidechain_addresses_count(); +} + signed_transaction wallet_api::create_witness(string owner_account, string url, bool broadcast /* = false */) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index a921c0c3..2901f2e0 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cf633dfd..b49e089e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,7 +8,7 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp new file mode 100644 index 00000000..eef76784 --- /dev/null +++ b/tests/tests/sidechain_addresses_test.cpp @@ -0,0 +1,143 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( sidechain_addresses_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) { + + BOOST_TEST_MESSAGE("sidechain_address_add_test"); + + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)); + + generate_block(); + set_expiration(db, trx); + + { + BOOST_TEST_MESSAGE("Send sidechain_address_add_operation"); + + sidechain_address_add_operation op; + + op.sidechain_address_account = alice_id; + op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; + op.address = "address"; + op.private_key = "private_key"; + op.public_key = "public_key"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check sidechain_address_add_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->sidechain_address_account == alice_id ); + BOOST_CHECK( obj->sidechain == graphene::peerplays_sidechain::sidechain_type::bitcoin ); + BOOST_CHECK( obj->address == "address" ); + BOOST_CHECK( obj->private_key == "private_key" ); + BOOST_CHECK( obj->public_key == "public_key" ); +} + +BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { + + BOOST_TEST_MESSAGE("sidechain_address_update_test"); + + INVOKE(sidechain_address_add_test); + + GET_ACTOR(alice); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); + BOOST_REQUIRE( obj != idx.end() ); + + std::string new_address = "new_address"; + std::string new_private_key = "new_private_key"; + std::string new_public_key = "new_public_key"; + + { + BOOST_TEST_MESSAGE("Send sidechain_address_update_operation"); + + sidechain_address_update_operation op; + op.sidechain_address_id = sidechain_address_id_type(0); + op.sidechain_address_account = obj->sidechain_address_account; + op.sidechain = obj->sidechain; + op.address = new_address; + op.private_key = new_private_key; + op.public_key = new_public_key; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + { + BOOST_TEST_MESSAGE("Check sidechain_address_update_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->sidechain_address_account == obj->sidechain_address_account ); + BOOST_CHECK( obj->sidechain == obj->sidechain ); + BOOST_CHECK( obj->address == new_address ); + BOOST_CHECK( obj->private_key == new_private_key ); + BOOST_CHECK( obj->public_key == new_public_key ); + } +} + +BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) { + + BOOST_TEST_MESSAGE("sidechain_address_delete_test"); + + INVOKE(sidechain_address_add_test); + + GET_ACTOR(alice); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); + BOOST_REQUIRE( obj != idx.end() ); + + { + BOOST_TEST_MESSAGE("Send sidechain_address_delete_operation"); + + sidechain_address_delete_operation op; + op.sidechain_address_id = sidechain_address_id_type(0); + op.sidechain_address_account = obj->sidechain_address_account; + op.sidechain = obj->sidechain; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + { + BOOST_TEST_MESSAGE("Check sidechain_address_delete_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 0 ); + auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); + BOOST_REQUIRE( obj == idx.end() ); + } +} + +BOOST_AUTO_TEST_SUITE_END() + From a347e9764908e6af252e881adc91a1a649437e27 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Tue, 24 Dec 2019 00:30:49 +1100 Subject: [PATCH 14/64] SON207 - Introduce scheduling for SONs similar to witnesses (#251) --- libraries/chain/db_block.cpp | 18 ++- libraries/chain/db_init.cpp | 24 +++ libraries/chain/db_maint.cpp | 11 +- libraries/chain/db_witness_schedule.cpp | 142 ++++++++++++++++++ .../chain/include/graphene/chain/database.hpp | 18 +++ .../include/graphene/chain/protocol/types.hpp | 6 +- .../chain/witness_schedule_object.hpp | 57 +++++++ 7 files changed, 266 insertions(+), 10 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index dafdc3ff..c6b4564c 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -649,8 +649,13 @@ void database::_apply_block( const signed_block& next_block ) _current_virtual_op = 0; } - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) - update_witness_schedule(next_block); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { + update_witness_schedule(next_block); + if(global_props.active_sons.size() > 0) { + update_son_schedule(next_block); + } + } + const uint32_t missed = update_witness_missed_blocks( next_block ); update_global_dynamic_data( next_block, missed ); update_signing_witness(signing_witness, next_block); @@ -678,8 +683,13 @@ void database::_apply_block( const signed_block& next_block ) // update_global_dynamic_data() as perhaps these methods only need // to be called for header validation? update_maintenance_flag( maint_needed ); - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) - update_witness_schedule(); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { + update_witness_schedule(); + if(global_props.active_sons.size() > 0) { + update_son_schedule(); + } + } + if( !_node_property_object.debug_updates.empty() ) apply_debug_updates(); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c4ddad18..31c0dcd5 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -314,6 +314,7 @@ void database::initialize_indexes() add_index< primary_index> >(); add_index< primary_index > >(); add_index< primary_index > >(); + add_index< primary_index > >(); add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); @@ -947,6 +948,29 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); assert( wso.id == witness_schedule_id_type() ); + // Initialize witness schedule +#ifndef NDEBUG + const son_schedule_object& sso = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_witnesses = get_global_properties().active_witnesses; + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); + + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( sso.id == son_schedule_id_type() ); + // Enable fees modify(get_global_properties(), [&genesis_state](global_property_object& p) { p.parameters.current_fees = genesis_state.initial_parameters.current_fees; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index cae17eda..8fb72566 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -455,11 +455,12 @@ void database::update_active_sons() }); }); - //const witness_schedule_object& wso = witness_schedule_id_type()(*this); - //modify(wso, [&](witness_schedule_object& _wso) - //{ - // _wso.scheduler.update(gpo.active_witnesses); - //}); + const son_schedule_object& sso = son_schedule_id_type()(*this); + modify(sso, [&](son_schedule_object& _sso) + { + flat_set active_sons(gpo.active_sons.begin(), gpo.active_sons.end()); + _sso.scheduler.update(active_sons); + }); } FC_CAPTURE_AND_RETHROW() } void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index e12c81dc..3ce11443 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -72,6 +73,47 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const return wid; } +son_id_type database::get_scheduled_son( uint32_t slot_num )const +{ + son_id_type sid; + const global_property_object& gpo = get_global_properties(); + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) + { + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + const son_schedule_object& sso = son_schedule_id_type()(*this); + uint64_t current_aslot = dpo.current_aslot + slot_num; + return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; + } + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && + slot_num != 0 ) + { + const son_schedule_object& sso = son_schedule_id_type()(*this); + // ask the near scheduler who goes in the given slot + bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); + if(! slot_is_near) + { + // if the near scheduler doesn't know, we have to extend it to + // a far scheduler. + // n.b. instantiating it is slow, but block gaps long enough to + // need it are likely pretty rare. + + witness_scheduler_rng far_rng(sso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV); + + far_future_son_scheduler far_scheduler = + far_future_son_scheduler(sso.scheduler, far_rng); + if(!far_scheduler.get_slot(slot_num-1, sid)) + { + // no scheduled son -- somebody set up us the bomb + // n.b. this code path is impossible, the present + // implementation of far_future_son_scheduler + // returns true unconditionally + assert( false ); + } + } + } + return sid; +} + fc::time_point_sec database::get_slot_time(uint32_t slot_num)const { if( slot_num == 0 ) @@ -146,6 +188,41 @@ void database::update_witness_schedule() } } +void database::update_son_schedule() +{ + const son_schedule_object& sso = son_schedule_id_type()(*this); + const global_property_object& gpo = get_global_properties(); + + if( head_block_num() % gpo.active_sons.size() == 0 ) + { + modify( sso, [&]( son_schedule_object& _sso ) + { + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); + + for( const son_id_type& w : gpo.active_sons ) + _sso.current_shuffled_sons.push_back( w ); + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) + { + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; + + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k%jmax; + std::swap( _sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j] ); + } + }); + } +} + vector database::get_near_witness_schedule()const { const witness_schedule_object& wso = witness_schedule_id_type()(*this); @@ -226,6 +303,71 @@ void database::update_witness_schedule(const signed_block& next_block) idump( ( double(total_time/1000000.0)/calls) ); } +void database::update_son_schedule(const signed_block& next_block) +{ + auto start = fc::time_point::now(); + const global_property_object& gpo = get_global_properties(); + const son_schedule_object& sso = get(son_schedule_id_type()); + uint32_t schedule_needs_filled = gpo.active_sons.size(); + uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); + + // We shouldn't be able to generate _pending_block with timestamp + // in the past, and incoming blocks from the network with timestamp + // in the past shouldn't be able to make it this far without + // triggering FC_ASSERT elsewhere + + assert( schedule_slot > 0 ); + + son_id_type first_son; + bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son ); + + son_id_type son; + + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + + assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); + assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); + + modify(sso, [&](son_schedule_object& _sso) + { + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); + + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) + { + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; + } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); + auto end = fc::time_point::now(); + static uint64_t total_time = 0; + static uint64_t calls = 0; + total_time += (end - start).count(); + if( ++calls % 1000 == 0 ) + idump( ( double(total_time/1000000.0)/calls) ); +} + uint32_t database::update_witness_missed_blocks( const signed_block& b ) { uint32_t missed_blocks = get_slot_at_time( b.timestamp ); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 1e989a21..719c6240 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -240,6 +240,22 @@ namespace graphene { namespace chain { */ witness_id_type get_scheduled_witness(uint32_t slot_num)const; + /** + * @brief Get the son scheduled for block production in a slot. + * + * slot_num always corresponds to a time in the future. + * + * If slot_num == 1, returns the next scheduled son. + * If slot_num == 2, returns the next scheduled son after + * 1 block gap. + * + * Use the get_slot_time() and get_slot_at_time() functions + * to convert between slot_num and timestamp. + * + * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS + */ + son_id_type get_scheduled_son(uint32_t slot_num)const; + /** * Get the time at which the given slot occurs. * @@ -263,6 +279,8 @@ namespace graphene { namespace chain { vector get_near_witness_schedule()const; void update_witness_schedule(); void update_witness_schedule(const signed_block& next_block); + void update_son_schedule(); + void update_son_schedule(const signed_block& next_block); void check_lottery_end_by_participants( asset_id_type asset_id ); void check_ending_lotteries(); diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 463c862f..5b040850 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -177,7 +177,8 @@ namespace graphene { namespace chain { impl_global_betting_statistics_object_type, impl_lottery_balance_object_type, impl_sweeps_vesting_balance_object_type, - impl_son_statistics_object_type + impl_son_statistics_object_type, + impl_son_schedule_object_type }; //typedef fc::unsigned_int object_id_type; @@ -264,6 +265,7 @@ namespace graphene { namespace chain { class lottery_balance_object; class sweeps_vesting_balance_object; class son_statistics_object; + class son_schedule_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; @@ -293,6 +295,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type; typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type; typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type; + typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type; typedef fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -453,6 +456,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_lottery_balance_object_type) (impl_sweeps_vesting_balance_object_type) (impl_son_statistics_object_type) + (impl_son_schedule_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index e4c4bb51..fc7d6d10 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -31,6 +31,7 @@ namespace graphene { namespace chain { class witness_schedule_object; +class son_schedule_object; typedef hash_ctr_rng< /* HashClass = */ fc::sha256, @@ -53,6 +54,22 @@ typedef generic_far_future_witness_scheduler< /* debug = */ true > far_future_witness_scheduler; +typedef generic_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > son_scheduler; + +typedef generic_far_future_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > far_future_son_scheduler; + class witness_schedule_object : public graphene::db::abstract_object { public: @@ -73,6 +90,26 @@ class witness_schedule_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_son_schedule_object_type; + + vector< son_id_type > current_shuffled_sons; + + son_scheduler scheduler; + uint32_t last_scheduling_block; + uint64_t slots_since_genesis = 0; + fc::array< char, sizeof(secret_hash_type) > rng_seed; + + /** + * Not necessary for consensus, but used for figuring out the participation rate. + * The nth bit is 0 if the nth slot was unfilled, else it is 1. + */ + fc::uint128 recent_slots_filled; +}; + } } @@ -96,3 +133,23 @@ FC_REFLECT_DERIVED( (recent_slots_filled) (current_shuffled_witnesses) ) +FC_REFLECT( graphene::chain::son_scheduler, + (_turns) + (_tokens) + (_min_token_count) + (_ineligible_waiting_for_token) + (_ineligible_no_turn) + (_eligible) + (_schedule) + (_lame_duck) + ) +FC_REFLECT_DERIVED( + graphene::chain::son_schedule_object, + (graphene::db::object), + (scheduler) + (last_scheduling_block) + (slots_since_genesis) + (rng_seed) + (recent_slots_filled) + (current_shuffled_sons) +) From 31ec55514bba09f846e5d622386f56f7f60cd196 Mon Sep 17 00:00:00 2001 From: obucinac Date: Mon, 23 Dec 2019 19:20:26 +0100 Subject: [PATCH 15/64] Extend SON objects to contain sidechain public keys (#254) --- .../include/graphene/chain/protocol/son.hpp | 7 +- .../include/graphene/chain/son_object.hpp | 4 +- libraries/chain/son_evaluator.cpp | 2 + .../graphene/peerplays_sidechain/defs.hpp | 7 +- .../wallet/include/graphene/wallet/wallet.hpp | 4 + libraries/wallet/wallet.cpp | 12 +- tests/cli/son.cpp | 109 +++++++++++++++--- tests/tests/son_operations_tests.cpp | 18 ++- 8 files changed, 132 insertions(+), 31 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 08e74a2d..914928a6 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace graphene { namespace chain { @@ -12,6 +13,7 @@ namespace graphene { namespace chain { std::string url; vesting_balance_id_type deposit; public_key_type signing_key; + flat_map sidechain_public_keys; vesting_balance_id_type pay_vb; account_id_type fee_payer()const { return owner_account; } @@ -28,6 +30,7 @@ namespace graphene { namespace chain { optional new_url; optional new_deposit; optional new_signing_key; + optional> new_sidechain_public_keys; optional new_pay_vb; account_id_type fee_payer()const { return owner_account; } @@ -63,12 +66,12 @@ namespace graphene { namespace chain { } } // namespace graphene::chain FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key) +FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)(sidechain_public_keys) (pay_vb) ) FC_REFLECT(graphene::chain::son_update_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(new_url)(new_deposit) - (new_signing_key)(new_pay_vb) ) + (new_signing_key)(new_sidechain_public_keys)(new_pay_vb) ) FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(payer)(owner_account) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 4cbff5ed..11cabc2a 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -60,6 +61,7 @@ namespace graphene { namespace chain { vesting_balance_id_type pay_vb; son_statistics_id_type statistics; son_status status = son_status::inactive; + flat_map sidechain_public_keys; void pay_son_fee(share_type pay, database& db); }; @@ -95,7 +97,7 @@ namespace graphene { namespace chain { FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance)(deregistered) ) FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), - (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) ) + (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(sidechain_public_keys) ) FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (graphene::db::object), diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 4300bdbb..cee9740a 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -30,6 +30,7 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op) obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; + obj.sidechain_public_keys = op.sidechain_public_keys; obj.pay_vb = op.pay_vb; obj.statistics = db().create([&](son_statistics_object& s){s.owner = obj.id;}).id; }); @@ -55,6 +56,7 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op) if(op.new_url.valid()) so.url = *op.new_url; if(op.new_deposit.valid()) so.deposit = *op.new_deposit; if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key; + if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys; if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb; }); } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 498784de..836cecb7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -10,8 +10,9 @@ namespace graphene { namespace peerplays_sidechain { enum class sidechain_type { bitcoin, - //ethereum, - //eos + ethereum, + eos, + peerplays }; using bytes = std::vector; @@ -69,4 +70,4 @@ struct sidechain_event_data { } } // graphene::peerplays_sidechain -FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin) ) +FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin)(ethereum)(eos)(peerplays) ) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 4c95b5f7..a158587a 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1312,6 +1312,7 @@ class wallet_api * display this when showing a list of SONs. May be blank. * @param deposit_id vesting balance id for SON deposit * @param pay_vb_id vesting balance id for SON pay_vb + * @param sidechain_public_keys The new set of sidechain public keys. * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction registering a SON */ @@ -1319,6 +1320,7 @@ class wallet_api string url, vesting_balance_id_type deposit_id, vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, bool broadcast = false); /** @@ -1327,11 +1329,13 @@ class wallet_api * @param witness The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON. * @param url Same as for create_son. The empty string makes it remain the same. * @param block_signing_key The new block signing public key. The empty string makes it remain the same. + * @param sidechain_public_keys The new set of sidechain public keys. The empty string makes it remain the same. * @param broadcast true if you wish to broadcast the transaction. */ signed_transaction update_son(string owner_account, string url, string block_signing_key, + flat_map sidechain_public_keys, bool broadcast = false); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6946a2cc..e88f4a00 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1863,6 +1863,7 @@ public: string url, vesting_balance_id_type deposit_id, vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, bool broadcast /* = false */) { try { account_object son_account = get_account(owner_account); @@ -1877,6 +1878,7 @@ public: son_create_op.url = url; son_create_op.deposit = deposit_id; son_create_op.pay_vb = pay_vb_id; + son_create_op.sidechain_public_keys = sidechain_public_keys; if (_remote_db->get_son_by_account(son_create_op.owner_account)) FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account)); @@ -1894,6 +1896,7 @@ public: signed_transaction update_son(string owner_account, string url, string block_signing_key, + flat_map sidechain_public_keys, bool broadcast /* = false */) { try { son_object son = get_son(owner_account); @@ -1906,6 +1909,9 @@ public: if( block_signing_key != "" ) { son_update_op.new_signing_key = public_key_type( block_signing_key ); } + if( !sidechain_public_keys.empty() ) { + son_update_op.new_sidechain_public_keys = sidechain_public_keys; + } signed_transaction tx; tx.operations.push_back( son_update_op ); @@ -4369,17 +4375,19 @@ signed_transaction wallet_api::create_son(string owner_account, string url, vesting_balance_id_type deposit_id, vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, bool broadcast /* = false */) { - return my->create_son(owner_account, url, deposit_id, pay_vb_id, broadcast); + return my->create_son(owner_account, url, deposit_id, pay_vb_id, sidechain_public_keys, broadcast); } signed_transaction wallet_api::update_son(string owner_account, string url, string block_signing_key, + flat_map sidechain_public_keys, bool broadcast /* = false */) { - return my->update_son(owner_account, url, block_signing_key, broadcast); + return my->update_son(owner_account, url, block_signing_key, sidechain_public_keys, broadcast); } signed_transaction wallet_api::delete_son(string owner_account, diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index b72bf567..b3b596c7 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -40,6 +40,7 @@ public: } void create_son(const std::string& account_name, const std::string& son_url, + flat_map& sidechain_public_keys, bool generate_maintenance = true) { graphene::wallet::brain_key_info bki; @@ -92,6 +93,7 @@ public: create_tx = fixture_.con.wallet_api_ptr->create_son(account_name, son_url, deposits[0].id, deposits[1].id, + sidechain_public_keys, true); if (generate_maintenance) @@ -110,9 +112,17 @@ BOOST_AUTO_TEST_CASE( create_sons ) BOOST_TEST_MESSAGE("SON cli wallet tests begin"); try { + flat_map sidechain_public_keys; + son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); auto son1_obj = con.wallet_api_ptr->get_son("son1account"); BOOST_CHECK(son1_obj.son_account == con.wallet_api_ptr->get_account_id("son1account")); @@ -136,8 +146,13 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) { BOOST_TEST_MESSAGE("Cli get_son and update_son Test"); + flat_map sidechain_public_keys; + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + son_test_helper sth(*this); - sth.create_son("sonmember", "http://sonmember"); + sth.create_son("sonmember", "http://sonmember", sidechain_public_keys); auto sonmember_acct = con.wallet_api_ptr->get_account("sonmember"); @@ -147,12 +162,16 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); // update SON - con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", true); + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + + con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated"); // update SON signing key - con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated2", "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG", true); + sidechain_public_keys.clear(); + con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated2", "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated2"); BOOST_CHECK(std::string(son_data.signing_key) == "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG"); @@ -168,9 +187,17 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_TEST_MESSAGE("SON Vote cli wallet tests begin"); try { + flat_map sidechain_public_keys; + son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_TEST_MESSAGE("Voting for SONs"); @@ -239,9 +266,17 @@ BOOST_AUTO_TEST_CASE( delete_son ) BOOST_TEST_MESSAGE("SON delete cli wallet tests begin"); try { - son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_TEST_MESSAGE("Deleting SONs"); signed_transaction delete_tx; @@ -279,11 +314,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) gpo = con.wallet_api_ptr->get_global_properties(); unsigned int son_number = gpo.parameters.maximum_son_count; + flat_map sidechain_public_keys; + // create son accounts for(unsigned int i = 0; i < son_number + 1; i++) { + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), - "http://son" + fc::to_pretty_string(i), false); + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); } BOOST_CHECK(generate_maintenance_block()); @@ -344,9 +385,17 @@ BOOST_AUTO_TEST_CASE( list_son ) BOOST_TEST_MESSAGE("List SONs cli wallet tests begin"); try { + flat_map sidechain_public_keys; + son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); auto res = con.wallet_api_ptr->list_sons("", 100); BOOST_REQUIRE(res.size() == 2); @@ -366,9 +415,17 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests begin"); try { - son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); @@ -515,9 +572,17 @@ BOOST_AUTO_TEST_CASE( related_functions ) global_property_object gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.size() == 0); + flat_map sidechain_public_keys; + son_test_helper sth(*this); - sth.create_son("son1account", "http://son1"); - sth.create_son("son2account", "http://son2"); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.size() == 2); @@ -543,11 +608,17 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) gpo = con.wallet_api_ptr->get_global_properties(); unsigned int son_number = gpo.parameters.maximum_son_count; + flat_map sidechain_public_keys; + // create son accounts for(unsigned int i = 0; i < son_number + 1; i++) { + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), - "http://son" + fc::to_pretty_string(i), false); + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); } BOOST_CHECK(generate_maintenance_block()); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 3740335c..6751ff03 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -83,12 +83,16 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { // alice became son { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + son_create_operation op; op.owner_account = alice_id; op.url = test_url; op.deposit = deposit; op.pay_vb = payment; op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; trx.operations.push_back(op); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -101,6 +105,7 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->url == test_url ); BOOST_CHECK( obj->signing_key == alice_public_key ); + BOOST_CHECK( obj->sidechain_public_keys.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" ); BOOST_CHECK( obj->deposit.instance == deposit.instance.value ); BOOST_CHECK( obj->pay_vb.instance == payment.instance.value ); } @@ -113,10 +118,14 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { std::string new_url = "https://anewurl.com"; { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "new bitcoin address"; + son_update_operation op; + op.son_id = son_id_type(0); op.owner_account = alice_id; op.new_url = new_url; - op.son_id = son_id_type(0); + op.new_sidechain_public_keys = sidechain_public_keys; trx.operations.push_back(op); sign(trx, alice_private_key); @@ -129,6 +138,7 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { auto obj = idx.find( alice_id ); BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->url == new_url ); + BOOST_CHECK( obj->sidechain_public_keys.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "new bitcoin address" ); } BOOST_AUTO_TEST_CASE( delete_son_test ) { @@ -335,7 +345,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::son; op.policy = dormant_vesting_policy_initializer {}; - + trx.operations.push_back(op); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); set_expiration(db, trx); @@ -352,7 +362,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) op.owner = bob_id; op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::normal; - + trx.operations.push_back(op); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); set_expiration(db, trx); @@ -652,7 +662,7 @@ BOOST_AUTO_TEST_CASE( son_witness_proposal_test ) generate_block(); } FC_LOG_AND_RETHROW() -} +} BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { From 01fb1db6a606bedde25bcd0bd702a10e14cdb569 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Mon, 6 Jan 2020 23:59:35 +1100 Subject: [PATCH 16/64] SON194-SON195 - Report SON Down, addition of SON Account for sidechain consensus (#244) * SON194-SON195 - Addition of SON BTC Account and report son down changes * SON194-SON195 - SON BTC Account errors rectification * SON194-SON195 - Adding Tests * User sidechain address mappings (#240) * WIP: Sidechain objects * Revert "WIP: Sidechain objects" This reverts commit 8676940a281604688771e96ceb1e65a35d98e8e5. * WIP: User sidechain address mappings * Fix reflection problem * Reflect missing members of sidechain_address_update_operation * Add sidechain address operation tests * Enable RPC calls * Fix build errors due to merge conflict * Fix RPC, add CLI wallet commands for sidechain addresses * Improved peerplays_sidechain_plugin_impl * Remove short param for son-id * Fix crashing errors on bitcoin event received * Code review changes * SON207 - Introduce scheduling for SONs similar to witnesses (#251) * Extend SON objects to contain sidechain public keys (#254) Co-authored-by: obucinac --- libraries/app/impacted.cpp | 3 + libraries/chain/db_init.cpp | 1 + libraries/chain/db_maint.cpp | 64 ++++++++++ libraries/chain/db_notify.cpp | 3 + .../chain/protocol/chain_parameters.hpp | 5 + .../graphene/chain/protocol/operations.hpp | 3 +- .../include/graphene/chain/protocol/son.hpp | 18 ++- .../include/graphene/chain/son_evaluator.hpp | 9 ++ libraries/chain/proposal_evaluator.cpp | 4 + libraries/chain/son_evaluator.cpp | 32 +++++ tests/tests/son_operations_tests.cpp | 114 ++++++++++++++++++ 11 files changed, 254 insertions(+), 2 deletions(-) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 5b6f5411..c8b1122e 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -319,6 +319,9 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } + void operator()( const son_report_down_operation& op ){ + _impacted.insert( op.payer ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 31c0dcd5..2aab032d 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -253,6 +253,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 8fb72566..267c58f5 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -461,6 +461,70 @@ void database::update_active_sons() flat_set active_sons(gpo.active_sons.begin(), gpo.active_sons.end()); _sso.scheduler.update(active_sons); }); + + if(gpo.active_sons.size() > 0 ) { + if(gpo.parameters.get_son_btc_account_id() == GRAPHENE_NULL_ACCOUNT) { + const auto& son_btc_account = create( [&]( account_object& obj ) { + uint64_t total_votes = 0; + obj.name = "son_btc_account"; + obj.statistics = create([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id; + obj.membership_expiration_date = time_point_sec::maximum(); + obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + + for( const auto& son_id : gpo.active_sons ) + { + const son_object& son = get(son_id); + total_votes += _vote_tally_buffer[son.vote_id]; + } + // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, + // then I want to keep the most significant 16 bits of what's left. + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + + for( const auto& son_id : gpo.active_sons ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + const son_object& son = get(son_id); + uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); + obj.active.account_auths[son.son_account] += votes; + obj.active.weight_threshold += votes; + } + obj.active.weight_threshold *= 2; + obj.active.weight_threshold /= 3; + obj.active.weight_threshold += 1; + }); + + modify( gpo, [&]( global_property_object& gpo ) { + gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); + }); + } else { + modify( get(gpo.parameters.get_son_btc_account_id()), [&]( account_object& obj ) + { + uint64_t total_votes = 0; + for( const auto& son_id : gpo.active_sons ) + { + const son_object& son = get(son_id); + total_votes += _vote_tally_buffer[son.vote_id]; + } + // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, + // then I want to keep the most significant 16 bits of what's left. + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + for( const auto& son_id : gpo.active_sons ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + const son_object& son = get(son_id); + uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); + obj.active.account_auths[son.son_account] += votes; + obj.active.weight_threshold += votes; + } + obj.active.weight_threshold *= 2; + obj.active.weight_threshold /= 3; + obj.active.weight_threshold += 1; + }); + } + } } FC_CAPTURE_AND_RETHROW() } void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 83a58c45..d53955a3 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -306,6 +306,9 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } + void operator()( const son_report_down_operation& op ) { + _impacted.insert( op.payer ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index c62274ba..51024e16 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -46,6 +46,7 @@ namespace graphene { namespace chain { optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; + optional < account_id_type > son_btc_account; }; struct chain_parameters @@ -138,6 +139,9 @@ namespace graphene { namespace chain { inline uint16_t son_pay_daily_max()const { return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX; } + inline account_id_type get_son_btc_account_id() const { + return extensions.value.son_btc_account.valid() ? *extensions.value.son_btc_account : GRAPHENE_NULL_ACCOUNT; + } }; } } // graphene::chain @@ -155,6 +159,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) + (son_btc_account) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index e1cc5225..646f2e69 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -144,7 +144,8 @@ namespace graphene { namespace chain { son_heartbeat_operation, sidechain_address_add_operation, sidechain_address_update_operation, - sidechain_address_delete_operation + sidechain_address_delete_operation, + son_report_down_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 914928a6..6f4eaa7e 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -63,6 +63,19 @@ namespace graphene { namespace chain { share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; + struct son_report_down_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type payer; + time_point_sec down_ts; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + } } // namespace graphene::chain FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) ) @@ -77,4 +90,7 @@ FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(payer)(owner_account) ) FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) ) \ No newline at end of file +FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) ) + +FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/son_evaluator.hpp b/libraries/chain/include/graphene/chain/son_evaluator.hpp index 6b82f5e5..4615c95b 100644 --- a/libraries/chain/include/graphene/chain/son_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/son_evaluator.hpp @@ -40,4 +40,13 @@ public: object_id_type do_apply(const son_heartbeat_operation& o); }; +class son_report_down_evaluator : public evaluator +{ +public: + typedef son_report_down_operation operation_type; + + void_result do_evaluate(const son_report_down_operation& o); + object_id_type do_apply(const son_report_down_operation& o); +}; + } } // namespace graphene::chain diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index b50d4b82..dc1aba3e 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -152,6 +152,10 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" ); } + void operator()(const son_report_down_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" ); + } + // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index cee9740a..619d2e22 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -143,4 +143,36 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } +void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op) +{ try { + FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account"); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_id) != idx.end() ); + auto itr = idx.find(op.son_id); + auto stats = itr->statistics( db() ); + FC_ASSERT(itr->status == son_status::active, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); + FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + if (itr->status == son_status::active) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.last_down_timestamp = op.down_ts; + }); + + db().modify(*itr, [&op](son_object &so) { + so.status = son_status::in_maintenance; + }); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + } } // namespace graphene::chain diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 6751ff03..ad8cf0bd 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -746,4 +746,118 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); } } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( son_report_down_test ) { + + try + { + INVOKE(son_heartbeat_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + BOOST_CHECK( obj->status == son_status::active); + + const auto& son_btc_account = db.create( [&]( account_object& obj ) { + obj.name = "son_btc_account"; + obj.statistics = db.create([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id; + obj.membership_expiration_date = time_point_sec::maximum(); + obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + + obj.owner.add_authority( bob_id, 1 ); + obj.active.add_authority( bob_id, 1 ); + obj.active.weight_threshold = 1; + obj.owner.weight_threshold = 1; + }); + + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + _gpo.parameters.extensions.value.son_pay_daily_max = 200; + _gpo.parameters.witness_pay_per_block = 0; + + _gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); + if( _gpo.pending_parameters ) + _gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); + }); + + { + // Check that transaction fails if down_ts < last_active_timestamp + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.son_id = son_id_type(0); + op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); + + trx.operations.push_back(op); + sign(trx, bob_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + + { + // Check that transaction fails if payer is not son_btc_account. + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = alice_id; + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + + { + // Check that transaction succeeds after getting enough approvals on son_btc_account. + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + + BOOST_CHECK( obj->status == son_status::in_maintenance); + BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts); + } + + { + // Check that transaction fails if report down sent for an in_maintenance SON. + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + sign(trx, bob_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From 59a02b1460baa877b18605e1fad86f9d021bc966 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Tue, 7 Jan 2020 00:06:49 +1100 Subject: [PATCH 17/64] SON206 - Plugin SON Heartbeat changes (#250) * SON206 - Plugin SON Heartbeat changes * SON206 - Plugin SON Heartbeat changes, comment removal * SON206 - Plugin SON Heartbeat changes, stub testing and changes * SON206 - Plugin SON Heartbeat changes, removing debugs prints --- .../include/graphene/chain/son_object.hpp | 4 + .../peerplays_sidechain_plugin.hpp | 1 + .../peerplays_sidechain_plugin.cpp | 89 ++++++++++++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 11cabc2a..8a876bfd 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -103,4 +103,8 @@ FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (graphene::db::object), (owner) (txs_signed) + (total_downtime) + (current_interval_downtime) + (last_down_timestamp) + (last_active_timestamp) ) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 2f72ae06..e986fe8e 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index a8741cff..0a6b4964 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -23,14 +23,18 @@ class peerplays_sidechain_plugin_impl boost::program_options::options_description& cfg); void plugin_initialize(const boost::program_options::variables_map& options); void plugin_startup(); - -private: + void schedule_heartbeat_loop(); + void heartbeat_loop(); + private: peerplays_sidechain_plugin& plugin; bool config_ready_son; bool config_ready_bitcoin; std::unique_ptr net_manager; + std::map _private_keys; + std::set _sons; + fc::future _heartbeat_task; }; peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : @@ -43,6 +47,14 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { + try { + if( _heartbeat_task.valid() ) + _heartbeat_task.cancel_and_wait(__FUNCTION__); + } catch(fc::canceled_exception&) { + //Expected exception. Move along. + } catch(fc::exception& e) { + edump((e.to_detail_string())); + } } void peerplays_sidechain_plugin_impl::plugin_set_program_options( @@ -74,6 +86,31 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt { config_ready_son = options.count( "son-id" ) && options.count( "peerplays-private-key" ); if (config_ready_son) { + LOAD_VALUE_SET(options, "son-id", _sons, chain::son_id_type) + if( options.count("peerplays-private-key") ) + { + const std::vector key_id_to_wif_pair_strings = options["peerplays-private-key"].as>(); + for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) + { + auto key_id_to_wif_pair = graphene::app::dejsonify >(key_id_to_wif_pair_string, 5); + ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first)); + fc::optional private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second); + if (!private_key) + { + // the key isn't in WIF format; see if they are still passing the old native private key format. This is + // just here to ease the transition, can be removed soon + try + { + private_key = fc::variant(key_id_to_wif_pair.second, 2).as(1); + } + catch (const fc::exception&) + { + FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second)); + } + } + _private_keys[key_id_to_wif_pair.first] = *private_key; + } + } } else { wlog("Haven't set up SON parameters"); throw; @@ -117,11 +154,59 @@ void peerplays_sidechain_plugin_impl::plugin_startup() ilog("Bitcoin sidechain handler running"); } + if( !_sons.empty() && !_private_keys.empty() ) + { + ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); + schedule_heartbeat_loop(); + } else + elog("No sons configured! Please add SON IDs and private keys to configuration."); + //if (config_ready_ethereum) { // ilog("Ethereum sidechain handler running"); //} } +void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() +{ + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_heartbeat = 180000000; + + fc::time_point next_wakeup( now + fc::microseconds( time_to_next_heartbeat ) ); + + _heartbeat_task = fc::schedule([this]{heartbeat_loop();}, + next_wakeup, "SON Heartbeat Production"); +} + +void peerplays_sidechain_plugin_impl::heartbeat_loop() +{ + chain::database& d = plugin.database(); + chain::son_id_type son_id = *(_sons.begin()); + const chain::global_property_object& gpo = d.get_global_properties(); + auto it = std::find(gpo.active_sons.begin(), gpo.active_sons.end(), son_id); + if(it != gpo.active_sons.end()) { + ilog("peerplays_sidechain_plugin: sending heartbeat"); + chain::son_heartbeat_operation op; + const auto& idx = d.get_index_type().indices().get(); + auto son_obj = idx.find( son_id ); + op.owner_account = son_obj->son_account; + op.son_id = son_id; + op.ts = fc::time_point::now() + fc::seconds(0); + chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx); + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin: sending heartbeat failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + schedule_heartbeat_loop(); +} + } // end namespace detail peerplays_sidechain_plugin::peerplays_sidechain_plugin() : From 47eafcf6c0ff66b25855cc0f4af69e10cc4096b2 Mon Sep 17 00:00:00 2001 From: obucinac Date: Mon, 13 Jan 2020 14:58:43 +0100 Subject: [PATCH 18/64] Wallet recreation on new set of SONs voted in (#256) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/database.cpp | 1 + libraries/chain/db_maint.cpp | 44 ++++++++++++++++--- libraries/chain/db_management.cpp | 1 + libraries/chain/db_sidechain.cpp | 10 +++++ libraries/chain/db_witness_schedule.cpp | 5 ++- .../chain/include/graphene/chain/database.hpp | 9 ++++ .../graphene/chain/global_property_object.hpp | 3 +- .../include/graphene/chain/protocol/types.hpp | 3 ++ .../chain/include/graphene/chain/son_info.hpp | 36 +++++++++++++++ .../graphene/chain/son_wallet_object.hpp | 36 +++++++++++++++ libraries/wallet/wallet.cpp | 11 ++++- 12 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 libraries/chain/db_sidechain.cpp create mode 100644 libraries/chain/include/graphene/chain/son_info.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_object.hpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 4e3d4625..bba9e7f1 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -19,6 +19,7 @@ if( GRAPHENE_DISABLE_UNITY_BUILD ) db_maint.cpp db_management.cpp db_market.cpp + db_sidechain.cpp db_update.cpp db_witness_schedule.cpp ) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 7711f543..36e2a161 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -31,6 +31,7 @@ #include "db_maint.cpp" #include "db_management.cpp" #include "db_market.cpp" +#include "db_sidechain.cpp" #include "db_update.cpp" #include "db_witness_schedule.cpp" #include "db_notify.cpp" \ No newline at end of file diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 267c58f5..df23cd10 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -445,20 +446,49 @@ void database::update_active_sons() } } ); + // Compare current and to-be lists of active sons + //const global_property_object& gpo = get_global_properties(); + auto cur_active_sons = gpo.active_sons; + vector new_active_sons; + for( const son_object& son : sons ) { + son_info swi; + swi.son_id = son.id; + swi.total_votes = son.total_votes; + swi.signing_key = son.signing_key; + swi.sidechain_public_keys = son.sidechain_public_keys; + new_active_sons.push_back(swi); + } + + bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size()); + if (son_sets_equal) { + for( size_t i = 0; i < cur_active_sons.size(); i++ ) { + son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i); + } + } + + if (son_sets_equal) { + ilog( "Active SONs set NOT CHANGED" ); + } else { + ilog( "Active SONs set CHANGED" ); + // Store new SON info, initiate wallet recreation and transfer of funds + } + modify(gpo, [&]( global_property_object& gp ){ gp.active_sons.clear(); - gp.active_sons.reserve(sons.size()); - std::transform(sons.begin(), sons.end(), - std::inserter(gp.active_sons, gp.active_sons.end()), - [](const son_object& s) { - return s.id; - }); + gp.active_sons.reserve(new_active_sons.size()); + gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end()); }); const son_schedule_object& sso = son_schedule_id_type()(*this); modify(sso, [&](son_schedule_object& _sso) { - flat_set active_sons(gpo.active_sons.begin(), gpo.active_sons.end()); + flat_set active_sons; + active_sons.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_sons, active_sons.end()), + [](const son_info& swi) { + return swi.son_id; + }); _sso.scheduler.update(active_sons); }); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 029a55d4..f6d164d2 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -40,6 +40,7 @@ database::database() : { initialize_indexes(); initialize_evaluators(); + initialize_db_sidechain(); } database::~database() diff --git a/libraries/chain/db_sidechain.cpp b/libraries/chain/db_sidechain.cpp new file mode 100644 index 00000000..77594b3f --- /dev/null +++ b/libraries/chain/db_sidechain.cpp @@ -0,0 +1,10 @@ +#include + +namespace graphene { namespace chain { + +void database::initialize_db_sidechain() +{ + recreate_primary_wallet = false; +} + +} } diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 3ce11443..31caad4b 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -200,8 +201,8 @@ void database::update_son_schedule() _sso.current_shuffled_sons.clear(); _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); - for( const son_id_type& w : gpo.active_sons ) - _sso.current_shuffled_sons.push_back( w ); + for( const son_info& w : gpo.active_sons ) + _sso.current_shuffled_sons.push_back( w.son_id ); auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 719c6240..78d05ef9 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,6 +40,8 @@ #include +#include + #include #include @@ -600,6 +602,13 @@ namespace graphene { namespace chain { * database::close() has not been called, or failed during execution. */ bool _opened = false; + + /////////////////////// db_sidechain.cpp //////////////////// + public: + bool recreate_primary_wallet; + void initialize_db_sidechain(); + protected: + private: }; namespace detail diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 130648e9..1d985a2d 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace graphene { namespace chain { @@ -51,7 +52,7 @@ namespace graphene { namespace chain { uint32_t next_available_vote_id = 0; vector active_committee_members; // updated once per maintenance interval flat_set active_witnesses; // updated once per maintenance interval - vector active_sons; // updated once per maintenance interval + vector active_sons; // updated once per maintenance interval }; /** diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 5b040850..1d056740 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -147,6 +147,7 @@ namespace graphene { namespace chain { bet_object_type, son_object_type, son_proposal_object_type, + son_wallet_object_type, sidechain_address_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -211,6 +212,7 @@ namespace graphene { namespace chain { class bet_object; class son_object; class son_proposal_object; + class son_wallet_object; class sidechain_address_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; @@ -240,6 +242,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type; typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; + typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; // implementation types diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp new file mode 100644 index 00000000..d30f0f6b --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_info + * @brief tracks information about a SON info required to re/create primary wallet + * @ingroup object + */ + struct son_info { + son_id_type son_id; + uint64_t total_votes = 0; + public_key_type signing_key; + flat_map sidechain_public_keys; + + bool operator==(const son_info& rhs) { + bool son_sets_equal = + (son_id == rhs.son_id) && + (total_votes == rhs.total_votes) && + (signing_key == rhs.signing_key) && + (sidechain_public_keys.size() == rhs.sidechain_public_keys.size()); + + if (son_sets_equal) { + // Compare sidechain public keys + } + return son_sets_equal; + } + }; + +} } + +FC_REFLECT( graphene::chain::son_info, + (son_id)(total_votes)(signing_key)(sidechain_public_keys) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp new file mode 100644 index 00000000..c3ea204d --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_object + * @brief tracks information about a SON wallet. + * @ingroup object + */ + class son_wallet_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_object_type; + + flat_map addresses; + }; + + struct by_sidechain_type; + struct by_address; + using son_wallet_multi_index_type = multi_index_container< + son_wallet_object, + indexed_by< + ordered_unique< tag, + member + > + > + >; + using son_wallet_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object), + (addresses) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index e88f4a00..be837940 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1942,7 +1942,14 @@ public: map list_active_sons() { try { global_property_object gpo = get_global_properties(); - std::vector> son_objects = _remote_db->get_sons(gpo.active_sons); + vector son_ids; + son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(son_ids, son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + std::vector> son_objects = _remote_db->get_sons(son_ids); vector owners; owners.resize(son_objects.size()); std::transform(son_objects.begin(), son_objects.end(), owners.begin(), @@ -1952,7 +1959,7 @@ public: }); vector> accs = _remote_db->get_accounts(owners); map result; - std::transform(accs.begin(), accs.end(), gpo.active_sons.begin(), + std::transform(accs.begin(), accs.end(), son_ids.begin(), std::inserter(result, result.end()), [](fc::optional& acct, son_id_type& sid) { FC_ASSERT(acct, "Invalid active SONs list in global properties."); From 6fe0acb12d1d86fa0f218dfe4cdb06f67e6b1716 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Mon, 13 Jan 2020 16:05:28 +0100 Subject: [PATCH 19/64] Fix build errors --- libraries/chain/db_maint.cpp | 16 ++++++++-------- .../peerplays_sidechain_plugin.cpp | 13 +++++++++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index df23cd10..7b111fd4 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -502,19 +502,19 @@ void database::update_active_sons() obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - for( const auto& son_id : gpo.active_sons ) + for( const auto& son_info : gpo.active_sons ) { - const son_object& son = get(son_id); + const son_object& son = get(son_info.son_id); total_votes += _vote_tally_buffer[son.vote_id]; } // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, // then I want to keep the most significant 16 bits of what's left. int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& son_id : gpo.active_sons ) + for( const auto& son_info : gpo.active_sons ) { // Ensure that everyone has at least one vote. Zero weights aren't allowed. - const son_object& son = get(son_id); + const son_object& son = get(son_info.son_id); uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); obj.active.account_auths[son.son_account] += votes; obj.active.weight_threshold += votes; @@ -533,18 +533,18 @@ void database::update_active_sons() modify( get(gpo.parameters.get_son_btc_account_id()), [&]( account_object& obj ) { uint64_t total_votes = 0; - for( const auto& son_id : gpo.active_sons ) + for( const auto& son_info : gpo.active_sons ) { - const son_object& son = get(son_id); + const son_object& son = get(son_info.son_id); total_votes += _vote_tally_buffer[son.vote_id]; } // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, // then I want to keep the most significant 16 bits of what's left. int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& son_id : gpo.active_sons ) + for( const auto& son_info : gpo.active_sons ) { // Ensure that everyone has at least one vote. Zero weights aren't allowed. - const son_object& son = get(son_id); + const son_object& son = get(son_info.son_id); uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); obj.active.account_auths[son.son_account] += votes; obj.active.weight_threshold += votes; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 0a6b4964..3dc48b6a 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -182,8 +182,17 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() chain::database& d = plugin.database(); chain::son_id_type son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); - auto it = std::find(gpo.active_sons.begin(), gpo.active_sons.end(), son_id); - if(it != gpo.active_sons.end()) { + + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); + if(it != active_son_ids.end()) { ilog("peerplays_sidechain_plugin: sending heartbeat"); chain::son_heartbeat_operation op; const auto& idx = d.get_index_type().indices().get(); From 77927da236283ca860609b5883c70533bfbb0bf9 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Wed, 15 Jan 2020 00:13:02 +1100 Subject: [PATCH 20/64] SON212-SON213 - Add Sidechain Plugin Code to report and approve SON Down proposal (#260) * SON212 - Add Sidechain Plugin Code to report SON Down * SON212-SON213 - Add Sidechain Plugin Code to report SON Down, Approve proposal from sidechain plugin --- .../peerplays_sidechain_plugin.cpp | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 3dc48b6a..8aa3e92a 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -25,6 +26,9 @@ class peerplays_sidechain_plugin_impl void plugin_startup(); void schedule_heartbeat_loop(); void heartbeat_loop(); + chain::proposal_create_operation create_son_down_proposal(chain::son_id_type son_id, fc::time_point_sec last_active_ts); + void on_block_applied( const signed_block& b ); + void on_objects_new(const vector& new_object_ids); private: peerplays_sidechain_plugin& plugin; @@ -182,7 +186,6 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() chain::database& d = plugin.database(); chain::son_id_type son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); - vector active_son_ids; active_son_ids.reserve(gpo.active_sons.size()); std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), @@ -207,7 +210,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin: sending heartbeat failed with exception ${e}",("e", e.what())); + ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); return false; } }); @@ -216,6 +219,113 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() schedule_heartbeat_loop(); } +chain::proposal_create_operation peerplays_sidechain_plugin_impl::create_son_down_proposal(chain::son_id_type son_id, fc::time_point_sec last_active_ts) +{ + chain::database& d = plugin.database(); + chain::son_id_type my_son_id = *(_sons.begin()); + const chain::global_property_object& gpo = d.get_global_properties(); + const auto& idx = d.get_index_type().indices().get(); + auto son_obj = idx.find( my_son_id ); + + chain::son_report_down_operation son_down_op; + son_down_op.payer = gpo.parameters.get_son_btc_account_id(); + son_down_op.son_id = son_id; + son_down_op.down_ts = last_active_ts; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = son_obj->son_account; + proposal_op.proposed_ops.push_back( op_wrapper( son_down_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( d.head_block_time().sec_since_epoch() + lifetime ); + return proposal_op; +} + +void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) +{ + chain::database& d = plugin.database(); + chain::son_id_type my_son_id = *(_sons.begin()); + const chain::global_property_object& gpo = d.get_global_properties(); + // Return if there are no active SONs + if(gpo.active_sons.size() <= 0) { + return; + } + + chain::son_id_type next_son_id = d.get_scheduled_son(1); + if(next_son_id == my_son_id) { + const auto& idx = d.get_index_type().indices().get(); + for(auto son_id: gpo.active_sons) { + auto son_obj = idx.find( son_id ); + auto stats = son_obj->statistics(d); + fc::time_point_sec last_active_ts = stats.last_active_timestamp; + int64_t down_threshold = 2*180000000; + if((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { + chain::proposal_create_operation op = create_son_down_proposal(son_id, last_active_ts); + chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx); + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } + } +} + +void peerplays_sidechain_plugin_impl::on_objects_new(const vector& new_object_ids) +{ + chain::database& d = plugin.database(); + chain::son_id_type my_son_id = *(_sons.begin()); + const chain::global_property_object& gpo = d.get_global_properties(); + const auto& idx = d.get_index_type().indices().get(); + auto son_obj = idx.find( my_son_id ); + + auto it = std::find(gpo.active_sons.begin(), gpo.active_sons.end(), my_son_id); + if(it == gpo.active_sons.end()) { + return; + } + + auto approve_proposal = [ & ]( const chain::proposal_id_type& id ) + { + chain::proposal_update_operation puo; + puo.fee_paying_account = son_obj->son_account; + puo.proposal = id; + puo.active_approvals_to_add = { son_obj->son_account }; + chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, puo); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx); + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + }; + + for(auto object_id: new_object_ids) { + if( object_id.is() ) { + const object* obj = d.find_object(object_id); + const chain::proposal_object* proposal = dynamic_cast(obj); + if(proposal == nullptr) { + return; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( proposal->id ); + } + } + } +} + } // end namespace detail peerplays_sidechain_plugin::peerplays_sidechain_plugin() : @@ -245,6 +355,8 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options: { ilog("peerplays sidechain plugin: plugin_initialize()"); my->plugin_initialize(options); + database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } ); + database().new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { my->on_objects_new(ids); }); } void peerplays_sidechain_plugin::plugin_startup() From 5d7ab51d58eaee98180e4ca786dab8cddf76cbbd Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Thu, 16 Jan 2020 07:47:05 +1100 Subject: [PATCH 21/64] SON212-SON213 - Fix Build Error (#262) * SON212-SON213 - Fix Build Error * SON212-SON213 - Fix Build Error Add smart_ref definition for linking --- .../peerplays_sidechain_plugin.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 8aa3e92a..e78c78c5 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -253,13 +254,13 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) chain::son_id_type next_son_id = d.get_scheduled_son(1); if(next_son_id == my_son_id) { const auto& idx = d.get_index_type().indices().get(); - for(auto son_id: gpo.active_sons) { - auto son_obj = idx.find( son_id ); + for(auto son_inf: gpo.active_sons) { + auto son_obj = idx.find( son_inf.son_id ); auto stats = son_obj->statistics(d); fc::time_point_sec last_active_ts = stats.last_active_timestamp; int64_t down_threshold = 2*180000000; if((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { - chain::proposal_create_operation op = create_son_down_proposal(son_id, last_active_ts); + chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); fc::future fut = fc::async( [&](){ try { @@ -284,9 +285,16 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector().indices().get(); auto son_obj = idx.find( my_son_id ); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); - auto it = std::find(gpo.active_sons.begin(), gpo.active_sons.end(), my_son_id); - if(it == gpo.active_sons.end()) { + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), my_son_id); + if(it == active_son_ids.end()) { return; } From 691468dff0d98a76066557a51a7eefb2a22f9ada Mon Sep 17 00:00:00 2001 From: Roshan Syed Date: Fri, 17 Jan 2020 08:22:26 -0400 Subject: [PATCH 22/64] Updated gitlab CI to sync submodules (#265) --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 188f4a45..530caf2f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ stages: build: stage: build script: + - git submodule sync - git submodule update --init --recursive - cmake . - make -j$(nproc) From 5af31dd90db61eb5b617c17138f73f2471e2736f Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Sat, 18 Jan 2020 07:28:13 +1100 Subject: [PATCH 23/64] SON217 - SON Maintenance,Heartbeat state transition changes (#264) * SON217 - SON Maintenance,Heartbeat state transition changes * SON217 - SON Maintenance,Heartbeat state transition changes --- libraries/chain/son_evaluator.cpp | 24 +++++++++++-- .../peerplays_sidechain_plugin.cpp | 8 +++-- tests/tests/son_operations_tests.cpp | 35 ++++++++++++++++++- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 619d2e22..fc4802d2 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -123,6 +123,22 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& auto itr = idx.find(op.son_id); if(itr != idx.end()) { + const global_property_object& gpo = db().get_global_properties(); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); + bool is_son_active = true; + + if(it_son == active_son_ids.end()) { + is_son_active = false; + } + if(itr->status == son_status::in_maintenance) { db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) { @@ -130,8 +146,12 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& sso.last_active_timestamp = op.ts; } ); - db().modify(*itr, [&op](son_object &so) { - so.status = son_status::active; + db().modify(*itr, [&is_son_active](son_object &so) { + if(is_son_active) { + so.status = son_status::active; + } else { + so.status = son_status::inactive; + } }); } else if (itr->status == son_status::active) { db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index e78c78c5..2874605b 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -186,6 +186,10 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { chain::database& d = plugin.database(); chain::son_id_type son_id = *(_sons.begin()); + const auto& idx = d.get_index_type().indices().get(); + auto son_obj = idx.find( son_id ); + if(son_obj == idx.end()) + return; const chain::global_property_object& gpo = d.get_global_properties(); vector active_son_ids; active_son_ids.reserve(gpo.active_sons.size()); @@ -196,11 +200,9 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() }); auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); - if(it != active_son_ids.end()) { + if(it != active_son_ids.end() || son_obj->status == chain::son_status::in_maintenance) { ilog("peerplays_sidechain_plugin: sending heartbeat"); chain::son_heartbeat_operation op; - const auto& idx = d.get_index_type().indices().get(); - auto son_obj = idx.find( son_id ); op.owner_account = son_obj->son_account; op.son_id = son_id; op.ts = fc::time_point::now() + fc::seconds(0); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index ad8cf0bd..f6037084 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -723,7 +723,40 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { generate_block(); trx.clear(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); - downtime = op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); + downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); + BOOST_CHECK( obj->status == son_status::inactive); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + + // Modify SON's status to in_maintenance + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + // SON is selected as one of the active SONs + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + son_info son_inf; + son_inf.son_id = son_id_type(0); + _gpo.active_sons.push_back(son_inf); + }); + + { + generate_block(); + // Send Heartbeat for an in_maintenance SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); + downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); BOOST_CHECK( obj->status == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); } From 11339c17342b51c7a5857e7359ecac5889c485cd Mon Sep 17 00:00:00 2001 From: gladcow Date: Fri, 17 Jan 2020 23:30:45 +0300 Subject: [PATCH 24/64] [SON-202] Implement cli_wallet commands for maintenance mode (#261) * Add stop_son_maintenance CLI call * fix bug with SON activation * son_maintenance_operation * son_maintenance_operation tests * cli test for son maintenance state * start_son_maintenance CLI call * keep maintenance state during active SON set changes --- libraries/app/impacted.cpp | 3 + libraries/chain/db_init.cpp | 1 + libraries/chain/db_maint.cpp | 50 +++++++++++++++ libraries/chain/db_notify.cpp | 3 + .../graphene/chain/protocol/operations.hpp | 3 +- .../include/graphene/chain/protocol/son.hpp | 18 +++++- .../include/graphene/chain/son_evaluator.hpp | 9 +++ .../include/graphene/chain/son_object.hpp | 2 +- libraries/chain/proposal_evaluator.cpp | 4 ++ libraries/chain/son_evaluator.cpp | 27 ++++++++ .../wallet/include/graphene/wallet/wallet.hpp | 18 ++++++ libraries/wallet/wallet.cpp | 45 ++++++++++++++ tests/cli/son.cpp | 61 +++++++++++++++++++ tests/tests/son_operations_tests.cpp | 34 ++++++++++- 14 files changed, 272 insertions(+), 6 deletions(-) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index c8b1122e..0aefe922 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -310,6 +310,9 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_maintenance_operation& op ){ + _impacted.insert( op.owner_account ); + } void operator()( const sidechain_address_add_operation& op ){ _impacted.insert( op.sidechain_address_account ); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2aab032d..5b5f8029 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -250,6 +250,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7b111fd4..31813724 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -471,6 +471,56 @@ void database::update_active_sons() } else { ilog( "Active SONs set CHANGED" ); // Store new SON info, initiate wallet recreation and transfer of funds + vector sons_to_remove; + // find all cur_active_sons members that is not in new_active_sons + for_each(cur_active_sons.begin(), cur_active_sons.end(), + [&sons_to_remove, &new_active_sons](const son_info& si) + { + if(std::find(new_active_sons.begin(), new_active_sons.end(), si) == + new_active_sons.end()) + { + sons_to_remove.push_back(si); + } + } + ); + const auto& idx = get_index_type().indices().get(); + for( const son_info& si : sons_to_remove ) + { + auto son = idx.find( si.son_id ); + if(son == idx.end()) // SON is deleted already + continue; + // keep maintenance status for nodes becoming inactive + if(son->status == son_status::active) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::inactive; + }); + } + } + vector sons_to_add; + // find all new_active_sons members that is not in cur_active_sons + for_each(new_active_sons.begin(), new_active_sons.end(), + [&sons_to_add, &cur_active_sons](const son_info& si) + { + if(std::find(cur_active_sons.begin(), cur_active_sons.end(), si) == + cur_active_sons.end()) + { + sons_to_add.push_back(si); + } + } + ); + for( const son_info& si : sons_to_add ) + { + auto son = idx.find( si.son_id ); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", si.son_id)); + // keep maintenance status for new nodes + if(son->status == son_status::inactive) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::active; + }); + } + } } modify(gpo, [&]( global_property_object& gp ){ diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index d53955a3..c7946906 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -297,6 +297,9 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_maintenance_operation& op ) { + _impacted.insert( op.owner_account ); + } void operator()( const sidechain_address_add_operation& op ) { _impacted.insert( op.sidechain_address_account ); } diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 646f2e69..07695705 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -145,7 +145,8 @@ namespace graphene { namespace chain { sidechain_address_add_operation, sidechain_address_update_operation, sidechain_address_delete_operation, - son_report_down_operation + son_report_down_operation, + son_maintenance_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 6f4eaa7e..dc11d5be 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -63,6 +63,7 @@ namespace graphene { namespace chain { share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; + struct son_report_down_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; @@ -76,6 +77,18 @@ namespace graphene { namespace chain { share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; + struct son_maintenance_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type owner_account; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + } } // namespace graphene::chain FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) ) @@ -93,4 +106,7 @@ FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) ) FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) ) \ No newline at end of file +FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) ) + +FC_REFLECT(graphene::chain::son_maintenance_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account) ) diff --git a/libraries/chain/include/graphene/chain/son_evaluator.hpp b/libraries/chain/include/graphene/chain/son_evaluator.hpp index 4615c95b..87554fbc 100644 --- a/libraries/chain/include/graphene/chain/son_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/son_evaluator.hpp @@ -49,4 +49,13 @@ public: object_id_type do_apply(const son_report_down_operation& o); }; +class son_maintenance_evaluator : public evaluator +{ +public: + typedef son_maintenance_operation operation_type; + + void_result do_evaluate(const son_maintenance_operation& o); + object_id_type do_apply(const son_maintenance_operation& o); +}; + } } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 8a876bfd..50f7385a 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -97,7 +97,7 @@ namespace graphene { namespace chain { FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance)(deregistered) ) FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), - (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(sidechain_public_keys) ) + (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(status)(sidechain_public_keys) ) FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (graphene::db::object), diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index dc1aba3e..767ca415 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -156,6 +156,10 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" ); } + void operator()(const son_maintenance_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" ); + } + // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index fc4802d2..34f4daa3 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -195,4 +195,31 @@ object_id_type son_report_down_evaluator::do_apply(const son_report_down_operati return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } +void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + FC_ASSERT( itr != idx.end() ); + // Inactive SONs can't go to maintenance + FC_ASSERT(itr->status == son_status::active || itr->status == son_status::in_maintenance, "Inactive SONs can't go to maintenance"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + if(itr->status == son_status::active) { + db().modify(*itr, [](son_object &so) { + so.status = son_status::in_maintenance; + }); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + } } // namespace graphene::chain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index a158587a..aeb9107d 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1350,6 +1350,24 @@ class wallet_api signed_transaction delete_son(string owner_account, bool broadcast = false); + /** Modify status of the SON owned by the given account to maintenance. + * + * @param owner_account the name or id of the account which is owning the SON + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction + */ + signed_transaction start_son_maintenance(string owner_account, + bool broadcast = false); + + /** Modify status of the SON owned by the given account back to active. + * + * @param owner_account the name or id of the account which is owning the SON + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction + */ + signed_transaction stop_son_maintenance(string owner_account, + bool broadcast = false); + /** Lists all SONs in the blockchain. * This returns a list of all account names that own SON, and the associated SON id, * sorted by name. This lists SONs whether they are currently voted in or not. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index be837940..658587f4 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1939,6 +1939,41 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + signed_transaction start_son_maintenance(string owner_account, + bool broadcast) + { try { + son_object son = get_son(owner_account); + + son_maintenance_operation op; + op.owner_account = son.son_account; + op.son_id = son.id; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + + signed_transaction stop_son_maintenance(string owner_account, + bool broadcast) + { try { + son_object son = get_son(owner_account); + + son_heartbeat_operation op; + op.owner_account = son.son_account; + op.son_id = son.id; + op.ts = _remote_db->get_dynamic_global_properties().time; // or fc::time_point_sec(fc::time_point::now()) ??? + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + map list_active_sons() { try { global_property_object gpo = get_global_properties(); @@ -4403,6 +4438,16 @@ signed_transaction wallet_api::delete_son(string owner_account, return my->delete_son(owner_account, broadcast); } +signed_transaction wallet_api::start_son_maintenance(string owner_account, bool broadcast) +{ + return my->start_son_maintenance(owner_account, broadcast); +} + +signed_transaction wallet_api::stop_son_maintenance(string owner_account, bool broadcast) +{ + return my->stop_son_maintenance(owner_account, broadcast); +} + map wallet_api::list_sons(const string& lowerbound, uint32_t limit) { return my->_remote_db->lookup_son_accounts(lowerbound, limit); diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index b3b596c7..5f8ad78a 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -658,6 +658,67 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons end"); } +BOOST_AUTO_TEST_CASE( maintenance_test ) +{ + BOOST_TEST_MESSAGE("SON maintenance cli wallet tests begin"); + try + { + son_test_helper sth(*this); + + std::string name("sonaccount1"); + + global_property_object gpo; + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count; + + flat_map sidechain_public_keys; + + // create son accounts + for(unsigned int i = 0; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + } + BOOST_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number + 1; i++) + { + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + + son_object son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::active); + + // put SON in maintenance mode + con.wallet_api_ptr->start_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is in maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::in_maintenance); + + // restore SON activity + con.wallet_api_ptr->stop_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is active + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::active); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON maintenance cli wallet tests end"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index f6037084..e0e56e19 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -684,6 +684,19 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); trx.clear(); } + + { + // Try to go in maintenance for an inactive SON + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } generate_block(); const auto& idx = db.get_index_type().indices().get(); @@ -696,17 +709,32 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { auto son_stats_obj = sidx.find( obj->statistics ); BOOST_REQUIRE( son_stats_obj != sidx.end() ); - // Modify SON's status to in_maintenance + // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.status = son_status::active; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(1)); + _s.last_down_timestamp = fc::time_point_sec(db.head_block_time()); }); + { + generate_block(); + // Put SON in maintenance + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_CHECK( obj->status == son_status::in_maintenance); + } + uint64_t downtime = 0; { From e0e427a36669f0ec8e2279be28dd062794c07163 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Mon, 20 Jan 2020 15:08:34 +0100 Subject: [PATCH 25/64] Quick fix for list_active_sons --- libraries/wallet/include/graphene/wallet/wallet.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index aeb9107d..39a2d8c7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2220,6 +2220,7 @@ FC_API( graphene::wallet::wallet_api, (update_son) (delete_son) (list_sons) + (list_active_sons) (add_sidechain_address) (update_sidechain_address) (delete_sidechain_address) From 80870749decda9378b22260475529c4548ca8cad Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Wed, 22 Jan 2020 03:09:54 +1100 Subject: [PATCH 26/64] SON199 - Fix Unit Test Failure (#268) --- tests/tests/son_operations_tests.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index e0e56e19..329b1629 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -679,6 +679,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.ts = fc::time_point::now(); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); // Expect an exception GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); @@ -692,6 +693,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.son_id = son_id_type(0); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); // Expect an exception GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); @@ -728,6 +730,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.son_id = son_id_type(0); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); PUSH_TX( db, trx, ~0); generate_block(); @@ -746,6 +749,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); PUSH_TX( db, trx, ~0); generate_block(); @@ -779,6 +783,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); PUSH_TX( db, trx, ~0); generate_block(); @@ -798,6 +803,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); PUSH_TX( db, trx, ~0); generate_block(); @@ -864,6 +870,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, bob_private_key); // Expect an exception GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); @@ -880,6 +887,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { op.down_ts = son_stats_obj->last_active_timestamp; trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, alice_private_key); // Expect an exception GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); @@ -896,6 +904,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { op.down_ts = son_stats_obj->last_active_timestamp; trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, bob_private_key); PUSH_TX( db, trx, ~0); generate_block(); @@ -915,6 +924,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { op.down_ts = son_stats_obj->last_active_timestamp; trx.operations.push_back(op); + set_expiration(db, trx); sign(trx, bob_private_key); // Expect an exception GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); From 7139b4a4e36c9ebe10dcd8c137986a19d7402f5d Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Thu, 23 Jan 2020 13:15:55 +0100 Subject: [PATCH 27/64] Quickfix for update_sidechain_address and delete_sidechain_address cli commands --- libraries/wallet/wallet.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 658587f4..1e9f306c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2035,8 +2035,12 @@ public: bool broadcast /* = false */) { try { account_id_type sidechain_address_account_id = get_account_id(account); + fc::optional sao = _remote_db->get_sidechain_address_by_account_and_sidechain(sidechain_address_account_id, sidechain); + if (!sao) + FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain)); sidechain_address_update_operation op; + op.sidechain_address_id = sao->id; op.sidechain_address_account = sidechain_address_account_id; op.sidechain = sidechain; op.address = address; @@ -2056,8 +2060,12 @@ public: bool broadcast /* = false */) { try { account_id_type sidechain_address_account_id = get_account_id(account); + fc::optional sao = _remote_db->get_sidechain_address_by_account_and_sidechain(sidechain_address_account_id, sidechain); + if (!sao) + FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain)); sidechain_address_delete_operation op; + op.sidechain_address_id = sao->id; op.sidechain_address_account = sidechain_address_account_id; op.sidechain = sidechain; From 61c6d7f572cdaefce9a932079ea710a7d20c8730 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Tue, 28 Jan 2020 20:42:30 +1100 Subject: [PATCH 28/64] SON206_Plugin_CrashFix_Reorg - Plugin Changes (#272) * SON206_Plugin_CrashFix_Reorg - Plugin Changes * SON206_Plugin_CrashFix_Reorg - add owner auths to consensus account --- libraries/chain/db_maint.cpp | 14 +++ .../include/graphene/chain/son_object.hpp | 2 +- .../peerplays_sidechain_plugin.cpp | 101 ++++++++++-------- 3 files changed, 73 insertions(+), 44 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 31813724..5d973a09 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -566,9 +566,14 @@ void database::update_active_sons() // Ensure that everyone has at least one vote. Zero weights aren't allowed. const son_object& son = get(son_info.son_id); uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); + obj.owner.account_auths[son.son_account] += votes; + obj.owner.weight_threshold += votes; obj.active.account_auths[son.son_account] += votes; obj.active.weight_threshold += votes; } + obj.owner.weight_threshold *= 2; + obj.owner.weight_threshold /= 3; + obj.owner.weight_threshold += 1; obj.active.weight_threshold *= 2; obj.active.weight_threshold /= 3; obj.active.weight_threshold += 1; @@ -583,6 +588,10 @@ void database::update_active_sons() modify( get(gpo.parameters.get_son_btc_account_id()), [&]( account_object& obj ) { uint64_t total_votes = 0; + obj.owner.weight_threshold = 0; + obj.owner.account_auths.clear(); + obj.active.weight_threshold = 0; + obj.active.account_auths.clear(); for( const auto& son_info : gpo.active_sons ) { const son_object& son = get(son_info.son_id); @@ -596,9 +605,14 @@ void database::update_active_sons() // Ensure that everyone has at least one vote. Zero weights aren't allowed. const son_object& son = get(son_info.son_id); uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); + obj.owner.account_auths[son.son_account] += votes; + obj.owner.weight_threshold += votes; obj.active.account_auths[son.son_account] += votes; obj.active.weight_threshold += votes; } + obj.owner.weight_threshold *= 2; + obj.owner.weight_threshold /= 3; + obj.owner.weight_threshold += 1; obj.active.weight_threshold *= 2; obj.active.weight_threshold /= 3; obj.active.weight_threshold += 1; diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 50f7385a..5ce45242 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -97,7 +97,7 @@ namespace graphene { namespace chain { FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance)(deregistered) ) FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), - (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(status)(sidechain_public_keys) ) + (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(statistics)(status)(sidechain_public_keys) ) FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (graphene::db::object), diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 2874605b..2f57715a 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -27,9 +27,9 @@ class peerplays_sidechain_plugin_impl void plugin_startup(); void schedule_heartbeat_loop(); void heartbeat_loop(); - chain::proposal_create_operation create_son_down_proposal(chain::son_id_type son_id, fc::time_point_sec last_active_ts); void on_block_applied( const signed_block& b ); void on_objects_new(const vector& new_object_ids); + void create_son_down_proposals(); private: peerplays_sidechain_plugin& plugin; @@ -162,7 +162,7 @@ void peerplays_sidechain_plugin_impl::plugin_startup() if( !_sons.empty() && !_private_keys.empty() ) { ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); - schedule_heartbeat_loop(); + heartbeat_loop(); } else elog("No sons configured! Please add SON IDs and private keys to configuration."); @@ -184,6 +184,7 @@ void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() void peerplays_sidechain_plugin_impl::heartbeat_loop() { + schedule_heartbeat_loop(); chain::database& d = plugin.database(); chain::son_id_type son_id = *(_sons.begin()); const auto& idx = d.get_index_type().indices().get(); @@ -210,7 +211,8 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() fc::future fut = fc::async( [&](){ try { d.push_transaction(trx); - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch(fc::exception e){ ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); @@ -219,28 +221,59 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() }); fut.wait(fc::seconds(10)); } - schedule_heartbeat_loop(); } -chain::proposal_create_operation peerplays_sidechain_plugin_impl::create_son_down_proposal(chain::son_id_type son_id, fc::time_point_sec last_active_ts) +void peerplays_sidechain_plugin_impl::create_son_down_proposals() { + auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { + chain::database& d = plugin.database(); + chain::son_id_type my_son_id = *(_sons.begin()); + const chain::global_property_object& gpo = d.get_global_properties(); + const auto& idx = d.get_index_type().indices().get(); + auto son_obj = idx.find( my_son_id ); + + chain::son_report_down_operation son_down_op; + son_down_op.payer = gpo.parameters.get_son_btc_account_id(); + son_down_op.son_id = son_id; + son_down_op.down_ts = last_active_ts; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = son_obj->son_account; + proposal_op.proposed_ops.push_back( op_wrapper( son_down_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( d.head_block_time().sec_since_epoch() + lifetime ); + return proposal_op; + }; + chain::database& d = plugin.database(); - chain::son_id_type my_son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); const auto& idx = d.get_index_type().indices().get(); - auto son_obj = idx.find( my_son_id ); - - chain::son_report_down_operation son_down_op; - son_down_op.payer = gpo.parameters.get_son_btc_account_id(); - son_down_op.son_id = son_id; - son_down_op.down_ts = last_active_ts; - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = son_obj->son_account; - proposal_op.proposed_ops.push_back( op_wrapper( son_down_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( d.head_block_time().sec_since_epoch() + lifetime ); - return proposal_op; + chain::son_id_type my_son_id = *(_sons.begin()); + for(auto son_inf: gpo.active_sons) { + if(my_son_id == son_inf.son_id) + continue; + auto son_obj = idx.find( son_inf.son_id ); + auto stats = son_obj->statistics(d); + fc::time_point_sec last_active_ts = stats.last_active_timestamp; + int64_t down_threshold = 2*180000000; + if(son_obj->status == chain::son_status::active && (fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { + ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); + chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); + chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } } void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) @@ -248,35 +281,15 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) chain::database& d = plugin.database(); chain::son_id_type my_son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); + bool latest_block = ((fc::time_point::now() - b.timestamp) < fc::microseconds(gpo.parameters.block_interval * 1000000)); // Return if there are no active SONs - if(gpo.active_sons.size() <= 0) { + if(gpo.active_sons.size() <= 0 || !latest_block) { return; } chain::son_id_type next_son_id = d.get_scheduled_son(1); if(next_son_id == my_son_id) { - const auto& idx = d.get_index_type().indices().get(); - for(auto son_inf: gpo.active_sons) { - auto son_obj = idx.find( son_inf.son_id ); - auto stats = son_obj->statistics(d); - fc::time_point_sec last_active_ts = stats.last_active_timestamp; - int64_t down_threshold = 2*180000000; - if((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { - chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); - chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); - fc::future fut = fc::async( [&](){ - try { - d.push_transaction(trx); - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}",("e", e.what())); - return false; - } - }); - fut.wait(fc::seconds(10)); - } - } + create_son_down_proposals(); } } @@ -302,6 +315,7 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vectorson_account; puo.proposal = id; @@ -310,7 +324,8 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector fut = fc::async( [&](){ try { d.push_transaction(trx); - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch(fc::exception e){ ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}",("e", e.what())); From 21c83377532cad9926bc59ddb9cf78e4deb37805 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Wed, 29 Jan 2020 23:20:36 +1100 Subject: [PATCH 29/64] SON165 - Keys mapping missing from wallet data (#274) --- libraries/wallet/wallet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 1e9f306c..39b71b37 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -858,6 +858,7 @@ public: // account, false otherwise (but it is stored either way) bool import_key(string account_name_or_id, string wif_key) { + fc::scoped_lock lock(_resync_mutex); fc::optional optional_private_key = wif_to_key(wif_key); if (!optional_private_key) FC_THROW("Invalid private key"); @@ -1359,6 +1360,7 @@ public: bool broadcast = false, bool save_wallet = true) { try { + fc::scoped_lock lock(_resync_mutex); int active_key_index = find_first_unused_derived_key_index(owner_privkey); fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index); @@ -1866,6 +1868,7 @@ public: flat_map sidechain_public_keys, bool broadcast /* = false */) { try { + fc::scoped_lock lock(_resync_mutex); account_object son_account = get_account(owner_account); fc::ecc::private_key active_private_key = get_private_key_for_account(son_account); int son_key_index = find_first_unused_derived_key_index(active_private_key); @@ -2082,6 +2085,7 @@ public: string url, bool broadcast /* = false */) { try { + fc::scoped_lock lock(_resync_mutex); account_object witness_account = get_account(owner_account); fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account); int witness_key_index = find_first_unused_derived_key_index(active_private_key); From b952522b016f4ce42aca878a5fd39ac5002c5240 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 31 Jan 2020 22:58:07 +1100 Subject: [PATCH 30/64] SON232 - Avoid duplicate proposals from sidechain plugin (#275) --- libraries/chain/db_getter.cpp | 18 +++++++++++++++++- libraries/chain/db_update.cpp | 1 + .../chain/include/graphene/chain/database.hpp | 1 + .../graphene/chain/proposal_evaluator.hpp | 1 + .../graphene/chain/son_proposal_object.hpp | 5 +++-- libraries/chain/proposal_evaluator.cpp | 9 +++++++++ .../peerplays_sidechain_plugin.cpp | 6 ++++-- 7 files changed, 36 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index a23ff6de..9749d642 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -179,6 +179,21 @@ std::set database::get_sons_to_be_deregistered() return ret; } +std::set database::get_sons_being_reported_down() +{ + std::set ret; + const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); + + for( auto& son_proposal : son_proposal_idx ) + { + if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal) + { + ret.insert(son_proposal.son_id); + } + } + return ret; +} + fc::optional database::create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness ) { son_delete_operation son_dereg_op; @@ -235,7 +250,8 @@ void database::process_son_proposals( const witness_object& current_witness, con void database::remove_son_proposal( const proposal_object& proposal ) { try { if( proposal.proposed_transaction.operations.size() == 1 && - ( proposal.proposed_transaction.operations.back().which() == operation::tag::value) ) + ( proposal.proposed_transaction.operations.back().which() == operation::tag::value || + proposal.proposed_transaction.operations.back().which() == operation::tag::value) ) { const auto& son_proposal_idx = get_index_type().indices().get(); auto son_proposal_itr = son_proposal_idx.find( proposal.id ); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 7df02a39..b33e3819 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -224,6 +224,7 @@ void database::clear_expired_proposals() elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}", ("proposal", proposal)("error", e.to_detail_string())); } + remove_son_proposal(proposal); remove(proposal); } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 78d05ef9..56a62577 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -300,6 +300,7 @@ namespace graphene { namespace chain { std::vector get_seeds( asset_id_type for_asset, uint8_t count_winners )const; uint64_t get_random_bits( uint64_t bound ); std::set get_sons_being_deregistered(); + std::set get_sons_being_reported_down(); std::set get_sons_to_be_deregistered(); fc::optional create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index a7b76471..7eb32489 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -43,6 +43,7 @@ namespace graphene { namespace chain { void operator()( const T &v ) const {} void operator()( const son_delete_operation &v ); + void operator()( const son_report_down_operation &v ); }; class proposal_create_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/son_proposal_object.hpp b/libraries/chain/include/graphene/chain/son_proposal_object.hpp index a8eb5384..7b1674b8 100644 --- a/libraries/chain/include/graphene/chain/son_proposal_object.hpp +++ b/libraries/chain/include/graphene/chain/son_proposal_object.hpp @@ -8,7 +8,8 @@ namespace graphene { namespace chain { enum class son_proposal_type { - son_deregister_proposal + son_deregister_proposal, + son_report_down_proposal }; class son_proposal_object : public abstract_object @@ -36,6 +37,6 @@ using son_proposal_index = generic_index([&]( son_proposal_object& son_prop ) { + son_prop.proposal_type = son_proposal_type::son_report_down_proposal; + son_prop.proposal_id = prop_id; + son_prop.son_id = v.son_id; + }); +} + void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) { try { const database& d = db(); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 2f57715a..59ea5786 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -248,10 +248,12 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() chain::database& d = plugin.database(); const chain::global_property_object& gpo = d.get_global_properties(); const auto& idx = d.get_index_type().indices().get(); + std::set sons_being_reported_down = d.get_sons_being_reported_down(); chain::son_id_type my_son_id = *(_sons.begin()); for(auto son_inf: gpo.active_sons) { - if(my_son_id == son_inf.son_id) + if(my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())){ continue; + } auto son_obj = idx.find( son_inf.son_id ); auto stats = son_obj->statistics(d); fc::time_point_sec last_active_ts = stats.last_active_timestamp; @@ -339,7 +341,7 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector() ) { const object* obj = d.find_object(object_id); const chain::proposal_object* proposal = dynamic_cast(obj); - if(proposal == nullptr) { + if(proposal == nullptr || (proposal->available_active_approvals.find(son_obj->son_account) != proposal->available_active_approvals.end())) { return; } From 6e61d6b055eb276757e426245a3a7c23a61b3854 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Tue, 4 Feb 2020 00:14:39 +1100 Subject: [PATCH 31/64] SON233 - Provide correct downtime metrics to user (#278) --- libraries/chain/db_maint.cpp | 16 ++++++++++++++++ .../chain/include/graphene/chain/database.hpp | 1 + libraries/chain/son_evaluator.cpp | 1 + .../peerplays_sidechain_plugin.cpp | 4 +++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 5d973a09..929346b9 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -165,6 +165,20 @@ void database::pay_sons() } } +void database::update_son_metrics() +{ + const auto& son_idx = get_index_type().indices().get< by_id >(); + for( auto& son : son_idx ) + { + auto& stats = son.statistics(*this); + modify( stats, [&]( son_statistics_object& _stats) + { + _stats.total_downtime += _stats.current_interval_downtime; + _stats.current_interval_downtime = 0; + }); + } +} + void database::pay_workers( share_type& budget ) { // ilog("Processing payroll! Available budget is ${b}", ("b", budget)); @@ -542,6 +556,8 @@ void database::update_active_sons() _sso.scheduler.update(active_sons); }); + update_son_metrics(); + if(gpo.active_sons.size() > 0 ) { if(gpo.parameters.get_son_btc_account_id() == GRAPHENE_NULL_ACCOUNT) { const auto& son_btc_account = create( [&]( account_object& obj ) { diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 56a62577..ea2ccffd 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -548,6 +548,7 @@ namespace graphene { namespace chain { void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); + void update_son_metrics(); void update_active_sons(); void update_worker_votes(); diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 34f4daa3..79660921 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -165,6 +165,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op) { try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account"); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 59ea5786..86caec2d 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -247,6 +247,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() chain::database& d = plugin.database(); const chain::global_property_object& gpo = d.get_global_properties(); + const chain::dynamic_global_property_object& dgpo = d.get_dynamic_global_properties(); const auto& idx = d.get_index_type().indices().get(); std::set sons_being_reported_down = d.get_sons_being_reported_down(); chain::son_id_type my_son_id = *(_sons.begin()); @@ -256,7 +257,8 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() } auto son_obj = idx.find( son_inf.son_id ); auto stats = son_obj->statistics(d); - fc::time_point_sec last_active_ts = stats.last_active_timestamp; + fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); int64_t down_threshold = 2*180000000; if(son_obj->status == chain::son_status::active && (fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); From a688bb93ed4e16232a907aa8c76e240c83c771bf Mon Sep 17 00:00:00 2001 From: obucinac Date: Tue, 4 Feb 2020 19:31:45 +0100 Subject: [PATCH 32/64] son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow --- CMakeLists.txt | 2 +- libraries/app/database_api.cpp | 56 +++ libraries/app/impacted.cpp | 12 +- .../app/include/graphene/app/database_api.hpp | 30 ++ libraries/chain/CMakeLists.txt | 2 + libraries/chain/db_init.cpp | 8 +- libraries/chain/db_maint.cpp | 36 +- libraries/chain/db_notify.cpp | 12 +- libraries/chain/hardfork.d/SON.hf | 5 +- .../graphene/chain/protocol/operations.hpp | 9 +- .../graphene/chain/protocol/son_wallet.hpp | 40 ++ .../include/graphene/chain/protocol/types.hpp | 2 + .../graphene/chain/son_wallet_evaluator.hpp | 25 ++ .../graphene/chain/son_wallet_object.hpp | 17 +- libraries/chain/son_wallet_evaluator.cpp | 81 ++++ .../plugins/peerplays_sidechain/addresses.txt | 391 ------------------ .../peerplays_sidechain_plugin.hpp | 9 +- .../sidechain_net_handler.hpp | 21 +- .../sidechain_net_handler_bitcoin.hpp | 5 +- .../sidechain_net_manager.hpp | 6 +- .../peerplays_sidechain_plugin.cpp | 123 +++++- .../sidechain_net_handler.cpp | 11 +- .../sidechain_net_handler_bitcoin.cpp | 97 ++++- .../sidechain_net_manager.cpp | 20 +- .../wallet/include/graphene/wallet/wallet.hpp | 23 ++ libraries/wallet/wallet.cpp | 30 ++ programs/js_operation_serializer/main.cpp | 1 + tests/common/database_fixture.cpp | 46 ++- tests/common/database_fixture.hpp | 18 +- tests/tests/son_wallet_tests.cpp | 225 ++++++++++ 30 files changed, 895 insertions(+), 468 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp create mode 100644 libraries/chain/son_wallet_evaluator.cpp delete mode 100644 libraries/plugins/peerplays_sidechain/addresses.txt create mode 100644 tests/tests/son_wallet_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6853e2c8..b26bbc57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux endif( APPLE ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof -Wno-terminate -Wno-sign-compare" ) elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" ) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c4566d24..c6c8a952 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -152,6 +152,11 @@ class database_api_impl : public std::enable_shared_from_this map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_son_count()const; + // SON wallets + optional get_active_son_wallet(); + optional get_son_wallet_by_time_point(time_point_sec time_point); + vector> get_son_wallets(uint32_t limit); + // Sidechain addresses vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; vector> get_sidechain_addresses_by_account(account_id_type account)const; @@ -1770,6 +1775,57 @@ uint64_t database_api_impl::get_son_count()const return _db.get_index_type().indices().size(); } +////////////////////////////////////////////////////////////////////// +// // +// SON Wallets // +// // +////////////////////////////////////////////////////////////////////// + +optional database_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional database_api_impl::get_active_son_wallet() +{ + const auto& idx = _db.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj != idx.rend()) { + return *obj; + } + return {}; +} + +optional database_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +optional database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point) +{ + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) { + if ((time_point >= swo.valid_from) && (time_point < swo.expires)) + return swo; + } + return {}; +} + +vector> database_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + +vector> database_api_impl::get_son_wallets(uint32_t limit) +{ + FC_ASSERT( limit <= 1000 ); + vector> result; + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) + result.push_back(swo); + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Sidechain Accounts // diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 0aefe922..6834fa51 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -310,9 +310,18 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_report_down_operation& op ){ + _impacted.insert( op.payer ); + } void operator()( const son_maintenance_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_wallet_recreate_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ){ + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ){ _impacted.insert( op.sidechain_address_account ); } @@ -322,9 +331,6 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } - void operator()( const son_report_down_operation& op ){ - _impacted.insert( op.payer ); - } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index e3252ec6..76ef822c 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -603,6 +604,30 @@ class database_api */ uint64_t get_son_count()const; + ///////////////////////// + // SON Wallets // + ///////////////////////// + + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + ///////////////////////// // Sidechain Addresses // ///////////////////////// @@ -855,6 +880,11 @@ FC_API(graphene::app::database_api, (lookup_son_accounts) (get_son_count) + // SON wallets + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) + // Sidechain addresses (get_sidechain_addresses) (get_sidechain_addresses_by_account) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index bba9e7f1..b392bc5e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -117,6 +117,8 @@ add_library( graphene_chain son_evaluator.cpp son_object.cpp + son_wallet_evaluator.cpp + sidechain_address_evaluator.cpp ${HEADERS} diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 5b5f8029..c41aad87 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,7 @@ #include #include #include +#include #include #include @@ -250,11 +252,13 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); } void database::initialize_indexes() @@ -299,6 +303,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 929346b9..935e520e 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -484,7 +484,41 @@ void database::update_active_sons() ilog( "Active SONs set NOT CHANGED" ); } else { ilog( "Active SONs set CHANGED" ); - // Store new SON info, initiate wallet recreation and transfer of funds + + bool should_recreate_pw = true; + + // Expire for current son_wallet_object wallet, if exists + const auto& idx_swi = get_index_type().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*obj).sons; + + bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); + if (wallet_son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + } + } + + should_recreate_pw = !wallet_son_sets_equal; + + if (should_recreate_pw) { + modify(*obj, [&, obj](son_wallet_object &swo) { + swo.expires = head_block_time(); + }); + } + } + + if (should_recreate_pw) { + // Create new son_wallet_object, to initiate wallet recreation + create( [&]( son_wallet_object& obj ) { + obj.valid_from = head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); + }); + } + vector sons_to_remove; // find all cur_active_sons members that is not in new_active_sons for_each(cur_active_sons.begin(), cur_active_sons.end(), diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index c7946906..fbd3888d 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -297,9 +297,18 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_report_down_operation& op ) { + _impacted.insert( op.payer ); + } void operator()( const son_maintenance_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_wallet_recreate_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ) { + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ) { _impacted.insert( op.sidechain_address_account ); } @@ -309,9 +318,6 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } - void operator()( const son_report_down_operation& op ) { - _impacted.insert( op.payer ); - } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf index 355c5b96..5c4e1e76 100644 --- a/libraries/chain/hardfork.d/SON.hf +++ b/libraries/chain/hardfork.d/SON.hf @@ -1,5 +1,6 @@ -// SON HARDFORK Monday, September 21, 2020 1:43:11 PM +// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800 +// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791 #ifndef HARDFORK_SON_TIME #include -#define HARDFORK_SON_TIME (fc::time_point_sec( time(NULL) - (60 * 60) )) +#define HARDFORK_SON_TIME (fc::time_point_sec( 1577836800 )) #endif diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 07695705..74fd532c 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -47,6 +47,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -142,11 +143,13 @@ namespace graphene { namespace chain { son_update_operation, son_delete_operation, son_heartbeat_operation, + son_report_down_operation, + son_maintenance_operation, + son_wallet_recreate_operation, + son_wallet_update_operation, sidechain_address_add_operation, sidechain_address_update_operation, - sidechain_address_delete_operation, - son_report_down_operation, - son_maintenance_operation + sidechain_address_delete_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp new file mode 100644 index 00000000..f41cfa1f --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + + struct son_wallet_recreate_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + vector sons; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_id_type son_wallet_id; + graphene::peerplays_sidechain::sidechain_type sidechain; + string address; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 1d056740..abfce9c8 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -430,6 +430,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (bet_object_type) (son_object_type) (son_proposal_object_type) + (son_wallet_object_type) (sidechain_address_object_type) (OBJECT_TYPE_COUNT) ) @@ -504,6 +505,7 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp new file mode 100644 index 00000000..78e8655f --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class recreate_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_recreate_operation operation_type; + + void_result do_evaluate(const son_wallet_recreate_operation& o); + object_id_type do_apply(const son_wallet_recreate_operation& o); +}; + +class update_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_update_operation operation_type; + + void_result do_evaluate(const son_wallet_update_operation& o); + object_id_type do_apply(const son_wallet_update_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index c3ea204d..aec28342 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include namespace graphene { namespace chain { @@ -16,16 +17,26 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = son_wallet_object_type; + time_point_sec valid_from; + time_point_sec expires; + flat_map addresses; + vector sons; }; - struct by_sidechain_type; - struct by_address; + struct by_valid_from; + struct by_expires; using son_wallet_multi_index_type = multi_index_container< son_wallet_object, indexed_by< ordered_unique< tag, member + >, + ordered_unique< tag, + member + >, + ordered_unique< tag, + member > > >; @@ -33,4 +44,4 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object), - (addresses) ) + (valid_from) (expires) (addresses) (sons) ) diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp new file mode 100644 index 00000000..55d4645d --- /dev/null +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include + +namespace graphene { namespace chain { + +void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*itr).sons; + auto new_wallet_sons = op.sons; + + bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); + if (son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i); + } + } + + FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed."); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + db().modify(*itr, [&, op](son_wallet_object &swo) { + swo.expires = db().head_block_time(); + }); + } + + const auto& new_son_wallet_object = db().create( [&]( son_wallet_object& obj ){ + obj.valid_from = db().head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons = op.sons; + }); + return new_son_wallet_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); + auto itr = idx.find(op.son_wallet_id); + FC_ASSERT( itr->addresses.find(peerplays_sidechain::sidechain_type::bitcoin) == itr->addresses.end() || + itr->addresses.at(peerplays_sidechain::sidechain_type::bitcoin).empty(), "Sidechain wallet address already set"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_wallet_object &swo) { + swo.addresses[op.sidechain] = op.address; + }); + } + return op.son_wallet_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/plugins/peerplays_sidechain/addresses.txt b/libraries/plugins/peerplays_sidechain/addresses.txt deleted file mode 100644 index df57167d..00000000 --- a/libraries/plugins/peerplays_sidechain/addresses.txt +++ /dev/null @@ -1,391 +0,0 @@ -2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J -{ - "address": "2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J", - "scriptPubKey": "a914873aad1ecf7510c80b83d8ca94d21432dc71b88787", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/2']0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd))#7s0qfnvz", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00141d30ac0c47f7b32460265daf49c5925236f5882d", - "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "1d30ac0c47f7b32460265daf49c5925236f5882d", - "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", - "address": "bcrt1qr5c2crz877ejgcpxtkh5n3vj2gm0tzpdqrw3n0", - "scriptPubKey": "00141d30ac0c47f7b32460265daf49c5925236f5882d" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/2'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2MxAnE469fhhdvUqUB7daU997VSearb2mn7 -{ - "address": "2MxAnE469fhhdvUqUB7daU997VSearb2mn7", - "scriptPubKey": "a914360175a50918495a20573aed68d506a790420fe587", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/3']02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d))#p3st4e4e", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00146c1c0571f3132eb0702f487123d2026495592830", - "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "6c1c0571f3132eb0702f487123d2026495592830", - "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", - "address": "bcrt1qdswq2u0nzvhtqup0fpcj85szvj24j2psgfwy6w", - "scriptPubKey": "00146c1c0571f3132eb0702f487123d2026495592830" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/3'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL -{ - "address": "2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL", - "scriptPubKey": "a914bdce56e7f2fc04614c0f6f4d1d59fff63b0b73f187", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/4']020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462))#3r63c8fu", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00146abd0d5f055df80b41bc6449328eda09f61a2be3", - "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "6abd0d5f055df80b41bc6449328eda09f61a2be3", - "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", - "address": "bcrt1qd27s6hc9thuqksduv3yn9rk6p8mp52lr2e846e", - "scriptPubKey": "00146abd0d5f055df80b41bc6449328eda09f61a2be3" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/4'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR -{ - "address": "2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR", - "scriptPubKey": "a914b7abe6d957106da3a21782eea1164f4964b521ba87", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/5']03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce))#e3sfze3l", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8", - "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "7a4fd72ff8e192004c70d8139b4ca53e1467d8c8", - "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", - "address": "bcrt1q0f8awtlcuxfqqnrsmqfekn998c2x0kxgf4t7dm", - "scriptPubKey": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/5'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL -{ - "address": "2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL", - "scriptPubKey": "a914dcb019e8330b4fffc50ba22bbf90215922a1379787", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/6']028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013))#3326m2za", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652", - "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "ccd3dee026a1d641352f5b8c7d72805d75fd0652", - "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", - "address": "bcrt1qenfaacpx58tyzdf0twx86u5qt46l6pjjef5y7u", - "scriptPubKey": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/6'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG -{ - "address": "2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG", - "scriptPubKey": "a9144cb2b8f97d8e7ad5bfb81afd611394387f374ab887", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/7']02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b))#7gvhakzu", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", - "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", - "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", - "address": "bcrt1quds85rm5tchm3vz0u8a87puvx5qf5a7pqw8u7l", - "scriptPubKey": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/7'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg -{ - "address": "2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg", - "scriptPubKey": "a914dae51c6601ef4e05817f67d57d3ac0c8cb64948e87", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/8']03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b))#ckuy6v83", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7", - "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "60c1c7776b1f92cd36fe6138b99212ebe6381fe7", - "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", - "address": "bcrt1qvrquwamtr7fv6dh7vyutnysja0nrs8l8vzcnwj", - "scriptPubKey": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/8'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX -{ - "address": "2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX", - "scriptPubKey": "a91413930c6d40f5f01b169f2fc7884c2b3984ff8a3087", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/9']022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac))#f8cdpnn9", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4", - "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "35b75b7c34a7f908955b8b93aaa677aa47fab2c4", - "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", - "address": "bcrt1qxkm4klp55lus392m3wf64fnh4frl4vkyng4ffg", - "scriptPubKey": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/9'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM -{ - "address": "2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM", - "scriptPubKey": "a9145e91543069ae37d8bace2f59aade945f5916dc3287", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/10']03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af))#x7xpvc0y", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", - "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", - "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", - "address": "bcrt1qe6e4hqvckkx8dlx83fcdr5aza5w5xf0jd22xet", - "scriptPubKey": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/10'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy -{ - "address": "2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy", - "scriptPubKey": "a914e132c578bd294a01a472d42b376b29e5ef6678c987", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/11']02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b))#2janhauz", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762", - "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "831d63f8d99be71f9b385f2ccc92ab2783da3762", - "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", - "address": "bcrt1qsvwk87xen0n3lxectukvey4ty7pa5dmz0yanuv", - "scriptPubKey": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/11'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index e986fe8e..9612cf55 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -2,11 +2,7 @@ #include -#include #include -#include - -#include namespace graphene { namespace peerplays_sidechain { using namespace chain; @@ -30,6 +26,11 @@ class peerplays_sidechain_plugin : public graphene::app::plugin virtual void plugin_startup() override; std::unique_ptr my; + + son_id_type get_son_id(); + son_object get_son_object(); + bool is_active_son(); + std::map& get_private_keys(); }; } } //graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index e841a639..da6321a9 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -1,27 +1,32 @@ #pragma once -#include -#include - #include #include +#include +#include +#include + namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { public: - sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options); + sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler(); + graphene::peerplays_sidechain::sidechain_type get_sidechain(); std::vector get_sidechain_addresses(); -protected: - std::shared_ptr database; - graphene::peerplays_sidechain::sidechain_type sidechain; - void sidechain_event_data_received(const sidechain_event_data& sed); + virtual void recreate_primary_wallet() = 0; + +protected: + peerplays_sidechain_plugin& plugin; + graphene::chain::database& database; + graphene::peerplays_sidechain::sidechain_type sidechain; + virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; virtual std::string sign_transaction( const std::string& transaction ) = 0; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 4c37c530..803b24de 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -19,6 +19,7 @@ public: bool receive_mempool_entry_tx( const std::string& tx_hash ); uint64_t receive_estimated_fee(); void send_btc_tx( const std::string& tx_hex ); + std::string add_multisig_address( const std::vector public_keys ); bool connection_is_not_defined() const; private: @@ -56,9 +57,11 @@ private: class sidechain_net_handler_bitcoin : public sidechain_net_handler { public: - sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options); + sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler_bitcoin(); + void recreate_primary_wallet(); + bool connection_is_not_defined() const; std::string create_multisignature_wallet( const std::vector public_keys ); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index c60aa73b..bd6f1ab3 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -12,12 +12,14 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_manager { public: - sidechain_net_manager(std::shared_ptr db); + sidechain_net_manager(peerplays_sidechain_plugin& _plugin); virtual ~sidechain_net_manager(); bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options); + void recreate_primary_wallet(); private: - std::shared_ptr database; + peerplays_sidechain_plugin& plugin; + graphene::chain::database& database; std::vector> net_handlers; }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 86caec2d..6f7a0d77 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,9 +1,14 @@ #include +#include +#include + #include #include -#include + #include +#include +#include #include #include @@ -25,11 +30,21 @@ class peerplays_sidechain_plugin_impl boost::program_options::options_description& cfg); void plugin_initialize(const boost::program_options::variables_map& options); void plugin_startup(); + + son_id_type get_son_id(); + son_object get_son_object(); + bool is_active_son(); + std::map& get_private_keys(); + void schedule_heartbeat_loop(); void heartbeat_loop(); + void create_son_down_proposals(); + void recreate_primary_wallet(); + void process_deposits(); + //void process_withdrawals(); void on_block_applied( const signed_block& b ); void on_objects_new(const vector& new_object_ids); - void create_son_down_proposals(); + private: peerplays_sidechain_plugin& plugin; @@ -40,6 +55,7 @@ class peerplays_sidechain_plugin_impl std::map _private_keys; std::set _sons; fc::future _heartbeat_task; + }; peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : @@ -121,7 +137,10 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt throw; } - net_manager = std::unique_ptr(new sidechain_net_manager(plugin.app().chain_database())); + plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } ); + plugin.database().new_objects.connect( [&] (const vector& ids, const flat_set& impacted_accounts) { on_objects_new(ids); } ); + + net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); config_ready_bitcoin = options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) && @@ -153,24 +172,62 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { if (config_ready_son) { ilog("SON running"); + + ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); + schedule_heartbeat_loop(); + } else { + elog("No sons configured! Please add SON IDs and private keys to configuration."); } if (config_ready_bitcoin) { ilog("Bitcoin sidechain handler running"); } - if( !_sons.empty() && !_private_keys.empty() ) - { - ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); - heartbeat_loop(); - } else - elog("No sons configured! Please add SON IDs and private keys to configuration."); - //if (config_ready_ethereum) { // ilog("Ethereum sidechain handler running"); //} } +son_id_type peerplays_sidechain_plugin_impl::get_son_id() +{ + return *(_sons.begin()); +} + +son_object peerplays_sidechain_plugin_impl::get_son_object() +{ + const auto& idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find( get_son_id() ); + if (son_obj == idx.end()) + return {}; + return *son_obj; +} + +bool peerplays_sidechain_plugin_impl::is_active_son() +{ + const auto& idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find( get_son_id() ); + if (son_obj == idx.end()) + return false; + + const chain::global_property_object& gpo = plugin.database().get_global_properties(); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), get_son_id()); + + return (it != active_son_ids.end()); +} + +std::map& peerplays_sidechain_plugin_impl::get_private_keys() +{ + return _private_keys; +} + void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { fc::time_point now = fc::time_point::now(); @@ -280,6 +337,17 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() } } +void peerplays_sidechain_plugin_impl::recreate_primary_wallet() +{ + net_manager->recreate_primary_wallet(); +} + +void peerplays_sidechain_plugin_impl::process_deposits() { +} + +//void peerplays_sidechain_plugin_impl::process_withdrawals() { +//} + void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) { chain::database& d = plugin.database(); @@ -293,7 +361,15 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) chain::son_id_type next_son_id = d.get_scheduled_son(1); if(next_son_id == my_son_id) { + create_son_down_proposals(); + + recreate_primary_wallet(); + + process_deposits(); + + //process_withdrawals(); + } } @@ -351,6 +427,11 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vectorproposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( proposal->id ); } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( proposal->id ); + } } } } @@ -384,8 +465,6 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options: { ilog("peerplays sidechain plugin: plugin_initialize()"); my->plugin_initialize(options); - database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } ); - database().new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { my->on_objects_new(ids); }); } void peerplays_sidechain_plugin::plugin_startup() @@ -394,5 +473,25 @@ void peerplays_sidechain_plugin::plugin_startup() my->plugin_startup(); } +son_id_type peerplays_sidechain_plugin::get_son_id() +{ + return my->get_son_id(); +} + +son_object peerplays_sidechain_plugin::get_son_object() +{ + return my->get_son_object(); +} + +bool peerplays_sidechain_plugin::is_active_son() +{ + return my->is_active_son(); +} + +std::map& peerplays_sidechain_plugin::get_private_keys() +{ + return my->get_private_keys(); +} + } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 85196685..bb036d4a 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -6,21 +6,26 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options) : - database(db) +sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : + plugin(_plugin), + database(_plugin.database()) { } sidechain_net_handler::~sidechain_net_handler() { } +graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidechain() { + return sidechain; +} + std::vector sidechain_net_handler::get_sidechain_addresses() { std::vector result; switch (sidechain) { case sidechain_type::bitcoin: { - const auto& sidechain_addresses_idx = database->get_index_type(); + const auto& sidechain_addresses_idx = database.get_index_type(); const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index a2bd8d94..e6bda28e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -12,6 +12,9 @@ #include #include +#include +#include +#include namespace graphene { namespace peerplays_sidechain { @@ -129,6 +132,41 @@ void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) } } +std::string bitcoin_rpc_client::add_multisig_address( const std::vector public_keys ) +{ + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", \"method\": \"addmultisigaddress\", \"params\": ["); + std::string params = "2, ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); + } + params = params + pubkeys + std::string("]"); + body = body + params + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + return ""; + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + return reply_str; + } + + if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "BTC multisig address creation failed! Reply: ${msg}", ("msg", reply_str) ); + } + return ""; +} + bool bitcoin_rpc_client::connection_is_not_defined() const { return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); @@ -186,8 +224,8 @@ void zmq_listener::handle_zmq() { // ============================================================================= -sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options) : - sidechain_net_handler(db, options) { +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : + sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::bitcoin; ip = options.at("bitcoin-node-ip").as(); @@ -215,6 +253,57 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + + if ((obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) || + (obj->addresses.at(sidechain_type::bitcoin).empty())) { + + const chain::global_property_object& gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + vector son_pubkeys_bitcoin; + for ( const son_info& si : active_sons ) { + son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + } + string reply_str = create_multisignature_wallet(son_pubkeys_bitcoin); + + ilog(reply_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree pt; + boost::property_tree::read_json( ss, pt ); + if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) { + ilog(__FUNCTION__); + + std::stringstream res; + boost::property_tree::json_parser::write_json(res, pt.get_child("result")); + + son_wallet_update_operation op; + op.payer = gpo.parameters.get_son_btc_account_id(); + op.son_wallet_id = (*obj).id; + op.sidechain = sidechain_type::bitcoin; + op.address = res.str(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object().son_account; + proposal_op.proposed_ops.push_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); + try { + database.push_transaction(trx); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + } + } + } + } +} + bool sidechain_net_handler_bitcoin::connection_is_not_defined() const { return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); @@ -222,7 +311,7 @@ bool sidechain_net_handler_bitcoin::connection_is_not_defined() const std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) { - return ""; + return bitcoin_client->add_multisig_address(public_keys); } std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) @@ -248,7 +337,7 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data if( block != "" ) { const auto& vins = extract_info_from_block( block ); - const auto& sidechain_addresses_idx = database->get_index_type().indices().get(); + const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); for( const auto& v : vins ) { const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain_type::bitcoin, v.address)); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index 7c39fd81..e21af593 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -1,12 +1,14 @@ #include #include +#include #include namespace graphene { namespace peerplays_sidechain { -sidechain_net_manager::sidechain_net_manager(std::shared_ptr db) : - database(db) +sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin) : + plugin(_plugin), + database(_plugin.database()) { ilog(__FUNCTION__); } @@ -22,10 +24,10 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s switch (sidechain) { case sidechain_type::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(database, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; } default: assert(false); @@ -34,5 +36,11 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s return ret_val; } +void sidechain_net_manager::recreate_primary_wallet() { + for ( size_t i = 0; i < net_handlers.size(); i++ ) { + net_handlers.at(i)->recreate_primary_wallet(); + } +} + } } // graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 39a2d8c7..3d470703 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1390,6 +1390,26 @@ class wallet_api */ map list_active_sons(); + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + /** Adds sidechain address owned by the given account for a given sidechain. * * An account can have at most one sidechain address for one sidechain. @@ -2221,6 +2241,9 @@ FC_API( graphene::wallet::wallet_api, (delete_son) (list_sons) (list_active_sons) + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) (add_sidechain_address) (update_sidechain_address) (delete_sidechain_address) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 39b71b37..8e438ec4 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2006,6 +2006,21 @@ public: return result; } FC_CAPTURE_AND_RETHROW() } + optional get_active_son_wallet() + { try { + return _remote_db->get_active_son_wallet(); + } FC_CAPTURE_AND_RETHROW() } + + optional get_son_wallet_by_time_point(time_point_sec time_point) + { try { + return _remote_db->get_son_wallet_by_time_point(time_point); + } FC_CAPTURE_AND_RETHROW() } + + vector> get_son_wallets(uint32_t limit) + { try { + return _remote_db->get_son_wallets(limit); + } FC_CAPTURE_AND_RETHROW() } + signed_transaction add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, string address, @@ -4470,6 +4485,21 @@ map wallet_api::list_active_sons() return my->list_active_sons(); } +optional wallet_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional wallet_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +vector> wallet_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + signed_transaction wallet_api::add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, string address, diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 2901f2e0..f9056a55 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index c9917c95..4e171b14 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -265,11 +266,11 @@ void database_fixture::verify_asset_supplies( const database& db ) total_balances[betting_market_group.asset_id] += o.fees_collected; } - + uint64_t sweeps_vestings = 0; for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) sweeps_vestings += svbo.balance; - + total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget; @@ -413,6 +414,23 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i generate_block(skip); } +bool database_fixture::generate_maintenance_block() { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~database::skip_fork_db; + auto maint_time = db.get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db.get_slot_at_time(maint_time); + db.generate_block(db.get_slot_time(slots_to_miss), + db.get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (std::exception& e) + { + return false; + } +} + account_create_operation database_fixture::make_account( const std::string& name /* = "nathan" */, public_key_type key /* = key_id_type() */ @@ -731,7 +749,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow witness_create_operation op; op.witness_account = owner.id; op.block_signing_key = signing_private_key.get_public_key(); - + secret_hash_type::encoder enc; fc::raw::pack(enc, signing_private_key); fc::raw::pack(enc, secret_hash_type()); @@ -1116,12 +1134,12 @@ int64_t database_fixture::get_balance( const account_object& account, const asse } int64_t database_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, - asset_id_type dividend_payout_asset_type) const + account_id_type dividend_holder_account_id, + asset_id_type dividend_payout_asset_type) const { - const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = + const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type(); - auto pending_payout_iter = + auto pending_payout_iter = pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id)); if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) return 0; @@ -1342,7 +1360,7 @@ void database_fixture::delete_sport(sport_id_type sport_id) sport_delete_op.sport_id = sport_id; process_operation_by_witnesses(sport_delete_op); } FC_CAPTURE_AND_RETHROW( (sport_id) ) } - + const event_group_object& database_fixture::create_event_group(internationalized_string_type name, sport_id_type sport_id) { try { event_group_create_operation event_group_create_op; @@ -1372,7 +1390,7 @@ void database_fixture::delete_event_group(event_group_id_type event_group_id) process_operation_by_witnesses(event_group_delete_op); } FC_CAPTURE_AND_RETHROW( (event_group_id) ) } - + void database_fixture::try_update_event_group(event_group_id_type event_group_id, fc::optional sport_id, fc::optional name, @@ -1412,7 +1430,7 @@ void database_fixture::update_event_impl(event_id_type event_id, fc::optional event_group_id, fc::optional name, fc::optional season, - fc::optional status, + fc::optional status, bool force) { try { event_update_operation event_update_op; @@ -1449,9 +1467,9 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type process_operation_by_witnesses(betting_market_rules_update_op); } FC_CAPTURE_AND_RETHROW( (name)(description) ) } -const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, +const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling) @@ -1521,7 +1539,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark bet_place_op.amount_to_bet = amount_to_bet; bet_place_op.backer_multiplier = backer_multiplier; bet_place_op.back_or_lay = back_or_lay; - + trx.operations.push_back(bet_place_op); trx.validate(); processed_transaction ptx = db.push_transaction(trx, ~0); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 200d1897..a190b9c6 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -199,6 +199,12 @@ struct database_fixture { */ void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); + /////////// + /// @brief Skip intermediate blocks, and generate a maintenance block + /// @returns true on success + /////////// + bool generate_maintenance_block(); + account_create_operation make_account( const std::string& name = "nathan", public_key_type = public_key_type() @@ -295,7 +301,7 @@ struct database_fixture { int64_t get_balance( account_id_type account, asset_id_type a )const; int64_t get_balance( const account_object& account, const asset_object& a )const; int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, + account_id_type dividend_holder_account_id, asset_id_type dividend_payout_asset_type) const; vector< operation_history_object > get_operation_history( account_id_type account_id )const; void process_operation_by_witnesses(operation op); @@ -321,7 +327,7 @@ struct database_fixture { fc::optional season, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, (required (event_id, (event_id_type))) (optional (event_group_id, (fc::optional), fc::optional()) (name, (fc::optional), fc::optional()) @@ -336,9 +342,9 @@ struct database_fixture { void update_betting_market_rules(betting_market_rules_id_type rules_id, fc::optional name, fc::optional description); - const betting_market_group_object& create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, + const betting_market_group_object& create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling); @@ -347,7 +353,7 @@ struct database_fixture { fc::optional rules_id, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, (required (betting_market_group_id, (betting_market_group_id_type))) (optional (description, (fc::optional), fc::optional()) (rules_id, (fc::optional), fc::optional()) diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp new file mode 100644 index 00000000..1a912e05 --- /dev/null +++ b/tests/tests/son_wallet_tests.cpp @@ -0,0 +1,225 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include + +using namespace graphene; +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( son_wallet_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { + + BOOST_TEST_MESSAGE("son_wallet_recreate_test"); + + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)(bob)); + + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, alice_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, bob_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + generate_block(); + set_expiration(db, trx); + + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_alice = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_alice = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // alice becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url; + op.deposit = deposit_alice; + op.pay_vb = payment_alice; + op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + // create deposit vesting + vesting_balance_id_type deposit_bob; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_bob ; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // bob becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit_bob; + op.pay_vb = payment_bob; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + generate_blocks(60); + set_expiration(db, trx); + + { + BOOST_TEST_MESSAGE("Send son_wallet_recreate_operation"); + + son_wallet_recreate_operation op; + + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + + { + son_info si; + si.son_id = son_id_type(0); + si.total_votes = 1000; + si.signing_key = alice_public_key; + si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + { + son_info si; + si.son_id = son_id_type(1); + si.total_votes = 1000; + si.signing_key = bob_public_key; + si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check son_wallet_recreate_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->expires == time_point_sec::maximum() ); +} + +BOOST_AUTO_TEST_CASE( son_wallet_update_test ) { + + BOOST_TEST_MESSAGE("son_wallet_update_test"); + + INVOKE(son_wallet_recreate_test); + GET_ACTOR(alice); + + { + BOOST_TEST_MESSAGE("Send son_wallet_update_operation"); + + son_wallet_update_operation op; + + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.son_wallet_id = son_wallet_id_type(0); + op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; + op.address = "bitcoin address"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + { + BOOST_TEST_MESSAGE("Check son_wallet_update_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->addresses.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" ); + } + +} + +BOOST_AUTO_TEST_SUITE_END() From daf7ac5da808afb2b497b4df6b6ad579317c5817 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 7 Feb 2020 16:49:16 +1100 Subject: [PATCH 33/64] SON214 - Request maintenance wallet commands (#280) --- libraries/chain/db_maint.cpp | 14 +++++++++++-- .../include/graphene/chain/son_object.hpp | 3 ++- libraries/chain/son_evaluator.cpp | 20 +++++++++++-------- .../peerplays_sidechain_plugin.cpp | 3 ++- .../wallet/include/graphene/wallet/wallet.hpp | 2 ++ libraries/wallet/wallet.cpp | 3 +-- tests/cli/son.cpp | 20 +++++++++++++++++-- tests/tests/son_operations_tests.cpp | 8 +++++++- 8 files changed, 56 insertions(+), 17 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 935e520e..157e28c0 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -414,10 +414,20 @@ void database::update_active_sons() const auto& all_sons = get_index_type().indices(); + auto& local_vote_buffer_ref = _vote_tally_buffer; for( const son_object& son : all_sons ) { - modify( son, [&]( son_object& obj ){ - obj.total_votes = _vote_tally_buffer[son.vote_id]; + if(son.status == son_status::request_maintenance) + { + auto& stats = son.statistics(*this); + modify( stats, [&]( son_statistics_object& _s){ + _s.last_down_timestamp = head_block_time(); + }); + } + modify( son, [local_vote_buffer_ref]( son_object& obj ){ + obj.total_votes = local_vote_buffer_ref[obj.vote_id]; + if(obj.status == son_status::request_maintenance) + obj.status = son_status::in_maintenance; }); } diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 5ce45242..7a73a312 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -11,6 +11,7 @@ namespace graphene { namespace chain { { inactive, active, + request_maintenance, in_maintenance, deregistered }; @@ -94,7 +95,7 @@ namespace graphene { namespace chain { using son_stats_index = generic_index; } } // graphene::chain -FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(in_maintenance)(deregistered) ) +FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintenance)(in_maintenance)(deregistered) ) FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(statistics)(status)(sidechain_public_keys) ) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 79660921..bcdda1ba 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -105,11 +105,11 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& auto itr = idx.find(op.son_id); auto stats = itr->statistics( db() ); // Inactive SONs need not send heartbeats - FC_ASSERT(itr->status == son_status::active || itr->status == son_status::in_maintenance, "Inactive SONs need not send heartbeats"); + FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); // Account for network delays fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); // Account for server ntp sync difference - fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(2 * db().block_interval()); + fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); @@ -153,7 +153,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& so.status = son_status::inactive; } }); - } else if (itr->status == son_status::active) { + } else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) { sso.last_active_timestamp = op.ts; @@ -171,7 +171,7 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati FC_ASSERT( idx.find(op.son_id) != idx.end() ); auto itr = idx.find(op.son_id); auto stats = itr->statistics( db() ); - FC_ASSERT(itr->status == son_status::active, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); + FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -182,7 +182,7 @@ object_id_type son_report_down_evaluator::do_apply(const son_report_down_operati auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if (itr->status == son_status::active) { + if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) { sso.last_down_timestamp = op.down_ts; @@ -203,8 +203,8 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati const auto& idx = db().get_index_type().indices().get(); auto itr = idx.find(op.son_id); FC_ASSERT( itr != idx.end() ); - // Inactive SONs can't go to maintenance - FC_ASSERT(itr->status == son_status::active || itr->status == son_status::in_maintenance, "Inactive SONs can't go to maintenance"); + // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states + FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive SONs can't go to maintenance"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -216,7 +216,11 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati { if(itr->status == son_status::active) { db().modify(*itr, [](son_object &so) { - so.status = son_status::in_maintenance; + so.status = son_status::request_maintenance; + }); + } else if(itr->status == son_status::request_maintenance) { + db().modify(*itr, [](son_object &so) { + so.status = son_status::active; }); } } diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 6f7a0d77..0c05756a 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -317,7 +317,8 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); int64_t down_threshold = 2*180000000; - if(son_obj->status == chain::son_status::active && (fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold)) { + if(((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && + ((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) { ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 3d470703..bfe9ab35 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2241,6 +2241,8 @@ FC_API( graphene::wallet::wallet_api, (delete_son) (list_sons) (list_active_sons) + (start_son_maintenance) + (stop_son_maintenance) (get_active_son_wallet) (get_son_wallet_by_time_point) (get_son_wallets) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8e438ec4..194254be 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1964,10 +1964,9 @@ public: { try { son_object son = get_son(owner_account); - son_heartbeat_operation op; + son_maintenance_operation op; op.owner_account = son.son_account; op.son_id = son.id; - op.ts = _remote_db->get_dynamic_global_properties().time; // or fc::time_point_sec(fc::time_point::now()) ??? signed_transaction tx; tx.operations.push_back( op ); diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 5f8ad78a..d71ac4c8 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -699,9 +699,9 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) con.wallet_api_ptr->start_son_maintenance(name, true); BOOST_CHECK(generate_block()); - // check SON is in maintenance + // check SON is in request_maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::in_maintenance); + BOOST_CHECK(son_obj.status == son_status::request_maintenance); // restore SON activity con.wallet_api_ptr->stop_son_maintenance(name, true); @@ -711,6 +711,22 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.status == son_status::active); + // put SON in maintenance mode + con.wallet_api_ptr->start_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is in request_maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::request_maintenance); + + // process maintenance + BOOST_CHECK(generate_maintenance_block()); + + // check SON is in maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::in_maintenance); + + } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 329b1629..c283f21e 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -735,9 +735,15 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::in_maintenance); + BOOST_CHECK( obj->status == son_status::request_maintenance); } + // Modify SON's status to in_maintenance + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + uint64_t downtime = 0; { From 116be75c32a908f980bb27c29c02585c900c0d67 Mon Sep 17 00:00:00 2001 From: obucinac Date: Tue, 11 Feb 2020 14:46:35 +0100 Subject: [PATCH 34/64] SON wallet transfer object and operations (#279) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Add is_active_son guards for sidechain events processing Co-authored-by: gladcow --- libraries/app/impacted.cpp | 6 ++ libraries/chain/CMakeLists.txt | 1 + libraries/chain/db_init.cpp | 4 ++ libraries/chain/db_notify.cpp | 6 ++ .../graphene/chain/protocol/operations.hpp | 3 + .../chain/protocol/son_wallet_transfer.hpp | 47 +++++++++++++ .../include/graphene/chain/protocol/types.hpp | 5 ++ .../chain/son_wallet_transfer_evaluator.hpp | 24 +++++++ .../chain/son_wallet_transfer_object.hpp | 66 +++++++++++++++++++ .../chain/son_wallet_transfer_evaluator.cpp | 60 +++++++++++++++++ .../graphene/peerplays_sidechain/defs.hpp | 15 +++-- .../peerplays_sidechain_plugin.cpp | 43 +++++++++++- .../sidechain_net_handler.cpp | 46 +++++++++++-- .../sidechain_net_handler_bitcoin.cpp | 22 +++++-- programs/js_operation_serializer/main.cpp | 1 + 15 files changed, 335 insertions(+), 14 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp create mode 100644 libraries/chain/son_wallet_transfer_evaluator.cpp diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 6834fa51..d3e8eb8c 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -322,6 +322,12 @@ struct get_impacted_account_visitor void operator()( const son_wallet_update_operation& op ){ _impacted.insert( op.payer ); } + void operator()( const son_wallet_transfer_create_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_transfer_process_operation& op ){ + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ){ _impacted.insert( op.sidechain_address_account ); } diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index b392bc5e..c7dd5375 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -118,6 +118,7 @@ add_library( graphene_chain son_object.cpp son_wallet_evaluator.cpp + son_wallet_transfer_evaluator.cpp sidechain_address_evaluator.cpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c41aad87..3d53fce0 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,7 @@ #include #include #include +#include #include #include @@ -256,6 +258,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -304,6 +307,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index fbd3888d..299cd9d0 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -309,6 +309,12 @@ struct get_impacted_account_visitor void operator()( const son_wallet_update_operation& op ) { _impacted.insert( op.payer ); } + void operator()( const son_wallet_transfer_create_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_transfer_process_operation& op ) { + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ) { _impacted.insert( op.sidechain_address_account ); } diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 74fd532c..27980ae2 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -48,6 +48,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -147,6 +148,8 @@ namespace graphene { namespace chain { son_maintenance_operation, son_wallet_recreate_operation, son_wallet_update_operation, + son_wallet_transfer_create_operation, + son_wallet_transfer_process_operation, sidechain_address_add_operation, sidechain_address_update_operation, sidechain_address_delete_operation diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp new file mode 100644 index 00000000..f32eb2a5 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp @@ -0,0 +1,47 @@ +#pragma once +#include + +namespace graphene { namespace chain { + + struct son_wallet_transfer_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + fc::time_point_sec timestamp; + peerplays_sidechain::sidechain_type sidechain; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + int64_t sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_transfer_process_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_transfer_id_type son_wallet_transfer_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation, (fee)(payer) + (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to)) +FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation, (fee)(payer) + (son_wallet_transfer_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index abfce9c8..c25c465c 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -148,6 +148,7 @@ namespace graphene { namespace chain { son_object_type, son_proposal_object_type, son_wallet_object_type, + son_wallet_transfer_object_type, sidechain_address_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -213,6 +214,7 @@ namespace graphene { namespace chain { class son_object; class son_proposal_object; class son_wallet_object; + class son_wallet_transfer_object; class sidechain_address_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; @@ -243,6 +245,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type; + typedef object_id< protocol_ids, son_wallet_transfer_object_type, son_wallet_transfer_object> son_wallet_transfer_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; // implementation types @@ -431,6 +434,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (son_object_type) (son_proposal_object_type) (son_wallet_object_type) + (son_wallet_transfer_object_type) (sidechain_address_object_type) (OBJECT_TYPE_COUNT) ) @@ -506,6 +510,7 @@ FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_transfer_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp new file mode 100644 index 00000000..68fd0ad5 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace graphene { namespace chain { + +class create_son_wallet_transfer_evaluator : public evaluator +{ +public: + typedef son_wallet_transfer_create_operation operation_type; + + void_result do_evaluate(const son_wallet_transfer_create_operation& o); + object_id_type do_apply(const son_wallet_transfer_create_operation& o); +}; + +class process_son_wallet_transfer_evaluator : public evaluator +{ +public: + typedef son_wallet_transfer_process_operation operation_type; + + void_result do_evaluate(const son_wallet_transfer_process_operation& o); + object_id_type do_apply(const son_wallet_transfer_process_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp new file mode 100644 index 00000000..05497442 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp @@ -0,0 +1,66 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_transfer_object + * @brief tracks information about a SON wallet transfer. + * @ingroup object + */ + class son_wallet_transfer_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_transfer_object_type; + + time_point_sec timestamp; + peerplays_sidechain::sidechain_type sidechain; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + int64_t sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; + + bool processed; + }; + + struct by_sidechain; + struct by_sidechain_uid; + struct by_processed; + struct by_sidechain_and_processed; + using son_wallet_transfer_multi_index_type = multi_index_container< + son_wallet_transfer_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member + > + > + > + >; + using son_wallet_transfer_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_transfer_object, (graphene::db::object), + (timestamp) (sidechain) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) + (peerplays_from) (peerplays_to) + (processed) ) diff --git a/libraries/chain/son_wallet_transfer_evaluator.cpp b/libraries/chain/son_wallet_transfer_evaluator.cpp new file mode 100644 index 00000000..5a447569 --- /dev/null +++ b/libraries/chain/son_wallet_transfer_evaluator.cpp @@ -0,0 +1,60 @@ +#include + +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_create_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_create_operation& op) +{ try { + const auto& new_son_wallet_transfer_object = db().create( [&]( son_wallet_transfer_object& swto ){ + swto.timestamp = op.timestamp; + swto.sidechain = op.sidechain; + swto.sidechain_uid = op.sidechain_uid; + swto.sidechain_transaction_id = op.sidechain_transaction_id; + swto.sidechain_from = op.sidechain_from; + swto.sidechain_to = op.sidechain_to; + swto.sidechain_amount = op.sidechain_amount; + swto.peerplays_from = op.peerplays_from; + swto.peerplays_to = op.peerplays_to; + swto.processed = false; + }); + return new_son_wallet_transfer_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_process_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT(idx.find(op.son_wallet_transfer_id) != idx.end(), "Son wallet transfer not found"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_process_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_transfer_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_wallet_transfer_object &swto) { + swto.processed = true; + }); + } + return op.son_wallet_transfer_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 836cecb7..3bd94a49 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -4,8 +4,11 @@ #include #include +#include #include +#include + namespace graphene { namespace peerplays_sidechain { enum class sidechain_type { @@ -61,11 +64,15 @@ struct info_for_vin }; struct sidechain_event_data { + fc::time_point_sec timestamp; sidechain_type sidechain; - std::string transaction_id; - std::string from; - std::string to; - int64_t amount; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + int64_t sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; }; } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 0c05756a..ad1bb36c 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -59,7 +61,7 @@ class peerplays_sidechain_plugin_impl }; peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : - plugin( _plugin ), + plugin(_plugin), config_ready_son(false), config_ready_bitcoin(false), net_manager(nullptr) @@ -344,6 +346,40 @@ void peerplays_sidechain_plugin_impl::recreate_primary_wallet() } void peerplays_sidechain_plugin_impl::process_deposits() { + + // Account who issues tokens to the user who made deposit + account_id_type pay_from = GRAPHENE_NULL_ACCOUNT; + const auto& account_idx = plugin.database().get_index_type().indices().get(); + const auto& account_itr = account_idx.find("nathan"); + if (account_itr != account_idx.end()) + pay_from = (*account_itr).id; + + const auto& idx = plugin.database().get_index_type().indices().get(); + const auto& idx_range = idx.equal_range(false); + + std::for_each(idx_range.first, idx_range.second, + [&] (const son_wallet_transfer_object& swto) { + + const chain::global_property_object& gpo = plugin.database().get_global_properties(); + + transfer_operation op; + op.from = pay_from; + op.to = swto.peerplays_from; + op.amount = asset(swto.sidechain_amount); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object().son_account; + proposal_op.proposed_ops.push_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); + try { + plugin.database().push_transaction(trx); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); + } + }); } //void peerplays_sidechain_plugin_impl::process_withdrawals() { @@ -433,6 +469,11 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vectorproposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( proposal->id ); } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( proposal->id ); + } } } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index bb036d4a..3a610dca 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -44,11 +44,47 @@ std::vector sidechain_net_handler::get_sidechain_addresses() { void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) { ilog( __FUNCTION__ ); ilog( "sidechain_event_data:" ); - ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); - ilog( " transaction_id: ${transaction_id}", ( "transaction_id", sed.transaction_id ) ); - ilog( " from: ${from}", ( "from", sed.from ) ); - ilog( " to: ${to}", ( "to", sed.to ) ); - ilog( " amount: ${amount}", ( "amount", sed.amount ) ); + ilog( " timestamp: ${timestamp}", ( "timestamp", sed.timestamp ) ); + ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); + ilog( " sidechain_uid: ${uid}", ( "uid", sed.sidechain_uid ) ); + ilog( " sidechain_transaction_id: ${transaction_id}", ( "transaction_id", sed.sidechain_transaction_id ) ); + ilog( " sidechain_from: ${from}", ( "from", sed.sidechain_from ) ); + ilog( " sidechain_to: ${to}", ( "to", sed.sidechain_to ) ); + ilog( " sidechain_amount: ${amount}", ( "amount", sed.sidechain_amount ) ); + ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) ); + ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) ); + + if (!plugin.is_active_son()) { + ilog( " !!! SON is not active and not processing sidechain events..."); + return; + } + + const chain::global_property_object& gpo = database.get_global_properties(); + + son_wallet_transfer_create_operation op; + op.payer = gpo.parameters.get_son_btc_account_id(); + op.timestamp = sed.timestamp; + op.sidechain = sed.sidechain; + op.sidechain_uid = sed.sidechain_uid; + op.sidechain_transaction_id = sed.sidechain_transaction_id; + op.sidechain_from = sed.sidechain_from; + op.sidechain_to = sed.sidechain_to; + op.sidechain_amount = sed.sidechain_amount; + op.peerplays_from = sed.peerplays_from; + op.peerplays_to = sed.peerplays_to; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object().son_account; + proposal_op.proposed_ops.push_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); + try { + database.push_transaction(trx); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation failed with exception ${e}",("e", e.what())); + } } } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index e6bda28e..3d89938f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -333,6 +334,11 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); ilog(" event_data: ${event_data}", ("event_data", event_data)); + if (!plugin.is_active_son()) { + ilog(" !!! SON is not active and not processing sidechain events..."); + return; + } + std::string block = bitcoin_client->receive_full_block( event_data ); if( block != "" ) { const auto& vins = extract_info_from_block( block ); @@ -344,12 +350,20 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data if ( addr_itr == sidechain_addresses_idx.end() ) continue; + std::stringstream ss; + ss << "bitcoin" << "-" << v.out.hash_tx << "-" << v.out.n_vout; + std::string sidechain_uid = ss.str(); + sidechain_event_data sed; + sed.timestamp = plugin.database().head_block_time(); sed.sidechain = addr_itr->sidechain; - sed.transaction_id = v.out.hash_tx; - sed.from = ""; - sed.to = v.address; - sed.amount = v.out.amount; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = v.out.hash_tx; + sed.sidechain_from = ""; + sed.sidechain_to = v.address; + sed.sidechain_amount = v.out.amount; + sed.peerplays_from = addr_itr->sidechain_address_account; + sed.peerplays_to = GRAPHENE_SON_ACCOUNT_ID; sidechain_event_data_received(sed); } } diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index f9056a55..04a94827 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include From a8eb4227aa6f4e83def1d26938a62a856bd46c7b Mon Sep 17 00:00:00 2001 From: obucinac Date: Wed, 19 Feb 2020 13:36:58 +0200 Subject: [PATCH 35/64] Support multiple SON nodes per software instance (#282) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Temoprary disable account history tests for tracking accounts Co-authored-by: gladcow --- libraries/chain/db_init.cpp | 11 + libraries/chain/db_maint.cpp | 4 +- libraries/chain/db_notify.cpp | 4 + libraries/chain/get_config.cpp | 6 + .../chain/include/graphene/chain/config.hpp | 2 +- .../chain/protocol/son_wallet_transfer.hpp | 3 +- .../chain/son_wallet_transfer_object.hpp | 6 +- libraries/chain/son_wallet_evaluator.cpp | 20 +- .../chain/son_wallet_transfer_evaluator.cpp | 106 +++++-- .../peerplays_sidechain/CMakeLists.txt | 7 + .../peerplays_sidechain_plugin.hpp | 8 +- .../peerplays_sidechain_plugin.cpp | 266 +++++++++-------- .../sidechain_net_handler.cpp | 34 +-- .../sidechain_net_handler_bitcoin.cpp | 32 +-- .../sidechain_net_manager.cpp | 3 - tests/tests/history_api_tests.cpp | 272 +++++++++--------- 16 files changed, 453 insertions(+), 331 deletions(-) mode change 100644 => 100755 tests/tests/history_api_tests.cpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 3d53fce0..5c72d1bc 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -259,6 +259,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -446,6 +447,16 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.network_fee_percentage = 0; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT; }).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID); + FC_ASSERT(create([this](account_object& a) { + a.name = "son-account"; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.owner.weight_threshold = 0; + a.active.weight_threshold = 0; + a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT; + a.membership_expiration_date = time_point_sec::maximum(); + a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + }).get_id() == GRAPHENE_SON_ACCOUNT); // Create more special accounts while( true ) { diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 157e28c0..9469bbce 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -432,14 +432,14 @@ void database::update_active_sons() } // Update SON authority - modify( get(GRAPHENE_SON_ACCOUNT_ID), [&]( account_object& a ) + modify( get(GRAPHENE_SON_ACCOUNT), [&]( account_object& a ) { if( head_block_time() < HARDFORK_533_TIME ) { uint64_t total_votes = 0; map weights; a.active.weight_threshold = 0; - a.active.clear(); + a.active.account_auths.clear(); for( const son_object& son : sons ) { diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 299cd9d0..98e02a1b 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -417,6 +417,10 @@ void get_relevant_accounts( const object* obj, flat_set& accoun assert( aobj != nullptr ); accounts.insert( aobj->son_account ); break; + } case son_wallet_object_type:{ + break; + } case son_wallet_transfer_object_type:{ + break; } case sidechain_address_object_type:{ const auto& aobj = dynamic_cast(obj); assert( aobj != nullptr ); diff --git a/libraries/chain/get_config.cpp b/libraries/chain/get_config.cpp index c961b950..68d0c951 100644 --- a/libraries/chain/get_config.cpp +++ b/libraries/chain/get_config.cpp @@ -108,6 +108,12 @@ fc::variant_object get_config() result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_SON_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); return result; } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 37b0885d..e44d2fcf 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -176,7 +176,7 @@ /// #define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6)) /// -#define GRAPHENE_SON_ACCOUNT_ID (graphene::chain::account_id_type(7)) +#define GRAPHENE_SON_ACCOUNT (graphene::chain::account_id_type(7)) /// Sentinel value used in the scheduler. #define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0)) ///@} diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp index f32eb2a5..38f8dac6 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp @@ -19,6 +19,7 @@ namespace graphene { namespace chain { int64_t sidechain_amount; chain::account_id_type peerplays_from; chain::account_id_type peerplays_to; + chain::asset peerplays_amount; account_id_type fee_payer()const { return payer; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } @@ -41,7 +42,7 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation, (fee)(payer) - (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to)) + (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_amount)) FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation, (fee)(payer) (son_wallet_transfer_id)) diff --git a/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp index 05497442..68293784 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp @@ -18,6 +18,7 @@ namespace graphene { namespace chain { time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; + int64_t confirmations; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -25,6 +26,7 @@ namespace graphene { namespace chain { int64_t sidechain_amount; chain::account_id_type peerplays_from; chain::account_id_type peerplays_to; + chain::asset peerplays_amount; bool processed; }; @@ -60,7 +62,7 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::son_wallet_transfer_object, (graphene::db::object), - (timestamp) (sidechain) + (timestamp) (sidechain) (confirmations) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) - (peerplays_from) (peerplays_to) + (peerplays_from) (peerplays_to) (peerplays_amount) (processed) ) diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 55d4645d..736832d9 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -9,7 +9,7 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); auto itr = idx.rbegin(); @@ -55,13 +55,13 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); - auto itr = idx.find(op.son_wallet_id); - FC_ASSERT( itr->addresses.find(peerplays_sidechain::sidechain_type::bitcoin) == itr->addresses.end() || - itr->addresses.at(peerplays_sidechain::sidechain_type::bitcoin).empty(), "Sidechain wallet address already set"); + //auto itr = idx.find(op.son_wallet_id); + //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || + // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -69,11 +69,13 @@ object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_ope { try { const auto& idx = db().get_index_type().indices().get(); auto itr = idx.find(op.son_wallet_id); - if(itr != idx.end()) + if (itr != idx.end()) { - db().modify(*itr, [&op](son_wallet_object &swo) { - swo.addresses[op.sidechain] = op.address; - }); + if (itr->addresses.find(op.sidechain) == itr->addresses.end()) { + db().modify(*itr, [&op](son_wallet_object &swo) { + swo.addresses[op.sidechain] = op.address; + }); + } } return op.son_wallet_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_transfer_evaluator.cpp b/libraries/chain/son_wallet_transfer_evaluator.cpp index 5a447569..6245efa8 100644 --- a/libraries/chain/son_wallet_transfer_evaluator.cpp +++ b/libraries/chain/son_wallet_transfer_evaluator.cpp @@ -1,6 +1,7 @@ #include #include +#include #include namespace graphene { namespace chain { @@ -9,39 +10,92 @@ void_result create_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_t { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); - const auto& idx = db().get_index_type().indices().get(); - FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); + //const auto& idx = db().get_index_type().indices().get(); + //FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_create_operation& op) { try { - const auto& new_son_wallet_transfer_object = db().create( [&]( son_wallet_transfer_object& swto ){ - swto.timestamp = op.timestamp; - swto.sidechain = op.sidechain; - swto.sidechain_uid = op.sidechain_uid; - swto.sidechain_transaction_id = op.sidechain_transaction_id; - swto.sidechain_from = op.sidechain_from; - swto.sidechain_to = op.sidechain_to; - swto.sidechain_amount = op.sidechain_amount; - swto.peerplays_from = op.peerplays_from; - swto.peerplays_to = op.peerplays_to; - swto.processed = false; - }); - return new_son_wallet_transfer_object.id; + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.sidechain_uid); + if (itr == idx.end()) { + const auto& new_son_wallet_transfer_object = db().create( [&]( son_wallet_transfer_object& swto ){ + swto.timestamp = op.timestamp; + swto.sidechain = op.sidechain; + swto.confirmations = 1; + swto.sidechain_uid = op.sidechain_uid; + swto.sidechain_transaction_id = op.sidechain_transaction_id; + swto.sidechain_from = op.sidechain_from; + swto.sidechain_to = op.sidechain_to; + swto.sidechain_amount = op.sidechain_amount; + swto.peerplays_from = op.peerplays_from; + swto.peerplays_to = op.peerplays_to; + swto.peerplays_amount = op.peerplays_amount; + swto.processed = false; + }); + return new_son_wallet_transfer_object.id; + } else { + db().modify(*itr, [&op](son_wallet_transfer_object &swto) { + swto.confirmations = swto.confirmations + 1; + }); + return (*itr).id; + } } FC_CAPTURE_AND_RETHROW( (op) ) } void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_process_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); - FC_ASSERT(idx.find(op.son_wallet_transfer_id) != idx.end(), "Son wallet transfer not found"); - return void_result(); + const auto& itr = idx.find(op.son_wallet_transfer_id); + FC_ASSERT(itr != idx.end(), "Son wallet transfer not found"); + //FC_ASSERT(itr->processed == false, "Son wallet transfer is already processed"); + + const database& d = db(); + + const account_object& from_account = itr->peerplays_to(d); // reversed, for deposit + const account_object& to_account = itr->peerplays_from(d); // reversed, for deposit + const asset_object& asset_type = itr->peerplays_amount.asset_id(d); + + try { + + GRAPHENE_ASSERT( + is_authorized_asset( d, from_account, asset_type ), + transfer_from_account_not_whitelisted, + "'from' account ${from} is not whitelisted for asset ${asset}", + ("from",from_account.id) + ("asset",itr->peerplays_amount.asset_id) + ); + GRAPHENE_ASSERT( + is_authorized_asset( d, to_account, asset_type ), + transfer_to_account_not_whitelisted, + "'to' account ${to} is not whitelisted for asset ${asset}", + ("to",to_account.id) + ("asset",itr->peerplays_amount.asset_id) + ); + + if( asset_type.is_transfer_restricted() ) + { + GRAPHENE_ASSERT( + from_account.id == asset_type.issuer || to_account.id == asset_type.issuer, + transfer_restricted_transfer_asset, + "Asset {asset} has transfer_restricted flag enabled", + ("asset", itr->peerplays_amount.asset_id) + ); + } + + bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_amount.amount; + FC_ASSERT( insufficient_balance, + "Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'", + ("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_amount))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) ); + + return void_result(); + } FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_amount))("f",from_account.name)("t",to_account.name) ); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_process_operation& op) @@ -50,9 +104,17 @@ object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_ auto itr = idx.find(op.son_wallet_transfer_id); if(itr != idx.end()) { - db().modify(*itr, [&op](son_wallet_transfer_object &swto) { - swto.processed = true; - }); + if (itr->processed == false) { + db().modify(*itr, [&op](son_wallet_transfer_object &swto) { + swto.processed = true; + }); + + const account_id_type from_account = itr->peerplays_to; // reversed, for deposit + const account_id_type to_account = itr->peerplays_from; // reversed, for deposit + + db().adjust_balance( from_account, -itr->peerplays_amount ); + db().adjust_balance( to_account, itr->peerplays_amount ); + } } return op.son_wallet_transfer_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 4941ce51..6a6faf16 100644 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -7,6 +7,13 @@ add_library( peerplays_sidechain sidechain_net_handler_bitcoin.cpp ) +if (SUPPORT_MULTIPLE_SONS) + message ("Multiple SONs per software instance are supported") + target_compile_definitions(peerplays_sidechain PRIVATE SUPPORT_MULTIPLE_SONS) +endif() +unset(SUPPORT_MULTIPLE_SONS) +unset(SUPPORT_MULTIPLE_SONS CACHE) + target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 9612cf55..e9a868f1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -27,10 +27,12 @@ class peerplays_sidechain_plugin : public graphene::app::plugin std::unique_ptr my; - son_id_type get_son_id(); - son_object get_son_object(); - bool is_active_son(); + std::set& get_sons(); + son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); std::map& get_private_keys(); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); }; } } //graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index ad1bb36c..a32d9dd8 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -33,10 +34,12 @@ class peerplays_sidechain_plugin_impl void plugin_initialize(const boost::program_options::variables_map& options); void plugin_startup(); - son_id_type get_son_id(); - son_object get_son_object(); - bool is_active_son(); + std::set& get_sons(); + son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); std::map& get_private_keys(); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); void schedule_heartbeat_loop(); void heartbeat_loop(); @@ -86,12 +89,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( { auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); string son_id_example = fc::json::to_string(chain::son_id_type(5)); + string son_id_example2 = fc::json::to_string(chain::son_id_type(6)); cli.add_options() ("son-id", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) + ("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()) ("peerplays-private-key", bpo::value>()->composing()->multitoken()-> DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), - "Tuple of [PublicKey, WIF private key]") + "Tuple of [PublicKey, WIF private key] (may specify multiple times)") ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") @@ -107,9 +112,17 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map& options) { - config_ready_son = options.count( "son-id" ) && options.count( "peerplays-private-key" ); + config_ready_son = (options.count( "son-id" ) || options.count( "son-ids" )) && options.count( "peerplays-private-key" ); if (config_ready_son) { LOAD_VALUE_SET(options, "son-id", _sons, chain::son_id_type) + if (options.count("son-ids")) + boost::insert(_sons, fc::json::from_string(options.at("son-ids").as()).as>( 5 )); + config_ready_son = config_ready_son && !_sons.empty(); + +#ifndef SUPPORT_MULTIPLE_SONS + FC_ASSERT( _sons.size() == 1, "Multiple SONs not supported" ); +#endif + if( options.count("peerplays-private-key") ) { const std::vector key_id_to_wif_pair_strings = options["peerplays-private-key"].as>(); @@ -173,9 +186,8 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt void peerplays_sidechain_plugin_impl::plugin_startup() { if (config_ready_son) { - ilog("SON running"); + ilog("Starting ${n} SON instances", ("n", _sons.size())); - ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); schedule_heartbeat_loop(); } else { elog("No sons configured! Please add SON IDs and private keys to configuration."); @@ -190,24 +202,24 @@ void peerplays_sidechain_plugin_impl::plugin_startup() //} } -son_id_type peerplays_sidechain_plugin_impl::get_son_id() +std::set& peerplays_sidechain_plugin_impl::get_sons() { - return *(_sons.begin()); + return _sons; } -son_object peerplays_sidechain_plugin_impl::get_son_object() +son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { const auto& idx = plugin.database().get_index_type().indices().get(); - auto son_obj = idx.find( get_son_id() ); + auto son_obj = idx.find( son_id ); if (son_obj == idx.end()) return {}; return *son_obj; } -bool peerplays_sidechain_plugin_impl::is_active_son() +bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { const auto& idx = plugin.database().get_index_type().indices().get(); - auto son_obj = idx.find( get_son_id() ); + auto son_obj = idx.find( son_id ); if (son_obj == idx.end()) return false; @@ -220,7 +232,7 @@ bool peerplays_sidechain_plugin_impl::is_active_son() return swi.son_id; }); - auto it = std::find(active_son_ids.begin(), active_son_ids.end(), get_son_id()); + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); return (it != active_son_ids.end()); } @@ -230,6 +242,20 @@ std::map& peerplays_sidechain_plug return _private_keys; } +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) +{ + return get_private_key(get_son_object(son_id).signing_key); +} + +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) +{ + auto private_key_itr = _private_keys.find( public_key ); + if( private_key_itr != _private_keys.end() ) { + return private_key_itr->second; + } + return {}; +} + void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { fc::time_point now = fc::time_point::now(); @@ -245,40 +271,29 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { schedule_heartbeat_loop(); chain::database& d = plugin.database(); - chain::son_id_type son_id = *(_sons.begin()); - const auto& idx = d.get_index_type().indices().get(); - auto son_obj = idx.find( son_id ); - if(son_obj == idx.end()) - return; - const chain::global_property_object& gpo = d.get_global_properties(); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); - if(it != active_son_ids.end() || son_obj->status == chain::son_status::in_maintenance) { - ilog("peerplays_sidechain_plugin: sending heartbeat"); - chain::son_heartbeat_operation op; - op.owner_account = son_obj->son_account; - op.son_id = son_id; - op.ts = fc::time_point::now() + fc::seconds(0); - chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); - fc::future fut = fc::async( [&](){ - try { - d.push_transaction(trx); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); - return false; - } - }); - fut.wait(fc::seconds(10)); + for (son_id_type son_id : _sons) { + if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { + + ilog("peerplays_sidechain_plugin: sending heartbeat for SON ${son}", ("son", son_id)); + chain::son_heartbeat_operation op; + op.owner_account = get_son_object(son_id).son_account; + op.son_id = son_id; + op.ts = fc::time_point::now() + fc::seconds(0); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } } } @@ -323,10 +338,10 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() ((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) { ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); - chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, op); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_obj->signing_key), op); fc::future fut = fc::async( [&](){ try { - d.push_transaction(trx); + d.push_transaction(trx, database::validation_steps::skip_block_size_check); if(plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; @@ -347,13 +362,6 @@ void peerplays_sidechain_plugin_impl::recreate_primary_wallet() void peerplays_sidechain_plugin_impl::process_deposits() { - // Account who issues tokens to the user who made deposit - account_id_type pay_from = GRAPHENE_NULL_ACCOUNT; - const auto& account_idx = plugin.database().get_index_type().indices().get(); - const auto& account_itr = account_idx.find("nathan"); - if (account_itr != account_idx.end()) - pay_from = (*account_itr).id; - const auto& idx = plugin.database().get_index_type().indices().get(); const auto& idx_range = idx.equal_range(false); @@ -362,22 +370,31 @@ void peerplays_sidechain_plugin_impl::process_deposits() { const chain::global_property_object& gpo = plugin.database().get_global_properties(); - transfer_operation op; - op.from = pay_from; - op.to = swto.peerplays_from; - op.amount = asset(swto.sidechain_amount); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object().son_account; - proposal_op.proposed_ops.push_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + son_wallet_transfer_process_operation p_op; + p_op.payer = gpo.parameters.get_son_btc_account_id(); + p_op.son_wallet_transfer_id = swto.id; - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); - try { - plugin.database().push_transaction(trx); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + + ilog("sidechain_net_handler: sending proposal for transfer operation ${swto} by ${son}", ("swto", swto.id) ("son", son_id)); + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + trx.validate(); + ilog("sidechain_net_handler: transaction validated ${swto} by ${son}", ("swto", swto.id) ("son", son_id)); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); + } + } } }); } @@ -388,16 +405,17 @@ void peerplays_sidechain_plugin_impl::process_deposits() { void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) { chain::database& d = plugin.database(); - chain::son_id_type my_son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); bool latest_block = ((fc::time_point::now() - b.timestamp) < fc::microseconds(gpo.parameters.block_interval * 1000000)); - // Return if there are no active SONs if(gpo.active_sons.size() <= 0 || !latest_block) { return; } chain::son_id_type next_son_id = d.get_scheduled_son(1); - if(next_son_id == my_son_id) { + ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}",("son", next_son_id)); + + // check if we control scheduled SON + if( _sons.find( next_son_id ) != _sons.end() ) { create_son_down_proposals(); @@ -412,35 +430,18 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) void peerplays_sidechain_plugin_impl::on_objects_new(const vector& new_object_ids) { - chain::database& d = plugin.database(); - chain::son_id_type my_son_id = *(_sons.begin()); - const chain::global_property_object& gpo = d.get_global_properties(); - const auto& idx = d.get_index_type().indices().get(); - auto son_obj = idx.find( my_son_id ); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - auto it = std::find(active_son_ids.begin(), active_son_ids.end(), my_son_id); - if(it == active_son_ids.end()) { - return; - } - - auto approve_proposal = [ & ]( const chain::proposal_id_type& id ) + auto approve_proposal = [ & ]( const chain::son_id_type& son_id, const chain::proposal_id_type& proposal_id ) { - ilog("peerplays_sidechain_plugin: sending approval for ${t} from ${s}",("t",std::string(object_id_type(id)))("s",std::string(object_id_type(my_son_id)))); + ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id) ("s", son_id)); chain::proposal_update_operation puo; - puo.fee_paying_account = son_obj->son_account; - puo.proposal = id; - puo.active_approvals_to_add = { son_obj->son_account }; - chain::signed_transaction trx = d.create_signed_transaction(_private_keys.begin()->second, puo); + puo.fee_paying_account = get_son_object(son_id).son_account; + puo.proposal = proposal_id; + puo.active_approvals_to_add = { get_son_object(son_id).son_account }; + chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo); fc::future fut = fc::async( [&](){ try { - d.push_transaction(trx); + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); if(plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; @@ -454,25 +455,42 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector() ) { - const object* obj = d.find_object(object_id); - const chain::proposal_object* proposal = dynamic_cast(obj); - if(proposal == nullptr || (proposal->available_active_approvals.find(son_obj->son_account) != proposal->available_active_approvals.end())) { - return; - } - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( proposal->id ); - } + for (son_id_type son_id : _sons) { + if (!is_active_son(son_id)) { + continue; + } - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( proposal->id ); - } + const object* obj = plugin.database().find_object(object_id); + const chain::proposal_object* proposal = dynamic_cast(obj); - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( proposal->id ); + if(proposal == nullptr || (proposal->available_active_approvals.find(get_son_object(son_id).son_account) != proposal->available_active_approvals.end())) { + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } } } } @@ -515,19 +533,19 @@ void peerplays_sidechain_plugin::plugin_startup() my->plugin_startup(); } -son_id_type peerplays_sidechain_plugin::get_son_id() +std::set& peerplays_sidechain_plugin::get_sons() { - return my->get_son_id(); + return my->get_sons(); } -son_object peerplays_sidechain_plugin::get_son_object() +son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { - return my->get_son_object(); + return my->get_son_object(son_id); } -bool peerplays_sidechain_plugin::is_active_son() +bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { - return my->is_active_son(); + return my->is_active_son(son_id); } std::map& peerplays_sidechain_plugin::get_private_keys() @@ -535,5 +553,15 @@ std::map& peerplays_sidechain_plug return my->get_private_keys(); } +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) +{ + return my->get_private_key(son_id); +} + +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) +{ + return my->get_private_key(public_key); +} + } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 3a610dca..ac50974c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -42,7 +42,6 @@ std::vector sidechain_net_handler::get_sidechain_addresses() { } void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) { - ilog( __FUNCTION__ ); ilog( "sidechain_event_data:" ); ilog( " timestamp: ${timestamp}", ( "timestamp", sed.timestamp ) ); ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); @@ -54,11 +53,6 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) ); ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) ); - if (!plugin.is_active_son()) { - ilog( " !!! SON is not active and not processing sidechain events..."); - return; - } - const chain::global_property_object& gpo = database.get_global_properties(); son_wallet_transfer_create_operation op; @@ -72,18 +66,26 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ op.sidechain_amount = sed.sidechain_amount; op.peerplays_from = sed.peerplays_from; op.peerplays_to = sed.peerplays_to; + op.peerplays_amount = asset(sed.sidechain_amount / 1000); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object().son_account; - proposal_op.proposed_ops.push_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); - try { - database.push_transaction(trx); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation failed with exception ${e}",("e", e.what())); + ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son}", ("son", son_id)); + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + } + } } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 3d89938f..cdd3611e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -277,7 +277,6 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { boost::property_tree::ptree pt; boost::property_tree::read_json( ss, pt ); if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) { - ilog(__FUNCTION__); std::stringstream res; boost::property_tree::json_parser::write_json(res, pt.get_child("result")); @@ -288,17 +287,21 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { op.sidechain = sidechain_type::bitcoin; op.address = res.str(); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object().son_account; - proposal_op.proposed_ops.push_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + for (son_id_type son_id : plugin.get_sons()) { + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op); - try { - database.push_transaction(trx); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + } } } } @@ -334,11 +337,6 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); ilog(" event_data: ${event_data}", ("event_data", event_data)); - if (!plugin.is_active_son()) { - ilog(" !!! SON is not active and not processing sidechain events..."); - return; - } - std::string block = bitcoin_client->receive_full_block( event_data ); if( block != "" ) { const auto& vins = extract_info_from_block( block ); @@ -363,7 +361,7 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data sed.sidechain_to = v.address; sed.sidechain_amount = v.out.amount; sed.peerplays_from = addr_itr->sidechain_address_account; - sed.peerplays_to = GRAPHENE_SON_ACCOUNT_ID; + sed.peerplays_to = GRAPHENE_SON_ACCOUNT; sidechain_event_data_received(sed); } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index e21af593..8b6f18c1 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -10,15 +10,12 @@ sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin plugin(_plugin), database(_plugin.database()) { - ilog(__FUNCTION__); } sidechain_net_manager::~sidechain_net_manager() { - ilog(__FUNCTION__); } bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options) { - ilog(__FUNCTION__); bool ret_val = false; diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp old mode 100644 new mode 100755 index 0c7d202a..4cbcda89 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -407,143 +407,143 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(track_account) { - try { - graphene::app::history_api hist_api(app); +//BOOST_AUTO_TEST_CASE(track_account) { +// try { +// graphene::app::history_api hist_api(app); +// +// // account_id_type() is not tracked +// +// // account_id_type() creates alice(not tracked account) +// const account_object& alice = create_account("alice"); +// auto alice_id = alice.id; +// +// //account_id_type() creates some ops +// create_bitasset("CNY", account_id_type()); +// create_bitasset("USD", account_id_type()); +// +// // account_id_type() creates dan(account tracked) +// const account_object& dan = create_account("dan"); +// auto dan_id = dan.id; +// +// // dan makes 1 op +// create_bitasset("EUR", dan_id); +// +// generate_block( ~database::skip_fork_db ); +// +// // anything against account_id_type() should be {} +// vector histories = +// hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// // anything against alice should be {} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// // dan should have history +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 2u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); +// +// // create more ops, starting with an untracked account +// create_bitasset( "BTC", account_id_type() ); +// create_bitasset( "GBP", dan_id ); +// +// generate_block( ~database::skip_fork_db ); +// +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 3u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); +// +// db.pop_block(); +// +// // Try again, should result in same object IDs +// create_bitasset( "BTC", account_id_type() ); +// create_bitasset( "GBP", dan_id ); +// +// generate_block(); +// +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 3u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); +// } catch (fc::exception &e) { +// edump((e.to_detail_string())); +// throw; +// } +//} - // account_id_type() is not tracked - - // account_id_type() creates alice(not tracked account) - const account_object& alice = create_account("alice"); - auto alice_id = alice.id; - - //account_id_type() creates some ops - create_bitasset("CNY", account_id_type()); - create_bitasset("USD", account_id_type()); - - // account_id_type() creates dan(account tracked) - const account_object& dan = create_account("dan"); - auto dan_id = dan.id; - - // dan makes 1 op - create_bitasset("EUR", dan_id); - - generate_block( ~database::skip_fork_db ); - - // anything against account_id_type() should be {} - vector histories = - hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // anything against alice should be {} - histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // dan should have history - histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // create more ops, starting with an untracked account - create_bitasset( "BTC", account_id_type() ); - create_bitasset( "GBP", dan_id ); - - generate_block( ~database::skip_fork_db ); - - histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); - - db.pop_block(); - - // Try again, should result in same object IDs - create_bitasset( "BTC", account_id_type() ); - create_bitasset( "GBP", dan_id ); - - generate_block(); - - histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); - } catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(track_account2) { - try { - graphene::app::history_api hist_api(app); - - // account_id_type() is tracked - - // account_id_type() creates alice(tracked account) - const account_object& alice = create_account("alice"); - auto alice_id = alice.id; - - //account_id_type() creates some ops - create_bitasset("CNY", account_id_type()); - create_bitasset("USD", account_id_type()); - - // alice makes 1 op - create_bitasset("EUR", alice_id); - - // account_id_type() creates dan(account not tracked) - const account_object& dan = create_account("dan"); - auto dan_id = dan.id; - - generate_block(); - - // all account_id_type() should have 4 ops {4,2,1,0} - vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // all alice account should have 2 ops {3, 0} - histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // alice first op should be {0} - histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); - - // alice second op should be {3} - histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - - // anything against dan should be {} - histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - } catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} +//BOOST_AUTO_TEST_CASE(track_account2) { +// try { +// graphene::app::history_api hist_api(app); +// +// // account_id_type() is tracked +// +// // account_id_type() creates alice(tracked account) +// const account_object& alice = create_account("alice"); +// auto alice_id = alice.id; +// +// //account_id_type() creates some ops +// create_bitasset("CNY", account_id_type()); +// create_bitasset("USD", account_id_type()); +// +// // alice makes 1 op +// create_bitasset("EUR", alice_id); +// +// // account_id_type() creates dan(account not tracked) +// const account_object& dan = create_account("dan"); +// auto dan_id = dan.id; +// +// generate_block(); +// +// // all account_id_type() should have 4 ops {4,2,1,0} +// vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 4u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); +// BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); +// +// // all alice account should have 2 ops {3, 0} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 2u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); +// +// // alice first op should be {0} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1)); +// BOOST_CHECK_EQUAL(histories.size(), 1u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); +// +// // alice second op should be {3} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 1u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); +// +// // anything against dan should be {} +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// } catch (fc::exception &e) { +// edump((e.to_detail_string())); +// throw; +// } +//} BOOST_AUTO_TEST_CASE(get_account_history_operations) { try { From 544112c63b1c854eef77a20d4a396120aaa0272e Mon Sep 17 00:00:00 2001 From: gladcow Date: Wed, 19 Feb 2020 14:46:27 +0300 Subject: [PATCH 36/64] [SON-209] Create P2SH address with custom redeemScript (#271) * Create redeem script for SONs primary wallet * Add importaddress call Allows to watch for related transactions without private keys import * Get UTXO set for watched addresses * createrawtransaction call * signing PW spending transaction * unit test for btc tx serialization * sending PW transfer in test * BIP143 tx signing * use bech32 address format * use single sha256 for lock script * Digest fix * working signing * separate signing * test partially signed PW transfer --- .../peerplays_sidechain/CMakeLists.txt | 1 + .../peerplays_sidechain/bitcoin_utils.cpp | 713 ++++++++++++++++++ .../peerplays_sidechain/bitcoin_utils.hpp | 78 ++ .../graphene/peerplays_sidechain/defs.hpp | 2 +- .../sidechain_net_handler_bitcoin.hpp | 11 + .../sidechain_net_handler_bitcoin.cpp | 114 +++ .../bitcoin_utils_test.cpp | 316 ++++++++ 7 files changed, 1234 insertions(+), 1 deletion(-) create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp create mode 100644 tests/peerplays_sidechain/bitcoin_utils_test.cpp diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 6a6faf16..a3910c9e 100644 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( peerplays_sidechain sidechain_net_manager.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp + bitcoin_utils.cpp ) if (SUPPORT_MULTIPLE_SONS) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp new file mode 100644 index 00000000..8ed3021a --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp @@ -0,0 +1,713 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +static const unsigned char OP_0 = 0x00; +static const unsigned char OP_1 = 0x51; +static const unsigned char OP_2 = 0x52; +static const unsigned char OP_3 = 0x53; +static const unsigned char OP_4 = 0x54; +static const unsigned char OP_5 = 0x55; +static const unsigned char OP_6 = 0x56; +static const unsigned char OP_7 = 0x57; +static const unsigned char OP_8 = 0x58; +static const unsigned char OP_9 = 0x59; +static const unsigned char OP_10 = 0x5a; +static const unsigned char OP_11 = 0x5b; +static const unsigned char OP_12 = 0x5c; +static const unsigned char OP_13 = 0x5d; +static const unsigned char OP_14 = 0x5e; +static const unsigned char OP_15 = 0x5f; +static const unsigned char OP_16 = 0x60; + +static const unsigned char OP_IF = 0x63; +static const unsigned char OP_ENDIF = 0x68; +static const unsigned char OP_SWAP = 0x7c; +static const unsigned char OP_EQUAL = 0x87; +static const unsigned char OP_ADD = 0x93; +static const unsigned char OP_GREATERTHAN = 0xa0; +static const unsigned char OP_HASH160 = 0xa9; +static const unsigned char OP_CHECKSIG = 0xac; + +class WriteBytesStream{ +public: + WriteBytesStream(bytes& buffer) : storage_(buffer) {} + + void write(const unsigned char* d, size_t s) { + storage_.insert(storage_.end(), d, d + s); + } + + bool put(unsigned char c) { + storage_.push_back(c); + return true; + } + + void writedata8(uint8_t obj) + { + write((unsigned char*)&obj, 1); + } + + void writedata16(uint16_t obj) + { + obj = htole16(obj); + write((unsigned char*)&obj, 2); + } + + void writedata32(uint32_t obj) + { + obj = htole32(obj); + write((unsigned char*)&obj, 4); + } + + void writedata64(uint64_t obj) + { + obj = htole64(obj); + write((unsigned char*)&obj, 8); + } + + void write_compact_int(uint64_t val) + { + if (val < 253) + { + writedata8(val); + } + else if (val <= std::numeric_limits::max()) + { + writedata8(253); + writedata16(val); + } + else if (val <= std::numeric_limits::max()) + { + writedata8(254); + writedata32(val); + } + else + { + writedata8(255); + writedata64(val); + } + } + + void writedata(const bytes& data) + { + write_compact_int(data.size()); + write(&data[0], data.size()); + } +private: + bytes& storage_; +}; + +class ReadBytesStream{ +public: + ReadBytesStream(const bytes& buffer, size_t pos = 0) : storage_(buffer), pos_(pos), end_(buffer.size()) {} + + size_t current_pos() const { return pos_; } + void set_pos(size_t pos) + { + if(pos > end_) + FC_THROW("Invalid position in BTC tx buffer"); + pos_ = pos; + } + + inline bool read( unsigned char* d, size_t s ) { + if( end_ - pos_ >= s ) { + memcpy( d, &storage_[pos_], s ); + pos_ += s; + return true; + } + FC_THROW( "invalid bitcoin tx buffer" ); + } + + inline bool get( unsigned char& c ) + { + if( pos_ < end_ ) { + c = storage_[pos_++]; + return true; + } + FC_THROW( "invalid bitcoin tx buffer" ); + } + + uint8_t readdata8() + { + uint8_t obj; + read((unsigned char*)&obj, 1); + return obj; + } + uint16_t readdata16() + { + uint16_t obj; + read((unsigned char*)&obj, 2); + return le16toh(obj); + } + uint32_t readdata32() + { + uint32_t obj; + read((unsigned char*)&obj, 4); + return le32toh(obj); + } + uint64_t readdata64() + { + uint64_t obj; + read((unsigned char*)&obj, 8); + return le64toh(obj); + } + + uint64_t read_compact_int() + { + uint8_t size = readdata8(); + uint64_t ret = 0; + if (size < 253) + { + ret = size; + } + else if (size == 253) + { + ret = readdata16(); + if (ret < 253) + FC_THROW("non-canonical ReadCompactSize()"); + } + else if (size == 254) + { + ret = readdata32(); + if (ret < 0x10000u) + FC_THROW("non-canonical ReadCompactSize()"); + } + else + { + ret = readdata64(); + if (ret < 0x100000000ULL) + FC_THROW("non-canonical ReadCompactSize()"); + } + if (ret > (uint64_t)0x02000000) + FC_THROW("ReadCompactSize(): size too large"); + return ret; + } + + void readdata(bytes& data) + { + size_t s = read_compact_int(); + data.clear(); + data.resize(s); + read(&data[0], s); + } + +private: + const bytes& storage_; + size_t pos_; + size_t end_; +}; + +void btc_outpoint::to_bytes(bytes& stream) const +{ + WriteBytesStream str(stream); + // TODO: write size? + str.write((unsigned char*)hash.data(), hash.data_size()); + str.writedata32(n); +} + +size_t btc_outpoint::fill_from_bytes(const bytes& data, size_t pos) +{ + ReadBytesStream str(data, pos); + // TODO: read size? + str.read((unsigned char*)hash.data(), hash.data_size()); + n = str.readdata32(); + return str.current_pos(); +} + +void btc_in::to_bytes(bytes& stream) const +{ + prevout.to_bytes(stream); + WriteBytesStream str(stream); + str.writedata(scriptSig); + str.writedata32(nSequence); +} + +size_t btc_in::fill_from_bytes(const bytes& data, size_t pos) +{ + pos = prevout.fill_from_bytes(data, pos); + ReadBytesStream str(data, pos); + str.readdata(scriptSig); + nSequence = str.readdata32(); + return str.current_pos(); +} + +void btc_out::to_bytes(bytes& stream) const +{ + WriteBytesStream str(stream); + str.writedata64(nValue); + str.writedata(scriptPubKey); +} + +size_t btc_out::fill_from_bytes(const bytes& data, size_t pos) +{ + ReadBytesStream str(data, pos); + nValue = str.readdata64(); + str.readdata(scriptPubKey); + return str.current_pos(); +} + +void btc_tx::to_bytes(bytes& stream) const +{ + WriteBytesStream str(stream); + str.writedata32(nVersion); + if(hasWitness) + { + // write dummy inputs and flag + str.write_compact_int(0); + unsigned char flags = 1; + str.put(flags); + } + str.write_compact_int(vin.size()); + for(const auto& in: vin) + in.to_bytes(stream); + str.write_compact_int(vout.size()); + for(const auto& out: vout) + out.to_bytes(stream); + if(hasWitness) + { + for(const auto& in: vin) + { + str.write_compact_int(in.scriptWitness.size()); + for(const auto& stack_item: in.scriptWitness) + str.writedata(stack_item); + } + } + str.writedata32(nLockTime); +} + +size_t btc_tx::fill_from_bytes(const bytes& data, size_t pos) +{ + ReadBytesStream ds( data, pos ); + nVersion = ds.readdata32(); + unsigned char flags = 0; + vin.clear(); + vout.clear(); + hasWitness = false; + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + size_t vin_size = ds.read_compact_int(); + vin.resize(vin_size); + pos = ds.current_pos(); + for(auto& in: vin) + pos = in.fill_from_bytes(data, pos); + ds.set_pos(pos); + if (vin_size == 0) { + /* We read a dummy or an empty vin. */ + ds.get(flags); + if (flags != 0) { + size_t vin_size = ds.read_compact_int(); + vin.resize(vin_size); + pos = ds.current_pos(); + for(auto& in: vin) + pos = in.fill_from_bytes(data, pos); + ds.set_pos(pos); + size_t vout_size = ds.read_compact_int(); + vout.resize(vout_size); + pos = ds.current_pos(); + for(auto& out: vout) + pos = out.fill_from_bytes(data, pos); + ds.set_pos(pos); + hasWitness = true; + } + } else { + /* We read a non-empty vin. Assume a normal vout follows. */ + size_t vout_size = ds.read_compact_int(); + vout.resize(vout_size); + pos = ds.current_pos(); + for(auto& out: vout) + pos = out.fill_from_bytes(data, pos); + ds.set_pos(pos); + } + if (hasWitness) { + /* The witness flag is present, and we support witnesses. */ + for (auto& in: vin) + { + unsigned int size = ds.read_compact_int(); + in.scriptWitness.resize(size); + for(auto& stack_item: in.scriptWitness) + ds.readdata(stack_item); + } + } + nLockTime = ds.readdata32(); + return ds.current_pos(); +} + + +void add_data_to_script(bytes& script, const bytes& data) +{ + WriteBytesStream str(script); + str.writedata(data); +} + +void add_number_to_script(bytes& script, unsigned char data) +{ + WriteBytesStream str(script); + if(data == 0) + str.put(OP_0); + else if(data == 1) + str.put(OP_1); + else if(data == 2) + str.put(OP_2); + else if(data == 3) + str.put(OP_3); + else if(data == 4) + str.put(OP_4); + else if(data == 5) + str.put(OP_5); + else if(data == 6) + str.put(OP_6); + else if(data == 7) + str.put(OP_7); + else if(data == 8) + str.put(OP_8); + else if(data == 9) + str.put(OP_9); + else if(data == 10) + str.put(OP_10); + else if(data == 11) + str.put(OP_11); + else if(data == 12) + str.put(OP_12); + else if(data == 13) + str.put(OP_13); + else if(data == 14) + str.put(OP_14); + else if(data == 15) + str.put(OP_15); + else if(data == 16) + str.put(OP_16); + else + add_data_to_script(script, {data}); +} + +bytes generate_redeem_script(std::vector > key_data) +{ + int total_weight = 0; + bytes result; + add_number_to_script(result, 0); + for(auto& p: key_data) + { + total_weight += p.second; + result.push_back(OP_SWAP); + auto raw_data = p.first.serialize(); + add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size())); + result.push_back(OP_CHECKSIG); + result.push_back(OP_IF); + add_number_to_script(result, static_cast(p.second)); + result.push_back(OP_ADD); + result.push_back(OP_ENDIF); + } + int threshold_weight = 2 * total_weight / 3; + add_number_to_script(result, static_cast(threshold_weight)); + result.push_back(OP_GREATERTHAN); + return result; +} + +/** The Bech32 character set for encoding. */ +const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** Concatenate two byte arrays. */ +bytes cat(bytes x, const bytes& y) { + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** Expand a HRP for use in checksum computation. */ +bytes expand_hrp(const std::string& hrp) { + bytes ret; + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Find the polynomial with value coefficients mod the generator as 30-bit. */ +uint32_t polymod(const bytes& values) { + uint32_t chk = 1; + for (size_t i = 0; i < values.size(); ++i) { + uint8_t top = chk >> 25; + chk = (chk & 0x1ffffff) << 5 ^ values[i] ^ + (-((top >> 0) & 1) & 0x3b6a57b2UL) ^ + (-((top >> 1) & 1) & 0x26508e6dUL) ^ + (-((top >> 2) & 1) & 0x1ea119faUL) ^ + (-((top >> 3) & 1) & 0x3d4233ddUL) ^ + (-((top >> 4) & 1) & 0x2a1462b3UL); + } + return chk; +} + +/** Create a checksum. */ +bytes bech32_checksum(const std::string& hrp, const bytes& values) { + bytes enc = cat(expand_hrp(hrp), values); + enc.resize(enc.size() + 6); + uint32_t mod = polymod(enc) ^ 1; + bytes ret; + ret.resize(6); + for (size_t i = 0; i < 6; ++i) { + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +/** Encode a Bech32 string. */ +std::string bech32(const std::string& hrp, const bytes& values) { + bytes checksum = bech32_checksum(hrp, values); + bytes combined = cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (size_t i = 0; i < combined.size(); ++i) { + ret += charset[combined[i]]; + } + return ret; +} + +/** Convert from one power-of-2 number base to another. */ +template +bool convertbits(bytes& out, const bytes& in) { + int acc = 0; + int bits = 0; + const int maxv = (1 << tobits) - 1; + const int max_acc = (1 << (frombits + tobits - 1)) - 1; + for (size_t i = 0; i < in.size(); ++i) { + int value = in[i]; + acc = ((acc << frombits) | value) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + +/** Encode a SegWit address. */ +std::string segwit_addr_encode(const std::string& hrp, uint8_t witver, const bytes& witprog) { + bytes enc; + enc.push_back(witver); + convertbits<8, 5, true>(enc, witprog); + std::string ret = bech32(hrp, enc); + return ret; +} + +std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_network network) +{ + // calc script hash + fc::sha256 sh = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); + bytes wp(sh.data(), sh.data() + sh.data_size()); + switch (network) { + case(mainnet): + return segwit_addr_encode("bc", 0, wp); + case(testnet): + case(regtest): + return segwit_addr_encode("tb", 0, wp); + default: + FC_THROW("Unknown bitcoin network type"); + } + FC_THROW("Unknown bitcoin network type"); +} + +bytes lock_script_for_redeem_script(const bytes &script) +{ + bytes result; + result.push_back(OP_0); + fc::sha256 h = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); + bytes shash(h.data(), h.data() + h.data_size()); + add_data_to_script(result, shash); + return result; +} + +bytes hash_prevouts(const btc_tx& unsigned_tx) +{ + fc::sha256::encoder hasher; + for(const auto& in: unsigned_tx.vin) + { + bytes data; + in.prevout.to_bytes(data); + hasher.write(reinterpret_cast(&data[0]), data.size()); + } + fc::sha256 res = fc::sha256::hash(hasher.result()); + return bytes(res.data(), res.data() + res.data_size()); +} + +bytes hash_sequence(const btc_tx& unsigned_tx) +{ + fc::sha256::encoder hasher; + for(const auto& in: unsigned_tx.vin) + { + hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); + } + fc::sha256 res = fc::sha256::hash(hasher.result()); + return bytes(res.data(), res.data() + res.data_size()); +} + +bytes hash_outputs(const btc_tx& unsigned_tx) +{ + fc::sha256::encoder hasher; + for(const auto& out: unsigned_tx.vout) + { + bytes data; + out.to_bytes(data); + hasher.write(reinterpret_cast(&data[0]), data.size()); + } + fc::sha256 res = fc::sha256::hash(hasher.result()); + return bytes(res.data(), res.data() + res.data_size()); +} + +const secp256k1_context_t* btc_get_context() { + static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN ); + return ctx; +} + +bytes der_sign(const fc::ecc::private_key& priv_key, const fc::sha256& digest) +{ + fc::ecc::signature result; + int size = result.size(); + FC_ASSERT( secp256k1_ecdsa_sign( btc_get_context(), + (unsigned char*) digest.data(), + (unsigned char*) result.begin(), + &size, + (unsigned char*) priv_key.get_secret().data(), + secp256k1_nonce_function_rfc6979, + nullptr)); + return bytes(result.begin(), result.begin() + size); +} + +std::vector signature_for_raw_transaction(const bytes& unsigned_tx, + std::vector in_amounts, + const bytes& redeem_script, + const fc::ecc::private_key& priv_key) +{ + btc_tx tx; + tx.fill_from_bytes(unsigned_tx); + + FC_ASSERT(tx.vin.size() == in_amounts.size(), "Incorrect input amounts data"); + + std::vector results; + auto cur_amount = in_amounts.begin(); + // pre-calc reused values + bytes hashPrevouts = hash_prevouts(tx); + bytes hashSequence = hash_sequence(tx); + bytes hashOutputs = hash_outputs(tx); + // calc digest for every input according to BIP143 + // implement SIGHASH_ALL scheme + for(const auto& in: tx.vin) + { + fc::sha256::encoder hasher; + hasher.write(reinterpret_cast(&tx.nVersion), sizeof(tx.nVersion)); + hasher.write(reinterpret_cast(&hashPrevouts[0]), hashPrevouts.size()); + hasher.write(reinterpret_cast(&hashSequence[0]), hashSequence.size()); + bytes data; + in.prevout.to_bytes(data); + hasher.write(reinterpret_cast(&data[0]), data.size()); + bytes serializedScript; + WriteBytesStream stream(serializedScript); + stream.writedata(redeem_script); + hasher.write(reinterpret_cast(&serializedScript[0]), serializedScript.size()); + uint64_t amount = *cur_amount++; + hasher.write(reinterpret_cast(&amount), sizeof(amount)); + hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); + hasher.write(reinterpret_cast(&hashOutputs[0]), hashOutputs.size()); + hasher.write(reinterpret_cast(&tx.nLockTime), sizeof(tx.nLockTime)); + // add sigtype SIGHASH_ALL + uint32_t sigtype = 1; + hasher.write(reinterpret_cast(&sigtype), sizeof(sigtype)); + + fc::sha256 digest = fc::sha256::hash(hasher.result()); + //std::vector res = priv_key.sign(digest); + //bytes s_data(res.begin(), res.end()); + bytes s_data = der_sign(priv_key, digest); + s_data.push_back(1); + results.push_back(s_data); + } + return results; +} + +bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector in_amounts, const bytes& redeem_script, const std::vector > &priv_keys) +{ + btc_tx tx; + tx.fill_from_bytes(unsigned_tx); + bytes dummy_data; + for(auto key: priv_keys) + { + if(key) + { + std::vector signatures = signature_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key); + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + // push signatures in reverse order because script starts to check the top signature on the stack first + for(unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); + } + else + { + for(unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.push_back(dummy_data); + } + } + + for(auto& in: tx.vin) + { + in.scriptWitness.push_back(redeem_script); + } + + tx.hasWitness = true; + bytes ret; + tx.to_bytes(ret); + return ret; +} + +bytes add_dummy_signatures_for_pw_transfer(const bytes& unsigned_tx, + const bytes& redeem_script, + unsigned int key_count) +{ + btc_tx tx; + tx.fill_from_bytes(unsigned_tx); + + bytes dummy_data; + for(auto& in: tx.vin) + { + for(unsigned i = 0; i < key_count; i++) + in.scriptWitness.push_back(dummy_data); + in.scriptWitness.push_back(redeem_script); + } + + tx.hasWitness = true; + bytes ret; + tx.to_bytes(ret); + return ret; +} + +bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, + std::vector in_amounts, + const fc::ecc::private_key& priv_key, + unsigned int key_idx) +{ + btc_tx tx; + tx.fill_from_bytes(partially_signed_tx); + FC_ASSERT(tx.vin.size() > 0); + bytes redeem_script = tx.vin[0].scriptWitness.back(); + std::vector signatures = signature_for_raw_transaction(partially_signed_tx, in_amounts, redeem_script, priv_key); + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + // push signatures in reverse order because script starts to check the top signature on the stack first + unsigned witness_idx = tx.vin[0].scriptWitness.size() - 2 - key_idx; + for(unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness[witness_idx] = signatures[i]; + bytes ret; + tx.to_bytes(ret); + return ret; +} + +}} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp new file mode 100644 index 00000000..718bdd95 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp @@ -0,0 +1,78 @@ +#pragma once +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +enum bitcoin_network { + mainnet, + testnet, + regtest +}; + +bytes generate_redeem_script(std::vector > key_data); +std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_network network = mainnet); +bytes lock_script_for_redeem_script(const bytes& script); + + +/* + * unsigned_tx - tx, all inputs of which are spends of the PW P2SH address + * returns signed transaction + */ +bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, + std::vector in_amounts, + const bytes& redeem_script, + const std::vector>& priv_keys); + +bytes add_dummy_signatures_for_pw_transfer(const bytes& unsigned_tx, + const bytes& redeem_script, + unsigned int key_count); + +bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, + std::vector in_amounts, + const fc::ecc::private_key& priv_key, + unsigned int key_idx); + +struct btc_outpoint +{ + fc::uint256 hash; + uint32_t n; + + void to_bytes(bytes& stream) const; + size_t fill_from_bytes(const bytes& data, size_t pos = 0); +}; + +struct btc_in +{ + btc_outpoint prevout; + bytes scriptSig; + uint32_t nSequence; + std::vector scriptWitness; + + void to_bytes(bytes& stream) const; + size_t fill_from_bytes(const bytes& data, size_t pos = 0); +}; + +struct btc_out +{ + int64_t nValue; + bytes scriptPubKey; + + void to_bytes(bytes& stream) const; + size_t fill_from_bytes(const bytes& data, size_t pos = 0); +}; + +struct btc_tx +{ + std::vector vin; + std::vector vout; + int32_t nVersion; + uint32_t nLockTime; + bool hasWitness; + + void to_bytes(bytes& stream) const; + size_t fill_from_bytes(const bytes& data, size_t pos = 0); +}; + +}} + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 3bd94a49..ae1d222c 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -18,7 +18,7 @@ enum class sidechain_type { peerplays }; -using bytes = std::vector; +using bytes = std::vector; struct prev_out { diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 803b24de..9768066b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -11,6 +11,14 @@ namespace graphene { namespace peerplays_sidechain { +class btc_txout +{ +public: + std::string txid_; + unsigned int out_num_; + double amount_; +}; + class bitcoin_rpc_client { public: bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ; @@ -21,6 +29,9 @@ public: void send_btc_tx( const std::string& tx_hex ); std::string add_multisig_address( const std::vector public_keys ); bool connection_is_not_defined() const; + void import_address( const std::string& address_or_script); + std::vector list_unspent(); + std::string prepare_tx(const std::vector& ins, const fc::flat_map outs); private: diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index cdd3611e..40da9240 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -173,6 +173,120 @@ bool bitcoin_rpc_client::connection_is_not_defined() const return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); } +void bitcoin_rpc_client::import_address(const std::string &address_or_script) +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"importaddress\", \"params\": [") + + std::string("\"") + address_or_script + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + { + wlog("Failed to import address [${addr}]", ("addr", address_or_script)); + return; + } + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump((address_or_script)(reply_str)); + return; + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "Failed to import address [${addr}]! Reply: ${msg}", ("addr", address_or_script)("msg", reply_str) ); + } +} + +std::vector bitcoin_rpc_client::list_unspent() +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": [] }"); + + const auto reply = send_post_request( body ); + + std::vector result; + if( reply.body.empty() ) + { + wlog("Failed to list unspent txo"); + return result; + } + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump((reply_str)); + if( json.count( "result" ) ) + { + for(auto& entry: json.get_child("result")) + { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + txo.amount_ = entry.second.get_child("amount").get_value(); + result.push_back(txo); + } + } + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) ); + } + return result; +} + +std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, const fc::flat_map outs) +{ + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"createrawtransaction\", \"params\": ["); + body += "["; + bool first = true; + for(const auto& entry: ins) + { + if(!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":\"" + fc::to_string(entry.out_num_) + "\"}"; + first = false; + } + body += "]"; + first = true; + body += "{"; + for(const auto& entry: outs) + { + if(!first) + body += ","; + body += "\"" + entry.first + "\":\"" + fc::to_string(entry.second) + "\""; + first = false; + } + body += "}"; + body += std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + { + wlog("Failed to create raw transaction: [${body}]", ("body", body)); + return std::string(); + } + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump((reply_str)); + if( json.count( "result" ) ) + return json.get_child("result").get_value(); + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", reply_str) ); + } + return std::string(); +} + fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) { fc::http::connection conn; diff --git a/tests/peerplays_sidechain/bitcoin_utils_test.cpp b/tests/peerplays_sidechain/bitcoin_utils_test.cpp new file mode 100644 index 00000000..878149cc --- /dev/null +++ b/tests/peerplays_sidechain/bitcoin_utils_test.cpp @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include + +using namespace graphene::peerplays_sidechain; + +BOOST_AUTO_TEST_CASE(tx_serialization) +{ + // use real mainnet transaction + // txid: 6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315 + // json: {"txid":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","hash":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","version":1,"size":224,"vsize":224,"weight":896,"locktime":0,"vin":[{"txid":"55d079ca797fee81416b71b373abedd8722e33c9f73177be0166b5d5fdac478b","vout":0,"scriptSig":{"asm":"3045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253[ALL] 02be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4","hex":"483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4"},"sequence":4294967295}],"vout":[{"value":1.26491535,"n":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 95783804d28e528fbc4b48c7700471e6845804eb OP_EQUALVERIFY OP_CHECKSIG","hex":"76a91495783804d28e528fbc4b48c7700471e6845804eb88ac","reqSigs":1,"type":"pubkeyhash","addresses":["1EdKhXv7zjGowPzgDQ4z1wa2ukVrXRXXkP"]}},{"value":0.0002,"n":1,"scriptPubKey":{"asm":"OP_HASH160 fb0670971091da8248b5c900c6515727a20e8662 OP_EQUAL","hex":"a914fb0670971091da8248b5c900c6515727a20e866287","reqSigs":1,"type":"scripthash","addresses":["3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU"]}}]} + // hex: "01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000" + fc::string strtx("01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000"); + bytes bintx; + bintx.resize(strtx.length() / 2); + fc::from_hex(strtx, reinterpret_cast(&bintx[0]), bintx.size()); + btc_tx tx; + BOOST_CHECK_NO_THROW(tx.fill_from_bytes(bintx)); + BOOST_CHECK(tx.nVersion == 1); + BOOST_CHECK(tx.nLockTime == 0); + BOOST_CHECK(tx.vin.size() == 1); + BOOST_CHECK(tx.vout.size() == 2); + bytes buff; + tx.to_bytes(buff); + BOOST_CHECK(bintx == buff); +} + +BOOST_AUTO_TEST_CASE(pw_transfer) +{ + // key set for the old Primary Wallet + std::vector priv_old; + for(unsigned i = 0; i < 15; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_old.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + // print old keys + for(auto key: priv_old) + { + fc::sha256 secret = key.get_secret(); + bytes data({239}); + data.insert(data.end(), secret.data(), secret.data() + secret.data_size()); + fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size())); + data.insert(data.end(), cs.data(), cs.data() + 4); + } + std::vector pub_old; + for(auto& key: priv_old) + pub_old.push_back(key.get_public_key()); + // old key weights + std::vector > weights_old; + for(unsigned i = 0; i < 15; ++i) + weights_old.push_back(std::make_pair(pub_old[i], i + 1)); + // redeem script for old PW + bytes redeem_old =generate_redeem_script(weights_old); + + // Old PW address + std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet); + // This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766 + BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e"); + + bytes scriptPubKey = lock_script_for_redeem_script(redeem_old); + + // key set for the new Primary Wallet + std::vector priv_new; + for(unsigned i = 16; i < 31; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_new.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_new; + for(auto& key: priv_new) + pub_new.push_back(key.get_public_key()); + // new key weights + std::vector > weights_new; + for(unsigned i = 0; i < 15; ++i) + weights_new.push_back(std::make_pair(pub_new[i], 16 - i)); + // redeem script for new PW + bytes redeem_new =generate_redeem_script(weights_new); + // New PW address + std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet); + BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y"); + + // try to move funds from old wallet to new one + + // get unspent outputs for old wallet with list_uspent (address should be + // added to wallet with import_address before). It should return + // 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0] + // with 20000 satoshis + // So, we creating a raw transaction with 1 input and one output that gets + // 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx) + // Here we just serialize the transaction without scriptSig in inputs then sign it. + btc_outpoint outpoint; + outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766"); + // reverse hash due to the different from_hex algo + std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size()); + outpoint.n = 0; + btc_in input; + input.prevout = outpoint; + input.nSequence = 0xffffffff; + btc_out output; + output.nValue = 19000; + output.scriptPubKey = lock_script_for_redeem_script(redeem_new); + btc_tx tx; + tx.nVersion = 2; + tx.nLockTime = 0; + tx.hasWitness = false; + tx.vin.push_back(input); + tx.vout.push_back(output); + bytes unsigned_tx; + tx.to_bytes(unsigned_tx); + std::vector in_amounts({20000}); + std::vector> keys_to_sign; + for(auto key: priv_old) + keys_to_sign.push_back(fc::optional(key)); + bytes signed_tx =sign_pw_transfer_transaction(unsigned_tx, in_amounts, redeem_old, keys_to_sign); + // this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850 + BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000"); +} + +BOOST_AUTO_TEST_CASE(pw_separate_sign) +{ + // key set for the old Primary Wallet + std::vector priv_old; + for(unsigned i = 0; i < 15; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_old.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + // print old keys + for(auto key: priv_old) + { + fc::sha256 secret = key.get_secret(); + bytes data({239}); + data.insert(data.end(), secret.data(), secret.data() + secret.data_size()); + fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size())); + data.insert(data.end(), cs.data(), cs.data() + 4); + } + std::vector pub_old; + for(auto& key: priv_old) + pub_old.push_back(key.get_public_key()); + // old key weights + std::vector > weights_old; + for(unsigned i = 0; i < 15; ++i) + weights_old.push_back(std::make_pair(pub_old[i], i + 1)); + // redeem script for old PW + bytes redeem_old =generate_redeem_script(weights_old); + + // Old PW address + std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet); + // This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766 + BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e"); + + bytes scriptPubKey = lock_script_for_redeem_script(redeem_old); + + // key set for the new Primary Wallet + std::vector priv_new; + for(unsigned i = 16; i < 31; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_new.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_new; + for(auto& key: priv_new) + pub_new.push_back(key.get_public_key()); + // new key weights + std::vector > weights_new; + for(unsigned i = 0; i < 15; ++i) + weights_new.push_back(std::make_pair(pub_new[i], 16 - i)); + // redeem script for new PW + bytes redeem_new =generate_redeem_script(weights_new); + // New PW address + std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet); + BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y"); + + // try to move funds from old wallet to new one + + // get unspent outputs for old wallet with list_uspent (address should be + // added to wallet with import_address before). It should return + // 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0] + // with 20000 satoshis + // So, we creating a raw transaction with 1 input and one output that gets + // 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx) + // Here we just serialize the transaction without scriptSig in inputs then sign it. + btc_outpoint outpoint; + outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766"); + // reverse hash due to the different from_hex algo + std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size()); + outpoint.n = 0; + btc_in input; + input.prevout = outpoint; + input.nSequence = 0xffffffff; + btc_out output; + output.nValue = 19000; + output.scriptPubKey = lock_script_for_redeem_script(redeem_new); + btc_tx tx; + tx.nVersion = 2; + tx.nLockTime = 0; + tx.hasWitness = false; + tx.vin.push_back(input); + tx.vout.push_back(output); + bytes unsigned_tx; + tx.to_bytes(unsigned_tx); + std::vector in_amounts({20000}); + + // prepare tx with dummy signs + bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15); + + // sign with every old key one by one + for(unsigned idx = 0; idx < 15; idx++) + partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx); + + // now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850 + BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000"); +} + +BOOST_AUTO_TEST_CASE(pw_partially_sign) +{ + // key set for the old Primary Wallet + std::vector priv_old; + for(unsigned i = 0; i < 15; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_old.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + // print old keys + for(auto key: priv_old) + { + fc::sha256 secret = key.get_secret(); + bytes data({239}); + data.insert(data.end(), secret.data(), secret.data() + secret.data_size()); + fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size())); + data.insert(data.end(), cs.data(), cs.data() + 4); + } + std::vector pub_old; + for(auto& key: priv_old) + pub_old.push_back(key.get_public_key()); + // old key weights + std::vector > weights_old; + for(unsigned i = 0; i < 15; ++i) + weights_old.push_back(std::make_pair(pub_old[i], i + 1)); + // redeem script for old PW + bytes redeem_old =generate_redeem_script(weights_old); + + // Old PW address + std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet); + // This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766 + BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e"); + + bytes scriptPubKey = lock_script_for_redeem_script(redeem_old); + + // key set for the new Primary Wallet + std::vector priv_new; + for(unsigned i = 16; i < 31; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_new.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_new; + for(auto& key: priv_new) + pub_new.push_back(key.get_public_key()); + // new key weights + std::vector > weights_new; + for(unsigned i = 0; i < 15; ++i) + weights_new.push_back(std::make_pair(pub_new[i], 16 - i)); + // redeem script for new PW + bytes redeem_new =generate_redeem_script(weights_new); + // New PW address + std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet); + BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y"); + + // try to move funds from old wallet to new one + + // Spent 1 UTXO: [7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf:0] + // with 29999 satoshis + // So, we creating a raw transaction with 1 input and one output that gets + // 29999 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx) + btc_outpoint outpoint; + outpoint.hash = fc::uint256("7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf"); + // reverse hash due to the different from_hex algo + std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size()); + outpoint.n = 0; + btc_in input; + input.prevout = outpoint; + input.nSequence = 0xffffffff; + btc_out output; + output.nValue = 29000; + output.scriptPubKey = lock_script_for_redeem_script(redeem_new); + btc_tx tx; + tx.nVersion = 2; + tx.nLockTime = 0; + tx.hasWitness = false; + tx.vin.push_back(input); + tx.vout.push_back(output); + bytes unsigned_tx; + tx.to_bytes(unsigned_tx); + std::vector in_amounts({29999}); + + // prepare tx with dummy signs + bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15); + + // sign with every old key one by one except the first one + for(unsigned idx = 1; idx < 15; idx++) + partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx); + + // now this is real testnet tx with id e86455c40da6993b6fed70daea2046287b206ab5c16e1ab58c4dfb4a7d6efb84 + BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "02000000000101bf9f1f1bb2658214f45260c020ab080d9012a12a257926d097e05fcd7fb707700000000000ffffffff0148710000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10483045022100c4c567419754c5c1768e959a35633012e8d22ccc90d7cd1b88d6d430a513fbbd0220729c2a3520d0cae7dd6dcd928624ffa3e0b6ce0c4f5c340653a6c18549182588014830450221008c868ea2cdf5b23bdf9e6c7d7c283b8424aeb4aec43621424baef1ee77dd399a02205431f608006f0f0dcd392fab4f25328808b45d4a73852a197e947b289faefece01483045022100aecac85bbb81bc0a4e127c15090c5ab82a62b9e27a9a6eb8eddf8de294aa9d920220482f2ba8d7b62e9f3f7a68b0ef3236bc56e44481d3eb59f62d1daf4b191dc86001483045022100eb27943f8b511a36b1a843f9b3ddf6930aece5a3c0be697dbafc921924fc049c022065ba3e1e4ad57f56337143136c5d3ee3f56dd60f36e798f07b5646e29343d7320147304402206e24158484ebb2cd14b9c410ecd04841d806d8464ce9a827533484c8ad8d921b022021baec9cd0ad46e7b19c8de7df286093b835df5c6243e90b14f5748dc1b7c13901473044022067bfaf0e39d72e49a081d4e43828746ab7524c4764e445173dd96cc7e6187d46022063ef107375cc45d1c26b1e1c87b97694f71645187ad871db9c05b8e981a0da8601483045022100da0162de3e4a5268b616b9d01a1a4f64b0c371c6b44fb1f740a264455f2bc20d02203a0b45a98a341722ad65ae4ad68538d617b1cfbb229751f875615317eaf15dd4014830450221008220c4f97585e67966d4435ad8497eb89945f13dd8ff24048b830582349041a002204cb03f7271895637a31ce6479d15672c2d70528148e3cd6196e6f722117745c50147304402203e83ab4b15bb0680f82779335acf9a3ce45316150a4538d5e3d25cb863fcec5702204b3913874077ed2cae4e10f8786053b6f157973a54d156d5863f13accca595f50147304402201420d2a2830278ffff5842ecb7173a23642f179435443e780b3d1fe04be5c32e02203818202390e0e63b4309b89f9cce08c0f4dfa539c2ed59b05e24325671e2747c0147304402205624ca9d47ae04afd8fff705706d6853f8c679abb385f19e01c36f9380a0bad602203dc817fc55497e4c1759a3dbfff1662faca593a9f10d3a9b3e24d5ee3165d4400147304402203a959f9a34587c56b86826e6ab65644ab19cbd09ca078459eb59956b02bc753002206df5ded568d0e3e3645f8cb8ca02874dd1bfa82933eb5e01ff2e5a773633e51601483045022100a84ed5be60b9e095d40f3f6bd698425cb9c4d8f95e8b43ca6c5120a6c599e9eb022064c703952d18d753f9198d78188a26888e6b06c832d93f8075311d57a13240160147304402202e71d3af33a18397b90072098881fdbdb8d6e4ffa34d21141212dd815c97d00f02207195f1c06a8f44ca72af15fdaba89b07cf6daef9be981c432b9f5c10f1e374200100fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000"); +} From a968ec922cc42d5b7f2e2c6f4a48330a220d67aa Mon Sep 17 00:00:00 2001 From: gladcow Date: Fri, 21 Feb 2020 16:32:06 +0300 Subject: [PATCH 37/64] add ability to gather signatures before signing (#290) --- .../peerplays_sidechain/bitcoin_utils.cpp | 31 +++++- .../peerplays_sidechain/bitcoin_utils.hpp | 32 ++++++ .../bitcoin_utils_test.cpp | 102 ++++++++++++++++++ 3 files changed, 162 insertions(+), 3 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp index 8ed3021a..23339afb 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp @@ -586,7 +586,7 @@ bytes der_sign(const fc::ecc::private_key& priv_key, const fc::sha256& digest) return bytes(result.begin(), result.begin() + size); } -std::vector signature_for_raw_transaction(const bytes& unsigned_tx, +std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, std::vector in_amounts, const bytes& redeem_script, const fc::ecc::private_key& priv_key) @@ -645,7 +645,7 @@ bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector signatures = signature_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key); + std::vector signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key); FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); // push signatures in reverse order because script starts to check the top signature on the stack first for(unsigned int i = 0; i < tx.vin.size(); i++) @@ -699,7 +699,7 @@ bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, tx.fill_from_bytes(partially_signed_tx); FC_ASSERT(tx.vin.size() > 0); bytes redeem_script = tx.vin[0].scriptWitness.back(); - std::vector signatures = signature_for_raw_transaction(partially_signed_tx, in_amounts, redeem_script, priv_key); + std::vector signatures = signatures_for_raw_transaction(partially_signed_tx, in_amounts, redeem_script, priv_key); FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); // push signatures in reverse order because script starts to check the top signature on the stack first unsigned witness_idx = tx.vin[0].scriptWitness.size() - 2 - key_idx; @@ -710,4 +710,29 @@ bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, return ret; } +bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector > &signature_set, const bytes &redeem_script) +{ + btc_tx tx; + tx.fill_from_bytes(unsigned_tx); + bytes dummy_data; + for(unsigned int i = 0; i < signature_set.size(); i++) + { + std::vector signatures = signature_set[i]; + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + // push signatures in reverse order because script starts to check the top signature on the stack first + for(unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); + } + + for(auto& in: tx.vin) + { + in.scriptWitness.push_back(redeem_script); + } + + tx.hasWitness = true; + bytes ret; + tx.to_bytes(ret); + return ret; +} + }} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp index 718bdd95..d2830496 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp @@ -15,6 +15,11 @@ std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_networ bytes lock_script_for_redeem_script(const bytes& script); +std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, + std::vector in_amounts, + const bytes& redeem_script, + const fc::ecc::private_key& priv_key); + /* * unsigned_tx - tx, all inputs of which are spends of the PW P2SH address * returns signed transaction @@ -24,15 +29,42 @@ bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, const bytes& redeem_script, const std::vector>& priv_keys); +/// +////// \brief Adds dummy signatures instead of real signatures +////// \param unsigned_tx +////// \param redeem_script +////// \param key_count +////// \return can be used as partially signed tx bytes add_dummy_signatures_for_pw_transfer(const bytes& unsigned_tx, const bytes& redeem_script, unsigned int key_count); +/// +/// \brief replaces dummy sgnatures in partially signed tx with real tx +/// \param partially_signed_tx +/// \param in_amounts +/// \param priv_key +/// \param key_idx +/// \return +/// bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, std::vector in_amounts, const fc::ecc::private_key& priv_key, unsigned int key_idx); +/// +/// \brief Creates ready to publish bitcoin transaction from unsigned tx and +/// full set of the signatures. This is alternative way to create tx +/// with partially_sign_pw_transfer_transaction +/// \param unsigned_tx +/// \param signatures +/// \param redeem_script +/// \return +/// +bytes add_signatures_to_unsigned_tx(const bytes& unsigned_tx, + const std::vector >& signatures, + const bytes& redeem_script); + struct btc_outpoint { fc::uint256 hash; diff --git a/tests/peerplays_sidechain/bitcoin_utils_test.cpp b/tests/peerplays_sidechain/bitcoin_utils_test.cpp index 878149cc..c0e6e7c1 100644 --- a/tests/peerplays_sidechain/bitcoin_utils_test.cpp +++ b/tests/peerplays_sidechain/bitcoin_utils_test.cpp @@ -220,6 +220,108 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign) BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000"); } +BOOST_AUTO_TEST_CASE(pw_separate_sign2) +{ + // key set for the old Primary Wallet + std::vector priv_old; + for(unsigned i = 0; i < 15; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_old.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + // print old keys + for(auto key: priv_old) + { + fc::sha256 secret = key.get_secret(); + bytes data({239}); + data.insert(data.end(), secret.data(), secret.data() + secret.data_size()); + fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size())); + data.insert(data.end(), cs.data(), cs.data() + 4); + } + std::vector pub_old; + for(auto& key: priv_old) + pub_old.push_back(key.get_public_key()); + // old key weights + std::vector > weights_old; + for(unsigned i = 0; i < 15; ++i) + weights_old.push_back(std::make_pair(pub_old[i], i + 1)); + // redeem script for old PW + bytes redeem_old =generate_redeem_script(weights_old); + + // Old PW address + std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet); + // This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766 + BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e"); + + bytes scriptPubKey = lock_script_for_redeem_script(redeem_old); + + // key set for the new Primary Wallet + std::vector priv_new; + for(unsigned i = 16; i < 31; ++i) + { + const char* seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_new.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_new; + for(auto& key: priv_new) + pub_new.push_back(key.get_public_key()); + // new key weights + std::vector > weights_new; + for(unsigned i = 0; i < 15; ++i) + weights_new.push_back(std::make_pair(pub_new[i], 16 - i)); + // redeem script for new PW + bytes redeem_new =generate_redeem_script(weights_new); + // New PW address + std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet); + BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y"); + + // try to move funds from old wallet to new one + + // get unspent outputs for old wallet with list_uspent (address should be + // added to wallet with import_address before). It should return + // 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0] + // with 20000 satoshis + // So, we creating a raw transaction with 1 input and one output that gets + // 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx) + // Here we just serialize the transaction without scriptSig in inputs then sign it. + btc_outpoint outpoint; + outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766"); + // reverse hash due to the different from_hex algo + std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size()); + outpoint.n = 0; + btc_in input; + input.prevout = outpoint; + input.nSequence = 0xffffffff; + btc_out output; + output.nValue = 19000; + output.scriptPubKey = lock_script_for_redeem_script(redeem_new); + btc_tx tx; + tx.nVersion = 2; + tx.nLockTime = 0; + tx.hasWitness = false; + tx.vin.push_back(input); + tx.vout.push_back(output); + bytes unsigned_tx; + tx.to_bytes(unsigned_tx); + std::vector in_amounts({20000}); + + // gather all signatures from all SONs separatelly + std::vector > signature_set; + for(auto key: priv_old) + { + std::vector signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_old, key); + signature_set.push_back(signatures); + } + + // create signed tx with all signatures + bytes signed_tx = add_signatures_to_unsigned_tx(unsigned_tx, signature_set, redeem_old); + + // now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850 + BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000"); +} + BOOST_AUTO_TEST_CASE(pw_partially_sign) { // key set for the old Primary Wallet From a9cfadc500dd2c4547bf54818f998b9b88b482f5 Mon Sep 17 00:00:00 2001 From: gladcow Date: Sun, 23 Feb 2020 19:31:51 +0300 Subject: [PATCH 38/64] [SON-242] fix list_active_sons call after deleting an active son (#292) * test to reproduce error in list_active_sons after delete_son * prevent exception in list_active_list --- libraries/wallet/wallet.cpp | 21 ++++++++++++--------- tests/cli/son.cpp | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 194254be..943b6f1c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1988,19 +1988,22 @@ public: }); std::vector> son_objects = _remote_db->get_sons(son_ids); vector owners; - owners.resize(son_objects.size()); - std::transform(son_objects.begin(), son_objects.end(), owners.begin(), - [](const fc::optional& obj) { - FC_ASSERT(obj, "Invalid active SONs list in global properties."); - return obj->son_account; - }); + for(auto obj: son_objects) + { + if (obj) + owners.push_back(obj->son_account); + } vector> accs = _remote_db->get_accounts(owners); + std::remove_if(son_objects.begin(), son_objects.end(), + [](const fc::optional& obj) -> bool { return obj.valid(); }); map result; - std::transform(accs.begin(), accs.end(), son_ids.begin(), + std::transform(accs.begin(), accs.end(), son_objects.begin(), std::inserter(result, result.end()), - [](fc::optional& acct, son_id_type& sid) { + [](fc::optional& acct, fc::optional son) { FC_ASSERT(acct, "Invalid active SONs list in global properties."); - return std::make_pair(string(acct->name), std::move(sid)); + if (son.valid()) + return std::make_pair(string(acct->name), std::move(son->id)); + return std::make_pair(string(acct->name), std::move(son_id_type())); }); return result; } FC_CAPTURE_AND_RETHROW() } diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index d71ac4c8..7915c71e 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -650,6 +650,9 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) BOOST_CHECK(active_sons.find(name) != active_sons.end()); } + // check list_active_son after SON deletion + con.wallet_api_ptr->delete_son("sonaccount1", true); + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->list_active_sons()); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); From 13d2b27ed98c3a920b17f277431b6fe30aa620b4 Mon Sep 17 00:00:00 2001 From: obucinac Date: Sun, 23 Feb 2020 18:33:43 +0200 Subject: [PATCH 39/64] [SON-260] Sidechain Token withdrawal (#286) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Skeleton of sidechain_net_handler_peerplays * Skeleton of Peerplays network listener * Temoprary disable account history tests for tracking accounts * Full Peerplays listener, use GRAPHENE_SON_ACCOUNT instead son_btc_account * Renaming son_wallet_transfer* to son_wallet_deposit*, introducing son_wallet_withdrawal* * Extend sidechain_address_object to contain withdrawal addresses - Withdrawal address is the address where system will send sidechain currencies * Rename son_wallet_withdrawal* to son_wallet_withdraw* * Some refactoring * Withdrawal refactoring * Withdrawal refactoring Co-authored-by: gladcow --- libraries/app/impacted.cpp | 10 +- libraries/chain/CMakeLists.txt | 3 +- libraries/chain/db_init.cpp | 15 +- libraries/chain/db_maint.cpp | 85 +------ libraries/chain/db_notify.cpp | 14 +- .../chain/protocol/chain_parameters.hpp | 5 - .../graphene/chain/protocol/operations.hpp | 9 +- .../chain/protocol/sidechain_address.hpp | 14 +- ...et_transfer.hpp => son_wallet_deposit.hpp} | 25 +- .../chain/protocol/son_wallet_withdraw.hpp | 50 ++++ .../include/graphene/chain/protocol/types.hpp | 15 +- .../chain/sidechain_address_object.hpp | 18 +- .../chain/son_wallet_deposit_evaluator.hpp | 24 ++ ...ject.hpp => son_wallet_deposit_object.hpp} | 37 +-- .../chain/son_wallet_transfer_evaluator.hpp | 24 -- .../chain/son_wallet_withdraw_evaluator.hpp | 24 ++ .../chain/son_wallet_withdraw_object.hpp | 67 +++++ .../include/graphene/chain/vote_count.hpp | 11 + .../chain/sidechain_address_evaluator.cpp | 10 +- libraries/chain/son_evaluator.cpp | 2 +- ...r.cpp => son_wallet_deposit_evaluator.cpp} | 60 +++-- libraries/chain/son_wallet_evaluator.cpp | 6 +- .../chain/son_wallet_withdraw_evaluator.cpp | 70 ++++++ .../peerplays_sidechain/CMakeLists.txt | 1 + .../graphene/peerplays_sidechain/defs.hpp | 6 +- .../sidechain_net_handler.hpp | 9 +- .../sidechain_net_handler_bitcoin.hpp | 7 +- .../sidechain_net_handler_peerplays.hpp | 34 +++ .../sidechain_net_manager.hpp | 2 + .../peerplays_sidechain_plugin.cpp | 89 ++++--- .../sidechain_net_handler.cpp | 229 ++++++++++++++---- .../sidechain_net_handler_bitcoin.cpp | 43 ++-- .../sidechain_net_handler_peerplays.cpp | 99 ++++++++ .../sidechain_net_manager.cpp | 19 ++ .../wallet/include/graphene/wallet/wallet.hpp | 20 +- libraries/wallet/wallet.cpp | 34 ++- programs/js_operation_serializer/main.cpp | 3 +- tests/tests/history_api_tests.cpp | 0 tests/tests/sidechain_addresses_test.cpp | 25 +- tests/tests/son_operations_tests.cpp | 33 +-- tests/tests/son_wallet_tests.cpp | 4 +- 41 files changed, 843 insertions(+), 412 deletions(-) mode change 100644 => 100755 libraries/chain/CMakeLists.txt rename libraries/chain/include/graphene/chain/protocol/{son_wallet_transfer.hpp => son_wallet_deposit.hpp} (57%) create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp rename libraries/chain/include/graphene/chain/{son_wallet_transfer_object.hpp => son_wallet_deposit_object.hpp} (52%) delete mode 100644 libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp rename libraries/chain/{son_wallet_transfer_evaluator.cpp => son_wallet_deposit_evaluator.cpp} (54%) create mode 100644 libraries/chain/son_wallet_withdraw_evaluator.cpp mode change 100644 => 100755 libraries/plugins/peerplays_sidechain/CMakeLists.txt create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp mode change 100755 => 100644 tests/tests/history_api_tests.cpp diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index d3e8eb8c..e0de1d05 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -322,10 +322,16 @@ struct get_impacted_account_visitor void operator()( const son_wallet_update_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const son_wallet_transfer_create_operation& op ){ + void operator()( const son_wallet_deposit_create_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const son_wallet_transfer_process_operation& op ){ + void operator()( const son_wallet_deposit_process_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_create_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_process_operation& op ){ _impacted.insert( op.payer ); } void operator()( const sidechain_address_add_operation& op ){ diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt old mode 100644 new mode 100755 index c7dd5375..85d5a91b --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -118,7 +118,8 @@ add_library( graphene_chain son_object.cpp son_wallet_evaluator.cpp - son_wallet_transfer_evaluator.cpp + son_wallet_deposit_evaluator.cpp + son_wallet_withdraw_evaluator.cpp sidechain_address_evaluator.cpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 5c72d1bc..0be37c42 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -57,7 +57,8 @@ #include #include #include -#include +#include +#include #include #include @@ -82,7 +83,8 @@ #include #include #include -#include +#include +#include #include #include @@ -258,8 +260,10 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); - register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -308,7 +312,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 9469bbce..3c1685b3 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -457,8 +457,9 @@ void database::update_active_sons() a.active.account_auths[weight.first] += votes; a.active.weight_threshold += votes; } - - a.active.weight_threshold /= 2; + + a.active.weight_threshold *= 2; + a.active.weight_threshold /= 3; a.active.weight_threshold += 1; } else @@ -466,12 +467,11 @@ void database::update_active_sons() vote_counter vc; for( const son_object& son : sons ) vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) ); - vc.finish( a.active ); + vc.finish_2_3( a.active ); } } ); // Compare current and to-be lists of active sons - //const global_property_object& gpo = get_global_properties(); auto cur_active_sons = gpo.active_sons; vector new_active_sons; for( const son_object& son : sons ) { @@ -602,83 +602,6 @@ void database::update_active_sons() update_son_metrics(); - if(gpo.active_sons.size() > 0 ) { - if(gpo.parameters.get_son_btc_account_id() == GRAPHENE_NULL_ACCOUNT) { - const auto& son_btc_account = create( [&]( account_object& obj ) { - uint64_t total_votes = 0; - obj.name = "son_btc_account"; - obj.statistics = create([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id; - obj.membership_expiration_date = time_point_sec::maximum(); - obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - - for( const auto& son_info : gpo.active_sons ) - { - const son_object& son = get(son_info.son_id); - total_votes += _vote_tally_buffer[son.vote_id]; - } - // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, - // then I want to keep the most significant 16 bits of what's left. - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - - for( const auto& son_info : gpo.active_sons ) - { - // Ensure that everyone has at least one vote. Zero weights aren't allowed. - const son_object& son = get(son_info.son_id); - uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); - obj.owner.account_auths[son.son_account] += votes; - obj.owner.weight_threshold += votes; - obj.active.account_auths[son.son_account] += votes; - obj.active.weight_threshold += votes; - } - obj.owner.weight_threshold *= 2; - obj.owner.weight_threshold /= 3; - obj.owner.weight_threshold += 1; - obj.active.weight_threshold *= 2; - obj.active.weight_threshold /= 3; - obj.active.weight_threshold += 1; - }); - - modify( gpo, [&]( global_property_object& gpo ) { - gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); - if( gpo.pending_parameters ) - gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); - }); - } else { - modify( get(gpo.parameters.get_son_btc_account_id()), [&]( account_object& obj ) - { - uint64_t total_votes = 0; - obj.owner.weight_threshold = 0; - obj.owner.account_auths.clear(); - obj.active.weight_threshold = 0; - obj.active.account_auths.clear(); - for( const auto& son_info : gpo.active_sons ) - { - const son_object& son = get(son_info.son_id); - total_votes += _vote_tally_buffer[son.vote_id]; - } - // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, - // then I want to keep the most significant 16 bits of what's left. - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& son_info : gpo.active_sons ) - { - // Ensure that everyone has at least one vote. Zero weights aren't allowed. - const son_object& son = get(son_info.son_id); - uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) ); - obj.owner.account_auths[son.son_account] += votes; - obj.owner.weight_threshold += votes; - obj.active.account_auths[son.son_account] += votes; - obj.active.weight_threshold += votes; - } - obj.owner.weight_threshold *= 2; - obj.owner.weight_threshold /= 3; - obj.owner.weight_threshold += 1; - obj.active.weight_threshold *= 2; - obj.active.weight_threshold /= 3; - obj.active.weight_threshold += 1; - }); - } - } } FC_CAPTURE_AND_RETHROW() } void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 98e02a1b..1b0b5158 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -309,10 +309,16 @@ struct get_impacted_account_visitor void operator()( const son_wallet_update_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const son_wallet_transfer_create_operation& op ) { + void operator()( const son_wallet_deposit_create_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const son_wallet_transfer_process_operation& op ) { + void operator()( const son_wallet_deposit_process_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_create_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_process_operation& op ) { _impacted.insert( op.payer ); } void operator()( const sidechain_address_add_operation& op ) { @@ -419,7 +425,9 @@ void get_relevant_accounts( const object* obj, flat_set& accoun break; } case son_wallet_object_type:{ break; - } case son_wallet_transfer_object_type:{ + } case son_wallet_deposit_object_type:{ + break; + } case son_wallet_withdraw_object_type:{ break; } case sidechain_address_object_type:{ const auto& aobj = dynamic_cast(obj); diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 51024e16..c62274ba 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -46,7 +46,6 @@ namespace graphene { namespace chain { optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; - optional < account_id_type > son_btc_account; }; struct chain_parameters @@ -139,9 +138,6 @@ namespace graphene { namespace chain { inline uint16_t son_pay_daily_max()const { return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX; } - inline account_id_type get_son_btc_account_id() const { - return extensions.value.son_btc_account.valid() ? *extensions.value.son_btc_account : GRAPHENE_NULL_ACCOUNT; - } }; } } // graphene::chain @@ -159,7 +155,6 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) - (son_btc_account) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 27980ae2..37eccf80 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -48,7 +48,8 @@ #include #include #include -#include +#include +#include namespace graphene { namespace chain { @@ -148,8 +149,10 @@ namespace graphene { namespace chain { son_maintenance_operation, son_wallet_recreate_operation, son_wallet_update_operation, - son_wallet_transfer_create_operation, - son_wallet_transfer_process_operation, + son_wallet_deposit_create_operation, + son_wallet_deposit_process_operation, + son_wallet_withdraw_create_operation, + son_wallet_withdraw_process_operation, sidechain_address_add_operation, sidechain_address_update_operation, sidechain_address_delete_operation diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp index d0e658b4..7418f55e 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp @@ -12,9 +12,8 @@ namespace graphene { namespace chain { asset fee; account_id_type sidechain_address_account; graphene::peerplays_sidechain::sidechain_type sidechain; - string address; - string private_key; - string public_key; + string deposit_address; + string withdraw_address; account_id_type fee_payer()const { return sidechain_address_account; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } @@ -28,9 +27,8 @@ namespace graphene { namespace chain { sidechain_address_id_type sidechain_address_id; account_id_type sidechain_address_account; graphene::peerplays_sidechain::sidechain_type sidechain; - optional address; - optional private_key; - optional public_key; + optional deposit_address; + optional withdraw_address; account_id_type fee_payer()const { return sidechain_address_account; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } @@ -53,12 +51,12 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee) - (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) + (sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) ) FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee) (sidechain_address_id) - (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) + (sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) ) FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp similarity index 57% rename from libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp rename to libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp index 38f8dac6..abcc4384 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_transfer.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp @@ -1,9 +1,11 @@ #pragma once #include +#include + namespace graphene { namespace chain { - struct son_wallet_transfer_create_operation : public base_operation + struct son_wallet_deposit_create_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; @@ -16,23 +18,24 @@ namespace graphene { namespace chain { std::string sidechain_transaction_id; std::string sidechain_from; std::string sidechain_to; - int64_t sidechain_amount; + std::string sidechain_currency; + fc::safe sidechain_amount; chain::account_id_type peerplays_from; chain::account_id_type peerplays_to; - chain::asset peerplays_amount; + chain::asset peerplays_asset; account_id_type fee_payer()const { return payer; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; - struct son_wallet_transfer_process_operation : public base_operation + struct son_wallet_deposit_process_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; asset fee; account_id_type payer; - son_wallet_transfer_id_type son_wallet_transfer_id; + son_wallet_deposit_id_type son_wallet_deposit_id; account_id_type fee_payer()const { return payer; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } @@ -40,9 +43,9 @@ namespace graphene { namespace chain { } } // namespace graphene::chain -FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_wallet_transfer_create_operation, (fee)(payer) - (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_amount)) -FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_wallet_transfer_process_operation, (fee)(payer) - (son_wallet_transfer_id)) +FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer) + (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset)) +FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer) + (son_wallet_deposit_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp new file mode 100644 index 00000000..99c263be --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp @@ -0,0 +1,50 @@ +#pragma once +#include + +#include + +namespace graphene { namespace chain { + + struct son_wallet_withdraw_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + fc::time_point_sec timestamp; + peerplays_sidechain::sidechain_type sidechain; + std::string peerplays_uid; + std::string peerplays_transaction_id; + chain::account_id_type peerplays_from; + chain::asset peerplays_asset; + peerplays_sidechain::sidechain_type withdraw_sidechain; + std::string withdraw_address; + std::string withdraw_currency; + safe withdraw_amount; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_withdraw_process_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_withdraw_id_type son_wallet_withdraw_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer) + (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) +FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer) + (son_wallet_withdraw_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index c25c465c..ac6ec067 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -148,7 +148,8 @@ namespace graphene { namespace chain { son_object_type, son_proposal_object_type, son_wallet_object_type, - son_wallet_transfer_object_type, + son_wallet_deposit_object_type, + son_wallet_withdraw_object_type, sidechain_address_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -214,7 +215,8 @@ namespace graphene { namespace chain { class son_object; class son_proposal_object; class son_wallet_object; - class son_wallet_transfer_object; + class son_wallet_deposit_object; + class son_wallet_withdraw_object; class sidechain_address_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; @@ -245,7 +247,8 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type; - typedef object_id< protocol_ids, son_wallet_transfer_object_type, son_wallet_transfer_object> son_wallet_transfer_id_type; + typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type; + typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; // implementation types @@ -434,7 +437,8 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (son_object_type) (son_proposal_object_type) (son_wallet_object_type) - (son_wallet_transfer_object_type) + (son_wallet_deposit_object_type) + (son_wallet_withdraw_object_type) (sidechain_address_object_type) (OBJECT_TYPE_COUNT) ) @@ -510,7 +514,8 @@ FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::son_wallet_transfer_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index 8c77fad2..1a8b6967 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -21,21 +21,19 @@ namespace graphene { namespace chain { account_id_type sidechain_address_account; graphene::peerplays_sidechain::sidechain_type sidechain; - string address; - string private_key; - string public_key; + string deposit_address; + string withdraw_address; sidechain_address_object() : sidechain(graphene::peerplays_sidechain::sidechain_type::bitcoin), - address(""), - private_key(""), - public_key("") {} + deposit_address(""), + withdraw_address("") {} }; struct by_account; struct by_sidechain; struct by_account_and_sidechain; - struct by_sidechain_and_address; + struct by_sidechain_and_deposit_address; using sidechain_address_multi_index_type = multi_index_container< sidechain_address_object, indexed_by< @@ -54,10 +52,10 @@ namespace graphene { namespace chain { member > >, - ordered_unique< tag, + ordered_unique< tag, composite_key, - member + member > > > @@ -67,4 +65,4 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object), - (sidechain_address_account)(sidechain)(address)(private_key)(public_key) ) + (sidechain_address_account) (sidechain) (deposit_address) (withdraw_address) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp new file mode 100644 index 00000000..f3780c1f --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace graphene { namespace chain { + +class create_son_wallet_deposit_evaluator : public evaluator +{ +public: + typedef son_wallet_deposit_create_operation operation_type; + + void_result do_evaluate(const son_wallet_deposit_create_operation& o); + object_id_type do_apply(const son_wallet_deposit_create_operation& o); +}; + +class process_son_wallet_deposit_evaluator : public evaluator +{ +public: + typedef son_wallet_deposit_process_operation operation_type; + + void_result do_evaluate(const son_wallet_deposit_process_operation& o); + object_id_type do_apply(const son_wallet_deposit_process_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp similarity index 52% rename from libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp rename to libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp index 68293784..668af1d1 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_transfer_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -6,15 +6,15 @@ namespace graphene { namespace chain { using namespace graphene::db; /** - * @class son_wallet_transfer_object - * @brief tracks information about a SON wallet transfer. + * @class son_wallet_deposit_object + * @brief tracks information about a SON wallet deposit. * @ingroup object */ - class son_wallet_transfer_object : public abstract_object + class son_wallet_deposit_object : public abstract_object { public: static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = son_wallet_transfer_object_type; + static const uint8_t type_id = son_wallet_deposit_object_type; time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; @@ -23,10 +23,11 @@ namespace graphene { namespace chain { std::string sidechain_transaction_id; std::string sidechain_from; std::string sidechain_to; - int64_t sidechain_amount; + std::string sidechain_currency; + safe sidechain_amount; chain::account_id_type peerplays_from; chain::account_id_type peerplays_to; - chain::asset peerplays_amount; + chain::asset peerplays_asset; bool processed; }; @@ -35,34 +36,34 @@ namespace graphene { namespace chain { struct by_sidechain_uid; struct by_processed; struct by_sidechain_and_processed; - using son_wallet_transfer_multi_index_type = multi_index_container< - son_wallet_transfer_object, + using son_wallet_deposit_multi_index_type = multi_index_container< + son_wallet_deposit_object, indexed_by< ordered_unique< tag, member >, ordered_non_unique< tag, - member + member >, ordered_unique< tag, - member + member >, ordered_non_unique< tag, - member + member >, ordered_non_unique< tag, - composite_key, - member + composite_key, + member > > > >; - using son_wallet_transfer_index = generic_index; + using son_wallet_deposit_index = generic_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::son_wallet_transfer_object, (graphene::db::object), +FC_REFLECT_DERIVED( graphene::chain::son_wallet_deposit_object, (graphene::db::object), (timestamp) (sidechain) (confirmations) - (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_amount) - (peerplays_from) (peerplays_to) (peerplays_amount) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) + (peerplays_from) (peerplays_to) (peerplays_asset) (processed) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp deleted file mode 100644 index 68fd0ad5..00000000 --- a/libraries/chain/include/graphene/chain/son_wallet_transfer_evaluator.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -namespace graphene { namespace chain { - -class create_son_wallet_transfer_evaluator : public evaluator -{ -public: - typedef son_wallet_transfer_create_operation operation_type; - - void_result do_evaluate(const son_wallet_transfer_create_operation& o); - object_id_type do_apply(const son_wallet_transfer_create_operation& o); -}; - -class process_son_wallet_transfer_evaluator : public evaluator -{ -public: - typedef son_wallet_transfer_process_operation operation_type; - - void_result do_evaluate(const son_wallet_transfer_process_operation& o); - object_id_type do_apply(const son_wallet_transfer_process_operation& o); -}; - -} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp new file mode 100644 index 00000000..f5c08cd3 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace graphene { namespace chain { + +class create_son_wallet_withdraw_evaluator : public evaluator +{ +public: + typedef son_wallet_withdraw_create_operation operation_type; + + void_result do_evaluate(const son_wallet_withdraw_create_operation& o); + object_id_type do_apply(const son_wallet_withdraw_create_operation& o); +}; + +class process_son_wallet_withdraw_evaluator : public evaluator +{ +public: + typedef son_wallet_withdraw_process_operation operation_type; + + void_result do_evaluate(const son_wallet_withdraw_process_operation& o); + object_id_type do_apply(const son_wallet_withdraw_process_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp new file mode 100644 index 00000000..68e870e0 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_withdraw_object + * @brief tracks information about a SON wallet withdrawal. + * @ingroup object + */ + class son_wallet_withdraw_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_withdraw_object_type; + + time_point_sec timestamp; + peerplays_sidechain::sidechain_type sidechain; + int64_t confirmations; + std::string peerplays_uid; + std::string peerplays_transaction_id; + chain::account_id_type peerplays_from; + chain::asset peerplays_asset; + peerplays_sidechain::sidechain_type withdraw_sidechain; + std::string withdraw_address; + std::string withdraw_currency; + safe withdraw_amount; + bool processed; + }; + + struct by_peerplays_uid; + struct by_withdraw_sidechain; + struct by_processed; + struct by_withdraw_sidechain_and_processed; + using son_wallet_withdraw_multi_index_type = multi_index_container< + son_wallet_withdraw_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member + > + > + > + >; + using son_wallet_withdraw_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_withdraw_object, (graphene::db::object), + (timestamp) (sidechain) (confirmations) + (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) + (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) + (processed) ) diff --git a/libraries/chain/include/graphene/chain/vote_count.hpp b/libraries/chain/include/graphene/chain/vote_count.hpp index f76a784d..ab2f3612 100644 --- a/libraries/chain/include/graphene/chain/vote_count.hpp +++ b/libraries/chain/include/graphene/chain/vote_count.hpp @@ -63,6 +63,17 @@ struct vote_counter out_auth = auth; } + void finish_2_3( authority& out_auth ) + { + if( total_votes == 0 ) + return; + assert( total_votes <= std::numeric_limits::max() ); + uint32_t weight = uint32_t( total_votes ); + weight = (weight * 2 / 3) + 1; + auth.weight_threshold = weight; + out_auth = auth; + } + bool is_empty()const { return (total_votes == 0); diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp index d79d4cb1..5382195d 100644 --- a/libraries/chain/sidechain_address_evaluator.cpp +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -19,9 +19,8 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address const auto& new_sidechain_address_object = db().create( [&]( sidechain_address_object& obj ){ obj.sidechain_address_account = op.sidechain_address_account; obj.sidechain = op.sidechain; - obj.address = op.address; - obj.private_key = op.private_key; - obj.public_key = op.public_key; + obj.deposit_address = op.deposit_address; + obj.withdraw_address = op.withdraw_address; }); return new_sidechain_address_object.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -41,9 +40,8 @@ object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_addr if(itr != idx.end()) { db().modify(*itr, [&op](sidechain_address_object &sao) { - if(op.address.valid()) sao.address = *op.address; - if(op.private_key.valid()) sao.private_key = *op.private_key; - if(op.public_key.valid()) sao.public_key = *op.public_key; + if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address; + if(op.withdraw_address.valid()) sao.withdraw_address = *op.withdraw_address; }); } return op.sidechain_address_id; diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index bcdda1ba..0adfa778 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -166,7 +166,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account"); + FC_ASSERT(op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer."); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); auto itr = idx.find(op.son_id); diff --git a/libraries/chain/son_wallet_transfer_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp similarity index 54% rename from libraries/chain/son_wallet_transfer_evaluator.cpp rename to libraries/chain/son_wallet_deposit_evaluator.cpp index 6245efa8..2f5a4f6b 100644 --- a/libraries/chain/son_wallet_transfer_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -1,28 +1,27 @@ -#include +#include #include #include -#include +#include namespace graphene { namespace chain { -void_result create_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_create_operation& op) +void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_create_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); - //const auto& idx = db().get_index_type().indices().get(); + //const auto& idx = db().get_index_type().indices().get(); //FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } -object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_create_operation& op) +object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_create_operation& op) { try { - const auto& idx = db().get_index_type().indices().get(); + const auto& idx = db().get_index_type().indices().get(); auto itr = idx.find(op.sidechain_uid); if (itr == idx.end()) { - const auto& new_son_wallet_transfer_object = db().create( [&]( son_wallet_transfer_object& swto ){ + const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swto ){ swto.timestamp = op.timestamp; swto.sidechain = op.sidechain; swto.confirmations = 1; @@ -33,26 +32,25 @@ object_id_type create_son_wallet_transfer_evaluator::do_apply(const son_wallet_t swto.sidechain_amount = op.sidechain_amount; swto.peerplays_from = op.peerplays_from; swto.peerplays_to = op.peerplays_to; - swto.peerplays_amount = op.peerplays_amount; + swto.peerplays_asset = op.peerplays_asset; swto.processed = false; }); - return new_son_wallet_transfer_object.id; + return new_son_wallet_deposit_object.id; } else { - db().modify(*itr, [&op](son_wallet_transfer_object &swto) { + db().modify(*itr, [&op](son_wallet_deposit_object &swto) { swto.confirmations = swto.confirmations + 1; }); return (*itr).id; } } FC_CAPTURE_AND_RETHROW( (op) ) } -void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_transfer_process_operation& op) +void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_process_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); - const auto& idx = db().get_index_type().indices().get(); - const auto& itr = idx.find(op.son_wallet_transfer_id); + const auto& idx = db().get_index_type().indices().get(); + const auto& itr = idx.find(op.son_wallet_deposit_id); FC_ASSERT(itr != idx.end(), "Son wallet transfer not found"); //FC_ASSERT(itr->processed == false, "Son wallet transfer is already processed"); @@ -60,7 +58,7 @@ void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_ const account_object& from_account = itr->peerplays_to(d); // reversed, for deposit const account_object& to_account = itr->peerplays_from(d); // reversed, for deposit - const asset_object& asset_type = itr->peerplays_amount.asset_id(d); + const asset_object& asset_type = itr->peerplays_asset.asset_id(d); try { @@ -69,14 +67,14 @@ void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_ transfer_from_account_not_whitelisted, "'from' account ${from} is not whitelisted for asset ${asset}", ("from",from_account.id) - ("asset",itr->peerplays_amount.asset_id) + ("asset",itr->peerplays_asset.asset_id) ); GRAPHENE_ASSERT( is_authorized_asset( d, to_account, asset_type ), transfer_to_account_not_whitelisted, "'to' account ${to} is not whitelisted for asset ${asset}", ("to",to_account.id) - ("asset",itr->peerplays_amount.asset_id) + ("asset",itr->peerplays_asset.asset_id) ); if( asset_type.is_transfer_restricted() ) @@ -85,38 +83,38 @@ void_result process_son_wallet_transfer_evaluator::do_evaluate(const son_wallet_ from_account.id == asset_type.issuer || to_account.id == asset_type.issuer, transfer_restricted_transfer_asset, "Asset {asset} has transfer_restricted flag enabled", - ("asset", itr->peerplays_amount.asset_id) + ("asset", itr->peerplays_asset.asset_id) ); } - bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_amount.amount; + bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_asset.amount; FC_ASSERT( insufficient_balance, "Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'", - ("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_amount))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) ); + ("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_asset))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) ); return void_result(); - } FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_amount))("f",from_account.name)("t",to_account.name) ); + } FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_asset))("f",from_account.name)("t",to_account.name) ); } FC_CAPTURE_AND_RETHROW( (op) ) } -object_id_type process_son_wallet_transfer_evaluator::do_apply(const son_wallet_transfer_process_operation& op) +object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op) { try { - const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_wallet_transfer_id); + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_deposit_id); if(itr != idx.end()) { if (itr->processed == false) { - db().modify(*itr, [&op](son_wallet_transfer_object &swto) { + db().modify(*itr, [&op](son_wallet_deposit_object &swto) { swto.processed = true; }); const account_id_type from_account = itr->peerplays_to; // reversed, for deposit const account_id_type to_account = itr->peerplays_from; // reversed, for deposit - db().adjust_balance( from_account, -itr->peerplays_amount ); - db().adjust_balance( to_account, itr->peerplays_amount ); + db().adjust_balance( from_account, -itr->peerplays_asset ); + db().adjust_balance( to_account, itr->peerplays_asset ); } } - return op.son_wallet_transfer_id; + return op.son_wallet_deposit_id; } FC_CAPTURE_AND_RETHROW( (op) ) } } } // namespace graphene::chain diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 736832d9..15a1d120 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -8,8 +8,7 @@ namespace graphene { namespace chain { void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); auto itr = idx.rbegin(); @@ -54,8 +53,7 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); - FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp new file mode 100644 index 00000000..d3a32a1b --- /dev/null +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -0,0 +1,70 @@ +#include + +#include +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_create_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.peerplays_uid); + if (itr == idx.end()) { + const auto& new_son_wallet_withdraw_object = db().create( [&]( son_wallet_withdraw_object& swwo ){ + swwo.timestamp = op.timestamp; + swwo.sidechain = op.sidechain; + swwo.confirmations = 1; + swwo.peerplays_uid = op.peerplays_uid; + swwo.peerplays_transaction_id = op.peerplays_transaction_id; + swwo.peerplays_from = op.peerplays_from; + swwo.peerplays_asset = op.peerplays_asset; + swwo.withdraw_sidechain = op.withdraw_sidechain; + swwo.withdraw_address = op.withdraw_address; + swwo.withdraw_currency = op.withdraw_currency; + swwo.withdraw_amount = op.withdraw_amount; + swwo.processed = false; + }); + return new_son_wallet_withdraw_object.id; + } else { + db().modify(*itr, [&op](son_wallet_withdraw_object &swto) { + swto.confirmations = swto.confirmations + 1; + }); + return (*itr).id; + } +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + + const auto& idx = db().get_index_type().indices().get(); + const auto& itr = idx.find(op.son_wallet_withdraw_id); + FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_process_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_withdraw_id); + if(itr != idx.end()) + { + if (itr->processed == false) { + db().modify(*itr, [&op](son_wallet_withdraw_object &swto) { + swto.processed = true; + }); + } + } + return op.son_wallet_withdraw_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt old mode 100644 new mode 100755 index a3910c9e..e7d9acfe --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( peerplays_sidechain sidechain_net_manager.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp + sidechain_net_handler_peerplays.cpp bitcoin_utils.cpp ) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index ae1d222c..7c6e8742 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -4,9 +4,11 @@ #include #include +#include #include #include +#include #include namespace graphene { namespace peerplays_sidechain { @@ -70,9 +72,11 @@ struct sidechain_event_data { std::string sidechain_transaction_id; std::string sidechain_from; std::string sidechain_to; - int64_t sidechain_amount; + std::string sidechain_currency; + fc::safe sidechain_amount; chain::account_id_type peerplays_from; chain::account_id_type peerplays_to; + chain::asset peerplays_asset; }; } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index da6321a9..fe042306 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -16,11 +16,16 @@ public: virtual ~sidechain_net_handler(); graphene::peerplays_sidechain::sidechain_type get_sidechain(); - std::vector get_sidechain_addresses(); + std::vector get_sidechain_deposit_addresses(); + std::vector get_sidechain_withdraw_addresses(); void sidechain_event_data_received(const sidechain_event_data& sed); virtual void recreate_primary_wallet() = 0; + virtual void process_deposits() = 0; + virtual void process_deposit(const son_wallet_deposit_object& swdo) = 0; + virtual void process_withdrawals() = 0; + virtual void process_withdrawal(const son_wallet_withdraw_object& swwo) = 0; protected: peerplays_sidechain_plugin& plugin; @@ -32,8 +37,6 @@ protected: virtual std::string sign_transaction( const std::string& transaction ) = 0; virtual std::string send_transaction( const std::string& transaction ) = 0; - virtual void handle_event( const std::string& event_data ) = 0; - private: }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9768066b..6128ced8 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -7,7 +7,6 @@ #include #include -#include namespace graphene { namespace peerplays_sidechain { @@ -72,8 +71,10 @@ public: virtual ~sidechain_net_handler_bitcoin(); void recreate_primary_wallet(); - - bool connection_is_not_defined() const; + void process_deposits(); + void process_deposit(const son_wallet_deposit_object& swdo); + void process_withdrawals(); + void process_withdrawal(const son_wallet_withdraw_object& swwo); std::string create_multisignature_wallet( const std::vector public_keys ); std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp new file mode 100644 index 00000000..13d5de52 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler_peerplays : public sidechain_net_handler { +public: + sidechain_net_handler_peerplays(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); + virtual ~sidechain_net_handler_peerplays(); + + void recreate_primary_wallet(); + void process_deposits(); + void process_deposit(const son_wallet_deposit_object& swdo); + void process_withdrawals(); + void process_withdrawal(const son_wallet_withdraw_object& swwo); + + std::string create_multisignature_wallet( const std::vector public_keys ); + std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); + std::string sign_transaction( const std::string& transaction ); + std::string send_transaction( const std::string& transaction ); + +private: + + void on_block_applied(const signed_block& b); + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index bd6f1ab3..b9ae658f 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -17,6 +17,8 @@ public: bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options); void recreate_primary_wallet(); + void process_deposits(); + void process_withdrawals(); private: peerplays_sidechain_plugin& plugin; graphene::chain::database& database; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index a32d9dd8..e5680d45 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,27 +46,30 @@ class peerplays_sidechain_plugin_impl void create_son_down_proposals(); void recreate_primary_wallet(); void process_deposits(); - //void process_withdrawals(); - void on_block_applied( const signed_block& b ); - void on_objects_new(const vector& new_object_ids); + void process_withdrawals(); private: peerplays_sidechain_plugin& plugin; bool config_ready_son; bool config_ready_bitcoin; + bool config_ready_peerplays; std::unique_ptr net_manager; std::map _private_keys; std::set _sons; fc::future _heartbeat_task; + void on_block_applied( const signed_block& b ); + void on_objects_new(const vector& new_object_ids); + }; peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : plugin(_plugin), config_ready_son(false), config_ready_bitcoin(false), + config_ready_peerplays(false), net_manager(nullptr) { } @@ -177,6 +180,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt // wlog("Haven't set up Ethereum sidechain parameters"); //} + config_ready_peerplays = true; + if (config_ready_peerplays) { + net_manager->create_handler(sidechain_type::peerplays, options); + ilog("Peerplays sidechain handler created"); + } else { + wlog("Haven't set up Peerplays sidechain parameters"); + } + if (!(config_ready_bitcoin /*&& config_ready_ethereum*/)) { wlog("Haven't set up any sidechain parameters"); throw; @@ -200,6 +211,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() //if (config_ready_ethereum) { // ilog("Ethereum sidechain handler running"); //} + + if (config_ready_peerplays) { + ilog("Peerplays sidechain handler running"); + } } std::set& peerplays_sidechain_plugin_impl::get_sons() @@ -307,7 +322,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() auto son_obj = idx.find( my_son_id ); chain::son_report_down_operation son_down_op; - son_down_op.payer = gpo.parameters.get_son_btc_account_id(); + son_down_op.payer = GRAPHENE_SON_ACCOUNT; son_down_op.son_id = son_id; son_down_op.down_ts = last_active_ts; @@ -360,47 +375,15 @@ void peerplays_sidechain_plugin_impl::recreate_primary_wallet() net_manager->recreate_primary_wallet(); } -void peerplays_sidechain_plugin_impl::process_deposits() { - - const auto& idx = plugin.database().get_index_type().indices().get(); - const auto& idx_range = idx.equal_range(false); - - std::for_each(idx_range.first, idx_range.second, - [&] (const son_wallet_transfer_object& swto) { - - const chain::global_property_object& gpo = plugin.database().get_global_properties(); - - for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { - - son_wallet_transfer_process_operation p_op; - p_op.payer = gpo.parameters.get_son_btc_account_id(); - p_op.son_wallet_transfer_id = swto.id; - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - - ilog("sidechain_net_handler: sending proposal for transfer operation ${swto} by ${son}", ("swto", swto.id) ("son", son_id)); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); - trx.validate(); - ilog("sidechain_net_handler: transaction validated ${swto} by ${son}", ("swto", swto.id) ("son", son_id)); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); - } - } - } - }); +void peerplays_sidechain_plugin_impl::process_deposits() +{ + net_manager->process_deposits(); } -//void peerplays_sidechain_plugin_impl::process_withdrawals() { -//} +void peerplays_sidechain_plugin_impl::process_withdrawals() +{ + net_manager->process_withdrawals(); +} void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) { @@ -423,7 +406,7 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) process_deposits(); - //process_withdrawals(); + process_withdrawals(); } } @@ -481,13 +464,25 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vectorproposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( son_id, proposal->id ); continue; } if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( son_id, proposal->id ); continue; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index ac50974c..e89bd9c8 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include @@ -19,25 +21,29 @@ graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidecha return sidechain; } -std::vector sidechain_net_handler::get_sidechain_addresses() { +std::vector sidechain_net_handler::get_sidechain_deposit_addresses() { std::vector result; - switch (sidechain) { - case sidechain_type::bitcoin: - { - const auto& sidechain_addresses_idx = database.get_index_type(); - const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); - const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); - std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, - [&result] (const sidechain_address_object& sao) { - result.push_back(sao.address); - }); - break; - } - default: - assert(false); - } + const auto& sidechain_addresses_idx = database.get_index_type(); + const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&result] (const sidechain_address_object& sao) { + result.push_back(sao.deposit_address); + }); + return result; +} +std::vector sidechain_net_handler::get_sidechain_withdraw_addresses() { + std::vector result; + + const auto& sidechain_addresses_idx = database.get_index_type(); + const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&result] (const sidechain_address_object& sao) { + result.push_back(sao.withdraw_address); + }); return result; } @@ -49,44 +55,181 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ ilog( " sidechain_transaction_id: ${transaction_id}", ( "transaction_id", sed.sidechain_transaction_id ) ); ilog( " sidechain_from: ${from}", ( "from", sed.sidechain_from ) ); ilog( " sidechain_to: ${to}", ( "to", sed.sidechain_to ) ); + ilog( " sidechain_currency: ${currency}", ( "currency", sed.sidechain_currency ) ); ilog( " sidechain_amount: ${amount}", ( "amount", sed.sidechain_amount ) ); ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) ); ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) ); + ilog( " peerplays_asset: ${peerplays_asset}", ( "peerplays_asset", sed.peerplays_asset ) ); const chain::global_property_object& gpo = database.get_global_properties(); - son_wallet_transfer_create_operation op; - op.payer = gpo.parameters.get_son_btc_account_id(); - op.timestamp = sed.timestamp; - op.sidechain = sed.sidechain; - op.sidechain_uid = sed.sidechain_uid; - op.sidechain_transaction_id = sed.sidechain_transaction_id; - op.sidechain_from = sed.sidechain_from; - op.sidechain_to = sed.sidechain_to; - op.sidechain_amount = sed.sidechain_amount; - op.peerplays_from = sed.peerplays_from; - op.peerplays_to = sed.peerplays_to; - op.peerplays_amount = asset(sed.sidechain_amount / 1000); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market + // Deposit request + if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") != 0)) { + son_wallet_deposit_create_operation op; + op.payer = GRAPHENE_SON_ACCOUNT; + op.timestamp = sed.timestamp; + op.sidechain = sed.sidechain; + op.sidechain_uid = sed.sidechain_uid; + op.sidechain_transaction_id = sed.sidechain_transaction_id; + op.sidechain_from = sed.sidechain_from; + op.sidechain_to = sed.sidechain_to; + op.sidechain_currency = sed.sidechain_currency; + op.sidechain_amount = sed.sidechain_amount; + op.peerplays_from = sed.peerplays_from; + op.peerplays_to = sed.peerplays_to; + op.peerplays_asset = sed.peerplays_asset; - for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son}", ("son", son_id)); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); - try { - database.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet transfer create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id)); + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + } } } + return; } + + // Withdrawal request + if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") == 0)) { + // BTC Payout only (for now) + const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin)); + if ( addr_itr == sidechain_addresses_idx.end() ) + return; + + son_wallet_withdraw_create_operation op; + op.payer = GRAPHENE_SON_ACCOUNT; + op.timestamp = sed.timestamp; + op.sidechain = sed.sidechain; + op.peerplays_uid = sed.sidechain_uid; + op.peerplays_transaction_id = sed.sidechain_transaction_id; + op.peerplays_from = sed.peerplays_from; + op.peerplays_asset = sed.peerplays_asset; + op.withdraw_sidechain = sidechain_type::bitcoin; // BTC payout only (for now) + op.withdraw_address = addr_itr->withdraw_address; // BTC payout only (for now) + op.withdraw_currency = "BTC"; // BTC payout only (for now) + op.withdraw_amount = sed.peerplays_asset.amount * 1000; // BTC payout only (for now) + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + + ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id)); + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + } + } + } + return; + } + + FC_ASSERT(false, "Invalid sidechain event"); +} + +void sidechain_net_handler::recreate_primary_wallet() { + FC_ASSERT(false, "recreate_primary_wallet not implemented"); +} + +void sidechain_net_handler::process_deposits() { + const auto& idx = plugin.database().get_index_type().indices().get(); + const auto& idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, + [&] (const son_wallet_deposit_object& swdo) { + + process_deposit(swdo); + + const chain::global_property_object& gpo = plugin.database().get_global_properties(); + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + + son_wallet_deposit_process_operation p_op; + p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.son_wallet_deposit_id = swdo.id; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + + ilog("sidechain_net_handler: sending proposal for transfer operation ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id)); + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + trx.validate(); + ilog("sidechain_net_handler: transaction validated ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id)); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); + } + } + } + }); +} + +void sidechain_net_handler::process_withdrawals() { + const auto& idx = plugin.database().get_index_type().indices().get(); + const auto& idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, + [&] (const son_wallet_withdraw_object& swwo) { + + process_withdrawal(swwo); + + const chain::global_property_object& gpo = plugin.database().get_global_properties(); + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + + ilog("SON ${son_id}: Withdraw to process: ${swwo}", ("son_id", son_id) ("swwo", swwo)); + //son_wallet_withdraw_process_operation p_op; + //p_op.payer = GRAPHENE_SON_ACCOUNT; + //p_op.son_wallet_withdraw_id = swwo.id; + // + //proposal_create_operation proposal_op; + //proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; + //proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); + //uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + //proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + // + //ilog("sidechain_net_handler: sending proposal for transfer operation ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id)); + //signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + //trx.validate(); + //ilog("sidechain_net_handler: transaction validated ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id)); + //try { + // plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + // if(plugin.app().p2p_node()) + // plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + //} catch(fc::exception e){ + // ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); + //} + } + } + }); } } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 40da9240..9dd5ab39 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include namespace graphene { namespace peerplays_sidechain { @@ -396,7 +398,7 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { boost::property_tree::json_parser::write_json(res, pt.get_child("result")); son_wallet_update_operation op; - op.payer = gpo.parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; op.son_wallet_id = (*obj).id; op.sidechain = sidechain_type::bitcoin; op.address = res.str(); @@ -422,43 +424,45 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { } } -bool sidechain_net_handler_bitcoin::connection_is_not_defined() const -{ - return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); +void sidechain_net_handler_bitcoin::process_deposits() { + sidechain_net_handler::process_deposits(); } -std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) -{ +void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object& swdo) { +} + +void sidechain_net_handler_bitcoin::process_withdrawals() { + sidechain_net_handler::process_withdrawals(); +} + +void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object& swwo) { +} + +std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) { return bitcoin_client->add_multisig_address(public_keys); } -std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) -{ +std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) { return ""; } -std::string sidechain_net_handler_bitcoin::sign_transaction( const std::string& transaction ) -{ +std::string sidechain_net_handler_bitcoin::sign_transaction( const std::string& transaction ) { return ""; } -std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& transaction ) -{ +std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& transaction ) { return ""; } void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { - ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); - ilog(" event_data: ${event_data}", ("event_data", event_data)); - std::string block = bitcoin_client->receive_full_block( event_data ); if( block != "" ) { const auto& vins = extract_info_from_block( block ); - const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); for( const auto& v : vins ) { - const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain_type::bitcoin, v.address)); + const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, v.address)); if ( addr_itr == sidechain_addresses_idx.end() ) continue; @@ -473,16 +477,17 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data sed.sidechain_transaction_id = v.out.hash_tx; sed.sidechain_from = ""; sed.sidechain_to = v.address; + sed.sidechain_currency = "BTC"; sed.sidechain_amount = v.out.amount; sed.peerplays_from = addr_itr->sidechain_address_account; sed.peerplays_to = GRAPHENE_SON_ACCOUNT; + sed.peerplays_asset = asset(sed.sidechain_amount / 1000); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market sidechain_event_data_received(sed); } } } -std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) -{ +std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) { std::stringstream ss( _block ); boost::property_tree::ptree block; boost::property_tree::read_json( ss, block ); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp new file mode 100644 index 00000000..bfdd5370 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -0,0 +1,99 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : + sidechain_net_handler(_plugin, options) { + sidechain = sidechain_type::peerplays; + plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } ); +} + +sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { +} + +void sidechain_net_handler_peerplays::recreate_primary_wallet() { +} + +void sidechain_net_handler_peerplays::process_deposits() { + sidechain_net_handler::process_deposits(); +} + +void sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object& swdo) { +} + +void sidechain_net_handler_peerplays::process_withdrawals() { + sidechain_net_handler::process_withdrawals(); +} + +void sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object& swwo) { +} + +std::string sidechain_net_handler_peerplays::create_multisignature_wallet( const std::vector public_keys ) { + return ""; +} + +std::string sidechain_net_handler_peerplays::transfer( const std::string& from, const std::string& to, const uint64_t amount ) { + return ""; +} + +std::string sidechain_net_handler_peerplays::sign_transaction( const std::string& transaction ) { + return ""; +} + +std::string sidechain_net_handler_peerplays::send_transaction( const std::string& transaction ) { + return ""; +} + +void sidechain_net_handler_peerplays::on_block_applied(const signed_block& b) { + for (const auto& trx: b.transactions) { + size_t operation_index = -1; + for (auto op: trx.operations) { + operation_index = operation_index + 1; + if (op.which() == operation::tag::value){ + transfer_operation transfer_op = op.get(); + if (transfer_op.to != GRAPHENE_SON_ACCOUNT) { + continue; + } + + std::stringstream ss; + ss << "peerplays" << "-" << trx.id().str() << "-" << operation_index; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = plugin.database().head_block_time(); + sed.sidechain = sidechain_type::peerplays; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = trx.id().str(); + sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); + sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); + sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(plugin.database()).symbol; + sed.sidechain_amount = transfer_op.amount.amount; + sed.peerplays_from = transfer_op.from; + sed.peerplays_to = transfer_op.to; + // We should calculate exchange rate between CORE/TEST and other Peerplays asset + sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(plugin.database()).options.core_exchange_rate.quote.amount); + sidechain_event_data_received(sed); + } + } + } +} + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index 8b6f18c1..b9e5dd8d 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace graphene { namespace peerplays_sidechain { @@ -26,6 +27,12 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s ret_val = true; break; } + case sidechain_type::peerplays: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; + } default: assert(false); } @@ -39,5 +46,17 @@ void sidechain_net_manager::recreate_primary_wallet() { } } +void sidechain_net_manager::process_deposits() { + for ( size_t i = 0; i < net_handlers.size(); i++ ) { + net_handlers.at(i)->process_deposits(); + } +} + +void sidechain_net_manager::process_withdrawals() { + for ( size_t i = 0; i < net_handlers.size(); i++ ) { + net_handlers.at(i)->process_withdrawals(); + } +} + } } // graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index bfe9ab35..34fc1885 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1416,17 +1416,15 @@ class wallet_api * * @param account the name or id of the account who owns the address * @param sidechain a sidechain to whom address belongs - * @param address sidechain address - * @param private_key private key for sidechain address - * @param public_key public key for sidechain address + * @param deposit_address sidechain address for deposits + * @param withdraw_address sidechain address for withdrawals * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction adding sidechain address */ signed_transaction add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast = false); /** Updates existing sidechain address owned by the given account for a given sidechain. @@ -1435,17 +1433,15 @@ class wallet_api * * @param account the name or id of the account who owns the address * @param sidechain a sidechain to whom address belongs - * @param address sidechain address - * @param private_key private key for sidechain address - * @param public_key public key for sidechain address + * @param deposit_address sidechain address for deposits + * @param withdraw_address sidechain address for withdrawals * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction updating sidechain address */ signed_transaction update_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast = false); /** Deletes existing sidechain address owned by the given account for a given sidechain. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 943b6f1c..8af353e2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2025,9 +2025,8 @@ public: signed_transaction add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast /* = false */) { try { account_id_type sidechain_address_account_id = get_account_id(account); @@ -2035,9 +2034,8 @@ public: sidechain_address_add_operation op; op.sidechain_address_account = sidechain_address_account_id; op.sidechain = sidechain; - op.address = address; - op.private_key = private_key; - op.public_key = public_key; + op.deposit_address = deposit_address; + op.withdraw_address = withdraw_address; signed_transaction tx; tx.operations.push_back( op ); @@ -2049,9 +2047,8 @@ public: signed_transaction update_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast /* = false */) { try { account_id_type sidechain_address_account_id = get_account_id(account); @@ -2063,9 +2060,8 @@ public: op.sidechain_address_id = sao->id; op.sidechain_address_account = sidechain_address_account_id; op.sidechain = sidechain; - op.address = address; - op.private_key = private_key; - op.public_key = public_key; + op.deposit_address = deposit_address; + op.withdraw_address = withdraw_address; signed_transaction tx; tx.operations.push_back( op ); @@ -4504,22 +4500,20 @@ vector> wallet_api::get_son_wallets(uint32_t limit) signed_transaction wallet_api::add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast /* = false */) { - return my->add_sidechain_address(account, sidechain, address, private_key, public_key, broadcast); + return my->add_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast); } signed_transaction wallet_api::update_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, - string address, - string private_key, - string public_key, + string deposit_address, + string withdraw_address, bool broadcast /* = false */) { - return my->update_sidechain_address(account, sidechain, address, private_key, public_key, broadcast); + return my->update_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast); } signed_transaction wallet_api::delete_sidechain_address(string account, diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 04a94827..3c2c2577 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -45,7 +45,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp old mode 100755 new mode 100644 diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index eef76784..bee7cec8 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -30,9 +30,8 @@ BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) { op.sidechain_address_account = alice_id; op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; - op.address = "address"; - op.private_key = "private_key"; - op.public_key = "public_key"; + op.deposit_address = "deposit_address"; + op.withdraw_address = "withdraw_address"; trx.operations.push_back(op); sign(trx, alice_private_key); @@ -48,9 +47,8 @@ BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) { BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->sidechain_address_account == alice_id ); BOOST_CHECK( obj->sidechain == graphene::peerplays_sidechain::sidechain_type::bitcoin ); - BOOST_CHECK( obj->address == "address" ); - BOOST_CHECK( obj->private_key == "private_key" ); - BOOST_CHECK( obj->public_key == "public_key" ); + BOOST_CHECK( obj->deposit_address == "deposit_address" ); + BOOST_CHECK( obj->withdraw_address == "withdraw_address" ); } BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { @@ -66,9 +64,8 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { auto obj = idx.find( boost::make_tuple( alice_id, graphene::peerplays_sidechain::sidechain_type::bitcoin ) ); BOOST_REQUIRE( obj != idx.end() ); - std::string new_address = "new_address"; - std::string new_private_key = "new_private_key"; - std::string new_public_key = "new_public_key"; + std::string new_deposit_address = "new_deposit_address"; + std::string new_withdraw_address = "new_withdraw_address"; { BOOST_TEST_MESSAGE("Send sidechain_address_update_operation"); @@ -77,9 +74,8 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { op.sidechain_address_id = sidechain_address_id_type(0); op.sidechain_address_account = obj->sidechain_address_account; op.sidechain = obj->sidechain; - op.address = new_address; - op.private_key = new_private_key; - op.public_key = new_public_key; + op.deposit_address = new_deposit_address; + op.withdraw_address = new_withdraw_address; trx.operations.push_back(op); sign(trx, alice_private_key); @@ -96,9 +92,8 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->sidechain_address_account == obj->sidechain_address_account ); BOOST_CHECK( obj->sidechain == obj->sidechain ); - BOOST_CHECK( obj->address == new_address ); - BOOST_CHECK( obj->private_key == new_private_key ); - BOOST_CHECK( obj->public_key == new_public_key ); + BOOST_CHECK( obj->deposit_address == new_deposit_address ); + BOOST_CHECK( obj->withdraw_address == new_withdraw_address ); } } diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index c283f21e..3925f02b 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -843,35 +843,12 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { BOOST_CHECK( obj->status == son_status::active); - const auto& son_btc_account = db.create( [&]( account_object& obj ) { - obj.name = "son_btc_account"; - obj.statistics = db.create([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id; - obj.membership_expiration_date = time_point_sec::maximum(); - obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - - obj.owner.add_authority( bob_id, 1 ); - obj.active.add_authority( bob_id, 1 ); - obj.active.weight_threshold = 1; - obj.owner.weight_threshold = 1; - }); - - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - _gpo.parameters.extensions.value.son_pay_daily_max = 200; - _gpo.parameters.witness_pay_per_block = 0; - - _gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); - if( _gpo.pending_parameters ) - _gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); - }); - { // Check that transaction fails if down_ts < last_active_timestamp generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; op.son_id = son_id_type(0); op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); @@ -884,7 +861,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { } { - // Check that transaction fails if payer is not son_btc_account. + // Check that transaction fails if payer is not GRAPHENE_SON_ACCOUNT. generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; @@ -901,11 +878,11 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { } { - // Check that transaction succeeds after getting enough approvals on son_btc_account. + // Check that transaction succeeds after getting enough approvals on GRAPHENE_SON_ACCOUNT. generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; op.son_id = son_id_type(0); op.down_ts = son_stats_obj->last_active_timestamp; @@ -925,7 +902,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; op.son_id = son_id_type(0); op.down_ts = son_stats_obj->last_active_timestamp; diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index 1a912e05..486f46ed 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { son_wallet_recreate_operation op; - op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; { son_info si; @@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE( son_wallet_update_test ) { son_wallet_update_operation op; - op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.payer = GRAPHENE_SON_ACCOUNT; op.son_wallet_id = son_wallet_id_type(0); op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; op.address = "bitcoin address"; From f859d61398eb2d20e57c0158a59a4613b837f79b Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Mon, 24 Feb 2020 13:04:54 +1100 Subject: [PATCH 40/64] SON261 - Bitcoin deposit, withdrawal, PW transfer (#287) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Skeleton of sidechain_net_handler_peerplays * Skeleton of Peerplays network listener * SON261 - Deposit transfer ( user address -> PW ) and Withdrawal transfer ( PW -> user address ) for m-of-n multisig * Temoprary disable account history tests for tracking accounts * Full Peerplays listener, use GRAPHENE_SON_ACCOUNT instead son_btc_account * Renaming son_wallet_transfer* to son_wallet_deposit*, introducing son_wallet_withdrawal* * Extend sidechain_address_object to contain withdrawal addresses - Withdrawal address is the address where system will send sidechain currencies * Rename son_wallet_withdrawal* to son_wallet_withdraw* * Some refactoring * SON261 - Withdrawal transfer ( PW -> user address ), addition of bitcoin public private key to config.ini for multiple sons mode * Withdrawal refactoring * Withdrawal refactoring * SON261 - Fix prepare_tx * SON261 - Add PW->PW Transfer and Code reorg * Fix file permissions Co-authored-by: obucinac Co-authored-by: gladcow --- libraries/app/impacted.cpp | 9 + libraries/chain/CMakeLists.txt | 1 + libraries/chain/db_init.cpp | 6 + libraries/chain/db_notify.cpp | 9 + .../graphene/chain/protocol/operations.hpp | 6 +- .../chain/protocol/sidechain_transaction.hpp | 60 +++ .../include/graphene/chain/protocol/types.hpp | 5 + .../chain/sidechain_transaction_evaluator.hpp | 35 ++ .../chain/sidechain_transaction_object.hpp | 39 ++ .../chain/sidechain_transaction_evaluator.cpp | 136 +++++++ .../sidechain_net_handler.hpp | 2 + .../sidechain_net_handler_bitcoin.hpp | 10 + .../peerplays_sidechain_plugin.cpp | 3 + .../sidechain_net_handler_bitcoin.cpp | 306 +++++++++++++- programs/js_operation_serializer/main.cpp | 1 + tests/tests/sidechain_transaction_tests.cpp | 380 ++++++++++++++++++ 16 files changed, 1000 insertions(+), 8 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp create mode 100644 libraries/chain/sidechain_transaction_evaluator.cpp create mode 100644 tests/tests/sidechain_transaction_tests.cpp diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index e0de1d05..90538087 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -343,6 +343,15 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } + void operator()( const bitcoin_transaction_send_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_transaction_sign_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_send_transaction_process_operation& op ){ + _impacted.insert( op.payer ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 85d5a91b..9c068ba5 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -122,6 +122,7 @@ add_library( graphene_chain son_wallet_withdraw_evaluator.cpp sidechain_address_evaluator.cpp + sidechain_transaction_evaluator.cpp ${HEADERS} ${PROTOCOL_HEADERS} diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 0be37c42..1d7b4370 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,7 @@ #include #include #include +#include #include @@ -267,6 +269,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -316,6 +321,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 1b0b5158..6cf7c7b0 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -330,6 +330,15 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } + void operator()( const bitcoin_transaction_send_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_transaction_sign_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_send_transaction_process_operation& op ) { + _impacted.insert( op.payer ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 37eccf80..d633056f 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -50,6 +50,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -155,7 +156,10 @@ namespace graphene { namespace chain { son_wallet_withdraw_process_operation, sidechain_address_add_operation, sidechain_address_update_operation, - sidechain_address_delete_operation + sidechain_address_delete_operation, + bitcoin_transaction_send_operation, + bitcoin_transaction_sign_operation, + bitcoin_send_transaction_process_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp new file mode 100644 index 00000000..eb7e942d --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + + struct bitcoin_transaction_send_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + // TODO: BTC Transaction Structs go here + fc::flat_map> signatures; + + account_id_type fee_payer()const { return payer; } + void validate()const {} + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct bitcoin_transaction_sign_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + proposal_id_type proposal_id; + std::vector signatures; + + account_id_type fee_payer()const { return payer; } + void validate()const {} + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + + struct bitcoin_send_transaction_process_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + bitcoin_transaction_id_type bitcoin_transaction_id; + + account_id_type fee_payer()const { return payer; } + void validate()const {} + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(signatures) ) + +FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) ) + +FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation, (fee)(payer)(bitcoin_transaction_id) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index ac6ec067..1caf1f9c 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -151,6 +151,7 @@ namespace graphene { namespace chain { son_wallet_deposit_object_type, son_wallet_withdraw_object_type, sidechain_address_object_type, + bitcoin_transaction_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -218,6 +219,7 @@ namespace graphene { namespace chain { class son_wallet_deposit_object; class son_wallet_withdraw_object; class sidechain_address_object; + class bitcoin_transaction_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; @@ -250,6 +252,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type; typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; + typedef object_id< protocol_ids, bitcoin_transaction_object_type,bitcoin_transaction_object> bitcoin_transaction_id_type; // implementation types class global_property_object; @@ -440,6 +443,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (son_wallet_deposit_object_type) (son_wallet_withdraw_object_type) (sidechain_address_object_type) + (bitcoin_transaction_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -517,6 +521,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::bitcoin_transaction_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp new file mode 100644 index 00000000..aac04698 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class bitcoin_transaction_send_evaluator : public evaluator +{ +public: + typedef bitcoin_transaction_send_operation operation_type; + + void_result do_evaluate(const bitcoin_transaction_send_operation& o); + object_id_type do_apply(const bitcoin_transaction_send_operation& o); +}; + +class bitcoin_transaction_sign_evaluator : public evaluator +{ +public: + typedef bitcoin_transaction_sign_operation operation_type; + + void_result do_evaluate(const bitcoin_transaction_sign_operation& o); + object_id_type do_apply(const bitcoin_transaction_sign_operation& o); + void update_proposal( const bitcoin_transaction_sign_operation& o ); +}; + +class bitcoin_send_transaction_process_evaluator : public evaluator +{ +public: + typedef bitcoin_send_transaction_process_operation operation_type; + + void_result do_evaluate(const bitcoin_send_transaction_process_operation& o); + object_id_type do_apply(const bitcoin_send_transaction_process_operation& o); +}; + +} } // namespace graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp new file mode 100644 index 00000000..95417852 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class bitcoin_transaction_object + * @brief tracks state of bitcoin transaction. + * @ingroup object + */ + class bitcoin_transaction_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = bitcoin_transaction_object_type; + // Bitcoin structs go here. + bool processed = false; + fc::flat_map> signatures; + }; + + struct by_processed; + using bitcoin_transaction_multi_index_type = multi_index_container< + bitcoin_transaction_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + > + > + >; + using bitcoin_transaction_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::db::object), + (processed)(signatures) ) diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp new file mode 100644 index 00000000..7a684b79 --- /dev/null +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -0,0 +1,136 @@ +#include + +#include +#include +#include +#include +#include + +namespace graphene +{ +namespace chain +{ + +void_result bitcoin_transaction_send_evaluator::do_evaluate(const bitcoin_transaction_send_operation &op) +{ + try + { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +object_id_type bitcoin_transaction_send_evaluator::do_apply(const bitcoin_transaction_send_operation &op) +{ + try + { + const auto &new_bitcoin_transaction_object = db().create([&](bitcoin_transaction_object &obj) { + obj.processed = false; + obj.signatures = op.signatures; + }); + return new_bitcoin_transaction_object.id; + } + FC_CAPTURE_AND_RETHROW((op)) +} + +void_result bitcoin_transaction_sign_evaluator::do_evaluate(const bitcoin_transaction_sign_operation &op) +{ + try + { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + const auto &proposal_idx = db().get_index_type().indices().get(); + const auto &proposal_itr = proposal_idx.find(op.proposal_id); + FC_ASSERT(proposal_idx.end() != proposal_itr, "proposal not found"); + // Checks can this SON approve this proposal + auto can_this_son_approve_this_proposal = [&]() { + const auto &sidx = db().get_index_type().indices().get(); + auto son_obj = sidx.find(op.payer); + if (son_obj == sidx.end()) + { + return false; + } + // TODO: Check if the SON is included in the PW script. + return true; + }; + + FC_ASSERT(can_this_son_approve_this_proposal(), "Invalid approval received"); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +object_id_type bitcoin_transaction_sign_evaluator::do_apply(const bitcoin_transaction_sign_operation &op) +{ + try + { + const auto &proposal = op.proposal_id(db()); + const auto &sidx = db().get_index_type().indices().get(); + auto son_obj = sidx.find(op.payer); + + db().modify(proposal, [&](proposal_object &po) { + auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get(); + bitcoin_transaction_send_op.signatures[son_obj->id] = op.signatures; + po.proposed_transaction.operations[0] = bitcoin_transaction_send_op; + }); + + update_proposal(op); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +void bitcoin_transaction_sign_evaluator::update_proposal(const bitcoin_transaction_sign_operation &op) +{ + database &d = db(); + proposal_update_operation update_op; + + update_op.fee_paying_account = op.payer; + update_op.proposal = op.proposal_id; + update_op.active_approvals_to_add = {op.payer}; + + bool skip_fee_old = trx_state->skip_fee; + bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check; + trx_state->skip_fee = true; + trx_state->skip_fee_schedule_check = true; + + d.apply_operation(*trx_state, update_op); + + trx_state->skip_fee = skip_fee_old; + trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old; +} + +void_result bitcoin_send_transaction_process_evaluator::do_evaluate(const bitcoin_send_transaction_process_operation &op) +{ + try + { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + const auto& btidx = db().get_index_type().indices().get(); + const auto btobj = btidx.find(op.bitcoin_transaction_id); + FC_ASSERT(btobj != btidx.end(), "Bitcoin Transaction Object not found"); + FC_ASSERT(btobj->processed == false, "Bitcoin Transaction already processed"); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +object_id_type bitcoin_send_transaction_process_evaluator::do_apply(const bitcoin_send_transaction_process_operation &op) +{ + try + { + const auto &btidx = db().get_index_type().indices().get(); + auto btobj = btidx.find(op.bitcoin_transaction_id); + if (btobj != btidx.end()) + { + db().modify(*btobj, [&op](bitcoin_transaction_object &bto) { + bto.processed = true; + }); + } + return op.bitcoin_transaction_id; + } + FC_CAPTURE_AND_RETHROW((op)) +} + +} // namespace chain +} // namespace graphene diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index fe042306..64fea1be 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -36,6 +36,8 @@ protected: virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; virtual std::string sign_transaction( const std::string& transaction ) = 0; virtual std::string send_transaction( const std::string& transaction ) = 0; + //virtual std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed) = 0; + //virtual std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) = 0; private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 6128ced8..854bac96 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -28,8 +28,12 @@ public: void send_btc_tx( const std::string& tx_hex ); std::string add_multisig_address( const std::vector public_keys ); bool connection_is_not_defined() const; + std::string create_raw_transaction(const std::string& txid, const std::string& vout, const std::string& out_address, double transfer_amount); + std::string sign_raw_transaction_with_wallet(const std::string& tx_hash); + std::string sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key); void import_address( const std::string& address_or_script); std::vector list_unspent(); + std::vector list_unspent_by_address_and_amount(const std::string& address, double transfer_amount); std::string prepare_tx(const std::vector& ins, const fc::flat_map outs); private: @@ -80,6 +84,11 @@ public: std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); std::string sign_transaction( const std::string& transaction ); std::string send_transaction( const std::string& transaction ); + std::string sign_and_send_transaction_with_wallet ( const std::string& tx_json ); + std::string transfer_all_btc(const std::string& from_address, const std::string& to_address); + std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed); + std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount); + private: std::string ip; @@ -87,6 +96,7 @@ private: uint32_t rpc_port; std::string rpc_user; std::string rpc_password; + std::map _private_keys; std::unique_ptr listener; std::unique_ptr bitcoin_client; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index e5680d45..5af5e5fc 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -109,6 +109,9 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") + ("bitcoin-private-keys", bpo::value>()->composing()->multitoken()-> + DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), + "Tuple of [Bitcoin PublicKey, Bitcoin Private key] (may specify multiple times)") ; cfg.add(cli); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 9dd5ab39..dd644f3d 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -112,6 +112,8 @@ void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + ilog(body); + const auto reply = send_post_request( body ); if( reply.body.empty() ) @@ -170,6 +172,72 @@ std::string bitcoin_rpc_client::add_multisig_address( const std::vector bitcoin_rpc_client::list_unspent() return result; } +std::vector bitcoin_rpc_client::list_unspent_by_address_and_amount(const std::string& address, double minimum_amount) +{ + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": ["); + body += std::string("1,999999,[\""); + body += address; + body += std::string("\"],true,{\"minimumAmount\":"); + body += std::to_string(minimum_amount); + body += std::string("}] }"); + + ilog(body); + + const auto reply = send_post_request( body ); + + std::vector result; + if( reply.body.empty() ) + { + wlog("Failed to list unspent txo"); + return result; + } + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump((reply_str)); + if( json.count( "result" ) ) + { + for(auto& entry: json.get_child("result")) + { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + txo.amount_ = entry.second.get_child("amount").get_value(); + result.push_back(txo); + } + } + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) ); + } + return result; +} + std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"createrawtransaction\", \"params\": ["); @@ -249,21 +362,21 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, co { if(!first) body += ","; - body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":\"" + fc::to_string(entry.out_num_) + "\"}"; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; first = false; } - body += "]"; + body += "],["; first = true; - body += "{"; for(const auto& entry: outs) { if(!first) body += ","; - body += "\"" + entry.first + "\":\"" + fc::to_string(entry.second) + "\""; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; first = false; } - body += "}"; - body += std::string("] }"); + body += std::string("]] }"); + + ilog(body); const auto reply = send_post_request( body ); @@ -282,7 +395,7 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, co if( reply.status == 200 ) { idump((reply_str)); if( json.count( "result" ) ) - return json.get_child("result").get_value(); + return reply_str; } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { wlog( "Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", reply_str) ); } @@ -351,6 +464,21 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain rpc_user = options.at("bitcoin-node-rpc-user").as(); rpc_password = options.at("bitcoin-node-rpc-password").as(); + if( options.count("bitcoin-private-keys") ) + { + const std::vector pub_priv_keys = options["bitcoin-private-keys"].as>(); + for (const std::string& itr_key_pair : pub_priv_keys) + { + auto key_pair = graphene::app::dejsonify >(itr_key_pair, 5); + ilog("Public Key: ${public}", ("public", key_pair.first)); + if(!key_pair.first.length() || !key_pair.second.length()) + { + FC_THROW("Invalid public private key pair."); + } + _private_keys[key_pair.first] = key_pair.second; + } + } + fc::http::connection conn; try { conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); @@ -454,6 +582,170 @@ std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& return ""; } +std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet ( const std::string& tx_json ) +{ + std::string reply_str = tx_json; + + ilog(reply_str); + + std::stringstream ss_utx(reply_str); + boost::property_tree::ptree pt; + boost::property_tree::read_json( ss_utx, pt ); + + if( !(pt.count( "error" ) && pt.get_child( "error" ).empty()) || !pt.count("result") ) { + return ""; + } + + std::string unsigned_tx_hex = pt.get("result"); + + reply_str = bitcoin_client->sign_raw_transaction_with_wallet(unsigned_tx_hex); + ilog(reply_str); + std::stringstream ss_stx(reply_str); + boost::property_tree::ptree stx_json; + boost::property_tree::read_json( ss_stx, stx_json ); + + if( !(stx_json.count( "error" ) && stx_json.get_child( "error" ).empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex") ) { + return ""; + } + + std::string signed_tx_hex = stx_json.get("result.hex"); + + bitcoin_client->send_btc_tx(signed_tx_hex); + + return reply_str; +} + +std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& from_address, const std::string& to_address) +{ + uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + double min_amount = ((double)fee_rate/100000000.0); // Account only for relay fee for now + double total_amount = 0.0; + std::vector unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(from_address, 0); + + if(unspent_utxo.size() == 0) + { + wlog("Failed to find UTXOs to spend for ${pw}",("pw", from_address)); + return ""; + } + else + { + for(const auto& utx: unspent_utxo) + { + total_amount += utx.amount_; + } + + if(min_amount >= total_amount) + { + wlog("Failed not enough BTC to transfer from ${fa}",("fa", from_address)); + return ""; + } + } + + fc::flat_map outs; + outs[to_address] = total_amount - min_amount; + + std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs); + return sign_and_send_transaction_with_wallet(reply_str); +} + +std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( const sidechain_event_data& sed ) +{ + const auto& idx = database.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + return ""; + } + + std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + + std::stringstream ss(pw_address_json); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + std::string pw_address = json.get("address"); + + std::string txid = sed.sidechain_transaction_id; + std::string suid = sed.sidechain_uid; + std::string nvout = suid.substr(suid.find_last_of("-")+1); + uint64_t deposit_amount = sed.sidechain_amount.value; + uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + deposit_amount -= fee_rate; // Deduct minimum relay fee + double transfer_amount = (double)deposit_amount/100000000.0; + + std::vector ins; + fc::flat_map outs; + + btc_txout utxo; + utxo.txid_ = txid; + utxo.out_num_ = std::stoul(nvout); + + ins.push_back(utxo); + + outs[pw_address] = transfer_amount; + + std::string reply_str = bitcoin_client->prepare_tx(ins, outs); + return sign_and_send_transaction_with_wallet(reply_str); +} + +std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) { + const auto& idx = database.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) + { + return ""; + } + + std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + + std::stringstream ss(pw_address_json); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + std::string pw_address = json.get("address"); + + uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + double min_amount = sidechain_amount + ((double)fee_rate/100000000.0); // Account only for relay fee for now + double total_amount = 0.0; + std::vector unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(pw_address, 0); + + if(unspent_utxo.size() == 0) + { + wlog("Failed to find UTXOs to spend for ${pw}",("pw", pw_address)); + return ""; + } + else + { + for(const auto& utx: unspent_utxo) + { + total_amount += utx.amount_; + } + + if(min_amount > total_amount) + { + wlog("Failed not enough BTC to spend for ${pw}",("pw", pw_address)); + return ""; + } + } + + fc::flat_map outs; + outs[user_address] = sidechain_amount; + if((total_amount - min_amount) > 0.0) + { + outs[pw_address] = total_amount - min_amount; + } + + std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs); + return sign_and_send_transaction_with_wallet(reply_str); +} + void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { std::string block = bitcoin_client->receive_full_block( event_data ); if( block != "" ) { diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 3c2c2577..706334eb 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include diff --git a/tests/tests/sidechain_transaction_tests.cpp b/tests/tests/sidechain_transaction_tests.cpp new file mode 100644 index 00000000..0246d009 --- /dev/null +++ b/tests/tests/sidechain_transaction_tests.cpp @@ -0,0 +1,380 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include +#include +#include + +using namespace graphene; +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE(sidechain_transaction_tests, database_fixture) + +BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) +{ + + try + { + BOOST_TEST_MESSAGE("bitcoin_transaction_send_test"); + + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)(bob)); + + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + + transfer(committee_account, alice_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, bob_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + + generate_block(); + set_expiration(db, trx); + + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer{}; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_alice = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_alice = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // alice becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url; + op.deposit = deposit_alice; + op.pay_vb = payment_alice; + op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + // create deposit vesting + vesting_balance_id_type deposit_bob; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer{}; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_bob; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // bob becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit_bob; + op.pay_vb = payment_bob; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + + generate_block(); + + const auto& acc_idx = db.get_index_type().indices().get(); + auto acc_itr = acc_idx.find(GRAPHENE_SON_ACCOUNT); + BOOST_REQUIRE(acc_itr != acc_idx.end()); + db.modify(*acc_itr, [&](account_object &obj) { + obj.active.account_auths.clear(); + obj.active.add_authority(bob_id, 1); + obj.active.add_authority(alice_id, 1); + obj.active.weight_threshold = 2; + obj.owner.account_auths.clear(); + obj.owner.add_authority(bob_id, 1); + obj.owner.add_authority(alice_id, 1); + obj.owner.weight_threshold = 2; + }); + + /*const auto &son_btc_account = db.create([&](account_object &obj) { + obj.name = "son_btc_account"; + obj.statistics = db.create([&](account_statistics_object &acc_stat) { acc_stat.owner = obj.id; }).id; + obj.membership_expiration_date = time_point_sec::maximum(); + obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + + obj.owner.add_authority(bob_id, 1); + obj.active.add_authority(bob_id, 1); + obj.owner.add_authority(alice_id, 1); + obj.active.add_authority(alice_id, 1); + obj.active.weight_threshold = 2; + obj.owner.weight_threshold = 2; + }); + + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + _gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); + if( _gpo.pending_parameters ) + _gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); + });*/ + + generate_block(); + + const global_property_object &gpo = db.get_global_properties(); + + { + BOOST_TEST_MESSAGE("Send bitcoin_transaction_send_operation"); + + bitcoin_transaction_send_operation send_op; + + send_op.payer = GRAPHENE_SON_ACCOUNT; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = alice_id; + proposal_op.proposed_ops.push_back(op_wrapper(send_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); + + trx.operations.push_back(proposal_op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check proposal results"); + + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.find(proposal_id_type(0)); + BOOST_REQUIRE(obj != idx.end()); + + const auto& btidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(btidx.size() == 0); + + std::vector a1 = {'a', 'l', 'i', 'c', 'e', '1'}; + std::vector a2 = {'a', 'l', 'i', 'c', 'e', '2'}; + std::vector a3 = {'a', 'l', 'i', 'c', 'e', '3'}; + + { + BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); + + bitcoin_transaction_sign_operation sign_op; + + sign_op.payer = alice_id; + sign_op.proposal_id = proposal_id_type(0); + sign_op.signatures.push_back(a1); + sign_op.signatures.push_back(a2); + sign_op.signatures.push_back(a3); + + trx.operations.push_back(sign_op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + + generate_block(); + + BOOST_REQUIRE(idx.size() == 1); + BOOST_REQUIRE(btidx.size() == 0); + auto pobj = idx.find(proposal_id_type(0)); + BOOST_REQUIRE(pobj != idx.end()); + + const auto& sidx = db.get_index_type().indices().get(); + const auto son_obj1 = sidx.find( alice_id ); + BOOST_REQUIRE(son_obj1 != sidx.end()); + + auto bitcoin_transaction_send_op = pobj->proposed_transaction.operations[0].get(); + BOOST_REQUIRE(bitcoin_transaction_send_op.signatures.size() == 1); + BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][0] == a1); + BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][1] == a2); + BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][2] == a3); + + std::vector b1 = {'b', 'o', 'b', '1'}; + std::vector b2 = {'b', 'o', 'b', '2'}; + std::vector b3 = {'b', 'o', 'b', '3'}; + + { + BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); + + bitcoin_transaction_sign_operation sign_op; + + sign_op.payer = bob_id; + sign_op.proposal_id = proposal_id_type(0); + sign_op.signatures.push_back(b1); + sign_op.signatures.push_back(b2); + sign_op.signatures.push_back(b3); + + trx.operations.push_back(sign_op); + set_expiration(db, trx); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + + generate_block(); + + BOOST_REQUIRE(idx.size() == 0); + + const auto son_obj2 = sidx.find( bob_id ); + BOOST_REQUIRE(son_obj2 != sidx.end()); + + BOOST_REQUIRE(btidx.size() == 1); + + const auto btobj = btidx.find(bitcoin_transaction_id_type(0)); + BOOST_REQUIRE(btobj != btidx.end()); + + BOOST_REQUIRE(btobj->processed == false); + + auto sigs = btobj->signatures; + + BOOST_REQUIRE(sigs[son_obj1->id][0] == a1); + BOOST_REQUIRE(sigs[son_obj1->id][1] == a2); + BOOST_REQUIRE(sigs[son_obj1->id][2] == a3); + + BOOST_REQUIRE(sigs[son_obj2->id][0] == b1); + BOOST_REQUIRE(sigs[son_obj2->id][1] == b2); + BOOST_REQUIRE(sigs[son_obj2->id][2] == b3); + + { + BOOST_TEST_MESSAGE("Send bitcoin_send_transaction_process_operation"); + + bitcoin_send_transaction_process_operation process_op; + process_op.bitcoin_transaction_id = bitcoin_transaction_id_type(0); + process_op.payer = GRAPHENE_SON_ACCOUNT; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = alice_id; + proposal_op.proposed_ops.push_back(op_wrapper(process_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); + + trx.operations.push_back(proposal_op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + + generate_block(); + BOOST_REQUIRE(idx.size() == 1); + obj = idx.find(proposal_id_type(1)); + BOOST_REQUIRE(obj != idx.end()); + + + { + BOOST_TEST_MESSAGE("Send proposal_update_operation"); + + proposal_update_operation puo; + puo.fee_paying_account = bob_id; + puo.proposal = obj->id; + puo.active_approvals_to_add = { bob_id }; + + trx.operations.push_back(puo); + set_expiration(db, trx); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + BOOST_REQUIRE(idx.size() == 1); + obj = idx.find(proposal_id_type(1)); + BOOST_REQUIRE(obj != idx.end()); + + BOOST_REQUIRE(btobj != btidx.end()); + BOOST_REQUIRE(btobj->processed == false); + + { + BOOST_TEST_MESSAGE("Send proposal_update_operation"); + + proposal_update_operation puo; + puo.fee_paying_account = alice_id; + puo.proposal = obj->id; + puo.active_approvals_to_add = { alice_id }; + + trx.operations.push_back(puo); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + BOOST_REQUIRE(idx.size() == 0); + + BOOST_REQUIRE(btobj != btidx.end()); + BOOST_REQUIRE(btobj->processed == true); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() From 47c98c203a16ce72963ea959d6a59c849406f0e1 Mon Sep 17 00:00:00 2001 From: obucinac Date: Mon, 24 Feb 2020 15:00:59 +0200 Subject: [PATCH 41/64] [SON-264] Integrating deposit/withdrawals with bitcoin transactions (feature/SON-260 + SON261 branches) (#291) * Partial integration done, some Bitcoin RPC refactoring * CLang Format config file * CLang Format config file v2.0 * Fix repeating tasks that should be executed by scheduled SON only * Fix withdrawal * Integrate PW wallet fund moving * Resolve conflicts Co-authored-by: gladcow Co-authored-by: satyakoneru --- .clang-format | 127 ++++ .../peerplays_sidechain_plugin.hpp | 1 + .../sidechain_net_handler.hpp | 2 - .../sidechain_net_handler_bitcoin.hpp | 30 +- .../sidechain_net_handler_peerplays.hpp | 2 +- .../peerplays_sidechain_plugin.cpp | 26 +- .../sidechain_net_handler.cpp | 89 +-- .../sidechain_net_handler_bitcoin.cpp | 688 ++++++++---------- .../sidechain_net_handler_peerplays.cpp | 4 +- 9 files changed, 516 insertions(+), 453 deletions(-) create mode 100755 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100755 index 00000000..2fffe7ba --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -3 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 6 +ContinuationIndentWidth: 6 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 3 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 3 +UseTab: Never +... + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index e9a868f1..bc1f6d21 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -28,6 +28,7 @@ class peerplays_sidechain_plugin : public graphene::app::plugin std::unique_ptr my; std::set& get_sons(); + son_id_type& get_current_son_id(); son_object get_son_object(son_id_type son_id); bool is_active_son(son_id_type son_id); std::map& get_private_keys(); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 64fea1be..fe042306 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -36,8 +36,6 @@ protected: virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; virtual std::string sign_transaction( const std::string& transaction ) = 0; virtual std::string send_transaction( const std::string& transaction ) = 0; - //virtual std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed) = 0; - //virtual std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) = 0; private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 854bac96..1772eef4 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -7,6 +7,8 @@ #include #include +#include +#include namespace graphene { namespace peerplays_sidechain { @@ -21,20 +23,18 @@ public: class bitcoin_rpc_client { public: bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ; - std::string receive_full_block( const std::string& block_hash ); - int32_t receive_confirmations_tx( const std::string& tx_hash ); - bool receive_mempool_entry_tx( const std::string& tx_hash ); - uint64_t receive_estimated_fee(); - void send_btc_tx( const std::string& tx_hex ); - std::string add_multisig_address( const std::vector public_keys ); bool connection_is_not_defined() const; - std::string create_raw_transaction(const std::string& txid, const std::string& vout, const std::string& out_address, double transfer_amount); - std::string sign_raw_transaction_with_wallet(const std::string& tx_hash); - std::string sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key); - void import_address( const std::string& address_or_script); - std::vector list_unspent(); - std::vector list_unspent_by_address_and_amount(const std::string& address, double transfer_amount); - std::string prepare_tx(const std::vector& ins, const fc::flat_map outs); + + std::string addmultisigaddress( const std::vector public_keys ); + std::string createrawtransaction(const std::vector& ins, const fc::flat_map outs); + uint64_t estimatesmartfee(); + std::string getblock( const std::string& block_hash, int32_t verbosity = 2 ); + void importaddress( const std::string& address_or_script); + std::vector listunspent(); + std::vector listunspent_by_address_and_amount(const std::string& address, double transfer_amount); + void sendrawtransaction( const std::string& tx_hex ); + std::string signrawtransactionwithkey(const std::string& tx_hash, const std::string& private_key); + std::string signrawtransactionwithwallet(const std::string& tx_hash); private: @@ -86,8 +86,8 @@ public: std::string send_transaction( const std::string& transaction ); std::string sign_and_send_transaction_with_wallet ( const std::string& tx_json ); std::string transfer_all_btc(const std::string& from_address, const std::string& to_address); - std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed); - std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount); + std::string transfer_deposit_to_primary_wallet (const son_wallet_deposit_object &swdo); + std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp index 13d5de52..7de7feb4 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -26,7 +26,7 @@ public: private: - void on_block_applied(const signed_block& b); + void on_applied_block(const signed_block& b); }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 5af5e5fc..0fa12c4e 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -35,6 +35,7 @@ class peerplays_sidechain_plugin_impl void plugin_startup(); std::set& get_sons(); + son_id_type& get_current_son_id(); son_object get_son_object(son_id_type son_id); bool is_active_son(son_id_type son_id); std::map& get_private_keys(); @@ -55,13 +56,15 @@ class peerplays_sidechain_plugin_impl bool config_ready_bitcoin; bool config_ready_peerplays; + son_id_type current_son_id; + std::unique_ptr net_manager; std::map _private_keys; std::set _sons; fc::future _heartbeat_task; - void on_block_applied( const signed_block& b ); - void on_objects_new(const vector& new_object_ids); + void on_applied_block( const signed_block& b ); + void on_new_objects(const vector& new_object_ids); }; @@ -70,6 +73,7 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec config_ready_son(false), config_ready_bitcoin(false), config_ready_peerplays(false), + current_son_id(son_id_type(std::numeric_limits().max())), net_manager(nullptr) { } @@ -158,8 +162,8 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt throw; } - plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } ); - plugin.database().new_objects.connect( [&] (const vector& ids, const flat_set& impacted_accounts) { on_objects_new(ids); } ); + plugin.database().applied_block.connect( [&] (const signed_block& b) { on_applied_block(b); } ); + plugin.database().new_objects.connect( [&] (const vector& ids, const flat_set& impacted_accounts) { on_new_objects(ids); } ); net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); @@ -225,6 +229,10 @@ std::set& peerplays_sidechain_plugin_impl::get_sons() return _sons; } +son_id_type& peerplays_sidechain_plugin_impl::get_current_son_id() { + return current_son_id; +} + son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { const auto& idx = plugin.database().get_index_type().indices().get(); @@ -388,7 +396,7 @@ void peerplays_sidechain_plugin_impl::process_withdrawals() net_manager->process_withdrawals(); } -void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) +void peerplays_sidechain_plugin_impl::on_applied_block( const signed_block& b ) { chain::database& d = plugin.database(); const chain::global_property_object& gpo = d.get_global_properties(); @@ -403,6 +411,8 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) // check if we control scheduled SON if( _sons.find( next_son_id ) != _sons.end() ) { + current_son_id = next_son_id; + create_son_down_proposals(); recreate_primary_wallet(); @@ -414,7 +424,7 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) } } -void peerplays_sidechain_plugin_impl::on_objects_new(const vector& new_object_ids) +void peerplays_sidechain_plugin_impl::on_new_objects(const vector& new_object_ids) { auto approve_proposal = [ & ]( const chain::son_id_type& son_id, const chain::proposal_id_type& proposal_id ) @@ -536,6 +546,10 @@ std::set& peerplays_sidechain_plugin::get_sons() return my->get_sons(); } +son_id_type& peerplays_sidechain_plugin::get_current_son_id() { + return my->get_current_son_id(); +} + son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index e89bd9c8..062c3094 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -87,7 +87,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id)); + //ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id)); signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -130,7 +130,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id)); + //ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id)); signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -158,35 +158,30 @@ void sidechain_net_handler::process_deposits() { std::for_each(idx_range.first, idx_range.second, [&] (const son_wallet_deposit_object& swdo) { + ilog("Deposit to process: ${swdo}", ("swdo", swdo)); + process_deposit(swdo); const chain::global_property_object& gpo = plugin.database().get_global_properties(); - for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + son_wallet_deposit_process_operation p_op; + p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.son_wallet_deposit_id = swdo.id; - son_wallet_deposit_process_operation p_op; - p_op.payer = GRAPHENE_SON_ACCOUNT; - p_op.son_wallet_deposit_id = swdo.id; + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - - ilog("sidechain_net_handler: sending proposal for transfer operation ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id)); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); - trx.validate(); - ilog("sidechain_net_handler: transaction validated ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id)); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); - } - } + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); } }); } @@ -198,36 +193,30 @@ void sidechain_net_handler::process_withdrawals() { std::for_each(idx_range.first, idx_range.second, [&] (const son_wallet_withdraw_object& swwo) { + ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); + process_withdrawal(swwo); const chain::global_property_object& gpo = plugin.database().get_global_properties(); - for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + son_wallet_withdraw_process_operation p_op; + p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.son_wallet_withdraw_id = swwo.id; - ilog("SON ${son_id}: Withdraw to process: ${swwo}", ("son_id", son_id) ("swwo", swwo)); - //son_wallet_withdraw_process_operation p_op; - //p_op.payer = GRAPHENE_SON_ACCOUNT; - //p_op.son_wallet_withdraw_id = swwo.id; - // - //proposal_create_operation proposal_op; - //proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - //proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); - //uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - //proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - // - //ilog("sidechain_net_handler: sending proposal for transfer operation ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id)); - //signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); - //trx.validate(); - //ilog("sidechain_net_handler: transaction validated ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id)); - //try { - // plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - // if(plugin.app().p2p_node()) - // plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - //} catch(fc::exception e){ - // ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); - //} - } + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); + + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); } }); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index dd644f3d..b6dd478d 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include namespace graphene { namespace peerplays_sidechain { @@ -30,116 +28,13 @@ bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::str authorization.val = "Basic " + fc::base64_encode( user + ":" + password ); } -std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash ) -{ - fc::http::connection conn; - conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); - - const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json"; - - const auto reply = conn.request( "GET", url ); - if ( reply.status != 200 ) - return ""; - - ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) ); - return std::string( reply.body.begin(), reply.body.end() ); +bool bitcoin_rpc_client::connection_is_not_defined() const { + return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); } -//int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash ) -//{ -// const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") + -// std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }"); -// -// const auto reply = send_post_request( body ); -// -// if ( reply.status != 200 ) -// return 0; -// -// const auto result = std::string( reply.body.begin(), reply.body.end() ); -// -// std::stringstream ss( result ); -// boost::property_tree::ptree tx; -// boost::property_tree::read_json( ss, tx ); -// -// if( tx.count( "result" ) ) { -// if( tx.get_child( "result" ).count( "confirmations" ) ) { -// return tx.get_child( "result" ).get_child( "confirmations" ).get_value(); -// } -// } -// return 0; -//} - -bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash ) -{ - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") + - std::string("\"") + tx_hash + std::string("\"") + std::string("] }"); - - const auto reply = send_post_request( body ); - - if ( reply.status != 200 ) - return false; - - return true; -} - -uint64_t bitcoin_rpc_client::receive_estimated_fee() -{ - static const auto confirmation_target_blocks = 6; - - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") + - std::to_string(confirmation_target_blocks) + std::string("] }"); - - const auto reply = send_post_request( body ); - - if( reply.status != 200 ) - return 0; - - std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) ); - boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); - - if( json.count( "result" ) ) - if ( json.get_child( "result" ).count( "feerate" ) ) { - auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value(); - feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() ); - return std::stoll( feerate_str ); - } - return 0; -} - -void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) -{ - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") + - std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); - - ilog(body); - - const auto reply = send_post_request( body ); - - if( reply.body.empty() ) - return; - - std::string reply_str( reply.body.begin(), reply.body.end() ); - - std::stringstream ss(reply_str); - boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); - - if( reply.status == 200 ) { - idump(( tx_hex )); - return; - } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { - const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value(); - if( error_code == -27 ) // transaction already in block chain - return; - - wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) ); - } -} - -std::string bitcoin_rpc_client::add_multisig_address( const std::vector public_keys ) -{ - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", \"method\": \"addmultisigaddress\", \"params\": ["); +std::string bitcoin_rpc_client::addmultisigaddress(const std::vector public_keys) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " + "\"method\": \"addmultisigaddress\", \"params\": ["); std::string params = "2, ["; std::string pubkeys = ""; for (std::string pubkey : public_keys) { @@ -151,225 +46,42 @@ std::string bitcoin_rpc_client::add_multisig_address( const std::vector bitcoin_rpc_client::list_unspent() -{ - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": [] }"); - - const auto reply = send_post_request( body ); - - std::vector result; - if( reply.body.empty() ) - { - wlog("Failed to list unspent txo"); - return result; - } - - std::string reply_str( reply.body.begin(), reply.body.end() ); - - std::stringstream ss(reply_str); - boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); - - if( reply.status == 200 ) { - idump((reply_str)); - if( json.count( "result" ) ) - { - for(auto& entry: json.get_child("result")) - { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - txo.amount_ = entry.second.get_child("amount").get_value(); - result.push_back(txo); - } - } - } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { - wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) ); - } - return result; -} - -std::vector bitcoin_rpc_client::list_unspent_by_address_and_amount(const std::string& address, double minimum_amount) -{ - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": ["); - body += std::string("1,999999,[\""); - body += address; - body += std::string("\"],true,{\"minimumAmount\":"); - body += std::to_string(minimum_amount); - body += std::string("}] }"); - - ilog(body); - - const auto reply = send_post_request( body ); - - std::vector result; - if( reply.body.empty() ) - { - wlog("Failed to list unspent txo"); - return result; - } - - std::string reply_str( reply.body.begin(), reply.body.end() ); - - std::stringstream ss(reply_str); - boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); - - if( reply.status == 200 ) { - idump((reply_str)); - if( json.count( "result" ) ) - { - for(auto& entry: json.get_child("result")) - { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - txo.amount_ = entry.second.get_child("amount").get_value(); - result.push_back(txo); - } - } - } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { - wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) ); - } - return result; -} - -std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, const fc::flat_map outs) -{ - std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"createrawtransaction\", \"params\": ["); +std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " + "\"method\": \"createrawtransaction\", \"params\": ["); body += "["; bool first = true; - for(const auto& entry: ins) - { - if(!first) + for (const auto &entry : ins) { + if (!first) body += ","; body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; first = false; } body += "],["; first = true; - for(const auto& entry: outs) - { - if(!first) + for (const auto &entry : outs) { + if (!first) body += ","; body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; first = false; @@ -378,30 +90,237 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector &ins, co ilog(body); - const auto reply = send_post_request( body ); + const auto reply = send_post_request(body); - if( reply.body.empty() ) - { - wlog("Failed to create raw transaction: [${body}]", ("body", body)); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); return std::string(); } - std::string reply_str( reply.body.begin(), reply.body.end() ); - - std::stringstream ss(reply_str); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); + boost::property_tree::read_json(ss, json); - if( reply.status == 200 ) { - idump((reply_str)); - if( json.count( "result" ) ) - return reply_str; - } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { - wlog( "Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", reply_str) ); + if (reply.status == 200) { + if (json.count("result")) + return ss.str(); + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", ss.str())); } return std::string(); } +uint64_t bitcoin_rpc_client::estimatesmartfee() { + static const auto confirmation_target_blocks = 6; + + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", " + "\"method\": \"estimatesmartfee\", \"params\": [") + + std::to_string(confirmation_target_blocks) + std::string("] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return 0; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (json.count("result")) + if (json.get_child("result").count("feerate")) { + auto feerate_str = json.get_child("result").get_child("feerate").get_value(); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } + return 0; +} + +std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": " + "\"getblock\", \"params\": [\"" + + block_hash + "\", " + std::to_string(verbosity) + "] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return std::string(); + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} failed with reply '${msg}'", ("function", __FUNCTION__)("msg", ss.str())); + } + return ""; +} + +void bitcoin_rpc_client::importaddress(const std::string &address_or_script) { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", " + "\"method\": \"importaddress\", \"params\": [") + + std::string("\"") + address_or_script + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + idump((address_or_script)(ss.str())); + return; + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Failed to import address [${addr}]! Reply: ${msg}", ("addr", address_or_script)("msg", ss.str())); + } +} + +std::vector bitcoin_rpc_client::listunspent() { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " + "\"listunspent\", \"params\": [] }"); + + const auto reply = send_post_request(body); + + std::vector result; + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + txo.amount_ = entry.second.get_child("amount").get_value(); + result.push_back(txo); + } + } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str())); + } + return result; +} + +std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " + "\"listunspent\", \"params\": ["); + body += std::string("1,999999,[\""); + body += address; + body += std::string("\"],true,{\"minimumAmount\":"); + body += std::to_string(minimum_amount); + body += std::string("}] }"); + + const auto reply = send_post_request(body); + + std::vector result; + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + txo.amount_ = entry.second.get_child("amount").get_value(); + result.push_back(txo); + } + } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str())); + } + return result; +} + +void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " + "\"method\": \"sendrawtransaction\", \"params\": [") + + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + + ilog(body); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return; + } else if (json.count("error") && !json.get_child("error").empty()) { + const auto error_code = json.get_child("error").get_child("code").get_value(); + if (error_code == -27) // transaction already in block chain + return; + + wlog("BTC tx is not sent! Reply: ${msg}", ("msg", ss.str())); + } +} + +std::string bitcoin_rpc_client::signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key) { + return ""; +} + +std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", " + "\"method\": \"signrawtransactionwithwallet\", \"params\": ["); + std::string params = "\"" + tx_hash + "\""; + body = body + params + std::string("]}"); + + ilog(body); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return std::string(); + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("BTC sign_raw_transaction_with_wallet failed! Reply: ${msg}", ("msg", ss.str())); + } + return ""; +} + fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) { fc::http::connection conn; @@ -499,12 +418,12 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { } void sidechain_net_handler_bitcoin::recreate_primary_wallet() { - const auto& idx_swi = database.get_index_type().indices().get(); - auto obj = idx_swi.rbegin(); - if (obj != idx_swi.rend()) { + const auto& swi = database.get_index_type().indices().get(); + const auto &active_sw = swi.rbegin(); + if (active_sw != swi.rend()) { - if ((obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) || - (obj->addresses.at(sidechain_type::bitcoin).empty())) { + if ((active_sw->addresses.find(sidechain_type::bitcoin) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain_type::bitcoin).empty())) { const chain::global_property_object& gpo = database.get_global_properties(); @@ -517,35 +436,46 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { ilog(reply_str); - std::stringstream ss(reply_str); - boost::property_tree::ptree pt; - boost::property_tree::read_json( ss, pt ); - if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) { + std::stringstream active_pw_ss(reply_str); + boost::property_tree::ptree active_pw_pt; + boost::property_tree::read_json( active_pw_ss, active_pw_pt ); + if( active_pw_pt.count( "error" ) && active_pw_pt.get_child( "error" ).empty() ) { std::stringstream res; - boost::property_tree::json_parser::write_json(res, pt.get_child("result")); + boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); son_wallet_update_operation op; op.payer = GRAPHENE_SON_ACCOUNT; - op.son_wallet_id = (*obj).id; + op.son_wallet_id = (*active_sw).id; op.sidechain = sidechain_type::bitcoin; op.address = res.str(); - for (son_id_type son_id : plugin.get_sons()) { - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); + uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); - try { - database.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); - } + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + return; + } + + const auto &prev_sw = std::next(active_sw); + if (prev_sw != swi.rend()) { + std::stringstream prev_sw_ss(prev_sw->addresses.at(sidechain_type::bitcoin)); + boost::property_tree::ptree prev_sw_pt; + boost::property_tree::read_json( prev_sw_ss, prev_sw_pt ); + + std::string active_pw_address = active_pw_pt.get_child("result").get("address"); + std::string prev_pw_address = prev_sw_pt.get("address"); + + transfer_all_btc(prev_pw_address, active_pw_address); } } } @@ -556,18 +486,22 @@ void sidechain_net_handler_bitcoin::process_deposits() { sidechain_net_handler::process_deposits(); } -void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object& swdo) { +void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { + ilog(__FUNCTION__); + transfer_deposit_to_primary_wallet(swdo); } void sidechain_net_handler_bitcoin::process_withdrawals() { sidechain_net_handler::process_withdrawals(); } -void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object& swwo) { +void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { + ilog(__FUNCTION__); + transfer_withdrawal_from_primary_wallet(swwo); } std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) { - return bitcoin_client->add_multisig_address(public_keys); + return bitcoin_client->addmultisigaddress(public_keys); } std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) { @@ -598,7 +532,7 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet std::string unsigned_tx_hex = pt.get("result"); - reply_str = bitcoin_client->sign_raw_transaction_with_wallet(unsigned_tx_hex); + reply_str = bitcoin_client->signrawtransactionwithwallet(unsigned_tx_hex); ilog(reply_str); std::stringstream ss_stx(reply_str); boost::property_tree::ptree stx_json; @@ -610,20 +544,20 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet std::string signed_tx_hex = stx_json.get("result.hex"); - bitcoin_client->send_btc_tx(signed_tx_hex); + bitcoin_client->sendrawtransaction(signed_tx_hex); return reply_str; } std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& from_address, const std::string& to_address) { - uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); double min_amount = ((double)fee_rate/100000000.0); // Account only for relay fee for now double total_amount = 0.0; - std::vector unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(from_address, 0); + std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(from_address, 0); if(unspent_utxo.size() == 0) { @@ -647,16 +581,16 @@ std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& f fc::flat_map outs; outs[to_address] = total_amount - min_amount; - std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs); + std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); return sign_and_send_transaction_with_wallet(reply_str); } -std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( const sidechain_event_data& sed ) +std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (const son_wallet_deposit_object &swdo) { const auto& idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { - return ""; + return ""; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -667,11 +601,11 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( std::string pw_address = json.get("address"); - std::string txid = sed.sidechain_transaction_id; - std::string suid = sed.sidechain_uid; + std::string txid = swdo.sidechain_transaction_id; + std::string suid = swdo.sidechain_uid; std::string nvout = suid.substr(suid.find_last_of("-")+1); - uint64_t deposit_amount = sed.sidechain_amount.value; - uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t deposit_amount = swdo.sidechain_amount.value; + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); deposit_amount -= fee_rate; // Deduct minimum relay fee @@ -688,16 +622,16 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( outs[pw_address] = transfer_amount; - std::string reply_str = bitcoin_client->prepare_tx(ins, outs); + std::string reply_str = bitcoin_client->createrawtransaction(ins, outs); return sign_and_send_transaction_with_wallet(reply_str); } -std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) { +std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) { const auto& idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { - return ""; + return ""; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -708,13 +642,13 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall std::string pw_address = json.get("address"); - uint64_t fee_rate = bitcoin_client->receive_estimated_fee(); + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); - double min_amount = sidechain_amount + ((double)fee_rate/100000000.0); // Account only for relay fee for now + double min_amount = ((double)(swwo.withdraw_amount.value + fee_rate) / 100000000.0); // Account only for relay fee for now double total_amount = 0.0; - std::vector unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(pw_address, 0); + std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); if(unspent_utxo.size() == 0) { @@ -731,23 +665,23 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall if(min_amount > total_amount) { wlog("Failed not enough BTC to spend for ${pw}",("pw", pw_address)); - return ""; + return ""; } } fc::flat_map outs; - outs[user_address] = sidechain_amount; + outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; if((total_amount - min_amount) > 0.0) { outs[pw_address] = total_amount - min_amount; } - std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs); + std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); return sign_and_send_transaction_with_wallet(reply_str); } void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { - std::string block = bitcoin_client->receive_full_block( event_data ); + std::string block = bitcoin_client->getblock(event_data); if( block != "" ) { const auto& vins = extract_info_from_block( block ); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index bfdd5370..f5d47e39 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -22,7 +22,7 @@ namespace graphene { namespace peerplays_sidechain { sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::peerplays; - plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } ); + plugin.database().applied_block.connect( [&] (const signed_block& b) { on_applied_block(b); } ); } sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { @@ -61,7 +61,7 @@ std::string sidechain_net_handler_peerplays::send_transaction( const std::string return ""; } -void sidechain_net_handler_peerplays::on_block_applied(const signed_block& b) { +void sidechain_net_handler_peerplays::on_applied_block(const signed_block& b) { for (const auto& trx: b.transactions) { size_t operation_index = -1; for (auto op: trx.operations) { From 477aa0332f50feb4a4fc8dcd87a29cf08ef17386 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 28 Feb 2020 01:06:20 +1100 Subject: [PATCH 42/64] SON200 - SON Down proposal broken after latest merges (#294) * SON200 - SON Down proposal broken after latest merges * Add the owner weight threshold similar to witnesses and committee accounts --- libraries/chain/db_init.cpp | 2 +- .../peerplays_sidechain/peerplays_sidechain_plugin.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1d7b4370..833e03e4 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -461,7 +461,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT(create([this](account_object& a) { a.name = "son-account"; a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; - a.owner.weight_threshold = 0; + a.owner.weight_threshold = 1; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 0fa12c4e..ef092994 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -327,10 +327,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { chain::database& d = plugin.database(); - chain::son_id_type my_son_id = *(_sons.begin()); const chain::global_property_object& gpo = d.get_global_properties(); - const auto& idx = d.get_index_type().indices().get(); - auto son_obj = idx.find( my_son_id ); chain::son_report_down_operation son_down_op; son_down_op.payer = GRAPHENE_SON_ACCOUNT; @@ -338,7 +335,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() son_down_op.down_ts = last_active_ts; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = son_obj->son_account; + proposal_op.fee_paying_account = get_son_object(plugin.get_current_son_id()).son_account; proposal_op.proposed_ops.push_back( op_wrapper( son_down_op ) ); uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; proposal_op.expiration_time = time_point_sec( d.head_block_time().sec_since_epoch() + lifetime ); @@ -350,7 +347,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() const chain::dynamic_global_property_object& dgpo = d.get_dynamic_global_properties(); const auto& idx = d.get_index_type().indices().get(); std::set sons_being_reported_down = d.get_sons_being_reported_down(); - chain::son_id_type my_son_id = *(_sons.begin()); + chain::son_id_type my_son_id = get_current_son_id(); for(auto son_inf: gpo.active_sons) { if(my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())){ continue; @@ -364,7 +361,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() ((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) { ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); - chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_obj->signing_key), op); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); fc::future fut = fc::async( [&](){ try { d.push_transaction(trx, database::validation_steps::skip_block_size_check); From abeae4e34d413b327c96d6ccb20a1ae4a45f3819 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 28 Feb 2020 07:51:04 +1100 Subject: [PATCH 43/64] SON269 - Move SON deregistration to Plugin from witness (#298) * SON200 - SON Down proposal broken after latest merges * Add the owner weight threshold similar to witnesses and committee accounts * SON269 - Move SON deregistration to Plugin from witness --- libraries/chain/db_block.cpp | 28 -- libraries/chain/db_getter.cpp | 37 +-- .../chain/include/graphene/chain/config.hpp | 4 +- .../chain/include/graphene/chain/database.hpp | 5 +- .../chain/protocol/chain_parameters.hpp | 15 + libraries/chain/son_evaluator.cpp | 4 +- .../peerplays_sidechain_plugin.cpp | 58 +++- tests/tests/son_operations_tests.cpp | 271 +++++------------- 8 files changed, 147 insertions(+), 275 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index c6b4564c..7625178a 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -427,34 +427,6 @@ signed_block database::_generate_block( _pending_tx_session.reset(); _pending_tx_session = _undo_db.start_undo_session(); - if( head_block_time() > HARDFORK_SON_TIME ) - { - // Approve proposals raised by me in previous schedule or before - process_son_proposals( witness_obj, block_signing_private_key ); - // Check for new SON Deregistration Proposals to be raised - std::set sons_to_be_dereg = get_sons_to_be_deregistered(); - if(sons_to_be_dereg.size() > 0) - { - // We shouldn't raise proposals for the SONs for which a de-reg - // proposal is already raised. - std::set sons_being_dereg = get_sons_being_deregistered(); - for( auto& son : sons_to_be_dereg) - { - // New SON to be deregistered - if(sons_being_dereg.find(son) == sons_being_dereg.end()) - { - // Creating the de-reg proposal - auto op = create_son_deregister_proposal(son, witness_obj); - if(op.valid()) - { - // Signing and pushing into the txs to be included in the block - _pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *op ) ); - } - } - } - } - } - uint64_t postponed_tx_count = 0; // pop pending state (reset to head block state) for( const processed_transaction& tx : _pending_tx ) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 9749d642..72e0327f 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -170,7 +170,7 @@ std::set database::get_sons_to_be_deregistered() // TODO : We need to add a function that returns if we can deregister SON // i.e. with introduction of PW code, we have to make a decision if the SON // is needed for release of funds from the PW - if(head_block_time() - stats.last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME)) + if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())) { ret.insert(son.id); } @@ -194,14 +194,14 @@ std::set database::get_sons_being_reported_down() return ret; } -fc::optional database::create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness ) +fc::optional database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ) { son_delete_operation son_dereg_op; - son_dereg_op.payer = current_witness.witness_account; + son_dereg_op.payer = GRAPHENE_SON_ACCOUNT; son_dereg_op.son_id = son_id; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = current_witness.witness_account; + proposal_op.fee_paying_account = paying_son; proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) ); uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3; proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime ); @@ -222,31 +222,6 @@ signed_transaction database::create_signed_transaction( const fc::ecc::private_k return processed_trx; } -void database::process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key ) -{ - const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); - const auto& proposal_idx = get_index_type().indices().get< by_id >(); - - auto approve_proposal = [ & ]( const proposal_id_type& id ) - { - proposal_update_operation puo; - puo.fee_paying_account = current_witness.witness_account; - puo.proposal = id; - puo.active_approvals_to_add = { current_witness.witness_account }; - _pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, puo ) ); - }; - - for( auto& son_proposal : son_proposal_idx ) - { - const auto& proposal = proposal_idx.find( son_proposal.proposal_id ); - FC_ASSERT( proposal != proposal_idx.end() ); - if( proposal->proposer == current_witness.witness_account) - { - approve_proposal( proposal->id ); - } - } -} - void database::remove_son_proposal( const proposal_object& proposal ) { try { if( proposal.proposed_transaction.operations.size() == 1 && @@ -262,13 +237,13 @@ void database::remove_son_proposal( const proposal_object& proposal ) } } FC_CAPTURE_AND_RETHROW( (proposal) ) } -bool database::is_son_dereg_valid( const son_id_type& son_id ) +bool database::is_son_dereg_valid( son_id_type son_id ) { const auto& son_idx = get_index_type().indices().get< by_id >(); auto son = son_idx.find( son_id ); FC_ASSERT( son != son_idx.end() ); bool ret = ( son->status == son_status::in_maintenance && - (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::hours(SON_DEREGISTER_TIME))); + (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); return ret; } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index e44d2fcf..65f4ab47 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -234,7 +234,9 @@ #define MIN_SON_MEMBER_COUNT 15 #define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY #define SON_VESTING_PERIOD (60*60*24*2) // 2 days -#define SON_DEREGISTER_TIME (12) // 12 Hours +#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds +#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds +#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds #define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200)) #define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT) #define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0)) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index ea2ccffd..5b8952df 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -302,11 +302,10 @@ namespace graphene { namespace chain { std::set get_sons_being_deregistered(); std::set get_sons_being_reported_down(); std::set get_sons_to_be_deregistered(); - fc::optional create_son_deregister_proposal(const son_id_type& son_id, const witness_object& current_witness ); + fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); - void process_son_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key ); void remove_son_proposal( const proposal_object& proposal ); - bool is_son_dereg_valid( const son_id_type& son_id ); + bool is_son_dereg_valid( son_id_type son_id ); time_point_sec head_block_time()const; uint32_t head_block_num()const; diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index c62274ba..cd870a2e 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -46,6 +46,9 @@ namespace graphene { namespace chain { optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; + optional < uint32_t > son_deregister_time; + optional < uint32_t > son_heartbeat_frequency; + optional < uint32_t > son_down_time; }; struct chain_parameters @@ -138,6 +141,15 @@ namespace graphene { namespace chain { inline uint16_t son_pay_daily_max()const { return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX; } + inline uint16_t son_deregister_time()const { + return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME; + } + inline uint16_t son_heartbeat_frequency()const { + return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY; + } + inline uint16_t son_down_time()const { + return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; + } }; } } // graphene::chain @@ -155,6 +167,9 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) + (son_deregister_time) + (son_heartbeat_frequency) + (son_down_time) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 0adfa778..f4a7548a 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -69,8 +69,8 @@ void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op) // Get the current block witness signatory witness_id_type wit_id = db().get_scheduled_witness(1); const witness_object& current_witness = wit_id(db()); - // Either owner can remove or witness - FC_ASSERT(db().get(op.son_id).son_account == op.owner_account || (db().is_son_dereg_valid(op.son_id) && op.payer == current_witness.witness_account)); + // Either owner can remove or consensus son account + FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == GRAPHENE_SON_ACCOUNT)); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); return void_result(); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index ef092994..517e3c7c 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -45,6 +45,7 @@ class peerplays_sidechain_plugin_impl void schedule_heartbeat_loop(); void heartbeat_loop(); void create_son_down_proposals(); + void create_son_deregister_proposals(); void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); @@ -285,9 +286,9 @@ fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::pub void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { fc::time_point now = fc::time_point::now(); - int64_t time_to_next_heartbeat = 180000000; + int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency(); - fc::time_point next_wakeup( now + fc::microseconds( time_to_next_heartbeat ) ); + fc::time_point next_wakeup( now + fc::seconds( time_to_next_heartbeat ) ); _heartbeat_task = fc::schedule([this]{heartbeat_loop();}, next_wakeup, "SON Heartbeat Production"); @@ -323,6 +324,47 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() } } +void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() +{ + chain::database& d = plugin.database(); + std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); + chain::son_id_type my_son_id = get_current_son_id(); + + if(sons_to_be_dereg.size() > 0) + { + // We shouldn't raise proposals for the SONs for which a de-reg + // proposal is already raised. + std::set sons_being_dereg = d.get_sons_being_deregistered(); + for( auto& son : sons_to_be_dereg) + { + // New SON to be deregistered + if(sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) + { + // Creating the de-reg proposal + auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); + if(op.valid()) + { + // Signing and pushing into the txs to be included in the block + ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son) ("s", my_son_id)); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); + fc::future fut = fc::async( [&](){ + try { + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if(plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch(fc::exception e){ + ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}",("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } + } + } +} + void peerplays_sidechain_plugin_impl::create_son_down_proposals() { auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { @@ -356,9 +398,9 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() auto stats = son_obj->statistics(d); fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); - int64_t down_threshold = 2*180000000; + int64_t down_threshold = gpo.parameters.son_down_time(); if(((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::microseconds(down_threshold))) { + ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); @@ -412,6 +454,8 @@ void peerplays_sidechain_plugin_impl::on_applied_block( const signed_block& b ) create_son_down_proposals(); + create_son_deregister_proposals(); + recreate_primary_wallet(); process_deposits(); @@ -467,6 +511,12 @@ void peerplays_sidechain_plugin_impl::on_new_objects(const vectorproposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( son_id, proposal->id ); + continue; + } + if(proposal->proposed_transaction.operations.size() == 1 && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( son_id, proposal->id ); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 3925f02b..13e3cf1f 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -184,6 +184,71 @@ catch (fc::exception &e) { throw; } } +BOOST_AUTO_TEST_CASE( delete_son_test_with_consensus_account ) { +try { + INVOKE(create_son_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + // Modify SON's status to active + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + db.modify( *son_stats_obj, [&]( son_statistics_object& _s) + { + _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + }); + + auto deposit_vesting = db.get(vesting_balance_id_type(0)); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw + + { + trx.clear(); + son_delete_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.payer = GRAPHENE_SON_ACCOUNT; + + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_REQUIRE( idx.size() == 0 ); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + BOOST_CHECK_EQUAL(deposit_vesting.policy.get().vesting_cliff_seconds, + db.get_global_properties().parameters.son_vesting_period()); // in linear policy + + now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw + + generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period())); + generate_block(); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed +} +catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; +} } + BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner try { @@ -458,212 +523,6 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) } -BOOST_AUTO_TEST_CASE( son_witness_proposal_test ) -{ - try - { - const dynamic_global_property_object& dpo = db.get_dynamic_global_properties(); - generate_blocks(HARDFORK_SON_TIME); - generate_block(); - generate_block(); - set_expiration(db, trx); - - ACTORS((alice)(bob)); - - upgrade_to_lifetime_member(alice); - upgrade_to_lifetime_member(bob); - - transfer( committee_account, alice_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); - transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); - - set_expiration(db, trx); - generate_block(); - // Now create SONs - std::string test_url1 = "https://create_son_test1"; - std::string test_url2 = "https://create_son_test2"; - - // create deposit vesting - vesting_balance_id_type deposit1; - { - vesting_balance_create_operation op; - op.creator = alice_id; - op.owner = alice_id; - op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::son; - op.policy = dormant_vesting_policy_initializer {}; - - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - set_expiration(db, trx); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - deposit1 = ptx.operation_results[0].get(); - } - - // create payment vesting - vesting_balance_id_type payment1; - { - vesting_balance_create_operation op; - op.creator = alice_id; - op.owner = alice_id; - op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::normal; - - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - set_expiration(db, trx); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - payment1 = ptx.operation_results[0].get(); - } - - // create deposit vesting - vesting_balance_id_type deposit2; - { - vesting_balance_create_operation op; - op.creator = bob_id; - op.owner = bob_id; - op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::son; - op.policy = dormant_vesting_policy_initializer {}; - - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - set_expiration(db, trx); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - deposit2 = ptx.operation_results[0].get(); - } - - // create payment vesting - vesting_balance_id_type payment2; - { - vesting_balance_create_operation op; - op.creator = bob_id; - op.owner = bob_id; - op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::normal; - - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - set_expiration(db, trx); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - payment2 = ptx.operation_results[0].get(); - } - - // alice becomes son - { - son_create_operation op; - op.owner_account = alice_id; - op.url = test_url1; - op.deposit = deposit1; - op.pay_vb = payment1; - op.fee = asset(0); - op.signing_key = alice_public_key; - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - // bob becomes son - { - son_create_operation op; - op.owner_account = bob_id; - op.url = test_url2; - op.deposit = deposit2; - op.pay_vb = payment2; - op.fee = asset(0); - op.signing_key = bob_public_key; - trx.operations.push_back(op); - for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); - sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - // Check if SONs are created properly - const auto& idx = db.get_index_type().indices().get(); - BOOST_REQUIRE( idx.size() == 2 ); - // Alice's SON - auto obj1 = idx.find( alice_id ); - BOOST_REQUIRE( obj1 != idx.end() ); - BOOST_CHECK( obj1->url == test_url1 ); - BOOST_CHECK( obj1->signing_key == alice_public_key ); - BOOST_CHECK( obj1->deposit.instance == deposit1.instance.value ); - BOOST_CHECK( obj1->pay_vb.instance == payment1.instance.value ); - // Bob's SON - auto obj2 = idx.find( bob_id ); - BOOST_REQUIRE( obj2 != idx.end() ); - BOOST_CHECK( obj2->url == test_url2 ); - BOOST_CHECK( obj2->signing_key == bob_public_key ); - BOOST_CHECK( obj2->deposit.instance == deposit2.instance.value ); - BOOST_CHECK( obj2->pay_vb.instance == payment2.instance.value ); - // Get the statistics object for the SONs - const auto& sidx = db.get_index_type().indices().get(); - BOOST_REQUIRE( sidx.size() == 2 ); - auto son_stats_obj1 = sidx.find( obj1->statistics ); - auto son_stats_obj2 = sidx.find( obj2->statistics ); - BOOST_REQUIRE( son_stats_obj1 != sidx.end() ); - BOOST_REQUIRE( son_stats_obj2 != sidx.end() ); - - - // Modify SON's status to in_maintenance - db.modify( *obj1, [&]( son_object& _s) - { - _s.status = son_status::in_maintenance; - }); - - // Modify the Alice's SON down timestamp to now-12 hours - db.modify( *son_stats_obj1, [&]( son_statistics_object& _s) - { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12)); - }); - - // Modify SON's status to in_maintenance - db.modify( *obj2, [&]( son_object& _s) - { - _s.status = son_status::in_maintenance; - }); - - // Modify the Bob's SON down timestamp to now-12 hours - db.modify( *son_stats_obj2, [&]( son_statistics_object& _s) - { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - fc::hours(12)); - }); - - const auto& son_proposal_idx = db.get_index_type().indices().get(); - const auto& proposal_idx = db.get_index_type().indices().get(); - - BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 ); - - generate_block(); - witness_id_type proposal_initiator = dpo.current_witness; - - BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 ); - - for(size_t i = 0 ; i < 3 * db.get_global_properties().active_witnesses.size() ; i++ ) - { - generate_block(); - if( dpo.current_witness != proposal_initiator) - { - BOOST_CHECK( son_proposal_idx.size() == 2 && proposal_idx.size() == 2 ); - } - else - { - break; - } - } - BOOST_CHECK( son_proposal_idx.size() == 0 && proposal_idx.size() == 0 ); - BOOST_REQUIRE( idx.size() == 0 ); - generate_block(); - } FC_LOG_AND_RETHROW() - -} - BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { try From d6e6bed907d55024c929edcac44013af48af6be1 Mon Sep 17 00:00:00 2001 From: obucinac Date: Mon, 2 Mar 2020 15:24:24 +0200 Subject: [PATCH 44/64] Various SON improvements (#297) * Refactor SON processing * Better exposure of sidechain private keys in sidechain handlers * Support non default Bitcoin wallets * Fix crash on config file recreation * clang-format formatting * New Bitcoin wallet related RPC calls * Add missing create_son_deregister_proposals calls * Add missing create_son_deregister_proposals calls * Add loading/unlocking/locking of non-default bitcoin wallet * Bitcon RFC logs improved, proposal aprovement improved * Move signal connection after handlers are created --- .../peerplays_sidechain/bitcoin_utils.cpp | 642 +++++++--------- .../peerplays_sidechain/bitcoin_utils.hpp | 72 +- .../graphene/peerplays_sidechain/defs.hpp | 48 +- .../peerplays_sidechain_plugin.hpp | 44 +- .../sidechain_net_handler.hpp | 43 +- .../sidechain_net_handler_bitcoin.hpp | 82 +- .../sidechain_net_handler_peerplays.hpp | 27 +- .../sidechain_net_manager.hpp | 15 +- .../peerplays_sidechain_plugin.cpp | 713 +++++++++--------- .../sidechain_net_handler.cpp | 246 +++--- .../sidechain_net_handler_bitcoin.cpp | 474 ++++++++---- .../sidechain_net_handler_peerplays.cpp | 96 ++- .../sidechain_net_manager.cpp | 46 +- 13 files changed, 1307 insertions(+), 1241 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp index 23339afb..a11647de 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp @@ -1,9 +1,9 @@ -#include -#include #include #include #include #include +#include +#include #include namespace graphene { namespace peerplays_sidechain { @@ -35,11 +35,13 @@ static const unsigned char OP_GREATERTHAN = 0xa0; static const unsigned char OP_HASH160 = 0xa9; static const unsigned char OP_CHECKSIG = 0xac; -class WriteBytesStream{ +class WriteBytesStream { public: - WriteBytesStream(bytes& buffer) : storage_(buffer) {} + WriteBytesStream(bytes &buffer) : + storage_(buffer) { + } - void write(const unsigned char* d, size_t s) { + void write(const unsigned char *d, size_t s) { storage_.insert(storage_.end(), d, d + s); } @@ -48,149 +50,128 @@ public: return true; } - void writedata8(uint8_t obj) - { - write((unsigned char*)&obj, 1); + void writedata8(uint8_t obj) { + write((unsigned char *)&obj, 1); } - void writedata16(uint16_t obj) - { - obj = htole16(obj); - write((unsigned char*)&obj, 2); + void writedata16(uint16_t obj) { + obj = htole16(obj); + write((unsigned char *)&obj, 2); } - void writedata32(uint32_t obj) - { - obj = htole32(obj); - write((unsigned char*)&obj, 4); + void writedata32(uint32_t obj) { + obj = htole32(obj); + write((unsigned char *)&obj, 4); } - void writedata64(uint64_t obj) - { - obj = htole64(obj); - write((unsigned char*)&obj, 8); + void writedata64(uint64_t obj) { + obj = htole64(obj); + write((unsigned char *)&obj, 8); } - void write_compact_int(uint64_t val) - { - if (val < 253) - { - writedata8(val); - } - else if (val <= std::numeric_limits::max()) - { - writedata8(253); - writedata16(val); - } - else if (val <= std::numeric_limits::max()) - { - writedata8(254); - writedata32(val); - } - else - { - writedata8(255); - writedata64(val); + void write_compact_int(uint64_t val) { + if (val < 253) { + writedata8(val); + } else if (val <= std::numeric_limits::max()) { + writedata8(253); + writedata16(val); + } else if (val <= std::numeric_limits::max()) { + writedata8(254); + writedata32(val); + } else { + writedata8(255); + writedata64(val); } } - void writedata(const bytes& data) - { + void writedata(const bytes &data) { write_compact_int(data.size()); write(&data[0], data.size()); } + private: - bytes& storage_; + bytes &storage_; }; -class ReadBytesStream{ +class ReadBytesStream { public: - ReadBytesStream(const bytes& buffer, size_t pos = 0) : storage_(buffer), pos_(pos), end_(buffer.size()) {} + ReadBytesStream(const bytes &buffer, size_t pos = 0) : + storage_(buffer), + pos_(pos), + end_(buffer.size()) { + } - size_t current_pos() const { return pos_; } - void set_pos(size_t pos) - { - if(pos > end_) + size_t current_pos() const { + return pos_; + } + void set_pos(size_t pos) { + if (pos > end_) FC_THROW("Invalid position in BTC tx buffer"); pos_ = pos; } - inline bool read( unsigned char* d, size_t s ) { - if( end_ - pos_ >= s ) { - memcpy( d, &storage_[pos_], s ); - pos_ += s; - return true; - } - FC_THROW( "invalid bitcoin tx buffer" ); + inline bool read(unsigned char *d, size_t s) { + if (end_ - pos_ >= s) { + memcpy(d, &storage_[pos_], s); + pos_ += s; + return true; + } + FC_THROW("invalid bitcoin tx buffer"); } - inline bool get( unsigned char& c ) - { - if( pos_ < end_ ) { - c = storage_[pos_++]; - return true; - } - FC_THROW( "invalid bitcoin tx buffer" ); + inline bool get(unsigned char &c) { + if (pos_ < end_) { + c = storage_[pos_++]; + return true; + } + FC_THROW("invalid bitcoin tx buffer"); } - uint8_t readdata8() - { - uint8_t obj; - read((unsigned char*)&obj, 1); - return obj; + uint8_t readdata8() { + uint8_t obj; + read((unsigned char *)&obj, 1); + return obj; } - uint16_t readdata16() - { - uint16_t obj; - read((unsigned char*)&obj, 2); - return le16toh(obj); + uint16_t readdata16() { + uint16_t obj; + read((unsigned char *)&obj, 2); + return le16toh(obj); } - uint32_t readdata32() - { - uint32_t obj; - read((unsigned char*)&obj, 4); - return le32toh(obj); + uint32_t readdata32() { + uint32_t obj; + read((unsigned char *)&obj, 4); + return le32toh(obj); } - uint64_t readdata64() - { - uint64_t obj; - read((unsigned char*)&obj, 8); - return le64toh(obj); + uint64_t readdata64() { + uint64_t obj; + read((unsigned char *)&obj, 8); + return le64toh(obj); } - uint64_t read_compact_int() - { - uint8_t size = readdata8(); - uint64_t ret = 0; - if (size < 253) - { - ret = size; - } - else if (size == 253) - { - ret = readdata16(); - if (ret < 253) - FC_THROW("non-canonical ReadCompactSize()"); - } - else if (size == 254) - { - ret = readdata32(); - if (ret < 0x10000u) - FC_THROW("non-canonical ReadCompactSize()"); - } - else - { - ret = readdata64(); - if (ret < 0x100000000ULL) - FC_THROW("non-canonical ReadCompactSize()"); - } - if (ret > (uint64_t)0x02000000) - FC_THROW("ReadCompactSize(): size too large"); - return ret; + uint64_t read_compact_int() { + uint8_t size = readdata8(); + uint64_t ret = 0; + if (size < 253) { + ret = size; + } else if (size == 253) { + ret = readdata16(); + if (ret < 253) + FC_THROW("non-canonical ReadCompactSize()"); + } else if (size == 254) { + ret = readdata32(); + if (ret < 0x10000u) + FC_THROW("non-canonical ReadCompactSize()"); + } else { + ret = readdata64(); + if (ret < 0x100000000ULL) + FC_THROW("non-canonical ReadCompactSize()"); + } + if (ret > (uint64_t)0x02000000) + FC_THROW("ReadCompactSize(): size too large"); + return ret; } - void readdata(bytes& data) - { + void readdata(bytes &data) { size_t s = read_compact_int(); data.clear(); data.resize(s); @@ -198,38 +179,34 @@ public: } private: - const bytes& storage_; + const bytes &storage_; size_t pos_; size_t end_; }; -void btc_outpoint::to_bytes(bytes& stream) const -{ +void btc_outpoint::to_bytes(bytes &stream) const { WriteBytesStream str(stream); // TODO: write size? - str.write((unsigned char*)hash.data(), hash.data_size()); + str.write((unsigned char *)hash.data(), hash.data_size()); str.writedata32(n); } -size_t btc_outpoint::fill_from_bytes(const bytes& data, size_t pos) -{ +size_t btc_outpoint::fill_from_bytes(const bytes &data, size_t pos) { ReadBytesStream str(data, pos); // TODO: read size? - str.read((unsigned char*)hash.data(), hash.data_size()); + str.read((unsigned char *)hash.data(), hash.data_size()); n = str.readdata32(); return str.current_pos(); } -void btc_in::to_bytes(bytes& stream) const -{ +void btc_in::to_bytes(bytes &stream) const { prevout.to_bytes(stream); WriteBytesStream str(stream); str.writedata(scriptSig); str.writedata32(nSequence); } -size_t btc_in::fill_from_bytes(const bytes& data, size_t pos) -{ +size_t btc_in::fill_from_bytes(const bytes &data, size_t pos) { pos = prevout.fill_from_bytes(data, pos); ReadBytesStream str(data, pos); str.readdata(scriptSig); @@ -237,53 +214,46 @@ size_t btc_in::fill_from_bytes(const bytes& data, size_t pos) return str.current_pos(); } -void btc_out::to_bytes(bytes& stream) const -{ +void btc_out::to_bytes(bytes &stream) const { WriteBytesStream str(stream); str.writedata64(nValue); str.writedata(scriptPubKey); } -size_t btc_out::fill_from_bytes(const bytes& data, size_t pos) -{ +size_t btc_out::fill_from_bytes(const bytes &data, size_t pos) { ReadBytesStream str(data, pos); nValue = str.readdata64(); str.readdata(scriptPubKey); return str.current_pos(); } -void btc_tx::to_bytes(bytes& stream) const -{ +void btc_tx::to_bytes(bytes &stream) const { WriteBytesStream str(stream); str.writedata32(nVersion); - if(hasWitness) - { + if (hasWitness) { // write dummy inputs and flag str.write_compact_int(0); unsigned char flags = 1; str.put(flags); } str.write_compact_int(vin.size()); - for(const auto& in: vin) + for (const auto &in : vin) in.to_bytes(stream); str.write_compact_int(vout.size()); - for(const auto& out: vout) + for (const auto &out : vout) out.to_bytes(stream); - if(hasWitness) - { - for(const auto& in: vin) - { + if (hasWitness) { + for (const auto &in : vin) { str.write_compact_int(in.scriptWitness.size()); - for(const auto& stack_item: in.scriptWitness) + for (const auto &stack_item : in.scriptWitness) str.writedata(stack_item); } } str.writedata32(nLockTime); } -size_t btc_tx::fill_from_bytes(const bytes& data, size_t pos) -{ - ReadBytesStream ds( data, pos ); +size_t btc_tx::fill_from_bytes(const bytes &data, size_t pos) { + ReadBytesStream ds(data, pos); nVersion = ds.readdata32(); unsigned char flags = 0; vin.clear(); @@ -293,43 +263,42 @@ size_t btc_tx::fill_from_bytes(const bytes& data, size_t pos) size_t vin_size = ds.read_compact_int(); vin.resize(vin_size); pos = ds.current_pos(); - for(auto& in: vin) + for (auto &in : vin) pos = in.fill_from_bytes(data, pos); ds.set_pos(pos); if (vin_size == 0) { - /* We read a dummy or an empty vin. */ - ds.get(flags); - if (flags != 0) { - size_t vin_size = ds.read_compact_int(); - vin.resize(vin_size); - pos = ds.current_pos(); - for(auto& in: vin) - pos = in.fill_from_bytes(data, pos); - ds.set_pos(pos); - size_t vout_size = ds.read_compact_int(); - vout.resize(vout_size); - pos = ds.current_pos(); - for(auto& out: vout) - pos = out.fill_from_bytes(data, pos); - ds.set_pos(pos); - hasWitness = true; - } + /* We read a dummy or an empty vin. */ + ds.get(flags); + if (flags != 0) { + size_t vin_size = ds.read_compact_int(); + vin.resize(vin_size); + pos = ds.current_pos(); + for (auto &in : vin) + pos = in.fill_from_bytes(data, pos); + ds.set_pos(pos); + size_t vout_size = ds.read_compact_int(); + vout.resize(vout_size); + pos = ds.current_pos(); + for (auto &out : vout) + pos = out.fill_from_bytes(data, pos); + ds.set_pos(pos); + hasWitness = true; + } } else { - /* We read a non-empty vin. Assume a normal vout follows. */ + /* We read a non-empty vin. Assume a normal vout follows. */ size_t vout_size = ds.read_compact_int(); vout.resize(vout_size); pos = ds.current_pos(); - for(auto& out: vout) + for (auto &out : vout) pos = out.fill_from_bytes(data, pos); ds.set_pos(pos); } if (hasWitness) { /* The witness flag is present, and we support witnesses. */ - for (auto& in: vin) - { + for (auto &in : vin) { unsigned int size = ds.read_compact_int(); in.scriptWitness.resize(size); - for(auto& stack_item: in.scriptWitness) + for (auto &stack_item : in.scriptWitness) ds.readdata(stack_item); } } @@ -337,61 +306,56 @@ size_t btc_tx::fill_from_bytes(const bytes& data, size_t pos) return ds.current_pos(); } - -void add_data_to_script(bytes& script, const bytes& data) -{ +void add_data_to_script(bytes &script, const bytes &data) { WriteBytesStream str(script); str.writedata(data); } -void add_number_to_script(bytes& script, unsigned char data) -{ +void add_number_to_script(bytes &script, unsigned char data) { WriteBytesStream str(script); - if(data == 0) + if (data == 0) str.put(OP_0); - else if(data == 1) + else if (data == 1) str.put(OP_1); - else if(data == 2) + else if (data == 2) str.put(OP_2); - else if(data == 3) + else if (data == 3) str.put(OP_3); - else if(data == 4) + else if (data == 4) str.put(OP_4); - else if(data == 5) + else if (data == 5) str.put(OP_5); - else if(data == 6) + else if (data == 6) str.put(OP_6); - else if(data == 7) + else if (data == 7) str.put(OP_7); - else if(data == 8) + else if (data == 8) str.put(OP_8); - else if(data == 9) + else if (data == 9) str.put(OP_9); - else if(data == 10) + else if (data == 10) str.put(OP_10); - else if(data == 11) + else if (data == 11) str.put(OP_11); - else if(data == 12) + else if (data == 12) str.put(OP_12); - else if(data == 13) + else if (data == 13) str.put(OP_13); - else if(data == 14) + else if (data == 14) str.put(OP_14); - else if(data == 15) + else if (data == 15) str.put(OP_15); - else if(data == 16) + else if (data == 16) str.put(OP_16); else add_data_to_script(script, {data}); } -bytes generate_redeem_script(std::vector > key_data) -{ +bytes generate_redeem_script(std::vector> key_data) { int total_weight = 0; bytes result; add_number_to_script(result, 0); - for(auto& p: key_data) - { + for (auto &p : key_data) { total_weight += p.second; result.push_back(OP_SWAP); auto raw_data = p.first.serialize(); @@ -409,110 +373,110 @@ bytes generate_redeem_script(std::vector > k } /** The Bech32 character set for encoding. */ -const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; +const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; /** Concatenate two byte arrays. */ -bytes cat(bytes x, const bytes& y) { - x.insert(x.end(), y.begin(), y.end()); - return x; +bytes cat(bytes x, const bytes &y) { + x.insert(x.end(), y.begin(), y.end()); + return x; } /** Expand a HRP for use in checksum computation. */ -bytes expand_hrp(const std::string& hrp) { - bytes ret; - ret.resize(hrp.size() * 2 + 1); - for (size_t i = 0; i < hrp.size(); ++i) { - unsigned char c = hrp[i]; - ret[i] = c >> 5; - ret[i + hrp.size() + 1] = c & 0x1f; - } - ret[hrp.size()] = 0; - return ret; +bytes expand_hrp(const std::string &hrp) { + bytes ret; + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; } /** Find the polynomial with value coefficients mod the generator as 30-bit. */ -uint32_t polymod(const bytes& values) { - uint32_t chk = 1; - for (size_t i = 0; i < values.size(); ++i) { - uint8_t top = chk >> 25; - chk = (chk & 0x1ffffff) << 5 ^ values[i] ^ +uint32_t polymod(const bytes &values) { + uint32_t chk = 1; + for (size_t i = 0; i < values.size(); ++i) { + uint8_t top = chk >> 25; + chk = (chk & 0x1ffffff) << 5 ^ values[i] ^ (-((top >> 0) & 1) & 0x3b6a57b2UL) ^ (-((top >> 1) & 1) & 0x26508e6dUL) ^ (-((top >> 2) & 1) & 0x1ea119faUL) ^ (-((top >> 3) & 1) & 0x3d4233ddUL) ^ (-((top >> 4) & 1) & 0x2a1462b3UL); - } - return chk; + } + return chk; } /** Create a checksum. */ -bytes bech32_checksum(const std::string& hrp, const bytes& values) { - bytes enc = cat(expand_hrp(hrp), values); - enc.resize(enc.size() + 6); - uint32_t mod = polymod(enc) ^ 1; - bytes ret; - ret.resize(6); - for (size_t i = 0; i < 6; ++i) { - ret[i] = (mod >> (5 * (5 - i))) & 31; - } - return ret; +bytes bech32_checksum(const std::string &hrp, const bytes &values) { + bytes enc = cat(expand_hrp(hrp), values); + enc.resize(enc.size() + 6); + uint32_t mod = polymod(enc) ^ 1; + bytes ret; + ret.resize(6); + for (size_t i = 0; i < 6; ++i) { + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; } /** Encode a Bech32 string. */ -std::string bech32(const std::string& hrp, const bytes& values) { - bytes checksum = bech32_checksum(hrp, values); - bytes combined = cat(values, checksum); - std::string ret = hrp + '1'; - ret.reserve(ret.size() + combined.size()); - for (size_t i = 0; i < combined.size(); ++i) { - ret += charset[combined[i]]; - } - return ret; +std::string bech32(const std::string &hrp, const bytes &values) { + bytes checksum = bech32_checksum(hrp, values); + bytes combined = cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (size_t i = 0; i < combined.size(); ++i) { + ret += charset[combined[i]]; + } + return ret; } /** Convert from one power-of-2 number base to another. */ -template -bool convertbits(bytes& out, const bytes& in) { - int acc = 0; - int bits = 0; - const int maxv = (1 << tobits) - 1; - const int max_acc = (1 << (frombits + tobits - 1)) - 1; - for (size_t i = 0; i < in.size(); ++i) { - int value = in[i]; - acc = ((acc << frombits) | value) & max_acc; - bits += frombits; - while (bits >= tobits) { - bits -= tobits; - out.push_back((acc >> bits) & maxv); - } - } - if (pad) { - if (bits) out.push_back((acc << (tobits - bits)) & maxv); - } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { - return false; - } - return true; +template +bool convertbits(bytes &out, const bytes &in) { + int acc = 0; + int bits = 0; + const int maxv = (1 << tobits) - 1; + const int max_acc = (1 << (frombits + tobits - 1)) - 1; + for (size_t i = 0; i < in.size(); ++i) { + int value = in[i]; + acc = ((acc << frombits) | value) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + } + if (pad) { + if (bits) + out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; } /** Encode a SegWit address. */ -std::string segwit_addr_encode(const std::string& hrp, uint8_t witver, const bytes& witprog) { - bytes enc; - enc.push_back(witver); - convertbits<8, 5, true>(enc, witprog); - std::string ret = bech32(hrp, enc); - return ret; +std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const bytes &witprog) { + bytes enc; + enc.push_back(witver); + convertbits<8, 5, true>(enc, witprog); + std::string ret = bech32(hrp, enc); + return ret; } -std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_network network) -{ +std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) { // calc script hash - fc::sha256 sh = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); + fc::sha256 sh = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); bytes wp(sh.data(), sh.data() + sh.data_size()); switch (network) { - case(mainnet): + case (mainnet): return segwit_addr_encode("bc", 0, wp); - case(testnet): - case(regtest): + case (testnet): + case (regtest): return segwit_addr_encode("tb", 0, wp); default: FC_THROW("Unknown bitcoin network type"); @@ -520,77 +484,68 @@ std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_networ FC_THROW("Unknown bitcoin network type"); } -bytes lock_script_for_redeem_script(const bytes &script) -{ +bytes lock_script_for_redeem_script(const bytes &script) { bytes result; result.push_back(OP_0); - fc::sha256 h = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); + fc::sha256 h = fc::sha256::hash(reinterpret_cast(&script[0]), script.size()); bytes shash(h.data(), h.data() + h.data_size()); add_data_to_script(result, shash); return result; } -bytes hash_prevouts(const btc_tx& unsigned_tx) -{ +bytes hash_prevouts(const btc_tx &unsigned_tx) { fc::sha256::encoder hasher; - for(const auto& in: unsigned_tx.vin) - { + for (const auto &in : unsigned_tx.vin) { bytes data; in.prevout.to_bytes(data); - hasher.write(reinterpret_cast(&data[0]), data.size()); + hasher.write(reinterpret_cast(&data[0]), data.size()); } fc::sha256 res = fc::sha256::hash(hasher.result()); return bytes(res.data(), res.data() + res.data_size()); } -bytes hash_sequence(const btc_tx& unsigned_tx) -{ +bytes hash_sequence(const btc_tx &unsigned_tx) { fc::sha256::encoder hasher; - for(const auto& in: unsigned_tx.vin) - { - hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); + for (const auto &in : unsigned_tx.vin) { + hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); } fc::sha256 res = fc::sha256::hash(hasher.result()); return bytes(res.data(), res.data() + res.data_size()); } -bytes hash_outputs(const btc_tx& unsigned_tx) -{ +bytes hash_outputs(const btc_tx &unsigned_tx) { fc::sha256::encoder hasher; - for(const auto& out: unsigned_tx.vout) - { + for (const auto &out : unsigned_tx.vout) { bytes data; out.to_bytes(data); - hasher.write(reinterpret_cast(&data[0]), data.size()); + hasher.write(reinterpret_cast(&data[0]), data.size()); } fc::sha256 res = fc::sha256::hash(hasher.result()); return bytes(res.data(), res.data() + res.data_size()); } -const secp256k1_context_t* btc_get_context() { - static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN ); - return ctx; +const secp256k1_context_t *btc_get_context() { + static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; } -bytes der_sign(const fc::ecc::private_key& priv_key, const fc::sha256& digest) -{ +bytes der_sign(const fc::ecc::private_key &priv_key, const fc::sha256 &digest) { fc::ecc::signature result; int size = result.size(); - FC_ASSERT( secp256k1_ecdsa_sign( btc_get_context(), - (unsigned char*) digest.data(), - (unsigned char*) result.begin(), - &size, - (unsigned char*) priv_key.get_secret().data(), - secp256k1_nonce_function_rfc6979, - nullptr)); + FC_ASSERT(secp256k1_ecdsa_sign(btc_get_context(), + (unsigned char *)digest.data(), + (unsigned char *)result.begin(), + &size, + (unsigned char *)priv_key.get_secret().data(), + secp256k1_nonce_function_rfc6979, + nullptr)); return bytes(result.begin(), result.begin() + size); } -std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, - std::vector in_amounts, - const bytes& redeem_script, - const fc::ecc::private_key& priv_key) -{ +std::vector signatures_for_raw_transaction(const bytes &unsigned_tx, + std::vector in_amounts, + const bytes &redeem_script, + const fc::ecc::private_key &priv_key) { btc_tx tx; tx.fill_from_bytes(unsigned_tx); @@ -604,27 +559,26 @@ std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, bytes hashOutputs = hash_outputs(tx); // calc digest for every input according to BIP143 // implement SIGHASH_ALL scheme - for(const auto& in: tx.vin) - { + for (const auto &in : tx.vin) { fc::sha256::encoder hasher; - hasher.write(reinterpret_cast(&tx.nVersion), sizeof(tx.nVersion)); - hasher.write(reinterpret_cast(&hashPrevouts[0]), hashPrevouts.size()); - hasher.write(reinterpret_cast(&hashSequence[0]), hashSequence.size()); + hasher.write(reinterpret_cast(&tx.nVersion), sizeof(tx.nVersion)); + hasher.write(reinterpret_cast(&hashPrevouts[0]), hashPrevouts.size()); + hasher.write(reinterpret_cast(&hashSequence[0]), hashSequence.size()); bytes data; in.prevout.to_bytes(data); - hasher.write(reinterpret_cast(&data[0]), data.size()); + hasher.write(reinterpret_cast(&data[0]), data.size()); bytes serializedScript; WriteBytesStream stream(serializedScript); stream.writedata(redeem_script); - hasher.write(reinterpret_cast(&serializedScript[0]), serializedScript.size()); + hasher.write(reinterpret_cast(&serializedScript[0]), serializedScript.size()); uint64_t amount = *cur_amount++; - hasher.write(reinterpret_cast(&amount), sizeof(amount)); - hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); - hasher.write(reinterpret_cast(&hashOutputs[0]), hashOutputs.size()); - hasher.write(reinterpret_cast(&tx.nLockTime), sizeof(tx.nLockTime)); + hasher.write(reinterpret_cast(&amount), sizeof(amount)); + hasher.write(reinterpret_cast(&in.nSequence), sizeof(in.nSequence)); + hasher.write(reinterpret_cast(&hashOutputs[0]), hashOutputs.size()); + hasher.write(reinterpret_cast(&tx.nLockTime), sizeof(tx.nLockTime)); // add sigtype SIGHASH_ALL uint32_t sigtype = 1; - hasher.write(reinterpret_cast(&sigtype), sizeof(sigtype)); + hasher.write(reinterpret_cast(&sigtype), sizeof(sigtype)); fc::sha256 digest = fc::sha256::hash(hasher.result()); //std::vector res = priv_key.sign(digest); @@ -636,30 +590,24 @@ std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, return results; } -bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector in_amounts, const bytes& redeem_script, const std::vector > &priv_keys) -{ +bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector in_amounts, const bytes &redeem_script, const std::vector> &priv_keys) { btc_tx tx; tx.fill_from_bytes(unsigned_tx); bytes dummy_data; - for(auto key: priv_keys) - { - if(key) - { + for (auto key : priv_keys) { + if (key) { std::vector signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key); FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); // push signatures in reverse order because script starts to check the top signature on the stack first - for(unsigned int i = 0; i < tx.vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); - } - else - { - for(unsigned int i = 0; i < tx.vin.size(); i++) + } else { + for (unsigned int i = 0; i < tx.vin.size(); i++) tx.vin[i].scriptWitness.push_back(dummy_data); } } - for(auto& in: tx.vin) - { + for (auto &in : tx.vin) { in.scriptWitness.push_back(redeem_script); } @@ -669,17 +617,15 @@ bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector in_amounts, - const fc::ecc::private_key& priv_key, - unsigned int key_idx) -{ +bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx, + std::vector in_amounts, + const fc::ecc::private_key &priv_key, + unsigned int key_idx) { btc_tx tx; tx.fill_from_bytes(partially_signed_tx); FC_ASSERT(tx.vin.size() > 0); @@ -703,29 +648,26 @@ bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); // push signatures in reverse order because script starts to check the top signature on the stack first unsigned witness_idx = tx.vin[0].scriptWitness.size() - 2 - key_idx; - for(unsigned int i = 0; i < tx.vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) tx.vin[i].scriptWitness[witness_idx] = signatures[i]; bytes ret; tx.to_bytes(ret); return ret; } -bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector > &signature_set, const bytes &redeem_script) -{ +bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector> &signature_set, const bytes &redeem_script) { btc_tx tx; tx.fill_from_bytes(unsigned_tx); bytes dummy_data; - for(unsigned int i = 0; i < signature_set.size(); i++) - { - std::vector signatures = signature_set[i]; - FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); - // push signatures in reverse order because script starts to check the top signature on the stack first - for(unsigned int i = 0; i < tx.vin.size(); i++) - tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); + for (unsigned int i = 0; i < signature_set.size(); i++) { + std::vector signatures = signature_set[i]; + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + // push signatures in reverse order because script starts to check the top signature on the stack first + for (unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); } - for(auto& in: tx.vin) - { + for (auto &in : tx.vin) { in.scriptWitness.push_back(redeem_script); } @@ -735,4 +677,4 @@ bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector< return ret; } -}} +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp index d2830496..9b2dc0c1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp @@ -1,6 +1,6 @@ #pragma once -#include #include +#include namespace graphene { namespace peerplays_sidechain { @@ -10,24 +10,23 @@ enum bitcoin_network { regtest }; -bytes generate_redeem_script(std::vector > key_data); -std::string p2wsh_address_from_redeem_script(const bytes& script, bitcoin_network network = mainnet); -bytes lock_script_for_redeem_script(const bytes& script); +bytes generate_redeem_script(std::vector> key_data); +std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet); +bytes lock_script_for_redeem_script(const bytes &script); - -std::vector signatures_for_raw_transaction(const bytes& unsigned_tx, - std::vector in_amounts, - const bytes& redeem_script, - const fc::ecc::private_key& priv_key); +std::vector signatures_for_raw_transaction(const bytes &unsigned_tx, + std::vector in_amounts, + const bytes &redeem_script, + const fc::ecc::private_key &priv_key); /* * unsigned_tx - tx, all inputs of which are spends of the PW P2SH address * returns signed transaction */ -bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, +bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector in_amounts, - const bytes& redeem_script, - const std::vector>& priv_keys); + const bytes &redeem_script, + const std::vector> &priv_keys); /// ////// \brief Adds dummy signatures instead of real signatures @@ -35,8 +34,8 @@ bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, ////// \param redeem_script ////// \param key_count ////// \return can be used as partially signed tx -bytes add_dummy_signatures_for_pw_transfer(const bytes& unsigned_tx, - const bytes& redeem_script, +bytes add_dummy_signatures_for_pw_transfer(const bytes &unsigned_tx, + const bytes &redeem_script, unsigned int key_count); /// @@ -47,10 +46,10 @@ bytes add_dummy_signatures_for_pw_transfer(const bytes& unsigned_tx, /// \param key_idx /// \return /// -bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, - std::vector in_amounts, - const fc::ecc::private_key& priv_key, - unsigned int key_idx); +bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx, + std::vector in_amounts, + const fc::ecc::private_key &priv_key, + unsigned int key_idx); /// /// \brief Creates ready to publish bitcoin transaction from unsigned tx and @@ -61,50 +60,45 @@ bytes partially_sign_pw_transfer_transaction(const bytes& partially_signed_tx, /// \param redeem_script /// \return /// -bytes add_signatures_to_unsigned_tx(const bytes& unsigned_tx, - const std::vector >& signatures, - const bytes& redeem_script); +bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, + const std::vector> &signatures, + const bytes &redeem_script); -struct btc_outpoint -{ +struct btc_outpoint { fc::uint256 hash; uint32_t n; - void to_bytes(bytes& stream) const; - size_t fill_from_bytes(const bytes& data, size_t pos = 0); + void to_bytes(bytes &stream) const; + size_t fill_from_bytes(const bytes &data, size_t pos = 0); }; -struct btc_in -{ +struct btc_in { btc_outpoint prevout; bytes scriptSig; uint32_t nSequence; std::vector scriptWitness; - void to_bytes(bytes& stream) const; - size_t fill_from_bytes(const bytes& data, size_t pos = 0); + void to_bytes(bytes &stream) const; + size_t fill_from_bytes(const bytes &data, size_t pos = 0); }; -struct btc_out -{ +struct btc_out { int64_t nValue; bytes scriptPubKey; - void to_bytes(bytes& stream) const; - size_t fill_from_bytes(const bytes& data, size_t pos = 0); + void to_bytes(bytes &stream) const; + size_t fill_from_bytes(const bytes &data, size_t pos = 0); }; -struct btc_tx -{ +struct btc_tx { std::vector vin; std::vector vout; int32_t nVersion; uint32_t nLockTime; bool hasWitness; - void to_bytes(bytes& stream) const; - size_t fill_from_bytes(const bytes& data, size_t pos = 0); + void to_bytes(bytes &stream) const; + size_t fill_from_bytes(const bytes &data, size_t pos = 0); }; -}} - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 7c6e8742..b0484efd 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -4,9 +4,9 @@ #include #include +#include #include #include -#include #include #include @@ -22,14 +22,11 @@ enum class sidechain_type { using bytes = std::vector; -struct prev_out -{ - bool operator!=( const prev_out& obj ) const - { - if( this->hash_tx != obj.hash_tx || +struct prev_out { + bool operator!=(const prev_out &obj) const { + if (this->hash_tx != obj.hash_tx || this->n_vout != obj.n_vout || - this->amount != obj.amount ) - { + this->amount != obj.amount) { return true; } return false; @@ -40,16 +37,15 @@ struct prev_out uint64_t amount; }; -struct info_for_vin -{ +struct info_for_vin { info_for_vin() = default; - info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes(), bool _resend = false ); + info_for_vin(const prev_out &_out, const std::string &_address, bytes _script = bytes(), bool _resend = false); - bool operator!=( const info_for_vin& obj ) const; + bool operator!=(const info_for_vin &obj) const; struct comparer { - bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const; + bool operator()(const info_for_vin &lhs, const info_for_vin &rhs) const; }; static uint64_t count_id_info_for_vin; @@ -66,19 +62,19 @@ struct info_for_vin }; struct sidechain_event_data { - fc::time_point_sec timestamp; - sidechain_type sidechain; - std::string sidechain_uid; - std::string sidechain_transaction_id; - std::string sidechain_from; - std::string sidechain_to; - std::string sidechain_currency; - fc::safe sidechain_amount; - chain::account_id_type peerplays_from; - chain::account_id_type peerplays_to; - chain::asset peerplays_asset; + fc::time_point_sec timestamp; + sidechain_type sidechain; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + std::string sidechain_currency; + fc::safe sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; + chain::asset peerplays_asset; }; -} } // graphene::peerplays_sidechain +}} // namespace graphene::peerplays_sidechain -FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin)(ethereum)(eos)(peerplays) ) +FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin)(ethereum)(eos)(peerplays)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index bc1f6d21..0d39b855 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -7,34 +7,30 @@ namespace graphene { namespace peerplays_sidechain { using namespace chain; -namespace detail -{ - class peerplays_sidechain_plugin_impl; +namespace detail { +class peerplays_sidechain_plugin_impl; } -class peerplays_sidechain_plugin : public graphene::app::plugin -{ - public: - peerplays_sidechain_plugin(); - virtual ~peerplays_sidechain_plugin(); +class peerplays_sidechain_plugin : public graphene::app::plugin { +public: + peerplays_sidechain_plugin(); + virtual ~peerplays_sidechain_plugin(); - std::string plugin_name()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) override; - virtual void plugin_initialize(const boost::program_options::variables_map& options) override; - virtual void plugin_startup() override; + std::string plugin_name() const override; + virtual void plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map &options) override; + virtual void plugin_startup() override; - std::unique_ptr my; + std::unique_ptr my; - std::set& get_sons(); - son_id_type& get_current_son_id(); - son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); - std::map& get_private_keys(); - fc::ecc::private_key get_private_key(son_id_type son_id); - fc::ecc::private_key get_private_key(chain::public_key_type public_key); + std::set &get_sons(); + son_id_type &get_current_son_id(); + son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); }; -} } //graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index fe042306..f3a7aa41 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -12,34 +12,35 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { public: - sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); - virtual ~sidechain_net_handler(); + sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler(); - graphene::peerplays_sidechain::sidechain_type get_sidechain(); - std::vector get_sidechain_deposit_addresses(); - std::vector get_sidechain_withdraw_addresses(); + graphene::peerplays_sidechain::sidechain_type get_sidechain(); + std::vector get_sidechain_deposit_addresses(); + std::vector get_sidechain_withdraw_addresses(); + std::string get_private_key(std::string public_key); - void sidechain_event_data_received(const sidechain_event_data& sed); + void sidechain_event_data_received(const sidechain_event_data &sed); + void process_deposits(); + void process_withdrawals(); - virtual void recreate_primary_wallet() = 0; - virtual void process_deposits() = 0; - virtual void process_deposit(const son_wallet_deposit_object& swdo) = 0; - virtual void process_withdrawals() = 0; - virtual void process_withdrawal(const son_wallet_withdraw_object& swwo) = 0; + virtual void recreate_primary_wallet() = 0; + virtual void process_deposit(const son_wallet_deposit_object &swdo) = 0; + virtual void process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; protected: - peerplays_sidechain_plugin& plugin; - graphene::chain::database& database; - graphene::peerplays_sidechain::sidechain_type sidechain; + peerplays_sidechain_plugin &plugin; + graphene::chain::database &database; + graphene::peerplays_sidechain::sidechain_type sidechain; - virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; - virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; - virtual std::string sign_transaction( const std::string& transaction ) = 0; - virtual std::string send_transaction( const std::string& transaction ) = 0; + std::map private_keys; + + virtual std::string create_multisignature_wallet(const std::vector public_keys) = 0; + virtual std::string transfer(const std::string &from, const std::string &to, const uint64_t amount) = 0; + virtual std::string sign_transaction(const std::string &transaction) = 0; + virtual std::string send_transaction(const std::string &transaction) = 0; private: - }; -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 1772eef4..ec9689f6 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -5,15 +5,14 @@ #include #include -#include #include +#include #include #include namespace graphene { namespace peerplays_sidechain { -class btc_txout -{ +class btc_txout { public: std::string txid_; unsigned int out_num_; @@ -22,28 +21,35 @@ public: class bitcoin_rpc_client { public: - bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ; + bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password); bool connection_is_not_defined() const; - std::string addmultisigaddress( const std::vector public_keys ); - std::string createrawtransaction(const std::vector& ins, const fc::flat_map outs); + std::string addmultisigaddress(const std::vector public_keys); + std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); + std::string createwallet(const std::string &wallet_name); + std::string encryptwallet(const std::string &passphrase); uint64_t estimatesmartfee(); - std::string getblock( const std::string& block_hash, int32_t verbosity = 2 ); - void importaddress( const std::string& address_or_script); + std::string getblock(const std::string &block_hash, int32_t verbosity = 2); + void importaddress(const std::string &address_or_script); std::vector listunspent(); - std::vector listunspent_by_address_and_amount(const std::string& address, double transfer_amount); - void sendrawtransaction( const std::string& tx_hex ); - std::string signrawtransactionwithkey(const std::string& tx_hash, const std::string& private_key); - std::string signrawtransactionwithwallet(const std::string& tx_hash); + std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount); + std::string loadwallet(const std::string &filename); + void sendrawtransaction(const std::string &tx_hex); + std::string signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key); + std::string signrawtransactionwithwallet(const std::string &tx_hash); + std::string unloadwallet(const std::string &filename); + std::string walletlock(); + bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: - - fc::http::reply send_post_request( std::string body ); + fc::http::reply send_post_request(std::string body, bool show_log = false); std::string ip; uint32_t rpc_port; std::string user; std::string password; + std::string wallet; + std::string wallet_password; fc::http::header authorization; }; @@ -52,10 +58,13 @@ private: class zmq_listener { public: - zmq_listener( std::string _ip, uint32_t _zmq ); - bool connection_is_not_defined() const { return zmq_port == 0; } + zmq_listener(std::string _ip, uint32_t _zmq); + bool connection_is_not_defined() const { + return zmq_port == 0; + } + + fc::signal event_received; - fc::signal event_received; private: void handle_zmq(); std::vector receive_multipart(); @@ -71,24 +80,12 @@ private: class sidechain_net_handler_bitcoin : public sidechain_net_handler { public: - sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); + sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); virtual ~sidechain_net_handler_bitcoin(); void recreate_primary_wallet(); - void process_deposits(); - void process_deposit(const son_wallet_deposit_object& swdo); - void process_withdrawals(); - void process_withdrawal(const son_wallet_withdraw_object& swwo); - - std::string create_multisignature_wallet( const std::vector public_keys ); - std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); - std::string sign_transaction( const std::string& transaction ); - std::string send_transaction( const std::string& transaction ); - std::string sign_and_send_transaction_with_wallet ( const std::string& tx_json ); - std::string transfer_all_btc(const std::string& from_address, const std::string& to_address); - std::string transfer_deposit_to_primary_wallet (const son_wallet_deposit_object &swdo); - std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); - + void process_deposit(const son_wallet_deposit_object &swdo); + void process_withdrawal(const son_wallet_withdraw_object &swwo); private: std::string ip; @@ -96,14 +93,23 @@ private: uint32_t rpc_port; std::string rpc_user; std::string rpc_password; - std::map _private_keys; + std::string wallet; + std::string wallet_password; - std::unique_ptr listener; std::unique_ptr bitcoin_client; + std::unique_ptr listener; - void handle_event( const std::string& event_data); - std::vector extract_info_from_block( const std::string& _block ); + std::string create_multisignature_wallet(const std::vector public_keys); + std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); + std::string sign_transaction(const std::string &transaction); + std::string send_transaction(const std::string &transaction); + std::string sign_and_send_transaction_with_wallet(const std::string &tx_json); + std::string transfer_all_btc(const std::string &from_address, const std::string &to_address); + std::string transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo); + std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); + + void handle_event(const std::string &event_data); + std::vector extract_info_from_block(const std::string &_block); }; -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp index 7de7feb4..943c3a90 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -10,25 +10,20 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler_peerplays : public sidechain_net_handler { public: - sidechain_net_handler_peerplays(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); - virtual ~sidechain_net_handler_peerplays(); + sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler_peerplays(); - void recreate_primary_wallet(); - void process_deposits(); - void process_deposit(const son_wallet_deposit_object& swdo); - void process_withdrawals(); - void process_withdrawal(const son_wallet_withdraw_object& swwo); - - std::string create_multisignature_wallet( const std::vector public_keys ); - std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); - std::string sign_transaction( const std::string& transaction ); - std::string send_transaction( const std::string& transaction ); + void recreate_primary_wallet(); + void process_deposit(const son_wallet_deposit_object &swdo); + void process_withdrawal(const son_wallet_withdraw_object &swwo); private: + std::string create_multisignature_wallet(const std::vector public_keys); + std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); + std::string sign_transaction(const std::string &transaction); + std::string send_transaction(const std::string &transaction); - void on_applied_block(const signed_block& b); - + void on_applied_block(const signed_block &b); }; -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index b9ae658f..8e246bce 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -12,19 +12,18 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_manager { public: - sidechain_net_manager(peerplays_sidechain_plugin& _plugin); + sidechain_net_manager(peerplays_sidechain_plugin &_plugin); virtual ~sidechain_net_manager(); - bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options); + bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map &options); void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); -private: - peerplays_sidechain_plugin& plugin; - graphene::chain::database& database; - std::vector> net_handlers; +private: + peerplays_sidechain_plugin &plugin; + graphene::chain::database &database; + std::vector> net_handlers; }; -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 517e3c7c..2ef59f39 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,17 +1,17 @@ #include -#include #include +#include #include #include #include #include +#include #include #include #include -#include #include #include @@ -19,287 +19,270 @@ namespace bpo = boost::program_options; namespace graphene { namespace peerplays_sidechain { -namespace detail -{ +namespace detail { -class peerplays_sidechain_plugin_impl -{ - public: - peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin); - virtual ~peerplays_sidechain_plugin_impl(); +class peerplays_sidechain_plugin_impl { +public: + peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin); + virtual ~peerplays_sidechain_plugin_impl(); - void plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg); - void plugin_initialize(const boost::program_options::variables_map& options); - void plugin_startup(); + void plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg); + void plugin_initialize(const boost::program_options::variables_map &opt); + void plugin_startup(); - std::set& get_sons(); - son_id_type& get_current_son_id(); - son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); - std::map& get_private_keys(); - fc::ecc::private_key get_private_key(son_id_type son_id); - fc::ecc::private_key get_private_key(chain::public_key_type public_key); + std::set &get_sons(); + son_id_type &get_current_son_id(); + son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); - void schedule_heartbeat_loop(); - void heartbeat_loop(); - void create_son_down_proposals(); - void create_son_deregister_proposals(); - void recreate_primary_wallet(); - void process_deposits(); - void process_withdrawals(); + void schedule_heartbeat_loop(); + void heartbeat_loop(); + void schedule_son_processing(); + void son_processing(); + void approve_proposals(); + void create_son_down_proposals(); + void create_son_deregister_proposals(); + void recreate_primary_wallet(); + void process_deposits(); + void process_withdrawals(); - private: - peerplays_sidechain_plugin& plugin; +private: + peerplays_sidechain_plugin &plugin; - bool config_ready_son; - bool config_ready_bitcoin; - bool config_ready_peerplays; + boost::program_options::variables_map options; - son_id_type current_son_id; + bool config_ready_son; + bool config_ready_bitcoin; + bool config_ready_peerplays; - std::unique_ptr net_manager; - std::map _private_keys; - std::set _sons; - fc::future _heartbeat_task; + son_id_type current_son_id; - void on_applied_block( const signed_block& b ); - void on_new_objects(const vector& new_object_ids); + std::unique_ptr net_manager; + std::set sons; + std::map private_keys; + fc::future _heartbeat_task; + fc::future _son_processing_task; + void on_applied_block(const signed_block &b); }; -peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : +peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin) : plugin(_plugin), config_ready_son(false), config_ready_bitcoin(false), config_ready_peerplays(false), current_son_id(son_id_type(std::numeric_limits().max())), - net_manager(nullptr) -{ + net_manager(nullptr) { } -peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() -{ +peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { try { - if( _heartbeat_task.valid() ) + if (_heartbeat_task.valid()) _heartbeat_task.cancel_and_wait(__FUNCTION__); - } catch(fc::canceled_exception&) { + } catch (fc::canceled_exception &) { //Expected exception. Move along. - } catch(fc::exception& e) { + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } + + try { + if (_son_processing_task.valid()) + _son_processing_task.cancel_and_wait(__FUNCTION__); + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { edump((e.to_detail_string())); } } void peerplays_sidechain_plugin_impl::plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) -{ + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) { auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); string son_id_example = fc::json::to_string(chain::son_id_type(5)); string son_id_example2 = fc::json::to_string(chain::son_id_type(6)); - cli.add_options() - ("son-id", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()) - ("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()) - ("peerplays-private-key", bpo::value>()->composing()->multitoken()-> - DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), - "Tuple of [PublicKey, WIF private key] (may specify multiple times)") - - ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") - ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") - ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") - ("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user") - ("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password") - ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") - ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") - ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") - ("bitcoin-private-keys", bpo::value>()->composing()->multitoken()-> - DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), - "Tuple of [Bitcoin PublicKey, Bitcoin Private key] (may specify multiple times)") - ; + cli.add_options()("son-id", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()); + cli.add_options()("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()); + cli.add_options()("peerplays-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), + "Tuple of [PublicKey, WIF private key] (may specify multiple times)"); + cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node"); + cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); + cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); + cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); + cli.add_options()("bitcoin-wallet-password", bpo::value(), "Bitcoin wallet password"); + cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), + "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); cfg.add(cli); } -void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map& options) -{ - config_ready_son = (options.count( "son-id" ) || options.count( "son-ids" )) && options.count( "peerplays-private-key" ); +void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map &opt) { + options = opt; + config_ready_son = (options.count("son-id") || options.count("son-ids")) && options.count("peerplays-private-key"); if (config_ready_son) { - LOAD_VALUE_SET(options, "son-id", _sons, chain::son_id_type) + LOAD_VALUE_SET(options, "son-id", sons, chain::son_id_type) if (options.count("son-ids")) - boost::insert(_sons, fc::json::from_string(options.at("son-ids").as()).as>( 5 )); - config_ready_son = config_ready_son && !_sons.empty(); + boost::insert(sons, fc::json::from_string(options.at("son-ids").as()).as>(5)); + config_ready_son = config_ready_son && !sons.empty(); #ifndef SUPPORT_MULTIPLE_SONS - FC_ASSERT( _sons.size() == 1, "Multiple SONs not supported" ); + FC_ASSERT(sons.size() == 1, "Multiple SONs not supported"); #endif - if( options.count("peerplays-private-key") ) - { + if (options.count("peerplays-private-key")) { const std::vector key_id_to_wif_pair_strings = options["peerplays-private-key"].as>(); - for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) - { - auto key_id_to_wif_pair = graphene::app::dejsonify >(key_id_to_wif_pair_string, 5); + for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) { + auto key_id_to_wif_pair = graphene::app::dejsonify>(key_id_to_wif_pair_string, 5); ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first)); fc::optional private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second); - if (!private_key) - { + if (!private_key) { // the key isn't in WIF format; see if they are still passing the old native private key format. This is // just here to ease the transition, can be removed soon - try - { + try { private_key = fc::variant(key_id_to_wif_pair.second, 2).as(1); - } - catch (const fc::exception&) - { + } catch (const fc::exception &) { FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second)); } } - _private_keys[key_id_to_wif_pair.first] = *private_key; + private_keys[key_id_to_wif_pair.first] = *private_key; } + config_ready_son = config_ready_son && !private_keys.empty(); } - } else { + } + if (!config_ready_son) { wlog("Haven't set up SON parameters"); throw; } - plugin.database().applied_block.connect( [&] (const signed_block& b) { on_applied_block(b); } ); - plugin.database().new_objects.connect( [&] (const vector& ids, const flat_set& impacted_accounts) { on_new_objects(ids); } ); - - net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); - - config_ready_bitcoin = options.count( "bitcoin-node-ip" ) && - options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) && - options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) && - options.count( "bitcoin-address" ) && options.count( "bitcoin-public-key" ) && options.count( "bitcoin-private-key" ); - if (config_ready_bitcoin) { - net_manager->create_handler(sidechain_type::bitcoin, options); - ilog("Bitcoin sidechain handler created"); - } else { + config_ready_bitcoin = options.count("bitcoin-node-ip") && + options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && + options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && + /*options.count( "bitcoin-wallet" ) && options.count( "bitcoin-wallet-password" ) &&*/ + options.count("bitcoin-private-key"); + if (!config_ready_bitcoin) { wlog("Haven't set up Bitcoin sidechain parameters"); } //config_ready_ethereum = options.count( "ethereum-node-ip" ) && // options.count( "ethereum-address" ) && options.count( "ethereum-public-key" ) && options.count( "ethereum-private-key" ); - //if (config_ready_ethereum) { - // net_manager->create_handler(sidechain_type::ethereum, options); - // ilog("Ethereum sidechain handler created"); - //} else { + //if (!config_ready_ethereum) { // wlog("Haven't set up Ethereum sidechain parameters"); //} config_ready_peerplays = true; - if (config_ready_peerplays) { - net_manager->create_handler(sidechain_type::peerplays, options); - ilog("Peerplays sidechain handler created"); - } else { + if (!config_ready_peerplays) { wlog("Haven't set up Peerplays sidechain parameters"); } - if (!(config_ready_bitcoin /*&& config_ready_ethereum*/)) { + if (!(config_ready_bitcoin /*&& config_ready_ethereum*/ && config_ready_peerplays)) { wlog("Haven't set up any sidechain parameters"); throw; } } -void peerplays_sidechain_plugin_impl::plugin_startup() -{ +void peerplays_sidechain_plugin_impl::plugin_startup() { + if (config_ready_son) { - ilog("Starting ${n} SON instances", ("n", _sons.size())); + ilog("Starting ${n} SON instances", ("n", sons.size())); schedule_heartbeat_loop(); } else { elog("No sons configured! Please add SON IDs and private keys to configuration."); } + net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); + if (config_ready_bitcoin) { + net_manager->create_handler(sidechain_type::bitcoin, options); ilog("Bitcoin sidechain handler running"); } //if (config_ready_ethereum) { + // net_manager->create_handler(sidechain_type::ethereum, options); // ilog("Ethereum sidechain handler running"); //} if (config_ready_peerplays) { + net_manager->create_handler(sidechain_type::peerplays, options); ilog("Peerplays sidechain handler running"); } + + plugin.database().applied_block.connect([&](const signed_block &b) { + on_applied_block(b); + }); } -std::set& peerplays_sidechain_plugin_impl::get_sons() -{ - return _sons; +std::set &peerplays_sidechain_plugin_impl::get_sons() { + return sons; } -son_id_type& peerplays_sidechain_plugin_impl::get_current_son_id() { +son_id_type &peerplays_sidechain_plugin_impl::get_current_son_id() { return current_son_id; } -son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) -{ - const auto& idx = plugin.database().get_index_type().indices().get(); - auto son_obj = idx.find( son_id ); +son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { + const auto &idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find(son_id); if (son_obj == idx.end()) return {}; return *son_obj; } -bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) -{ - const auto& idx = plugin.database().get_index_type().indices().get(); - auto son_obj = idx.find( son_id ); +bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { + const auto &idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find(son_id); if (son_obj == idx.end()) return false; - const chain::global_property_object& gpo = plugin.database().get_global_properties(); + const chain::global_property_object &gpo = plugin.database().get_global_properties(); vector active_son_ids; active_son_ids.reserve(gpo.active_sons.size()); std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); + [](const son_info &swi) { + return swi.son_id; + }); auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); return (it != active_son_ids.end()); } -std::map& peerplays_sidechain_plugin_impl::get_private_keys() -{ - return _private_keys; -} - -fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) -{ +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) { return get_private_key(get_son_object(son_id).signing_key); } -fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) -{ - auto private_key_itr = _private_keys.find( public_key ); - if( private_key_itr != _private_keys.end() ) { +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) { + auto private_key_itr = private_keys.find(public_key); + if (private_key_itr != private_keys.end()) { return private_key_itr->second; } return {}; } -void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() -{ +void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { fc::time_point now = fc::time_point::now(); int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency(); - fc::time_point next_wakeup( now + fc::seconds( time_to_next_heartbeat ) ); + fc::time_point next_wakeup(now + fc::seconds(time_to_next_heartbeat)); - _heartbeat_task = fc::schedule([this]{heartbeat_loop();}, - next_wakeup, "SON Heartbeat Production"); + _heartbeat_task = fc::schedule([this] { + heartbeat_loop(); + }, + next_wakeup, "SON Heartbeat Production"); } -void peerplays_sidechain_plugin_impl::heartbeat_loop() -{ +void peerplays_sidechain_plugin_impl::heartbeat_loop() { schedule_heartbeat_loop(); - chain::database& d = plugin.database(); + chain::database &d = plugin.database(); - for (son_id_type son_id : _sons) { + for (son_id_type son_id : sons) { if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { ilog("peerplays_sidechain_plugin: sending heartbeat for SON ${son}", ("son", son_id)); @@ -308,14 +291,14 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() op.son_id = son_id; op.ts = fc::time_point::now() + fc::seconds(0); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op); - fc::future fut = fc::async( [&](){ + fc::future fut = fc::async([&]() { try { d.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) + if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}",("e", e.what())); + } catch (fc::exception e) { + ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}", ("e", e.what())); return false; } }); @@ -324,131 +307,32 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() } } -void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() -{ - chain::database& d = plugin.database(); - std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); - chain::son_id_type my_son_id = get_current_son_id(); +void peerplays_sidechain_plugin_impl::schedule_son_processing() { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_son_processing = 500000; - if(sons_to_be_dereg.size() > 0) - { - // We shouldn't raise proposals for the SONs for which a de-reg - // proposal is already raised. - std::set sons_being_dereg = d.get_sons_being_deregistered(); - for( auto& son : sons_to_be_dereg) - { - // New SON to be deregistered - if(sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) - { - // Creating the de-reg proposal - auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); - if(op.valid()) - { - // Signing and pushing into the txs to be included in the block - ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son) ("s", my_son_id)); - chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); - fc::future fut = fc::async( [&](){ - try { - d.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}",("e", e.what())); - return false; - } - }); - fut.wait(fc::seconds(10)); - } - } - } - } + fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing)); + + _son_processing_task = fc::schedule([this] { + son_processing(); + }, + next_wakeup, "SON Processing"); } -void peerplays_sidechain_plugin_impl::create_son_down_proposals() -{ - auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { - chain::database& d = plugin.database(); - const chain::global_property_object& gpo = d.get_global_properties(); - - chain::son_report_down_operation son_down_op; - son_down_op.payer = GRAPHENE_SON_ACCOUNT; - son_down_op.son_id = son_id; - son_down_op.down_ts = last_active_ts; - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.push_back( op_wrapper( son_down_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( d.head_block_time().sec_since_epoch() + lifetime ); - return proposal_op; - }; - - chain::database& d = plugin.database(); - const chain::global_property_object& gpo = d.get_global_properties(); - const chain::dynamic_global_property_object& dgpo = d.get_dynamic_global_properties(); - const auto& idx = d.get_index_type().indices().get(); - std::set sons_being_reported_down = d.get_sons_being_reported_down(); - chain::son_id_type my_son_id = get_current_son_id(); - for(auto son_inf: gpo.active_sons) { - if(my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())){ - continue; - } - auto son_obj = idx.find( son_inf.son_id ); - auto stats = son_obj->statistics(d); - fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); - int64_t down_threshold = gpo.parameters.son_down_time(); - if(((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { - ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}",("t",std::string(object_id_type(son_obj->id)))("s",std::string(object_id_type(my_son_id)))); - chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); - chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); - fc::future fut = fc::async( [&](){ - try { - d.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}",("e", e.what())); - return false; - } - }); - fut.wait(fc::seconds(10)); - } - } -} - -void peerplays_sidechain_plugin_impl::recreate_primary_wallet() -{ - net_manager->recreate_primary_wallet(); -} - -void peerplays_sidechain_plugin_impl::process_deposits() -{ - net_manager->process_deposits(); -} - -void peerplays_sidechain_plugin_impl::process_withdrawals() -{ - net_manager->process_withdrawals(); -} - -void peerplays_sidechain_plugin_impl::on_applied_block( const signed_block& b ) -{ - chain::database& d = plugin.database(); - const chain::global_property_object& gpo = d.get_global_properties(); - bool latest_block = ((fc::time_point::now() - b.timestamp) < fc::microseconds(gpo.parameters.block_interval * 1000000)); - if(gpo.active_sons.size() <= 0 || !latest_block) { +void peerplays_sidechain_plugin_impl::son_processing() { + if (plugin.database().get_global_properties().active_sons.size() <= 0) { return; } - chain::son_id_type next_son_id = d.get_scheduled_son(1); - ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}",("son", next_son_id)); + chain::son_id_type next_son_id = plugin.database().get_scheduled_son(1); + ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}", ("son", next_son_id)); - // check if we control scheduled SON - if( _sons.find( next_son_id ) != _sons.end() ) { + // Tasks that are executed by all active SONs, no matter if scheduled + // E.g. sending approvals and signing + approve_proposals(); + + // Tasks that are executed by scheduled and active SON + if (sons.find(next_son_id) != sons.end()) { current_son_id = next_son_id; @@ -461,166 +345,251 @@ void peerplays_sidechain_plugin_impl::on_applied_block( const signed_block& b ) process_deposits(); process_withdrawals(); - } } -void peerplays_sidechain_plugin_impl::on_new_objects(const vector& new_object_ids) -{ +void peerplays_sidechain_plugin_impl::approve_proposals() { - auto approve_proposal = [ & ]( const chain::son_id_type& son_id, const chain::proposal_id_type& proposal_id ) - { - ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id) ("s", son_id)); + auto approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_id_type &proposal_id) { + ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id)); chain::proposal_update_operation puo; puo.fee_paying_account = get_son_object(son_id).son_account; puo.proposal = proposal_id; - puo.active_approvals_to_add = { get_son_object(son_id).son_account }; + puo.active_approvals_to_add = {get_son_object(son_id).son_account}; chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo); - fc::future fut = fc::async( [&](){ + fc::future fut = fc::async([&]() { try { plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) + if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch(fc::exception e){ - ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}",("e", e.what())); + } catch (fc::exception e) { + ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}", ("e", e.what())); return false; } }); fut.wait(fc::seconds(10)); }; - for(auto object_id: new_object_ids) { - if( object_id.is() ) { + const auto &idx = plugin.database().get_index_type().indices().get(); + vector proposals; + for (const auto &proposal : idx) { + proposals.push_back(proposal.id); + } - for (son_id_type son_id : _sons) { - if (!is_active_son(son_id)) { - continue; + for (const auto proposal_id : proposals) { + for (son_id_type son_id : sons) { + if (!is_active_son(son_id)) { + continue; + } + + const object *obj = plugin.database().find_object(proposal_id); + const chain::proposal_object *proposal_ptr = dynamic_cast(obj); + if (proposal_ptr == nullptr) { + continue; + } + const proposal_object proposal = *proposal_ptr; + + if (proposal.available_active_approvals.find(get_son_object(son_id).son_account) != proposal.available_active_approvals.end()) { + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal(son_id, proposal.id); + continue; + } + } + } +} + +void peerplays_sidechain_plugin_impl::create_son_down_proposals() { + auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { + chain::database &d = plugin.database(); + const chain::global_property_object &gpo = d.get_global_properties(); + + chain::son_report_down_operation son_down_op; + son_down_op.payer = GRAPHENE_SON_ACCOUNT; + son_down_op.son_id = son_id; + son_down_op.down_ts = last_active_ts; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.push_back(op_wrapper(son_down_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); + return proposal_op; + }; + + chain::database &d = plugin.database(); + const chain::global_property_object &gpo = d.get_global_properties(); + const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); + const auto &idx = d.get_index_type().indices().get(); + std::set sons_being_reported_down = d.get_sons_being_reported_down(); + chain::son_id_type my_son_id = get_current_son_id(); + for (auto son_inf : gpo.active_sons) { + if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { + continue; + } + auto son_obj = idx.find(son_inf.son_id); + auto stats = son_obj->statistics(d); + fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); + int64_t down_threshold = gpo.parameters.son_down_time(); + if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && + ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { + ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); + chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); + fc::future fut = fc::async([&]() { + try { + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}", ("e", e.what())); + return false; } + }); + fut.wait(fc::seconds(10)); + } + } +} - const object* obj = plugin.database().find_object(object_id); - const chain::proposal_object* proposal = dynamic_cast(obj); +void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { + chain::database &d = plugin.database(); + std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); + chain::son_id_type my_son_id = get_current_son_id(); - if(proposal == nullptr || (proposal->available_active_approvals.find(get_son_object(son_id).son_account) != proposal->available_active_approvals.end())) { - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; - } - - if(proposal->proposed_transaction.operations.size() == 1 - && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal( son_id, proposal->id ); - continue; + if (sons_to_be_dereg.size() > 0) { + // We shouldn't raise proposals for the SONs for which a de-reg + // proposal is already raised. + std::set sons_being_dereg = d.get_sons_being_deregistered(); + for (auto &son : sons_to_be_dereg) { + // New SON to be deregistered + if (sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) { + // Creating the de-reg proposal + auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); + if (op.valid()) { + // Signing and pushing into the txs to be included in the block + ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); + fc::future fut = fc::async([&]() { + try { + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}", ("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); } } } } } +void peerplays_sidechain_plugin_impl::recreate_primary_wallet() { + net_manager->recreate_primary_wallet(); +} + +void peerplays_sidechain_plugin_impl::process_deposits() { + net_manager->process_deposits(); +} + +void peerplays_sidechain_plugin_impl::process_withdrawals() { + net_manager->process_withdrawals(); +} + +void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { + schedule_son_processing(); +} + } // end namespace detail peerplays_sidechain_plugin::peerplays_sidechain_plugin() : - my( new detail::peerplays_sidechain_plugin_impl(*this) ) -{ + my(new detail::peerplays_sidechain_plugin_impl(*this)) { } -peerplays_sidechain_plugin::~peerplays_sidechain_plugin() -{ +peerplays_sidechain_plugin::~peerplays_sidechain_plugin() { return; } -std::string peerplays_sidechain_plugin::plugin_name()const -{ +std::string peerplays_sidechain_plugin::plugin_name() const { return "peerplays_sidechain"; } void peerplays_sidechain_plugin::plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) -{ - ilog("peerplays sidechain plugin: plugin_set_program_options()"); - my->plugin_set_program_options(cli, cfg); + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) { + my->plugin_set_program_options(cli, cfg); } -void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ +void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) { ilog("peerplays sidechain plugin: plugin_initialize()"); my->plugin_initialize(options); } -void peerplays_sidechain_plugin::plugin_startup() -{ +void peerplays_sidechain_plugin::plugin_startup() { ilog("peerplays sidechain plugin: plugin_startup()"); my->plugin_startup(); } -std::set& peerplays_sidechain_plugin::get_sons() -{ - return my->get_sons(); +std::set &peerplays_sidechain_plugin::get_sons() { + return my->get_sons(); } -son_id_type& peerplays_sidechain_plugin::get_current_son_id() { - return my->get_current_son_id(); +son_id_type &peerplays_sidechain_plugin::get_current_son_id() { + return my->get_current_son_id(); } -son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) -{ +son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); } -bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) -{ +bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { return my->is_active_son(son_id); } -std::map& peerplays_sidechain_plugin::get_private_keys() -{ - return my->get_private_keys(); -} - -fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) -{ +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) { return my->get_private_key(son_id); } -fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) -{ - return my->get_private_key(public_key); +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) { + return my->get_private_key(public_key); } -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 062c3094..19188440 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -8,10 +8,9 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : - plugin(_plugin), - database(_plugin.database()) -{ +sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + plugin(_plugin), + database(_plugin.database()) { } sidechain_net_handler::~sidechain_net_handler() { @@ -24,44 +23,52 @@ graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidecha std::vector sidechain_net_handler::get_sidechain_deposit_addresses() { std::vector result; - const auto& sidechain_addresses_idx = database.get_index_type(); - const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); - const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, - [&result] (const sidechain_address_object& sao) { - result.push_back(sao.deposit_address); - }); + [&result](const sidechain_address_object &sao) { + result.push_back(sao.deposit_address); + }); return result; } std::vector sidechain_net_handler::get_sidechain_withdraw_addresses() { std::vector result; - const auto& sidechain_addresses_idx = database.get_index_type(); - const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); - const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, - [&result] (const sidechain_address_object& sao) { - result.push_back(sao.withdraw_address); - }); + [&result](const sidechain_address_object &sao) { + result.push_back(sao.withdraw_address); + }); return result; } -void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data& sed) { - ilog( "sidechain_event_data:" ); - ilog( " timestamp: ${timestamp}", ( "timestamp", sed.timestamp ) ); - ilog( " sidechain: ${sidechain}", ( "sidechain", sed.sidechain ) ); - ilog( " sidechain_uid: ${uid}", ( "uid", sed.sidechain_uid ) ); - ilog( " sidechain_transaction_id: ${transaction_id}", ( "transaction_id", sed.sidechain_transaction_id ) ); - ilog( " sidechain_from: ${from}", ( "from", sed.sidechain_from ) ); - ilog( " sidechain_to: ${to}", ( "to", sed.sidechain_to ) ); - ilog( " sidechain_currency: ${currency}", ( "currency", sed.sidechain_currency ) ); - ilog( " sidechain_amount: ${amount}", ( "amount", sed.sidechain_amount ) ); - ilog( " peerplays_from: ${peerplays_from}", ( "peerplays_from", sed.peerplays_from ) ); - ilog( " peerplays_to: ${peerplays_to}", ( "peerplays_to", sed.peerplays_to ) ); - ilog( " peerplays_asset: ${peerplays_asset}", ( "peerplays_asset", sed.peerplays_asset ) ); +std::string sidechain_net_handler::get_private_key(std::string public_key) { + auto private_key_itr = private_keys.find(public_key); + if (private_key_itr != private_keys.end()) { + return private_key_itr->second; + } + return std::string(); +} - const chain::global_property_object& gpo = database.get_global_properties(); +void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data &sed) { + ilog("sidechain_event_data:"); + ilog(" timestamp: ${timestamp}", ("timestamp", sed.timestamp)); + ilog(" sidechain: ${sidechain}", ("sidechain", sed.sidechain)); + ilog(" sidechain_uid: ${uid}", ("uid", sed.sidechain_uid)); + ilog(" sidechain_transaction_id: ${transaction_id}", ("transaction_id", sed.sidechain_transaction_id)); + ilog(" sidechain_from: ${from}", ("from", sed.sidechain_from)); + ilog(" sidechain_to: ${to}", ("to", sed.sidechain_to)); + ilog(" sidechain_currency: ${currency}", ("currency", sed.sidechain_currency)); + ilog(" sidechain_amount: ${amount}", ("amount", sed.sidechain_amount)); + ilog(" peerplays_from: ${peerplays_from}", ("peerplays_from", sed.peerplays_from)); + ilog(" peerplays_to: ${peerplays_to}", ("peerplays_to", sed.peerplays_to)); + ilog(" peerplays_asset: ${peerplays_asset}", ("peerplays_asset", sed.peerplays_asset)); + + const chain::global_property_object &gpo = database.get_global_properties(); // Deposit request if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") != 0)) { @@ -83,18 +90,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ if (plugin.is_active_son(son_id)) { proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - //ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id)); signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) + if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + } catch (fc::exception e) { + ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -104,9 +110,9 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ // Withdrawal request if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") == 0)) { // BTC Payout only (for now) - const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); - const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin)); - if ( addr_itr == sidechain_addresses_idx.end() ) + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin)); + if (addr_itr == sidechain_addresses_idx.end()) return; son_wallet_withdraw_create_operation op; @@ -117,27 +123,26 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ op.peerplays_transaction_id = sed.sidechain_transaction_id; op.peerplays_from = sed.peerplays_from; op.peerplays_asset = sed.peerplays_asset; - op.withdraw_sidechain = sidechain_type::bitcoin; // BTC payout only (for now) - op.withdraw_address = addr_itr->withdraw_address; // BTC payout only (for now) - op.withdraw_currency = "BTC"; // BTC payout only (for now) + op.withdraw_sidechain = sidechain_type::bitcoin; // BTC payout only (for now) + op.withdraw_address = addr_itr->withdraw_address; // BTC payout only (for now) + op.withdraw_currency = "BTC"; // BTC payout only (for now) op.withdraw_amount = sed.peerplays_asset.amount * 1000; // BTC payout only (for now) for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_active_son(son_id)) { proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - //ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id)); signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) + if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id) ("e", e.what())); + } catch (fc::exception e) { + ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -147,79 +152,84 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ FC_ASSERT(false, "Invalid sidechain event"); } +void sidechain_net_handler::process_deposits() { + const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, + [&](const son_wallet_deposit_object &swdo) { + ilog("Deposit to process: ${swdo}", ("swdo", swdo)); + + process_deposit(swdo); + + const chain::global_property_object &gpo = plugin.database().get_global_properties(); + + son_wallet_deposit_process_operation p_op; + p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.son_wallet_deposit_id = swdo.id; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::process_withdrawals() { + const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, + [&](const son_wallet_withdraw_object &swwo) { + ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); + + process_withdrawal(swwo); + + const chain::global_property_object &gpo = plugin.database().get_global_properties(); + + son_wallet_withdraw_process_operation p_op; + p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.son_wallet_withdraw_id = swwo.id; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; + proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); + } + }); +} + void sidechain_net_handler::recreate_primary_wallet() { FC_ASSERT(false, "recreate_primary_wallet not implemented"); } -void sidechain_net_handler::process_deposits() { - const auto& idx = plugin.database().get_index_type().indices().get(); - const auto& idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - - std::for_each(idx_range.first, idx_range.second, - [&] (const son_wallet_deposit_object& swdo) { - - ilog("Deposit to process: ${swdo}", ("swdo", swdo)); - - process_deposit(swdo); - - const chain::global_property_object& gpo = plugin.database().get_global_properties(); - - son_wallet_deposit_process_operation p_op; - p_op.payer = GRAPHENE_SON_ACCOUNT; - p_op.son_wallet_deposit_id = swdo.id; - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); - } - }); +void sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) { + FC_ASSERT(false, "process_deposit not implemented"); } -void sidechain_net_handler::process_withdrawals() { - const auto& idx = plugin.database().get_index_type().indices().get(); - const auto& idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - - std::for_each(idx_range.first, idx_range.second, - [&] (const son_wallet_withdraw_object& swwo) { - - ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); - - process_withdrawal(swwo); - - const chain::global_property_object& gpo = plugin.database().get_global_properties(); - - son_wallet_withdraw_process_operation p_op; - p_op.payer = GRAPHENE_SON_ACCOUNT; - p_op.son_wallet_withdraw_id = swwo.id; - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime ); - - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what())); - } - }); +void sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) { + FC_ASSERT(false, "process_withdrawal not implemented"); } -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index b6dd478d..7eaa4d44 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -4,28 +4,32 @@ #include #include -#include #include +#include #include #include #include #include +#include #include #include #include -#include namespace graphene { namespace peerplays_sidechain { // ============================================================================= -bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password ): - ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password ) -{ +bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password) : + ip(_ip), + rpc_port(_rpc), + user(_user), + password(_password), + wallet(_wallet), + wallet_password(_wallet_password) { authorization.key = "Authorization"; - authorization.val = "Basic " + fc::base64_encode( user + ":" + password ); + authorization.val = "Basic " + fc::base64_encode(user + ":" + password); } bool bitcoin_rpc_client::connection_is_not_defined() const { @@ -62,7 +66,7 @@ std::string bitcoin_rpc_client::addmultisigaddress(const std::vector bitcoin_rpc_client::listunspent() { } } } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str())); + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } return result; } @@ -254,18 +312,44 @@ std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(con } } } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str())); + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } return result; } +std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"loadwallet\", \"method\": " + "\"loadwallet\", \"params\": [\"" + + filename + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return std::string(); + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " "\"method\": \"sendrawtransaction\", \"params\": [") + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); - ilog(body); - const auto reply = send_post_request(body); if (reply.body.empty()) { @@ -284,7 +368,7 @@ void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { if (error_code == -27) // transaction already in block chain return; - wlog("BTC tx is not sent! Reply: ${msg}", ("msg", ss.str())); + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } } @@ -298,8 +382,6 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & std::string params = "\"" + tx_hash + "\""; body = body + params + std::string("]}"); - ilog(body); - const auto reply = send_post_request(body); if (reply.body.empty()) { @@ -316,64 +398,161 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & } if (json.count("error") && !json.get_child("error").empty()) { - wlog("BTC sign_raw_transaction_with_wallet failed! Reply: ${msg}", ("msg", ss.str())); + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } return ""; } -fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) -{ +std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"unloadwallet\", \"method\": " + "\"unloadwallet\", \"params\": [\"" + + filename + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return std::string(); + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::walletlock() { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": " + "\"walletlock\", \"params\": [] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return std::string(); + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " + "\"walletpassphrase\", \"params\": [\"" + + passphrase + "\", " + std::to_string(timeout) + "] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return false; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return true; + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return false; +} + +fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) { fc::http::connection conn; - conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); - const auto url = "http://" + ip + ":" + std::to_string( rpc_port ); + std::string url = "http://" + ip + ":" + std::to_string(rpc_port); - return conn.request( "POST", url, body, fc::http::headers{authorization} ); + if (wallet.length() > 0) { + url = url + "/wallet/" + wallet; + } + + fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); + + if (show_log) { + ilog("Request URL: ${url}", ("url", url)); + ilog("Request: ${body}", ("body", body)); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + ilog("Response: ${ss}", ("ss", ss.str())); + } + + return reply; } // ============================================================================= -zmq_listener::zmq_listener( std::string _ip, uint32_t _zmq ): ip( _ip ), zmq_port( _zmq ), ctx( 1 ), socket( ctx, ZMQ_SUB ) { - std::thread( &zmq_listener::handle_zmq, this ).detach(); +zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : + ip(_ip), + zmq_port(_zmq), + ctx(1), + socket(ctx, ZMQ_SUB) { + std::thread(&zmq_listener::handle_zmq, this).detach(); } std::vector zmq_listener::receive_multipart() { std::vector msgs; int32_t more; - size_t more_size = sizeof( more ); - while ( true ) { + size_t more_size = sizeof(more); + while (true) { zmq::message_t msg; - socket.recv( &msg, 0 ); - socket.getsockopt( ZMQ_RCVMORE, &more, &more_size ); + socket.recv(&msg, 0); + socket.getsockopt(ZMQ_RCVMORE, &more, &more_size); - if ( !more ) + if (!more) break; - msgs.push_back( std::move(msg) ); + msgs.push_back(std::move(msg)); } return msgs; } void zmq_listener::handle_zmq() { - socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 9 ); + socket.setsockopt(ZMQ_SUBSCRIBE, "hashblock", 9); //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); - socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) ); + socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); - while ( true ) { + while (true) { auto msg = receive_multipart(); - const auto header = std::string( static_cast( msg[0].data() ), msg[0].size() ); - const auto block_hash = boost::algorithm::hex( std::string( static_cast( msg[1].data() ), msg[1].size() ) ); + const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); + const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - event_received( block_hash ); + event_received(block_hash); } } // ============================================================================= -sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::bitcoin; @@ -382,64 +561,70 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain rpc_port = options.at("bitcoin-node-rpc-port").as(); rpc_user = options.at("bitcoin-node-rpc-user").as(); rpc_password = options.at("bitcoin-node-rpc-password").as(); + wallet = ""; + if (options.count("bitcoin-wallet")) { + wallet = options.at("bitcoin-wallet").as(); + } + wallet_password = ""; + if (options.count("bitcoin-wallet-password")) { + wallet_password = options.at("bitcoin-wallet-password").as(); + } - if( options.count("bitcoin-private-keys") ) - { - const std::vector pub_priv_keys = options["bitcoin-private-keys"].as>(); - for (const std::string& itr_key_pair : pub_priv_keys) - { - auto key_pair = graphene::app::dejsonify >(itr_key_pair, 5); - ilog("Public Key: ${public}", ("public", key_pair.first)); - if(!key_pair.first.length() || !key_pair.second.length()) - { + if (options.count("bitcoin-private-key")) { + const std::vector pub_priv_keys = options["bitcoin-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Bitcoin Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { FC_THROW("Invalid public private key pair."); } - _private_keys[key_pair.first] = key_pair.second; + private_keys[key_pair.first] = key_pair.second; } } fc::http::connection conn; try { - conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); - } catch ( fc::exception e ) { - elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip) ("port", rpc_port) ); - FC_ASSERT( false ); + conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); + } catch (fc::exception e) { + elog("No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip)("port", rpc_port)); + FC_ASSERT(false); } - listener = std::unique_ptr( new zmq_listener( ip, zmq_port ) ); - bitcoin_client = std::unique_ptr( new bitcoin_rpc_client( ip, rpc_port, rpc_user, rpc_password ) ); + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(ip, rpc_port, rpc_user, rpc_password, wallet, wallet_password)); + if (!wallet.empty()) { + bitcoin_client->loadwallet(wallet); + } - listener->event_received.connect([this]( const std::string& event_data ) { - std::thread( &sidechain_net_handler_bitcoin::handle_event, this, event_data ).detach(); - } ); + listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); + listener->event_received.connect([this](const std::string &event_data) { + std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); + }); } sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { } void sidechain_net_handler_bitcoin::recreate_primary_wallet() { - const auto& swi = database.get_index_type().indices().get(); + const auto &swi = database.get_index_type().indices().get(); const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { if ((active_sw->addresses.find(sidechain_type::bitcoin) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain_type::bitcoin).empty())) { - const chain::global_property_object& gpo = database.get_global_properties(); + const chain::global_property_object &gpo = database.get_global_properties(); auto active_sons = gpo.active_sons; vector son_pubkeys_bitcoin; - for ( const son_info& si : active_sons ) { + for (const son_info &si : active_sons) { son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); } string reply_str = create_multisignature_wallet(son_pubkeys_bitcoin); - ilog(reply_str); - std::stringstream active_pw_ss(reply_str); boost::property_tree::ptree active_pw_pt; - boost::property_tree::read_json( active_pw_ss, active_pw_pt ); - if( active_pw_pt.count( "error" ) && active_pw_pt.get_child( "error" ).empty() ) { + boost::property_tree::read_json(active_pw_ss, active_pw_pt); + if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { std::stringstream res; boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); @@ -452,17 +637,17 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back( op_wrapper( op ) ); - uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3; - proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime ); + proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); - if(plugin.app().p2p_node()) + if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch(fc::exception e){ - ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + } catch (fc::exception e) { + ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; } @@ -470,7 +655,7 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { if (prev_sw != swi.rend()) { std::stringstream prev_sw_ss(prev_sw->addresses.at(sidechain_type::bitcoin)); boost::property_tree::ptree prev_sw_pt; - boost::property_tree::read_json( prev_sw_ss, prev_sw_pt ); + boost::property_tree::read_json(prev_sw_ss, prev_sw_pt); std::string active_pw_address = active_pw_pt.get_child("result").get("address"); std::string prev_pw_address = prev_sw_pt.get("address"); @@ -482,63 +667,57 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { } } -void sidechain_net_handler_bitcoin::process_deposits() { - sidechain_net_handler::process_deposits(); -} - void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { - ilog(__FUNCTION__); transfer_deposit_to_primary_wallet(swdo); } -void sidechain_net_handler_bitcoin::process_withdrawals() { - sidechain_net_handler::process_withdrawals(); -} - void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { - ilog(__FUNCTION__); transfer_withdrawal_from_primary_wallet(swwo); } -std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) { +std::string sidechain_net_handler_bitcoin::create_multisignature_wallet(const std::vector public_keys) { return bitcoin_client->addmultisigaddress(public_keys); } -std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) { +std::string sidechain_net_handler_bitcoin::transfer(const std::string &from, const std::string &to, const uint64_t amount) { return ""; } -std::string sidechain_net_handler_bitcoin::sign_transaction( const std::string& transaction ) { +std::string sidechain_net_handler_bitcoin::sign_transaction(const std::string &transaction) { return ""; } -std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& transaction ) { +std::string sidechain_net_handler_bitcoin::send_transaction(const std::string &transaction) { return ""; } -std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet ( const std::string& tx_json ) -{ +std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet(const std::string &tx_json) { std::string reply_str = tx_json; - ilog(reply_str); - std::stringstream ss_utx(reply_str); boost::property_tree::ptree pt; - boost::property_tree::read_json( ss_utx, pt ); + boost::property_tree::read_json(ss_utx, pt); - if( !(pt.count( "error" ) && pt.get_child( "error" ).empty()) || !pt.count("result") ) { + if (!(pt.count("error") && pt.get_child("error").empty()) || !pt.count("result")) { return ""; } + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 60); + } + std::string unsigned_tx_hex = pt.get("result"); reply_str = bitcoin_client->signrawtransactionwithwallet(unsigned_tx_hex); - ilog(reply_str); std::stringstream ss_stx(reply_str); boost::property_tree::ptree stx_json; - boost::property_tree::read_json( ss_stx, stx_json ); + boost::property_tree::read_json(ss_stx, stx_json); - if( !(stx_json.count( "error" ) && stx_json.get_child( "error" ).empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex") ) { + //if (!wallet_password.empty()) { + // bitcoin_client->walletlock(); + //} + + if (!(stx_json.count("error") && stx_json.get_child("error").empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex")) { return ""; } @@ -549,31 +728,25 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet return reply_str; } -std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& from_address, const std::string& to_address) -{ +std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &from_address, const std::string &to_address) { uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); - double min_amount = ((double)fee_rate/100000000.0); // Account only for relay fee for now + double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now double total_amount = 0.0; std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(from_address, 0); - if(unspent_utxo.size() == 0) - { - wlog("Failed to find UTXOs to spend for ${pw}",("pw", from_address)); + if (unspent_utxo.size() == 0) { + wlog("Failed to find UTXOs to spend for ${pw}", ("pw", from_address)); return ""; - } - else - { - for(const auto& utx: unspent_utxo) - { + } else { + for (const auto &utx : unspent_utxo) { total_amount += utx.amount_; } - if(min_amount >= total_amount) - { - wlog("Failed not enough BTC to transfer from ${fa}",("fa", from_address)); + if (min_amount >= total_amount) { + wlog("Failed not enough BTC to transfer from ${fa}", ("fa", from_address)); return ""; } } @@ -585,9 +758,8 @@ std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& f return sign_and_send_transaction_with_wallet(reply_str); } -std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (const son_wallet_deposit_object &swdo) -{ - const auto& idx = database.get_index_type().indices().get(); +std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo) { + const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { return ""; @@ -597,19 +769,19 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (c std::stringstream ss(pw_address_json); boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); + boost::property_tree::read_json(ss, json); std::string pw_address = json.get("address"); std::string txid = swdo.sidechain_transaction_id; std::string suid = swdo.sidechain_uid; - std::string nvout = suid.substr(suid.find_last_of("-")+1); + std::string nvout = suid.substr(suid.find_last_of("-") + 1); uint64_t deposit_amount = swdo.sidechain_amount.value; uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); deposit_amount -= fee_rate; // Deduct minimum relay fee - double transfer_amount = (double)deposit_amount/100000000.0; + double transfer_amount = (double)deposit_amount / 100000000.0; std::vector ins; fc::flat_map outs; @@ -627,10 +799,9 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (c } std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) { - const auto& idx = database.get_index_type().indices().get(); + const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) - { + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { return ""; } @@ -638,7 +809,7 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall std::stringstream ss(pw_address_json); boost::property_tree::ptree json; - boost::property_tree::read_json( ss, json ); + boost::property_tree::read_json(ss, json); std::string pw_address = json.get("address"); @@ -650,29 +821,23 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall double total_amount = 0.0; std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); - if(unspent_utxo.size() == 0) - { - wlog("Failed to find UTXOs to spend for ${pw}",("pw", pw_address)); + if (unspent_utxo.size() == 0) { + wlog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); return ""; - } - else - { - for(const auto& utx: unspent_utxo) - { + } else { + for (const auto &utx : unspent_utxo) { total_amount += utx.amount_; } - if(min_amount > total_amount) - { - wlog("Failed not enough BTC to spend for ${pw}",("pw", pw_address)); + if (min_amount > total_amount) { + wlog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); return ""; } } fc::flat_map outs; outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; - if((total_amount - min_amount) > 0.0) - { + if ((total_amount - min_amount) > 0.0) { outs[pw_address] = total_amount - min_amount; } @@ -680,20 +845,21 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall return sign_and_send_transaction_with_wallet(reply_str); } -void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { +void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { std::string block = bitcoin_client->getblock(event_data); - if( block != "" ) { - const auto& vins = extract_info_from_block( block ); + if (block != "") { + const auto &vins = extract_info_from_block(block); - const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); - for( const auto& v : vins ) { - const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, v.address)); - if ( addr_itr == sidechain_addresses_idx.end() ) + for (const auto &v : vins) { + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, v.address)); + if (addr_itr == sidechain_addresses_idx.end()) continue; std::stringstream ss; - ss << "bitcoin" << "-" << v.out.hash_tx << "-" << v.out.n_vout; + ss << "bitcoin" + << "-" << v.out.hash_tx << "-" << v.out.n_vout; std::string sidechain_uid = ss.str(); sidechain_event_data sed; @@ -713,31 +879,32 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data } } -std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) { - std::stringstream ss( _block ); +std::vector sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) { + std::stringstream ss(_block); boost::property_tree::ptree block; - boost::property_tree::read_json( ss, block ); + boost::property_tree::read_json(ss, block); std::vector result; - for (const auto& tx_child : block.get_child("tx")) { - const auto& tx = tx_child.second; + for (const auto &tx_child : block.get_child("tx")) { + const auto &tx = tx_child.second; - for ( const auto& o : tx.get_child("vout") ) { + for (const auto &o : tx.get_child("vout")) { const auto script = o.second.get_child("scriptPubKey"); - if( !script.count("addresses") ) continue; + if (!script.count("addresses")) + continue; - for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses? + for (const auto &addr : script.get_child("addresses")) { // in which cases there can be more addresses? const auto address_base58 = addr.second.get_value(); info_for_vin vin; vin.out.hash_tx = tx.get_child("txid").get_value(); - string amount = o.second.get_child( "value" ).get_value(); + string amount = o.second.get_child("value").get_value(); amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); vin.out.amount = std::stoll(amount); - vin.out.n_vout = o.second.get_child( "n" ).get_value(); + vin.out.n_vout = o.second.get_child("n").get_value(); vin.address = address_base58; - result.push_back( vin ); + result.push_back(vin); } } } @@ -747,5 +914,4 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block // ============================================================================= -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index f5d47e39..a30dc4d7 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -4,25 +4,27 @@ #include #include -#include #include +#include #include #include #include #include +#include #include #include #include -#include namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : +sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::peerplays; - plugin.database().applied_block.connect( [&] (const signed_block& b) { on_applied_block(b); } ); + plugin.database().applied_block.connect([&](const signed_block &b) { + on_applied_block(b); + }); } sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { @@ -31,69 +33,61 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { void sidechain_net_handler_peerplays::recreate_primary_wallet() { } -void sidechain_net_handler_peerplays::process_deposits() { - sidechain_net_handler::process_deposits(); +void sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { } -void sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object& swdo) { +void sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { } -void sidechain_net_handler_peerplays::process_withdrawals() { - sidechain_net_handler::process_withdrawals(); -} - -void sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object& swwo) { -} - -std::string sidechain_net_handler_peerplays::create_multisignature_wallet( const std::vector public_keys ) { +std::string sidechain_net_handler_peerplays::create_multisignature_wallet(const std::vector public_keys) { return ""; } -std::string sidechain_net_handler_peerplays::transfer( const std::string& from, const std::string& to, const uint64_t amount ) { +std::string sidechain_net_handler_peerplays::transfer(const std::string &from, const std::string &to, const uint64_t amount) { return ""; } -std::string sidechain_net_handler_peerplays::sign_transaction( const std::string& transaction ) { +std::string sidechain_net_handler_peerplays::sign_transaction(const std::string &transaction) { return ""; } -std::string sidechain_net_handler_peerplays::send_transaction( const std::string& transaction ) { +std::string sidechain_net_handler_peerplays::send_transaction(const std::string &transaction) { return ""; } -void sidechain_net_handler_peerplays::on_applied_block(const signed_block& b) { - for (const auto& trx: b.transactions) { - size_t operation_index = -1; - for (auto op: trx.operations) { - operation_index = operation_index + 1; - if (op.which() == operation::tag::value){ - transfer_operation transfer_op = op.get(); - if (transfer_op.to != GRAPHENE_SON_ACCOUNT) { - continue; - } - - std::stringstream ss; - ss << "peerplays" << "-" << trx.id().str() << "-" << operation_index; - std::string sidechain_uid = ss.str(); - - sidechain_event_data sed; - sed.timestamp = plugin.database().head_block_time(); - sed.sidechain = sidechain_type::peerplays; - sed.sidechain_uid = sidechain_uid; - sed.sidechain_transaction_id = trx.id().str(); - sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); - sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); - sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(plugin.database()).symbol; - sed.sidechain_amount = transfer_op.amount.amount; - sed.peerplays_from = transfer_op.from; - sed.peerplays_to = transfer_op.to; - // We should calculate exchange rate between CORE/TEST and other Peerplays asset - sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(plugin.database()).options.core_exchange_rate.quote.amount); - sidechain_event_data_received(sed); +void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { + for (const auto &trx : b.transactions) { + size_t operation_index = -1; + for (auto op : trx.operations) { + operation_index = operation_index + 1; + if (op.which() == operation::tag::value) { + transfer_operation transfer_op = op.get(); + if (transfer_op.to != GRAPHENE_SON_ACCOUNT) { + continue; } - } - } + + std::stringstream ss; + ss << "peerplays" + << "-" << trx.id().str() << "-" << operation_index; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = plugin.database().head_block_time(); + sed.sidechain = sidechain_type::peerplays; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = trx.id().str(); + sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); + sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); + sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(plugin.database()).symbol; + sed.sidechain_amount = transfer_op.amount.amount; + sed.peerplays_from = transfer_op.from; + sed.peerplays_to = transfer_op.to; + // We should calculate exchange rate between CORE/TEST and other Peerplays asset + sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(plugin.database()).options.core_exchange_rate.quote.amount); + sidechain_event_data_received(sed); + } + } + } } -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index b9e5dd8d..05b6b5b8 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -7,56 +7,54 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin) : - plugin(_plugin), - database(_plugin.database()) -{ +sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) : + plugin(_plugin), + database(_plugin.database()) { } sidechain_net_manager::~sidechain_net_manager() { } -bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options) { +bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map &options) { bool ret_val = false; switch (sidechain) { - case sidechain_type::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - case sidechain_type::peerplays: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - default: - assert(false); + case sidechain_type::bitcoin: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; + } + case sidechain_type::peerplays: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; + } + default: + assert(false); } return ret_val; } void sidechain_net_manager::recreate_primary_wallet() { - for ( size_t i = 0; i < net_handlers.size(); i++ ) { + for (size_t i = 0; i < net_handlers.size(); i++) { net_handlers.at(i)->recreate_primary_wallet(); } } void sidechain_net_manager::process_deposits() { - for ( size_t i = 0; i < net_handlers.size(); i++ ) { + for (size_t i = 0; i < net_handlers.size(); i++) { net_handlers.at(i)->process_deposits(); } } void sidechain_net_manager::process_withdrawals() { - for ( size_t i = 0; i < net_handlers.size(); i++ ) { + for (size_t i = 0; i < net_handlers.size(); i++) { net_handlers.at(i)->process_withdrawals(); } } -} } // graphene::peerplays_sidechain - +}} // namespace graphene::peerplays_sidechain From 11718af9b84399248fad7ce22b87cae6356bff84 Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Tue, 3 Mar 2020 16:42:51 -0400 Subject: [PATCH 45/64] Merge develop into SONS --- .gitmodules | 3 +- CMakeDoxyfile.in | 279 ++++ Doxyfile | 2 +- libraries/app/CMakeLists.txt | 4 +- libraries/app/api.cpp | 65 +- libraries/app/application.cpp | 29 +- libraries/app/database_api.cpp | 351 +++- libraries/app/impacted.cpp | 369 ----- libraries/app/include/graphene/app/api.hpp | 60 +- .../app/include/graphene/app/application.hpp | 8 +- .../app/include/graphene/app/database_api.hpp | 101 +- libraries/chain/CMakeLists.txt | 2 + libraries/chain/account_evaluator.cpp | 83 +- libraries/chain/account_object.cpp | 19 +- libraries/chain/asset_evaluator.cpp | 72 +- libraries/chain/asset_object.cpp | 16 +- libraries/chain/balance_evaluator.cpp | 1 + .../chain/committee_member_evaluator.cpp | 10 +- libraries/chain/db_balance.cpp | 11 +- libraries/chain/db_block.cpp | 219 ++- libraries/chain/db_debug.cpp | 2 +- libraries/chain/db_getter.cpp | 30 +- libraries/chain/db_init.cpp | 89 +- libraries/chain/db_maint.cpp | 506 ++++-- libraries/chain/db_management.cpp | 14 +- libraries/chain/db_market.cpp | 20 +- libraries/chain/db_notify.cpp | 28 +- libraries/chain/db_update.cpp | 102 +- libraries/chain/db_witness_schedule.cpp | 12 +- libraries/chain/genesis_state.cpp | 69 + libraries/chain/hardfork.d/GPOS.hf | 4 + .../include/graphene/chain/account_object.hpp | 96 +- .../include/graphene/chain/asset_object.hpp | 108 +- .../include/graphene/chain/balance_object.hpp | 2 + .../include/graphene/chain/block_database.hpp | 2 + .../graphene/chain/block_summary_object.hpp | 4 + .../graphene/chain/budget_record_object.hpp | 17 +- .../include/graphene/chain/buyback_object.hpp | 2 + .../graphene/chain/chain_property_object.hpp | 4 +- .../chain/committee_member_object.hpp | 5 +- .../graphene/chain/confidential_object.hpp | 9 +- .../chain/include/graphene/chain/config.hpp | 5 +- .../chain/include/graphene/chain/database.hpp | 45 +- .../include/graphene/chain/exceptions.hpp | 16 + .../include/graphene/chain/fba_object.hpp | 5 +- .../include/graphene/chain/fork_database.hpp | 5 + .../include/graphene/chain/genesis_state.hpp | 81 +- .../graphene/chain/global_property_object.hpp | 4 +- .../chain/immutable_chain_parameters.hpp | 7 +- .../include/graphene/chain}/impacted.hpp | 4 +- .../include/graphene/chain/market_object.hpp | 4 + .../chain/operation_history_object.hpp | 19 +- .../graphene/chain/proposal_object.hpp | 5 +- .../graphene/chain/protocol/account.hpp | 22 +- .../graphene/chain/protocol/address.hpp | 12 +- .../graphene/chain/protocol/assert.hpp | 3 + .../include/graphene/chain/protocol/asset.hpp | 4 + .../graphene/chain/protocol/asset_ops.hpp | 27 + .../graphene/chain/protocol/authority.hpp | 3 + .../graphene/chain/protocol/balance.hpp | 4 + .../include/graphene/chain/protocol/base.hpp | 5 + .../include/graphene/chain/protocol/block.hpp | 5 + .../graphene/chain/protocol/buyback.hpp | 2 + .../chain/protocol/chain_parameters.hpp | 34 +- .../chain/protocol/committee_member.hpp | 7 + .../graphene/chain/protocol/confidential.hpp | 7 + .../graphene/chain/protocol/custom.hpp | 3 + .../include/graphene/chain/protocol/ext.hpp | 1 + .../include/graphene/chain/protocol/fba.hpp | 3 + .../graphene/chain/protocol/fee_schedule.hpp | 3 + .../graphene/chain/protocol/market.hpp | 11 +- .../include/graphene/chain/protocol/memo.hpp | 3 + .../graphene/chain/protocol/operations.hpp | 2 + .../graphene/chain/protocol/proposal.hpp | 8 + .../chain/protocol/special_authority.hpp | 2 + .../graphene/chain/protocol/transaction.hpp | 5 + .../graphene/chain/protocol/transfer.hpp | 6 + .../include/graphene/chain/protocol/types.hpp | 30 +- .../graphene/chain/protocol/vesting.hpp | 20 +- .../include/graphene/chain/protocol/vote.hpp | 9 +- .../chain/protocol/withdraw_permission.hpp | 10 + .../graphene/chain/protocol/witness.hpp | 6 + .../graphene/chain/protocol/worker.hpp | 3 + .../include/graphene/chain/pts_address.hpp | 11 +- .../chain/special_authority_object.hpp | 2 + .../graphene/chain/transaction_object.hpp | 4 +- .../chain/vesting_balance_evaluator.hpp | 1 + .../graphene/chain/vesting_balance_object.hpp | 8 +- .../chain/withdraw_permission_object.hpp | 2 + .../include/graphene/chain/witness_object.hpp | 4 +- .../chain/witness_schedule_object.hpp | 3 + .../include/graphene/chain/worker_object.hpp | 5 +- libraries/chain/proposal_evaluator.cpp | 14 +- libraries/chain/proposal_object.cpp | 4 +- libraries/chain/protocol/account.cpp | 16 + libraries/chain/protocol/address.cpp | 5 +- libraries/chain/protocol/assert.cpp | 10 +- libraries/chain/protocol/asset.cpp | 11 +- libraries/chain/protocol/asset_ops.cpp | 29 + libraries/chain/protocol/authority.cpp | 3 + libraries/chain/protocol/block.cpp | 5 + libraries/chain/protocol/committee_member.cpp | 11 + libraries/chain/protocol/confidential.cpp | 13 +- libraries/chain/protocol/custom.cpp | 5 + libraries/chain/protocol/fee_schedule.cpp | 4 + libraries/chain/protocol/market.cpp | 9 + libraries/chain/protocol/memo.cpp | 4 + libraries/chain/protocol/operations.cpp | 5 + libraries/chain/protocol/proposal.cpp | 9 + libraries/chain/protocol/small_ops.cpp | 44 + libraries/chain/protocol/tournament.cpp | 1 + libraries/chain/protocol/transaction.cpp | 5 + libraries/chain/protocol/transfer.cpp | 7 + libraries/chain/protocol/vote.cpp | 2 + .../chain/protocol/withdraw_permission.cpp | 11 +- libraries/chain/protocol/witness.cpp | 6 + libraries/chain/protocol/worker.cpp | 4 + libraries/chain/pts_address.cpp | 11 +- libraries/chain/small_objects.cpp | 72 + libraries/chain/special_authority.cpp | 5 + libraries/chain/vesting_balance_evaluator.cpp | 115 +- libraries/chain/vesting_balance_object.cpp | 40 +- libraries/chain/worker_evaluator.cpp | 2 +- libraries/egenesis/egenesis_none.cpp | 2 + libraries/fc | 2 +- libraries/net/CMakeLists.txt | 1 + .../net/include/graphene/net/message.hpp | 14 +- .../include/graphene/net/peer_connection.hpp | 7 +- .../include/graphene/net/peer_database.hpp | 8 +- libraries/net/message.cpp | 29 + libraries/net/message_oriented_connection.cpp | 34 +- libraries/net/node.cpp | 24 +- libraries/net/peer_connection.cpp | 13 +- libraries/net/peer_database.cpp | 11 + libraries/plugins/CMakeLists.txt | 2 + .../account_history_plugin.cpp | 7 +- .../accounts_list/accounts_list_plugin.cpp | 2 +- .../affiliate_stats_plugin.cpp | 2 +- libraries/plugins/bookie/bookie_plugin.cpp | 10 +- .../delayed_node/delayed_node_plugin.cpp | 2 +- .../plugins/elasticsearch/CMakeLists.txt | 23 + .../elasticsearch/elasticsearch_plugin.cpp | 622 +++++++ .../elasticsearch/elasticsearch_plugin.hpp | 289 ++++ libraries/plugins/es_objects/CMakeLists.txt | 23 + libraries/plugins/es_objects/es_objects.cpp | 401 +++++ .../graphene/es_objects/es_objects.hpp | 113 ++ libraries/plugins/snapshot/CMakeLists.txt | 2 + .../include/graphene/snapshot/snapshot.hpp | 1 + libraries/plugins/snapshot/snapshot.cpp | 5 + libraries/utilities/CMakeLists.txt | 1 + libraries/utilities/elasticsearch.cpp | 190 +++ .../graphene/utilities/elasticsearch.hpp | 68 + .../wallet/include/graphene/wallet/wallet.hpp | 88 +- libraries/wallet/wallet.cpp | 468 +++++- programs/cli_wallet/main.cpp | 12 +- programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/genesis.json | 37 +- programs/witness_node/main.cpp | 19 +- tests/CMakeLists.txt | 20 +- tests/app/main.cpp | 1 + tests/cli/main.cpp | 641 +++++++- tests/common/database_fixture.cpp | 65 +- tests/elasticsearch/main.cpp | 535 ++++++ tests/tests/block_tests.cpp | 13 +- tests/tests/gpos_tests.cpp | 1453 +++++++++++++++++ tests/tests/history_api_tests.cpp | 400 ++--- tests/tests/voting_tests.cpp | 362 +++- 167 files changed, 8390 insertions(+), 1445 deletions(-) create mode 100644 CMakeDoxyfile.in delete mode 100644 libraries/app/impacted.cpp create mode 100644 libraries/chain/hardfork.d/GPOS.hf rename libraries/{app/include/graphene/app => chain/include/graphene/chain}/impacted.hpp (96%) create mode 100644 libraries/chain/protocol/small_ops.cpp create mode 100644 libraries/chain/small_objects.cpp create mode 100644 libraries/net/message.cpp create mode 100644 libraries/plugins/elasticsearch/CMakeLists.txt create mode 100644 libraries/plugins/elasticsearch/elasticsearch_plugin.cpp create mode 100644 libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp create mode 100644 libraries/plugins/es_objects/CMakeLists.txt create mode 100644 libraries/plugins/es_objects/es_objects.cpp create mode 100644 libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp create mode 100644 libraries/utilities/elasticsearch.cpp create mode 100644 libraries/utilities/include/graphene/utilities/elasticsearch.hpp create mode 100644 tests/elasticsearch/main.cpp create mode 100644 tests/tests/gpos_tests.cpp diff --git a/.gitmodules b/.gitmodules index 5572259c..4d3518d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,5 +4,6 @@ ignore = dirty [submodule "libraries/fc"] path = libraries/fc - url = https://github.com/PBSA/peerplays-fc.git + url = https://github.com/peerplays-network/peerplays-fc.git + branch = latest-fc ignore = dirty diff --git a/CMakeDoxyfile.in b/CMakeDoxyfile.in new file mode 100644 index 00000000..b0ed02fb --- /dev/null +++ b/CMakeDoxyfile.in @@ -0,0 +1,279 @@ +# +# DO NOT EDIT! THIS FILE WAS GENERATED BY CMAKE! +# + +DOXYFILE_ENCODING = @DOXYGEN_DOXYFILE_ENCODING@ +PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ +PROJECT_BRIEF = @DOXYGEN_PROJECT_BRIEF@ +PROJECT_LOGO = @DOXYGEN_PROJECT_LOGO@ +OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ +CREATE_SUBDIRS = @DOXYGEN_CREATE_SUBDIRS@ +ALLOW_UNICODE_NAMES = @DOXYGEN_ALLOW_UNICODE_NAMES@ +OUTPUT_LANGUAGE = @DOXYGEN_OUTPUT_LANGUAGE@ +OUTPUT_TEXT_DIRECTION = @DOXYGEN_OUTPUT_TEXT_DIRECTION@ +BRIEF_MEMBER_DESC = @DOXYGEN_BRIEF_MEMBER_DESC@ +REPEAT_BRIEF = @DOXYGEN_REPEAT_BRIEF@ +ABBREVIATE_BRIEF = @DOXYGEN_ABBREVIATE_BRIEF@ +ALWAYS_DETAILED_SEC = @DOXYGEN_ALWAYS_DETAILED_SEC@ +INLINE_INHERITED_MEMB = @DOXYGEN_INLINE_INHERITED_MEMB@ +FULL_PATH_NAMES = @DOXYGEN_FULL_PATH_NAMES@ +STRIP_FROM_PATH = @DOXYGEN_STRIP_FROM_PATH@ +STRIP_FROM_INC_PATH = @DOXYGEN_STRIP_FROM_INC_PATH@ +SHORT_NAMES = @DOXYGEN_SHORT_NAMES@ +JAVADOC_AUTOBRIEF = @DOXYGEN_JAVADOC_AUTOBRIEF@ +JAVADOC_BANNER = @DOXYGEN_JAVADOC_BANNER@ +QT_AUTOBRIEF = @DOXYGEN_QT_AUTOBRIEF@ +MULTILINE_CPP_IS_BRIEF = @DOXYGEN_MULTILINE_CPP_IS_BRIEF@ +INHERIT_DOCS = @DOXYGEN_INHERIT_DOCS@ +SEPARATE_MEMBER_PAGES = @DOXYGEN_SEPARATE_MEMBER_PAGES@ +TAB_SIZE = @DOXYGEN_TAB_SIZE@ +ALIASES = @DOXYGEN_ALIASES@ +TCL_SUBST = @DOXYGEN_TCL_SUBST@ +OPTIMIZE_OUTPUT_FOR_C = @DOXYGEN_OPTIMIZE_OUTPUT_FOR_C@ +OPTIMIZE_OUTPUT_JAVA = @DOXYGEN_OPTIMIZE_OUTPUT_JAVA@ +OPTIMIZE_FOR_FORTRAN = @DOXYGEN_OPTIMIZE_FOR_FORTRAN@ +OPTIMIZE_OUTPUT_VHDL = @DOXYGEN_OPTIMIZE_OUTPUT_VHDL@ +OPTIMIZE_OUTPUT_SLICE = @DOXYGEN_OPTIMIZE_OUTPUT_SLICE@ +EXTENSION_MAPPING = @DOXYGEN_EXTENSION_MAPPING@ +MARKDOWN_SUPPORT = @DOXYGEN_MARKDOWN_SUPPORT@ +TOC_INCLUDE_HEADINGS = @DOXYGEN_TOC_INCLUDE_HEADINGS@ +AUTOLINK_SUPPORT = @DOXYGEN_AUTOLINK_SUPPORT@ +BUILTIN_STL_SUPPORT = @DOXYGEN_BUILTIN_STL_SUPPORT@ +CPP_CLI_SUPPORT = @DOXYGEN_CPP_CLI_SUPPORT@ +SIP_SUPPORT = @DOXYGEN_SIP_SUPPORT@ +IDL_PROPERTY_SUPPORT = @DOXYGEN_IDL_PROPERTY_SUPPORT@ +DISTRIBUTE_GROUP_DOC = @DOXYGEN_DISTRIBUTE_GROUP_DOC@ +GROUP_NESTED_COMPOUNDS = @DOXYGEN_GROUP_NESTED_COMPOUNDS@ +SUBGROUPING = @DOXYGEN_SUBGROUPING@ +INLINE_GROUPED_CLASSES = @DOXYGEN_INLINE_GROUPED_CLASSES@ +INLINE_SIMPLE_STRUCTS = @DOXYGEN_INLINE_SIMPLE_STRUCTS@ +TYPEDEF_HIDES_STRUCT = @DOXYGEN_TYPEDEF_HIDES_STRUCT@ +LOOKUP_CACHE_SIZE = @DOXYGEN_LOOKUP_CACHE_SIZE@ +EXTRACT_ALL = @DOXYGEN_EXTRACT_ALL@ +EXTRACT_PRIVATE = @DOXYGEN_EXTRACT_PRIVATE@ +EXTRACT_PRIV_VIRTUAL = @DOXYGEN_EXTRACT_PRIV_VIRTUAL@ +EXTRACT_PACKAGE = @DOXYGEN_EXTRACT_PACKAGE@ +EXTRACT_STATIC = @DOXYGEN_EXTRACT_STATIC@ +EXTRACT_LOCAL_CLASSES = @DOXYGEN_EXTRACT_LOCAL_CLASSES@ +EXTRACT_LOCAL_METHODS = @DOXYGEN_EXTRACT_LOCAL_METHODS@ +EXTRACT_ANON_NSPACES = @DOXYGEN_EXTRACT_ANON_NSPACES@ +HIDE_UNDOC_MEMBERS = @DOXYGEN_HIDE_UNDOC_MEMBERS@ +HIDE_UNDOC_CLASSES = @DOXYGEN_HIDE_UNDOC_CLASSES@ +HIDE_FRIEND_COMPOUNDS = @DOXYGEN_HIDE_FRIEND_COMPOUNDS@ +HIDE_IN_BODY_DOCS = @DOXYGEN_HIDE_IN_BODY_DOCS@ +INTERNAL_DOCS = @DOXYGEN_INTERNAL_DOCS@ +CASE_SENSE_NAMES = @DOXYGEN_CASE_SENSE_NAMES@ +HIDE_SCOPE_NAMES = @DOXYGEN_HIDE_SCOPE_NAMES@ +HIDE_COMPOUND_REFERENCE= @DOXYGEN_HIDE_COMPOUND_REFERENCE@ +SHOW_INCLUDE_FILES = @DOXYGEN_SHOW_INCLUDE_FILES@ +SHOW_GROUPED_MEMB_INC = @DOXYGEN_SHOW_GROUPED_MEMB_INC@ +FORCE_LOCAL_INCLUDES = @DOXYGEN_FORCE_LOCAL_INCLUDES@ +INLINE_INFO = @DOXYGEN_INLINE_INFO@ +SORT_MEMBER_DOCS = @DOXYGEN_SORT_MEMBER_DOCS@ +SORT_BRIEF_DOCS = @DOXYGEN_SORT_BRIEF_DOCS@ +SORT_MEMBERS_CTORS_1ST = @DOXYGEN_SORT_MEMBERS_CTORS_1ST@ +SORT_GROUP_NAMES = @DOXYGEN_SORT_GROUP_NAMES@ +SORT_BY_SCOPE_NAME = @DOXYGEN_SORT_BY_SCOPE_NAME@ +STRICT_PROTO_MATCHING = @DOXYGEN_STRICT_PROTO_MATCHING@ +GENERATE_TODOLIST = @DOXYGEN_GENERATE_TODOLIST@ +GENERATE_TESTLIST = @DOXYGEN_GENERATE_TESTLIST@ +GENERATE_BUGLIST = @DOXYGEN_GENERATE_BUGLIST@ +GENERATE_DEPRECATEDLIST= @DOXYGEN_GENERATE_DEPRECATEDLIST@ +ENABLED_SECTIONS = @DOXYGEN_ENABLED_SECTIONS@ +MAX_INITIALIZER_LINES = @DOXYGEN_MAX_INITIALIZER_LINES@ +SHOW_USED_FILES = @DOXYGEN_SHOW_USED_FILES@ +SHOW_FILES = @DOXYGEN_SHOW_FILES@ +SHOW_NAMESPACES = @DOXYGEN_SHOW_NAMESPACES@ +FILE_VERSION_FILTER = @DOXYGEN_FILE_VERSION_FILTER@ +LAYOUT_FILE = @DOXYGEN_LAYOUT_FILE@ +CITE_BIB_FILES = @DOXYGEN_CITE_BIB_FILES@ +QUIET = @DOXYGEN_QUIET@ +WARNINGS = @DOXYGEN_WARNINGS@ +WARN_IF_UNDOCUMENTED = @DOXYGEN_WARN_IF_UNDOCUMENTED@ +WARN_IF_DOC_ERROR = @DOXYGEN_WARN_IF_DOC_ERROR@ +WARN_NO_PARAMDOC = @DOXYGEN_WARN_NO_PARAMDOC@ +WARN_AS_ERROR = @DOXYGEN_WARN_AS_ERROR@ +WARN_FORMAT = @DOXYGEN_WARN_FORMAT@ +WARN_LOGFILE = @DOXYGEN_WARN_LOGFILE@ +INPUT = @DOXYGEN_INPUT@ +INPUT_ENCODING = @DOXYGEN_INPUT_ENCODING@ +FILE_PATTERNS = @DOXYGEN_FILE_PATTERNS@ +RECURSIVE = @DOXYGEN_RECURSIVE@ +EXCLUDE = @DOXYGEN_EXCLUDE@ +EXCLUDE_SYMLINKS = @DOXYGEN_EXCLUDE_SYMLINKS@ +EXCLUDE_PATTERNS = @DOXYGEN_EXCLUDE_PATTERNS@ +EXCLUDE_SYMBOLS = @DOXYGEN_EXCLUDE_SYMBOLS@ +EXAMPLE_PATH = @DOXYGEN_EXAMPLE_PATH@ +EXAMPLE_PATTERNS = @DOXYGEN_EXAMPLE_PATTERNS@ +EXAMPLE_RECURSIVE = @DOXYGEN_EXAMPLE_RECURSIVE@ +IMAGE_PATH = @DOXYGEN_IMAGE_PATH@ +INPUT_FILTER = @DOXYGEN_INPUT_FILTER@ +FILTER_PATTERNS = @DOXYGEN_FILTER_PATTERNS@ +FILTER_SOURCE_FILES = @DOXYGEN_FILTER_SOURCE_FILES@ +FILTER_SOURCE_PATTERNS = @DOXYGEN_FILTER_SOURCE_PATTERNS@ +USE_MDFILE_AS_MAINPAGE = @DOXYGEN_USE_MDFILE_AS_MAINPAGE@ +SOURCE_BROWSER = @DOXYGEN_SOURCE_BROWSER@ +INLINE_SOURCES = @DOXYGEN_INLINE_SOURCES@ +STRIP_CODE_COMMENTS = @DOXYGEN_STRIP_CODE_COMMENTS@ +REFERENCED_BY_RELATION = @DOXYGEN_REFERENCED_BY_RELATION@ +REFERENCES_RELATION = @DOXYGEN_REFERENCES_RELATION@ +REFERENCES_LINK_SOURCE = @DOXYGEN_REFERENCES_LINK_SOURCE@ +SOURCE_TOOLTIPS = @DOXYGEN_SOURCE_TOOLTIPS@ +USE_HTAGS = @DOXYGEN_USE_HTAGS@ +VERBATIM_HEADERS = @DOXYGEN_VERBATIM_HEADERS@ +CLANG_ASSISTED_PARSING = @DOXYGEN_CLANG_ASSISTED_PARSING@ +CLANG_OPTIONS = @DOXYGEN_CLANG_OPTIONS@ +CLANG_DATABASE_PATH = @DOXYGEN_CLANG_DATABASE_PATH@ +ALPHABETICAL_INDEX = @DOXYGEN_ALPHABETICAL_INDEX@ +COLS_IN_ALPHA_INDEX = @DOXYGEN_COLS_IN_ALPHA_INDEX@ +IGNORE_PREFIX = @DOXYGEN_IGNORE_PREFIX@ +GENERATE_HTML = @DOXYGEN_GENERATE_HTML@ +HTML_OUTPUT = @DOXYGEN_HTML_OUTPUT@ +HTML_FILE_EXTENSION = @DOXYGEN_HTML_FILE_EXTENSION@ +HTML_HEADER = @DOXYGEN_HTML_HEADER@ +HTML_FOOTER = @DOXYGEN_HTML_FOOTER@ +HTML_STYLESHEET = @DOXYGEN_HTML_STYLESHEET@ +HTML_EXTRA_STYLESHEET = @DOXYGEN_HTML_EXTRA_STYLESHEET@ +HTML_EXTRA_FILES = @DOXYGEN_HTML_EXTRA_FILES@ +HTML_COLORSTYLE_HUE = @DOXYGEN_HTML_COLORSTYLE_HUE@ +HTML_COLORSTYLE_SAT = @DOXYGEN_HTML_COLORSTYLE_SAT@ +HTML_COLORSTYLE_GAMMA = @DOXYGEN_HTML_COLORSTYLE_GAMMA@ +HTML_TIMESTAMP = @DOXYGEN_HTML_TIMESTAMP@ +HTML_DYNAMIC_MENUS = @DOXYGEN_HTML_DYNAMIC_MENUS@ +HTML_DYNAMIC_SECTIONS = @DOXYGEN_HTML_DYNAMIC_SECTIONS@ +HTML_INDEX_NUM_ENTRIES = @DOXYGEN_HTML_INDEX_NUM_ENTRIES@ +GENERATE_DOCSET = @DOXYGEN_GENERATE_DOCSET@ +DOCSET_FEEDNAME = @DOXYGEN_DOCSET_FEEDNAME@ +DOCSET_BUNDLE_ID = @DOXYGEN_DOCSET_BUNDLE_ID@ +DOCSET_PUBLISHER_ID = @DOXYGEN_DOCSET_PUBLISHER_ID@ +DOCSET_PUBLISHER_NAME = @DOXYGEN_DOCSET_PUBLISHER_NAME@ +GENERATE_HTMLHELP = @DOXYGEN_GENERATE_HTMLHELP@ +CHM_FILE = @DOXYGEN_CHM_FILE@ +HHC_LOCATION = @DOXYGEN_HHC_LOCATION@ +GENERATE_CHI = @DOXYGEN_GENERATE_CHI@ +CHM_INDEX_ENCODING = @DOXYGEN_CHM_INDEX_ENCODING@ +BINARY_TOC = @DOXYGEN_BINARY_TOC@ +TOC_EXPAND = @DOXYGEN_TOC_EXPAND@ +GENERATE_QHP = @DOXYGEN_GENERATE_QHP@ +QCH_FILE = @DOXYGEN_QCH_FILE@ +QHP_NAMESPACE = @DOXYGEN_QHP_NAMESPACE@ +QHP_VIRTUAL_FOLDER = @DOXYGEN_QHP_VIRTUAL_FOLDER@ +QHP_CUST_FILTER_NAME = @DOXYGEN_QHP_CUST_FILTER_NAME@ +QHP_CUST_FILTER_ATTRS = @DOXYGEN_QHP_CUST_FILTER_ATTRS@ +QHP_SECT_FILTER_ATTRS = @DOXYGEN_QHP_SECT_FILTER_ATTRS@ +QHG_LOCATION = @DOXYGEN_QHG_LOCATION@ +GENERATE_ECLIPSEHELP = @DOXYGEN_GENERATE_ECLIPSEHELP@ +ECLIPSE_DOC_ID = @DOXYGEN_ECLIPSE_DOC_ID@ +DISABLE_INDEX = @DOXYGEN_DISABLE_INDEX@ +GENERATE_TREEVIEW = @DOXYGEN_GENERATE_TREEVIEW@ +ENUM_VALUES_PER_LINE = @DOXYGEN_ENUM_VALUES_PER_LINE@ +TREEVIEW_WIDTH = @DOXYGEN_TREEVIEW_WIDTH@ +EXT_LINKS_IN_WINDOW = @DOXYGEN_EXT_LINKS_IN_WINDOW@ +FORMULA_FONTSIZE = @DOXYGEN_FORMULA_FONTSIZE@ +FORMULA_TRANSPARENT = @DOXYGEN_FORMULA_TRANSPARENT@ +USE_MATHJAX = @DOXYGEN_USE_MATHJAX@ +MATHJAX_FORMAT = @DOXYGEN_MATHJAX_FORMAT@ +MATHJAX_RELPATH = @DOXYGEN_MATHJAX_RELPATH@ +MATHJAX_EXTENSIONS = @DOXYGEN_MATHJAX_EXTENSIONS@ +MATHJAX_CODEFILE = @DOXYGEN_MATHJAX_CODEFILE@ +SEARCHENGINE = @DOXYGEN_SEARCHENGINE@ +SERVER_BASED_SEARCH = @DOXYGEN_SERVER_BASED_SEARCH@ +EXTERNAL_SEARCH = @DOXYGEN_EXTERNAL_SEARCH@ +SEARCHENGINE_URL = @DOXYGEN_SEARCHENGINE_URL@ +SEARCHDATA_FILE = @DOXYGEN_SEARCHDATA_FILE@ +EXTERNAL_SEARCH_ID = @DOXYGEN_EXTERNAL_SEARCH_ID@ +EXTRA_SEARCH_MAPPINGS = @DOXYGEN_EXTRA_SEARCH_MAPPINGS@ +GENERATE_LATEX = @DOXYGEN_GENERATE_LATEX@ +LATEX_OUTPUT = @DOXYGEN_LATEX_OUTPUT@ +LATEX_CMD_NAME = @DOXYGEN_LATEX_CMD_NAME@ +MAKEINDEX_CMD_NAME = @DOXYGEN_MAKEINDEX_CMD_NAME@ +LATEX_MAKEINDEX_CMD = @DOXYGEN_LATEX_MAKEINDEX_CMD@ +COMPACT_LATEX = @DOXYGEN_COMPACT_LATEX@ +PAPER_TYPE = @DOXYGEN_PAPER_TYPE@ +EXTRA_PACKAGES = @DOXYGEN_EXTRA_PACKAGES@ +LATEX_HEADER = @DOXYGEN_LATEX_HEADER@ +LATEX_FOOTER = @DOXYGEN_LATEX_FOOTER@ +LATEX_EXTRA_STYLESHEET = @DOXYGEN_LATEX_EXTRA_STYLESHEET@ +LATEX_EXTRA_FILES = @DOXYGEN_LATEX_EXTRA_FILES@ +PDF_HYPERLINKS = @DOXYGEN_PDF_HYPERLINKS@ +USE_PDFLATEX = @DOXYGEN_USE_PDFLATEX@ +LATEX_BATCHMODE = @DOXYGEN_LATEX_BATCHMODE@ +LATEX_HIDE_INDICES = @DOXYGEN_LATEX_HIDE_INDICES@ +LATEX_SOURCE_CODE = @DOXYGEN_LATEX_SOURCE_CODE@ +LATEX_BIB_STYLE = @DOXYGEN_LATEX_BIB_STYLE@ +LATEX_TIMESTAMP = @DOXYGEN_LATEX_TIMESTAMP@ +LATEX_EMOJI_DIRECTORY = @DOXYGEN_LATEX_EMOJI_DIRECTORY@ +GENERATE_RTF = @DOXYGEN_GENERATE_RTF@ +RTF_OUTPUT = @DOXYGEN_RTF_OUTPUT@ +COMPACT_RTF = @DOXYGEN_COMPACT_RTF@ +RTF_HYPERLINKS = @DOXYGEN_RTF_HYPERLINKS@ +RTF_STYLESHEET_FILE = @DOXYGEN_RTF_STYLESHEET_FILE@ +RTF_EXTENSIONS_FILE = @DOXYGEN_RTF_EXTENSIONS_FILE@ +RTF_SOURCE_CODE = @DOXYGEN_RTF_SOURCE_CODE@ +GENERATE_MAN = @DOXYGEN_GENERATE_MAN@ +MAN_OUTPUT = @DOXYGEN_MAN_OUTPUT@ +MAN_EXTENSION = @DOXYGEN_MAN_EXTENSION@ +MAN_SUBDIR = @DOXYGEN_MAN_SUBDIR@ +MAN_LINKS = @DOXYGEN_MAN_LINKS@ +GENERATE_XML = @DOXYGEN_GENERATE_XML@ +XML_OUTPUT = @DOXYGEN_XML_OUTPUT@ +XML_PROGRAMLISTING = @DOXYGEN_XML_PROGRAMLISTING@ +XML_NS_MEMB_FILE_SCOPE = @DOXYGEN_XML_NS_MEMB_FILE_SCOPE@ +GENERATE_DOCBOOK = @DOXYGEN_GENERATE_DOCBOOK@ +DOCBOOK_OUTPUT = @DOXYGEN_DOCBOOK_OUTPUT@ +DOCBOOK_PROGRAMLISTING = @DOXYGEN_DOCBOOK_PROGRAMLISTING@ +GENERATE_AUTOGEN_DEF = @DOXYGEN_GENERATE_AUTOGEN_DEF@ +GENERATE_PERLMOD = @DOXYGEN_GENERATE_PERLMOD@ +PERLMOD_LATEX = @DOXYGEN_PERLMOD_LATEX@ +PERLMOD_PRETTY = @DOXYGEN_PERLMOD_PRETTY@ +PERLMOD_MAKEVAR_PREFIX = @DOXYGEN_PERLMOD_MAKEVAR_PREFIX@ +ENABLE_PREPROCESSING = @DOXYGEN_ENABLE_PREPROCESSING@ +MACRO_EXPANSION = @DOXYGEN_MACRO_EXPANSION@ +EXPAND_ONLY_PREDEF = @DOXYGEN_EXPAND_ONLY_PREDEF@ +SEARCH_INCLUDES = @DOXYGEN_SEARCH_INCLUDES@ +INCLUDE_PATH = @DOXYGEN_INCLUDE_PATH@ +INCLUDE_FILE_PATTERNS = @DOXYGEN_INCLUDE_FILE_PATTERNS@ +PREDEFINED = @DOXYGEN_PREDEFINED@ +EXPAND_AS_DEFINED = @DOXYGEN_EXPAND_AS_DEFINED@ +SKIP_FUNCTION_MACROS = @DOXYGEN_SKIP_FUNCTION_MACROS@ +TAGFILES = @DOXYGEN_TAGFILES@ +GENERATE_TAGFILE = @DOXYGEN_GENERATE_TAGFILE@ +ALLEXTERNALS = @DOXYGEN_ALLEXTERNALS@ +EXTERNAL_GROUPS = @DOXYGEN_EXTERNAL_GROUPS@ +EXTERNAL_PAGES = @DOXYGEN_EXTERNAL_PAGES@ +CLASS_DIAGRAMS = @DOXYGEN_CLASS_DIAGRAMS@ +DIA_PATH = @DOXYGEN_DIA_PATH@ +HIDE_UNDOC_RELATIONS = @DOXYGEN_HIDE_UNDOC_RELATIONS@ +HAVE_DOT = @DOXYGEN_HAVE_DOT@ +DOT_NUM_THREADS = @DOXYGEN_DOT_NUM_THREADS@ +DOT_FONTNAME = @DOXYGEN_DOT_FONTNAME@ +DOT_FONTSIZE = @DOXYGEN_DOT_FONTSIZE@ +DOT_FONTPATH = @DOXYGEN_DOT_FONTPATH@ +CLASS_GRAPH = @DOXYGEN_CLASS_GRAPH@ +COLLABORATION_GRAPH = @DOXYGEN_COLLABORATION_GRAPH@ +GROUP_GRAPHS = @DOXYGEN_GROUP_GRAPHS@ +UML_LOOK = @DOXYGEN_UML_LOOK@ +UML_LIMIT_NUM_FIELDS = @DOXYGEN_UML_LIMIT_NUM_FIELDS@ +TEMPLATE_RELATIONS = @DOXYGEN_TEMPLATE_RELATIONS@ +INCLUDE_GRAPH = @DOXYGEN_INCLUDE_GRAPH@ +INCLUDED_BY_GRAPH = @DOXYGEN_INCLUDED_BY_GRAPH@ +CALL_GRAPH = @DOXYGEN_CALL_GRAPH@ +CALLER_GRAPH = @DOXYGEN_CALLER_GRAPH@ +GRAPHICAL_HIERARCHY = @DOXYGEN_GRAPHICAL_HIERARCHY@ +DIRECTORY_GRAPH = @DOXYGEN_DIRECTORY_GRAPH@ +DOT_IMAGE_FORMAT = @DOXYGEN_DOT_IMAGE_FORMAT@ +INTERACTIVE_SVG = @DOXYGEN_INTERACTIVE_SVG@ +DOT_PATH = @DOXYGEN_DOT_PATH@ +DOTFILE_DIRS = @DOXYGEN_DOTFILE_DIRS@ +MSCFILE_DIRS = @DOXYGEN_MSCFILE_DIRS@ +DIAFILE_DIRS = @DOXYGEN_DIAFILE_DIRS@ +PLANTUML_JAR_PATH = @DOXYGEN_PLANTUML_JAR_PATH@ +PLANTUML_CFG_FILE = @DOXYGEN_PLANTUML_CFG_FILE@ +PLANTUML_INCLUDE_PATH = @DOXYGEN_PLANTUML_INCLUDE_PATH@ +DOT_GRAPH_MAX_NODES = @DOXYGEN_DOT_GRAPH_MAX_NODES@ +MAX_DOT_GRAPH_DEPTH = @DOXYGEN_MAX_DOT_GRAPH_DEPTH@ +DOT_TRANSPARENT = @DOXYGEN_DOT_TRANSPARENT@ +DOT_MULTI_TARGETS = @DOXYGEN_DOT_MULTI_TARGETS@ +GENERATE_LEGEND = @DOXYGEN_GENERATE_LEGEND@ +DOT_CLEANUP = @DOXYGEN_DOT_CLEANUP@ diff --git a/Doxyfile b/Doxyfile index 75931ef9..18bb33e2 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Graphene" +PROJECT_NAME = "Peerplays" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index e6f8940c..ea0a2c07 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -5,7 +5,6 @@ add_library( graphene_app api.cpp application.cpp database_api.cpp - impacted.cpp plugin.cpp config_util.cpp ${HEADERS} @@ -14,7 +13,8 @@ add_library( graphene_app # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain ) +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain graphene_elasticsearch) + target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index d31abe19..11d39f69 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -40,8 +39,19 @@ #include #include +#include #include +template class fc::api; +template class fc::api; +template class fc::api; +template class fc::api; +template class fc::api; +template class fc::api; +template class fc::api; +template class fc::api; + + namespace graphene { namespace app { login_api::login_api(application& a) @@ -103,7 +113,7 @@ namespace graphene { namespace app { } else if( api_name == "asset_api" ) { - _asset_api = std::make_shared< asset_api >( std::ref( *_app.chain_database() ) ); + _asset_api = std::make_shared< asset_api >( _app ); } else if( api_name == "debug_api" ) { @@ -536,10 +546,12 @@ namespace graphene { namespace app { } // end get_relevant_accounts( obj ) #endif - vector history_api::get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const + vector history_api::get_fill_order_history( std::string asset_a, std::string asset_b, uint32_t limit )const { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); + asset_id_type a = database_api.get_asset_id_from_string( asset_a ); + asset_id_type b = database_api.get_asset_id_from_string( asset_b ); if( a > b ) std::swap(a,b); const auto& history_idx = db.get_index_type().indices().get(); history_key hkey; @@ -561,7 +573,7 @@ namespace graphene { namespace app { return result; } - vector history_api::get_account_history( account_id_type account, + vector history_api::get_account_history( const std::string account_id_or_name, operation_history_id_type stop, unsigned limit, operation_history_id_type start ) const @@ -570,12 +582,26 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT( limit <= 100 ); vector result; + account_id_type account; try { + account = database_api.get_account_id_from_string(account_id_or_name); const account_transaction_history_object& node = account(db).statistics(db).most_recent_op(db); if(start == operation_history_id_type() || start.instance.value > node.operation_id.instance.value) start = node.operation_id; } catch(...) { return result; } + if(_app.is_plugin_enabled("elasticsearch")) { + auto es = _app.get_plugin("elasticsearch"); + if(es.get()->get_running_mode() != elasticsearch::mode::only_save) { + if(!_app.elasticsearch_thread) + _app.elasticsearch_thread= std::make_shared("elasticsearch"); + + return _app.elasticsearch_thread->async([&es, &account, &stop, &limit, &start]() { + return es->get_account_history(account, stop, limit, start); + }, "thread invoke for method " BOOST_PP_STRINGIZE(method_name)).wait(); + } + } + const auto& hist_idx = db.get_index_type(); const auto& by_op_idx = hist_idx.indices().get(); auto index_start = by_op_idx.begin(); @@ -594,7 +620,7 @@ namespace graphene { namespace app { return result; } - vector history_api::get_account_history_operations( account_id_type account, + vector history_api::get_account_history_operations( const std::string account_id_or_name, int operation_id, operation_history_id_type start, operation_history_id_type stop, @@ -604,6 +630,11 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT( limit <= 100 ); vector result; + account_id_type account; + try { + account = database_api.get_account_id_from_string(account_id_or_name); + } catch (...) { return result; } + const auto& stats = account(db).statistics(db); if( stats.most_recent_op == account_transaction_history_id_type() ) return result; const account_transaction_history_object* node = &stats.most_recent_op(db); @@ -630,7 +661,7 @@ namespace graphene { namespace app { } - vector history_api::get_relative_account_history( account_id_type account, + vector history_api::get_relative_account_history( const std::string account_id_or_name, uint32_t stop, unsigned limit, uint32_t start) const @@ -639,6 +670,10 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT(limit <= 100); vector result; + account_id_type account; + try { + account = database_api.get_account_id_from_string(account_id_or_name); + } catch(...) { return result; } const auto& stats = account(db).statistics(db); if( start == 0 ) start = stats.total_ops; @@ -678,11 +713,13 @@ namespace graphene { namespace app { return hist->tracked_buckets(); } - vector history_api::get_market_history( asset_id_type a, asset_id_type b, + vector history_api::get_market_history( std::string asset_a, std::string asset_b, uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const { try { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); + asset_id_type a = database_api.get_asset_id_from_string( asset_a ); + asset_id_type b = database_api.get_asset_id_from_string( asset_b ); vector result; result.reserve(200); @@ -702,7 +739,7 @@ namespace graphene { namespace app { ++itr; } return result; - } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } + } FC_CAPTURE_AND_RETHROW( (asset_a)(asset_b)(bucket_seconds)(start)(end) ) } crypto_api::crypto_api(){}; @@ -761,12 +798,16 @@ namespace graphene { namespace app { } // asset_api - asset_api::asset_api(graphene::chain::database& db) : _db(db) { } + asset_api::asset_api(graphene::app::application& app) : + _app(app), + _db( *app.chain_database()), + database_api( std::ref(*app.chain_database())) { } asset_api::~asset_api() { } - vector asset_api::get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit ) const { + vector asset_api::get_asset_holders( std::string asset, uint32_t start, uint32_t limit ) const { FC_ASSERT(limit <= 100); + asset_id_type asset_id = database_api.get_asset_id_from_string( asset ); const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); @@ -797,11 +838,11 @@ namespace graphene { namespace app { return result; } // get number of asset holders. - int asset_api::get_asset_holders_count( asset_id_type asset_id ) const { + int asset_api::get_asset_holders_count( std::string asset ) const { const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); + asset_id_type asset_id = database_api.get_asset_id_from_string( asset ); auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); - int count = boost::distance(range) - 1; return count; diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 0f0c0690..adfd8402 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -226,7 +226,7 @@ namespace detail { void new_connection( const fc::http::websocket_connection_ptr& c ) { - auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); auto login = std::make_shared( std::ref(*_self) ); login->enable_api("database_api"); @@ -375,6 +375,11 @@ namespace detail { } _chain_db->add_checkpoints( loaded_checkpoints ); + if( _options->count("enable-standby-votes-tracking") ) + { + _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as() ); + } + bool replay = false; std::string replay_reason = "reason not provided"; @@ -926,6 +931,10 @@ void application::set_program_options(boost::program_options::options_descriptio ("genesis-json", bpo::value(), "File to read Genesis State from") ("dbg-init-key", bpo::value(), "Block signing key to use for init witnesses, overrides genesis file") ("api-access", bpo::value(), "JSON file specifying API permissions") + ("enable-standby-votes-tracking", bpo::value()->implicit_value(true), + "Whether to enable tracking of votes of standby witnesses and committee members. " + "Set it to true to provide accurate data to API clients, set to false for slightly better performance.") + ("plugins", bpo::value(), "Space-separated list of plugins to activate") ; command_line_options.add(configuration_file_options); command_line_options.add_options() @@ -982,9 +991,20 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti wanted.push_back("witness"); wanted.push_back("account_history"); wanted.push_back("market_history"); + wanted.push_back("bookie"); } + int es_ah_conflict_counter = 0; for (auto& it : wanted) { + if(it == "account_history") + ++es_ah_conflict_counter; + if(it == "elasticsearch") + ++es_ah_conflict_counter; + + if(es_ah_conflict_counter > 1) { + elog("Can't start program with elasticsearch and account_history plugin at the same time"); + std::exit(EXIT_FAILURE); + } if (!it.empty()) enable_plugin(it); } } @@ -1009,9 +1029,7 @@ std::shared_ptr application::get_plugin(const string& name) con bool application::is_plugin_enabled(const string& name) const { - if(my->_active_plugins.find(name) == my->_active_plugins.end()) - return false; - return true; + return !(my->_active_plugins.find(name) == my->_active_plugins.end()); } net::node_ptr application::p2p_node() @@ -1051,7 +1069,8 @@ void graphene::app::application::enable_plugin(const string& name) my->_active_plugins[name]->plugin_set_app(this); } -void graphene::app::application::add_available_plugin(std::shared_ptr p) { +void graphene::app::application::add_available_plugin(std::shared_ptr p) +{ my->_available_plugins[p->plugin_name()] = p; } diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c6c8a952..c9aba7ff 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -26,11 +26,15 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include #include @@ -45,6 +49,8 @@ typedef std::map< std::pair, std::vector > market_queue_type; +template class fc::api; + namespace graphene { namespace app { class database_api_impl : public std::enable_shared_from_this @@ -81,23 +87,26 @@ class database_api_impl : public std::enable_shared_from_this bool is_public_key_registered(string public_key) const; // Accounts - vector> get_accounts(const vector& account_ids)const; + account_id_type get_account_id_from_string(const std::string& name_or_id)const; + vector> get_accounts(const vector& account_names_or_ids)const; std::map get_full_accounts( const vector& names_or_ids, bool subscribe ); optional get_account_by_name( string name )const; - vector get_account_references( account_id_type account_id )const; + vector get_account_references( const std::string account_id_or_name )const; vector> lookup_account_names(const vector& account_names)const; map lookup_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_account_count()const; // Balances - vector get_account_balances(account_id_type id, const flat_set& assets)const; - vector get_named_account_balances(const std::string& name, const flat_set& assets)const; + vector get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const; vector get_balance_objects( const vector
& addrs )const; vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( account_id_type account_id )const; + vector get_vesting_balances( const std::string account_id_or_name )const; // Assets - vector> get_assets(const vector& asset_ids)const; + asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const; + vector> get_assets(const vector& asset_symbols_or_ids)const; + // helper function + vector> get_assets( const vector& asset_ids )const; vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; vector> lookup_asset_symbols(const vector& symbols_or_ids)const; uint64_t get_asset_count()const; @@ -124,12 +133,13 @@ class database_api_impl : public std::enable_shared_from_this asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const; // Markets / feeds - vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; - vector get_call_orders(asset_id_type a, uint32_t limit)const; - vector get_settle_orders(asset_id_type a, uint32_t limit)const; - vector get_margin_positions( const account_id_type& id )const; - void subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b); - void unsubscribe_from_market(asset_id_type a, asset_id_type b); + vector get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const; + vector get_limit_orders( const std::string& a, const std::string& b, const uint32_t limit)const; + vector get_call_orders(const std::string& a, uint32_t limit)const; + vector get_settle_orders(const std::string& a, uint32_t limit)const; + vector get_margin_positions( const std::string account_id_or_name )const; + void subscribe_to_market(std::function callback, const std::string& a, const std::string& b); + void unsubscribe_from_market(const std::string& a, const std::string& b); market_ticker get_ticker( const string& base, const string& quote )const; market_volume get_24_volume( const string& base, const string& quote )const; order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const; @@ -137,13 +147,13 @@ class database_api_impl : public std::enable_shared_from_this // Witnesses vector> get_witnesses(const vector& witness_ids)const; - fc::optional get_witness_by_account(account_id_type account)const; + fc::optional get_witness_by_account(const std::string account_id_or_name)const; map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_witness_count()const; // Committee members vector> get_committee_members(const vector& committee_member_ids)const; - fc::optional get_committee_member_by_account(account_id_type account)const; + fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; // SON members @@ -175,10 +185,10 @@ class database_api_impl : public std::enable_shared_from_this bool verify_authority( const signed_transaction& trx )const; bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; processed_transaction validate_transaction( const signed_transaction& trx )const; - vector< fc::variant > get_required_fees( const vector& ops, asset_id_type id )const; + vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; // Proposed transactions - vector get_proposed_transactions( account_id_type id )const; + vector get_proposed_transactions( const std::string account_id_or_name )const; // Blinded balances vector get_blinded_balances( const flat_set& commitments )const; @@ -189,8 +199,14 @@ class database_api_impl : public std::enable_shared_from_this vector get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state); vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; + // gpos + gpos_info get_gpos_info(const account_id_type account) const; //private: + const account_object* get_account_from_string( const std::string& name_or_id, + bool throw_if_not_found = true ) const; + const asset_object* get_asset_from_string( const std::string& symbol_or_id, + bool throw_if_not_found = true ) const; template void subscribe_to_item( const T& i )const { @@ -630,22 +646,27 @@ bool database_api_impl::is_public_key_registered(string public_key) const // // ////////////////////////////////////////////////////////////////////// -vector> database_api::get_accounts(const vector& account_ids)const +account_id_type database_api::get_account_id_from_string(const std::string& name_or_id)const { - return my->get_accounts( account_ids ); + return my->get_account_from_string( name_or_id )->id; } -vector> database_api_impl::get_accounts(const vector& account_ids)const +vector> database_api::get_accounts(const vector& account_names_or_ids)const { - vector> result; result.reserve(account_ids.size()); - std::transform(account_ids.begin(), account_ids.end(), std::back_inserter(result), - [this](account_id_type id) -> optional { - if(auto o = _db.find(id)) - { - subscribe_to_item( id ); - return *o; - } - return {}; + return my->get_accounts( account_names_or_ids ); +} + +vector> database_api_impl::get_accounts(const vector& account_names_or_ids)const +{ + vector> result; result.reserve(account_names_or_ids.size()); + std::transform(account_names_or_ids.begin(), account_names_or_ids.end(), std::back_inserter(result), + [this](std::string id_or_name) -> optional { + const account_object *account = get_account_from_string(id_or_name, false); + if(account == nullptr) + return {}; + + subscribe_to_item( account->id ); + return *account; }); return result; } @@ -774,16 +795,17 @@ optional database_api_impl::get_account_by_name( string name )co return optional(); } -vector database_api::get_account_references( account_id_type account_id )const +vector database_api::get_account_references( const std::string account_id_or_name )const { - return my->get_account_references( account_id ); + return my->get_account_references( account_id_or_name ); } -vector database_api_impl::get_account_references( account_id_type account_id )const +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& 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); vector result; @@ -852,13 +874,16 @@ uint64_t database_api_impl::get_account_count()const // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_account_balances(account_id_type id, const flat_set& assets)const +vector database_api::get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const { - return my->get_account_balances( id, assets ); + return my->get_account_balances( account_name_or_id, assets ); } -vector database_api_impl::get_account_balances(account_id_type acnt, const flat_set& assets)const +vector database_api_impl::get_account_balances( const std::string& account_name_or_id, + const flat_set& assets)const { + const account_object* account = get_account_from_string(account_name_or_id); + account_id_type acnt = account->id; vector result; if (assets.empty()) { @@ -881,15 +906,7 @@ vector database_api_impl::get_account_balances(account_id_type acnt, cons vector database_api::get_named_account_balances(const std::string& name, const flat_set& assets)const { - return my->get_named_account_balances( name, assets ); -} - -vector database_api_impl::get_named_account_balances(const std::string& name, const flat_set& assets) const -{ - const auto& accounts_by_name = _db.get_index_type().indices().get(); - auto itr = accounts_by_name.find(name); - FC_ASSERT( itr != accounts_by_name.end() ); - return get_account_balances(itr->get_id(), assets); + return my->get_account_balances( name, assets ); } vector database_api::get_balance_objects( const vector
& addrs )const @@ -939,24 +956,26 @@ vector database_api_impl::get_vested_balances( const vector database_api::get_vesting_balances( account_id_type account_id )const +vector database_api::get_vesting_balances( const std::string account_id_or_name )const { - return my->get_vesting_balances( account_id ); + return my->get_vesting_balances( account_id_or_name ); } -vector database_api_impl::get_vesting_balances( account_id_type account_id )const +vector database_api_impl::get_vesting_balances( const std::string account_id_or_name )const { try { + const account_id_type account_id = get_account_from_string(account_id_or_name)->id; vector result; auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id); std::for_each(vesting_range.first, vesting_range.second, [&result](const vesting_balance_object& balance) { - result.emplace_back(balance); + if(balance.balance.amount > 0) + result.emplace_back(balance); }); return result; } - FC_CAPTURE_AND_RETHROW( (account_id) ); + FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); } ////////////////////////////////////////////////////////////////////// @@ -965,9 +984,48 @@ vector database_api_impl::get_vesting_balances( account_ // // ////////////////////////////////////////////////////////////////////// -vector> database_api::get_assets(const vector& asset_ids)const +asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_or_id)const { - return my->get_assets( asset_ids ); + return my->get_asset_from_string( symbol_or_id )->id; +} + +const asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id, + bool throw_if_not_found ) const +{ + // TODO cache the result to avoid repeatly fetching from db + FC_ASSERT( symbol_or_id.size() > 0); + const asset_object* asset = nullptr; + if (std::isdigit(symbol_or_id[0])) + asset = _db.find(fc::variant(symbol_or_id, 1).as(1)); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(symbol_or_id); + if (itr != idx.end()) + asset = &*itr; + } + if(throw_if_not_found) + FC_ASSERT( asset, "no such asset" ); + return asset; +} + +vector> database_api::get_assets(const vector& asset_symbols_or_ids)const +{ + return my->get_assets( asset_symbols_or_ids ); +} + +vector> database_api_impl::get_assets(const vector& asset_symbols_or_ids)const +{ + vector> result; result.reserve(asset_symbols_or_ids.size()); + std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result), + [this](std::string id_or_name) -> optional { + const asset_object* asset_obj = get_asset_from_string( id_or_name, false ); + if( asset_obj == nullptr ) + return {}; + subscribe_to_item(asset_obj->id ); + return asset_object( *asset_obj ); + }); + return result; } vector> database_api_impl::get_assets(const vector& asset_ids)const @@ -1223,7 +1281,7 @@ vector database_api_impl::get_all_unmatched_bets_for_bettor(account_ // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const +vector database_api::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const { return my->get_limit_orders( a, b, limit ); } @@ -1231,12 +1289,22 @@ vector database_api::get_limit_orders(asset_id_type a, asset /** * @return the limit orders for both sides of the book for the two assets specified up to limit number on each side. */ -vector database_api_impl::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const +vector database_api_impl::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const +{ + const asset_id_type asset_a_id = get_asset_from_string(a)->id; + const asset_id_type asset_b_id = get_asset_from_string(b)->id; + + return get_limit_orders(asset_a_id, asset_b_id, limit); +} + +vector database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b, + const uint32_t limit )const { const auto& limit_order_idx = _db.get_index_type(); const auto& limit_price_idx = limit_order_idx.indices().get(); vector result; + result.reserve(limit*2); uint32_t count = 0; auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); @@ -1260,45 +1328,46 @@ vector database_api_impl::get_limit_orders(asset_id_type a, return result; } -vector database_api::get_call_orders(asset_id_type a, uint32_t limit)const +vector database_api::get_call_orders(const std::string& a, uint32_t limit)const { return my->get_call_orders( a, limit ); } -vector database_api_impl::get_call_orders(asset_id_type a, uint32_t limit)const +vector database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const { const auto& call_index = _db.get_index_type().indices().get(); - const asset_object& mia = _db.get(a); - price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id()); + const asset_object* mia = get_asset_from_string(a); + price index_price = price::min(mia->bitasset_data(_db).options.short_backing_asset, mia->get_id()); return vector(call_index.lower_bound(index_price.min()), call_index.lower_bound(index_price.max())); } -vector database_api::get_settle_orders(asset_id_type a, uint32_t limit)const +vector database_api::get_settle_orders(const std::string& a, uint32_t limit)const { return my->get_settle_orders( a, limit ); } -vector database_api_impl::get_settle_orders(asset_id_type a, uint32_t limit)const +vector database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const { const auto& settle_index = _db.get_index_type().indices().get(); - const asset_object& mia = _db.get(a); - return vector(settle_index.lower_bound(mia.get_id()), - settle_index.upper_bound(mia.get_id())); + const asset_object* mia = get_asset_from_string(a); + return vector(settle_index.lower_bound(mia->get_id()), + settle_index.upper_bound(mia->get_id())); } -vector database_api::get_margin_positions( const account_id_type& id )const +vector database_api::get_margin_positions( const std::string account_id_or_name )const { - return my->get_margin_positions( id ); + return my->get_margin_positions( account_id_or_name ); } -vector database_api_impl::get_margin_positions( const account_id_type& id )const +vector database_api_impl::get_margin_positions( const std::string account_id_or_name )const { try { const auto& idx = _db.get_index_type(); const auto& aidx = idx.indices().get(); + const account_id_type id = get_account_from_string(account_id_or_name)->id; auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) ); auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) ); vector result; @@ -1308,31 +1377,37 @@ vector database_api_impl::get_margin_positions( const account ++start; } return result; - } FC_CAPTURE_AND_RETHROW( (id) ) + } FC_CAPTURE_AND_RETHROW( (account_id_or_name) ) } -void database_api::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) +void database_api::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) { my->subscribe_to_market( callback, a, b ); } -void database_api_impl::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) +void database_api_impl::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) { - if(a > b) std::swap(a,b); - FC_ASSERT(a != b); - _market_subscriptions[ std::make_pair(a,b) ] = callback; + auto asset_a_id = get_asset_from_string(a)->id; + auto asset_b_id = get_asset_from_string(b)->id; + + if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id); + FC_ASSERT(asset_a_id != asset_b_id); + _market_subscriptions[ std::make_pair(asset_a_id,asset_b_id) ] = callback; } -void database_api::unsubscribe_from_market(asset_id_type a, asset_id_type b) +void database_api::unsubscribe_from_market(const std::string& a, const std::string& b) { my->unsubscribe_from_market( a, b ); } -void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b) +void database_api_impl::unsubscribe_from_market(const std::string& a, const std::string& b) { - if(a > b) std::swap(a,b); - FC_ASSERT(a != b); - _market_subscriptions.erase(std::make_pair(a,b)); + auto asset_a_id = get_asset_from_string(a)->id; + auto asset_b_id = get_asset_from_string(b)->id; + + if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id); + FC_ASSERT(asset_a_id != asset_b_id); + _market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id)); } market_ticker database_api::get_ticker( const string& base, const string& quote )const @@ -1555,9 +1630,10 @@ vector> database_api::get_witnesses(const vectorget_witnesses( witness_ids ); } -vector database_api::get_workers_by_account(account_id_type account)const +vector database_api::get_workers_by_account(const std::string account_id_or_name)const { const auto& idx = my->_db.get_index_type().indices().get(); + const account_id_type account = my->get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); vector result; @@ -1583,14 +1659,15 @@ vector> database_api_impl::get_witnesses(const vector database_api::get_witness_by_account(account_id_type account)const +fc::optional database_api::get_witness_by_account(const std::string account_id_or_name)const { - return my->get_witness_by_account( account ); + return my->get_witness_by_account( account_id_or_name ); } -fc::optional database_api_impl::get_witness_by_account(account_id_type account) const +fc::optional database_api_impl::get_witness_by_account(const std::string account_id_or_name) const { const auto& idx = _db.get_index_type().indices().get(); + const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if( itr != idx.end() ) return *itr; @@ -1658,14 +1735,15 @@ vector> database_api_impl::get_committee_membe return result; } -fc::optional database_api::get_committee_member_by_account(account_id_type account)const +fc::optional database_api::get_committee_member_by_account(const std::string account_id_or_name)const { - return my->get_committee_member_by_account( account ); + return my->get_committee_member_by_account( account_id_or_name ); } -fc::optional database_api_impl::get_committee_member_by_account(account_id_type account) const +fc::optional database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const { const auto& idx = _db.get_index_type().indices().get(); + const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if( itr != idx.end() ) return *itr; @@ -2134,9 +2212,9 @@ processed_transaction database_api_impl::validate_transaction( const signed_tran return _db.validate_transaction(trx); } -vector< fc::variant > database_api::get_required_fees( const vector& ops, asset_id_type id )const +vector< fc::variant > database_api::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const { - return my->get_required_fees( ops, id ); + return my->get_required_fees( ops, asset_id_or_symbol ); } /** @@ -2195,7 +2273,7 @@ struct get_required_fees_helper uint32_t current_recursion = 0; }; -vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, asset_id_type id )const +vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const { vector< operation > _ops = ops; // @@ -2205,7 +2283,7 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector result; result.reserve(ops.size()); - const asset_object& a = id(_db); + const asset_object& a = *get_asset_from_string(asset_id_or_symbol); get_required_fees_helper helper( _db.current_fee_schedule(), a.options.core_exchange_rate, @@ -2223,16 +2301,17 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector database_api::get_proposed_transactions( account_id_type id )const +vector database_api::get_proposed_transactions( const std::string account_id_or_name )const { - return my->get_proposed_transactions( id ); + return my->get_proposed_transactions( account_id_or_name ); } /** TODO: add secondary index that will accelerate this process */ -vector database_api_impl::get_proposed_transactions( account_id_type id )const +vector database_api_impl::get_proposed_transactions( const std::string account_id_or_name )const { const auto& idx = _db.get_index_type(); vector result; + const account_id_type id = get_account_from_string(account_id_or_name)->id; idx.inspect_all_objects( [&](const object& obj){ const proposal_object& p = static_cast(obj); @@ -2347,6 +2426,26 @@ vector database_api_impl::get_tournaments_by_state(tournament return result; } +const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id, + bool throw_if_not_found ) const +{ + // TODO cache the result to avoid repeatly fetching from db + FC_ASSERT( name_or_id.size() > 0); + const account_object* account = nullptr; + if (std::isdigit(name_or_id[0])) + account = _db.find(fc::variant(name_or_id, 1).as(1)); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(name_or_id); + if (itr != idx.end()) + account = &*itr; + } + if(throw_if_not_found) + FC_ASSERT( account, "no such account" ); + return account; +} + vector database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const { return my->get_registered_tournaments(account_filter, limit); @@ -2364,6 +2463,80 @@ vector database_api_impl::get_registered_tournaments(account return tournament_ids; } +////////////////////////////////////////////////////////////////////// +// // +// GPOS methods // +// // +////////////////////////////////////////////////////////////////////// + +graphene::app::gpos_info database_api::get_gpos_info(const account_id_type account) const +{ + return my->get_gpos_info(account); + +} +graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const +{ + FC_ASSERT( _db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time + gpos_info result; + + result.vesting_factor = _db.calculate_vesting_factor(account(_db)); + result.current_subperiod = _db.get_gpos_current_subperiod(); + result.last_voted_time = account(_db).statistics(_db).last_vote_time; + + const auto& dividend_data = asset_id_type()(_db).dividend_data(_db); + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db); + result.award = _db.get_balance(dividend_distribution_account, asset_id_type()(_db)); + + share_type total_amount; + auto balance_type = vesting_balance_type::gpos; +#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX + // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset + auto vesting_balances_begin = + vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type)); + auto vesting_balances_end = + vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type())); + + for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) + { + total_amount += vesting_balance_obj.balance.amount; + } +#else + const vesting_balance_index& vesting_index = _db.get_index_type(); + const auto& vesting_balances = vesting_index.indices().get(); + for (const vesting_balance_object& vesting_balance_obj : vesting_balances) + { + if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance_type == balance_type) + { + total_amount += vesting_balance_obj.balance.amount; + } + } +#endif + + vector account_vbos; + const time_point_sec now = _db.head_block_time(); + auto vesting_range = _db.get_index_type().indices().get().equal_range(account); + std::for_each(vesting_range.first, vesting_range.second, + [&account_vbos, now](const vesting_balance_object& balance) { + if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos + && balance.balance.asset_id == asset_id_type()) + account_vbos.emplace_back(balance); + }); + + share_type allowed_withdraw_amount = 0, account_vested_balance = 0; + + for (const vesting_balance_object& vesting_balance_obj : account_vbos) + { + account_vested_balance += vesting_balance_obj.balance.amount; + if(vesting_balance_obj.is_withdraw_allowed(_db.head_block_time(), vesting_balance_obj.balance.amount)) + allowed_withdraw_amount += vesting_balance_obj.balance.amount; + } + + result.total_amount = total_amount; + result.allowed_withdraw_amount = allowed_withdraw_amount; + result.account_vested_balance = account_vested_balance; + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp deleted file mode 100644 index 90538087..00000000 --- a/libraries/app/impacted.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., 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 - -namespace graphene { namespace app { - -using namespace fc; -using namespace graphene::chain; - -// TODO: Review all of these, especially no-ops -struct get_impacted_account_visitor -{ - flat_set& _impacted; - get_impacted_account_visitor( flat_set& impact ):_impacted(impact) {} - typedef void result_type; - - void operator()( const transfer_operation& op ) - { - _impacted.insert( op.to ); - } - - void operator()( const asset_claim_fees_operation& op ){} - void operator()( const limit_order_create_operation& op ) {} - void operator()( const limit_order_cancel_operation& op ) - { - _impacted.insert( op.fee_paying_account ); - } - void operator()( const call_order_update_operation& op ) {} - void operator()( const fill_order_operation& op ) - { - _impacted.insert( op.account_id ); - } - - void operator()( const account_create_operation& op ) - { - _impacted.insert( op.registrar ); - _impacted.insert( op.referrer ); - add_authority_accounts( _impacted, op.owner ); - add_authority_accounts( _impacted, op.active ); - } - - void operator()( const account_update_operation& op ) - { - _impacted.insert( op.account ); - if( op.owner ) - add_authority_accounts( _impacted, *(op.owner) ); - if( op.active ) - add_authority_accounts( _impacted, *(op.active) ); - } - - void operator()( const account_whitelist_operation& op ) - { - _impacted.insert( op.account_to_list ); - } - - void operator()( const account_upgrade_operation& op ) {} - void operator()( const account_transfer_operation& op ) - { - _impacted.insert( op.new_owner ); - } - - void operator()( const asset_create_operation& op ) {} - void operator()( const asset_update_operation& op ) - { - if( op.new_issuer ) - _impacted.insert( *(op.new_issuer) ); - } - - void operator()( const asset_update_bitasset_operation& op ) {} - void operator()( const asset_update_dividend_operation& op ) {} - void operator()( const asset_dividend_distribution_operation& op ) - { - _impacted.insert( op.account_id ); - } - - void operator()( const asset_update_feed_producers_operation& op ) {} - - void operator()( const asset_issue_operation& op ) - { - _impacted.insert( op.issue_to_account ); - } - - void operator()( const asset_reserve_operation& op ) {} - void operator()( const asset_fund_fee_pool_operation& op ) {} - void operator()( const asset_settle_operation& op ) {} - void operator()( const asset_global_settle_operation& op ) {} - void operator()( const asset_publish_feed_operation& op ) {} - void operator()( const witness_create_operation& op ) - { - _impacted.insert( op.witness_account ); - } - void operator()( const witness_update_operation& op ) - { - _impacted.insert( op.witness_account ); - } - - void operator()( const proposal_create_operation& op ) - { - vector other; - for( const auto& proposed_op : op.proposed_ops ) - operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other ); - for( auto& o : other ) - add_authority_accounts( _impacted, o ); - } - - void operator()( const proposal_update_operation& op ) {} - void operator()( const proposal_delete_operation& op ) {} - - void operator()( const withdraw_permission_create_operation& op ) - { - _impacted.insert( op.authorized_account ); - } - - void operator()( const withdraw_permission_update_operation& op ) - { - _impacted.insert( op.authorized_account ); - } - - void operator()( const withdraw_permission_claim_operation& op ) - { - _impacted.insert( op.withdraw_from_account ); - } - - void operator()( const withdraw_permission_delete_operation& op ) - { - _impacted.insert( op.authorized_account ); - } - - void operator()( const committee_member_create_operation& op ) - { - _impacted.insert( op.committee_member_account ); - } - void operator()( const committee_member_update_operation& op ) - { - _impacted.insert( op.committee_member_account ); - } - void operator()( const committee_member_update_global_parameters_operation& op ) {} - - void operator()( const vesting_balance_create_operation& op ) - { - _impacted.insert( op.owner ); - } - - void operator()( const vesting_balance_withdraw_operation& op ) {} - void operator()( const worker_create_operation& op ) {} - void operator()( const custom_operation& op ) {} - void operator()( const assert_operation& op ) {} - void operator()( const balance_claim_operation& op ) {} - - void operator()( const override_transfer_operation& op ) - { - _impacted.insert( op.to ); - _impacted.insert( op.from ); - _impacted.insert( op.issuer ); - } - - void operator()( const transfer_to_blind_operation& op ) - { - _impacted.insert( op.from ); - for( const auto& out : op.outputs ) - add_authority_accounts( _impacted, out.owner ); - } - - void operator()( const blind_transfer_operation& op ) - { - for( const auto& in : op.inputs ) - add_authority_accounts( _impacted, in.owner ); - for( const auto& out : op.outputs ) - add_authority_accounts( _impacted, out.owner ); - } - - void operator()( const transfer_from_blind_operation& op ) - { - _impacted.insert( op.to ); - for( const auto& in : op.inputs ) - add_authority_accounts( _impacted, in.owner ); - } - - void operator()( const asset_settle_cancel_operation& op ) - { - _impacted.insert( op.account ); - } - - void operator()( const fba_distribute_operation& op ) - { - _impacted.insert( op.account_id ); - } - - void operator()( const sport_create_operation& op ) {} - void operator()( const sport_update_operation& op ) {} - void operator()( const sport_delete_operation& op ) {} - void operator()( const event_group_create_operation& op ) {} - void operator()( const event_group_update_operation& op ) {} - void operator()( const event_group_delete_operation& op ) {} - void operator()( const event_create_operation& op ) {} - void operator()( const event_update_operation& op ) {} - void operator()( const event_update_status_operation& op ) {} - void operator()( const betting_market_rules_create_operation& op ) {} - void operator()( const betting_market_rules_update_operation& op ) {} - void operator()( const betting_market_group_create_operation& op ) {} - void operator()( const betting_market_group_update_operation& op ) {} - void operator()( const betting_market_create_operation& op ) {} - void operator()( const betting_market_update_operation& op ) {} - void operator()( const betting_market_group_resolve_operation& op ) {} - void operator()( const betting_market_group_cancel_unmatched_bets_operation& op ) {} - - void operator()( const bet_place_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - void operator()( const bet_cancel_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - void operator()( const bet_canceled_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - void operator()( const bet_adjusted_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - void operator()( const bet_matched_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - void operator()( const betting_market_group_resolved_operation& op ) - { - _impacted.insert( op.bettor_id ); - } - - void operator()( const tournament_create_operation& op ) - { - _impacted.insert( op.creator ); - _impacted.insert( op.options.whitelist.begin(), op.options.whitelist.end() ); - } - void operator()( const tournament_join_operation& op ) - { - _impacted.insert( op.payer_account_id ); - _impacted.insert( op.player_account_id ); - } - void operator()( const tournament_leave_operation& op ) - { - //if account canceling registration is not the player, it must be the payer - if (op.canceling_account_id != op.player_account_id) - _impacted.erase( op.canceling_account_id ); - _impacted.erase( op.player_account_id ); - } - void operator()( const game_move_operation& op ) - { - _impacted.insert( op.player_account_id ); - } - void operator()( const tournament_payout_operation& op ) - { - _impacted.insert( op.payout_account_id ); - } - void operator()( const affiliate_payout_operation& op ) - { - _impacted.insert( op.affiliate ); - } - void operator()( const affiliate_referral_payout_operation& op ) { } - void operator()( const lottery_asset_create_operation& op) { } - void operator()( const ticket_purchase_operation& op ) - { - _impacted.insert( op.buyer ); - } - void operator()( const lottery_reward_operation& op ) { - _impacted.insert( op.winner ); - } - void operator()( const lottery_end_operation& op ) { - for( auto participant : op.participants ) { - _impacted.insert(participant.first); - } - } - void operator()( const sweeps_vesting_claim_operation& op ) { - _impacted.insert( op.account ); - } - void operator()( const son_create_operation& op ){ - _impacted.insert( op.owner_account ); - } - void operator()( const son_update_operation& op ){ - _impacted.insert( op.owner_account ); - } - void operator()( const son_delete_operation& op ){ - _impacted.insert( op.owner_account ); - } - void operator()( const son_heartbeat_operation& op ){ - _impacted.insert( op.owner_account ); - } - void operator()( const son_report_down_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_maintenance_operation& op ){ - _impacted.insert( op.owner_account ); - } - void operator()( const son_wallet_recreate_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_wallet_update_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_wallet_deposit_create_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_wallet_deposit_process_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_wallet_withdraw_create_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const son_wallet_withdraw_process_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const sidechain_address_add_operation& op ){ - _impacted.insert( op.sidechain_address_account ); - } - void operator()( const sidechain_address_update_operation& op ){ - _impacted.insert( op.sidechain_address_account ); - } - void operator()( const sidechain_address_delete_operation& op ){ - _impacted.insert( op.sidechain_address_account ); - } - void operator()( const bitcoin_transaction_send_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const bitcoin_transaction_sign_operation& op ){ - _impacted.insert( op.payer ); - } - void operator()( const bitcoin_send_transaction_process_operation& op ){ - _impacted.insert( op.payer ); - } -}; - -void operation_get_impacted_accounts( const operation& op, flat_set& result ) -{ - get_impacted_account_visitor vtor = get_impacted_account_visitor( result ); - op.visit( vtor ); -} - -void transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) -{ - for( const auto& op : tx.operations ) - operation_get_impacted_accounts( op, result ); -} - -} } diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index a263c4dd..4adf73a3 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -31,6 +31,8 @@ #include #include +#include + #include #include #include @@ -95,31 +97,32 @@ namespace graphene { namespace app { class history_api { public: - history_api(application& app):_app(app){} + history_api(application& app) + :_app(app), database_api( std::ref(*app.chain_database())) {} /** * @brief Get operations relevant to the specificed account - * @param account The account whose history should be queried + * @param account_id_or_name The account ID or name whose history should be queried * @param stop ID of the earliest operation to retrieve * @param limit Maximum number of operations to retrieve (must not exceed 100) * @param start ID of the most recent operation to retrieve * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_account_history(account_id_type account, + vector get_account_history(const std::string account_id_or_name, operation_history_id_type stop = operation_history_id_type(), unsigned limit = 100, operation_history_id_type start = operation_history_id_type())const; /** * @brief Get only asked operations relevant to the specified account - * @param account The account whose history should be queried + * @param account_id_or_name The account ID or name whose history should be queried * @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...) * @param stop ID of the earliest operation to retrieve * @param limit Maximum number of operations to retrieve (must not exceed 100) * @param start ID of the most recent operation to retrieve * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_account_history_operations(account_id_type account, + vector get_account_history_operations(const std::string account_id_or_name, int operation_id, operation_history_id_type start = operation_history_id_type(), operation_history_id_type stop = operation_history_id_type(), @@ -129,7 +132,7 @@ namespace graphene { namespace app { * @breif Get operations relevant to the specified account referenced * by an event numbering specific to the account. The current number of operations * for the account can be found in the account statistics (or use 0 for start). - * @param account The account whose history should be queried + * @param account_id_or_name The account ID or name whose history should be queried * @param stop Sequence number of earliest operation. 0 is default and will * query 'limit' number of operations. * @param limit Maximum number of operations to retrieve (must not exceed 100) @@ -137,18 +140,19 @@ namespace graphene { namespace app { * 0 is default, which will start querying from the most recent operation. * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_relative_account_history( account_id_type account, + vector get_relative_account_history( const std::string account_id_or_name, uint32_t stop = 0, unsigned limit = 100, uint32_t start = 0) const; - vector get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const; - vector get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds, + vector get_fill_order_history( std::string asset_a, std::string asset_b, uint32_t limit )const; + vector get_market_history( std::string asset_a, std::string asset_b, uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const; vector list_core_accounts()const; flat_set get_market_history_buckets()const; private: application& _app; + graphene::app::database_api database_api; }; /** @@ -325,17 +329,47 @@ namespace graphene { namespace app { class asset_api { public: - asset_api(graphene::chain::database& db); + asset_api(graphene::app::application& app); ~asset_api(); - vector get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit )const; - int get_asset_holders_count( asset_id_type asset_id )const; + /** + * @brief Get asset holders for a specific asset + * @param asset The specific asset id or symbol + * @param start The start index + * @param limit Maximum limit must not exceed 100 + * @return A list of asset holders for the specified asset + */ + vector get_asset_holders( std::string asset, uint32_t start, uint32_t limit )const; + + /** + * @brief Get asset holders count for a specific asset + * @param asset The specific asset id or symbol + * @return Holders count for the specified asset + */ + int get_asset_holders_count( std::string asset )const; + + /** + * @brief Get all asset holders + * @return A list of all asset holders + */ vector get_all_asset_holders() const; private: + graphene::app::application& _app; graphene::chain::database& _db; + graphene::app::database_api database_api; }; +} } // graphene::app +extern template class fc::api; +extern template class fc::api; +extern template class fc::api; +extern template class fc::api; +extern template class fc::api; +extern template class fc::api; +extern template class fc::api; + +namespace graphene { namespace app { /** * @brief The login_api class implements the bottom layer of the RPC API * @@ -397,6 +431,8 @@ namespace graphene { namespace app { }} // graphene::app +extern template class fc::api; + FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation, (id)(block_num)(trx_num)(trx) ) FC_REFLECT( graphene::app::verify_range_result, diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index b0ace3d7..a436aacd 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -56,8 +56,8 @@ namespace graphene { namespace app { auto plug = std::make_shared(); plug->plugin_set_app(this); - string cli_plugin_desc = plug->plugin_name() + " plugin. " + plug->plugin_description() + "\nOptions"; - boost::program_options::options_description plugin_cli_options( cli_plugin_desc ), plugin_cfg_options; + boost::program_options::options_description plugin_cli_options(plug->plugin_name() + " plugin. " + plug->plugin_description() + "\nOptions"), plugin_cfg_options; + //boost::program_options::options_description plugin_cli_options("Options for plugin " + plug->plugin_name()), plugin_cfg_options; plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options); if( !plugin_cli_options.options().empty() ) _cli_options.add(plugin_cli_options); @@ -99,7 +99,9 @@ namespace graphene { namespace app { bool is_plugin_enabled(const string& name) const; - private: + std::shared_ptr elasticsearch_thread; + + private: void add_available_plugin( std::shared_ptr p ); std::shared_ptr my; diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 76ef822c..a89224b4 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -117,6 +117,16 @@ struct market_trade double value; }; +struct gpos_info { + double vesting_factor; + asset award; + share_type total_amount; + uint32_t current_subperiod; + fc::time_point_sec last_voted_time; + share_type allowed_withdraw_amount; + share_type account_vested_balance; +}; + /** * @brief The database_api class implements the RPC API for the chain database. * @@ -244,13 +254,21 @@ class database_api ////////////// /** - * @brief Get a list of accounts by ID + * @brief Get account object from a name or ID + * @param name_or_id name or ID of the account + * @return Account ID + * + */ + account_id_type get_account_id_from_string(const std::string& name_or_id)const; + + /** + * @brief Get a list of accounts by ID or Name * @param account_ids IDs of the accounts to retrieve * @return The accounts corresponding to the provided IDs * * This function has semantics identical to @ref get_objects */ - vector> get_accounts(const vector& account_ids)const; + vector> get_accounts(const vector& account_names_or_ids)const; /** * @brief Fetch all objects relevant to the specified accounts and subscribe to updates @@ -270,7 +288,7 @@ class database_api /** * @return all accounts that referr to the key or account id in their owner or active authorities. */ - vector get_account_references( account_id_type account_id )const; + vector get_account_references( const std::string account_name_or_id )const; /** * @brief Get a list of accounts by name @@ -299,7 +317,8 @@ class database_api * @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in * @return Balances of the account */ - vector get_account_balances(account_id_type id, const flat_set& assets)const; + vector get_account_balances( const std::string& account_name_or_id, + const flat_set& assets )const; /// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID. vector get_named_account_balances(const std::string& name, const flat_set& assets)const; @@ -309,7 +328,7 @@ class database_api vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( account_id_type account_id )const; + vector get_vesting_balances( const std::string account_id_or_name )const; /** * @brief Get the total number of accounts registered with the blockchain @@ -320,14 +339,21 @@ class database_api // Assets // //////////// + /** + * @brief Get asset ID from an asset symbol or ID + * @param symbol_or_id symbol name or ID of the asset + * @return asset ID + */ + asset_id_type get_asset_id_from_string(const std::string& symbol_or_id) const; + /** * @brief Get a list of assets by ID - * @param asset_ids IDs of the assets to retrieve + * @param asset_symbols_or_ids IDs or names of the assets to retrieve * @return The assets corresponding to the provided IDs * * This function has semantics identical to @ref get_objects */ - vector> get_assets(const vector& asset_ids)const; + vector> get_assets(const vector& asset_symbols_or_ids)const; /** * @brief Get assets alphabetically by symbol name @@ -429,47 +455,47 @@ class database_api * @param limit Maximum number of orders to retrieve * @return The limit orders, ordered from least price to greatest */ - vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; + vector get_limit_orders(const std::string& a, const std::string& b, uint32_t limit)const; /** * @brief Get call orders in a given asset - * @param a ID of asset being called + * @param a ID or name of asset being called * @param limit Maximum number of orders to retrieve * @return The call orders, ordered from earliest to be called to latest */ - vector get_call_orders(asset_id_type a, uint32_t limit)const; + vector get_call_orders(const std::string& a, uint32_t limit)const; /** * @brief Get forced settlement orders in a given asset - * @param a ID of asset being settled + * @param a ID or name of asset being settled * @param limit Maximum number of orders to retrieve * @return The settle orders, ordered from earliest settlement date to latest */ - vector get_settle_orders(asset_id_type a, uint32_t limit)const; + vector get_settle_orders(const std::string& a, uint32_t limit)const; /** * @return all open margin positions for a given account id. */ - vector get_margin_positions( const account_id_type& id )const; + vector get_margin_positions( const std::string account_id_or_name )const; /** * @brief Request notification when the active orders in the market between two assets changes * @param callback Callback method which is called when the market changes - * @param a First asset ID - * @param b Second asset ID + * @param a First asset ID or name + * @param b Second asset ID or name * * Callback will be passed a variant containing a vector>. The vector will * contain, in order, the operations which changed the market, and their results. */ void subscribe_to_market(std::function callback, - asset_id_type a, asset_id_type b); + const std::string& a, const std::string& b); /** * @brief Unsubscribe from updates to a given market - * @param a First asset ID - * @param b Second asset ID + * @param a First asset ID or name + * @param b Second asset ID or name */ - void unsubscribe_from_market( asset_id_type a, asset_id_type b ); + void unsubscribe_from_market( const std::string& a, const std::string& b ); /** * @brief Returns the ticker for the market assetA:assetB @@ -528,7 +554,7 @@ class database_api * @param account The ID of the account whose witness should be retrieved * @return The witness object, or null if the account does not have a witness */ - fc::optional get_witness_by_account(account_id_type account)const; + fc::optional get_witness_by_account(const std::string account_name_or_id)const; /** * @brief Get names and IDs for registered witnesses @@ -558,10 +584,10 @@ class database_api /** * @brief Get the committee_member owned by a given account - * @param account The ID of the account whose committee_member should be retrieved + * @param account_id_or_name The ID or name of the account whose committee_member should be retrieved * @return The committee_member object, or null if the account does not have a committee_member */ - fc::optional get_committee_member_by_account(account_id_type account)const; + fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; /** * @brief Get names and IDs for registered committee_members @@ -671,9 +697,11 @@ class database_api /// WORKERS /** - * Return the worker objects associated with this account. + * @brief Return the worker objects associated with this account. + * @param account_id_or_name The ID or name of the account whose worker should be retrieved + * @return The worker object or null if the account does not have a worker */ - vector get_workers_by_account(account_id_type account)const; + vector get_workers_by_account(const std::string account_id_or_name)const; /////////// @@ -730,7 +758,7 @@ class database_api * For each operation calculate the required fee in the specified asset type. If the asset type does * not have a valid core_exchange_rate */ - vector< fc::variant > get_required_fees( const vector& ops, asset_id_type id )const; + vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; /////////////////////////// // Proposed transactions // @@ -739,7 +767,7 @@ class database_api /** * @return the set of proposed transactions relevant to the specified account id. */ - vector get_proposed_transactions( account_id_type id )const; + vector get_proposed_transactions( const std::string account_id_or_name )const; ////////////////////// // Blinded balances // @@ -772,17 +800,31 @@ class database_api */ vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; - private: + ////////// + // GPOS // + ////////// + /** + * @return account and network GPOS information + */ + gpos_info get_gpos_info(const account_id_type account) const; + + + +private: std::shared_ptr< database_api_impl > my; }; } } +extern template class fc::api; + FC_REFLECT( graphene::app::order, (price)(quote)(base) ); FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) ); +FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount)(current_subperiod)(last_voted_time)(allowed_withdraw_amount)(account_vested_balance) ); + FC_API(graphene::app::database_api, // Objects @@ -813,6 +855,7 @@ FC_API(graphene::app::database_api, (is_public_key_registered) // Accounts + (get_account_id_from_string) (get_accounts) (get_full_accounts) (get_account_by_name) @@ -833,6 +876,7 @@ FC_API(graphene::app::database_api, (list_assets) (lookup_asset_symbols) (get_asset_count) + (get_asset_id_from_string) // Peerplays (list_sports) @@ -918,4 +962,7 @@ FC_API(graphene::app::database_api, (get_tournaments_by_state) (get_tournaments ) (get_registered_tournaments) + + // gpos + (get_gpos_info) ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 9c068ba5..8fba8a43 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -61,6 +61,7 @@ add_library( graphene_chain protocol/confidential.cpp protocol/vote.cpp protocol/tournament.cpp + protocol/small_ops.cpp genesis_state.cpp get_config.cpp @@ -94,6 +95,7 @@ add_library( graphene_chain fba_object.cpp proposal_object.cpp vesting_balance_object.cpp + small_objects.cpp block_database.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 2d117f52..ad6ac5dc 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -162,33 +162,39 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio if( referrer_percent > GRAPHENE_100_PERCENT ) referrer_percent = GRAPHENE_100_PERCENT; } + const auto& global_properties = d.get_global_properties(); - const auto& new_acnt_object = db().create( [&]( account_object& obj ){ - obj.registrar = o.registrar; - obj.referrer = o.referrer; - obj.lifetime_referrer = o.referrer(db()).lifetime_referrer; + const auto& new_acnt_object = d.create( [&o,&d,&global_properties,referrer_percent]( account_object& obj ) + { + obj.registrar = o.registrar; + obj.referrer = o.referrer; + obj.lifetime_referrer = o.referrer(d).lifetime_referrer; - auto& params = db().get_global_properties().parameters; - obj.network_fee_percentage = params.network_percent_of_fee; - obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee; - obj.referrer_rewards_percentage = referrer_percent; + const auto& params = global_properties.parameters; + obj.network_fee_percentage = params.network_percent_of_fee; + obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee; + obj.referrer_rewards_percentage = referrer_percent; - obj.name = o.name; - obj.owner = o.owner; - obj.active = o.active; - obj.options = o.options; - obj.statistics = db().create([&](account_statistics_object& s){s.owner = obj.id;}).id; + obj.name = o.name; + obj.owner = o.owner; + obj.active = o.active; + obj.options = o.options; + obj.statistics = d.create([&obj](account_statistics_object& s){ + s.owner = obj.id; + s.name = obj.name; + s.is_voting = obj.options.is_voting(); + }).id; - if( o.extensions.value.owner_special_authority.valid() ) - obj.owner_special_authority = *(o.extensions.value.owner_special_authority); - if( o.extensions.value.active_special_authority.valid() ) - obj.active_special_authority = *(o.extensions.value.active_special_authority); - if( o.extensions.value.buyback_options.valid() ) - { - obj.allowed_assets = o.extensions.value.buyback_options->markets; - obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy ); - } - obj.affiliate_distributions = o.extensions.value.affiliate_distributions; + if( o.extensions.value.owner_special_authority.valid() ) + obj.owner_special_authority = *(o.extensions.value.owner_special_authority); + if( o.extensions.value.active_special_authority.valid() ) + obj.active_special_authority = *(o.extensions.value.active_special_authority); + if( o.extensions.value.buyback_options.valid() ) + { + obj.allowed_assets = o.extensions.value.buyback_options->markets; + obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy ); + } + obj.affiliate_distributions = o.extensions.value.affiliate_distributions; }); if( has_small_percent ) @@ -200,17 +206,18 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio wlog( "Affected account object is ${o}", ("o", new_acnt_object) ); } - const auto& dynamic_properties = db().get_dynamic_global_properties(); - db().modify(dynamic_properties, [](dynamic_global_property_object& p) { + const auto& dynamic_properties = d.get_dynamic_global_properties(); + d.modify(dynamic_properties, [](dynamic_global_property_object& p) { ++p.accounts_registered_this_interval; }); - const auto& global_properties = db().get_global_properties(); - if( dynamic_properties.accounts_registered_this_interval % - global_properties.parameters.accounts_per_fee_scale == 0 ) - db().modify(global_properties, [&dynamic_properties](global_property_object& p) { + if( dynamic_properties.accounts_registered_this_interval % global_properties.parameters.accounts_per_fee_scale == 0 + && global_properties.parameters.account_fee_scale_bitshifts != 0 ) + { + d.modify(global_properties, [&dynamic_properties](global_property_object& p) { p.parameters.current_fees->get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); + } if( o.extensions.value.owner_special_authority.valid() || o.extensions.value.active_special_authority.valid() ) @@ -280,18 +287,26 @@ void_result account_update_evaluator::do_apply( const account_update_operation& { try { database& d = db(); + bool sa_before = acnt->has_special_authority(); + + // update account statistics if( o.new_options.valid() ) { d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso ) { + fc::optional< bool > flag = o.extensions.value.update_last_voting_time; if((o.new_options->votes != acnt->options.votes || - o.new_options->voting_account != acnt->options.voting_account)) + o.new_options->voting_account != acnt->options.voting_account) || + (flag.valid() && *flag)) aso.last_vote_time = d.head_block_time(); + + if(o.new_options->is_voting() != acnt->options.is_voting()) + aso.is_voting = !aso.is_voting; } ); } - bool sa_before, sa_after; - d.modify( *acnt, [&](account_object& a){ + // update account object + d.modify( *acnt, [&o](account_object& a){ if( o.owner ) { a.owner = *o.owner; @@ -303,7 +318,6 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.top_n_control_flags = 0; } if( o.new_options ) a.options = *o.new_options; - sa_before = a.has_special_authority(); if( o.extensions.value.owner_special_authority.valid() ) { a.owner_special_authority = *(o.extensions.value.owner_special_authority); @@ -314,9 +328,10 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.active_special_authority = *(o.extensions.value.active_special_authority); a.top_n_control_flags = 0; } - sa_after = a.has_special_authority(); }); + bool sa_after = acnt->has_special_authority(); + if( sa_before & (!sa_after) ) { const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get(); diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index e51e1705..71ee28de 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -22,9 +22,9 @@ * THE SOFTWARE. */ #include -#include #include -#include + +#include #include namespace graphene { namespace chain { @@ -46,6 +46,8 @@ void account_balance_object::adjust_balance(const asset& delta) { assert(delta.asset_id == asset_type); balance += delta.amount; + if( asset_type == asset_id_type() ) // CORE asset + maintenance_flag = true; } void account_statistics_object::process_fees(const account_object& a, database& d) const @@ -57,8 +59,8 @@ void account_statistics_object::process_fees(const account_object& a, database& // Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead. // No need to check the registrar; registrars are required to be lifetime members. if( account.referrer(d).is_basic_account(d.head_block_time()) ) - d.modify(account, [](account_object& a) { - a.referrer = a.lifetime_referrer; + d.modify( account, [](account_object& acc) { + acc.referrer = acc.lifetime_referrer; }); share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); @@ -74,8 +76,8 @@ void account_statistics_object::process_fees(const account_object& a, database& share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage); share_type referral = core_fee_total - network_cut - lifetime_cut; - d.modify(asset_dynamic_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) { - d.accumulated_fees += network_cut; + d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) { + addo.accumulated_fees += network_cut; }); // Potential optimization: Skip some of this math and object lookups by special casing on the account type. @@ -318,3 +320,8 @@ const account_balance_object* balances_by_account_index::get_account_balance( co } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_statistics_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::pending_dividend_payout_balance_for_holder_object ) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 59b590dd..7a26a2cb 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -133,33 +133,36 @@ void asset_create_evaluator::pay_fee() object_id_type asset_create_evaluator::do_apply( const asset_create_operation& op ) { try { + database& d = db(); + // includes changes from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) bool hf_429 = fee_is_odd && db().head_block_time() > HARDFORK_CORE_429_TIME; const asset_dynamic_data_object& dyn_asset = - db().create( [&]( asset_dynamic_data_object& a ) { + d.create( [hf_429,this]( asset_dynamic_data_object& a ) { a.current_supply = 0; a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0); }); - if( fee_is_odd && !hf_429 ) - { - const auto& core_dd = db().get( asset_id_type() ).dynamic_data( db() ); - db().modify( core_dd, [=]( asset_dynamic_data_object& dd ) { + if( fee_is_odd && !hf_429 ) + { + const auto& core_dd = d.get_core_asset().dynamic_data( d ); + d.modify( core_dd, []( asset_dynamic_data_object& dd ) { dd.current_supply++; - }); - } + }); + } + + auto next_asset_id = d.get_index_type().get_next_id(); asset_bitasset_data_id_type bit_asset_id; if( op.bitasset_opts.valid() ) - bit_asset_id = db().create( [&]( asset_bitasset_data_object& a ) { + bit_asset_id = d.create( [&]( asset_bitasset_data_object& a ) { a.options = *op.bitasset_opts; a.is_prediction_market = op.is_prediction_market; + a.asset_id = next_asset_id; }).id; - auto next_asset_id = db().get_index_type().get_next_id(); - const asset_object& new_asset = - db().create( [&]( asset_object& a ) { + d.create( [&]( asset_object& a ) { a.issuer = op.issuer; a.symbol = op.symbol; a.precision = op.precision; @@ -175,7 +178,7 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o if( op.bitasset_opts.valid() ) a.bitasset_data_id = bit_asset_id; }); - assert( new_asset.id == next_asset_id ); + FC_ASSERT( new_asset.id == next_asset_id ); return new_asset.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -281,33 +284,36 @@ void lottery_asset_create_evaluator::pay_fee() object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_create_operation& op ) { try { + database& d = db(); + // includes changes from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) - bool hf_429 = fee_is_odd && db().head_block_time() > HARDFORK_CORE_429_TIME; + bool hf_429 = fee_is_odd && d.head_block_time() > HARDFORK_CORE_429_TIME; const asset_dynamic_data_object& dyn_asset = - db().create( [&]( asset_dynamic_data_object& a ) { + d.create( [&]( asset_dynamic_data_object& a ) { a.current_supply = 0; a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0); }); if( fee_is_odd && !hf_429 ) { - const auto& core_dd = db().get( asset_id_type() ).dynamic_data( db() ); - db().modify( core_dd, [=]( asset_dynamic_data_object& dd ) { + const auto& core_dd = d.get( asset_id_type() ).dynamic_data( db() ); + d.modify( core_dd, [=]( asset_dynamic_data_object& dd ) { dd.current_supply++; }); } + auto next_asset_id = d.get_index_type().get_next_id(); + asset_bitasset_data_id_type bit_asset_id; if( op.bitasset_opts.valid() ) - bit_asset_id = db().create( [&]( asset_bitasset_data_object& a ) { + bit_asset_id = d.create( [&op,next_asset_id]( asset_bitasset_data_object& a ) { a.options = *op.bitasset_opts; a.is_prediction_market = op.is_prediction_market; + a.asset_id = next_asset_id; }).id; - auto next_asset_id = db().get_index_type().get_next_id(); - const asset_object& new_asset = - db().create( [&]( asset_object& a ) { + d.create( [&op,next_asset_id,&dyn_asset,bit_asset_id,&d]( asset_object& a ) { a.issuer = op.issuer; a.symbol = op.symbol; a.precision = op.precision; @@ -316,7 +322,7 @@ object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_cre a.lottery_options = op.extensions; //a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id ); a.lottery_options->owner = a.id; - db().create([&](lottery_balance_object& lbo) { + d.create([&a](lottery_balance_object& lbo) { lbo.lottery_id = a.id; }); if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 ) @@ -327,7 +333,7 @@ object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_cre if( op.bitasset_opts.valid() ) a.bitasset_data_id = bit_asset_id; }); - assert( new_asset.id == next_asset_id ); + FC_ASSERT( new_asset.id == next_asset_id, "Unexpected object database error, object id mismatch" ); return new_asset.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -354,7 +360,7 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o ) { try { db().adjust_balance( o.issue_to_account, o.asset_to_issue ); - db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){ + db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){ data.current_supply += o.asset_to_issue.amount; }); @@ -386,7 +392,7 @@ void_result asset_reserve_evaluator::do_apply( const asset_reserve_operation& o { try { db().adjust_balance( o.payer, -o.amount_to_reserve ); - db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){ + db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){ data.current_supply -= o.amount_to_reserve.amount; }); @@ -408,7 +414,7 @@ void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_op { try { db().adjust_balance(o.from_account, -o.amount); - db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ) { + db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ) { data.fee_pool += o.amount; }); @@ -483,7 +489,21 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o) d.cancel_order(*itr); } - d.modify(*asset_to_update, [&](asset_object& a) { + // For market-issued assets, if core change rate changed, update flag in bitasset data + if( asset_to_update->is_market_issued() + && asset_to_update->options.core_exchange_rate != o.new_options.core_exchange_rate ) + { + const auto& bitasset = asset_to_update->bitasset_data(d); + if( !bitasset.asset_cer_updated ) + { + d.modify( bitasset, [](asset_bitasset_data_object& b) + { + b.asset_cer_updated = true; + }); + } + } + + d.modify(*asset_to_update, [&o](asset_object& a) { if( o.new_issuer ) a.issuer = *o.new_issuer; a.options = o.new_options; diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 63df70a3..88e5dfca 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -24,10 +24,9 @@ #include #include +#include #include -#include - using namespace graphene::chain; share_type asset_bitasset_data_object::max_force_settlement_volume(share_type current_supply) const @@ -61,12 +60,15 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time if( current_feeds.size() < options.minimum_feeds ) { //... don't calculate a median, and set a null feed + feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance current_feed_publication_time = current_time; current_feed = price_feed(); return; } if( current_feeds.size() == 1 ) { + if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate ) + feed_cer_updated = true; current_feed = std::move(current_feeds.front()); return; } @@ -85,6 +87,8 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time #undef CALCULATE_MEDIAN_VALUE // *** End Median Calculations *** + if( current_feed.core_exchange_rate != median_feed.core_exchange_rate ) + feed_cer_updated = true; current_feed = median_feed; } @@ -291,3 +295,11 @@ void sweeps_vesting_balance_object::adjust_balance( const asset& delta ) FC_ASSERT( delta.asset_id == asset_id ); balance += delta.amount.value; } + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dynamic_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_bitasset_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dividend_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::total_distributed_dividend_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::lottery_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::sweeps_vesting_balance_object ) diff --git a/libraries/chain/balance_evaluator.cpp b/libraries/chain/balance_evaluator.cpp index 8d29c01d..817d736f 100644 --- a/libraries/chain/balance_evaluator.cpp +++ b/libraries/chain/balance_evaluator.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index d3756698..73d7703b 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -77,15 +77,7 @@ void_result committee_member_update_evaluator::do_apply( const committee_member_ void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o) { try { FC_ASSERT(trx_state->_is_proposed_trx); - - if( db().head_block_time() < HARDFORK_1000_TIME ) // TODO: remove after hf - FC_ASSERT( !o.new_parameters.extensions.value.min_bet_multiplier.valid() - && !o.new_parameters.extensions.value.max_bet_multiplier.valid() - && !o.new_parameters.extensions.value.betting_rake_fee_percentage.valid() - && !o.new_parameters.extensions.value.permitted_betting_odds_increments.valid() - && !o.new_parameters.extensions.value.live_betting_delay_time.valid(), - "Parameter extensions are not allowed yet!" ); - + dgpo = &db().get_global_properties(); if( o.new_parameters.extensions.value.min_bet_multiplier.valid() && !o.new_parameters.extensions.value.max_bet_multiplier.valid() ) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 7a46df17..55729050 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -77,6 +77,8 @@ void database::adjust_balance(account_id_type account, asset delta ) b.owner = account; b.asset_type = delta.asset_id; b.balance = delta.amount.value; + if( b.asset_type == asset_id_type() ) // CORE asset + b.maintenance_flag = true; }); } else { if( delta.amount < 0 ) @@ -208,7 +210,7 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b acct.get_id() == GRAPHENE_TEMP_ACCOUNT ) { // The blockchain's accounts do not get cashback; it simply goes to the reserve pool. - modify(get(asset_id_type()).dynamic_asset_data_id(*this), [amount](asset_dynamic_data_object& d) { + modify( get_core_dynamic_data(), [amount](asset_dynamic_data_object& d) { d.current_supply -= amount; }); return; @@ -223,10 +225,15 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b if( new_vbid.valid() ) { - modify( acct, [&]( account_object& _acct ) + modify( acct, [&new_vbid]( account_object& _acct ) { _acct.cashback_vb = *new_vbid; } ); + + modify( acct.statistics( *this ), []( account_statistics_object& aso ) + { + aso.has_cashback_vb = true; + } ); } return; diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 7625178a..eb843b8b 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -197,82 +198,90 @@ bool database::push_block(const signed_block& new_block, uint32_t skip) bool database::_push_block(const signed_block& new_block) { try { uint32_t skip = get_node_properties().skip_flags; - if( !(skip&skip_fork_db) ) + const auto now = fc::time_point::now().sec_since_epoch(); + + if( _fork_db.head() && new_block.timestamp.sec_since_epoch() > now - 86400 ) { - /// TODO: if the block is greater than the head block and before the next maitenance interval // verify that the block signer is in the current set of active witnesses. + shared_ptr prev_block = _fork_db.fetch_block( new_block.previous ); + GRAPHENE_ASSERT( prev_block, unlinkable_block_exception, "block does not link to known chain" ); + if( prev_block->scheduled_witnesses && !(skip&(skip_witness_schedule_check|skip_witness_signature)) ) + verify_signing_witness( new_block, *prev_block ); + } + shared_ptr new_head = _fork_db.push_block(new_block); - shared_ptr new_head = _fork_db.push_block(new_block); - //If the head block from the longest chain does not build off of the current head, we need to switch forks. - if( new_head->data.previous != head_block_id() ) + //If the head block from the longest chain does not build off of the current head, we need to switch forks. + if( new_head->data.previous != head_block_id() ) + { + //If the newly pushed block is the same height as head, we get head back in new_head + //Only switch forks if new_head is actually higher than head + if( new_head->data.block_num() > head_block_num() ) { - //If the newly pushed block is the same height as head, we get head back in new_head - //Only switch forks if new_head is actually higher than head - if( new_head->data.block_num() > head_block_num() ) + wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); + auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); + + // pop blocks until we hit the forked block + while( head_block_id() != branches.second.back()->data.previous ) { - wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); - auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); - - // pop blocks until we hit the forked block - while( head_block_id() != branches.second.back()->data.previous ) - { - ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); - pop_block(); - } - - // push all blocks on the new fork - for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) - { - ilog( "pushing block from fork #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); - optional except; - try { - undo_database::session session = _undo_db.start_undo_session(); - apply_block( (*ritr)->data, skip ); - _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); - session.commit(); - } - catch ( const fc::exception& e ) { except = e; } - if( except ) - { - wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); - // remove the rest of branches.first from the fork_db, those blocks are invalid - while( ritr != branches.first.rend() ) - { - ilog( "removing block from fork_db #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); - _fork_db.remove( (*ritr)->id ); - ++ritr; - } - _fork_db.set_head( branches.second.front() ); - - // pop all blocks from the bad fork - while( head_block_id() != branches.second.back()->data.previous ) - { - ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); - pop_block(); - } - - ilog( "Switching back to fork: ${id}", ("id",branches.second.front()->data.id()) ); - // restore all blocks from the good fork - for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 ) - { - ilog( "pushing block #${n} ${id}", ("n",(*ritr2)->data.block_num())("id",(*ritr2)->id) ); - auto session = _undo_db.start_undo_session(); - apply_block( (*ritr2)->data, skip ); - _block_id_to_block.store( (*ritr2)->id, (*ritr2)->data ); - session.commit(); - } - throw *except; - } - } - return true; + ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); + pop_block(); } - else return false; + + // push all blocks on the new fork + for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) + { + ilog( "pushing block from fork #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); + optional except; + try { + undo_database::session session = _undo_db.start_undo_session(); + apply_block( (*ritr)->data, skip ); + update_witnesses( **ritr ); + _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); + session.commit(); + } + catch ( const fc::exception& e ) { except = e; } + if( except ) + { + wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); + // remove the rest of branches.first from the fork_db, those blocks are invalid + while( ritr != branches.first.rend() ) + { + ilog( "removing block from fork_db #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); + _fork_db.remove( (*ritr)->id ); + ++ritr; + } + _fork_db.set_head( branches.second.front() ); + + // pop all blocks from the bad fork + while( head_block_id() != branches.second.back()->data.previous ) + { + ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); + pop_block(); + } + + ilog( "Switching back to fork: ${id}", ("id",branches.second.front()->data.id()) ); + // restore all blocks from the good fork + for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 ) + { + ilog( "pushing block #${n} ${id}", ("n",(*ritr2)->data.block_num())("id",(*ritr2)->id) ); + auto session = _undo_db.start_undo_session(); + apply_block( (*ritr2)->data, skip ); + _block_id_to_block.store( (*ritr2)->id, (*ritr2)->data ); + session.commit(); + } + throw *except; + } + } + return true; } + else return false; } try { auto session = _undo_db.start_undo_session(); apply_block(new_block, skip); + if( new_block.timestamp.sec_since_epoch() > now - 86400 ) + update_witnesses( *new_head ); _block_id_to_block.store(new_block.id(), new_block); session.commit(); } catch ( const fc::exception& e ) { @@ -284,6 +293,73 @@ bool database::_push_block(const signed_block& new_block) return false; } FC_CAPTURE_AND_RETHROW( (new_block) ) } +void database::verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const +{ + FC_ASSERT( new_block.timestamp >= fork_entry.next_block_time ); + uint32_t slot_num = ( new_block.timestamp - fork_entry.next_block_time ).to_seconds() / block_interval(); + const global_property_object& gpo = get_global_properties(); + + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) + { + uint64_t index = ( fork_entry.next_block_aslot + slot_num ) % fork_entry.scheduled_witnesses->size(); + const auto& scheduled_witness = (*fork_entry.scheduled_witnesses)[index]; + FC_ASSERT( new_block.witness == scheduled_witness.first, "Witness produced block at wrong time", + ("block witness",new_block.witness)("scheduled",scheduled_witness)("slot_num",slot_num) ); + FC_ASSERT( new_block.validate_signee( scheduled_witness.second ) ); + } + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && + slot_num != 0 ) + { + witness_id_type wid; + const witness_schedule_object& wso = get_witness_schedule_object(); + // ask the near scheduler who goes in the given slot + bool slot_is_near = wso.scheduler.get_slot(slot_num, wid); + if(! slot_is_near) + { + // if the near scheduler doesn't know, we have to extend it to + // a far scheduler. + // n.b. instantiating it is slow, but block gaps long enough to + // need it are likely pretty rare. + + witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV); + + far_future_witness_scheduler far_scheduler = + far_future_witness_scheduler(wso.scheduler, far_rng); + if(!far_scheduler.get_slot(slot_num, wid)) + { + // no scheduled witness -- somebody set up us the bomb + // n.b. this code path is impossible, the present + // implementation of far_future_witness_scheduler + // returns true unconditionally + assert( false ); + } + } + + FC_ASSERT( new_block.witness == wid, "Witness produced block at wrong time", + ("block witness",new_block.witness)("scheduled",wid)("slot_num",slot_num) ); + FC_ASSERT( new_block.validate_signee( wid(*this).signing_key ) ); + } +} + +void database::update_witnesses( fork_item& fork_entry )const +{ + if( fork_entry.scheduled_witnesses ) return; + + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + fork_entry.next_block_aslot = dpo.current_aslot + 1; + fork_entry.next_block_time = get_slot_time( 1 ); + + const witness_schedule_object& wso = get_witness_schedule_object(); + fork_entry.scheduled_witnesses = std::make_shared< vector< pair< witness_id_type, public_key_type > > >(); + fork_entry.scheduled_witnesses->reserve( wso.current_shuffled_witnesses.size() ); + + for( size_t i = 0; i < wso.current_shuffled_witnesses.size(); ++i ) + { + const auto& witness = wso.current_shuffled_witnesses[i](*this); + fork_entry.scheduled_witnesses->emplace_back( wso.current_shuffled_witnesses[i], witness.signing_key ); + } +} + /** * Attempts to push the transaction into the pending queue * @@ -324,7 +400,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx temp_session.merge(); // notify anyone listening to pending transactions - on_pending_transaction( trx ); + notify_on_pending_transaction( trx ); return processed_trx; } @@ -336,8 +412,6 @@ processed_transaction database::validate_transaction( const signed_transaction& processed_transaction database::push_proposal(const proposal_object& proposal) { try { - FC_ASSERT( _undo_db.size() < _undo_db.max_size(), "Undo database is full!" ); - transaction_evaluation_state eval_state(this); eval_state._is_proposed_trx = true; @@ -347,6 +421,8 @@ processed_transaction database::push_proposal(const proposal_object& proposal) size_t old_applied_ops_size = _applied_ops.size(); try { + 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); for( auto& op : proposal.proposed_transaction.operations ) eval_state.operation_results.emplace_back(apply_operation(eval_state, op)); @@ -593,7 +669,7 @@ void database::_apply_block( const signed_block& next_block ) const witness_object& signing_witness = validate_block_header(skip, next_block); const auto& global_props = get_global_properties(); - const auto& dynamic_global_props = get(dynamic_global_property_id_type()); + const auto& dynamic_global_props = get_dynamic_global_properties(); bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp); _current_block_num = next_block_num; @@ -601,6 +677,8 @@ void database::_apply_block( const signed_block& next_block ) _current_op_in_trx = 0; _current_virtual_op = 0; + _issue_453_affected_assets.clear(); + for( const auto& trx : next_block.transactions ) { /* We do not need to push the undo state for each transaction @@ -644,7 +722,8 @@ void database::_apply_block( const signed_block& next_block ) clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); - update_expired_feeds(); + 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(); update_tournaments(); update_betting_markets(next_block.timestamp); @@ -666,7 +745,7 @@ void database::_apply_block( const signed_block& next_block ) apply_debug_updates(); // notify observers that the block has been applied - applied_block( next_block ); //emit + notify_applied_block( next_block ); //emit _applied_ops.clear(); notify_changed_objects(); diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp index 0fa5eb58..27beb3ed 100644 --- a/libraries/chain/db_debug.cpp +++ b/libraries/chain/db_debug.cpp @@ -42,7 +42,7 @@ void database::debug_dump() const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); const auto& balance_index = db.get_index_type().indices(); - const simple_index& statistics_index = db.get_index_type>(); + const auto& statistics_index = db.get_index_type().indices(); map total_balances; map total_debts; share_type core_in_orders; diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 72e0327f..30fd776f 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -38,22 +38,27 @@ namespace graphene { namespace chain { const asset_object& database::get_core_asset() const { - return get(asset_id_type()); + return *_p_core_asset_obj; +} + +const asset_dynamic_data_object& database::get_core_dynamic_data() const +{ + return *_p_core_dynamic_data_obj; } const global_property_object& database::get_global_properties()const { - return get( global_property_id_type() ); + return *_p_global_prop_obj; } const chain_property_object& database::get_chain_properties()const { - return get( chain_property_id_type() ); + return *_p_chain_property_obj; } const dynamic_global_property_object& database::get_dynamic_global_properties() const { - return get( dynamic_global_property_id_type() ); + return *_p_dyn_global_prop_obj; } const fee_schedule& database::current_fee_schedule()const @@ -63,17 +68,17 @@ const fee_schedule& database::current_fee_schedule()const time_point_sec database::head_block_time()const { - return get( dynamic_global_property_id_type() ).time; + return get_dynamic_global_properties().time; } uint32_t database::head_block_num()const { - return get( dynamic_global_property_id_type() ).head_block_number; + return get_dynamic_global_properties().head_block_number; } block_id_type database::head_block_id()const { - return get( dynamic_global_property_id_type() ).head_block_id; + return get_dynamic_global_properties().head_block_id; } decltype( chain_parameters::block_interval ) database::block_interval( )const @@ -245,6 +250,17 @@ bool database::is_son_dereg_valid( son_id_type son_id ) bool ret = ( son->status == son_status::in_maintenance && (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); return ret; +const account_statistics_object& database::get_account_stats_by_owner( account_id_type owner )const +{ + auto& idx = get_index_type().indices().get(); + auto itr = idx.find( owner ); + FC_ASSERT( itr != idx.end(), "Can not find account statistics object for owner ${a}", ("a",owner) ); + return *itr; +} + +const witness_schedule_object& database::get_witness_schedule_object()const +{ + return *_p_witness_schedule_obj; } } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 833e03e4..09e45276 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -333,7 +333,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index> >(); add_index< primary_index> >(); - add_index< primary_index> >(); + add_index< primary_index >(); add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index > >(); @@ -395,12 +395,19 @@ void database::init_genesis(const genesis_state_type& genesis_state) n.owner.weight_threshold = 1; n.active.weight_threshold = 1; n.name = "committee-account"; - n.statistics = create( [&](account_statistics_object& s){ s.owner = n.id; }).id; + n.statistics = create( [&n](account_statistics_object& s){ + s.owner = n.id; + s.name = n.name; + s.core_in_balance = GRAPHENE_MAX_SHARE_SUPPLY; + }).id; }); FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "witness-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT; @@ -410,7 +417,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_WITNESS_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "relaxed-committee-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; @@ -420,7 +430,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "null-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -430,7 +443,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_NULL_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "temp-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 0; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT; @@ -440,7 +456,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_TEMP_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "proxy-to-self"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -450,7 +469,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "default-dividend-distribution"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_PROXY_TO_SELF_ACCOUNT; @@ -474,9 +496,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) uint64_t id = get_index().get_next_id().instance(); if( id >= genesis_state.immutable_parameters.num_special_accounts ) break; - const account_object& acct = create([&](account_object& a) { + const account_object& acct = create([this,id](account_object& a) { a.name = "special-account-" + std::to_string(id); - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id); @@ -490,12 +515,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Create core asset const asset_dynamic_data_object& dyn_asset = - create([&](asset_dynamic_data_object& a) { + create([](asset_dynamic_data_object& a) { a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY; }); const asset_dividend_data_object& div_asset = - create([&](asset_dividend_data_object& a) { + create([&genesis_state](asset_dividend_data_object& a) { a.options.minimum_distribution_interval = 3*24*60*60; a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT; a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1); @@ -504,7 +529,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); const asset_object& core_asset = - create( [&]( asset_object& a ) { + create( [&genesis_state,&div_asset,&dyn_asset]( asset_object& a ) { a.symbol = GRAPHENE_SYMBOL; a.options.max_supply = genesis_state.max_core_supply; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -517,9 +542,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; a.dividend_data_id = div_asset.id; - }); - assert( asset_id_type(core_asset.id) == asset().asset_id ); - assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); + }); + FC_ASSERT( dyn_asset.id == asset_dynamic_data_id_type() ); + FC_ASSERT( asset_id_type(core_asset.id) == asset().asset_id ); + FC_ASSERT( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); + _p_core_asset_obj = &core_asset; + _p_core_dynamic_data_obj = &dyn_asset; #ifdef _DEFAULT_DIVIDEND_ASSET // Create default dividend asset @@ -552,7 +580,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.dynamic_asset_data_id = dyn_asset1.id; a.dividend_data_id = div_asset1.id; }); - assert( default_asset.id == asset_id_type(1) ); + FC_ASSERT( default_asset.id == asset_id_type(1) ); #endif // Create more special assets @@ -562,10 +590,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) if( id >= genesis_state.immutable_parameters.num_special_assets ) break; const asset_dynamic_data_object& dyn_asset = - create([&](asset_dynamic_data_object& a) { + create([](asset_dynamic_data_object& a) { a.current_supply = 0; }); - const asset_object& asset_obj = create( [&]( asset_object& a ) { + const asset_object& asset_obj = create( [id,&dyn_asset]( asset_object& a ) { a.symbol = "SPECIAL" + std::to_string( id ); a.options.max_supply = 0; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -585,14 +613,14 @@ void database::init_genesis(const genesis_state_type& genesis_state) chain_id_type chain_id = genesis_state.compute_chain_id(); // Create global properties - create([&](global_property_object& p) { + _p_global_prop_obj = & create([&genesis_state](global_property_object& p) { p.parameters = genesis_state.initial_parameters; // Set fees to zero initially, so that genesis initialization needs not pay them // We'll fix it at the end of the function p.parameters.current_fees->zero_all_fees(); }); - create([&](dynamic_global_property_object& p) { + _p_dyn_global_prop_obj = & create([&genesis_state](dynamic_global_property_object& p) { p.time = genesis_state.initial_timestamp; p.dynamic_flags = 0; p.witness_budget = 0; @@ -605,7 +633,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" ); FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" ); - create([&](chain_property_object& p) + _p_chain_property_obj = & create([chain_id,&genesis_state](chain_property_object& p) { p.chain_id = chain_id; p.immutable_parameters = genesis_state.immutable_parameters; @@ -729,7 +757,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) cop.active = cop.owner; account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get(); - modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) { + modify( owner_account_id(*this).statistics(*this), [&collateral_rec]( account_statistics_object& o ) { o.total_core_in_orders = collateral_rec.collateral; }); @@ -945,7 +973,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); // Set active witnesses - modify(get_global_properties(), [&](global_property_object& p) { + modify(get_global_properties(), [&genesis_state](global_property_object& p) { for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { p.active_witnesses.insert(witness_id_type(i)); @@ -953,10 +981,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); // Initialize witness schedule -#ifndef NDEBUG - const witness_schedule_object& wso = -#endif - create([&](witness_schedule_object& _wso) + _p_witness_schedule_obj = & create([this](witness_schedule_object& _wso) { // for scheduled memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size()); @@ -980,7 +1005,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) for( const witness_id_type& wid : get_global_properties().active_witnesses ) _wso.current_shuffled_witnesses.push_back( wid ); }); - assert( wso.id == witness_schedule_id_type() ); + FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); // Initialize witness schedule #ifndef NDEBUG @@ -1010,12 +1035,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) p.parameters.current_fees = genesis_state.initial_parameters.current_fees; }); - // Create witness scheduler - //create([&]( witness_schedule_object& wso ) - //{ - // for( const witness_id_type& wid : get_global_properties().active_witnesses ) - // wso.current_shuffled_witnesses.push_back( wid ); - //}); // Create FBA counters create([&]( fba_accumulator_object& acc ) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 3c1685b3..7207545a 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -77,12 +77,44 @@ vector> database::sort return refs; } -template -void database::perform_account_maintenance(std::tuple helpers) +template +void database::perform_account_maintenance(Type tally_helper) { - const auto& idx = get_index_type().indices().get(); - for( const account_object& a : idx ) - detail::for_each(helpers, a, detail::gen_seq()); + const auto& bal_idx = get_index_type< account_balance_index >().indices().get< by_maintenance_flag >(); + if( bal_idx.begin() != bal_idx.end() ) + { + auto bal_itr = bal_idx.rbegin(); + while( bal_itr->maintenance_flag ) + { + const account_balance_object& bal_obj = *bal_itr; + + modify( get_account_stats_by_owner( bal_obj.owner ), [&bal_obj](account_statistics_object& aso) { + aso.core_in_balance = bal_obj.balance; + }); + + modify( bal_obj, []( account_balance_object& abo ) { + abo.maintenance_flag = false; + }); + + bal_itr = bal_idx.rbegin(); + } + } + + const auto& stats_idx = get_index_type< account_stats_index >().indices().get< by_maintenance_seq >(); + auto stats_itr = stats_idx.lower_bound( true ); + + while( stats_itr != stats_idx.end() ) + { + const account_statistics_object& acc_stat = *stats_itr; + const account_object& acc_obj = acc_stat.owner( *this ); + ++stats_itr; + + if( acc_stat.has_some_core_voting() ) + tally_helper( acc_obj, acc_stat ); + + if( acc_stat.has_pending_fees() ) + acc_stat.process_fees( acc_obj, *this ); + } } /// @brief A visitor for @ref worker_type which calls pay_worker on the worker within @@ -181,12 +213,13 @@ void database::update_son_metrics() void database::pay_workers( share_type& budget ) { + const auto head_time = head_block_time(); // ilog("Processing payroll! Available budget is ${b}", ("b", budget)); vector> active_workers; - get_index_type().inspect_all_objects([this, &active_workers](const object& o) { + // TODO optimization: add by_expiration index to avoid iterating through all objects + get_index_type().inspect_all_objects([head_time, &active_workers](const object& o) { const worker_object& w = static_cast(o); - auto now = head_block_time(); - if( w.is_active(now) && w.approving_stake() > 0 ) + if( w.is_active(head_time) && w.approving_stake() > 0 ) active_workers.emplace_back(w); }); @@ -200,17 +233,22 @@ void database::pay_workers( share_type& budget ) return wa.id < wb.id; }); + const auto last_budget_time = get_dynamic_global_properties().last_budget_time; + const auto passed_time_ms = head_time - last_budget_time; + const auto passed_time_count = passed_time_ms.count(); + const auto day_count = fc::days(1).count(); for( uint32_t i = 0; i < active_workers.size() && budget > 0; ++i ) { const worker_object& active_worker = active_workers[i]; share_type requested_pay = active_worker.daily_pay; - if( head_block_time() - get_dynamic_global_properties().last_budget_time != fc::days(1) ) - { - fc::uint128 pay(requested_pay.value); - pay *= (head_block_time() - get_dynamic_global_properties().last_budget_time).count(); - pay /= fc::days(1).count(); - requested_pay = pay.to_uint64(); - } + + // Note: if there is a good chance that passed_time_count == day_count, + // for better performance, can avoid the 128 bit calculation by adding a check. + // Since it's not the case on BitShares mainnet, we're not using a check here. + fc::uint128 pay(requested_pay.value); + pay *= passed_time_count; + pay /= day_count; + requested_pay = pay.to_uint64(); share_type actual_pay = std::min(budget, requested_pay); //ilog(" ==> Paying ${a} to worker ${w}", ("w", active_worker.id)("a", actual_pay)); @@ -247,13 +285,27 @@ void database::update_active_witnesses() const global_property_object& gpo = get_global_properties(); - const auto& all_witnesses = get_index_type().indices(); + auto update_witness_total_votes = [this]( const witness_object& wit ) { + modify( wit, [this]( witness_object& obj ) + { + obj.total_votes = _vote_tally_buffer[obj.vote_id]; + }); + }; - for( const witness_object& wit : all_witnesses ) + if( _track_standby_votes ) { - modify( wit, [&]( witness_object& obj ){ - obj.total_votes = _vote_tally_buffer[wit.vote_id]; - }); + const auto& all_witnesses = get_index_type().indices(); + for( const witness_object& wit : all_witnesses ) + { + update_witness_total_votes( wit ); + } + } + else + { + for( const witness_object& wit : wits ) + { + update_witness_total_votes( wit ); + } } // Update witness authority @@ -329,13 +381,29 @@ void database::update_active_committee_members() const chain_property_object& cpo = get_chain_properties(); auto committee_members = sort_votable_objects(std::max(committee_member_count*2+1, (size_t)cpo.immutable_parameters.min_committee_member_count)); - for( const committee_member_object& del : committee_members ) - { - modify( del, [&]( committee_member_object& obj ){ - obj.total_votes = _vote_tally_buffer[del.vote_id]; - }); - } + auto update_committee_member_total_votes = [this]( const committee_member_object& cm ) { + modify( cm, [this]( committee_member_object& obj ) + { + obj.total_votes = _vote_tally_buffer[obj.vote_id]; + }); + }; + if( _track_standby_votes ) + { + const auto& all_committee_members = get_index_type().indices(); + for( const committee_member_object& cm : all_committee_members ) + { + update_committee_member_total_votes( cm ); + } + } + else + { + for( const committee_member_object& cm : committee_members ) + { + update_committee_member_total_votes( cm ); + } + } + // Update committee authorities if( !committee_members.empty() ) { @@ -607,8 +675,8 @@ void database::update_active_sons() void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const asset_object& core = asset_id_type(0)(*this); - const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(*this); + const asset_object& core = get_core_asset(); + const asset_dynamic_data_object& core_dd = get_core_dynamic_data(); rec.from_initial_reserve = core.reserved(*this); rec.from_accumulated_fees = core_dd.accumulated_fees; @@ -661,8 +729,7 @@ void database::process_budget() { const global_property_object& gpo = get_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const asset_dynamic_data_object& core = - asset_id_type(0)(*this).dynamic_asset_data_id(*this); + const asset_dynamic_data_object& core = get_core_dynamic_data(); fc::time_point_sec now = head_block_time(); int64_t time_to_maint = (dpo.next_maintenance_time - now).to_seconds(); @@ -842,8 +909,7 @@ void split_fba_balance( if( fba.accumulated_fba_fees == 0 ) return; - const asset_object& core = asset_id_type(0)(db); - const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(db); + const asset_dynamic_data_object& core_dd = db.get_core_dynamic_data(); if( !fba.is_configured(db) ) { @@ -1017,6 +1083,154 @@ void deprecate_annual_members( database& db ) return; } +uint32_t database::get_gpos_current_subperiod() +{ + if(this->head_block_time() < HARDFORK_GPOS_TIME) //Can be deleted after GPOS hardfork time + return 0; + + fc::time_point_sec last_date_voted; + + const auto &gpo = this->get_global_properties(); + const auto vesting_period = gpo.parameters.gpos_period(); + const auto vesting_subperiod = gpo.parameters.gpos_subperiod(); + const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start()); + + // variables needed + const fc::time_point_sec period_end = period_start + vesting_period; + const auto number_of_subperiods = vesting_period / vesting_subperiod; + const auto now = this->head_block_time(); + auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch(); + + FC_ASSERT(period_start <= now && now <= period_end); + + // get in what sub period we are + uint32_t current_subperiod = 0; + std::list period_list(number_of_subperiods); + std::iota(period_list.begin(), period_list.end(), 1); + + std::for_each(period_list.begin(), period_list.end(),[&](uint32_t period) { + if(seconds_since_period_start >= vesting_subperiod * (period - 1) && + seconds_since_period_start < vesting_subperiod * period) + current_subperiod = period; + }); + + return current_subperiod; +} + +double database::calculate_vesting_factor(const account_object& stake_account) +{ + fc::time_point_sec last_date_voted; + // get last time voted form account stats + // check last_vote_time of proxy voting account if proxy is set + if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT) + last_date_voted = stake_account.statistics(*this).last_vote_time; + else + last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time; + + // get global data related to gpos + const auto &gpo = this->get_global_properties(); + const auto vesting_period = gpo.parameters.gpos_period(); + const auto vesting_subperiod = gpo.parameters.gpos_subperiod(); + const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start()); + + // variables needed + const auto number_of_subperiods = vesting_period / vesting_subperiod; + double vesting_factor; + + // get in what sub period we are + uint32_t current_subperiod = get_gpos_current_subperiod(); + + if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0; + + // On starting new vesting period, all votes become zero until someone votes, To avoid a situation of zero votes, + // changes were done to roll in GPOS rules, the vesting factor will be 1 for whoever votes in 6th sub-period of last vesting period + // BLOCKBACK-174 fix + if(current_subperiod == 1 && this->head_block_time() >= HARDFORK_GPOS_TIME + vesting_period) //Applicable only from 2nd vesting period + { + if(last_date_voted > period_start - vesting_subperiod) + return 1; + } + if(last_date_voted < period_start) return 0; + + double numerator = number_of_subperiods; + + if(current_subperiod > 1) { + std::list subperiod_list(current_subperiod - 1); + std::iota(subperiod_list.begin(), subperiod_list.end(), 2); + subperiod_list.reverse(); + + for(auto subperiod: subperiod_list) + { + numerator--; + + auto last_period_start = period_start + fc::seconds(vesting_subperiod * (subperiod - 1)); + auto last_period_end = period_start + fc::seconds(vesting_subperiod * (subperiod)); + + if (last_date_voted > last_period_start && last_date_voted <= last_period_end) { + numerator++; + break; + } + } + } + vesting_factor = numerator / number_of_subperiods; + return vesting_factor; +} + +share_type credit_account(database& db, const account_id_type owner_id, const std::string owner_name, + share_type remaining_amount_to_distribute, + const share_type shares_to_credit, const asset_id_type payout_asset_type, + const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index, + const asset_id_type dividend_id) { + + //wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); + if (shares_to_credit.value) { + + remaining_amount_to_distribute -= shares_to_credit; + + dlog("Crediting account ${account} with ${amount}", + ("account", owner_name) + ("amount", asset(shares_to_credit, payout_asset_type))); + auto pending_payout_iter = + pending_payout_balance_index.indices().get().find( + boost::make_tuple(dividend_id, payout_asset_type, + owner_id)); + if (pending_payout_iter == + pending_payout_balance_index.indices().get().end()) + db.create( + [&](pending_dividend_payout_balance_for_holder_object &obj) { + obj.owner = owner_id; + obj.dividend_holder_asset_type = dividend_id; + obj.dividend_payout_asset_type = payout_asset_type; + obj.pending_balance = shares_to_credit; + }); + else + db.modify(*pending_payout_iter, + [&](pending_dividend_payout_balance_for_holder_object &pending_balance) { + pending_balance.pending_balance += shares_to_credit; + }); + } + return remaining_amount_to_distribute; +} + +void rolling_period_start(database& db) +{ + if(db.head_block_time() >= HARDFORK_GPOS_TIME) + { + auto gpo = db.get_global_properties(); + auto period_start = db.get_global_properties().parameters.gpos_period_start(); + auto vesting_period = db.get_global_properties().parameters.gpos_period(); + + auto now = db.head_block_time(); + if(now.sec_since_epoch() >= (period_start + vesting_period)) + { + // roll + db.modify(db.get_global_properties(), [now](global_property_object& p) { + p.parameters.extensions.value.gpos_period_start = now.sec_since_epoch(); + }); + } + } +} + // Schedules payouts from a dividend distribution account to the current holders of the // dividend-paying asset. This takes any deposits made to the dividend distribution account // since the last time it was called, and distributes them to the current owners of the @@ -1048,34 +1262,42 @@ void schedule_pending_dividend_balances(database& db, balance_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); auto holder_balances_end = balance_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); - uint32_t holder_account_count = std::distance(holder_balances_begin, holder_balances_end); uint64_t distribution_base_fee = gpo.parameters.current_fees->get().distribution_base_fee; uint32_t distribution_fee_per_holder = gpo.parameters.current_fees->get().distribution_fee_per_holder; - // the fee, in BTS, for distributing each asset in the account - uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; std::map vesting_amounts; + + auto balance_type = vesting_balance_type::normal; + if(db.head_block_time() >= HARDFORK_GPOS_TIME) + balance_type = vesting_balance_type::gpos; + + uint32_t holder_account_count = 0; + #ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset auto vesting_balances_begin = - vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); + vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type)); auto vesting_balances_end = - vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); + vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type, share_type())); + for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - //dlog("Vesting balance for account: ${owner}, amount: ${amount}", - // ("owner", vesting_balance_obj.owner(db).name) - // ("amount", vesting_balance_obj.balance.amount)); + ++holder_account_count; + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(db).name) + ("amount", vesting_balance_obj.balance.amount)); } #else // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset const auto& vesting_balances = vesting_index.indices().get(); for (const vesting_balance_object& vesting_balance_obj : vesting_balances) { - if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount) + if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount && + vesting_balance_object.balance_type == balance_type) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; + ++gpos_holder_account_count; dlog("Vesting balance for account: ${owner}, amount: ${amount}", ("owner", vesting_balance_obj.owner(db).name) ("amount", vesting_balance_obj.balance.amount)); @@ -1084,6 +1306,12 @@ void schedule_pending_dividend_balances(database& db, #endif auto current_distribution_account_balance_iter = current_distribution_account_balance_range.begin(); + if(db.head_block_time() < HARDFORK_GPOS_TIME) + holder_account_count = std::distance(holder_balances_begin, holder_balances_end); + // the fee, in BTS, for distributing each asset in the account + uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; + + //auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first; auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first; dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}", ("current", (int64_t)std::distance(current_distribution_account_balance_range.begin(), current_distribution_account_balance_range.end())) @@ -1093,14 +1321,23 @@ void schedule_pending_dividend_balances(database& db, // accounts other than the distribution account (it would be silly to distribute dividends back to // the distribution account) share_type total_balance_of_dividend_asset; - for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) - if (holder_balance_object.owner != dividend_data.dividend_distribution_account) - { - total_balance_of_dividend_asset += holder_balance_object.balance; - auto itr = vesting_amounts.find(holder_balance_object.owner); - if (itr != vesting_amounts.end()) - total_balance_of_dividend_asset += itr->second; - } + if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // only core + for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range(vesting_balances_begin, + vesting_balances_end)) + if (holder_balance_object.owner != dividend_data.dividend_distribution_account) { + total_balance_of_dividend_asset += holder_balance_object.balance.amount; + } + } + else { + for (const account_balance_object &holder_balance_object : boost::make_iterator_range(holder_balances_begin, + holder_balances_end)) + if (holder_balance_object.owner != dividend_data.dividend_distribution_account) { + total_balance_of_dividend_asset += holder_balance_object.balance; + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + total_balance_of_dividend_asset += itr->second; + } + } // loop through all of the assets currently or previously held in the distribution account while (current_distribution_account_balance_iter != current_distribution_account_balance_range.end() || previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second) @@ -1224,46 +1461,68 @@ void schedule_pending_dividend_balances(database& db, ("total", total_balance_of_dividend_asset)); share_type remaining_amount_to_distribute = delta_balance; - // credit each account with their portion, don't send any back to the dividend distribution account - for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) - { - if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; + if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // core only + // credit each account with their portion, don't send any back to the dividend distribution account + for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range( + vesting_balances_begin, vesting_balances_end)) { + if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; - auto holder_balance = holder_balance_object.balance; + auto vesting_factor = db.calculate_vesting_factor(holder_balance_object.owner(db)); - auto itr = vesting_amounts.find(holder_balance_object.owner); - if (itr != vesting_amounts.end()) - holder_balance += itr->second; + auto holder_balance = holder_balance_object.balance; - fc::uint128_t amount_to_credit(delta_balance.value); - amount_to_credit *= holder_balance.value; - amount_to_credit /= total_balance_of_dividend_asset.value; - share_type shares_to_credit((int64_t)amount_to_credit.to_uint64()); - if (shares_to_credit.value) - { - wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); + fc::uint128_t amount_to_credit(delta_balance.value); + amount_to_credit *= holder_balance.amount.value; + amount_to_credit /= total_balance_of_dividend_asset.value; + share_type full_shares_to_credit((int64_t) amount_to_credit.to_uint64()); + share_type shares_to_credit = (uint64_t) floor(full_shares_to_credit.value * vesting_factor); - remaining_amount_to_distribute -= shares_to_credit; + if (shares_to_credit < full_shares_to_credit) { + // Todo: sending results of decay to committee account, need to change to specified account + dlog("Crediting committee_account with ${amount}", + ("amount", asset(full_shares_to_credit - shares_to_credit, payout_asset_type))); + db.adjust_balance(dividend_data.dividend_distribution_account, + -asset(full_shares_to_credit - shares_to_credit, payout_asset_type)); + db.adjust_balance(account_id_type(0), asset(full_shares_to_credit - shares_to_credit, payout_asset_type)); + } - dlog("Crediting account ${account} with ${amount}", - ("account", holder_balance_object.owner(db).name) - ("amount", asset(shares_to_credit, payout_asset_type))); - auto pending_payout_iter = - pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type, holder_balance_object.owner)); - if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) - db.create( [&]( pending_dividend_payout_balance_for_holder_object& obj ){ - obj.owner = holder_balance_object.owner; - obj.dividend_holder_asset_type = dividend_holder_asset_obj.id; - obj.dividend_payout_asset_type = payout_asset_type; - obj.pending_balance = shares_to_credit; - }); - else - db.modify(*pending_payout_iter, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){ - pending_balance.pending_balance += shares_to_credit; - }); + remaining_amount_to_distribute = credit_account(db, + holder_balance_object.owner, + holder_balance_object.owner(db).name, + remaining_amount_to_distribute, + shares_to_credit, + payout_asset_type, + pending_payout_balance_index, + dividend_holder_asset_obj.id); } } + else { + // credit each account with their portion, don't send any back to the dividend distribution account + for (const account_balance_object &holder_balance_object : boost::make_iterator_range( + holder_balances_begin, holder_balances_end)) { + if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; + auto holder_balance = holder_balance_object.balance; + + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + holder_balance += itr->second; + + fc::uint128_t amount_to_credit(delta_balance.value); + amount_to_credit *= holder_balance.value; + amount_to_credit /= total_balance_of_dividend_asset.value; + share_type shares_to_credit((int64_t) amount_to_credit.to_uint64()); + + remaining_amount_to_distribute = credit_account(db, + holder_balance_object.owner, + holder_balance_object.owner(db).name, + remaining_amount_to_distribute, + shares_to_credit, + payout_asset_type, + pending_payout_balance_index, + dividend_holder_asset_obj.id); + } + } for (const auto& pending_payout : pending_payout_balance_index.indices()) if (pending_payout.pending_balance.value) dlog("Pending payout: ${account_name} -> ${amount}", @@ -1526,6 +1785,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g process_dividend_assets(*this); + rolling_period_start(*this); + struct vote_tally_helper { database& d; const global_property_object& props; @@ -1540,24 +1801,28 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1); d._total_voting_stake = 0; + auto balance_type = vesting_balance_type::normal; + if(d.head_block_time() >= HARDFORK_GPOS_TIME) + balance_type = vesting_balance_type::gpos; + const vesting_balance_index& vesting_index = d.get_index_type(); #ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX auto vesting_balances_begin = - vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type())); + vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type)); auto vesting_balances_end = - vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), share_type())); + vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type())); for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - //dlog("Vesting balance for account: ${owner}, amount: ${amount}", - // ("owner", vesting_balance_obj.owner(d).name) - // ("amount", vesting_balance_obj.balance.amount)); + dlog("Vesting balance for account: ${owner}, amount: ${amount}", + ("owner", vesting_balance_obj.owner(d).name) + ("amount", vesting_balance_obj.balance.amount)); } #else const auto& vesting_balances = vesting_index.indices().get(); for (const vesting_balance_object& vesting_balance_obj : vesting_balances) { - if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount) + if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount && vesting_balance_obj.balance_type == balance_type) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; dlog("Vesting balance for account: ${owner}, amount: ${amount}", @@ -1568,7 +1833,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g #endif } - void operator()(const account_object& stake_account) { + void operator()( const account_object& stake_account, const account_statistics_object& stats ) + { if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) ) { // There may be a difference between the account whose stake is voting and the one specifying opinions. @@ -1585,13 +1851,35 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g const account_object& opinion_account = *opinion_account_ptr; const auto& stats = stake_account.statistics(d); - uint64_t voting_stake = stats.total_core_in_orders.value - + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) - + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; + uint64_t voting_stake = 0; auto itr = vesting_amounts.find(stake_account.id); if (itr != vesting_amounts.end()) voting_stake += itr->second.value; + + if(d.head_block_time() >= HARDFORK_GPOS_TIME) + { + if (itr == vesting_amounts.end() && d.head_block_time() >= (HARDFORK_GPOS_TIME + props.parameters.gpos_subperiod()/2)) + return; + + auto vesting_factor = d.calculate_vesting_factor(stake_account); + voting_stake = (uint64_t)floor(voting_stake * vesting_factor); + + //Include votes(based on stake) for the period of gpos_subperiod()/2 as system has zero votes on GPOS activation + if(d.head_block_time() < (HARDFORK_GPOS_TIME + props.parameters.gpos_subperiod()/2)) + { + voting_stake += stats.total_core_in_orders.value + + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value : 0) + + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; + } + } + else + { + voting_stake += stats.total_core_in_orders.value + + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value : 0) + + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; + } + for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); @@ -1639,23 +1927,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } } tally_helper(*this, gpo); - struct process_fees_helper { - database& d; - const global_property_object& props; - - process_fees_helper(database& d, const global_property_object& gpo) - : d(d), props(gpo) {} - - void operator()(const account_object& a) { - a.statistics(d).process_fees(a, d); - } - } fee_helper(*this, gpo); - - perform_account_maintenance(std::tie( - tally_helper, - fee_helper - )); - + + perform_account_maintenance( tally_helper ); struct clear_canary { clear_canary(vector& target): target(target){} ~clear_canary() { target.clear(); } @@ -1673,9 +1946,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g update_active_sons(); update_worker_votes(); - modify(gpo, [this](global_property_object& p) { + const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); + + modify(gpo, [&dgpo](global_property_object& p) { // Remove scaling of account registration fee - const auto& dgpo = get_dynamic_global_properties(); p.parameters.current_fees->get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale); @@ -1691,12 +1965,20 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments; if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() ) p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time; + if( !p.pending_parameters->extensions.value.gpos_period_start.valid() ) + p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; + if( !p.pending_parameters->extensions.value.gpos_period.valid() ) + p.pending_parameters->extensions.value.gpos_period = p.parameters.extensions.value.gpos_period; + if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() ) + p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod; + if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() ) + p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); } }); - auto next_maintenance_time = get(dynamic_global_property_id_type()).next_maintenance_time; + auto next_maintenance_time = dgpo.next_maintenance_time; auto maintenance_interval = gpo.parameters.maintenance_interval; if( next_maintenance_time <= next_block.timestamp ) @@ -1726,8 +2008,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } - const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); - if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) ) deprecate_annual_members(*this); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index f6d164d2..9560aae3 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -24,6 +24,9 @@ #include +#include +#include +#include #include #include @@ -177,7 +180,7 @@ void database::wipe(const fc::path& data_dir, bool include_blocks) { ilog("Wiping database", ("include_blocks", include_blocks)); if (_opened) { - close(); + close(false); } object_database::wipe(data_dir); if( include_blocks ) @@ -215,6 +218,15 @@ void database::open( if( !find(global_property_id_type()) ) init_genesis(genesis_loader()); + else + { + _p_core_asset_obj = &get( asset_id_type() ); + _p_core_dynamic_data_obj = &get( asset_dynamic_data_id_type() ); + _p_global_prop_obj = &get( global_property_id_type() ); + _p_chain_property_obj = &get( chain_property_id_type() ); + _p_dyn_global_prop_obj = &get( dynamic_global_property_id_type() ); + _p_witness_schedule_obj = &get( witness_schedule_id_type() ); + } fc::optional last_block = _block_id_to_block.last_id(); if( last_block.valid() ) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 59f77762..ad888532 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -426,14 +426,16 @@ bool database::fill_order(const force_settlement_object& settle, const asset& pa * * @return true if a margin call was executed. */ -bool database::check_call_orders(const asset_object& mia, bool enable_black_swan) +bool database::check_call_orders( const asset_object& mia, bool enable_black_swan, bool for_new_limit_order, + const asset_bitasset_data_object* bitasset_ptr ) { try { if( !mia.is_market_issued() ) return false; - if( check_for_blackswan( mia, enable_black_swan ) ) + const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); + + if( check_for_blackswan( mia, enable_black_swan, &bitasset ) ) return false; - const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); if( bitasset.is_prediction_market ) return false; if( bitasset.current_feed.settlement_price.is_null() ) return false; @@ -464,7 +466,12 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan bool filled_limit = false; bool margin_called = false; - while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end ) + auto head_time = head_block_time(); + auto head_num = head_block_num(); + + bool after_hardfork_436 = ( head_time > HARDFORK_436_TIME ); + + while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) && call_itr != call_end ) { bool filled_call = false; price match_price; @@ -481,7 +488,7 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan // would be margin called, but there is no matching order #436 bool feed_protected = ( bitasset.current_feed.settlement_price > ~call_itr->call_price ); - if( feed_protected && (head_block_time() > HARDFORK_436_TIME) ) + if( feed_protected && after_hardfork_436 ) return margin_called; // would be margin called, but there is no matching order @@ -506,7 +513,8 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan if( usd_to_buy * match_price > call_itr->get_collateral() ) { - elog( "black swan detected" ); + elog( "black swan detected on asset ${symbol} (${id}) at block ${b}", + ("id",mia.id)("symbol",mia.symbol)("b",head_num) ); edump((enable_black_swan)); FC_ASSERT( enable_black_swan ); globally_settle_asset(mia, bitasset.current_feed.settlement_price ); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 6cf7c7b0..a762fe2c 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -33,6 +33,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + using namespace fc; using namespace graphene::chain; @@ -341,13 +349,13 @@ struct get_impacted_account_visitor } }; -void operation_get_impacted_accounts( const operation& op, flat_set& result ) +void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) { get_impacted_account_visitor vtor = get_impacted_account_visitor( result ); op.visit( vtor ); } -void transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) +void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) { for( const auto& op : tx.operations ) operation_get_impacted_accounts( op, result ); @@ -503,6 +511,16 @@ void get_relevant_accounts( const object* obj, flat_set& accoun namespace graphene { namespace chain { +void database::notify_applied_block( const signed_block& block ) +{ + GRAPHENE_TRY_NOTIFY( applied_block, block ) +} + +void database::notify_on_pending_transaction( const signed_transaction& tx ) +{ + GRAPHENE_TRY_NOTIFY( on_pending_transaction, tx ) +} + void database::notify_changed_objects() { try { if( _undo_db.enabled() ) @@ -522,7 +540,7 @@ void database::notify_changed_objects() get_relevant_accounts(obj, new_accounts_impacted); } - new_objects(new_ids, new_accounts_impacted); + GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) } // Changed @@ -536,7 +554,7 @@ void database::notify_changed_objects() get_relevant_accounts(item.second.get(), changed_accounts_impacted); } - changed_objects(changed_ids, changed_accounts_impacted); + GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) } // Removed @@ -553,7 +571,7 @@ void database::notify_changed_objects() get_relevant_accounts(obj, removed_accounts_impacted); } - removed_objects(removed_ids, removed, removed_accounts_impacted); + GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) } } } FC_CAPTURE_AND_LOG( (0) ) } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index b33e3819..8b5957ee 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -45,7 +45,7 @@ namespace graphene { namespace chain { void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks ) { - const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this); + const dynamic_global_property_object& _dgp = get_dynamic_global_properties(); const global_property_object& gpo = get_global_properties(); // dynamic global properties updating @@ -121,6 +121,7 @@ void database::update_last_irreversible_block() const global_property_object& gpo = get_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + // TODO for better performance, move this to db_maint, because only need to do it once per maintenance interval vector< const witness_object* > wit_objs; wit_objs.reserve( gpo.active_witnesses.size() ); for( const witness_id_type& wid : gpo.active_witnesses ) @@ -238,11 +239,12 @@ void database::clear_expired_proposals() * * A black swan occurs if MAX(HB,SP) <= LC */ -bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan ) +bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan, + const asset_bitasset_data_object* bitasset_ptr ) { if( !mia.is_market_issued() ) return false; - const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); + const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); if( bitasset.has_settlement() ) return true; // already force settled auto settle_price = bitasset.current_feed.settlement_price; if( settle_price.is_null() ) return false; // no feed @@ -467,32 +469,84 @@ void database::clear_expired_orders() void database::update_expired_feeds() { - auto& asset_idx = get_index_type().indices().get(); - auto itr = asset_idx.lower_bound( true /** market issued */ ); - while( itr != asset_idx.end() ) - { - const asset_object& a = *itr; - ++itr; - assert( a.is_market_issued() ); + const auto head_time = head_block_time(); + bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME ); - const asset_bitasset_data_object& b = a.bitasset_data(*this); - bool feed_is_expired; - if( head_block_time() < HARDFORK_615_TIME ) - feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() ); - else - feed_is_expired = b.feed_is_expired( head_block_time() ); - if( feed_is_expired ) + const auto& idx = get_index_type().indices().get(); + auto itr = idx.begin(); + while( itr != idx.end() && itr->feed_is_expired( head_time ) ) + { + const asset_bitasset_data_object& b = *itr; + ++itr; // not always process begin() because old code skipped updating some assets before hf 615 + bool update_cer = false; // for better performance, to only update bitasset once, also check CER in this function + const asset_object* asset_ptr = nullptr; + // update feeds, check margin calls + if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) ) { - modify(b, [this](asset_bitasset_data_object& a) { - a.update_median_feeds(head_block_time()); + auto old_median_feed = b.current_feed; + modify( b, [head_time,&update_cer]( asset_bitasset_data_object& abdo ) + { + abdo.update_median_feeds( head_time ); + if( abdo.need_to_update_cer() ) + { + update_cer = true; + abdo.asset_cer_updated = false; + abdo.feed_cer_updated = false; + } }); - check_call_orders(b.current_feed.settlement_price.base.asset_id(*this)); + if( !b.current_feed.settlement_price.is_null() && !( b.current_feed == old_median_feed ) ) // `==` check is safe here + { + asset_ptr = &b.asset_id( *this ); + check_call_orders( *asset_ptr, true, false, &b ); + } } - if( !b.current_feed.core_exchange_rate.is_null() && - a.options.core_exchange_rate != b.current_feed.core_exchange_rate ) - modify(a, [&b](asset_object& a) { - a.options.core_exchange_rate = b.current_feed.core_exchange_rate; + // update CER + if( update_cer ) + { + if( !asset_ptr ) + asset_ptr = &b.asset_id( *this ); + if( asset_ptr->options.core_exchange_rate != b.current_feed.core_exchange_rate ) + { + modify( *asset_ptr, [&b]( asset_object& ao ) + { + ao.options.core_exchange_rate = b.current_feed.core_exchange_rate; + }); + } + } + } // for each asset whose feed is expired + + // process assets affected by bitshares-core issue 453 before hard fork 615 + if( !after_hardfork_615 ) + { + for( asset_id_type a : _issue_453_affected_assets ) + { + check_call_orders( a(*this) ); + } + } +} + +void database::update_core_exchange_rates() +{ + const auto& idx = get_index_type().indices().get(); + if( idx.begin() != idx.end() ) + { + for( auto itr = idx.rbegin(); itr->need_to_update_cer(); itr = idx.rbegin() ) + { + const asset_bitasset_data_object& b = *itr; + const asset_object& a = b.asset_id( *this ); + if( a.options.core_exchange_rate != b.current_feed.core_exchange_rate ) + { + modify( a, [&b]( asset_object& ao ) + { + ao.options.core_exchange_rate = b.current_feed.core_exchange_rate; + }); + } + modify( b, []( asset_bitasset_data_object& abdo ) + { + abdo.asset_cer_updated = false; + abdo.feed_cer_updated = false; }); + } } } diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 31caad4b..084c8e1d 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -40,14 +40,14 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const witness_schedule_object& wso = witness_schedule_id_type()(*this); + const witness_schedule_object& wso = get_witness_schedule_object();; uint64_t current_aslot = dpo.current_aslot + slot_num; return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ]; } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && slot_num != 0 ) { - const witness_schedule_object& wso = witness_schedule_id_type()(*this); + const witness_schedule_object& wso = get_witness_schedule_object(); // ask the near scheduler who goes in the given slot bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid); if(! slot_is_near) @@ -156,7 +156,7 @@ uint32_t database::get_slot_at_time(fc::time_point_sec when)const void database::update_witness_schedule() { - const witness_schedule_object& wso = witness_schedule_id_type()(*this); + const witness_schedule_object& wso = get_witness_schedule_object(); const global_property_object& gpo = get_global_properties(); if( head_block_num() % gpo.active_witnesses.size() == 0 ) @@ -226,7 +226,7 @@ void database::update_son_schedule() vector database::get_near_witness_schedule()const { - const witness_schedule_object& wso = witness_schedule_id_type()(*this); + const witness_schedule_object& wso = get_witness_schedule_object(); vector result; result.reserve(wso.scheduler.size()); @@ -243,7 +243,7 @@ void database::update_witness_schedule(const signed_block& next_block) { auto start = fc::time_point::now(); const global_property_object& gpo = get_global_properties(); - const witness_schedule_object& wso = get(witness_schedule_id_type()); + const witness_schedule_object& wso = get_witness_schedule_object(); uint32_t schedule_needs_filled = gpo.active_witnesses.size(); uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); @@ -395,7 +395,7 @@ uint32_t database::witness_participation_rate()const } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { - const witness_schedule_object& wso = get(witness_schedule_id_type()); + const witness_schedule_object& wso = get_witness_schedule_object(); return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128; } return 0; diff --git a/libraries/chain/genesis_state.cpp b/libraries/chain/genesis_state.cpp index a278b680..53311012 100644 --- a/libraries/chain/genesis_state.cpp +++ b/libraries/chain/genesis_state.cpp @@ -36,3 +36,72 @@ chain_id_type genesis_state_type::compute_chain_id() const } } } // graphene::chain + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_account_type, BOOST_PP_SEQ_NIL, (name)(owner_key)(active_key)(is_lifetime_member)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type, BOOST_PP_SEQ_NIL, + (symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)(collateral_records)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position, BOOST_PP_SEQ_NIL, + (owner)(collateral)(debt)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_balance_type, BOOST_PP_SEQ_NIL, + (owner)(asset_symbol)(amount)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_vesting_balance_type, BOOST_PP_SEQ_NIL, + (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_witness_type, BOOST_PP_SEQ_NIL, (owner_name)(block_signing_key)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_committee_member_type, BOOST_PP_SEQ_NIL, (owner_name)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_worker_type, BOOST_PP_SEQ_NIL, (owner_name)(daily_pay)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority, BOOST_PP_SEQ_NIL, + (weight_threshold) + (account_auths) + (key_auths) + (address_auths)) +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy, BOOST_PP_SEQ_NIL, + (vesting_seconds) + (coin_seconds_earned) + (start_claim) + (coin_seconds_earned_last_update)) +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy, BOOST_PP_SEQ_NIL, + (begin_timestamp) + (vesting_cliff_seconds) + (vesting_duration_seconds) + (begin_balance)) +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance, BOOST_PP_SEQ_NIL, + (asset_symbol) + (amount) + (policy_type) + (policy)) +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type, BOOST_PP_SEQ_NIL, + (name) + (owner_authority) + (active_authority) + (core_balance) + (vesting_balances)) + +FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type, BOOST_PP_SEQ_NIL, + (initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances) + (initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates) + (initial_committee_candidates)(initial_worker_candidates) + (initial_chain_id) + (immutable_parameters)) + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_account_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_asset_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_balance_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_vesting_balance_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_witness_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_committee_member_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_worker_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type) diff --git a/libraries/chain/hardfork.d/GPOS.hf b/libraries/chain/hardfork.d/GPOS.hf new file mode 100644 index 00000000..52e95a72 --- /dev/null +++ b/libraries/chain/hardfork.d/GPOS.hf @@ -0,0 +1,4 @@ +// GPOS HARDFORK Monday, 6 January 2020 01:00:00 GMT +#ifndef HARDFORK_GPOS_TIME +#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1578272400 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 4e940326..5f24adeb 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -22,8 +22,9 @@ * THE SOFTWARE. */ #pragma once -#include +#include #include +#include #include namespace graphene { namespace chain { @@ -46,6 +47,8 @@ namespace graphene { namespace chain { account_id_type owner; + string name; ///< redundantly store account name here for better maintenance performance + /** * Keep the most recent operation as a root pointer to a linked list of the transaction history. */ @@ -62,6 +65,19 @@ namespace graphene { namespace chain { */ share_type total_core_in_orders; + share_type core_in_balance = 0; ///< redundantly store core balance here for better maintenance performance + + bool has_cashback_vb = false; ///< redundantly store this for better maintenance performance + + bool is_voting = false; ///< redundately store whether this account is voting for better maintenance performance + + + /// Whether this account owns some CORE asset and is voting + inline bool has_some_core_voting() const + { + return is_voting && ( total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb ); + } + /** * Tracks the total fees paid by this account for the purpose of calculating bulk discounts. */ @@ -87,6 +103,12 @@ namespace graphene { namespace chain { */ time_point_sec last_vote_time; + /// Whether this account has pending fees, no matter vested or not + inline bool has_pending_fees() const { return pending_fees > 0 || pending_vested_fees > 0; } + + /// Whether need to process this account during the maintenance interval + inline bool need_maintenance() const { return has_some_core_voting() || has_pending_fees(); } + /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees void process_fees(const account_object& a, database& d) const; @@ -112,6 +134,7 @@ namespace graphene { namespace chain { account_id_type owner; asset_id_type asset_type; share_type balance; + bool maintenance_flag = false; ///< Whether need to process this balance object in maintenance interval asset get_balance()const { return asset(balance, asset_type); } void adjust_balance(const asset& delta); @@ -388,6 +411,9 @@ namespace graphene { namespace chain { }; struct by_asset_balance; + struct by_maintenance_flag; + struct by_account_asset; + /** * @ingroup object_index */ @@ -395,6 +421,15 @@ namespace graphene { namespace chain { account_balance_object, indexed_by< 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, @@ -434,26 +469,6 @@ namespace graphene { namespace chain { */ typedef generic_index account_index; - struct by_owner; - struct by_maintenance_seq; - - /** - * @ingroup object_index - */ - typedef multi_index_container< - account_statistics_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_unique< tag, - member< account_statistics_object, account_id_type, &account_statistics_object::owner > > - > - > account_stats_multi_index_type; - - /** - * @ingroup object_index - */ - typedef generic_index account_stats_index; - struct by_dividend_payout_account{}; // use when calculating pending payouts struct by_dividend_account_payout{}; // use when doing actual payouts struct by_account_dividend_payout{}; // use in get_full_accounts() @@ -497,6 +512,33 @@ namespace graphene { namespace chain { */ typedef generic_index pending_dividend_payout_balance_for_holder_object_index; + struct by_owner; + struct by_maintenance_seq; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + account_statistics_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, + member< account_statistics_object, account_id_type, &account_statistics_object::owner > >, + ordered_unique< tag, + composite_key< + account_statistics_object, + const_mem_fun, + member + > + > + > + > account_stats_multi_index_type; + + /** + * @ingroup object_index + */ + typedef generic_index account_stats_index; + }} FC_REFLECT_DERIVED( graphene::chain::account_object, @@ -513,14 +555,17 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), - (owner)(asset_type)(balance) ) + (owner)(asset_type)(balance)(maintenance_flag) ) FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object), - (owner) + (owner)(name) (most_recent_op) (total_ops)(removed_ops) (total_core_in_orders) + (core_in_balance) + (has_cashback_vb) + (is_voting) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) (last_vote_time) @@ -530,4 +575,7 @@ FC_REFLECT_DERIVED( graphene::chain::pending_dividend_payout_balance_for_holder_ (graphene::db::object), (owner)(dividend_holder_asset_type)(dividend_payout_asset_type)(pending_balance) ) - +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_statistics_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::pending_dividend_payout_balance_for_holder_object ) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index f1df4681..8978a6d1 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -22,10 +22,11 @@ * THE SOFTWARE. */ #pragma once +#include +#include +#include #include #include -#include -#include /** * @defgroup prediction_market Prediction Market @@ -38,11 +39,10 @@ */ namespace graphene { namespace chain { - class account_object; class database; class transaction_evaluation_state; using namespace graphene::db; - + /** * @brief tracks the asset information that changes frequently * @ingroup object @@ -118,9 +118,9 @@ namespace graphene { namespace chain { /// Convert an asset to a textual representation with symbol, i.e. "123.45 USD" string amount_to_pretty_string(const asset &amount)const { FC_ASSERT(amount.asset_id == id); return amount_to_pretty_string(amount.amount); } - + uint32_t get_issuer_num()const - { return issuer.instance.value; } + { return issuer.instance.value; } /// Ticker symbol for this asset, i.e. "USD" string symbol; /// Maximum number of digits after the decimal point (must be <= 12) @@ -138,7 +138,7 @@ namespace graphene { namespace chain { map< account_id_type, vector< uint16_t > > distribute_winners_part( database& db ); void distribute_sweeps_holders_part( database& db ); void end_lottery( database& db ); - + /// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently. asset_dynamic_data_id_type dynamic_asset_data_id; /// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true @@ -150,7 +150,7 @@ namespace graphene { namespace chain { optional dividend_data_id; asset_id_type get_id()const { return id; } - + void validate()const { // UIAs may not be prediction markets, have force settlement, or global settlements @@ -174,7 +174,7 @@ namespace graphene { namespace chain { { return db.get(dynamic_asset_data_id); } /** - * The total amount of an asset that is reserved for future issuance. + * The total amount of an asset that is reserved for future issuance. */ template share_type reserved( const DB& db )const @@ -193,6 +193,9 @@ namespace graphene { namespace chain { static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_asset_bitasset_data_type; + /// The asset this object belong to + asset_id_type asset_id; + /// The tunable options for BitAssets are stored in this field. bitasset_options options; @@ -230,6 +233,18 @@ namespace graphene { namespace chain { share_type settlement_fund; ///@} + /// Track whether core_exchange_rate in corresponding asset_object has updated + bool asset_cer_updated = false; + + /// Track whether core exchange rate in current feed has updated + bool feed_cer_updated = false; + + /// Whether need to update core_exchange_rate in asset_object + bool need_to_update_cer() const + { + return ( ( feed_cer_updated || asset_cer_updated ) && !current_feed.core_exchange_rate.is_null() ); + } + /// The time when @ref current_feed would expire time_point_sec feed_expiration_time()const { @@ -239,7 +254,7 @@ namespace graphene { namespace chain { else return current_feed_publication_time + options.feed_lifetime_sec; } - + bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const { return feed_expiration_time() >= current_time; } bool feed_is_expired(time_point_sec current_time)const @@ -247,14 +262,34 @@ namespace graphene { namespace chain { void update_median_feeds(time_point_sec current_time); }; + // key extractor for short backing asset + struct bitasset_short_backing_asset_extractor + { + typedef asset_id_type result_type; + result_type operator() (const asset_bitasset_data_object& obj) const + { + return obj.options.short_backing_asset; + } + }; + + struct by_short_backing_asset; struct by_feed_expiration; + struct by_cer_update; + typedef multi_index_container< asset_bitasset_data_object, indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, - const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time > - > + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, bitasset_short_backing_asset_extractor >, + ordered_unique< tag, + composite_key< asset_bitasset_data_object, + const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time >, + member< asset_bitasset_data_object, asset_id_type, &asset_bitasset_data_object::asset_id > + > + >, + ordered_non_unique< tag, + const_mem_fun< asset_bitasset_data_object, bool, &asset_bitasset_data_object::need_to_update_cer > + > > > asset_bitasset_data_object_multi_index_type; //typedef flat_index asset_bitasset_data_index; @@ -343,7 +378,7 @@ namespace graphene { namespace chain { /// This field is reset any time the dividend_asset_options are updated fc::optional last_scheduled_payout_time; - /// The time payouts on this asset were last processed + /// The time payouts on this asset were last processed /// (this should be the maintenance interval at or after last_scheduled_payout_time) /// This can be displayed for the user fc::optional last_payout_time; @@ -370,7 +405,7 @@ namespace graphene { namespace chain { typedef generic_index asset_dividend_data_object_index; - // This tracks the balances in a dividend distribution account at the last time + // This tracks the balances in a dividend distribution account at the last time // pending dividend payouts were calculated (last maintenance interval). // At each maintenance interval, we will compare the current balance to the // balance stored here to see how much was deposited during that interval. @@ -399,9 +434,9 @@ namespace graphene { namespace chain { > > total_distributed_dividend_balance_object_multi_index_type; typedef generic_index total_distributed_dividend_balance_object_index; - - - + + + /** * @ingroup object */ @@ -410,17 +445,17 @@ namespace graphene { namespace chain { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_lottery_balance_object_type; - + asset_id_type lottery_id; asset balance; - + asset get_balance()const { return balance; } void adjust_balance(const asset& delta); }; - - + + struct by_owner; - + /** * @ingroup object_index */ @@ -433,13 +468,13 @@ namespace graphene { namespace chain { > > > lottery_balance_index_type; - + /** * @ingroup object_index */ typedef generic_index lottery_balance_index; - - + + class sweeps_vesting_balance_object : public abstract_object { public: @@ -451,7 +486,7 @@ namespace graphene { namespace chain { uint64_t balance; asset_id_type asset_id; time_point_sec last_claim_date; - + uint64_t get_balance()const { return balance; } void adjust_balance(const asset& delta); asset available_for_claim() const { return asset( balance / SWEEPS_VESTING_BALANCE_MULTIPLIER , asset_id ); } @@ -481,6 +516,7 @@ FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::o (current_supply)(sweeps_tickets_sold)(confidential_supply)(accumulated_fees)(fee_pool) ) FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::object), + (asset_id) (feeds) (current_feed) (current_feed_publication_time) @@ -489,11 +525,13 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db:: (is_prediction_market) (settlement_price) (settlement_fund) + (asset_cer_updated) + (feed_cer_updated) ) - + FC_REFLECT_DERIVED( graphene::chain::asset_dividend_data_object, (graphene::db::object), (options) - (last_scheduled_payout_time) + (last_scheduled_payout_time) (last_payout_time ) (last_scheduled_distribution_time) (last_distribution_time) @@ -523,3 +561,13 @@ FC_REFLECT_DERIVED( graphene::chain::lottery_balance_object, (graphene::db::obje FC_REFLECT_DERIVED( graphene::chain::sweeps_vesting_balance_object, (graphene::db::object), (owner)(balance)(asset_id)(last_claim_date) ) + + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dynamic_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_bitasset_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dividend_data_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::total_distributed_dividend_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::lottery_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::sweeps_vesting_balance_object ) + diff --git a/libraries/chain/include/graphene/chain/balance_object.hpp b/libraries/chain/include/graphene/chain/balance_object.hpp index 8d531d0c..38a1a649 100644 --- a/libraries/chain/include/graphene/chain/balance_object.hpp +++ b/libraries/chain/include/graphene/chain/balance_object.hpp @@ -73,3 +73,5 @@ namespace graphene { namespace chain { FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object), (owner)(balance)(vesting_policy)(last_claim_date) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::balance_object ) diff --git a/libraries/chain/include/graphene/chain/block_database.hpp b/libraries/chain/include/graphene/chain/block_database.hpp index d902cd1b..c5cf5df9 100644 --- a/libraries/chain/include/graphene/chain/block_database.hpp +++ b/libraries/chain/include/graphene/chain/block_database.hpp @@ -25,6 +25,8 @@ #include #include +#include + namespace graphene { namespace chain { class index_entry; diff --git a/libraries/chain/include/graphene/chain/block_summary_object.hpp b/libraries/chain/include/graphene/chain/block_summary_object.hpp index f002c030..9f79d43e 100644 --- a/libraries/chain/include/graphene/chain/block_summary_object.hpp +++ b/libraries/chain/include/graphene/chain/block_summary_object.hpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #pragma once +#include #include namespace graphene { namespace chain { @@ -47,4 +48,7 @@ namespace graphene { namespace chain { } } + FC_REFLECT_DERIVED( graphene::chain::block_summary_object, (graphene::db::object), (block_id) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::block_summary_object ) diff --git a/libraries/chain/include/graphene/chain/budget_record_object.hpp b/libraries/chain/include/graphene/chain/budget_record_object.hpp index 63784c71..007d46a7 100644 --- a/libraries/chain/include/graphene/chain/budget_record_object.hpp +++ b/libraries/chain/include/graphene/chain/budget_record_object.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include #include namespace graphene { namespace chain { @@ -56,8 +55,6 @@ struct budget_record share_type supply_delta = 0; }; -class budget_record_object; - class budget_record_object : public graphene::db::abstract_object { public: @@ -70,8 +67,7 @@ class budget_record_object : public graphene::db::abstract_object buyback_index; } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::buyback_object ) diff --git a/libraries/chain/include/graphene/chain/chain_property_object.hpp b/libraries/chain/include/graphene/chain/chain_property_object.hpp index 3d2c82a6..3c7a77ff 100644 --- a/libraries/chain/include/graphene/chain/chain_property_object.hpp +++ b/libraries/chain/include/graphene/chain/chain_property_object.hpp @@ -27,8 +27,6 @@ namespace graphene { namespace chain { -class chain_property_object; - /** * Contains invariants which are set at genesis and never changed. */ @@ -48,3 +46,5 @@ FC_REFLECT_DERIVED( graphene::chain::chain_property_object, (graphene::db::objec (chain_id) (immutable_parameters) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::chain_property_object ) diff --git a/libraries/chain/include/graphene/chain/committee_member_object.hpp b/libraries/chain/include/graphene/chain/committee_member_object.hpp index 7b0d8e75..fe7968d3 100644 --- a/libraries/chain/include/graphene/chain/committee_member_object.hpp +++ b/libraries/chain/include/graphene/chain/committee_member_object.hpp @@ -29,8 +29,6 @@ namespace graphene { namespace chain { using namespace graphene::db; - class account_object; - /** * @brief tracks information about a committee_member account. * @ingroup object @@ -73,5 +71,8 @@ namespace graphene { namespace chain { using committee_member_index = generic_index; } } // graphene::chain + FC_REFLECT_DERIVED( graphene::chain::committee_member_object, (graphene::db::object), (committee_member_account)(vote_id)(total_votes)(url) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_object ) diff --git a/libraries/chain/include/graphene/chain/confidential_object.hpp b/libraries/chain/include/graphene/chain/confidential_object.hpp index f98e20a9..acdb0ba5 100644 --- a/libraries/chain/include/graphene/chain/confidential_object.hpp +++ b/libraries/chain/include/graphene/chain/confidential_object.hpp @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -50,8 +49,6 @@ class blinded_balance_object : public graphene::db::abstract_object get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const; std::vector get_seeds( asset_id_type for_asset, uint8_t count_winners )const; uint64_t get_random_bits( uint64_t bound ); @@ -306,6 +308,7 @@ namespace graphene { namespace chain { signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); void remove_son_proposal( const proposal_object& proposal ); bool is_son_dereg_valid( son_id_type son_id ); + const witness_schedule_object& get_witness_schedule_object()const; time_point_sec head_block_time()const; uint32_t head_block_num()const; @@ -463,7 +466,8 @@ namespace graphene { namespace chain { bool fill_order( const call_order_object& order, const asset& pays, const asset& receives ); bool fill_order( const force_settlement_object& settle, const asset& pays, const asset& receives ); - bool check_call_orders( const asset_object& mia, bool enable_black_swan = true ); + bool check_call_orders( const asset_object& mia, bool enable_black_swan = true, bool for_new_limit_order = false, + const asset_bitasset_data_object* bitasset_ptr = nullptr ); // helpers to fill_order void pay_order( const account_object& receiver, const asset& receives, const asset& pays ); @@ -472,7 +476,7 @@ namespace graphene { namespace chain { asset pay_market_fees( const asset_object& recv_asset, const asset& receives ); - ///@} + ///@{ /** * This method validates transactions without adding it to the pending state. * @return true if the transaction would validate @@ -487,9 +491,13 @@ 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; } 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(); } + void notify_applied_block( const signed_block& block ); + void notify_on_pending_transaction( const signed_transaction& tx ); void notify_changed_objects(); private: @@ -515,6 +523,8 @@ namespace graphene { namespace chain { const witness_object& validate_block_header( uint32_t skip, const signed_block& next_block )const; const witness_object& _validate_block_header( const signed_block& next_block )const; + void verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const; + void update_witnesses( fork_item& fork_entry )const; void create_block_summary(const signed_block& next_block); //////////////////// db_witness_schedule.cpp //////////////////// @@ -529,11 +539,13 @@ namespace graphene { namespace chain { void clear_expired_proposals(); void clear_expired_orders(); void update_expired_feeds(); + void update_core_exchange_rates(); void update_maintenance_flag( bool new_maintenance_flag ); void update_withdraw_permissions(); void update_tournaments(); void update_betting_markets(fc::time_point_sec current_block_time); - bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true ); + bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, + const asset_bitasset_data_object* bitasset_ptr = nullptr ); ///Steps performed only at maintenance intervals ///@{ @@ -550,9 +562,13 @@ namespace graphene { namespace chain { void update_son_metrics(); void update_active_sons(); void update_worker_votes(); - - template - void perform_account_maintenance(std::tuple helpers); + + public: + double calculate_vesting_factor(const account_object& stake_account); + uint32_t get_gpos_current_subperiod(); + + template + void perform_account_maintenance(Type tally_helper); ///@} ///@} @@ -592,6 +608,11 @@ namespace graphene { namespace chain { flat_map _checkpoints; node_property_object _node_property_object; + + /// Whether to update votes of standby witnesses and committee members when performing chain maintenance. + /// Set it to true to provide accurate data to API clients, set to false to have better performance. + bool _track_standby_votes = true; + fc::hash_ctr_rng _random_number_generator; bool _slow_replays = false; @@ -610,6 +631,18 @@ namespace graphene { namespace chain { void initialize_db_sidechain(); protected: private: + /// Tracks assets affected by bitshares-core issue #453 before hard fork #615 in one block + flat_set _issue_453_affected_assets; + + /// Pointers to core asset object and global objects who will have immutable addresses after created + ///@{ + const asset_object* _p_core_asset_obj = nullptr; + const asset_dynamic_data_object* _p_core_dynamic_data_obj = nullptr; + const global_property_object* _p_global_prop_obj = nullptr; + const dynamic_global_property_object* _p_dyn_global_prop_obj = nullptr; + const chain_property_object* _p_chain_property_obj = nullptr; + const witness_schedule_object* _p_witness_schedule_obj = nullptr; + ///@} }; namespace detail diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index 2e07ca26..ee264029 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -65,6 +65,21 @@ msg \ ) +#define GRAPHENE_TRY_NOTIFY( signal, ... ) \ + try \ + { \ + signal( __VA_ARGS__ ); \ + } \ + catch( const graphene::chain::plugin_exception& e ) \ + { \ + elog( "Caught plugin exception: ${e}", ("e", e.to_detail_string() ) ); \ + throw; \ + } \ + catch( ... ) \ + { \ + wlog( "Caught unexpected exception in plugin" ); \ + } + namespace graphene { namespace chain { FC_DECLARE_EXCEPTION( chain_exception, 3000000, "blockchain exception" ) @@ -77,6 +92,7 @@ namespace graphene { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, graphene::chain::chain_exception, 3070000, "undo database exception" ) FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, graphene::chain::chain_exception, 3080000, "unlinkable block" ) FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, graphene::chain::chain_exception, 3090000, "black swan" ) + FC_DECLARE_DERIVED_EXCEPTION( plugin_exception, graphene::chain::chain_exception, 3100000, "plugin exception" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" ) diff --git a/libraries/chain/include/graphene/chain/fba_object.hpp b/libraries/chain/include/graphene/chain/fba_object.hpp index aec9e9cd..3d1e1be0 100644 --- a/libraries/chain/include/graphene/chain/fba_object.hpp +++ b/libraries/chain/include/graphene/chain/fba_object.hpp @@ -49,4 +49,7 @@ class fba_accumulator_object : public graphene::db::abstract_object< fba_accumul } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), (accumulated_fba_fees)(designated_asset) ) +FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), + (accumulated_fba_fees)(designated_asset) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fba_accumulator_object ) diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index 8ca95b5e..4007ca09 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -51,6 +51,11 @@ namespace graphene { namespace chain { bool invalid = false; block_id_type id; signed_block data; + + // contains witness block signing keys scheduled *after* the block has been applied + shared_ptr< vector< pair< witness_id_type, public_key_type > > > scheduled_witnesses; + uint64_t next_block_aslot = 0; + fc::time_point_sec next_block_time; }; typedef shared_ptr item_ptr; diff --git a/libraries/chain/include/graphene/chain/genesis_state.hpp b/libraries/chain/include/graphene/chain/genesis_state.hpp index ebd153b6..b2f76118 100644 --- a/libraries/chain/include/graphene/chain/genesis_state.hpp +++ b/libraries/chain/include/graphene/chain/genesis_state.hpp @@ -23,6 +23,7 @@ */ #pragma once +#include #include #include #include @@ -169,56 +170,32 @@ struct genesis_state_type { } } // namespace graphene::chain -FC_REFLECT(graphene::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)(is_lifetime_member)) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_account_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_balance_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_vesting_balance_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_witness_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_committee_member_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_worker_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type) +FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type) -FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type, - (symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)(collateral_records)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position, - (owner)(collateral)(debt)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type, - (owner)(asset_symbol)(amount)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type, - (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay)) - -FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority, - (weight_threshold) - (account_auths) - (key_auths) - (address_auths)) -FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy, - (vesting_seconds) - (coin_seconds_earned) - (start_claim) - (coin_seconds_earned_last_update)) -FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy, - (begin_timestamp) - (vesting_cliff_seconds) - (vesting_duration_seconds) - (begin_balance)) -FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance, - (asset_symbol) - (amount) - (policy_type) - (policy)) -FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type, - (name) - (owner_authority) - (active_authority) - (core_balance) - (vesting_balances)) - -FC_REFLECT(graphene::chain::genesis_state_type, - (initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances) - (initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates) - (initial_committee_candidates)(initial_worker_candidates) - (initial_chain_id) - (immutable_parameters)) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_account_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_asset_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_balance_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_vesting_balance_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_witness_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_committee_member_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_worker_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type) diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 1d985a2d..28b23e55 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -130,7 +130,6 @@ namespace graphene { namespace chain { }} FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object), - (random) (head_block_number) (head_block_id) (time) @@ -154,3 +153,6 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje (active_witnesses) (active_sons) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::dynamic_global_property_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::global_property_object ) diff --git a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp index ade1a459..f7128889 100644 --- a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp @@ -23,11 +23,8 @@ */ #pragma once -#include - -#include - #include +#include namespace graphene { namespace chain { @@ -49,3 +46,5 @@ FC_REFLECT( graphene::chain::immutable_chain_parameters, (num_special_accounts) (num_special_assets) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::immutable_chain_parameters ) diff --git a/libraries/app/include/graphene/app/impacted.hpp b/libraries/chain/include/graphene/chain/impacted.hpp similarity index 96% rename from libraries/app/include/graphene/app/impacted.hpp rename to libraries/chain/include/graphene/chain/impacted.hpp index 2e59b910..2a22cbd1 100644 --- a/libraries/app/include/graphene/app/impacted.hpp +++ b/libraries/chain/include/graphene/chain/impacted.hpp @@ -28,7 +28,7 @@ #include #include -namespace graphene { namespace app { +namespace graphene { namespace chain { void operation_get_impacted_accounts( const graphene::chain::operation& op, @@ -39,4 +39,4 @@ void transaction_get_impacted_accounts( fc::flat_set& result ); -} } // graphene::app +} } // graphene::app \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index b56f4e9c..4bd3e048 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -217,3 +217,7 @@ FC_REFLECT_DERIVED( graphene::chain::force_settlement_object, (graphene::db::object), (owner)(balance)(settlement_date) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::force_settlement_object ) diff --git a/libraries/chain/include/graphene/chain/operation_history_object.hpp b/libraries/chain/include/graphene/chain/operation_history_object.hpp index d8b90b58..89199472 100644 --- a/libraries/chain/include/graphene/chain/operation_history_object.hpp +++ b/libraries/chain/include/graphene/chain/operation_history_object.hpp @@ -22,8 +22,10 @@ * THE SOFTWARE. */ #pragma once + #include #include + #include namespace graphene { namespace chain { @@ -94,15 +96,22 @@ namespace graphene { namespace chain { operation_history_id_type operation_id; uint32_t sequence = 0; /// the operation position within the given account account_transaction_history_id_type next; - - //std::pair account_op()const { return std::tie( account, operation_id ); } - //std::pair account_seq()const { return std::tie( account, sequence ); } }; struct by_id; struct by_seq; struct by_op; struct by_opid; + +typedef multi_index_container< + operation_history_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > + > +> operation_history_multi_index_type; + +typedef generic_index operation_history_index; + typedef multi_index_container< account_transaction_history_object, indexed_by< @@ -132,6 +141,8 @@ typedef generic_index #include +#include namespace graphene { namespace chain { - + class database; /** * @brief tracks the approval of a partially approved transaction @@ -97,3 +98,5 @@ FC_REFLECT_DERIVED( graphene::chain::proposal_object, (graphene::chain::object), (expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals) (available_active_approvals)(required_owner_approvals)(available_owner_approvals) (available_key_approvals)(proposer)(fail_reason)) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_object ) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 496e9067..50ccb8ae 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -59,6 +59,12 @@ namespace graphene { namespace chain { /// account's balance of core asset. flat_set votes; extensions_type extensions; + + /// Whether this account is voting + inline bool is_voting() const + { + return ( voting_account != GRAPHENE_PROXY_TO_SELF_ACCOUNT || !votes.empty() ); + } void validate()const; }; @@ -143,6 +149,7 @@ namespace graphene { namespace chain { optional< void_t > null_ext; optional< special_authority > owner_special_authority; optional< special_authority > active_special_authority; + optional< bool > update_last_voting_time; }; struct fee_parameters_type @@ -298,7 +305,7 @@ FC_REFLECT( graphene::chain::account_create_operation, (name)(owner)(active)(options)(extensions) ) -FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) ) +FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(update_last_voting_time) ) FC_REFLECT( graphene::chain::account_update_operation, (fee)(account)(owner)(active)(new_options)(extensions) ) @@ -313,5 +320,16 @@ FC_REFLECT( graphene::chain::account_whitelist_operation::fee_parameters_type, ( FC_REFLECT( graphene::chain::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) ) FC_REFLECT( graphene::chain::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) ) FC_REFLECT( graphene::chain::account_transfer_operation::fee_parameters_type, (fee) ) - FC_REFLECT( graphene::chain::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_whitelist_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_upgrade_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_whitelist_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_upgrade_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_transfer_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/address.hpp b/libraries/chain/include/graphene/chain/protocol/address.hpp index b225b42c..8bf0fab6 100644 --- a/libraries/chain/include/graphene/chain/protocol/address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/address.hpp @@ -25,14 +25,10 @@ #include #include +#include -#include #include - -namespace fc { namespace ecc { - class public_key; - typedef fc::array public_key_data; -} } // fc::ecc +#include namespace graphene { namespace chain { @@ -51,7 +47,7 @@ namespace graphene { namespace chain { class address { public: - address(); ///< constructs empty / null address + address(){} ///< constructs empty / null address explicit address( const std::string& base58str ); ///< converts to binary, validates checksum address( const fc::ecc::public_key& pub ); ///< converts to binary explicit address( const fc::ecc::public_key_data& pub ); ///< converts to binary @@ -97,3 +93,5 @@ namespace std #include FC_REFLECT( graphene::chain::address, (addr) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::address ) diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index c9f3b277..ce758862 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -112,3 +113,5 @@ FC_REFLECT( graphene::chain::block_id_predicate, (id) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::assert_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::assert_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/asset.hpp b/libraries/chain/include/graphene/chain/protocol/asset.hpp index a938129a..60bd3cd0 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset.hpp @@ -218,3 +218,7 @@ FC_REFLECT( graphene::chain::price, (base)(quote) ) (core_exchange_rate) FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::price ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::price_feed ) diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index a567c5a1..ae5dc211 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -764,3 +764,30 @@ FC_REFLECT( graphene::chain::asset_reserve_operation, FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) ); FC_REFLECT( graphene::chain::asset_dividend_distribution_operation, (fee)(dividend_asset_id)(account_id)(amounts)(extensions) ); + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::bitasset_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_global_settle_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dividend_distribution_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_claim_fees_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_bitasset_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_feed_producers_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_publish_feed_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_issue_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_reserve_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_global_settle_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_cancel_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_fund_fee_pool_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_claim_fees_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_bitasset_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_feed_producers_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_publish_feed_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_issue_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_reserve_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/authority.hpp b/libraries/chain/include/graphene/chain/protocol/authority.hpp index 70b674b3..d279402d 100644 --- a/libraries/chain/include/graphene/chain/protocol/authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/authority.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -134,3 +135,5 @@ void add_authority_accounts( FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) ) // FC_REFLECT_TYPENAME( graphene::chain::authority::classification ) FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::authority ) diff --git a/libraries/chain/include/graphene/chain/protocol/balance.hpp b/libraries/chain/include/graphene/chain/protocol/balance.hpp index f60087a7..9d0b252f 100644 --- a/libraries/chain/include/graphene/chain/protocol/balance.hpp +++ b/libraries/chain/include/graphene/chain/protocol/balance.hpp @@ -23,6 +23,8 @@ */ #pragma once #include +#include +#include namespace graphene { namespace chain { @@ -57,3 +59,5 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::balance_claim_operation::fee_parameters_type, ) FC_REFLECT( graphene::chain::balance_claim_operation, (fee)(deposit_to_account)(balance_to_claim)(balance_owner_key)(total_claimed) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::balance_claim_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/base.hpp b/libraries/chain/include/graphene/chain/protocol/base.hpp index 52240b93..23c285d3 100644 --- a/libraries/chain/include/graphene/chain/protocol/base.hpp +++ b/libraries/chain/include/graphene/chain/protocol/base.hpp @@ -27,8 +27,13 @@ #include #include +#include + namespace graphene { namespace chain { + struct asset; + struct authority; + /** * @defgroup operations Operations * @ingroup transactions Transactions diff --git a/libraries/chain/include/graphene/chain/protocol/block.hpp b/libraries/chain/include/graphene/chain/protocol/block.hpp index 46ac0f6d..ad5b0327 100644 --- a/libraries/chain/include/graphene/chain/protocol/block.hpp +++ b/libraries/chain/include/graphene/chain/protocol/block.hpp @@ -69,3 +69,8 @@ FC_REFLECT( graphene::chain::block_header, (extensions) ) FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) ) FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) ) + + +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::block_header) +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_block_header) +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_block) diff --git a/libraries/chain/include/graphene/chain/protocol/buyback.hpp b/libraries/chain/include/graphene/chain/protocol/buyback.hpp index 6adad52d..4a51e8c7 100644 --- a/libraries/chain/include/graphene/chain/protocol/buyback.hpp +++ b/libraries/chain/include/graphene/chain/protocol/buyback.hpp @@ -50,3 +50,5 @@ struct buyback_account_options } } FC_REFLECT( graphene::chain::buyback_account_options, (asset_to_buy)(asset_to_buy_issuer)(markets) ); + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::buyback_account_options ) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index cd870a2e..cc898775 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -28,6 +28,8 @@ #include #include +#include <../hardfork.d/GPOS.hf> +#include namespace graphene { namespace chain { struct fee_schedule; } } @@ -39,10 +41,16 @@ namespace graphene { namespace chain { optional< uint16_t > betting_rake_fee_percentage; optional< flat_map > permitted_betting_odds_increments; optional< uint16_t > live_betting_delay_time; + optional< uint16_t > sweeps_distribution_percentage = SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE; + optional< asset_id_type > sweeps_distribution_asset = SWEEPS_DEFAULT_DISTRIBUTION_ASSET; + optional< account_id_type > sweeps_vesting_accumulator_account= SWEEPS_ACCUMULATOR_ACCOUNT; + /* gpos parameters */ + optional < uint32_t > gpos_period = GPOS_PERIOD; + optional < uint32_t > gpos_subperiod = GPOS_SUBPERIOD; + optional < uint32_t > gpos_period_start = HARDFORK_GPOS_TIME.sec_since_epoch(); + optional < uint32_t > gpos_vesting_lockin_period = GPOS_VESTING_LOCKIN_PERIOD; + optional < uint16_t > son_count; - optional< uint16_t > sweeps_distribution_percentage; - optional< asset_id_type > sweeps_distribution_asset; - optional< account_id_type > sweeps_vesting_accumulator_account; optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; @@ -150,6 +158,18 @@ namespace graphene { namespace chain { inline uint16_t son_down_time()const { return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; } + inline uint32_t gpos_period()const { + return extensions.value.gpos_period.valid() ? *extensions.value.gpos_period : GPOS_PERIOD; /// total seconds of current gpos period + } + inline uint32_t gpos_subperiod()const { + return extensions.value.gpos_subperiod.valid() ? *extensions.value.gpos_subperiod : GPOS_SUBPERIOD; /// gpos_period % gpos_subperiod = 0 + } + inline uint32_t gpos_period_start()const { + return extensions.value.gpos_period_start.valid() ? *extensions.value.gpos_period_start : HARDFORK_GPOS_TIME.sec_since_epoch(); /// current period start date + } + inline uint32_t gpos_vesting_lockin_period()const { + return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period + } }; } } // graphene::chain @@ -164,12 +184,16 @@ FC_REFLECT( graphene::chain::parameter_extension, (sweeps_distribution_percentage) (sweeps_distribution_asset) (sweeps_vesting_accumulator_account) + (gpos_period) + (gpos_subperiod) + (gpos_period_start) + (gpos_vesting_lockin_period) (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) (son_deregister_time) (son_heartbeat_frequency) - (son_down_time) + (son_down_time) ) FC_REFLECT( graphene::chain::chain_parameters, @@ -218,3 +242,5 @@ FC_REFLECT( graphene::chain::chain_parameters, (maximum_tournament_number_of_wins) (extensions) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::chain_parameters ) diff --git a/libraries/chain/include/graphene/chain/protocol/committee_member.hpp b/libraries/chain/include/graphene/chain/protocol/committee_member.hpp index 77188367..8aaed748 100644 --- a/libraries/chain/include/graphene/chain/protocol/committee_member.hpp +++ b/libraries/chain/include/graphene/chain/protocol/committee_member.hpp @@ -104,3 +104,10 @@ FC_REFLECT( graphene::chain::committee_member_create_operation, FC_REFLECT( graphene::chain::committee_member_update_operation, (fee)(committee_member)(committee_member_account)(new_url) ) FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation, (fee)(new_parameters) ); + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_global_parameters_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/confidential.hpp b/libraries/chain/include/graphene/chain/protocol/confidential.hpp index 763006ae..697ef35b 100644 --- a/libraries/chain/include/graphene/chain/protocol/confidential.hpp +++ b/libraries/chain/include/graphene/chain/protocol/confidential.hpp @@ -281,3 +281,10 @@ FC_REFLECT( graphene::chain::blind_transfer_operation, FC_REFLECT( graphene::chain::transfer_to_blind_operation::fee_parameters_type, (fee)(price_per_output) ) FC_REFLECT( graphene::chain::transfer_from_blind_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::blind_transfer_operation::fee_parameters_type, (fee)(price_per_output) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_to_blind_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_from_blind_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::blind_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_to_blind_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_from_blind_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::blind_transfer_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/custom.hpp b/libraries/chain/include/graphene/chain/protocol/custom.hpp index e5701a4b..5596aaad 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom.hpp @@ -56,3 +56,6 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::custom_operation::fee_parameters_type, (fee)(price_per_kbyte) ) FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::custom_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::custom_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/ext.hpp b/libraries/chain/include/graphene/chain/protocol/ext.hpp index 31f66506..6c974630 100644 --- a/libraries/chain/include/graphene/chain/protocol/ext.hpp +++ b/libraries/chain/include/graphene/chain/protocol/ext.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include #include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/fba.hpp b/libraries/chain/include/graphene/chain/protocol/fba.hpp index 7460ca8d..dc672436 100644 --- a/libraries/chain/include/graphene/chain/protocol/fba.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fba.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -45,3 +46,5 @@ struct fba_distribute_operation : public base_operation FC_REFLECT( graphene::chain::fba_distribute_operation::fee_parameters_type, ) FC_REFLECT( graphene::chain::fba_distribute_operation, (fee)(account_id)(fba_id)(amount) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fba_distribute_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index e250ab17..9baaffc7 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #pragma once +#include #include namespace graphene { namespace chain { @@ -85,3 +86,5 @@ namespace graphene { namespace chain { FC_REFLECT_TYPENAME( graphene::chain::fee_parameters ) FC_REFLECT( graphene::chain::fee_schedule, (parameters)(scale) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fee_schedule ) diff --git a/libraries/chain/include/graphene/chain/protocol/market.hpp b/libraries/chain/include/graphene/chain/protocol/market.hpp index 56352c60..2bff8c56 100644 --- a/libraries/chain/include/graphene/chain/protocol/market.hpp +++ b/libraries/chain/include/graphene/chain/protocol/market.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -165,9 +166,15 @@ FC_REFLECT( graphene::chain::limit_order_cancel_operation::fee_parameters_type, FC_REFLECT( graphene::chain::call_order_update_operation::fee_parameters_type, (fee) ) /// THIS IS THE ONLY VIRTUAL OPERATION THUS FAR... FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, ) - - FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions)) FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) ) FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) ) FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fill_order_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/memo.hpp b/libraries/chain/include/graphene/chain/protocol/memo.hpp index b126d3a7..6c5b69fb 100644 --- a/libraries/chain/include/graphene/chain/protocol/memo.hpp +++ b/libraries/chain/include/graphene/chain/protocol/memo.hpp @@ -89,3 +89,6 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::memo_message, (checksum)(text) ) FC_REFLECT( graphene::chain::memo_data, (from)(to)(nonce)(message) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::memo_message ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::memo_data ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index d633056f..caca89dd 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -191,3 +191,5 @@ namespace graphene { namespace chain { FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT( graphene::chain::op_wrapper, (op) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::op_wrapper ) diff --git a/libraries/chain/include/graphene/chain/protocol/proposal.hpp b/libraries/chain/include/graphene/chain/protocol/proposal.hpp index 3383b6cf..141ec35f 100644 --- a/libraries/chain/include/graphene/chain/protocol/proposal.hpp +++ b/libraries/chain/include/graphene/chain/protocol/proposal.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { /** @@ -179,3 +180,10 @@ FC_REFLECT( graphene::chain::proposal_update_operation, (fee)(fee_paying_account (active_approvals_to_add)(active_approvals_to_remove)(owner_approvals_to_add)(owner_approvals_to_remove) (key_approvals_to_add)(key_approvals_to_remove)(extensions) ) FC_REFLECT( graphene::chain::proposal_delete_operation, (fee)(fee_paying_account)(using_owner_authority)(proposal)(extensions) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_delete_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_delete_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/special_authority.hpp b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp index 3ee6f15f..05a80719 100644 --- a/libraries/chain/include/graphene/chain/protocol/special_authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp @@ -48,3 +48,5 @@ void validate_special_authority( const special_authority& auth ); FC_REFLECT( graphene::chain::no_special_authority, ) FC_REFLECT( graphene::chain::top_holders_special_authority, (asset)(num_top_holders) ) FC_REFLECT_TYPENAME( graphene::chain::special_authority ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::top_holders_special_authority ) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 95c39961..2a9909a5 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -230,3 +230,8 @@ FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expi // 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) ) + + +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::transaction) +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_transaction) +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::processed_transaction) diff --git a/libraries/chain/include/graphene/chain/protocol/transfer.hpp b/libraries/chain/include/graphene/chain/protocol/transfer.hpp index f4417bb7..5366a7ab 100644 --- a/libraries/chain/include/graphene/chain/protocol/transfer.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transfer.hpp @@ -24,6 +24,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { @@ -105,3 +106,8 @@ FC_REFLECT( graphene::chain::override_transfer_operation::fee_parameters_type, ( FC_REFLECT( graphene::chain::override_transfer_operation, (fee)(issuer)(from)(to)(amount)(memo)(extensions) ) FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo)(extensions) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::override_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::override_transfer_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 1caf1f9c..f31cb3cf 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -27,13 +27,15 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include +#include #include #include #include @@ -42,10 +44,34 @@ #include #include #include -#include #include #include +#define GRAPHENE_EXTERNAL_SERIALIZATION(ext, type) \ +namespace fc { \ + ext template void from_variant( const variant& v, type& vo, uint32_t max_depth ); \ + ext template void to_variant( const type& v, variant& vo, uint32_t max_depth ); \ +namespace raw { \ + ext template void pack< datastream, type >( datastream& s, const type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ + ext template void pack< datastream, type >( datastream& s, const type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ + ext template void unpack< datastream, type >( datastream& s, type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ +} } // fc::raw + +#define FC_REFLECT_DERIVED_NO_TYPENAME( TYPE, INHERITS, MEMBERS ) \ +namespace fc { \ +template<> struct reflector {\ + typedef TYPE type; \ + typedef fc::true_type is_defined; \ + typedef fc::false_type is_enum; \ + enum member_count_enum { \ + local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\ + total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\ + }; \ + FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ +}; \ +} // fc + + namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 9fcbda66..d3eb9560 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -23,11 +23,24 @@ */ #pragma once #include +#include namespace graphene { namespace chain { enum class vesting_balance_type { normal, gpos, son }; + inline std::string get_vesting_balance_type(vesting_balance_type type) { + switch (type) { + case vesting_balance_type::normal: + return "NORMAL"; + case vesting_balance_type::son: + return "SON"; + case vesting_balance_type::gpos: + default: + return "GPOS"; + } + } + struct linear_vesting_policy_initializer { /** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */ @@ -124,4 +137,9 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vestin FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) -FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) +FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son)) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 7ef2c8a1..8a46954d 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -24,12 +24,7 @@ #pragma once -#include -#include -#include - -#include -#include +#include namespace graphene { namespace chain { @@ -151,3 +146,5 @@ FC_REFLECT_TYPENAME( fc::flat_set ) FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp b/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp index 7bc905ac..7963e99f 100644 --- a/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp +++ b/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp @@ -24,6 +24,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { @@ -179,3 +180,12 @@ FC_REFLECT( graphene::chain::withdraw_permission_update_operation, (fee)(withdra FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw_permission)(withdraw_from_account)(withdraw_to_account)(amount_to_withdraw)(memo) ); FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_claim_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_delete_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_claim_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_delete_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/witness.hpp b/libraries/chain/include/graphene/chain/protocol/witness.hpp index b096e826..2b5e88b0 100644 --- a/libraries/chain/include/graphene/chain/protocol/witness.hpp +++ b/libraries/chain/include/graphene/chain/protocol/witness.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -84,3 +85,8 @@ FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(ur FC_REFLECT( graphene::chain::witness_update_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key)(new_initial_secret) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_update_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/worker.hpp b/libraries/chain/include/graphene/chain/protocol/worker.hpp index 9e6eef35..11e0aa05 100644 --- a/libraries/chain/include/graphene/chain/protocol/worker.hpp +++ b/libraries/chain/include/graphene/chain/protocol/worker.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { @@ -104,3 +105,5 @@ FC_REFLECT( graphene::chain::worker_create_operation::fee_parameters_type, (fee) FC_REFLECT( graphene::chain::worker_create_operation, (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(name)(url)(initializer) ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_create_operation ) diff --git a/libraries/chain/include/graphene/chain/pts_address.hpp b/libraries/chain/include/graphene/chain/pts_address.hpp index 636e2f11..c0bc80ff 100644 --- a/libraries/chain/include/graphene/chain/pts_address.hpp +++ b/libraries/chain/include/graphene/chain/pts_address.hpp @@ -24,6 +24,8 @@ #pragma once #include +#include +#include #include namespace fc { namespace ecc { class public_key; } } @@ -75,4 +77,11 @@ namespace fc { void to_variant( const graphene::chain::pts_address& var, fc::variant& vo, uint32_t max_depth = 1 ); void from_variant( const fc::variant& var, graphene::chain::pts_address& vo, uint32_t max_depth = 1 ); -} +namespace raw { + extern template void pack( datastream& s, const graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); + extern template void pack( datastream& s, const graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); + extern template void unpack( datastream& s, graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); +} } // fc::raw diff --git a/libraries/chain/include/graphene/chain/special_authority_object.hpp b/libraries/chain/include/graphene/chain/special_authority_object.hpp index da9ecc5e..75093f3a 100644 --- a/libraries/chain/include/graphene/chain/special_authority_object.hpp +++ b/libraries/chain/include/graphene/chain/special_authority_object.hpp @@ -68,3 +68,5 @@ FC_REFLECT_DERIVED( (graphene::db::object), (account) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::special_authority_object ) diff --git a/libraries/chain/include/graphene/chain/transaction_object.hpp b/libraries/chain/include/graphene/chain/transaction_object.hpp index 4f76d6be..aaaa31f1 100644 --- a/libraries/chain/include/graphene/chain/transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/transaction_object.hpp @@ -22,12 +22,10 @@ * THE SOFTWARE. */ #pragma once -#include #include #include #include -#include #include #include @@ -72,3 +70,5 @@ namespace graphene { namespace chain { } } FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(trx_id) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transaction_object ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp index fccfbb75..9bb7520e 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp @@ -46,6 +46,7 @@ class vesting_balance_withdraw_evaluator : public evaluator, + member, member_offset //member //member_offset >, composite_key_compare< std::less< asset_id_type >, + std::less< vesting_balance_type >, std::greater< share_type > //std::less< account_id_type > > @@ -255,3 +255,7 @@ FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::objec (policy) (balance_type) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::linear_vesting_policy ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::cdd_vesting_policy ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_object ) diff --git a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp index 000573bd..a6fee0c5 100644 --- a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp +++ b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp @@ -114,3 +114,5 @@ FC_REFLECT_DERIVED( graphene::chain::withdraw_permission_object, (graphene::db:: (expiration) (claimed_this_period) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_object ) diff --git a/libraries/chain/include/graphene/chain/witness_object.hpp b/libraries/chain/include/graphene/chain/witness_object.hpp index 2d1b7666..7928b46e 100644 --- a/libraries/chain/include/graphene/chain/witness_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_object.hpp @@ -29,8 +29,6 @@ namespace graphene { namespace chain { using namespace graphene::db; - class witness_object; - class witness_object : public abstract_object { public: @@ -85,3 +83,5 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object), (total_missed) (last_confirmed_block_num) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_object ) diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index fc7d6d10..b934fd01 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -153,3 +153,6 @@ FC_REFLECT_DERIVED( (recent_slots_filled) (current_shuffled_sons) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_scheduler ) +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_schedule_object ) diff --git a/libraries/chain/include/graphene/chain/worker_object.hpp b/libraries/chain/include/graphene/chain/worker_object.hpp index 1219fc1c..5e23f0b8 100644 --- a/libraries/chain/include/graphene/chain/worker_object.hpp +++ b/libraries/chain/include/graphene/chain/worker_object.hpp @@ -22,8 +22,9 @@ * THE SOFTWARE. */ #pragma once -#include +#include #include +#include namespace graphene { namespace chain { @@ -175,3 +176,5 @@ FC_REFLECT_DERIVED( graphene::chain::worker_object, (graphene::db::object), (name) (url) ) + +GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_object ) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 1a8e2ee2..f1eef69f 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -46,15 +46,7 @@ struct proposal_operation_hardfork_visitor template void operator()(const T &v) const {} - void operator()(const committee_member_update_global_parameters_operation &op) const { - if( block_time < HARDFORK_1000_TIME ) // TODO: remove after hf - FC_ASSERT( !op.new_parameters.extensions.value.min_bet_multiplier.valid() - && !op.new_parameters.extensions.value.max_bet_multiplier.valid() - && !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid() - && !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid() - && !op.new_parameters.extensions.value.live_betting_delay_time.valid(), - "Parameter extensions are not allowed yet!" ); - } + void operator()(const committee_member_update_global_parameters_operation &op) const {} void operator()(const graphene::chain::tournament_payout_operation &o) const { // TODO: move check into tournament_payout_operation::validate after HARDFORK_999_TIME @@ -158,6 +150,10 @@ struct proposal_operation_hardfork_visitor void operator()(const son_maintenance_operation &v) const { FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" ); + + void operator()(const vesting_balance_create_operation &vbco) const { + if(block_time < HARDFORK_GPOS_TIME) + FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" ); } // loop and self visit in proposals diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 343edce2..1d5a8706 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -37,7 +37,7 @@ bool proposal_object::is_authorized_to_execute(database& db) const [&]( account_id_type id ){ return &id(db).active; }, [&]( account_id_type id ){ return &id(db).owner; }, db.get_global_properties().parameters.max_authority_depth, - true, /* allow committeee */ + true, /* allow committee */ available_active_approvals, available_owner_approvals ); } @@ -90,3 +90,5 @@ void required_approval_index::object_removed( const object& obj ) } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::proposal_object ) diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index 6721bb07..2405369a 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -24,6 +24,9 @@ #include #include #include + +#include + namespace graphene { namespace chain { /** @@ -281,6 +284,7 @@ void account_update_operation::validate()const || new_options.valid() || extensions.value.owner_special_authority.valid() || extensions.value.active_special_authority.valid() + || extensions.value.update_last_voting_time.valid() ); FC_ASSERT( has_action ); @@ -326,3 +330,15 @@ void account_transfer_operation::validate()const } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_whitelist_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_upgrade_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_whitelist_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_upgrade_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transfer_operation ) diff --git a/libraries/chain/protocol/address.cpp b/libraries/chain/protocol/address.cpp index 19bb4df5..f0edbd49 100644 --- a/libraries/chain/protocol/address.cpp +++ b/libraries/chain/protocol/address.cpp @@ -27,9 +27,10 @@ #include #include +#include + namespace graphene { namespace chain { - address::address(){} address::address( const std::string& base58str ) { @@ -110,3 +111,5 @@ namespace fc vo = graphene::chain::address( var.as_string() ); } } + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::address ) diff --git a/libraries/chain/protocol/assert.cpp b/libraries/chain/protocol/assert.cpp index 60f26e3f..5ce61e45 100644 --- a/libraries/chain/protocol/assert.cpp +++ b/libraries/chain/protocol/assert.cpp @@ -21,7 +21,11 @@ * 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 { @@ -62,5 +66,7 @@ share_type assert_operation::calculate_fee(const fee_parameters_type& k)const return k.fee * predicates.size(); } - } } // namespace graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::assert_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::assert_operation ) diff --git a/libraries/chain/protocol/asset.cpp b/libraries/chain/protocol/asset.cpp index e1169b0c..525e193b 100644 --- a/libraries/chain/protocol/asset.cpp +++ b/libraries/chain/protocol/asset.cpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace graphene { namespace chain { typedef boost::multiprecision::uint128_t uint128_t; @@ -130,7 +131,11 @@ namespace graphene { namespace chain { return ~(asset( cp.numerator().convert_to(), debt.asset_id ) / asset( cp.denominator().convert_to(), collateral.asset_id )); } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) } - bool price::is_null() const { return *this == price(); } + bool price::is_null() const + { + // Effectively same as "return *this == price();" but perhaps faster + return ( base.asset_id == asset_id_type() && quote.asset_id == asset_id_type() ); + } void price::validate() const { try { @@ -202,3 +207,7 @@ const int64_t scaled_precision_lut[19] = }; } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::price ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::price_feed ) diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index e4942aa4..5dfd09ee 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace graphene { namespace chain { /** @@ -288,3 +290,30 @@ void lottery_asset_options::validate() const } } } // namespace graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::bitasset_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_global_settle_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_claim_fees_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_bitasset_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_feed_producers_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_publish_feed_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_issue_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_reserve_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_global_settle_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_cancel_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_fund_fee_pool_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_claim_fees_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dividend_distribution_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_bitasset_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_feed_producers_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_publish_feed_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_issue_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_reserve_operation ) diff --git a/libraries/chain/protocol/authority.cpp b/libraries/chain/protocol/authority.cpp index 97470d33..6cfed2ec 100644 --- a/libraries/chain/protocol/authority.cpp +++ b/libraries/chain/protocol/authority.cpp @@ -23,6 +23,7 @@ */ #include +#include namespace graphene { namespace chain { @@ -36,3 +37,5 @@ void add_authority_accounts( } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::authority ) diff --git a/libraries/chain/protocol/block.cpp b/libraries/chain/protocol/block.cpp index d32365dd..725ea3a7 100644 --- a/libraries/chain/protocol/block.cpp +++ b/libraries/chain/protocol/block.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include #include #include #include @@ -90,3 +91,7 @@ namespace graphene { namespace chain { } } } + +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::block_header) +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_block_header) +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_block) diff --git a/libraries/chain/protocol/committee_member.cpp b/libraries/chain/protocol/committee_member.cpp index 4c8c5d25..1824870a 100644 --- a/libraries/chain/protocol/committee_member.cpp +++ b/libraries/chain/protocol/committee_member.cpp @@ -21,8 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include +#include #include +#include + namespace graphene { namespace chain { void committee_member_create_operation::validate()const @@ -45,3 +49,10 @@ void committee_member_update_global_parameters_operation::validate() const } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_global_parameters_operation ) diff --git a/libraries/chain/protocol/confidential.cpp b/libraries/chain/protocol/confidential.cpp index 603befa1..2e8fbc68 100644 --- a/libraries/chain/protocol/confidential.cpp +++ b/libraries/chain/protocol/confidential.cpp @@ -27,7 +27,6 @@ #include #include -#include namespace graphene { namespace chain { @@ -141,9 +140,6 @@ share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k return k.fee + outputs.size() * k.price_per_output; } - - - /** * Packs *this then encodes as base58 encoded string. */ @@ -159,6 +155,11 @@ stealth_confirmation::stealth_confirmation( const std::string& base58 ) *this = fc::raw::unpack( fc::from_base58( base58 ) ); } - - } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation ) diff --git a/libraries/chain/protocol/custom.cpp b/libraries/chain/protocol/custom.cpp index b69243be..72f8dd44 100644 --- a/libraries/chain/protocol/custom.cpp +++ b/libraries/chain/protocol/custom.cpp @@ -23,6 +23,8 @@ */ #include +#include + namespace graphene { namespace chain { void custom_operation::validate()const @@ -35,3 +37,6 @@ share_type custom_operation::calculate_fee(const fee_parameters_type& k)const } } } + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::custom_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::custom_operation ) diff --git a/libraries/chain/protocol/fee_schedule.cpp b/libraries/chain/protocol/fee_schedule.cpp index 138d801e..6d494e37 100644 --- a/libraries/chain/protocol/fee_schedule.cpp +++ b/libraries/chain/protocol/fee_schedule.cpp @@ -35,6 +35,8 @@ namespace fc //template const graphene::chain::fee_schedule& smart_ref::operator*() const; } +#include + #define MAX_FEE_STABILIZATION_ITERATION 4 namespace graphene { namespace chain { @@ -208,3 +210,5 @@ namespace graphene { namespace chain { } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fee_schedule ) diff --git a/libraries/chain/protocol/market.cpp b/libraries/chain/protocol/market.cpp index 923f4763..ae0a3a68 100644 --- a/libraries/chain/protocol/market.cpp +++ b/libraries/chain/protocol/market.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include namespace graphene { namespace chain { @@ -46,3 +47,11 @@ void call_order_update_operation::validate()const } FC_CAPTURE_AND_RETHROW((*this)) } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_cancel_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_cancel_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fill_order_operation ) diff --git a/libraries/chain/protocol/memo.cpp b/libraries/chain/protocol/memo.cpp index e04b5e43..afa0b486 100644 --- a/libraries/chain/protocol/memo.cpp +++ b/libraries/chain/protocol/memo.cpp @@ -23,6 +23,7 @@ */ #include #include +#include namespace graphene { namespace chain { @@ -88,3 +89,6 @@ memo_message memo_message::deserialize(const string& serial) } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::memo_message ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::memo_data ) diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 40a37eba..7db51078 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -21,7 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include +#include +#include namespace graphene { namespace chain { @@ -85,3 +88,5 @@ void operation_get_required_authorities( const operation& op, } } } // namespace graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::op_wrapper ) diff --git a/libraries/chain/protocol/proposal.cpp b/libraries/chain/protocol/proposal.cpp index 069824af..c77e71e4 100644 --- a/libraries/chain/protocol/proposal.cpp +++ b/libraries/chain/protocol/proposal.cpp @@ -25,6 +25,8 @@ #include #include +#include + namespace graphene { namespace chain { proposal_create_operation proposal_create_operation::committee_proposal(const chain_parameters& global_params, fc::time_point_sec head_block_time ) @@ -105,3 +107,10 @@ void proposal_update_operation::get_required_owner_authorities( flat_set +#include +#include +#include +#include +#include +#include + +#include + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::balance_claim_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::buyback_account_options ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fba_distribute_operation ) + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_withdraw_operation ) + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::chain_parameters ) diff --git a/libraries/chain/protocol/tournament.cpp b/libraries/chain/protocol/tournament.cpp index 57e80bf3..78ab4c01 100644 --- a/libraries/chain/protocol/tournament.cpp +++ b/libraries/chain/protocol/tournament.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index a11e3335..093e7833 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -390,3 +391,7 @@ void signed_transaction::verify_authority( } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::transaction) +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_transaction) +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::processed_transaction) diff --git a/libraries/chain/protocol/transfer.cpp b/libraries/chain/protocol/transfer.cpp index 3dfe4eb7..0fb0aefa 100644 --- a/libraries/chain/protocol/transfer.cpp +++ b/libraries/chain/protocol/transfer.cpp @@ -23,6 +23,8 @@ */ #include +#include + namespace graphene { namespace chain { share_type transfer_operation::calculate_fee( const fee_parameters_type& schedule )const @@ -61,3 +63,8 @@ void override_transfer_operation::validate()const } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::override_transfer_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::override_transfer_operation ) diff --git a/libraries/chain/protocol/vote.cpp b/libraries/chain/protocol/vote.cpp index f78f2b4f..68f476f5 100644 --- a/libraries/chain/protocol/vote.cpp +++ b/libraries/chain/protocol/vote.cpp @@ -49,3 +49,5 @@ void from_variant( const variant& var, graphene::chain::vote_id_type& vo, uint32 } } // fc + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vote_id_type ) diff --git a/libraries/chain/protocol/withdraw_permission.cpp b/libraries/chain/protocol/withdraw_permission.cpp index 33b40c85..b36c378d 100644 --- a/libraries/chain/protocol/withdraw_permission.cpp +++ b/libraries/chain/protocol/withdraw_permission.cpp @@ -23,6 +23,8 @@ */ #include +#include + namespace graphene { namespace chain { void withdraw_permission_update_operation::validate()const @@ -65,6 +67,13 @@ void withdraw_permission_delete_operation::validate() const FC_ASSERT( withdraw_from_account != authorized_account ); } - } } // graphene::chain +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_claim_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_delete_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_update_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_claim_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_delete_operation ) diff --git a/libraries/chain/protocol/witness.cpp b/libraries/chain/protocol/witness.cpp index 82fa462a..90583cd8 100644 --- a/libraries/chain/protocol/witness.cpp +++ b/libraries/chain/protocol/witness.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include namespace graphene { namespace chain { @@ -39,3 +40,8 @@ void witness_update_operation::validate() const } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_update_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_create_operation ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_update_operation ) diff --git a/libraries/chain/protocol/worker.cpp b/libraries/chain/protocol/worker.cpp index eb133da0..932148ec 100644 --- a/libraries/chain/protocol/worker.cpp +++ b/libraries/chain/protocol/worker.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include namespace graphene { namespace chain { @@ -36,3 +37,6 @@ void worker_create_operation::validate() const } } } + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_create_operation::fee_parameters_type ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_create_operation ) diff --git a/libraries/chain/pts_address.cpp b/libraries/chain/pts_address.cpp index 27f3d256..c6d74f58 100644 --- a/libraries/chain/pts_address.cpp +++ b/libraries/chain/pts_address.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace graphene { namespace chain { @@ -97,4 +98,12 @@ namespace fc { vo = graphene::chain::pts_address( var.as_string() ); } -} + +namespace raw { + template void pack( datastream& s, const graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); + template void pack( datastream& s, const graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); + template void unpack( datastream& s, graphene::chain::pts_address& tx, + uint32_t _max_depth=FC_PACK_MAX_DEPTH ); +} } // fc::raw diff --git a/libraries/chain/small_objects.cpp b/libraries/chain/small_objects.cpp new file mode 100644 index 00000000..a74fa116 --- /dev/null +++ b/libraries/chain/small_objects.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 BitShares Blockchain 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::block_summary_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::budget_record ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::budget_record_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::buyback_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::immutable_chain_parameters ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::force_settlement_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::chain_property_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blinded_balance_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fba_accumulator_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::dynamic_global_property_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::global_property_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::operation_history_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transaction_history_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::special_authority_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transaction_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_scheduler ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_schedule_object ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_object ) diff --git a/libraries/chain/special_authority.cpp b/libraries/chain/special_authority.cpp index ca974f30..74889f80 100644 --- a/libraries/chain/special_authority.cpp +++ b/libraries/chain/special_authority.cpp @@ -25,6 +25,8 @@ #include #include +#include + namespace graphene { namespace chain { struct special_authority_validate_visitor @@ -68,3 +70,6 @@ void evaluate_special_authority( const database& db, const special_authority& a } } } // graphene::chain + + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::top_holders_special_authority ) diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index cc82aa3e..9f93a5ff 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -48,6 +48,9 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() ); + if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass + FC_ASSERT( op.balance_type == vesting_balance_type::normal); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -103,23 +106,70 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance // If making changes to this logic, check if those changes should also be made there as well. obj.owner = op.owner; obj.balance = op.amount; + if(op.balance_type == vesting_balance_type::gpos) + { + const auto &gpo = d.get_global_properties(); + // forcing gpos policy + linear_vesting_policy p; + p.begin_timestamp = now; + p.vesting_cliff_seconds = gpo.parameters.gpos_vesting_lockin_period(); + p.vesting_duration_seconds = gpo.parameters.gpos_subperiod(); + obj.policy = p; + } + else { + op.policy.visit(init_policy_visitor(obj.policy, op.amount.amount, now)); + } obj.balance_type = op.balance_type; - op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); return vbo.id; } FC_CAPTURE_AND_RETHROW( (op) ) } +operation_result vesting_balance_withdraw_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) +{ try { + trx_state = &eval_state; + const auto& oper = op.get(); + + //check_required_authorities(op); + auto result = evaluate( oper ); + + if( apply ) result = this->apply( oper ); + return result; +} FC_CAPTURE_AND_RETHROW() } + void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balance_withdraw_operation& op ) { try { const database& d = db(); const time_point_sec now = d.head_block_time(); const vesting_balance_object& vbo = op.vesting_balance( d ); - FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) ); - FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) ); - assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached + if(vbo.balance_type == vesting_balance_type::normal) + { + FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) ); + FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "Account has insufficient ${balance_type} Vested Balance to withdraw", + ("balance_type", get_vesting_balance_type(vbo.balance_type))("now", now)("op", op)("vbo", vbo) ); + assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached + } + else if(now > HARDFORK_GPOS_TIME && vbo.balance_type == vesting_balance_type::gpos) + { + const account_id_type account_id = op.owner; + vector vbos; + auto vesting_range = d.get_index_type().indices().get().equal_range(account_id); + std::for_each(vesting_range.first, vesting_range.second, + [&vbos, now](const vesting_balance_object& balance) { + if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos + && balance.is_withdraw_allowed(now, balance.balance.amount) && balance.balance.asset_id == asset_id_type()) + vbos.emplace_back(balance); + }); - /* const account_object& owner_account = */ op.owner( d ); + asset total_amount; + for (const vesting_balance_object& vesting_balance_obj : vbos) + { + total_amount += vesting_balance_obj.balance.amount; + } + FC_ASSERT( op.amount <= total_amount, "Account has either insufficient GPOS Vested Balance or lock-in period is not matured"); + } + + /* const account_object& owner_account = op.owner( d ); */ // TODO: Check asset authorizations and withdrawals return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -127,22 +177,55 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_withdraw_operation& op ) { try { database& d = db(); + const time_point_sec now = d.head_block_time(); - + //Handling all GPOS withdrawls separately from normal and SONs(future extension). + // One request/transaction would be sufficient to withdraw from multiple vesting balance ids const vesting_balance_object& vbo = op.vesting_balance( d ); - - // Allow zero balance objects to stick around, (1) to comply - // with the chain's "objects live forever" design principle, (2) - // if it's cashback or worker, it'll be filled up again. - - d.modify( vbo, [&]( vesting_balance_object& vbo ) + if(vbo.balance_type == vesting_balance_type::normal) { - vbo.withdraw( now, op.amount ); - } ); + // Allow zero balance objects to stick around, (1) to comply + // with the chain's "objects live forever" design principle, (2) + // if it's cashback or worker, it'll be filled up again. - d.adjust_balance( op.owner, op.amount ); + d.modify( vbo, [&]( vesting_balance_object& vbo ) + { + vbo.withdraw( now, op.amount ); + } ); + + d.adjust_balance( op.owner, op.amount ); + } + else if(now > HARDFORK_GPOS_TIME && vbo.balance_type == vesting_balance_type::gpos) + { + const account_id_type account_id = op.owner; + vector ids; + auto vesting_range = d.get_index_type().indices().get().equal_range(account_id); + std::for_each(vesting_range.first, vesting_range.second, + [&ids, now](const vesting_balance_object& balance) { + if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos + && balance.is_withdraw_allowed(now, balance.balance.amount) && balance.balance.asset_id == asset_id_type()) + ids.emplace_back(balance.id); + }); + + asset total_withdraw_amount = op.amount; + for (const vesting_balance_id_type& id : ids) + { + const vesting_balance_object& vbo = id( d ); + if(total_withdraw_amount.amount > vbo.balance.amount) + { + total_withdraw_amount.amount -= vbo.balance.amount; + d.adjust_balance( op.owner, vbo.balance ); + d.modify( vbo, [&]( vesting_balance_object& vbo ) {vbo.withdraw( now, vbo.balance );} ); + } + else + { + d.modify( vbo, [&]( vesting_balance_object& vbo ) {vbo.withdraw( now, total_withdraw_amount );} ); + d.adjust_balance( op.owner, total_withdraw_amount); + break; + } + } + } - // TODO: Check asset authorizations and withdrawals return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 742482ce..3334d4f6 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -24,6 +24,8 @@ #include +#include + namespace graphene { namespace chain { inline bool sum_below_max_shares(const asset& a, const asset& b) @@ -45,23 +47,33 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& if( elapsed_seconds >= vesting_cliff_seconds ) { - share_type total_vested = 0; - if( elapsed_seconds < vesting_duration_seconds ) + // BLOCKBACK-154 fix, Begin balance for linear vesting applies only to initial account balance from genesis + // So, for any GPOS vesting, the begin balance would be 0 and should be able to withdraw balance amount based on lockin period + if(begin_balance == 0) { - total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); + allowed_withdraw = ctx.balance.amount; + return asset( allowed_withdraw, ctx.balance.asset_id ); } else { - total_vested = begin_balance; + share_type total_vested = 0; + if( elapsed_seconds < vesting_duration_seconds ) + { + total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); + } + else + { + total_vested = begin_balance; + } + assert( total_vested >= 0 ); + + const share_type withdrawn_already = begin_balance - ctx.balance.amount; + assert( withdrawn_already >= 0 ); + + allowed_withdraw = total_vested - withdrawn_already; + assert( allowed_withdraw >= 0 ); } - assert( total_vested >= 0 ); - - const share_type withdrawn_already = begin_balance - ctx.balance.amount; - assert( withdrawn_already >= 0 ); - - allowed_withdraw = total_vested - withdrawn_already; - assert( allowed_withdraw >= 0 ); - } + } } return asset( allowed_withdraw, ctx.balance.asset_id ); @@ -265,3 +277,7 @@ asset vesting_balance_object::get_allowed_withdraw(const time_point_sec& now)con } } } // graphene::chain + +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::linear_vesting_policy ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::cdd_vesting_policy ) +GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_object ) diff --git a/libraries/chain/worker_evaluator.cpp b/libraries/chain/worker_evaluator.cpp index cf6f0e00..b5aea8f3 100644 --- a/libraries/chain/worker_evaluator.cpp +++ b/libraries/chain/worker_evaluator.cpp @@ -106,7 +106,7 @@ object_id_type worker_create_evaluator::do_apply(const worker_create_evaluator:: void refund_worker_type::pay_worker(share_type pay, database& db) { total_burned += pay; - db.modify(db.get(asset_id_type()).dynamic_data(db), [pay](asset_dynamic_data_object& d) { + db.modify( db.get_core_dynamic_data(), [pay](asset_dynamic_data_object& d) { d.current_supply -= pay; }); } diff --git a/libraries/egenesis/egenesis_none.cpp b/libraries/egenesis/egenesis_none.cpp index 825f7f83..c7a0dcdd 100644 --- a/libraries/egenesis/egenesis_none.cpp +++ b/libraries/egenesis/egenesis_none.cpp @@ -24,6 +24,8 @@ #include +#include + namespace graphene { namespace egenesis { using namespace graphene::chain; diff --git a/libraries/fc b/libraries/fc index f13d0632..6096e94e 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit f13d0632b08b9983a275304317a033914938e339 +Subproject commit 6096e94e1b4c48a393c9335580365df144f2758f diff --git a/libraries/net/CMakeLists.txt b/libraries/net/CMakeLists.txt index f7f549ed..82522e5a 100644 --- a/libraries/net/CMakeLists.txt +++ b/libraries/net/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOURCES node.cpp core_messages.cpp peer_database.cpp peer_connection.cpp + message.cpp message_oriented_connection.cpp) add_library( graphene_net ${SOURCES} ${HEADERS} ) diff --git a/libraries/net/include/graphene/net/message.hpp b/libraries/net/include/graphene/net/message.hpp index cfef1519..686fea24 100644 --- a/libraries/net/include/graphene/net/message.hpp +++ b/libraries/net/include/graphene/net/message.hpp @@ -22,12 +22,16 @@ * THE SOFTWARE. */ #pragma once +#include + +#include + #include #include #include -#include +#include #include -#include +#include namespace graphene { namespace net { @@ -108,10 +112,10 @@ namespace graphene { namespace net { } }; - - - } } // graphene::net FC_REFLECT( graphene::net::message_header, (size)(msg_type) ) FC_REFLECT_DERIVED( graphene::net::message, (graphene::net::message_header), (data) ) + +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::message_header) +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::message) diff --git a/libraries/net/include/graphene/net/peer_connection.hpp b/libraries/net/include/graphene/net/peer_connection.hpp index 6f9a4b20..61f1cef5 100644 --- a/libraries/net/include/graphene/net/peer_connection.hpp +++ b/libraries/net/include/graphene/net/peer_connection.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -35,9 +34,7 @@ #include #include #include -#include #include -#include #include #include @@ -264,13 +261,13 @@ namespace graphene { namespace net fc::future accept_or_connect_task_done; firewall_check_state_data *firewall_check_state = nullptr; -#ifndef NDEBUG + private: +#ifndef NDEBUG fc::thread* _thread = nullptr; unsigned _send_message_queue_tasks_running = 0; // temporary debugging #endif bool _currently_handling_message = false; // true while we're in the middle of handling a message from the remote system - private: peer_connection(peer_connection_delegate* delegate); void destroy(); public: diff --git a/libraries/net/include/graphene/net/peer_database.hpp b/libraries/net/include/graphene/net/peer_database.hpp index d0a06dd9..ff7f4036 100644 --- a/libraries/net/include/graphene/net/peer_database.hpp +++ b/libraries/net/include/graphene/net/peer_database.hpp @@ -24,13 +24,14 @@ #pragma once #include +#include + #include #include #include #include #include #include -#include namespace graphene { namespace net { @@ -118,5 +119,6 @@ namespace graphene { namespace net { } } // end namespace graphene::net -FC_REFLECT_ENUM(graphene::net::potential_peer_last_connection_disposition, (never_attempted_to_connect)(last_connection_failed)(last_connection_rejected)(last_connection_handshaking_failed)(last_connection_succeeded)) -FC_REFLECT(graphene::net::potential_peer_record, (endpoint)(last_seen_time)(last_connection_disposition)(last_connection_attempt_time)(number_of_successful_connection_attempts)(number_of_failed_connection_attempts)(last_error) ) +FC_REFLECT_TYPENAME( graphene::net::potential_peer_record ) + +GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::potential_peer_record) diff --git a/libraries/net/message.cpp b/libraries/net/message.cpp new file mode 100644 index 00000000..6d35bfe5 --- /dev/null +++ b/libraries/net/message.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 BitShares Blockchain 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 + +#include + +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::message_header) +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::message) diff --git a/libraries/net/message_oriented_connection.cpp b/libraries/net/message_oriented_connection.cpp index 5dea08d4..1bc1832e 100644 --- a/libraries/net/message_oriented_connection.cpp +++ b/libraries/net/message_oriented_connection.cpp @@ -62,7 +62,8 @@ namespace graphene { namespace net { fc::time_point _last_message_received_time; fc::time_point _last_message_sent_time; - bool _send_message_in_progress; + std::atomic_bool _send_message_in_progress; + std::atomic_bool _read_loop_in_progress; #ifndef NDEBUG fc::thread* _thread; #endif @@ -98,7 +99,8 @@ namespace graphene { namespace net { _delegate(delegate), _bytes_received(0), _bytes_sent(0), - _send_message_in_progress(false) + _send_message_in_progress(false), + _read_loop_in_progress(false) #ifndef NDEBUG ,_thread(&fc::thread::current()) #endif @@ -138,6 +140,21 @@ namespace graphene { namespace net { _sock.bind(local_endpoint); } + class no_parallel_execution_guard final + { + std::atomic_bool* _flag; + public: + explicit no_parallel_execution_guard(std::atomic_bool* flag) : _flag(flag) + { + bool expected = false; + FC_ASSERT( flag->compare_exchange_strong( expected, true ), "Only one thread at time can visit it"); + } + ~no_parallel_execution_guard() + { + *_flag = false; + } + }; + void message_oriented_connection_impl::read_loop() { VERIFY_CORRECT_THREAD(); @@ -145,6 +162,7 @@ namespace graphene { namespace net { const int LEFTOVER = BUFFER_SIZE - sizeof(message_header); static_assert(BUFFER_SIZE >= sizeof(message_header), "insufficient buffer"); + no_parallel_execution_guard guard( &_read_loop_in_progress ); _connected_time = fc::time_point::now(); fc::oexception exception_to_rethrow; @@ -241,17 +259,7 @@ namespace graphene { namespace net { } send_message_scope_logger(remote_endpoint); #endif #endif - struct verify_no_send_in_progress { - bool& var; - verify_no_send_in_progress(bool& var) : var(var) - { - if (var) - elog("Error: two tasks are calling message_oriented_connection::send_message() at the same time"); - assert(!var); - var = true; - } - ~verify_no_send_in_progress() { var = false; } - } _verify_no_send_in_progress(_send_message_in_progress); + no_parallel_execution_guard guard( &_send_message_in_progress ); try { diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index a38199fd..0fc61dde 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -1249,7 +1250,7 @@ namespace graphene { namespace net { namespace detail { for (const peer_connection_ptr& peer : _active_connections) { // only advertise to peers who are in sync with us - wdump((peer->peer_needs_sync_items_from_us)); + idump((peer->peer_needs_sync_items_from_us)); if( !peer->peer_needs_sync_items_from_us ) { std::map > items_to_advertise_by_type; @@ -1257,7 +1258,7 @@ namespace graphene { namespace net { namespace detail { // or anything it has advertised to us // group the items we need to send by type, because we'll need to send one inventory message per type unsigned total_items_to_send_to_this_peer = 0; - wdump((inventory_to_advertise)); + idump((inventory_to_advertise)); for (const item_id& item_to_advertise : inventory_to_advertise) { auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise); @@ -1276,9 +1277,9 @@ namespace graphene { namespace net { namespace detail { else { if (adv_to_peer != peer->inventory_advertised_to_peer.end() ) - wdump( (*adv_to_peer) ); + idump( (*adv_to_peer) ); if (adv_to_us != peer->inventory_peer_advertised_to_us.end() ) - wdump( (*adv_to_us) ); + idump( (*adv_to_us) ); } } dlog("advertising ${count} new item(s) of ${types} type(s) to peer ${endpoint}", @@ -2278,7 +2279,7 @@ namespace graphene { namespace net { namespace detail { bool disconnect_from_inhibited_peer = false; // if our client doesn't have any items after the item the peer requested, it will send back // a list containing the last item the peer requested - wdump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis)); + idump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis)); if( reply_message.item_hashes_available.empty() ) originating_peer->peer_needs_sync_items_from_us = false; /* I have no items in my blockchain */ else if( !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() && @@ -2649,11 +2650,6 @@ namespace graphene { namespace net { namespace detail { if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty()) assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back()); - // append the remaining items to the peer's list - boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received); - - originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count; - // at any given time, there's a maximum number of blocks that can possibly be out there // [(now - genesis time) / block interval]. If they offer us more blocks than that, // they must be an attacker or have a buggy client. @@ -2676,6 +2672,12 @@ namespace graphene { namespace net { namespace detail { return; } + + // append the remaining items to the peer's list + boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received); + + originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count; + uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers(); if (new_number_of_unfetched_items != _total_number_of_unfetched_items) _delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type, @@ -2935,7 +2937,7 @@ namespace graphene { namespace net { namespace detail { if( closing_connection_message_received.closing_due_to_error ) { - elog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", + wlog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", ( "peer", originating_peer->get_remote_endpoint() ) ( "msg", closing_connection_message_received.reason_for_closing ) ( "error", closing_connection_message_received.error ) ); diff --git a/libraries/net/peer_connection.cpp b/libraries/net/peer_connection.cpp index f1f20d3f..9b753e6c 100644 --- a/libraries/net/peer_connection.cpp +++ b/libraries/net/peer_connection.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -260,7 +261,7 @@ namespace graphene { namespace net } catch ( fc::exception& e ) { - elog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) ); + wlog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) ); throw; } } // connect_to() @@ -312,24 +313,24 @@ namespace graphene { namespace net } catch (const fc::exception& send_error) { - elog("Error sending message: ${exception}. Closing connection.", ("exception", send_error)); + wlog("Error sending message: ${exception}. Closing connection.", ("exception", send_error)); try { close_connection(); } catch (const fc::exception& close_error) { - elog("Caught error while closing connection: ${exception}", ("exception", close_error)); + wlog("Caught error while closing connection: ${exception}", ("exception", close_error)); } return; } catch (const std::exception& e) { - elog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what())); + wlog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what())); } catch (...) { - elog("message_oriented_exception::send_message() threw an unhandled exception"); + wlog("message_oriented_exception::send_message() threw an unhandled exception"); } _queued_messages.front()->transmission_finish_time = fc::time_point::now(); _total_queued_messages_size -= _queued_messages.front()->get_size_in_queue(); @@ -345,7 +346,7 @@ namespace graphene { namespace net _queued_messages.emplace(std::move(message_to_send)); if (_total_queued_messages_size > GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES) { - elog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)", + wlog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)", ("max", GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)("current", _total_queued_messages_size)); try { diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 2b20364e..76ae9c8c 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -274,3 +274,14 @@ namespace graphene { namespace net { } } } // end namespace graphene::net + +FC_REFLECT_ENUM( graphene::net::potential_peer_last_connection_disposition, + (never_attempted_to_connect) + (last_connection_failed)(last_connection_rejected) + (last_connection_handshaking_failed)(last_connection_succeeded) ) +FC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::potential_peer_record, BOOST_PP_SEQ_NIL, + (endpoint)(last_seen_time)(last_connection_disposition) + (last_connection_attempt_time)(number_of_successful_connection_attempts) + (number_of_failed_connection_attempts)(last_error) ) + +GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::potential_peer_record) diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index fb944627..d2a5be16 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory( witness ) add_subdirectory( account_history ) add_subdirectory( accounts_list ) add_subdirectory( affiliate_stats ) +add_subdirectory( elasticsearch ) add_subdirectory( market_history ) add_subdirectory( delayed_node ) add_subdirectory( bookie ) @@ -10,3 +11,4 @@ add_subdirectory( generate_uia_sharedrop_genesis ) add_subdirectory( debug_witness ) add_subdirectory( snapshot ) add_subdirectory( peerplays_sidechain ) +add_subdirectory( es_objects ) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 81acb01e..67322f80 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include @@ -128,8 +128,8 @@ void account_history_plugin_impl::update_account_histories( const signed_block& if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - graphene::app::operation_get_impacted_accounts( op.op, impacted ); - if( op.op.which() == operation::tag< lottery_end_operation >::value ) + graphene::chain::operation_get_impacted_accounts( op.op, impacted ); + if( op.op.which() == operation::tag< lottery_end_operation >::value ) { auto lop = op.op.get< lottery_end_operation >(); auto asset_object = lop.lottery( db ); @@ -137,6 +137,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block& for( auto benefactor : asset_object.lottery_options->benefactors ) impacted.insert( benefactor.id ); } + for( auto& a : other ) for( auto& item : a.account_auths ) impacted.insert( item.first ); diff --git a/libraries/plugins/accounts_list/accounts_list_plugin.cpp b/libraries/plugins/accounts_list/accounts_list_plugin.cpp index aabf711d..757891ea 100644 --- a/libraries/plugins/accounts_list/accounts_list_plugin.cpp +++ b/libraries/plugins/accounts_list/accounts_list_plugin.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include diff --git a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp index 438b1aca..da9c8a04 100644 --- a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp +++ b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index f15ac2f7..261de241 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -370,8 +370,8 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) assert(bet_iter != persistent_bets_by_bet_id.end()); if (bet_iter != persistent_bets_by_bet_id.end()) { - ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations", - ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id)); + // ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations", + // ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id)); if (is_operation_history_object_stored(op.id)) db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { obj.associated_operations.emplace_back(op.id); @@ -386,8 +386,8 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) assert(bet_iter != persistent_bets_by_bet_id.end()); if (bet_iter != persistent_bets_by_bet_id.end()) { - ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations", - ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id)); + // ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations", + // ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id)); if (is_operation_history_object_stored(op.id)) db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { obj.associated_operations.emplace_back(op.id); diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 3eadda34..d49129b0 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -65,7 +65,7 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c void delayed_node_plugin::connect() { - my->client_connection = std::make_shared(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); + my->client_connection = std::make_shared(*my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); my->database_api = my->client_connection->get_remote_api(0); my->client_connection_closed = my->client_connection->closed.connect([this] { connection_failed(); diff --git a/libraries/plugins/elasticsearch/CMakeLists.txt b/libraries/plugins/elasticsearch/CMakeLists.txt new file mode 100644 index 00000000..f4815576 --- /dev/null +++ b/libraries/plugins/elasticsearch/CMakeLists.txt @@ -0,0 +1,23 @@ +file(GLOB HEADERS "include/graphene/elasticsearch/*.hpp") + +add_library( graphene_elasticsearch + elasticsearch_plugin.cpp + ) + +target_link_libraries( graphene_elasticsearch graphene_chain graphene_app curl ) +target_include_directories( graphene_elasticsearch + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties(elasticsearch_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) + +install( TARGETS + graphene_elasticsearch + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/elasticsearch" ) + diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp new file mode 100644 index 00000000..484aef9c --- /dev/null +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2017 Cryptonomex, Inc., 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 elasticsearch { + +namespace detail +{ + +class elasticsearch_plugin_impl +{ + public: + elasticsearch_plugin_impl(elasticsearch_plugin& _plugin) + : _self( _plugin ) + { curl = curl_easy_init(); } + virtual ~elasticsearch_plugin_impl(); + + bool update_account_histories( const signed_block& b ); + + graphene::chain::database& database() + { + return _self.database(); + } + + elasticsearch_plugin& _self; + primary_index< operation_history_index >* _oho_index; + + std::string _elasticsearch_node_url = "http://localhost:9200/"; + uint32_t _elasticsearch_bulk_replay = 10000; + uint32_t _elasticsearch_bulk_sync = 100; + bool _elasticsearch_visitor = false; + std::string _elasticsearch_basic_auth = ""; + std::string _elasticsearch_index_prefix = "peerplays-"; + bool _elasticsearch_operation_object = false; + uint32_t _elasticsearch_start_es_after_block = 0; + bool _elasticsearch_operation_string = true; + mode _elasticsearch_mode = mode::only_save; + CURL *curl; // curl handler + vector bulk_lines; // vector of op lines + vector prepare; + + graphene::utilities::ES es; + uint32_t limit_documents; + int16_t op_type; + operation_history_struct os; + block_struct bs; + visitor_struct vs; + bulk_struct bulk_line_struct; + std::string bulk_line; + std::string index_name; + bool is_sync = false; + fc::time_point last_sync; + private: + 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); + const account_statistics_object& getStatsObject(const account_id_type account_id); + void growStats(const account_statistics_object& stats_obj, const account_transaction_history_object& ath); + void getOperationType(const optional & oho); + void doOperationHistory(const optional & oho); + void doBlock(const optional & oho, const signed_block& b); + void doVisitor(const optional & oho); + void checkState(const fc::time_point_sec& block_time); + void cleanObjects(const account_transaction_history_object& ath, account_id_type account_id); + void createBulkLine(const account_transaction_history_object& ath); + void prepareBulk(const account_transaction_history_id_type& ath_id); + void populateESstruct(); +}; + +elasticsearch_plugin_impl::~elasticsearch_plugin_impl() +{ + if (curl) { + curl_easy_cleanup(curl); + curl = nullptr; + } + return; +} + +bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b ) +{ + checkState(b.timestamp); + index_name = graphene::utilities::generateIndexName(b.timestamp, _elasticsearch_index_prefix); + + graphene::chain::database& db = database(); + const vector >& hist = db.get_applied_operations(); + bool is_first = true; + auto skip_oho_id = [&is_first,&db,this]() { + if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo + { + db.remove( db.create( []( operation_history_object& obj) {} ) ); + is_first = false; + } + else + _oho_index->use_next_id(); + }; + for( const optional< operation_history_object >& o_op : hist ) { + optional oho; + + auto create_oho = [&]() { + is_first = false; + return optional( + db.create([&](operation_history_object &h) { + if (o_op.valid()) + { + h.op = o_op->op; + h.result = o_op->result; + h.block_num = o_op->block_num; + h.trx_in_block = o_op->trx_in_block; + h.op_in_trx = o_op->op_in_trx; + h.virtual_op = o_op->virtual_op; + } + })); + }; + + if( !o_op.valid() ) { + skip_oho_id(); + continue; + } + oho = create_oho(); + + // populate what we can before impacted loop + getOperationType(oho); + doOperationHistory(oho); + doBlock(oho, b); + if(_elasticsearch_visitor) + doVisitor(oho); + + const operation_history_object& op = *o_op; + + // get the set of accounts this operation applies to + flat_set impacted; + vector other; + operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here + + if( op.op.which() == operation::tag< account_create_operation >::value ) + impacted.insert( op.result.get() ); + else + graphene::chain::operation_get_impacted_accounts( op.op, impacted ); + + for( auto& a : other ) + for( auto& item : a.account_auths ) + impacted.insert( item.first ); + + for( auto& account_id : impacted ) + { + if(!add_elasticsearch( account_id, oho, b.block_num() )) + return false; + } + } + // we send bulk at end of block when we are in sync for better real time client experience + if(is_sync) + { + populateESstruct(); + if(es.bulk_lines.size() > 0) + { + prepare.clear(); + if(!graphene::utilities::SendBulk(es)) + return false; + else + bulk_lines.clear(); + } + } + + return true; +} + +void elasticsearch_plugin_impl::checkState(const fc::time_point_sec& block_time) +{ + fc::time_point current_time(fc::time_point::now()); + if(((current_time - block_time) < fc::seconds(30)) || (current_time - last_sync > fc::seconds(60))) + { + limit_documents = _elasticsearch_bulk_sync; + is_sync = true; + last_sync = current_time; + } + else + { + limit_documents = _elasticsearch_bulk_replay; + is_sync = false; + } +} + +void elasticsearch_plugin_impl::getOperationType(const optional & oho) +{ + if (!oho->id.is_null()) + op_type = oho->op.which(); +} + +void elasticsearch_plugin_impl::doOperationHistory(const optional & oho) +{ + os.trx_in_block = oho->trx_in_block; + os.op_in_trx = oho->op_in_trx; + os.operation_result = fc::json::to_string(oho->result); + os.virtual_op = oho->virtual_op; + + if(_elasticsearch_operation_object) { + oho->op.visit(fc::from_static_variant(os.op_object, FC_PACK_MAX_DEPTH)); + adaptor_struct adaptor; + os.op_object = adaptor.adapt(os.op_object.get_object()); + } + if(_elasticsearch_operation_string) + os.op = fc::json::to_string(oho->op); +} + +void elasticsearch_plugin_impl::doBlock(const optional & oho, const signed_block& b) +{ + std::string trx_id = ""; + if(oho->trx_in_block < b.transactions.size()) + trx_id = b.transactions[oho->trx_in_block].id().str(); + bs.block_num = b.block_num(); + bs.block_time = b.timestamp; + bs.trx_id = trx_id; +} + +void elasticsearch_plugin_impl::doVisitor(const optional & oho) +{ + operation_visitor o_v; + oho->op.visit(o_v); + + vs.fee_data.asset = o_v.fee_asset; + vs.fee_data.amount = o_v.fee_amount; + + vs.transfer_data.asset = o_v.transfer_asset_id; + vs.transfer_data.amount = o_v.transfer_amount; + vs.transfer_data.from = o_v.transfer_from; + vs.transfer_data.to = o_v.transfer_to; + + vs.fill_data.order_id = o_v.fill_order_id; + vs.fill_data.account_id = o_v.fill_account_id; + vs.fill_data.pays_asset_id = o_v.fill_pays_asset_id; + vs.fill_data.pays_amount = o_v.fill_pays_amount; + vs.fill_data.receives_asset_id = o_v.fill_receives_asset_id; + vs.fill_data.receives_amount = o_v.fill_receives_amount; + //vs.fill_data.fill_price = o_v.fill_fill_price; + //vs.fill_data.is_maker = o_v.fill_is_maker; +} + +bool elasticsearch_plugin_impl::add_elasticsearch( const account_id_type account_id, + 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); + if(block_number > _elasticsearch_start_es_after_block) { + createBulkLine(ath); + prepareBulk(ath.id); + } + cleanObjects(ath, account_id); + + if (curl && bulk_lines.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech + prepare.clear(); + populateESstruct(); + if(!graphene::utilities::SendBulk(es)) + return false; + else + bulk_lines.clear(); + } + + return true; +} + +const account_statistics_object& elasticsearch_plugin_impl::getStatsObject(const account_id_type account_id) +{ + graphene::chain::database& db = database(); + const auto &acct = db.get(account_id); + return acct.statistics(db); +} + +const account_transaction_history_object& elasticsearch_plugin_impl::addNewEntry(const account_statistics_object& stats_obj, + const account_id_type account_id, + const optional & oho) +{ + graphene::chain::database& db = database(); + const auto &ath = db.create([&](account_transaction_history_object &obj) { + obj.operation_id = oho->id; + obj.account = account_id; + obj.sequence = stats_obj.total_ops + 1; + obj.next = stats_obj.most_recent_op; + }); + + return ath; +} + +void elasticsearch_plugin_impl::growStats(const account_statistics_object& stats_obj, + const account_transaction_history_object& ath) +{ + graphene::chain::database& db = database(); + db.modify(stats_obj, [&](account_statistics_object &obj) { + obj.most_recent_op = ath.id; + obj.total_ops = ath.sequence; + }); +} + +void elasticsearch_plugin_impl::createBulkLine(const account_transaction_history_object& ath) +{ + bulk_line_struct.account_history = ath; + bulk_line_struct.operation_history = os; + bulk_line_struct.operation_type = op_type; + bulk_line_struct.operation_id_num = ath.operation_id.instance.value; + bulk_line_struct.block_data = bs; + if(_elasticsearch_visitor) + bulk_line_struct.additional_data = vs; + bulk_line = fc::json::to_string(bulk_line_struct); +} + +void elasticsearch_plugin_impl::prepareBulk(const account_transaction_history_id_type& ath_id) +{ + const std::string _id = fc::json::to_string(ath_id); + fc::mutable_variant_object bulk_header; + bulk_header["_index"] = index_name; + bulk_header["_type"] = "data"; + bulk_header["_id"] = fc::to_string(ath_id.space_id) + "." + fc::to_string(ath_id.type_id) + "." + ath_id.instance; + prepare = graphene::utilities::createBulk(bulk_header, bulk_line); + bulk_lines.insert(bulk_lines.end(), prepare.begin(), prepare.end()); +} + +void elasticsearch_plugin_impl::cleanObjects(const account_transaction_history_object& ath, account_id_type account_id) +{ + graphene::chain::database& db = database(); + // remove everything except current object from ath + const auto &his_idx = db.get_index_type(); + const auto &by_seq_idx = his_idx.indices().get(); + auto itr = by_seq_idx.lower_bound(boost::make_tuple(account_id, 0)); + if (itr != by_seq_idx.end() && itr->account == account_id && itr->id != ath.id) { + // if found, remove the entry + const auto remove_op_id = itr->operation_id; + const auto itr_remove = itr; + ++itr; + db.remove( *itr_remove ); + // modify previous node's next pointer + // this should be always true, but just have a check here + if( itr != by_seq_idx.end() && itr->account == account_id ) + { + db.modify( *itr, [&]( account_transaction_history_object& obj ){ + obj.next = account_transaction_history_id_type(); + }); + } + // do the same on oho + const auto &by_opid_idx = his_idx.indices().get(); + if (by_opid_idx.find(remove_op_id) == by_opid_idx.end()) { + db.remove(remove_op_id(db)); + } + } +} + +void elasticsearch_plugin_impl::populateESstruct() +{ + es.curl = curl; + es.bulk_lines = bulk_lines; + es.elasticsearch_url = _elasticsearch_node_url; + es.auth = _elasticsearch_basic_auth; +} + +} // end namespace detail + +elasticsearch_plugin::elasticsearch_plugin() : + my( new detail::elasticsearch_plugin_impl(*this) ) +{ +} + +elasticsearch_plugin::~elasticsearch_plugin() +{ +} + +std::string elasticsearch_plugin::plugin_name()const +{ + return "elasticsearch"; +} +std::string elasticsearch_plugin::plugin_description()const +{ + return "Stores account history data in elasticsearch database(EXPERIMENTAL)."; +} + +void elasticsearch_plugin::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg + ) +{ + cli.add_options() + ("elasticsearch-node-url", boost::program_options::value(), + "Elastic Search database node url(http://localhost:9200/)") + ("elasticsearch-bulk-replay", boost::program_options::value(), + "Number of bulk documents to index on replay(10000)") + ("elasticsearch-bulk-sync", boost::program_options::value(), + "Number of bulk documents to index on a syncronied chain(100)") + ("elasticsearch-visitor", boost::program_options::value(), + "Use visitor to index additional data(slows down the replay(false))") + ("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(peerplays-)") + ("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)") + ("elasticsearch-operation-string", boost::program_options::value(), + "Save operation as string. Needed to serve history api calls(true)") + ("elasticsearch-mode", boost::program_options::value(), + "Mode of operation: only_save(0), only_query(1), all(2) - Default: 0") + ; + cfg.add(cli); +} + +void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + my->_oho_index = database().add_index< primary_index< operation_history_index > >(); + database().add_index< primary_index< account_transaction_history_index > >(); + + if (options.count("elasticsearch-node-url")) { + my->_elasticsearch_node_url = options["elasticsearch-node-url"].as(); + } + if (options.count("elasticsearch-bulk-replay")) { + my->_elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as(); + } + if (options.count("elasticsearch-bulk-sync")) { + my->_elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as(); + } + if (options.count("elasticsearch-visitor")) { + my->_elasticsearch_visitor = options["elasticsearch-visitor"].as(); + } + if (options.count("elasticsearch-basic-auth")) { + my->_elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as(); + } + if (options.count("elasticsearch-index-prefix")) { + my->_elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as(); + } + 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(); + } + if (options.count("elasticsearch-operation-string")) { + my->_elasticsearch_operation_string = options["elasticsearch-operation-string"].as(); + } + if (options.count("elasticsearch-mode")) { + const auto option_number = options["elasticsearch-mode"].as(); + if(option_number > mode::all) + FC_THROW_EXCEPTION(fc::exception, "Elasticsearch mode not valid"); + my->_elasticsearch_mode = static_cast(options["elasticsearch-mode"].as()); + } + + if(my->_elasticsearch_mode != mode::only_query) { + if (my->_elasticsearch_mode == mode::all && !my->_elasticsearch_operation_string) + FC_THROW_EXCEPTION(fc::exception, + "If elasticsearch-mode is set to all then elasticsearch-operation-string need to be true"); + + database().applied_block.connect([this](const signed_block &b) { + if (!my->update_account_histories(b)) + FC_THROW_EXCEPTION(fc::exception, + "Error populating ES database, we are going to keep trying."); + }); + } +} + +void elasticsearch_plugin::plugin_startup() +{ + graphene::utilities::ES es; + es.curl = my->curl; + es.elasticsearch_url = my->_elasticsearch_node_url; + es.auth = my->_elasticsearch_basic_auth; + + if(!graphene::utilities::checkES(es)) + FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_elasticsearch_node_url)); + ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() begin"); +} + +operation_history_object elasticsearch_plugin::get_operation_by_id(operation_history_id_type id) +{ + const string operation_id_string = std::string(object_id_type(id)); + + const string query = R"( + { + "query": { + "match": + { + "account_history.operation_id": )" + operation_id_string + R"(" + } + } + } + )"; + + auto es = prepareHistoryQuery(query); + const auto response = graphene::utilities::simpleQuery(es); + variant variant_response = fc::json::from_string(response); + const auto source = variant_response["hits"]["hits"][size_t(0)]["_source"]; + return fromEStoOperation(source); +} + +vector elasticsearch_plugin::get_account_history( + const account_id_type account_id, + operation_history_id_type stop = operation_history_id_type(), + unsigned limit = 100, + operation_history_id_type start = operation_history_id_type()) +{ + const string account_id_string = std::string(object_id_type(account_id)); + + const auto stop_number = stop.instance.value; + const auto start_number = start.instance.value; + + string range = ""; + if(stop_number == 0) + range = " AND operation_id_num: ["+fc::to_string(stop_number)+" TO "+fc::to_string(start_number)+"]"; + else if(stop_number > 0) + range = " AND operation_id_num: {"+fc::to_string(stop_number)+" TO "+fc::to_string(start_number)+"]"; + + const string query = R"( + { + "size": )" + fc::to_string(limit) + R"(, + "sort" : [{ "operation_id_num" : {"order" : "desc"}}], + "query": { + "bool": { + "must": [ + { + "query_string": { + "query": "account_history.account: )" + account_id_string + range + R"(" + } + } + ] + } + } + } + )"; + + auto es = prepareHistoryQuery(query); + + vector result; + + if(!graphene::utilities::checkES(es)) + return result; + + const auto response = graphene::utilities::simpleQuery(es); + variant variant_response = fc::json::from_string(response); + + const auto hits = variant_response["hits"]["total"]["value"]; + uint32_t size; + if( hits.is_object() ) // ES-7 ? + size = static_cast(hits["value"].as_uint64()); + else // probably ES-6 + size = static_cast(hits.as_uint64()); + + size = std::min( size, limit ); + + for(unsigned i=0; i_elasticsearch_node_url; + es.index_prefix = my->_elasticsearch_index_prefix; + es.endpoint = es.index_prefix + "*/data/_search"; + es.query = query; + + return es; +} + +mode elasticsearch_plugin::get_running_mode() +{ + return my->_elasticsearch_mode; +} + + +} } diff --git a/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp b/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp new file mode 100644 index 00000000..01a83244 --- /dev/null +++ b/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2017 Cryptonomex, Inc., 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 + +namespace graphene { namespace elasticsearch { + using namespace chain; + +// +// Plugins should #define their SPACE_ID's so plugins with +// conflicting SPACE_ID assignments can be compiled into the +// same binary (by simply re-assigning some of the conflicting #defined +// SPACE_ID's in a build script). +// +// Assignment of SPACE_ID's cannot be done at run-time because +// various template automagic depends on them being known at compile +// time. +// +#ifndef ELASTICSEARCH_SPACE_ID +#define ELASTICSEARCH_SPACE_ID 6 +#endif + +namespace detail +{ + class elasticsearch_plugin_impl; +} + +enum mode { only_save = 0 , only_query = 1, all = 2 }; + +class elasticsearch_plugin : public graphene::app::plugin +{ + public: + elasticsearch_plugin(); + virtual ~elasticsearch_plugin(); + + std::string plugin_name()const override; + std::string plugin_description()const override; + virtual void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map& options) override; + virtual void plugin_startup() override; + + operation_history_object get_operation_by_id(operation_history_id_type id); + vector get_account_history(const account_id_type account_id, + operation_history_id_type stop, unsigned limit, operation_history_id_type start); + mode get_running_mode(); + + friend class detail::elasticsearch_plugin_impl; + std::unique_ptr my; + + private: + operation_history_object fromEStoOperation(variant source); + graphene::utilities::ES prepareHistoryQuery(string query); +}; + + +struct operation_visitor +{ + typedef void result_type; + + share_type fee_amount; + asset_id_type fee_asset; + + asset_id_type transfer_asset_id; + share_type transfer_amount; + account_id_type transfer_from; + account_id_type transfer_to; + + void operator()( const graphene::chain::transfer_operation& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + + transfer_asset_id = o.amount.asset_id; + transfer_amount = o.amount.amount; + transfer_from = o.from; + transfer_to = o.to; + } + + object_id_type fill_order_id; + account_id_type fill_account_id; + asset_id_type fill_pays_asset_id; + share_type fill_pays_amount; + asset_id_type fill_receives_asset_id; + share_type fill_receives_amount; + //double fill_fill_price; + //bool fill_is_maker; + + void operator()( const graphene::chain::fill_order_operation& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + + fill_order_id = o.order_id; + fill_account_id = o.account_id; + fill_pays_asset_id = o.pays.asset_id; + fill_pays_amount = o.pays.amount; + fill_receives_asset_id = o.receives.asset_id; + fill_receives_amount = o.receives.amount; + //fill_fill_price = o.fill_price.to_real(); + //fill_is_maker = o.is_maker; + } + + template + void operator()( const T& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + } +}; + +struct operation_history_struct { + int trx_in_block; + int op_in_trx; + std::string operation_result; + int virtual_op; + std::string op; + variant op_object; +}; + +struct block_struct { + int block_num; + fc::time_point_sec block_time; + std::string trx_id; +}; + +struct fee_struct { + asset_id_type asset; + share_type amount; +}; + +struct transfer_struct { + asset_id_type asset; + share_type amount; + account_id_type from; + account_id_type to; +}; + +struct fill_struct { + object_id_type order_id; + account_id_type account_id; + asset_id_type pays_asset_id; + share_type pays_amount; + asset_id_type receives_asset_id; + share_type receives_amount; + double fill_price; + bool is_maker; +}; + +struct visitor_struct { + fee_struct fee_data; + transfer_struct transfer_data; + fill_struct fill_data; +}; + +struct bulk_struct { + account_transaction_history_object account_history; + operation_history_struct operation_history; + int operation_type; + int operation_id_num; + block_struct block_data; + optional additional_data; +}; + +struct adaptor_struct { + variant adapt(const variant_object& op) + { + fc::mutable_variant_object o(op); + 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("memo") != o.end()) + { + auto& memo = o["memo"]; + if (memo.is_string()) + { + o["memo_"] = o["memo"]; + o.erase("memo"); + } + else if (memo.is_object()) + { + fc::mutable_variant_object tmp(memo.get_object()); + if (tmp.find("nonce") != tmp.end()) + { + tmp["nonce"] = tmp["nonce"].as_string(); + o["memo"] = tmp; + } + } + } + if (o.find("new_parameters") != o.end()) + { + auto& tmp = o["new_parameters"]; + if (tmp.is_object()) + { + fc::mutable_variant_object tmp2(tmp.get_object()); + if (tmp2.find("current_fees") != tmp2.end()) + { + tmp2.erase("current_fees"); + o["new_parameters"] = tmp2; + } + } + } + if (o.find("owner") != o.end() && o["owner"].is_string()) + { + o["owner_"] = o["owner"].as_string(); + o.erase("owner"); + } + if (o.find("proposed_ops") != o.end()) + { + o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]); + } + if (o.find("initializer") != o.end()) + { + o["initializer"] = fc::json::to_string(o["initializer"]); + } + + 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::elasticsearch + +FC_REFLECT_ENUM( graphene::elasticsearch::mode, (only_save)(only_query)(all) ) +FC_REFLECT( graphene::elasticsearch::operation_history_struct, (trx_in_block)(op_in_trx)(operation_result)(virtual_op)(op)(op_object) ) +FC_REFLECT( graphene::elasticsearch::block_struct, (block_num)(block_time)(trx_id) ) +FC_REFLECT( graphene::elasticsearch::fee_struct, (asset)(amount) ) +FC_REFLECT( graphene::elasticsearch::transfer_struct, (asset)(amount)(from)(to) ) +FC_REFLECT( graphene::elasticsearch::fill_struct, (order_id)(account_id)(pays_asset_id)(pays_amount)(receives_asset_id)(receives_amount)(fill_price)(is_maker)) +FC_REFLECT( graphene::elasticsearch::visitor_struct, (fee_data)(transfer_data)(fill_data) ) +FC_REFLECT( graphene::elasticsearch::bulk_struct, (account_history)(operation_history)(operation_type)(operation_id_num)(block_data)(additional_data) ) diff --git a/libraries/plugins/es_objects/CMakeLists.txt b/libraries/plugins/es_objects/CMakeLists.txt new file mode 100644 index 00000000..92e3d150 --- /dev/null +++ b/libraries/plugins/es_objects/CMakeLists.txt @@ -0,0 +1,23 @@ +file(GLOB HEADERS "include/graphene/es_objects/*.hpp") + +add_library( graphene_es_objects + es_objects.cpp + ) + +target_link_libraries( graphene_es_objects graphene_chain graphene_app curl ) +target_include_directories( graphene_es_objects + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties(es_objects.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) + +install( TARGETS + graphene_es_objects + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/es_objects" ) + diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp new file mode 100644 index 00000000..b9083cbc --- /dev/null +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2018 oxarbitrage, 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 +#include +#include +#include + +#include + +namespace graphene { namespace es_objects { + +namespace detail +{ + +class es_objects_plugin_impl +{ + public: + es_objects_plugin_impl(es_objects_plugin& _plugin) + : _self( _plugin ) + { curl = curl_easy_init(); } + virtual ~es_objects_plugin_impl(); + + bool index_database(const vector& ids, std::string action); + bool genesis(); + void remove_from_database(object_id_type id, std::string index); + + es_objects_plugin& _self; + std::string _es_objects_elasticsearch_url = "http://localhost:9200/"; + std::string _es_objects_auth = ""; + uint32_t _es_objects_bulk_replay = 10000; + uint32_t _es_objects_bulk_sync = 100; + bool _es_objects_proposals = true; + bool _es_objects_accounts = true; + bool _es_objects_assets = true; + bool _es_objects_balances = true; + bool _es_objects_limit_orders = true; + bool _es_objects_asset_bitasset = true; + std::string _es_objects_index_prefix = "ppobjects-"; + uint32_t _es_objects_start_es_after_block = 0; + CURL *curl; // curl handler + vector bulk; + vector prepare; + + bool _es_objects_keep_only_current = true; + + uint32_t block_number; + fc::time_point_sec block_time; + + private: + template + void prepareTemplate(T blockchain_object, string index_name); +}; + +bool es_objects_plugin_impl::genesis() +{ + + ilog("elasticsearch OBJECTS: inserting data from genesis"); + + graphene::chain::database &db = _self.database(); + + block_number = db.head_block_num(); + block_time = db.head_block_time(); + + if (_es_objects_accounts) { + auto &index_accounts = db.get_index(1, 2); + index_accounts.inspect_all_objects([this, &db](const graphene::db::object &o) { + auto obj = db.find_object(o.id); + auto a = static_cast(obj); + prepareTemplate(*a, "account"); + }); + } + if (_es_objects_assets) { + auto &index_assets = db.get_index(1, 3); + index_assets.inspect_all_objects([this, &db](const graphene::db::object &o) { + auto obj = db.find_object(o.id); + auto a = static_cast(obj); + prepareTemplate(*a, "asset"); + }); + } + if (_es_objects_balances) { + auto &index_balances = db.get_index(2, 5); + index_balances.inspect_all_objects([this, &db](const graphene::db::object &o) { + auto obj = db.find_object(o.id); + auto b = static_cast(obj); + prepareTemplate(*b, "balance"); + }); + } + + graphene::utilities::ES es; + es.curl = curl; + es.bulk_lines = bulk; + es.elasticsearch_url = _es_objects_elasticsearch_url; + es.auth = _es_objects_auth; + if (!graphene::utilities::SendBulk(es)) + FC_THROW_EXCEPTION(fc::exception, "Error inserting genesis data."); + else + bulk.clear(); + + return true; +} + +bool es_objects_plugin_impl::index_database(const vector& ids, std::string action) +{ + graphene::chain::database &db = _self.database(); + + block_time = db.head_block_time(); + block_number = db.head_block_num(); + + if(block_number > _es_objects_start_es_after_block) { + + // check if we are in replay or in sync and change number of bulk documents accordingly + uint32_t limit_documents = 0; + if ((fc::time_point::now() - block_time) < fc::seconds(30)) + limit_documents = _es_objects_bulk_sync; + else + limit_documents = _es_objects_bulk_replay; + + + for (auto const &value: ids) { + if (value.is() && _es_objects_proposals) { + auto obj = db.find_object(value); + auto p = static_cast(obj); + if (p != nullptr) { + if (action == "delete") + remove_from_database(p->id, "proposal"); + else + prepareTemplate(*p, "proposal"); + } + } else if (value.is() && _es_objects_accounts) { + auto obj = db.find_object(value); + auto a = static_cast(obj); + if (a != nullptr) { + if (action == "delete") + remove_from_database(a->id, "account"); + else + prepareTemplate(*a, "account"); + } + } else if (value.is() && _es_objects_assets) { + auto obj = db.find_object(value); + auto a = static_cast(obj); + if (a != nullptr) { + if (action == "delete") + remove_from_database(a->id, "asset"); + else + prepareTemplate(*a, "asset"); + } + } else if (value.is() && _es_objects_balances) { + auto obj = db.find_object(value); + auto b = static_cast(obj); + if (b != nullptr) { + if (action == "delete") + remove_from_database(b->id, "balance"); + else + prepareTemplate(*b, "balance"); + } + } else if (value.is() && _es_objects_limit_orders) { + auto obj = db.find_object(value); + auto l = static_cast(obj); + if (l != nullptr) { + if (action == "delete") + remove_from_database(l->id, "limitorder"); + else + prepareTemplate(*l, "limitorder"); + } + } else if (value.is() && _es_objects_asset_bitasset) { + auto obj = db.find_object(value); + auto ba = static_cast(obj); + if (ba != nullptr) { + if (action == "delete") + remove_from_database(ba->id, "bitasset"); + else + prepareTemplate(*ba, "bitasset"); + } + } + } + + if (curl && bulk.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech + + graphene::utilities::ES es; + es.curl = curl; + es.bulk_lines = bulk; + es.elasticsearch_url = _es_objects_elasticsearch_url; + es.auth = _es_objects_auth; + + if (!graphene::utilities::SendBulk(es)) + return false; + else + bulk.clear(); + } + } + + return true; +} + +void es_objects_plugin_impl::remove_from_database( object_id_type id, std::string index) +{ + if(_es_objects_keep_only_current) + { + fc::mutable_variant_object delete_line; + delete_line["_id"] = string(id); + delete_line["_index"] = _es_objects_index_prefix + index; + delete_line["_type"] = "data"; + fc::mutable_variant_object final_delete_line; + final_delete_line["delete"] = delete_line; + prepare.push_back(fc::json::to_string(final_delete_line)); + std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); + prepare.clear(); + } +} + +template +void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_name) +{ + fc::mutable_variant_object bulk_header; + bulk_header["_index"] = _es_objects_index_prefix + index_name; + bulk_header["_type"] = "data"; + if(_es_objects_keep_only_current) + { + bulk_header["_id"] = string(blockchain_object.id); + } + + adaptor_struct adaptor; + 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()); + + o["object_id"] = string(blockchain_object.id); + o["block_time"] = block_time; + o["block_number"] = block_number; + + string data = fc::json::to_string(o); + + 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() +{ + if (curl) { + curl_easy_cleanup(curl); + curl = nullptr; + } + return; +} + +} // end namespace detail + +es_objects_plugin::es_objects_plugin() : + my( new detail::es_objects_plugin_impl(*this) ) +{ +} + +es_objects_plugin::~es_objects_plugin() +{ +} + +std::string es_objects_plugin::plugin_name()const +{ + return "es_objects"; +} +std::string es_objects_plugin::plugin_description()const +{ + return "Stores blockchain objects in ES database. Experimental."; +} + +void es_objects_plugin::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg + ) +{ + cli.add_options() + ("es-objects-elasticsearch-url", boost::program_options::value(), "Elasticsearch node url(http://localhost:9200/)") + ("es-objects-auth", boost::program_options::value(), "Basic auth username:password('')") + ("es-objects-bulk-replay", boost::program_options::value(), "Number of bulk documents to index on replay(10000)") + ("es-objects-bulk-sync", boost::program_options::value(), "Number of bulk documents to index on a synchronized chain(100)") + ("es-objects-proposals", boost::program_options::value(), "Store proposal objects(true)") + ("es-objects-accounts", boost::program_options::value(), "Store account objects(true)") + ("es-objects-assets", boost::program_options::value(), "Store asset objects(true)") + ("es-objects-balances", boost::program_options::value(), "Store balances objects(true)") + ("es-objects-limit-orders", boost::program_options::value(), "Store limit order objects(true)") + ("es-objects-asset-bitasset", boost::program_options::value(), "Store feed data(true)") + ("es-objects-index-prefix", boost::program_options::value(), "Add a prefix to the index(ppobjects-)") + ("es-objects-keep-only-current", boost::program_options::value(), "Keep only current state of the objects(true)") + ("es-objects-start-es-after-block", boost::program_options::value(), "Start doing ES job after block(0)") + ; + cfg.add(cli); +} + +void es_objects_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + database().applied_block.connect([this](const signed_block &b) { + if(b.block_num() == 1) { + if (!my->genesis()) + FC_THROW_EXCEPTION(fc::exception, "Error populating genesis data."); + } + }); + + database().new_objects.connect([this]( const vector& ids, const flat_set& impacted_accounts ) { + if(!my->index_database(ids, "create")) + { + FC_THROW_EXCEPTION(fc::exception, "Error creating object from ES database, we are going to keep trying."); + } + }); + database().changed_objects.connect([this]( const vector& ids, const flat_set& impacted_accounts ) { + if(!my->index_database(ids, "update")) + { + FC_THROW_EXCEPTION(fc::exception, "Error updating object from ES database, we are going to keep trying."); + } + }); + database().removed_objects.connect([this](const vector& ids, const vector& objs, const flat_set& impacted_accounts) { + if(!my->index_database(ids, "delete")) + { + FC_THROW_EXCEPTION(fc::exception, "Error deleting object from ES database, we are going to keep trying."); + } + }); + + + if (options.count("es-objects-elasticsearch-url")) { + my->_es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as(); + } + if (options.count("es-objects-auth")) { + my->_es_objects_auth = options["es-objects-auth"].as(); + } + if (options.count("es-objects-bulk-replay")) { + my->_es_objects_bulk_replay = options["es-objects-bulk-replay"].as(); + } + if (options.count("es-objects-bulk-sync")) { + my->_es_objects_bulk_sync = options["es-objects-bulk-sync"].as(); + } + if (options.count("es-objects-proposals")) { + my->_es_objects_proposals = options["es-objects-proposals"].as(); + } + if (options.count("es-objects-accounts")) { + my->_es_objects_accounts = options["es-objects-accounts"].as(); + } + if (options.count("es-objects-assets")) { + my->_es_objects_assets = options["es-objects-assets"].as(); + } + if (options.count("es-objects-balances")) { + my->_es_objects_balances = options["es-objects-balances"].as(); + } + if (options.count("es-objects-limit-orders")) { + my->_es_objects_limit_orders = options["es-objects-limit-orders"].as(); + } + if (options.count("es-objects-asset-bitasset")) { + my->_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as(); + } + if (options.count("es-objects-index-prefix")) { + my->_es_objects_index_prefix = options["es-objects-index-prefix"].as(); + } + if (options.count("es-objects-keep-only-current")) { + my->_es_objects_keep_only_current = options["es-objects-keep-only-current"].as(); + } + if (options.count("es-objects-start-es-after-block")) { + my->_es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as(); + } +} + +void es_objects_plugin::plugin_startup() +{ + graphene::utilities::ES es; + es.curl = my->curl; + es.elasticsearch_url = my->_es_objects_elasticsearch_url; + es.auth = my->_es_objects_auth; + es.auth = my->_es_objects_index_prefix; + + if(!graphene::utilities::checkES(es)) + FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_es_objects_elasticsearch_url)); + ilog("elasticsearch OBJECTS: plugin_startup() begin"); +} + +} } 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 new file mode 100644 index 00000000..fa91e3bd --- /dev/null +++ b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 oxarbitrage, 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 + +namespace graphene { namespace es_objects { + +using namespace chain; + +namespace detail +{ + class es_objects_plugin_impl; +} + +class es_objects_plugin : public graphene::app::plugin +{ + public: + es_objects_plugin(); + virtual ~es_objects_plugin(); + + std::string plugin_name()const override; + std::string plugin_description()const override; + virtual void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map& options) override; + virtual void plugin_startup() override; + + friend class detail::es_objects_plugin_impl; + std::unique_ptr my; +}; + +struct adaptor_struct { + 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) { + 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"]); + } + + return o; + } + + 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 diff --git a/libraries/plugins/snapshot/CMakeLists.txt b/libraries/plugins/snapshot/CMakeLists.txt index 227c3860..728740de 100644 --- a/libraries/plugins/snapshot/CMakeLists.txt +++ b/libraries/plugins/snapshot/CMakeLists.txt @@ -15,3 +15,5 @@ install( TARGETS LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) + +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/snapshot" ) diff --git a/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp b/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp index b3ee30c6..eb8d3a16 100644 --- a/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp +++ b/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp @@ -35,6 +35,7 @@ class snapshot_plugin : public graphene::app::plugin { ~snapshot_plugin() {} std::string plugin_name()const override; + std::string plugin_description()const override; virtual void plugin_set_program_options( boost::program_options::options_description &command_line_options, diff --git a/libraries/plugins/snapshot/snapshot.cpp b/libraries/plugins/snapshot/snapshot.cpp index fe856ecb..f74ad589 100644 --- a/libraries/plugins/snapshot/snapshot.cpp +++ b/libraries/plugins/snapshot/snapshot.cpp @@ -54,6 +54,11 @@ std::string snapshot_plugin::plugin_name()const return "snapshot"; } +std::string snapshot_plugin::plugin_description()const +{ + return "Create snapshots at a specified time or block number."; +} + void snapshot_plugin::plugin_initialize(const boost::program_options::variables_map& options) { try { ilog("snapshot plugin: plugin_initialize() begin"); diff --git a/libraries/utilities/CMakeLists.txt b/libraries/utilities/CMakeLists.txt index f2d646d5..98086b10 100644 --- a/libraries/utilities/CMakeLists.txt +++ b/libraries/utilities/CMakeLists.txt @@ -14,6 +14,7 @@ set(sources string_escape.cpp tempdir.cpp words.cpp + elasticsearch.cpp ${HEADERS}) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp" @ONLY) diff --git a/libraries/utilities/elasticsearch.cpp b/libraries/utilities/elasticsearch.cpp new file mode 100644 index 00000000..11a9561b --- /dev/null +++ b/libraries/utilities/elasticsearch.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018 oxarbitrage, 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 + +size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +namespace graphene { namespace utilities { + +bool checkES(ES& es) +{ + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url + "_nodes"; + curl_request.auth = es.auth; + curl_request.type = "GET"; + + if(doCurl(curl_request).empty()) + return false; + return true; + +} +const std::string simpleQuery(ES& es) +{ + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url + es.endpoint; + curl_request.auth = es.auth; + curl_request.type = "POST"; + curl_request.query = es.query; + + return doCurl(curl_request); +} + +bool SendBulk(ES& es) +{ + std::string bulking = joinBulkLines(es.bulk_lines); + + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url + "_bulk"; + curl_request.auth = es.auth; + curl_request.type = "POST"; + curl_request.query = bulking; + + auto curlResponse = doCurl(curl_request); + + if(handleBulkResponse(getResponseCode(curl_request.handler), curlResponse)) + return true; + return false; +} + +const std::string joinBulkLines(const std::vector& bulk) +{ + auto bulking = boost::algorithm::join(bulk, "\n"); + bulking = bulking + "\n"; + + return bulking; +} +long getResponseCode(CURL *handler) +{ + long http_code = 0; + curl_easy_getinfo (handler, CURLINFO_RESPONSE_CODE, &http_code); + return http_code; +} + +bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer) +{ + if(http_code == 200) { + // all good, but check errors in response + fc::variant j = fc::json::from_string(CurlReadBuffer); + bool errors = j["errors"].as_bool(); + if(errors == true) { + return false; + } + } + else { + if(http_code == 413) { + elog( "413 error: Can be low disk space" ); + } + else if(http_code == 401) { + elog( "401 error: Unauthorized" ); + } + else { + elog( std::to_string(http_code) + " error: Unknown error" ); + } + return false; + } + return true; +} + +const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data) +{ + std::vector bulk; + fc::mutable_variant_object final_bulk_header; + final_bulk_header["index"] = bulk_header; + bulk.push_back(fc::json::to_string(final_bulk_header)); + bulk.push_back(data); + + return bulk; +} + +bool deleteAll(ES& es) +{ + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url + es.index_prefix + "*"; + curl_request.auth = es.auth; + curl_request.type = "DELETE"; + + auto curl_response = doCurl(curl_request); + if(curl_response.empty()) + return false; + else + return true; +} +const std::string getEndPoint(ES& es) +{ + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url + es.endpoint; + curl_request.auth = es.auth; + curl_request.type = "GET"; + + return doCurl(curl_request); +} + +const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix) +{ + auto block_date_string = block_date.to_iso_string(); + std::vector parts; + boost::split(parts, block_date_string, boost::is_any_of("-")); + std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1]; + return index_name; +} + +const std::string doCurl(CurlRequest& curl) +{ + std::string CurlReadBuffer; + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/json"); + + curl_easy_setopt(curl.handler, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl.handler, CURLOPT_URL, curl.url.c_str()); + curl_easy_setopt(curl.handler, CURLOPT_CUSTOMREQUEST, curl.type.c_str()); + if(curl.type == "POST") + { + curl_easy_setopt(curl.handler, CURLOPT_POST, true); + curl_easy_setopt(curl.handler, CURLOPT_POSTFIELDS, curl.query.c_str()); + } + curl_easy_setopt(curl.handler, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl.handler, CURLOPT_WRITEDATA, (void *)&CurlReadBuffer); + curl_easy_setopt(curl.handler, CURLOPT_USERAGENT, "libcrp/0.1"); + if(!curl.auth.empty()) + curl_easy_setopt(curl.handler, CURLOPT_USERPWD, curl.auth.c_str()); + curl_easy_perform(curl.handler); + + return CurlReadBuffer; +} + +} } // end namespace graphene::utilities diff --git a/libraries/utilities/include/graphene/utilities/elasticsearch.hpp b/libraries/utilities/include/graphene/utilities/elasticsearch.hpp new file mode 100644 index 00000000..898464b1 --- /dev/null +++ b/libraries/utilities/include/graphene/utilities/elasticsearch.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 oxarbitrage, 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 + +size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp); + +namespace graphene { namespace utilities { + + class ES { + public: + CURL *curl; + std::vector bulk_lines; + std::string elasticsearch_url; + std::string index_prefix; + std::string auth; + std::string endpoint; + std::string query; + }; + class CurlRequest { + public: + CURL *handler; + std::string url; + std::string type; + std::string auth; + std::string query; + }; + + bool SendBulk(ES& es); + const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data); + bool checkES(ES& es); + const std::string simpleQuery(ES& es); + bool deleteAll(ES& es); + bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer); + const std::string getEndPoint(ES& es); + const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix); + const std::string doCurl(CurlRequest& curl); + const std::string joinBulkLines(const std::vector& bulk); + long getResponseCode(CURL *handler); + +} } // end namespace graphene::utilities diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 34fc1885..91eb8dbd 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -498,6 +498,11 @@ class wallet_api * @ingroup Transaction Builder API */ signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true); + /** Broadcast signed transaction + * @param tx signed transaction + * @returns the transaction ID along with the signed transaction. + */ + pair broadcast_transaction(signed_transaction tx); /** * @ingroup Transaction Builder API */ @@ -597,6 +602,12 @@ class wallet_api */ bool load_wallet_file(string wallet_filename = ""); + /** Quitting from Peerplays wallet. + * + * The current wallet will be closed. + */ + void quit(); + /** Saves the current wallet to the given filename. * * @warning This does not change the wallet filename that will be used for future @@ -1296,6 +1307,12 @@ class wallet_api */ witness_object get_witness(string owner_account); + /** Returns true if the account is witness, false otherwise + * @param owner_account the name or id of the witness account owner, or the id of the witness + * @returns true if account is witness, false otherwise + */ + bool is_witness(string owner_account); + /** Returns information about the given committee_member. * @param owner_account the name or id of the committee_member account owner, or the id of the committee_member * @returns the information about the committee_member stored in the block chain @@ -1570,7 +1587,7 @@ class wallet_api vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); /** - * Withdraw a vesting balance. + * Withdraw a normal(old) vesting balance. * * @param witness_name The account name of the witness, also accepts account ID or vesting balance ID type. * @param amount The amount to withdraw. @@ -1583,6 +1600,20 @@ class wallet_api string asset_symbol, bool broadcast = false); + /** + * Withdraw a GPOS vesting balance. + * + * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type. + * @param amount The amount to withdraw. + * @param asset_symbol The symbol of the asset to withdraw. + * @param broadcast true if you wish to broadcast the transaction + */ + signed_transaction withdraw_GPOS_vesting_balance( + string account_name, + string amount, + string asset_symbol, + bool broadcast = false); + /** Vote for a given committee_member. * * An account can publish a list of all committee_memberes they approve of. This @@ -1771,6 +1802,37 @@ class wallet_api */ signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); + /** Get transaction signers. + * + * Returns information about who signed the transaction, specifically, + * the corresponding public keys of the private keys used to sign the transaction. + * @param tx the signed transaction + * @return the set of public_keys + */ + flat_set get_transaction_signers(const signed_transaction &tx) const; + + /** Get key references. + * + * Returns accounts related to given public keys. + * @param keys public keys to search for related accounts + * @return the set of related accounts + */ + vector> get_key_references(const vector &keys) const; + + /** Signs a transaction. + * + * Given a fully-formed transaction with or without signatures, signs + * the transaction with the owned keys and optionally broadcasts the + * transaction. + * + * @param tx the unsigned transaction + * @param broadcast true if you wish to broadcast the transaction + * + * @return the signed transaction + */ + signed_transaction add_transaction_signature( signed_transaction tx, + bool broadcast = false ); + /** Returns an uninitialized object representing a given blockchain operation. * * This returns a default-initialized object of the given type; it can be used @@ -2077,6 +2139,20 @@ class wallet_api rock_paper_scissors_gesture gesture, bool broadcast); + /** Create a vesting balance including gpos vesting balance after HARDFORK_GPOS_TIME + * @param owner vesting balance owner and creator + * @param amount amount to vest + * @param asset_symbol the symbol of the asset to vest + * @param is_gpos True if the balance is of gpos type + * @param broadcast true if you wish to broadcast the transaction + * @return the signed version of the transaction + */ + signed_transaction create_vesting_balance(string owner, + string amount, + string asset_symbol, + bool is_gpos, + bool broadcast); + void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); void dbg_push_blocks( std::string src_filename, uint32_t count ); @@ -2109,6 +2185,8 @@ class wallet_api } } +extern template class fc::api; + FC_REFLECT( graphene::wallet::key_label, (label)(key) ) FC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) ) FC_REFLECT( graphene::wallet::blind_confirmation::output, (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) ) @@ -2178,6 +2256,7 @@ FC_API( graphene::wallet::wallet_api, (set_fees_on_builder_transaction) (preview_builder_transaction) (sign_builder_transaction) + (broadcast_transaction) (propose_builder_transaction) (propose_builder_transaction2) (remove_builder_transaction) @@ -2229,6 +2308,7 @@ FC_API( graphene::wallet::wallet_api, (create_committee_member) (get_son) (get_witness) + (is_witness) (get_committee_member) (list_witnesses) (list_committee_members) @@ -2256,6 +2336,7 @@ FC_API( graphene::wallet::wallet_api, (create_vesting_balance) (get_vesting_balances) (withdraw_vesting) + (withdraw_GPOS_vesting_balance) (vote_for_committee_member) (vote_for_son) (update_son_votes) @@ -2284,6 +2365,9 @@ FC_API( graphene::wallet::wallet_api, (save_wallet_file) (serialize_transaction) (sign_transaction) + (get_transaction_signers) + (get_key_references) + (add_transaction_signature) (get_prototype_operation) (propose_parameter_change) (propose_fee_change) @@ -2339,6 +2423,7 @@ FC_API( graphene::wallet::wallet_api, (tournament_join) (tournament_leave) (rps_throw) + (create_vesting_balance) (get_upcoming_tournaments) (get_tournaments) (get_tournaments_by_state) @@ -2350,4 +2435,5 @@ FC_API( graphene::wallet::wallet_api, (get_matched_bets_for_bettor) (get_all_matched_bets_for_bettor) (buy_ticket) + (quit) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8af353e2..a7ce3b87 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,8 @@ # include #endif +template class fc::api; + #define BRAIN_KEY_WORD_COUNT 16 namespace graphene { namespace wallet { @@ -275,6 +278,7 @@ public: private: void claim_registered_account(const account_object& account) { + bool import_keys = false; auto it = _wallet.pending_account_registrations.find( account.name ); FC_ASSERT( it != _wallet.pending_account_registrations.end() ); for (const std::string& wif_key : it->second) @@ -290,8 +294,13 @@ private: // possibility of migrating to a fork where the // name is available, the user can always // manually re-register) + } else { + import_keys = true; } _wallet.pending_account_registrations.erase( it ); + + if (import_keys) + save_wallet_file(); } // after a son registration succeeds, this saves the private key in the wallet permanently @@ -367,7 +376,8 @@ private: for( const fc::optional& optional_account : owner_account_objects ) if (optional_account) { - fc::optional witness_obj = _remote_db->get_witness_by_account(optional_account->id); + std::string account_id = account_id_to_string(optional_account->id); + fc::optional witness_obj = _remote_db->get_witness_by_account(account_id); if (witness_obj) claim_registered_witness(optional_account->name); } @@ -635,6 +645,13 @@ public: fc::async([this, object]{subscribed_object_changed(object);}, "Object changed"); } + void quit() + { + ilog( "Quitting Cli Wallet ..." ); + + throw fc::canceled_exception(); + } + bool copy_wallet_file( string destination_filename ) { fc::path src_path = get_wallet_filename(); @@ -753,9 +770,17 @@ public: { return _remote_db->get_dynamic_global_properties(); } + std::string account_id_to_string(account_id_type id) const + { + std::string account_id = fc::to_string(id.space_id) + + "." + fc::to_string(id.type_id) + + "." + fc::to_string(id.instance.value); + return account_id; + } account_object get_account(account_id_type id) const { - auto rec = _remote_db->get_accounts({id}).front(); + std::string account_id = account_id_to_string(id); + auto rec = _remote_db->get_accounts({account_id}).front(); FC_ASSERT(rec); return *rec; } @@ -777,9 +802,16 @@ public: { return get_account(account_name_or_id).get_id(); } + std::string asset_id_to_string(asset_id_type id) const + { + std::string asset_id = fc::to_string(id.space_id) + + "." + fc::to_string(id.type_id) + + "." + fc::to_string(id.instance.value); + return asset_id; + } optional find_asset(asset_id_type id)const { - auto rec = _remote_db->get_assets({id}).front(); + auto rec = _remote_db->get_assets({asset_id_to_string(id)}).front(); if( rec ) _asset_cache[id] = *rec; return rec; @@ -1043,7 +1075,7 @@ public: ("chain_id", _chain_id) ); size_t account_pagination = 100; - vector< account_id_type > account_ids_to_send; + vector< std::string > account_ids_to_send; size_t n = _wallet.my_accounts.size(); account_ids_to_send.reserve( std::min( account_pagination, n ) ); auto it = _wallet.my_accounts.begin(); @@ -1058,7 +1090,8 @@ public: { assert( it != _wallet.my_accounts.end() ); old_accounts.push_back( *it ); - account_ids_to_send.push_back( old_accounts.back().id ); + std::string account_id = account_id_to_string(old_accounts.back().id); + account_ids_to_send.push_back( account_id ); ++it; } std::vector< optional< account_object > > accounts = _remote_db->get_accounts(account_ids_to_send); @@ -1090,6 +1123,7 @@ public: return true; } + void save_wallet_file(string wallet_filename = "") { // @@ -1104,7 +1138,7 @@ public: if( wallet_filename == "" ) wallet_filename = _wallet_filename; - wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); + ilog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); string data = fc::json::to_pretty_string( _wallet ); try @@ -1116,14 +1150,38 @@ public: // // http://en.wikipedia.org/wiki/Most_vexing_parse // - fc::ofstream outfile{ fc::path( wallet_filename ) }; + std::string tmp_wallet_filename = wallet_filename + ".tmp"; + fc::ofstream outfile{ fc::path( tmp_wallet_filename ) }; outfile.write( data.c_str(), data.length() ); outfile.flush(); outfile.close(); + + ilog( "saved successfully wallet to tmp file ${fn}", ("fn", tmp_wallet_filename) ); + + std::string wallet_file_content; + fc::read_file_contents(tmp_wallet_filename, wallet_file_content); + + if (wallet_file_content == data) { + dlog( "validated successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); + fc::rename( tmp_wallet_filename, wallet_filename ); + dlog( "renamed successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); + } + else + { + FC_THROW("tmp wallet file cannot be validated ${fn}", ("fn", tmp_wallet_filename) ); + } + + ilog( "successfully saved wallet to file ${fn}", ("fn", wallet_filename) ); + disable_umask_protection(); } catch(...) { + string ws_password = _wallet.ws_password; + _wallet.ws_password = ""; + elog("wallet file content is: ${data}", ("data", fc::json::to_pretty_string( _wallet ) ) ); + _wallet.ws_password = ws_password; + disable_umask_protection(); throw; } @@ -1185,6 +1243,20 @@ public: return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast); } + + pair broadcast_transaction(signed_transaction tx) + { + try { + _remote_net_broadcast->broadcast_transaction(tx); + } + catch (const fc::exception& e) { + elog("Caught exception while broadcasting tx ${id}: ${e}", + ("id", tx.id().str())("e", e.to_detail_string())); + throw; + } + return std::make_pair(tx.id(),tx); + } + signed_transaction propose_builder_transaction( transaction_handle_type handle, time_point_sec expiration = time_point::now() + fc::minutes(1), @@ -1745,7 +1817,7 @@ public: committee_member_create_operation committee_member_create_op; committee_member_create_op.committee_member_account = get_account_id(owner_account); committee_member_create_op.url = url; - if (_remote_db->get_committee_member_by_account(committee_member_create_op.committee_member_account)) + if (_remote_db->get_committee_member_by_account(owner_account)) FC_THROW("Account ${owner_account} is already a committee_member", ("owner_account", owner_account)); signed_transaction tx; @@ -1775,7 +1847,7 @@ public: // then maybe it's the owner account try { - account_id_type owner_account_id = get_account_id(owner_account); + std::string owner_account_id = account_id_to_string(get_account_id(owner_account)); fc::optional witness = _remote_db->get_witness_by_account(owner_account_id); if (witness) return *witness; @@ -1826,6 +1898,42 @@ public: FC_CAPTURE_AND_RETHROW( (owner_account) ) } + bool is_witness(string owner_account) + { + try + { + fc::optional witness_id = maybe_id(owner_account); + if (witness_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*witness_id); + std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get); + if (witness_objects.front()) + return true; + else + return false; + } + else + { + // then maybe it's the owner account + try + { + std::string owner_account_id = account_id_to_string(get_account_id(owner_account)); + fc::optional witness = _remote_db->get_witness_by_account(owner_account_id); + if (witness) + return true; + else + return false; + } + catch (const fc::exception&) + { + return false; + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + committee_member_object get_committee_member(string owner_account) { try @@ -1845,8 +1953,7 @@ public: // then maybe it's the owner account try { - account_id_type owner_account_id = get_account_id(owner_account); - fc::optional committee_member = _remote_db->get_committee_member_by_account(owner_account_id); + fc::optional committee_member = _remote_db->get_committee_member_by_account(owner_account); if (committee_member) return *committee_member; else @@ -2116,7 +2223,7 @@ public: witness_create_op.initial_secret = enc.result(); - if (_remote_db->get_witness_by_account(witness_create_op.witness_account)) + if (_remote_db->get_witness_by_account(account_id_to_string(witness_create_op.witness_account))) FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account)); signed_transaction tx; @@ -2308,12 +2415,7 @@ public: return result; } - // try casting to avoid a round-trip if we were given an account ID - fc::optional acct_id = maybe_id( account_name ); - if( !acct_id ) - acct_id = get_account( account_name ).id; - - vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( *acct_id ); + vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( account_name ); if( vbos.size() == 0 ) return result; @@ -2334,12 +2436,21 @@ public: fc::optional vbid = maybe_id(witness_name); if( !vbid ) { - witness_object wit = get_witness( witness_name ); - FC_ASSERT( wit.pay_vb ); - vbid = wit.pay_vb; + if (is_witness(witness_name)) + { + witness_object wit = get_witness( witness_name ); + FC_ASSERT( wit.pay_vb, "Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); + vbid = wit.pay_vb; + } + else + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); } vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid ); + + if(vbo.balance_type != vesting_balance_type::normal) + FC_THROW("Allowed to withdraw only Normal type vest balances with this method"); + vesting_balance_withdraw_operation vesting_balance_withdraw_op; vesting_balance_withdraw_op.vesting_balance = *vbid; @@ -2355,21 +2466,100 @@ public: } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) ) } + signed_transaction withdraw_GPOS_vesting_balance( + string account_name, + string amount, + string asset_symbol, + bool broadcast = false) + { try { + + //Can be deleted after GPOS hardfork time + time_point_sec now = time_point::now(); + if(now < HARDFORK_GPOS_TIME) + FC_THROW("GPOS related functionality is not avaiable until next Spring"); + + asset_object asset_obj = get_asset( asset_symbol ); + vector< vesting_balance_object > vbos; + vesting_balance_object vbo; + fc::optional vbid = maybe_id(account_name); + if( !vbid ) + { + vbos = _remote_db->get_vesting_balances( account_name ); + if( vbos.size() == 0 ) + FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name)); + } + + //whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types + if(!vbos.size()) + vbos.emplace_back( get_object(*vbid) ); + + for (const vesting_balance_object& vesting_balance_obj: vbos) { + if(vesting_balance_obj.balance_type == vesting_balance_type::gpos) + { + vbo = vesting_balance_obj; + break; + } + } + + vesting_balance_withdraw_operation vesting_balance_withdraw_op; + + vesting_balance_withdraw_op.vesting_balance = vbo.id; + vesting_balance_withdraw_op.owner = vbo.owner; + vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount); + + signed_transaction tx; + tx.operations.push_back( vesting_balance_withdraw_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (account_name)(amount) ) + } + signed_transaction vote_for_committee_member(string voting_account, string committee_member, bool approve, bool broadcast /* = false */) { try { + std::vector vbo_info = get_vesting_balances(voting_account); + + time_point_sec now = time_point::now(); + if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass + { + std::vector::iterator vbo_iter; + vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); + if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the committee member", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); + } + account_object voting_account_object = get_account(voting_account); - account_id_type committee_member_owner_account_id = get_account_id(committee_member); - fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id); + fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member); if (!committee_member_obj) FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member)); + + bool update_vote_time = false; + if (approve) { auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id); - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); + if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass + { + account_id_type stake_account = get_account_id(voting_account); + const auto gpos_info = _remote_db->get_gpos_info(stake_account); + const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); + const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); + const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; + + if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) + FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member)); + else + update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF) + } + else + { + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); + } } else { @@ -2380,6 +2570,7 @@ public: account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; + account_update_op.extensions.value.update_last_voting_time = update_vote_time; signed_transaction tx; tx.operations.push_back( account_update_op ); @@ -2470,26 +2661,57 @@ public: bool approve, bool broadcast /* = false */) { try { + std::vector vbo_info = get_vesting_balances(voting_account); + + time_point_sec now = time_point::now(); + if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass + { + std::vector::iterator vbo_iter; + vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); + if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the witness", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); + } + account_object voting_account_object = get_account(voting_account); - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + + fc::optional witness_obj = _remote_db->get_witness_by_account(witness); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); + + bool update_vote_time = false; if (approve) { auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); + if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass + { + account_id_type stake_account = get_account_id(voting_account); + const auto gpos_info = _remote_db->get_gpos_info(stake_account); + const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); + const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); + const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; + + if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) + FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness)); + else + update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF) + } + else + { + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); + } } else { unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); if (!votes_removed) - FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); + FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness)); } + account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; + account_update_op.extensions.value.update_last_voting_time = update_vote_time; signed_transaction tx; tx.operations.push_back( account_update_op ); @@ -2508,8 +2730,7 @@ public: account_object voting_account_object = get_account(voting_account); for (const std::string& witness : witnesses_to_approve) { - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); @@ -2518,8 +2739,7 @@ public: } for (const std::string& witness : witnesses_to_reject) { - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); @@ -2660,6 +2880,84 @@ public: return tx; } + flat_set get_transaction_signers(const signed_transaction &tx) const + { + return tx.get_signature_keys(_chain_id); + } + + vector> get_key_references(const vector &keys) const + { + return _remote_db->get_key_references(keys); + } + + /** + * Get the required public keys to sign the transaction which had been + * owned by us + * + * NOTE, if `erase_existing_sigs` set to true, the original trasaction's + * signatures will be erased + * + * @param tx The transaction to be signed + * @param erase_existing_sigs + * The transaction could have been partially signed already, + * if set to false, the corresponding public key of existing + * signatures won't be returned. + * If set to true, the existing signatures will be erased and + * all required keys returned. + */ + set get_owned_required_keys( signed_transaction &tx, + bool erase_existing_sigs = true) + { + set pks = _remote_db->get_potential_signatures( tx ); + flat_set owned_keys; + owned_keys.reserve( pks.size() ); + std::copy_if( pks.begin(), pks.end(), + std::inserter( owned_keys, owned_keys.end() ), + [this]( const public_key_type &pk ) { + return _keys.find( pk ) != _keys.end(); + } ); + + if ( erase_existing_sigs ) + tx.signatures.clear(); + + return _remote_db->get_required_signatures( tx, owned_keys ); + } + + signed_transaction add_transaction_signature( signed_transaction tx, + bool broadcast ) + { + set approving_key_set = get_owned_required_keys(tx, false); + + if ( ( ( tx.ref_block_num == 0 && tx.ref_block_prefix == 0 ) || + tx.expiration == fc::time_point_sec() ) && + tx.signatures.empty() ) + { + auto dyn_props = get_dynamic_global_properties(); + auto parameters = get_global_properties().parameters; + fc::time_point_sec now = dyn_props.time; + tx.set_reference_block( dyn_props.head_block_id ); + tx.set_expiration( now + parameters.maximum_time_until_expiration ); + } + for ( const public_key_type &key : approving_key_set ) + tx.sign( get_private_key( key ), _chain_id ); + + if ( broadcast ) + { + try + { + _remote_net_broadcast->broadcast_transaction( tx ); + } + catch ( const fc::exception &e ) + { + elog( "Caught exception while broadcasting tx ${id}: ${e}", + ( "id", tx.id().str() )( "e", e.to_detail_string() ) ); + FC_THROW( "Caught exception while broadcasting tx" ); + } + } + + return tx; + } + signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, @@ -3825,8 +4123,8 @@ map wallet_api::list_accounts(const string& lowerbound, vector wallet_api::list_account_balances(const string& id) { if( auto real_id = detail::maybe_id(id) ) - return my->_remote_db->get_account_balances(*real_id, flat_set()); - return my->_remote_db->get_account_balances(get_account(id).id, flat_set()); + return my->_remote_db->get_account_balances(id, flat_set()); + return my->_remote_db->get_account_balances(id, flat_set()); } vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const @@ -3862,8 +4160,7 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const vector wallet_api::get_account_history(string name, int limit) const { vector result; - auto account_id = get_account(name).get_id(); - + while (limit > 0) { bool skip_first_row = false; @@ -3883,7 +4180,7 @@ vector wallet_api::get_account_history(string name, int limit) int page_limit = skip_first_row ? std::min(100, limit + 1) : std::min(100, limit); - vector current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(), + vector current = my->_remote_hist->get_account_history(name, operation_history_id_type(), page_limit, start); bool first_row = true; for (auto &o : current) @@ -3918,11 +4215,10 @@ vector wallet_api::get_relative_account_history(string name, u FC_ASSERT( start > 0 || limit <= 100 ); vector result; - auto account_id = get_account(name).get_id(); while( limit > 0 ) { - vector current = my->_remote_hist->get_relative_account_history(account_id, stop, std::min(100, limit), start); + vector current = my->_remote_hist->get_relative_account_history(name, stop, std::min(100, limit), start); for (auto &o : current) { std::stringstream ss; auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); @@ -3943,22 +4239,22 @@ vector wallet_api::list_core_accounts()const vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket , fc::time_point_sec start, fc::time_point_sec end )const { - return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, start, end ); + return my->_remote_hist->get_market_history( symbol1, symbol2, bucket, start, end ); } vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const { - return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); + return my->_remote_db->get_limit_orders(a, b, limit); } vector wallet_api::get_call_orders(string a, uint32_t limit)const { - return my->_remote_db->get_call_orders(get_asset(a).id, limit); + return my->_remote_db->get_call_orders(a, limit); } vector wallet_api::get_settle_orders(string a, uint32_t limit)const { - return my->_remote_db->get_settle_orders(get_asset(a).id, limit); + return my->_remote_db->get_settle_orders(a, limit); } brain_key_info wallet_api::suggest_brain_key()const @@ -4055,6 +4351,11 @@ signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type return my->sign_builder_transaction(transaction_handle, broadcast); } +pair wallet_api::broadcast_transaction(signed_transaction tx) +{ + return my->broadcast_transaction(tx); +} + signed_transaction wallet_api::propose_builder_transaction( transaction_handle_type handle, time_point_sec expiration, @@ -4424,6 +4725,11 @@ witness_object wallet_api::get_witness(string owner_account) return my->get_witness(owner_account); } +bool wallet_api::is_witness(string owner_account) +{ + return my->is_witness(owner_account); +} + committee_member_object wallet_api::get_committee_member(string owner_account) { return my->get_committee_member(owner_account); @@ -4592,11 +4898,20 @@ signed_transaction wallet_api::withdraw_vesting( string witness_name, string amount, string asset_symbol, - bool broadcast /* = false */) + bool broadcast) { return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast ); } +signed_transaction wallet_api::withdraw_GPOS_vesting_balance( + string account_name, + string amount, + string asset_symbol, + bool broadcast) +{ + return my->withdraw_GPOS_vesting_balance( account_name, amount, asset_symbol, broadcast ); +} + signed_transaction wallet_api::vote_for_committee_member(string voting_account, string witness, bool approve, @@ -4665,6 +4980,22 @@ signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broa return my->sign_transaction( tx, broadcast); } FC_CAPTURE_AND_RETHROW( (tx) ) } +signed_transaction wallet_api::add_transaction_signature( signed_transaction tx, + bool broadcast ) +{ + return my->add_transaction_signature( tx, broadcast ); +} + +flat_set wallet_api::get_transaction_signers(const signed_transaction &tx) const +{ try { + return my->get_transaction_signers(tx); +} FC_CAPTURE_AND_RETHROW( (tx) ) } + +vector> wallet_api::get_key_references(const vector &keys) const +{ try { + return my->get_key_references(keys); +} FC_CAPTURE_AND_RETHROW( (keys) ) } + operation wallet_api::get_prototype_operation(string operation_name) { return my->get_prototype_operation( operation_name ); @@ -4852,6 +5183,11 @@ bool wallet_api::load_wallet_file( string wallet_filename ) return my->load_wallet_file( wallet_filename ); } +void wallet_api::quit() +{ + my->quit(); +} + void wallet_api::save_wallet_file( string wallet_filename ) { my->save_wallet_file( wallet_filename ); @@ -6334,6 +6670,41 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id, return my->sign_transaction( tx, broadcast ); } +signed_transaction wallet_api::create_vesting_balance(string owner, + string amount, + string asset_symbol, + bool is_gpos, + bool broadcast) +{ + FC_ASSERT( !is_locked() ); + //Can be deleted after GPOS hardfork time + time_point_sec now = time_point::now(); + if(is_gpos && now < HARDFORK_GPOS_TIME) + FC_THROW("GPOS related functionality is not avaiable until next Spring"); + + account_object owner_account = get_account(owner); + account_id_type owner_id = owner_account.id; + + fc::optional asset_obj = get_asset(asset_symbol); + + auto type = vesting_balance_type::normal; + if(is_gpos) + type = vesting_balance_type::gpos; + + vesting_balance_create_operation op; + op.creator = owner_id; + op.owner = owner_id; + op.amount = asset_obj->amount_from_string(amount); + op.balance_type = type; + + signed_transaction trx; + trx.operations.push_back(op); + my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); + trx.validate(); + + return my->sign_transaction( trx, broadcast ); +} + // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info() { @@ -6389,7 +6760,10 @@ vesting_balance_object_with_info::vesting_balance_object_with_info( const vestin : vesting_balance_object( vbo ) { allowed_withdraw = get_allowed_withdraw( now ); - allowed_withdraw_time = now; + if(vbo.balance_type == vesting_balance_type::gpos) + allowed_withdraw_time = vbo.policy.get().begin_timestamp + vbo.policy.get().vesting_cliff_seconds; + else + allowed_withdraw_time = now; } } } // graphene::wallet diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index d68f25b8..b7abdabe 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -113,11 +113,13 @@ int main( int argc, char** argv ) cfg.appenders.push_back(fc::appender_config( "rpc", "file", fc::variant(ac, 5))); cfg.loggers = { fc::logger_config("default"), fc::logger_config( "rpc") }; - cfg.loggers.front().level = fc::log_level::info; + cfg.loggers.front().level = fc::log_level::warn; cfg.loggers.front().appenders = {"default"}; - cfg.loggers.back().level = fc::log_level::debug; + cfg.loggers.back().level = fc::log_level::info; cfg.loggers.back().appenders = {"rpc"}; + fc::configure_logging( cfg ); + fc::ecc::private_key committee_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); idump( (key_to_wif( committee_private_key ) ) ); @@ -174,7 +176,7 @@ int main( int argc, char** argv ) fc::http::websocket_client client; idump((wdata.ws_server)); auto con = client.connect( wdata.ws_server ); - auto apic = std::make_shared(con, GRAPHENE_MAX_NESTED_OBJECTS); + auto apic = std::make_shared(*con, GRAPHENE_MAX_NESTED_OBJECTS); auto remote_api = apic->get_remote_api< login_api >(1); edump((wdata.ws_user)(wdata.ws_password) ); @@ -213,7 +215,7 @@ int main( int argc, char** argv ) _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){ std::cout << "here... \n"; wlog("." ); - auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); @@ -230,7 +232,7 @@ int main( int argc, char** argv ) if( options.count("rpc-tls-endpoint") ) { _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index e43172a0..44048602 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc graphene_snapshot graphene_es_objects peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index ab153e7b..e07cec92 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -1,5 +1,5 @@ { - "initial_timestamp": "2019-05-14T18:47:51", + "initial_timestamp": "2020-01-13T06:03:15", "max_core_supply": "1000000000000000", "initial_parameters": { "current_fees": { @@ -307,6 +307,27 @@ 75,{} ],[ 76,{} + ],[ + 77,{ + "lottery_asset": 2000000, + "price_per_kbyte": 10 + } + ],[ + 78,{ + "fee": 0 + } + ],[ + 79,{ + "fee": 0 + } + ],[ + 80,{ + "fee": 0 + } + ],[ + 81,{ + "fee": 2000000 + } ] ], "scale": 10000 @@ -352,7 +373,19 @@ "maximum_tournament_start_time_in_future": 2419200, "maximum_tournament_start_delay": 604800, "maximum_tournament_number_of_wins": 100, - "extensions": {} + "extensions": { + "min_bet_multiplier": 10100, + "max_bet_multiplier": 10000000, + "betting_rake_fee_percentage": 300, + "live_betting_delay_time": 5, + "sweeps_distribution_percentage": 200, + "sweeps_distribution_asset": "1.3.0", + "sweeps_vesting_accumulator_account": "1.2.0", + "gpos_period": 15552000, + "gpos_subperiod": 2592000, + "gpos_period_start": 1601528400, + "gpos_vesting_lockin_period": 2592000 + } }, "initial_bts_accounts": [], "initial_accounts": [{ diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 6675ee87..19b1460d 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -25,8 +25,11 @@ #include #include +#include #include #include +#include +#include #include //#include //#include @@ -34,15 +37,18 @@ #include #include #include -//#include +#include #include #include #include #include +<<<<<<< HEAD #include #include +======= +>>>>>>> 24e7610bceb97ab361fe003622c80a79bdecf730 #include #include @@ -84,7 +90,10 @@ int main(int argc, char** argv) { "Space-separated list of plugins to activate"); auto witness_plug = node->register_plugin(); + auto debug_witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); + auto elasticsearch_plug = node->register_plugin(); + auto es_objects_plug = node->register_plugin(); auto market_history_plug = node->register_plugin(); //auto generate_genesis_plug = node->register_plugin(); //auto generate_uia_sharedrop_genesis_plug = node->register_plugin(); @@ -92,7 +101,7 @@ int main(int argc, char** argv) { auto affiliate_stats_plug = node->register_plugin(); auto bookie_plug = node->register_plugin(); auto peerplays_sidechain = node->register_plugin(); -// auto snapshot_plug = node->register_plugin(); + auto snapshot_plug = node->register_plugin(); // add plugin options to config try @@ -171,7 +180,7 @@ int main(int argc, char** argv) { exit_promise->set_value(signal); }, SIGTERM); - ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); + ilog("Started Peerplays node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) ); int signal = exit_promise->wait(); @@ -190,6 +199,10 @@ int main(int argc, char** argv) { elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string())); node->shutdown(); delete node; +<<<<<<< HEAD return EXIT_FAILURE; +======= + return 1; +>>>>>>> 24e7610bceb97ab361fe003622c80a79bdecf730 } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b49e089e..3cb14768 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,30 +8,30 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) file(GLOB PERFORMANCE_TESTS "performance/*.cpp") add_executable( performance_test ${PERFORMANCE_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB BENCH_MARKS "benchmarks/*.cpp") add_executable( chain_bench ${BENCH_MARKS} ${COMMON_SOURCES} ) -target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) -target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_elasticsearch graphene_es_objects graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB INTENSE_SOURCES "intense/*.cpp") add_executable( intense_test ${INTENSE_SOURCES} ${COMMON_SOURCES} ) -target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB BETTING_TESTS "betting/*.cpp") add_executable( betting_test ${BETTING_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB PEERPLAYS_SIDECHAIN_TESTS "peerplays_sidechain/*.cpp") add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ${COMMON_SOURCES} ) @@ -39,11 +39,11 @@ target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app grap file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB RANDOM_SOURCES "random/*.cpp") add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} ) -target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( random_test graphene_chain graphene_app graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB CLI_SOURCES "cli/*.cpp") add_executable( cli_test ${CLI_SOURCES} ) @@ -55,4 +55,8 @@ if(MSVC) set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) +file(GLOB ES_SOURCES "elasticsearch/*.cpp") +add_executable( es_test ${ES_SOURCES} ${COMMON_SOURCES} ) +target_link_libraries( es_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + add_subdirectory( generate_empty_blocks ) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index b3775b1f..68b9e1f0 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -63,6 +63,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) app1.register_plugin(); boost::program_options::variables_map cfg; cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); + cfg.emplace("plugins", boost::program_options::variable_value(string(" "), false)); app1.initialize(app_dir.path(), cfg); cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index cc155979..505178c0 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -23,11 +23,291 @@ */ #include "cli_fixture.hpp" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include #include #define BOOST_TEST_MODULE Test Application #include +/***** + * Global Initialization for Windows + * ( sets up Winsock stuf ) + */ +#ifdef _WIN32 +int sockInit(void) +{ + WSADATA wsa_data; + return WSAStartup(MAKEWORD(1,1), &wsa_data); +} +int sockQuit(void) +{ + return WSACleanup(); +} +#endif + +/********************* + * Helper Methods + *********************/ + +#include "../common/genesis_file_util.hpp" + +using std::exception; +using std::cerr; + +#define INVOKE(test) ((struct test*)this)->test_method(); + +////// +/// @brief attempt to find an available port on localhost +/// @returns an available port number, or -1 on error +///// +int get_available_port() +{ + struct sockaddr_in sin; + int socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd == -1) + return -1; + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1) + return -1; + socklen_t len = sizeof(sin); + if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1) + return -1; +#ifdef _WIN32 + closesocket(socket_fd); +#else + close(socket_fd); +#endif + return ntohs(sin.sin_port); +} + +/////////// +/// @brief Start the application +/// @param app_dir the temporary directory to use +/// @param server_port_number to be filled with the rpc endpoint port number +/// @returns the application object +////////// +std::shared_ptr start_application(fc::temp_directory& app_dir, int& server_port_number) { + std::shared_ptr app1(new graphene::app::application{}); + + app1->register_plugin< graphene::bookie::bookie_plugin>(); + app1->register_plugin(); + app1->register_plugin< graphene::market_history::market_history_plugin >(); + app1->register_plugin< graphene::witness_plugin::witness_plugin >(); + app1->startup_plugins(); + boost::program_options::variables_map cfg; +#ifdef _WIN32 + sockInit(); +#endif + server_port_number = get_available_port(); + cfg.emplace( + "rpc-endpoint", + boost::program_options::variable_value(string("127.0.0.1:" + std::to_string(server_port_number)), false) + ); + cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); + cfg.emplace("seed-nodes", boost::program_options::variable_value(string("[]"), false)); + + app1->initialize(app_dir.path(), cfg); + + app1->initialize_plugins(cfg); + app1->startup_plugins(); + + app1->startup(); + fc::usleep(fc::milliseconds(500)); + return app1; +} + +/////////// +/// 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, 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(); + returned_block = db->generate_block( db->get_slot_time(1), + db->get_scheduled_witness(1), + committee_key, + database::skip_nothing ); + return true; + } catch (exception &e) { + return false; + } +} + +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 +/// @returns true on success +/////////// +bool generate_maintenance_block(std::shared_ptr app) { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~0; + auto db = app->chain_database(); + auto maint_time = db->get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db->get_slot_at_time(maint_time); + db->generate_block(db->get_slot_time(slots_to_miss), + db->get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (exception& e) + { + return false; + } +} + +/////////// +/// @brief a class to make connecting to the application server easier +/////////// +class client_connection +{ +public: + ///////// + // constructor + ///////// + client_connection( + std::shared_ptr app, + const fc::temp_directory& data_dir, + const int server_port_number + ) + { + wallet_data.chain_id = app->chain_database()->get_chain_id(); + wallet_data.ws_server = "ws://127.0.0.1:" + std::to_string(server_port_number); + wallet_data.ws_user = ""; + wallet_data.ws_password = ""; + websocket_connection = websocket_client.connect( wallet_data.ws_server ); + + api_connection = std::make_shared(*websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); + + remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); + BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); + + wallet_api_ptr = std::make_shared(wallet_data, remote_login_api); + wallet_filename = data_dir.path().generic_string() + "/wallet.json"; + wallet_api_ptr->set_wallet_filename(wallet_filename); + + wallet_api = fc::api(wallet_api_ptr); + + wallet_cli = std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); + for( auto& name_formatter : wallet_api_ptr->get_result_formatters() ) + wallet_cli->format_result( name_formatter.first, name_formatter.second ); + + boost::signals2::scoped_connection closed_connection(websocket_connection->closed.connect([=]{ + cerr << "Server has disconnected us.\n"; + wallet_cli->stop(); + })); + (void)(closed_connection); + } + ~client_connection() + { + // wait for everything to finish up + fc::usleep(fc::milliseconds(500)); + } +public: + fc::http::websocket_client websocket_client; + graphene::wallet::wallet_data wallet_data; + fc::http::websocket_connection_ptr websocket_connection; + std::shared_ptr api_connection; + fc::api remote_login_api; + std::shared_ptr wallet_api_ptr; + fc::api wallet_api; + std::shared_ptr wallet_cli; + std::string wallet_filename; +}; + + +/////////////////////////////// +// Cli Wallet Fixture +/////////////////////////////// + +struct cli_fixture +{ + class dummy + { + public: + ~dummy() + { + // wait for everything to finish up + fc::usleep(fc::milliseconds(500)); + } + }; + dummy dmy; + int server_port_number; + fc::temp_directory app_dir; + std::shared_ptr app1; + client_connection con; + std::vector nathan_keys; + + cli_fixture() : + server_port_number(0), + app_dir( graphene::utilities::temp_directory_path() ), + app1( start_application(app_dir, server_port_number) ), + con( app1, app_dir, server_port_number ), + nathan_keys( {"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"} ) + { + BOOST_TEST_MESSAGE("Setup cli_wallet::boost_fixture_test_case"); + + using namespace graphene::chain; + using namespace graphene::app; + + try + { + 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"); + BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); + BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } + } + + ~cli_fixture() + { + BOOST_TEST_MESSAGE("Cleanup cli_wallet::boost_fixture_test_case"); + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + + app1->shutdown(); +#ifdef _WIN32 + sockQuit(); +#endif + } +}; + /////////////////////////////// // Tests /////////////////////////////// @@ -39,7 +319,17 @@ BOOST_AUTO_TEST_CASE( cli_connect ) BOOST_TEST_MESSAGE("Testing wallet connection."); } -BOOST_AUTO_TEST_CASE( upgrade_nathan_account ) +//////////////// +// Start a server and connect using the same calls as the CLI +// Quit wallet and be sure that file was saved correctly +//////////////// +BOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture ) +{ + BOOST_TEST_MESSAGE("Testing wallet connection and quit command."); + BOOST_CHECK_THROW( con.wallet_api_ptr->quit(), fc::canceled_exception ); +} + +BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture ) { init_nathan(); } @@ -60,8 +350,11 @@ BOOST_AUTO_TEST_CASE( create_new_account ) BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - // attempt to give jmjatlanta some CORE - BOOST_TEST_MESSAGE("Transferring CORE from Nathan to jmjatlanta"); + BOOST_CHECK(generate_block(app1)); + fc::usleep( fc::seconds(1) ); + + // attempt to give jmjatlanta some peerplays + BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to jmjatlanta"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer( "nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true ); @@ -76,19 +369,22 @@ BOOST_AUTO_TEST_CASE( create_new_account ) // Vote for two witnesses, and make sure they both stay there // after a maintenance block /////////////////////// -BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) + +// Todo: Removed by GPOS, refactor test. +/* +BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) { try { BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses"); - - init_nathan(); + + INVOKE(create_new_account); // get the details for init1 witness_object init1_obj = con.wallet_api_ptr->get_witness("init1"); int init1_start_votes = init1_obj.total_votes; // Vote for a witness - signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true); + signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init1", true, true); // generate a block to get things started BOOST_CHECK(generate_block()); @@ -103,7 +399,7 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) // Vote for a 2nd witness int init2_start_votes = init2_obj.total_votes; - signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init2", true, true); + signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init2", true, true); // send another block to trigger maintenance interval BOOST_CHECK(generate_maintenance_block()); @@ -121,6 +417,43 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) throw; } } +*/ + +BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture ) +{ + try + { + INVOKE(upgrade_nathan_account); + + // register account and transfer funds + const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); + con.wallet_api_ptr->register_account( + "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true + ); + con.wallet_api_ptr->transfer("nathan", "test", "1000", "1.3.0", "", true); + + // import key and save wallet + BOOST_CHECK(con.wallet_api_ptr->import_key("test", test_bki.wif_priv_key)); + con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + + // create transaction and check expected result + auto signed_trx = con.wallet_api_ptr->transfer("test", "nathan", "10", "1.3.0", "", true); + + const auto &test_acc = con.wallet_api_ptr->get_account("test"); + flat_set expected_signers = {test_bki.pub_key}; + vector > expected_key_refs{{test_acc.id, test_acc.id}}; + + auto signers = con.wallet_api_ptr->get_transaction_signers(signed_trx); + BOOST_CHECK(signers == expected_signers); + + auto key_refs = con.wallet_api_ptr->get_key_references({test_bki.pub_key}); + BOOST_CHECK(key_refs == expected_key_refs); + + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} /////////////////////// // Check account history pagination @@ -131,7 +464,7 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) { INVOKE(create_new_account); - // attempt to give jmjatlanta some peerplay + // attempt to give jmjatlanta some peerplay BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta"); for(int i = 1; i <= 199; i++) { @@ -141,13 +474,13 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) BOOST_CHECK(generate_block()); - // now get account history and make sure everything is there (and no duplicates) + // now get account history and make sure everything is there (and no duplicates) std::vector history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300); BOOST_CHECK_EQUAL(201u, history.size() ); - std::set operation_ids; + std::set operation_ids; - for(auto& op : history) + for(auto& op : history) { if( operation_ids.find(op.op.id) != operation_ids.end() ) { @@ -161,4 +494,286 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) } } -BOOST_AUTO_TEST_SUITE_END() +BOOST_FIXTURE_TEST_CASE( cli_get_available_transaction_signers, cli_fixture ) +{ + try + { + INVOKE(upgrade_nathan_account); + + // register account + const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); + con.wallet_api_ptr->register_account( + "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true + ); + const auto &test_acc = con.wallet_api_ptr->get_account("test"); + + // create and sign transaction + signed_transaction trx; + trx.operations = {transfer_operation()}; + + // sign with test key + const auto test_privkey = wif_to_key( test_bki.wif_priv_key ); + BOOST_REQUIRE( test_privkey ); + trx.sign( *test_privkey, con.wallet_data.chain_id ); + + // sign with other keys + const auto privkey_1 = fc::ecc::private_key::generate(); + trx.sign( privkey_1, con.wallet_data.chain_id ); + + const auto privkey_2 = fc::ecc::private_key::generate(); + trx.sign( privkey_2, con.wallet_data.chain_id ); + + // verify expected result + flat_set expected_signers = {test_bki.pub_key, + privkey_1.get_public_key(), + privkey_2.get_public_key()}; + + auto signers = con.wallet_api_ptr->get_transaction_signers(trx); + BOOST_CHECK(signers == expected_signers); + + // blockchain has no references to unknown accounts (privkey_1, privkey_2) + // only test account available + vector > expected_key_refs; + expected_key_refs.push_back(vector()); + expected_key_refs.push_back(vector()); + expected_key_refs.push_back({test_acc.id, test_acc.id}); + + auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()}); + std::sort(key_refs.begin(), key_refs.end()); + + BOOST_CHECK(key_refs == expected_key_refs); + + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_FIXTURE_TEST_CASE( cli_cant_get_signers_from_modified_transaction, cli_fixture ) +{ + try + { + INVOKE(upgrade_nathan_account); + + // register account + const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); + con.wallet_api_ptr->register_account( + "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true + ); + + // create and sign transaction + signed_transaction trx; + trx.operations = {transfer_operation()}; + + // sign with test key + const auto test_privkey = wif_to_key( test_bki.wif_priv_key ); + BOOST_REQUIRE( test_privkey ); + trx.sign( *test_privkey, con.wallet_data.chain_id ); + + // modify transaction (MITM-attack) + trx.operations.clear(); + + // verify if transaction has no valid signature of test account + flat_set expected_signers_of_valid_transaction = {test_bki.pub_key}; + auto signers = con.wallet_api_ptr->get_transaction_signers(trx); + BOOST_CHECK(signers != expected_signers_of_valid_transaction); + + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + +/////////////////// +// Start a server and connect using the same calls as the CLI +// Set a voting proxy and be assured that it sticks +/////////////////// +BOOST_FIXTURE_TEST_CASE( cli_set_voting_proxy, cli_fixture ) +{ + try { + INVOKE(create_new_account); + + // grab account for comparison + account_object prior_voting_account = con.wallet_api_ptr->get_account("jmjatlanta"); + // set the voting proxy to nathan + BOOST_TEST_MESSAGE("About to set voting proxy."); + signed_transaction voting_tx = con.wallet_api_ptr->set_voting_proxy("jmjatlanta", "nathan", true); + account_object after_voting_account = con.wallet_api_ptr->get_account("jmjatlanta"); + // see if it changed + BOOST_CHECK(prior_voting_account.options.voting_account != after_voting_account.options.voting_account); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + + +/////////////////////// +// Create a multi-sig account and verify that only when all signatures are +// signed, the transaction could be broadcast +/////////////////////// +BOOST_AUTO_TEST_CASE( cli_multisig_transaction ) +{ + 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 multisig account + graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr->suggest_brain_key(); + graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr->suggest_brain_key(); + graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr->suggest_brain_key(); + graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki1.brain_priv_key.empty()); + BOOST_CHECK(!bki2.brain_priv_key.empty()); + BOOST_CHECK(!bki3.brain_priv_key.empty()); + BOOST_CHECK(!bki4.brain_priv_key.empty()); + + signed_transaction create_multisig_acct_tx; + account_create_operation account_create_op; + + account_create_op.referrer = nathan_acct_after_upgrade.id; + account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage; + account_create_op.registrar = nathan_acct_after_upgrade.id; + account_create_op.name = "cifer.test"; + account_create_op.owner = authority(1, bki1.pub_key, 1); + account_create_op.active = authority(2, bki2.pub_key, 1, bki3.pub_key, 1); + account_create_op.options.memo_key = bki4.pub_key; + account_create_op.fee = asset(1000000); // should be enough for creating account + + create_multisig_acct_tx.operations.push_back(account_create_op); + con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true); + + // attempt to give cifer.test some peerplays + BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to cifer.test"); + signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer("nathan", "cifer.test", "10000", "1.3.0", "Here are some BTS for your new account", true); + + // transfer bts from cifer.test to nathan + BOOST_TEST_MESSAGE("Transferring peerplays from cifer.test to nathan"); + auto dyn_props = app1->chain_database()->get_dynamic_global_properties(); + account_object cifer_test = con.wallet_api_ptr->get_account("cifer.test"); + + // construct a transfer transaction + signed_transaction transfer_tx2; + transfer_operation xfer_op; + xfer_op.from = cifer_test.id; + xfer_op.to = nathan_acct_after_upgrade.id; + xfer_op.amount = asset(100000000); + xfer_op.fee = asset(3000000); // should be enough for transfer + transfer_tx2.operations.push_back(xfer_op); + + // case1: sign a transaction without TaPoS and expiration fields + // expect: return a transaction with TaPoS and expiration filled + transfer_tx2 = + con.wallet_api_ptr->add_transaction_signature( transfer_tx2, false ); + BOOST_CHECK( ( transfer_tx2.ref_block_num != 0 && + transfer_tx2.ref_block_prefix != 0 ) || + ( transfer_tx2.expiration != fc::time_point_sec() ) ); + + // case2: broadcast without signature + // expect: exception with missing active authority + BOOST_CHECK_THROW(con.wallet_api_ptr->broadcast_transaction(transfer_tx2), fc::exception); + + // case3: + // import one of the private keys for this new account in the wallet file, + // sign and broadcast with partial signatures + // + // expect: exception with missing active authority + BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki2.wif_priv_key)); + BOOST_CHECK_THROW(con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true), fc::exception); + + // case4: sign again as signature exists + // expect: num of signatures not increase + // transfer_tx2 = con.wallet_api_ptr->add_transaction_signature(transfer_tx2, false); + // BOOST_CHECK_EQUAL(transfer_tx2.signatures.size(), 1); + + // case5: + // import another private key, sign and broadcast without full signatures + // + // expect: transaction broadcast successfully + BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki3.wif_priv_key)); + con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true); + auto balances = con.wallet_api_ptr->list_account_balances( "cifer.test" ); + for (auto b : balances) { + if (b.asset_id == asset_id_type()) { + BOOST_ASSERT(b == asset(900000000 - 3000000)); + } + } + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } + app1->shutdown(); +} + +graphene::wallet::plain_keys decrypt_keys( const std::string& password, const vector& cipher_keys ) +{ + auto pw = fc::sha512::hash( password.c_str(), password.size() ); + vector decrypted = fc::aes_decrypt( pw, cipher_keys ); + return fc::raw::unpack( decrypted ); +} + +BOOST_AUTO_TEST_CASE( saving_keys_wallet_test ) +{ + cli_fixture cli; + + cli.con.wallet_api_ptr->import_balance( "nathan", cli.nathan_keys, true ); + cli.con.wallet_api_ptr->upgrade_account( "nathan", true ); + std::string brain_key( "FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL" ); + cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true ); + + BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) ); + + std::string path( cli.app_dir.path().generic_string() + "/wallet.json" ); + graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); + BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan + BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1 + BOOST_CHECK( wallet.pending_account_registrations["account1"].size() == 2 ); // account1 active key + account1 memo key + + graphene::wallet::plain_keys pk = decrypt_keys( "supersecret", wallet.cipher_keys ); + BOOST_CHECK( pk.keys.size() == 1 ); // nathan key + + BOOST_CHECK( generate_block( cli.app1 ) ); + fc::usleep( fc::seconds(1) ); + + wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); + BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1 + BOOST_CHECK( wallet.pending_account_registrations.empty() ); + BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) ); + + pk = decrypt_keys( "supersecret", wallet.cipher_keys ); + BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key +} diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 4e171b14..5631ccaa 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -29,11 +29,9 @@ #include #include #include +#include +#include -#include - -#include -#include #include #include #include @@ -54,7 +52,6 @@ #include #include #include -#include #include "database_fixture.hpp" @@ -82,7 +79,7 @@ database_fixture::database_fixture() std::cout << "running test " << boost::unit_test::framework::current_test_case().p_name << std::endl; } - auto ahplugin = app.register_plugin(); + //auto ahplugin = app.register_plugin(); auto mhplugin = app.register_plugin(); auto bookieplugin = app.register_plugin(); auto affiliateplugin = app.register_plugin(); @@ -128,9 +125,58 @@ database_fixture::database_fixture() options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); } + // standby votes tracking + if( boost::unit_test::framework::current_test_case().p_name.value == "track_votes_witnesses_disabled" || + boost::unit_test::framework::current_test_case().p_name.value == "track_votes_committee_disabled") { + app.chain_database()->enable_standby_votes_tracking( false ); + } + // app.initialize(); - ahplugin->plugin_set_app(&app); - ahplugin->plugin_initialize(options); + + auto test_name = boost::unit_test::framework::current_test_case().p_name.value; + if(test_name == "elasticsearch_account_history" || test_name == "elasticsearch_suite" || + test_name == "elasticsearch_history_api") { + auto esplugin = app.register_plugin(); + esplugin->plugin_set_app(&app); + + options.insert(std::make_pair("elasticsearch-node-url", boost::program_options::variable_value(string("http://localhost:9200/"), false))); + options.insert(std::make_pair("elasticsearch-bulk-replay", boost::program_options::variable_value(uint32_t(2), false))); + options.insert(std::make_pair("elasticsearch-bulk-sync", boost::program_options::variable_value(uint32_t(2), false))); + options.insert(std::make_pair("elasticsearch-start-es-after-block", boost::program_options::variable_value(uint32_t(0), false))); + options.insert(std::make_pair("elasticsearch-visitor", boost::program_options::variable_value(false, false))); + options.insert(std::make_pair("elasticsearch-operation-object", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("elasticsearch-operation-string", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("elasticsearch-mode", boost::program_options::variable_value(uint16_t(2), false))); + + esplugin->plugin_initialize(options); + esplugin->plugin_startup(); + } + else { + auto ahplugin = app.register_plugin(); + app.enable_plugin("affiliate_stats"); + ahplugin->plugin_set_app(&app); + ahplugin->plugin_initialize(options); + ahplugin->plugin_startup(); + } + + if(test_name == "elasticsearch_objects" || test_name == "elasticsearch_suite") { + auto esobjects_plugin = app.register_plugin(); + esobjects_plugin->plugin_set_app(&app); + + options.insert(std::make_pair("es-objects-elasticsearch-url", boost::program_options::variable_value(string("http://localhost:9200/"), false))); + options.insert(std::make_pair("es-objects-bulk-replay", boost::program_options::variable_value(uint32_t(2), false))); + options.insert(std::make_pair("es-objects-bulk-sync", boost::program_options::variable_value(uint32_t(2), false))); + options.insert(std::make_pair("es-objects-proposals", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("es-objects-accounts", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("es-objects-assets", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("es-objects-balances", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("es-objects-limit-orders", boost::program_options::variable_value(true, false))); + options.insert(std::make_pair("es-objects-asset-bitasset", boost::program_options::variable_value(true, false))); + + esobjects_plugin->plugin_initialize(options); + esobjects_plugin->plugin_startup(); + } + mhplugin->plugin_set_app(&app); mhplugin->plugin_initialize(options); bookieplugin->plugin_set_app(&app); @@ -138,7 +184,6 @@ database_fixture::database_fixture() affiliateplugin->plugin_set_app(&app); affiliateplugin->plugin_initialize(options); - ahplugin->plugin_startup(); mhplugin->plugin_startup(); bookieplugin->plugin_startup(); affiliateplugin->plugin_startup(); @@ -194,7 +239,7 @@ void database_fixture::verify_asset_supplies( const database& db ) const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); BOOST_CHECK(core_asset_data.fee_pool == 0); - const simple_index& statistics_index = db.get_index_type>(); + const auto& statistics_index = db.get_index_type().indices(); const auto& balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); const auto& tournaments_index = db.get_index_type().indices(); diff --git a/tests/elasticsearch/main.cpp b/tests/elasticsearch/main.cpp new file mode 100644 index 00000000..28d3522c --- /dev/null +++ b/tests/elasticsearch/main.cpp @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2018 oxarbitrage 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 + +#include "../common/database_fixture.hpp" + +#define BOOST_TEST_MODULE Elastic Search Database Tests +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::app; + +BOOST_FIXTURE_TEST_SUITE( elasticsearch_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { + try { + + CURL *curl; // curl handler + curl = curl_easy_init(); + + graphene::utilities::ES es; + es.curl = curl; + es.elasticsearch_url = "http://localhost:9200/"; + es.index_prefix = "peerplays-"; + //es.auth = "elastic:changeme"; + + // delete all first + auto delete_account_history = graphene::utilities::deleteAll(es); + fc::usleep(fc::milliseconds(1000)); // this is because index.refresh_interval, nothing to worry + + if(delete_account_history) { // all records deleted + + //account_id_type() do 3 ops + create_bitasset("USD", account_id_type()); + auto dan = create_account("dan"); + auto bob = create_account("bob"); + + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + // for later use + //int asset_crobjeate_op_id = operation::tag::value; + //int account_create_op_id = operation::tag::value; + + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + es.endpoint = es.index_prefix + "*/data/_count"; + es.query = query; + + auto res = graphene::utilities::simpleQuery(es); + variant j = fc::json::from_string(res); + auto total = j["count"].as_string(); + BOOST_CHECK_EQUAL(total, "5"); + + es.endpoint = es.index_prefix + "*/data/_search"; + res = graphene::utilities::simpleQuery(es); + j = fc::json::from_string(res); + auto first_id = j["hits"]["hits"][size_t(0)]["_id"].as_string(); + BOOST_CHECK_EQUAL(first_id, "2.9.0"); + + generate_block(); + auto willie = create_account("willie"); + generate_block(); + + fc::usleep(fc::milliseconds(1000)); // index.refresh_interval + + es.endpoint = es.index_prefix + "*/data/_count"; + res = graphene::utilities::simpleQuery(es); + j = fc::json::from_string(res); + + total = j["count"].as_string(); + BOOST_CHECK_EQUAL(total, "7"); + + // do some transfers in 1 block + transfer(account_id_type()(db), bob, asset(100)); + transfer(account_id_type()(db), bob, asset(200)); + transfer(account_id_type()(db), bob, asset(300)); + + generate_block(); + fc::usleep(fc::milliseconds(1000)); // index.refresh_interval + + res = graphene::utilities::simpleQuery(es); + j = fc::json::from_string(res); + + total = j["count"].as_string(); + BOOST_CHECK_EQUAL(total, "13"); + + // check the visitor data + auto block_date = db.head_block_time(); + std::string index_name = graphene::utilities::generateIndexName(block_date, "peerplays-"); + + es.endpoint = index_name + "/data/2.9.12"; // we know last op is a transfer of amount 300 + res = graphene::utilities::getEndPoint(es); + j = fc::json::from_string(res); + auto last_transfer_amount = j["_source"]["operation_history"]["op_object"]["amount_"]["amount"].as_string(); + BOOST_CHECK_EQUAL(last_transfer_amount, "300"); + } + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(elasticsearch_objects) { + try { + + CURL *curl; // curl handler + curl = curl_easy_init(); + + graphene::utilities::ES es; + es.curl = curl; + es.elasticsearch_url = "http://localhost:9200/"; + es.index_prefix = "ppobjects-"; + //es.auth = "elastic:changeme"; + + // delete all first + auto delete_objects = graphene::utilities::deleteAll(es); + + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + if(delete_objects) { // all records deleted + + // asset and bitasset + create_bitasset("USD", account_id_type()); + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + es.endpoint = es.index_prefix + "*/data/_count"; + es.query = query; + + auto res = graphene::utilities::simpleQuery(es); + variant j = fc::json::from_string(res); + auto total = j["count"].as_string(); + BOOST_CHECK_EQUAL(total, "2"); + + es.endpoint = es.index_prefix + "asset/data/_search"; + res = graphene::utilities::simpleQuery(es); + j = fc::json::from_string(res); + auto first_id = j["hits"]["hits"][size_t(0)]["_source"]["symbol"].as_string(); + BOOST_CHECK_EQUAL(first_id, "USD"); + + auto bitasset_data_id = j["hits"]["hits"][size_t(0)]["_source"]["bitasset_data_id"].as_string(); + es.endpoint = es.index_prefix + "bitasset/data/_search"; + es.query = "{ \"query\" : { \"bool\": { \"must\" : [{ \"term\": { \"object_id\": \""+bitasset_data_id+"\"}}] } } }"; + res = graphene::utilities::simpleQuery(es); + j = fc::json::from_string(res); + auto bitasset_object_id = j["hits"]["hits"][size_t(0)]["_source"]["object_id"].as_string(); + BOOST_CHECK_EQUAL(bitasset_object_id, bitasset_data_id); + } + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(elasticsearch_suite) { + try { + + CURL *curl; // curl handler + curl = curl_easy_init(); + + graphene::utilities::ES es; + es.curl = curl; + es.elasticsearch_url = "http://localhost:9200/"; + es.index_prefix = "peerplays-"; + auto delete_account_history = graphene::utilities::deleteAll(es); + fc::usleep(fc::milliseconds(1000)); + es.index_prefix = "ppobjects-"; + auto delete_objects = graphene::utilities::deleteAll(es); + fc::usleep(fc::milliseconds(1000)); + + if(delete_account_history && delete_objects) { // all records deleted + + + } + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(elasticsearch_history_api) { + try { + CURL *curl; // curl handler + curl = curl_easy_init(); + + graphene::utilities::ES es; + es.curl = curl; + es.elasticsearch_url = "http://localhost:9200/"; + es.index_prefix = "peerplays-"; + + auto delete_account_history = graphene::utilities::deleteAll(es); + + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + if(delete_account_history) { + + create_bitasset("USD", account_id_type()); // create op 0 + const account_object& dan = create_account("dan"); // create op 1 + create_bitasset("CNY", dan.id); // create op 2 + create_bitasset("BTC", account_id_type()); // create op 3 + create_bitasset("XMR", dan.id); // create op 4 + create_bitasset("EUR", account_id_type()); // create op 5 + create_bitasset("OIL", dan.id); // create op 6 + + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + graphene::app::history_api hist_api(app); + app.enable_plugin("elasticsearch"); + + // f(A, 0, 4, 9) = { 5, 3, 1, 0 } + auto histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(9)); + + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); + + // f(A, 0, 4, 6) = { 5, 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(6)); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); + + // f(A, 0, 4, 5) = { 5, 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(5)); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); + + // f(A, 0, 4, 4) = { 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(4)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); + + // f(A, 0, 4, 3) = { 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(3)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); + + // f(A, 0, 4, 2) = { 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); + + // f(A, 0, 4, 1) = { 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); + + // f(A, 0, 4, 0) = { 5, 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type()); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); + + // f(A, 1, 5, 9) = { 5, 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(9)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + + // f(A, 1, 5, 6) = { 5, 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(6)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + + // f(A, 1, 5, 5) = { 5, 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(5)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + + // f(A, 1, 5, 4) = { 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(4)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + + // f(A, 1, 5, 3) = { 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(3)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + + // f(A, 1, 5, 2) = { } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(A, 1, 5, 1) = { } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(A, 1, 5, 0) = { 5, 3 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + + // f(A, 0, 3, 9) = { 5, 3, 1 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(9)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(A, 0, 3, 6) = { 5, 3, 1 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(6)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(A, 0, 3, 5) = { 5, 3, 1 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(5)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(A, 0, 3, 4) = { 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(4)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); + + // f(A, 0, 3, 3) = { 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(3)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); + + // f(A, 0, 3, 2) = { 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); + + // f(A, 0, 3, 1) = { 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); + + // f(A, 0, 3, 0) = { 5, 3, 1 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type()); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(B, 0, 4, 9) = { 6, 4, 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(9)); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); + + // f(B, 0, 4, 6) = { 6, 4, 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(6)); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); + + // f(B, 0, 4, 5) = { 4, 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(5)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(B, 0, 4, 4) = { 4, 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(4)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + + // f(B, 0, 4, 3) = { 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(3)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + + // f(B, 0, 4, 2) = { 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); + + // f(B, 0, 4, 1) = { 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); + + // f(B, 0, 4, 0) = { 6, 4, 2, 1 } + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type()); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); + + // f(B, 2, 4, 9) = { 6, 4 } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(9)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + + // f(B, 2, 4, 6) = { 6, 4 } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(6)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + + // f(B, 2, 4, 5) = { 4 } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(5)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + + // f(B, 2, 4, 4) = { 4 } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(4)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + + // f(B, 2, 4, 3) = { } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(3)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(B, 2, 4, 2) = { } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(B, 2, 4, 1) = { } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(B, 2, 4, 0) = { 6, 4 } + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + + // 0 limits + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 0, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(3), 0, operation_history_id_type(9)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // non existent account + histories = hist_api.get_account_history("1.2.18", operation_history_id_type(0), 4, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // create a new account C = alice { 7 } + auto alice = create_account("alice"); + + generate_block(); + fc::usleep(fc::milliseconds(1000)); + + // f(C, 0, 4, 10) = { 7 } + histories = hist_api.get_account_history("alice", operation_history_id_type(0), 4, operation_history_id_type(10)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); + + // f(C, 8, 4, 10) = { } + histories = hist_api.get_account_history("alice", operation_history_id_type(8), 4, operation_history_id_type(10)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 } + histories = hist_api.get_account_history("1.2.0", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 5u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[4].id.instance(), 0u); + } + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 9f74a34c..b7ed69fe 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -745,6 +745,8 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } + + generate_block(); transfer(account_id_type()(db), nathan, asset(5000)); generate_blocks(maintenence_time - initial_properties.parameters.block_interval); @@ -959,18 +961,23 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) processed_transaction ptx; account_object committee_account_object = committee_account(db); + generate_block(skip_flags); // transfer from committee account to Sam account transfer(committee_account_object, sam_account_object, core.amount(100000)); generate_block(skip_flags); - create_account("alice"); + private_key_type charlie_key = generate_private_key("charlie"); + create_account("charlie", charlie_key); generate_block(skip_flags); - create_account("bob"); generate_block(skip_flags); - + private_key_type bob_key = generate_private_key("bob"); + create_account("bob", bob_key); + generate_block(skip_flags); + db.pop_block(); db.pop_block(); + } catch(const fc::exception& e) { edump( (e.to_detail_string()) ); throw; diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp new file mode 100644 index 00000000..aa9969ee --- /dev/null +++ b/tests/tests/gpos_tests.cpp @@ -0,0 +1,1453 @@ +/* + * Copyright (c) 2018 oxarbitrage 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 +#include +#include +#include + +#include "../common/database_fixture.hpp" + +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +struct gpos_fixture: database_fixture +{ + const worker_object& create_worker( const account_id_type owner, const share_type daily_pay, + const fc::microseconds& duration ) { + worker_create_operation op; + op.owner = owner; + op.daily_pay = daily_pay; + op.initializer = vesting_balance_worker_initializer(1); + op.work_begin_date = db.head_block_time(); + op.work_end_date = op.work_begin_date + duration; + trx.operations.push_back(op); + set_expiration(db, trx); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.clear(); + return db.get(ptx.operation_results[0].get()); + } + const vesting_balance_object& create_vesting(const account_id_type owner, const asset amount, + const vesting_balance_type type) + { + vesting_balance_create_operation op; + op.creator = owner; + op.owner = owner; + op.amount = amount; + op.balance_type = type; + + trx.operations.push_back(op); + set_expiration(db, trx); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + return db.get(ptx.operation_results[0].get()); + } + + void withdraw_gpos_vesting(const vesting_balance_id_type v_bid, const account_id_type owner, const asset amount, + /*const vesting_balance_type type, */const fc::ecc::private_key& key) + { + vesting_balance_withdraw_operation op; + op.vesting_balance = v_bid; + op.owner = owner; + op.amount = amount; + //op.balance_type = type; + + trx.operations.push_back(op); + set_expiration(db, trx); + trx.validate(); + sign(trx, key); + PUSH_TX(db, trx); + trx.clear(); + } + + void update_payout_interval(std::string asset_name, fc::time_point start, uint32_t interval) + { + auto dividend_holder_asset_object = get_asset(asset_name); + asset_update_dividend_operation op; + op.issuer = dividend_holder_asset_object.issuer; + op.asset_to_update = dividend_holder_asset_object.id; + op.new_options.next_payout_time = start; + op.new_options.payout_interval = interval; + trx.operations.push_back(op); + set_expiration(db, trx); + PUSH_TX(db, trx, ~0); + trx.operations.clear(); + } + + void update_gpos_global(uint32_t vesting_period, uint32_t vesting_subperiod, fc::time_point_sec period_start) + { + db.modify(db.get_global_properties(), [vesting_period, vesting_subperiod, period_start](global_property_object& p) { + p.parameters.extensions.value.gpos_period = vesting_period; + p.parameters.extensions.value.gpos_subperiod = vesting_subperiod; + p.parameters.extensions.value.gpos_period_start = period_start.sec_since_epoch(); + p.parameters.extensions.value.gpos_vesting_lockin_period = vesting_subperiod; + }); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), vesting_period); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), vesting_subperiod); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), period_start.sec_since_epoch()); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_vesting_lockin_period(), vesting_subperiod); + } + + void update_maintenance_interval(uint32_t new_interval) + { + db.modify(db.get_global_properties(), [new_interval](global_property_object& p) { + p.parameters.maintenance_interval = new_interval; + }); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, new_interval); + } + + void vote_for(const account_id_type account_id, const vote_id_type vote_for, const fc::ecc::private_key& key) + { + account_update_operation op; + op.account = account_id; + op.new_options = account_id(db).options; + op.new_options->votes.insert(vote_for); + op.extensions.value.update_last_voting_time = true; + trx.operations.push_back(op); + set_expiration(db, trx); + trx.validate(); + sign(trx, key); + PUSH_TX(db, trx); + trx.clear(); + } + void fill_reserve_pool(const account_id_type account_id, asset amount) + { + asset_reserve_operation op; + op.payer = account_id; + op.amount_to_reserve = amount; + trx.operations.push_back(op); + trx.validate(); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + } + + void advance_x_maint(int periods) + { + for(int i=0; i(ptx.operation_results[0].get()); + + // check created vesting amount and policy + BOOST_CHECK_EQUAL(alice_vesting.balance.amount.value, 100); + BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_duration_seconds, + db.get_global_properties().parameters.gpos_subperiod()); + BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_cliff_seconds, + db.get_global_properties().parameters.gpos_subperiod()); + + // bob creates a gpos vesting with his custom policy + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = core.amount(200); + op.balance_type = vesting_balance_type::gpos; + op.policy = cdd_vesting_policy_initializer{ 60*60*24 }; + + trx.operations.push_back(op); + set_expiration(db, trx); + ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + } + auto bob_vesting = db.get(ptx.operation_results[0].get()); + + generate_block(); + + // policy is not the one defined by the user but default + BOOST_CHECK_EQUAL(bob_vesting.balance.amount.value, 200); + BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_duration_seconds, + db.get_global_properties().parameters.gpos_subperiod()); + BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_cliff_seconds, + db.get_global_properties().parameters.gpos_subperiod()); + + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( dividends ) +{ + ACTORS((alice)(bob)); + try + { + // move to 1 week before hardfork + generate_blocks( HARDFORK_GPOS_TIME - fc::days(7) ); + generate_block(); + + const auto& core = asset_id_type()(db); + + // all core coins are in the committee_account + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 1000000000000000); + + // transfer half of the total stake to alice so not all the dividends will go to the committee_account + transfer( committee_account, alice_id, core.amount( 500000000000000 ) ); + generate_block(); + + // send some to bob + transfer( committee_account, bob_id, core.amount( 1000 ) ); + generate_block(); + + // committee balance + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999999000); + + // alice balance + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000000); + + // bob balance + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000); + + // get core asset object + const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); + + // by default core token pays dividends once per month + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days + + // update the payout interval for speed purposes of the test + update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day + + generate_block(); + + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 86400); // 1 day now + + // get the dividend distribution account + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + + // transfering some coins to distribution account. + // simulating the blockchain haves some dividends to pay. + transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); + generate_block(); + + // committee balance + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998900 ); + + // distribution account balance + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); + + // get when is the next payout time as we need to advance there + auto next_payout_time = dividend_data.options.next_payout_time; + + // advance to next payout + generate_blocks(*next_payout_time); + wdump((*next_payout_time)); + + // advance to next maint after payout time arrives + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // check balances now, dividends are paid "normally" + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998949 ); + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 ); + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 1); + + // advance to hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + + // advance to next maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // send 99 to the distribution account so it will have 100 PPY again to share + transfer( committee_account, dividend_distribution_account.id, core.amount( 99 ) ); + generate_block(); + + // get when is the next payout time as we need to advance there + next_payout_time = dividend_data.options.next_payout_time; + + // advance to next payout + generate_blocks(*next_payout_time); + + // advance to next maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // make sure no dividends were paid "normally" + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998850 ); + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 ); + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); + + // create vesting balance + create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); + + // need to vote to get paid + auto witness1 = witness_id_type(1)(db); + vote_for(bob_id, witness1.vote_id, bob_private_key); + + generate_block(); + + // check balances + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 900 ); + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); + + // advance to next payout + generate_blocks(*next_payout_time); + + // advance to next maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // check balances, dividends paid to bob + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 0); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( gpos_basic_dividend_distribution_to_core_asset ) +{ + using namespace graphene; + ACTORS((alice)(bob)(carol)(dave)); + try { + const auto& core = asset_id_type()(db); + BOOST_TEST_MESSAGE("Creating test asset"); + { + asset_create_operation creator; + creator.issuer = account_id_type(); + creator.fee = asset(); + creator.symbol = "TESTB"; + creator.common_options.max_supply = 100000000; + creator.precision = 2; + creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/ + creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; + creator.common_options.flags = charge_market_fee; + creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))}); + trx.operations.push_back(std::move(creator)); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + + // pass hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + generate_block(); + + const auto& dividend_holder_asset_object = asset_id_type(0)(db); + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + const account_object& alice = get_account("alice"); + const account_object& bob = get_account("bob"); + const account_object& carol = get_account("carol"); + const account_object& dave = get_account("dave"); + const auto& test_asset_object = get_asset("TESTB"); + + auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue) + { + asset_issue_operation op; + op.issuer = asset_to_issue.issuer; + op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id); + op.issue_to_account = destination_account.id; + trx.operations.push_back( op ); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + }; + + auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) { + int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id, + holder_account_obj.id, + payout_asset_obj.id); + BOOST_CHECK_EQUAL(pending_balance, expected_balance); + }; + + auto advance_to_next_payout_time = [&]() { + // Advance to the next upcoming payout time + BOOST_REQUIRE(dividend_data.options.next_payout_time); + fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time; + idump((next_payout_scheduled_time)); + // generate blocks up to the next scheduled time + generate_blocks(next_payout_scheduled_time); + // if the scheduled time fell on a maintenance interval, then we should have paid out. + // if not, we need to advance to the next maintenance interval to trigger the payout + if (dividend_data.options.next_payout_time) + { + // we know there was a next_payout_time set when we entered this, so if + // it has been cleared, we must have already processed payouts, no need to + // further advance time. + BOOST_REQUIRE(dividend_data.options.next_payout_time); + if (*dividend_data.options.next_payout_time == next_payout_scheduled_time) + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + } + idump((db.head_block_time())); + }; + + // the first test will be testing pending balances, so we need to hit a + // maintenance interval that isn't the payout interval. Payout is + // every 3 days, maintenance interval is every 1 day. + advance_to_next_payout_time(); + + // Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total + // supply of the core asset. + // Then deposit 400 TEST in the distribution account, and see that they + // each are credited 100 TEST. + transfer( committee_account(db), alice, asset( 250000000000000 ) ); + transfer( committee_account(db), bob, asset( 250000000000000 ) ); + transfer( committee_account(db), carol, asset( 250000000000000 ) ); + transfer( committee_account(db), dave, asset( 250000000000000 ) ); + + // create vesting balance + // bob has not vested anything + create_vesting(alice_id, core.amount(25000000), vesting_balance_type::gpos); + create_vesting(carol_id, core.amount(25000000), vesting_balance_type::gpos); + create_vesting(dave_id, core.amount(25000000), vesting_balance_type::gpos); + + // need to vote to get paid + // carol doesn't participate in voting + auto witness1 = witness_id_type(1)(db); + vote_for(alice_id, witness1.vote_id, alice_private_key); + vote_for(bob_id, witness1.vote_id, bob_private_key); + vote_for(dave_id, witness1.vote_id, dave_private_key); + + // issuing 30000 TESTB to the dividend account + // alice and dave should receive 10000 TESTB as they have gpos vesting and + // participated in voting + // bob should not receive any TESTB as he doesn't have gpos vested + // carol should not receive any TESTB as she doesn't participated in voting + // remaining 10000 TESTB should be deposited in commitee_accoount. + BOOST_TEST_MESSAGE("Issuing 30000 TESTB to the dividend account"); + issue_asset_to_account(test_asset_object, dividend_distribution_account, 30000); + + generate_block(); + + 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 + + verify_pending_balance(alice, test_asset_object, 10000); + verify_pending_balance(bob, test_asset_object, 0); + verify_pending_balance(carol, test_asset_object, 0); + verify_pending_balance(dave, test_asset_object, 10000); + + advance_to_next_payout_time(); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout) + { + BOOST_TEST_MESSAGE("Verifying the virtual op was created"); + const account_transaction_history_index& hist_idx = db.get_index_type(); + auto account_history_range = hist_idx.indices().get().equal_range(boost::make_tuple(destination_account.id)); + BOOST_REQUIRE(account_history_range.first != account_history_range.second); + const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db); + const asset_dividend_distribution_operation& distribution_operation = history_object.op.get(); + BOOST_CHECK(distribution_operation.account_id == destination_account.id); + BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout) + != distribution_operation.amounts.end()); + }; + + BOOST_TEST_MESSAGE("Verifying the payouts"); + BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 10000); + verify_dividend_payout_operations(alice, asset(10000, test_asset_object.id)); + verify_pending_balance(alice, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0); + verify_pending_balance(bob, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0); + verify_pending_balance(carol, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 10000); + verify_dividend_payout_operations(dave, asset(10000, test_asset_object.id)); + verify_pending_balance(dave, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(account_id_type(0)(db), test_asset_object), 10000); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( votes_on_gpos_activation ) +{ + ACTORS((alice)(bob)); + try { + const auto& core = asset_id_type()(db); + + // send some asset to alice and bob + transfer( committee_account, alice_id, core.amount( 1000 ) ); + transfer( committee_account, bob_id, core.amount( 1000 ) ); + generate_block(); + + // update default gpos + auto now = db.head_block_time(); + // 5184000 = 60x60x24x6 = 6 days + // 864000 = 60x60x24x1 = 1 days + update_gpos_global(518400, 86400, HARDFORK_GPOS_TIME); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch()); + // no votes for witness 1 + auto witness1 = witness_id_type(1)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 0); + + // no votes for witness 2 + auto witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness2.total_votes, 0); + + // vote for witness1 and witness2 - this before GPOS period starts + vote_for(alice_id, witness1.vote_id, alice_private_key); + vote_for(bob_id, witness2.vote_id, bob_private_key); + + // go to maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // vote is the same as amount in the first subperiod since voting + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 1000); + BOOST_CHECK_EQUAL(witness2.total_votes, 1000); + + update_maintenance_interval(3600); //update maintenance interval to 1hr to evaluate sub-periods + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 3600); + + // move to hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + generate_block(); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 1000); + BOOST_CHECK_EQUAL(witness2.total_votes, 1000); + + // add some vesting to alice and don't add anything for Bob + create_vesting(alice_id, core.amount(99), vesting_balance_type::gpos); + generate_block(); + vote_for(alice_id, witness1.vote_id, alice_private_key); + generate_block(); + + advance_x_maint(1); + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + //System needs to consider votes based on both regular balance + GPOS balance for 1/2 sub-period on GPOS activation + BOOST_CHECK_EQUAL(witness1.total_votes, 1000); + BOOST_CHECK_EQUAL(witness2.total_votes, 1000); + + advance_x_maint(6); + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 1000); + BOOST_CHECK_EQUAL(witness2.total_votes, 1000); + + advance_x_maint(5); + generate_block(); + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + //Since Alice has votes, votes should be based on GPOS balance i.e 99 + //Since Bob not voted after GPOS activation, witness2 votes should be 0 after crossing 1/2 sub-period(12 maintanence intervals in this case) + BOOST_CHECK_EQUAL(witness1.total_votes, 99); + BOOST_CHECK_EQUAL(witness2.total_votes, 0); + + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( voting ) +{ + ACTORS((alice)(bob)); + try { + // move to hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + generate_block(); + + const auto& core = asset_id_type()(db); + + // send some asset to alice and bob + transfer( committee_account, alice_id, core.amount( 1000 ) ); + transfer( committee_account, bob_id, core.amount( 1000 ) ); + generate_block(); + + // default maintenance_interval is 1 day + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 86400); + + // add some vesting to alice and bob + create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); + create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); + generate_block(); + + // default gpos values + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 15552000); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 2592000); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch()); + + // update default gpos for test speed + auto now = db.head_block_time(); + // 5184000 = 60x60x24x60 = 60 days + // 864000 = 60x60x24x10 = 10 days + update_gpos_global(5184000, 864000, now); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 5184000); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 864000); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); + // end global changes + + generate_block(); + + // no votes for witness 1 + auto witness1 = witness_id_type(1)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 0); + + // no votes for witness 2 + auto witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness2.total_votes, 0); + + // vote for witness1 and witness2 - sub-period 1 + vote_for(alice_id, witness1.vote_id, alice_private_key); + vote_for(bob_id, witness2.vote_id, bob_private_key); + + // go to maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // need to consider both gpos and regular balance for first 1/2 sub period + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 1000); + BOOST_CHECK_EQUAL(witness2.total_votes, 1000); + + advance_x_maint(6); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 100); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(4); + + //Bob votes for witness2 - sub-period 2 + vote_for(bob_id, witness2.vote_id, bob_private_key); + // go to maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // vote decay as time pass + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 83); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(10); + //Bob votes for witness2 - sub-period 3 + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // decay more + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 66); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(10); + + // Bob votes for witness2 - sub-period 4 + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // decay more + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 50); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(10); + + // Bob votes for witness2 - sub-period 5 + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // decay more + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 33); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(10); + + // Bob votes for witness2 - sub-period 6 + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // decay more + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 16); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + // we are still in gpos period 1 + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); + + advance_x_maint(5); + // a new GPOS period is in but vote from user is before the start. Whoever votes in 6th sub-period, votes will carry + now = db.head_block_time(); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); + + generate_block(); + + // we are in the second GPOS period, at subperiod 1, + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 0); + //It's critical here, since bob votes in 6th sub-period of last vesting period, witness2 should retain his votes + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + + // lets vote here from alice to generate votes for witness 1 + //vote from bob to reatin VF 1 + vote_for(alice_id, witness1.vote_id, alice_private_key); + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_block(); + + // go to maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 100); + BOOST_CHECK_EQUAL(witness2.total_votes, 100); + + advance_x_maint(10); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 83); + BOOST_CHECK_EQUAL(witness2.total_votes, 83); + + vote_for(bob_id, witness2.vote_id, bob_private_key); + generate_block(); + + advance_x_maint(10); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 66); + BOOST_CHECK_EQUAL(witness2.total_votes, 83); + + // alice votes again, now for witness 2, her vote worth 100 now + vote_for(alice_id, witness2.vote_id, alice_private_key); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + witness1 = witness_id_type(1)(db); + witness2 = witness_id_type(2)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 100); + BOOST_CHECK_EQUAL(witness2.total_votes, 183); + + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( rolling_period_start ) +{ + // period start rolls automatically after HF + try { + // update default gpos global parameters to make this thing faster + update_gpos_global(518400, 86400, HARDFORK_GPOS_TIME); + generate_blocks(HARDFORK_GPOS_TIME); + update_maintenance_interval(3600); //update maintenance interval to 1hr to evaluate sub-periods + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 3600); + + auto vesting_period_1 = db.get_global_properties().parameters.gpos_period_start(); + + auto now = db.head_block_time(); + // moving outside period: + while( db.head_block_time() <= now + fc::days(6) ) + { + generate_block(); + } + generate_block(); + auto vesting_period_2 = db.get_global_properties().parameters.gpos_period_start(); + + //difference between start of two consecutive vesting periods should be 6 days + BOOST_CHECK_EQUAL(vesting_period_1 + 518400, vesting_period_2); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( worker_dividends_voting ) +{ + try { + // advance to HF + generate_blocks(HARDFORK_GPOS_TIME); + generate_block(); + + // update default gpos global parameters to 4 days + auto now = db.head_block_time(); + update_gpos_global(345600, 86400, now); + + generate_block(); + set_expiration(db, trx); + const auto& core = asset_id_type()(db); + + // get core asset object + const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); + + // by default core token pays dividends once per month + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days + + // update the payout interval to 1 day for speed purposes of the test + update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day + + generate_block(); + + // get the dividend distribution account + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + + // transfering some coins to distribution account. + transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); + generate_block(); + + ACTORS((nathan)(voter1)(voter2)(voter3)); + + transfer( committee_account, nathan_id, core.amount( 1000 ) ); + transfer( committee_account, voter1_id, core.amount( 1000 ) ); + transfer( committee_account, voter2_id, core.amount( 1000 ) ); + + generate_block(); + + upgrade_to_lifetime_member(nathan_id); + + auto worker = create_worker(nathan_id, 10, fc::days(6)); + + // add some vesting to voter1 + create_vesting(voter1_id, core.amount(100), vesting_balance_type::gpos); + + // add some vesting to voter2 + create_vesting(voter2_id, core.amount(100), vesting_balance_type::gpos); + + generate_block(); + + // vote for worker + vote_for(voter1_id, worker.vote_for, voter1_private_key); + + // first maint pass, coefficient will be 1 + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + worker = worker_id_type()(db); + BOOST_CHECK_EQUAL(worker.total_votes_for, 100); + + // here dividends are paid to voter1 and voter2 + // voter1 get paid full dividend share as coefficent is at 1 here + BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 950); + + // voter2 didnt voted so he dont get paid + BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900); + + // send some asset to the reserve pool so the worker can get paid + fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2)); + + BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 0); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // worker is getting paid + BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 10); + BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 10); + + // second maint pass, coefficient will be 0.75 + worker = worker_id_type()(db); + BOOST_CHECK_EQUAL(worker.total_votes_for, 75); + + // more decay + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + worker = worker_id_type()(db); + BOOST_CHECK_EQUAL(worker.total_votes_for, 50); + + transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); + generate_block(); + + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996850); + + // more decay + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + worker = worker_id_type()(db); + BOOST_CHECK_EQUAL(worker.total_votes_for, 25); + + // here voter1 get paid again but less money by vesting coefficient + BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 962); + BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900); + + // remaining dividends not paid by coeffcient are sent to committee account + BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996938); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( account_multiple_vesting ) +{ + try { + // advance to HF + generate_blocks(HARDFORK_GPOS_TIME); + generate_block(); + set_expiration(db, trx); + + // update default gpos global parameters to 4 days + auto now = db.head_block_time(); + update_gpos_global(345600, 86400, now); + + ACTORS((sam)(patty)); + + const auto& core = asset_id_type()(db); + + transfer( committee_account, sam_id, core.amount( 300 ) ); + transfer( committee_account, patty_id, core.amount( 100 ) ); + + // add some vesting to sam + create_vesting(sam_id, core.amount(100), vesting_balance_type::gpos); + + // have another balance with 200 more + create_vesting(sam_id, core.amount(200), vesting_balance_type::gpos); + + // patty also have vesting balance + create_vesting(patty_id, core.amount(100), vesting_balance_type::gpos); + + // get core asset object + const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + + // update the payout interval + update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day + + // get the dividend distribution account + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + + // transfering some coins to distribution account. + transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); + generate_block(); + + // vote for a votable object + auto witness1 = witness_id_type(1)(db); + vote_for(sam_id, witness1.vote_id, sam_private_key); + vote_for(patty_id, witness1.vote_id, patty_private_key); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // amount in vested balanced will sum up as voting power + witness1 = witness_id_type(1)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 400); + + // sam get paid dividends + BOOST_CHECK_EQUAL(get_balance(sam_id(db), core), 75); + + // patty also + BOOST_CHECK_EQUAL(get_balance(patty_id(db), core), 25); + + // total vote not decaying + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + witness1 = witness_id_type(1)(db); + + BOOST_CHECK_EQUAL(witness1.total_votes, 300); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( Withdraw_gpos_vesting_balance ) +{ + try { + // advance to HF + generate_blocks(HARDFORK_GPOS_TIME); + generate_block(); + set_expiration(db, trx); + + // update default gpos global parameters to 4 days + auto now = db.head_block_time(); + update_gpos_global(345600, 86400, now); + + ACTORS((alice)(bob)); + + graphene::app::database_api db_api1(db); + const auto& core = asset_id_type()(db); + + + transfer( committee_account, alice_id, core.amount( 500 ) ); + transfer( committee_account, bob_id, core.amount( 99 ) ); + + // add some vesting to Alice, Bob + vesting_balance_object vbo1, vbo2; + vbo1 = create_vesting(alice_id, core.amount(150), vesting_balance_type::gpos); + vbo2 = create_vesting(bob_id, core.amount(99), vesting_balance_type::gpos); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + generate_block(); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_blocks(db.get_global_properties().parameters.gpos_vesting_lockin_period()); + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 350); + withdraw_gpos_vesting(vbo1.id, alice_id, core.amount(50), /*vesting_balance_type::gpos, */alice_private_key); + withdraw_gpos_vesting(vbo2.id, bob_id, core.amount(99), /*vesting_balance_type::gpos, */bob_private_key); + generate_block(); + // verify charles balance + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 400); + BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 99); + + // Add more 50 and 73 vesting objects and withdraw 90 from + // total vesting balance of user + vbo1 = create_vesting(alice_id, core.amount(50), vesting_balance_type::gpos); + vbo2 = create_vesting(alice_id, core.amount(73), vesting_balance_type::gpos); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + generate_block(); + + vector vbos = db_api1.get_vesting_balances("alice"); + asset total_vesting; + for (const vesting_balance_object& vbo : vbos) + { + if (vbo.balance_type == vesting_balance_type::gpos && vbo.balance.asset_id == asset_id_type()) + total_vesting += vbo.balance; + } + // total vesting balance of alice + BOOST_CHECK_EQUAL(total_vesting.amount.value, core.amount(223).amount.value); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_blocks(db.get_global_properties().parameters.gpos_vesting_lockin_period()); + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 277); + withdraw_gpos_vesting(vbo1.id, alice_id, core.amount(90), /*vesting_balance_type::gpos,*/ alice_private_key); + generate_block(); + // verify alice balance + BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 367); + + // verify remaining vesting balance + vbos = db_api1.get_vesting_balances("alice"); + asset remaining_vesting; + for (const vesting_balance_object& vbo : vbos) + { + if (vbo.balance_type == vesting_balance_type::gpos && vbo.balance.asset_id == asset_id_type()) + remaining_vesting += vbo.balance; + } + // remaining vesting balance of alice + BOOST_CHECK_EQUAL(remaining_vesting.amount.value, core.amount(133).amount.value); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +/* +BOOST_AUTO_TEST_CASE( competing_proposals ) +{ + try { + // advance to HF + generate_blocks(HARDFORK_GPOS_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((voter1)(voter2)(worker1)(worker2)); + + const auto& core = asset_id_type()(db); + + transfer( committee_account, worker1_id, core.amount( 1000 ) ); + transfer( committee_account, worker2_id, core.amount( 1000 ) ); + transfer( committee_account, voter1_id, core.amount( 1000 ) ); + transfer( committee_account, voter2_id, core.amount( 1000 ) ); + + create_vesting(voter1_id, core.amount(200), vesting_balance_type::gpos); + create_vesting(voter2_id, core.amount(300), vesting_balance_type::gpos); + + generate_block(); + + auto now = db.head_block_time(); + update_gpos_global(518400, 86400, now); + + update_payout_interval(core.symbol, fc::time_point::now() + fc::minutes(1), 60 * 60 * 24); // 1 day + + upgrade_to_lifetime_member(worker1_id); + upgrade_to_lifetime_member(worker2_id); + + // create 2 competing proposals asking a lot of token + // todo: maybe a refund worker here so we can test with smaller numbers + auto w1 = create_worker(worker1_id, 100000000000, fc::days(10)); + auto w1_id_instance = w1.id.instance(); + auto w2 = create_worker(worker2_id, 100000000000, fc::days(10)); + auto w2_id_instance = w2.id.instance(); + + fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2)); + + // vote for the 2 workers + vote_for(voter1_id, w1.vote_for, voter1_private_key); + vote_for(voter2_id, w2.vote_for, voter2_private_key); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + w1 = worker_id_type(w1_id_instance)(db); + w2 = worker_id_type(w2_id_instance)(db); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // only w2 is getting paid as it haves more votes and money is only enough for 1 + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 100000000000); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 150000000000); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + w1 = worker_id_type(w1_id_instance)(db); + w2 = worker_id_type(w2_id_instance)(db); + + // as votes decay w1 is still getting paid as it always have more votes than w1 + BOOST_CHECK_EQUAL(w1.total_votes_for, 100); + BOOST_CHECK_EQUAL(w2.total_votes_for, 150); + + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 200000000000); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + w1 = worker_id_type(w1_id_instance)(db); + w2 = worker_id_type(w2_id_instance)(db); + + BOOST_CHECK_EQUAL(w1.total_votes_for, 66); + BOOST_CHECK_EQUAL(w2.total_votes_for, 100); + + // worker is sil getting paid as days pass + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 250000000000); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + w1 = worker_id_type(w1_id_instance)(db); + w2 = worker_id_type(w2_id_instance)(db); + + BOOST_CHECK_EQUAL(w1.total_votes_for, 33); + BOOST_CHECK_EQUAL(w2.total_votes_for, 50); + + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + w1 = worker_id_type(w1_id_instance)(db); + w2 = worker_id_type(w2_id_instance)(db); + + // worker2 will not get paid anymore as it haves 0 votes + BOOST_CHECK_EQUAL(w1.total_votes_for, 0); + BOOST_CHECK_EQUAL(w2.total_votes_for, 0); + + BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); + BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} +*/ +BOOST_AUTO_TEST_CASE( proxy_voting ) +{ + ACTORS((alice)(bob)); + try { + // move to hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + generate_block(); + + // database api + graphene::app::database_api db_api(db); + + const auto& core = asset_id_type()(db); + + // send some asset to alice and bob + transfer( committee_account, alice_id, core.amount( 1000 ) ); + transfer( committee_account, bob_id, core.amount( 1000 ) ); + generate_block(); + + // add some vesting to alice and bob + create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); + generate_block(); + + // total balance is 100 rest of data at 0 + auto gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100); + + create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); + generate_block(); + + gpos_info = db_api.get_gpos_info(bob_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + + auto now = db.head_block_time(); + update_gpos_global(518400, 86400, now); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); + + // alice assign bob as voting account + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice_id(db).options; + op.new_options->voting_account = bob_id; + trx.operations.push_back(op); + set_expiration(db, trx); + trx.validate(); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + + generate_block(); + + // vote for witness1 + auto witness1 = witness_id_type(1)(db); + vote_for(bob_id, witness1.vote_id, bob_private_key); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // check vesting factor of current subperiod + BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1); + BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // GPOS 2nd subperiod started. + // vesting factor decay + BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337); + BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // GPOS 3rd subperiod started + // vesting factor decay + BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.66666666666666663); + BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.66666666666666663); + + // vote for witness2 + auto witness2 = witness_id_type(2)(db); + vote_for(bob_id, witness2.vote_id, bob_private_key); + + // vesting factor should be 1 for both alice and bob for the current subperiod + BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1); + BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // vesting factor decay + BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337); + BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( no_proposal ) +{ + try { + + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( database_api ) +{ + ACTORS((alice)(bob)); + try { + // move to hardfork + generate_blocks( HARDFORK_GPOS_TIME ); + generate_block(); + + // database api + graphene::app::database_api db_api(db); + + const auto& core = asset_id_type()(db); + + // send some asset to alice and bob + transfer( committee_account, alice_id, core.amount( 1000 ) ); + transfer( committee_account, bob_id, core.amount( 1000 ) ); + generate_block(); + + // add some vesting to alice and bob + create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); + generate_block(); + + // total balance is 100 rest of data at 0 + auto gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100); + + create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); + generate_block(); + + // total gpos balance is now 200 + gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + + // update default gpos and dividend interval to 10 days + auto now = db.head_block_time(); + update_gpos_global(5184000, 864000, now); // 10 days subperiods + update_payout_interval(core.symbol, now + fc::minutes(1), 60 * 60 * 24 * 10); // 10 days + + generate_block(); + + // no votes for witness 1 + auto witness1 = witness_id_type(1)(db); + BOOST_CHECK_EQUAL(witness1.total_votes, 0); + + // no votes for witness 2 + auto witness2 = witness_id_type(2)(db); + BOOST_CHECK_EQUAL(witness2.total_votes, 0); + + // transfering some coins to distribution account. + const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); + generate_block(); + + // award balance is now 100 + gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 100); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + + // vote for witness1 + vote_for(alice_id, witness1.vote_id, alice_private_key); + vote_for(bob_id, witness1.vote_id, bob_private_key); + + // go to maint + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // payment for alice and bob is done, distribution account is back in 0 + gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 1); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + + advance_x_maint(10); + + // alice vesting coeffcient decay + gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.83333333333333337); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + + advance_x_maint(10); + + // vesting factor for alice decaying more + gpos_info = db_api.get_gpos_info(alice_id); + BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.66666666666666663); + BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); + BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); + } + catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp index 4cbcda89..943b8265 100644 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -55,25 +55,25 @@ BOOST_AUTO_TEST_CASE(get_account_history) { int account_create_op_id = operation::tag::value; //account_id_type() did 3 ops and includes id0 - vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 100, operation_history_id_type()); + vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); BOOST_CHECK_EQUAL(histories[2].op.which(), asset_create_op_id); // 1 account_create op larger than id1 - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 100, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK(histories[0].id.instance() != 0); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // Limit 2 returns 2 result - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 2, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 2, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK(histories[1].id.instance() != 0); BOOST_CHECK_EQUAL(histories[1].op.which(), account_create_op_id); // bob has 1 op - histories = hist_api.get_account_history(bob_acc.get_id(), operation_history_id_type(), 100, operation_history_id_type()); + histories = hist_api.get_account_history("bob", operation_history_id_type(), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); } FC_LOG_AND_RETHROW() @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(zero_id_object) { graphene::app::history_api hist_api(app); // no history at all in the chain - vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0)); + vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); create_bitasset("USD", account_id_type()); // create op 0 @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(zero_id_object) { fc::usleep(fc::milliseconds(2000)); // what if the account only has one history entry and it is 0? - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); } FC_LOG_AND_RETHROW() @@ -107,13 +107,13 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { // account_id_type() and dan share operation id 1(account create) - share can be also in id 0 // no history at all in the chain - vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0)); + vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); create_bitasset("USD", account_id_type()); // create op 0 generate_block(); // what if the account only has one history entry and it is 0? - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { generate_block(); // f(A, 0, 4, 9) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 6) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 5) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -152,33 +152,33 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 4, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 4, 2) = { 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 4, 1) = { 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 4, 0) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -186,103 +186,103 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 1, 5, 9) = { 5, 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(9)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 6) = { 5, 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(6)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 5) = { 5, 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(5)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 4) = { 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(4)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); // f(A, 1, 5, 3) = { 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(3)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); // f(A, 1, 5, 2) = { } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(2)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 1, 5, 1) = { } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(1)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 1, 5, 0) = { 5, 3 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(0)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 0, 3, 9) = { 5, 3, 1 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(9)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 6) = { 5, 3, 1 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(6)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 5) = { 5, 3, 1 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(5)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(4)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 3, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(3)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 3, 2) = { 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(2)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 3, 1) = { 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(1)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 3, 0) = { 5, 3, 1 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type()); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 9) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 0, 4, 6) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -298,38 +298,38 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 0, 4, 5) = { 4, 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 4) = { 4, 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 3) = { 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); // f(B, 0, 4, 2) = { 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); // f(B, 0, 4, 1) = { 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); // f(B, 0, 4, 0) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -337,49 +337,49 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 2, 4, 9) = { 6, 4 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // f(B, 2, 4, 6) = { 6, 4 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // f(B, 2, 4, 5) = { 4 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); // f(B, 2, 4, 4) = { 4 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); // f(B, 2, 4, 3) = { } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 2) = { } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 1) = { } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 0) = { 6, 4 } - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(0)); + histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // 0 limits - histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(0), 0, operation_history_id_type(0)); + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 0, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(3), 0, operation_history_id_type(9)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(3), 0, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 0u); // create a new account C = alice { 7 } @@ -388,16 +388,16 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { generate_block(); // f(C, 0, 4, 10) = { 7 } - histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(0), 4, operation_history_id_type(10)); + histories = hist_api.get_account_history("alice", operation_history_id_type(0), 4, operation_history_id_type(10)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); // f(C, 8, 4, 10) = { } - histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(8), 4, operation_history_id_type(10)); + histories = hist_api.get_account_history("alice", operation_history_id_type(8), 4, operation_history_id_type(10)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 } - histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 5u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u); @@ -407,148 +407,155 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { } FC_LOG_AND_RETHROW() } -//BOOST_AUTO_TEST_CASE(track_account) { -// try { -// graphene::app::history_api hist_api(app); -// -// // account_id_type() is not tracked -// -// // account_id_type() creates alice(not tracked account) -// const account_object& alice = create_account("alice"); -// auto alice_id = alice.id; -// -// //account_id_type() creates some ops -// create_bitasset("CNY", account_id_type()); -// create_bitasset("USD", account_id_type()); -// -// // account_id_type() creates dan(account tracked) -// const account_object& dan = create_account("dan"); -// auto dan_id = dan.id; -// -// // dan makes 1 op -// create_bitasset("EUR", dan_id); -// -// generate_block( ~database::skip_fork_db ); -// -// // anything against account_id_type() should be {} -// vector histories = -// hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// -// // anything against alice should be {} -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// -// // dan should have history -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 2u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); -// BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); -// -// // create more ops, starting with an untracked account -// create_bitasset( "BTC", account_id_type() ); -// create_bitasset( "GBP", dan_id ); -// -// generate_block( ~database::skip_fork_db ); -// -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 3u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); -// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); -// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); -// -// db.pop_block(); -// -// // Try again, should result in same object IDs -// create_bitasset( "BTC", account_id_type() ); -// create_bitasset( "GBP", dan_id ); -// -// generate_block(); -// -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 3u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); -// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); -// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); -// } catch (fc::exception &e) { -// edump((e.to_detail_string())); -// throw; -// } -//} +BOOST_AUTO_TEST_CASE(track_account) { + try { + graphene::app::history_api hist_api(app); -//BOOST_AUTO_TEST_CASE(track_account2) { -// try { -// graphene::app::history_api hist_api(app); -// -// // account_id_type() is tracked -// -// // account_id_type() creates alice(tracked account) -// const account_object& alice = create_account("alice"); -// auto alice_id = alice.id; -// -// //account_id_type() creates some ops -// create_bitasset("CNY", account_id_type()); -// create_bitasset("USD", account_id_type()); -// -// // alice makes 1 op -// create_bitasset("EUR", alice_id); -// -// // account_id_type() creates dan(account not tracked) -// const account_object& dan = create_account("dan"); -// auto dan_id = dan.id; -// -// generate_block(); -// -// // all account_id_type() should have 4 ops {4,2,1,0} -// vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 4u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); -// BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); -// BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); -// BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); -// -// // all alice account should have 2 ops {3, 0} -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 2u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); -// BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); -// -// // alice first op should be {0} -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1)); -// BOOST_CHECK_EQUAL(histories.size(), 1u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); -// -// // alice second op should be {3} -// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 1u); -// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); -// -// // anything against dan should be {} -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2)); -// BOOST_CHECK_EQUAL(histories.size(), 0u); -// -// } catch (fc::exception &e) { -// edump((e.to_detail_string())); -// throw; -// } -//} + // account_id_type() is not tracked + + // account_id_type() creates alice(not tracked account) + const account_object& alice = create_account("alice"); + auto alice_id = alice.id; + + //account_id_type() creates some ops + create_bitasset("CNY", account_id_type()); + create_bitasset("USD", account_id_type()); + + // account_id_type() creates dan(account tracked) + const account_object& dan = create_account("dan"); + auto dan_id = dan.id; + + // dan makes 1 op + create_bitasset("EUR", dan_id); + + generate_block( ~database::skip_fork_db ); + + // anything against account_id_type() should be {} + vector histories = + hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 1, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // anything against alice should be {} + histories = hist_api.get_account_history("alice", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("alice", operation_history_id_type(1), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("alice", operation_history_id_type(1), 1, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + // dan should have history + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); + + // create more ops, starting with an untracked account + create_bitasset( "BTC", account_id_type() ); + create_bitasset( "GBP", dan_id ); + + generate_block( ~database::skip_fork_db ); + + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); + + db.pop_block(); + + // Try again, should result in same object IDs + create_bitasset( "BTC", account_id_type() ); + create_bitasset( "GBP", dan_id ); + + generate_block(); + + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 3u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); + } catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(track_account2) { + try { + graphene::app::history_api hist_api(app); + + // account_id_type() is tracked + + // account_id_type() creates alice(tracked account) + const account_object& alice = create_account("alice"); + auto alice_id = alice.id; + + //account_id_type() creates some ops + create_bitasset("CNY", account_id_type()); + create_bitasset("USD", account_id_type()); + + // alice makes 1 op + create_bitasset("EUR", alice_id); + + // account_id_type() creates dan(account not tracked) + const account_object& dan = create_account("dan"); + auto dan_id = dan.id; + + generate_block(); + + // all account_id_type() should have 4 ops {4,2,1,0} + vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 4u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); + BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); + BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); + + // all alice account should have 2 ops {3, 0} + histories = hist_api.get_account_history("alice", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 2u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); + + // alice first op should be {0} + histories = hist_api.get_account_history("alice", operation_history_id_type(0), 1, operation_history_id_type(1)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); + + // alice second op should be {3} + histories = hist_api.get_account_history("alice", operation_history_id_type(1), 1, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 1u); + BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); + + // anything against dan should be {} + histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("dan", operation_history_id_type(1), 10, operation_history_id_type(0)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + histories = hist_api.get_account_history("dan", operation_history_id_type(1), 1, operation_history_id_type(2)); + BOOST_CHECK_EQUAL(histories.size(), 0u); + + } catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; + } +} BOOST_AUTO_TEST_CASE(get_account_history_operations) { try { graphene::app::history_api hist_api(app); + int asset_create_op_id = operation::tag::value; + int account_create_op_id = operation::tag::value; + + // no asset_create operation on account_id_type() should not throw any exception + vector histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); + BOOST_CHECK_EQUAL(histories.size(), 0u); + //account_id_type() do 3 ops create_bitasset("CNY", account_id_type()); create_account("sam"); @@ -557,31 +564,28 @@ BOOST_AUTO_TEST_CASE(get_account_history_operations) { generate_block(); fc::usleep(fc::milliseconds(2000)); - int asset_create_op_id = operation::tag::value; - int account_create_op_id = operation::tag::value; - //account_id_type() did 1 asset_create op - vector histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); + histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); BOOST_CHECK_EQUAL(histories[0].op.which(), asset_create_op_id); //account_id_type() did 2 account_create ops - histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); + histories = hist_api.get_account_history_operations("committee-account", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // No asset_create op larger than id1 - histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100); + histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100); BOOST_CHECK_EQUAL(histories.size(), 0u); // Limit 1 returns 1 result - histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1); + histories = hist_api.get_account_history_operations("committee-account", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // alice has 1 op - histories = hist_api.get_account_history_operations(get_account("alice").id, account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100); + histories = hist_api.get_account_history_operations("alice", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); @@ -591,4 +595,4 @@ BOOST_AUTO_TEST_CASE(get_account_history_operations) { } } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index b88f485a..79f80e1f 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(last_voting_date) // we are going to vote for this witness auto witness1 = witness_id_type(1)(db); - auto stats_obj = alice_id(db).statistics(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 @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(last_voting_date) auto now = db.head_block_time().sec_since_epoch(); // last_vote_time is updated for alice - stats_obj = alice_id(db).statistics(db); + 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() @@ -163,4 +163,360 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(put_my_witnesses) +{ + try + { + graphene::app::database_api db_api1(db); + + ACTORS( (witness0) + (witness1) + (witness2) + (witness3) + (witness4) + (witness5) + (witness6) + (witness7) + (witness8) + (witness9) + (witness10) + (witness11) + (witness12) + (witness13) ); + + // Upgrade all accounts to LTM + upgrade_to_lifetime_member(witness0_id); + upgrade_to_lifetime_member(witness1_id); + upgrade_to_lifetime_member(witness2_id); + upgrade_to_lifetime_member(witness3_id); + upgrade_to_lifetime_member(witness4_id); + upgrade_to_lifetime_member(witness5_id); + upgrade_to_lifetime_member(witness6_id); + upgrade_to_lifetime_member(witness7_id); + upgrade_to_lifetime_member(witness8_id); + upgrade_to_lifetime_member(witness9_id); + upgrade_to_lifetime_member(witness10_id); + upgrade_to_lifetime_member(witness11_id); + upgrade_to_lifetime_member(witness12_id); + upgrade_to_lifetime_member(witness13_id); + + // Create all the witnesses + const witness_id_type witness0_witness_id = create_witness(witness0_id, witness0_private_key).id; + const witness_id_type witness1_witness_id = create_witness(witness1_id, witness1_private_key).id; + const witness_id_type witness2_witness_id = create_witness(witness2_id, witness2_private_key).id; + const witness_id_type witness3_witness_id = create_witness(witness3_id, witness3_private_key).id; + const witness_id_type witness4_witness_id = create_witness(witness4_id, witness4_private_key).id; + const witness_id_type witness5_witness_id = create_witness(witness5_id, witness5_private_key).id; + const witness_id_type witness6_witness_id = create_witness(witness6_id, witness6_private_key).id; + const witness_id_type witness7_witness_id = create_witness(witness7_id, witness7_private_key).id; + const witness_id_type witness8_witness_id = create_witness(witness8_id, witness8_private_key).id; + const witness_id_type witness9_witness_id = create_witness(witness9_id, witness9_private_key).id; + const witness_id_type witness10_witness_id = create_witness(witness10_id, witness10_private_key).id; + const witness_id_type witness11_witness_id = create_witness(witness11_id, witness11_private_key).id; + const witness_id_type witness12_witness_id = create_witness(witness12_id, witness12_private_key).id; + const witness_id_type witness13_witness_id = create_witness(witness13_id, witness13_private_key).id; + + // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time + const vector private_keys = { + witness0_private_key, + witness1_private_key, + witness2_private_key, + witness3_private_key, + witness4_private_key, + witness5_private_key, + witness6_private_key, + witness7_private_key, + witness8_private_key, + witness9_private_key, + witness10_private_key, + witness11_private_key, + witness12_private_key, + witness13_private_key + + }; + + // create a map with account id and witness id of the first 11 witnesses + const flat_map witness_map = { + {witness0_id, witness0_witness_id}, + {witness1_id, witness1_witness_id}, + {witness2_id, witness2_witness_id}, + {witness3_id, witness3_witness_id}, + {witness4_id, witness4_witness_id}, + {witness5_id, witness5_witness_id}, + {witness6_id, witness6_witness_id}, + {witness7_id, witness7_witness_id}, + {witness8_id, witness8_witness_id}, + {witness9_id, witness9_witness_id}, + {witness10_id, witness10_witness_id}, + {witness11_id, witness11_witness_id}, + {witness12_id, witness12_witness_id}, + {witness13_id, witness13_witness_id} + }; + + // Check current default witnesses, default chain is configured with 10 witnesses + auto witnesses = db.get_global_properties().active_witnesses; + BOOST_CHECK_EQUAL(witnesses.size(), 10); + BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1); + BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2); + BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3); + BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 4); + BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 5); + BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 6); + BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7); + BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8); + BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9); + BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 10); + + // Activate all witnesses + // Each witness is voted with incremental stake so last witness created will be the ones with more votes + int c = 0; + for (auto l : witness_map) { + int stake = 100 + c + 10; + transfer(committee_account, l.first, asset(stake)); + { + set_expiration(db, trx); + account_update_operation op; + op.account = l.first; + op.new_options = l.first(db).options; + op.new_options->votes.insert(l.second(db).vote_id); + + trx.operations.push_back(op); + sign(trx, private_keys.at(c)); + PUSH_TX(db, trx); + trx.clear(); + } + ++c; + } + + // Trigger the new witnesses + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // Check my witnesses are now in control of the system + witnesses = db.get_global_properties().active_witnesses; + BOOST_CHECK_EQUAL(witnesses.size(), 11); + BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 14); + BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 15); + BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 16); + BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 17); + BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 18); + BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 19); + BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 20); + BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 21); + BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 22); + BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 23); + BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 24); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled) +{ + try + { + graphene::app::database_api db_api1(db); + + INVOKE(put_my_witnesses); + + const account_id_type witness1_id= get_account("witness1").id; + auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name); + BOOST_CHECK_EQUAL(witness1_object->total_votes, 111); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(track_votes_witnesses_disabled) +{ + try + { + graphene::app::database_api db_api1(db); + + INVOKE(put_my_witnesses); + + const account_id_type witness1_id= get_account("witness1").id; + auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name); + BOOST_CHECK_EQUAL(witness1_object->total_votes, 0); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(put_my_committee_members) +{ + try + { + graphene::app::database_api db_api1(db); + + ACTORS( (committee0) + (committee1) + (committee2) + (committee3) + (committee4) + (committee5) + (committee6) + (committee7) + (committee8) + (committee9) + (committee10) + (committee11) + (committee12) + (committee13) ); + + // Upgrade all accounts to LTM + upgrade_to_lifetime_member(committee0_id); + upgrade_to_lifetime_member(committee1_id); + upgrade_to_lifetime_member(committee2_id); + upgrade_to_lifetime_member(committee3_id); + upgrade_to_lifetime_member(committee4_id); + upgrade_to_lifetime_member(committee5_id); + upgrade_to_lifetime_member(committee6_id); + upgrade_to_lifetime_member(committee7_id); + upgrade_to_lifetime_member(committee8_id); + upgrade_to_lifetime_member(committee9_id); + upgrade_to_lifetime_member(committee10_id); + upgrade_to_lifetime_member(committee11_id); + upgrade_to_lifetime_member(committee12_id); + upgrade_to_lifetime_member(committee13_id); + + // Create all the committee + const committee_member_id_type committee0_committee_id = create_committee_member(committee0_id(db)).id; + const committee_member_id_type committee1_committee_id = create_committee_member(committee1_id(db)).id; + const committee_member_id_type committee2_committee_id = create_committee_member(committee2_id(db)).id; + const committee_member_id_type committee3_committee_id = create_committee_member(committee3_id(db)).id; + const committee_member_id_type committee4_committee_id = create_committee_member(committee4_id(db)).id; + const committee_member_id_type committee5_committee_id = create_committee_member(committee5_id(db)).id; + const committee_member_id_type committee6_committee_id = create_committee_member(committee6_id(db)).id; + const committee_member_id_type committee7_committee_id = create_committee_member(committee7_id(db)).id; + const committee_member_id_type committee8_committee_id = create_committee_member(committee8_id(db)).id; + const committee_member_id_type committee9_committee_id = create_committee_member(committee9_id(db)).id; + const committee_member_id_type committee10_committee_id = create_committee_member(committee10_id(db)).id; + const committee_member_id_type committee11_committee_id = create_committee_member(committee11_id(db)).id; + const committee_member_id_type committee12_committee_id = create_committee_member(committee12_id(db)).id; + const committee_member_id_type committee13_committee_id = create_committee_member(committee13_id(db)).id; + + // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time + const vector private_keys = { + committee0_private_key, + committee1_private_key, + committee2_private_key, + committee3_private_key, + committee4_private_key, + committee5_private_key, + committee6_private_key, + committee7_private_key, + committee8_private_key, + committee9_private_key, + committee10_private_key, + committee11_private_key, + committee12_private_key, + committee13_private_key + }; + + // create a map with account id and committee id of the first 11 witnesses + const flat_map committee_map = { + {committee0_id, committee0_committee_id}, + {committee1_id, committee1_committee_id}, + {committee2_id, committee2_committee_id}, + {committee3_id, committee3_committee_id}, + {committee4_id, committee4_committee_id}, + {committee5_id, committee5_committee_id}, + {committee6_id, committee6_committee_id}, + {committee7_id, committee7_committee_id}, + {committee8_id, committee8_committee_id}, + {committee9_id, committee9_committee_id}, + {committee10_id, committee10_committee_id}, + {committee11_id, committee11_committee_id}, + {committee12_id, committee12_committee_id}, + {committee13_id, committee13_committee_id} + }; + + // Check current default witnesses, default chain is configured with 10 witnesses + auto committee_members = db.get_global_properties().active_committee_members; + + BOOST_CHECK_EQUAL(committee_members.size(), 10); + BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 0); + BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 1); + BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 2); + BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 3); + BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 4); + BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 5); + BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 6); + BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 7); + BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 8); + BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 9); + + // Activate all committee + // Each witness is voted with incremental stake so last witness created will be the ones with more votes + int c = 0; + for (auto committee : committee_map) { + int stake = 100 + c + 10; + transfer(committee_account, committee.first, asset(stake)); + { + set_expiration(db, trx); + account_update_operation op; + op.account = committee.first; + op.new_options = committee.first(db).options; + op.new_options->votes.insert(committee.second(db).vote_id); + + trx.operations.push_back(op); + sign(trx, private_keys.at(c)); + PUSH_TX(db, trx); + trx.clear(); + } + ++c; + } + + // Trigger the new committee + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // Check my witnesses are now in control of the system + committee_members = db.get_global_properties().active_committee_members; + BOOST_CHECK_EQUAL(committee_members.size(), 11); + + /* TODO we are not in full control, seems to committee members have votes by default + BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 14); + BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 15); + BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 16); + BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 17); + BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 18); + BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 19); + BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 20); + BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 21); + BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 22); + BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 23); + BOOST_CHECK_EQUAL(committee_members.begin()[10].instance.value, 24); + */ + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(track_votes_committee_enabled) +{ + try + { + graphene::app::database_api db_api1(db); + + INVOKE(put_my_committee_members); + + const account_id_type committee1_id= get_account("committee1").id; + auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name); + BOOST_CHECK_EQUAL(committee1_object->total_votes, 111); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(track_votes_committee_disabled) +{ + try + { + graphene::app::database_api db_api1(db); + + INVOKE(put_my_committee_members); + + const account_id_type committee1_id= get_account("committee1").id; + auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name); + BOOST_CHECK_EQUAL(committee1_object->total_votes, 0); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From dbf73509ba5f74b13a8bab01702c5a2c17410cdb Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Thu, 5 Mar 2020 02:21:15 +1100 Subject: [PATCH 46/64] SON118 - Add tx sign metrics for SON rewards (#302) --- libraries/chain/db_maint.cpp | 1 + libraries/chain/include/graphene/chain/son_object.hpp | 3 +++ libraries/chain/sidechain_transaction_evaluator.cpp | 4 ++++ tests/tests/sidechain_transaction_tests.cpp | 6 ++++++ tests/tests/son_operations_tests.cpp | 3 +++ 5 files changed, 17 insertions(+) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 3c1685b3..16a9ff95 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -153,6 +153,7 @@ void database::pay_sons() //Reset the tx counter in each son statistics object modify( s, [&]( son_statistics_object& _s) { + _s.total_txs_signed += _s.txs_signed; _s.txs_signed = 0; }); } diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 7a73a312..0d4a7317 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -30,6 +30,8 @@ namespace graphene { namespace chain { static const uint8_t type_id = impl_son_statistics_object_type; son_id_type owner; + // Lifetime total transactions signed + uint64_t total_txs_signed = 0; // Transactions signed since the last son payouts uint64_t txs_signed = 0; // Total Downtime barring the current down time in seconds, used for stats to present to user @@ -103,6 +105,7 @@ FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (graphene::db::object), (owner) + (total_txs_signed) (txs_signed) (total_downtime) (current_interval_downtime) diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index 7a684b79..a77c7f64 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -75,6 +75,10 @@ object_id_type bitcoin_transaction_sign_evaluator::do_apply(const bitcoin_transa po.proposed_transaction.operations[0] = bitcoin_transaction_send_op; }); + db().modify( son_obj->statistics( db() ), [&]( son_statistics_object& sso ) { + sso.txs_signed += 1; + } ); + update_proposal(op); } FC_CAPTURE_AND_RETHROW((op)) diff --git a/tests/tests/sidechain_transaction_tests.cpp b/tests/tests/sidechain_transaction_tests.cpp index 0246d009..25e319f0 100644 --- a/tests/tests/sidechain_transaction_tests.cpp +++ b/tests/tests/sidechain_transaction_tests.cpp @@ -296,6 +296,12 @@ BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) BOOST_REQUIRE(btobj->processed == false); + auto stats1 = son_obj1->statistics( db ); + auto stats2 = son_obj2->statistics( db ); + + BOOST_REQUIRE(stats1.txs_signed == 1); + BOOST_REQUIRE(stats2.txs_signed == 1); + auto sigs = btobj->signatures; BOOST_REQUIRE(sigs[son_obj1->id][0] == a1); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 13e3cf1f..9f3c0937 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -513,6 +513,9 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) // Check if the signed transaction statistics are reset for both SONs BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed, 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed, 0); + + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed, 2); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed, 3); // Check that Alice and Bob are paid for signing the transactions in the previous day/cycle BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance); BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance); From da4954c686f5283f790d8dc3b09a7c21c307085c Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Thu, 5 Mar 2020 16:20:24 -0400 Subject: [PATCH 47/64] resolved compilation issues and other conflicts --- libraries/chain/db_getter.cpp | 2 + libraries/chain/proposal_evaluator.cpp | 1 + libraries/fc | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 15 - libraries/wallet/wallet.cpp | 51 +- programs/witness_node/main.cpp | 7 - tests/CMakeLists.txt | 4 +- tests/cli/cli_fixture.cpp | 2 +- tests/cli/main.cpp | 641 +----------------- tests/common/genesis_file_util.hpp | 2 +- 10 files changed, 31 insertions(+), 696 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 30fd776f..bf5cc2dc 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -250,6 +250,8 @@ bool database::is_son_dereg_valid( son_id_type son_id ) bool ret = ( son->status == son_status::in_maintenance && (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); return ret; +} + const account_statistics_object& database::get_account_stats_by_owner( account_id_type owner )const { auto& idx = get_index_type().indices().get(); diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index f1eef69f..6664476f 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -150,6 +150,7 @@ struct proposal_operation_hardfork_visitor void operator()(const son_maintenance_operation &v) const { FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" ); + } void operator()(const vesting_balance_create_operation &vbco) const { if(block_time < HARDFORK_GPOS_TIME) diff --git a/libraries/fc b/libraries/fc index 6096e94e..a76b9ff8 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 6096e94e1b4c48a393c9335580365df144f2758f +Subproject commit a76b9ff81c6887ebe1dc9fa03ef15e1433029c65 diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 91eb8dbd..61342530 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2139,20 +2139,6 @@ class wallet_api rock_paper_scissors_gesture gesture, bool broadcast); - /** Create a vesting balance including gpos vesting balance after HARDFORK_GPOS_TIME - * @param owner vesting balance owner and creator - * @param amount amount to vest - * @param asset_symbol the symbol of the asset to vest - * @param is_gpos True if the balance is of gpos type - * @param broadcast true if you wish to broadcast the transaction - * @return the signed version of the transaction - */ - signed_transaction create_vesting_balance(string owner, - string amount, - string asset_symbol, - bool is_gpos, - bool broadcast); - void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); void dbg_push_blocks( std::string src_filename, uint32_t count ); @@ -2333,7 +2319,6 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) - (create_vesting_balance) (get_vesting_balances) (withdraw_vesting) (withdraw_GPOS_vesting_balance) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index a7ce3b87..fb9d242d 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2094,13 +2094,16 @@ public: return swi.son_id; }); std::vector> son_objects = _remote_db->get_sons(son_ids); - vector owners; + vector owners; for(auto obj: son_objects) { if (obj) - owners.push_back(obj->son_account); + { + std::string acc_id = account_id_to_string(obj->son_account); + owners.push_back(acc_id); + } } - vector> accs = _remote_db->get_accounts(owners); + vector< optional< account_object> > accs = _remote_db->get_accounts(owners); std::remove_if(son_objects.begin(), son_objects.end(), [](const fc::optional& obj) -> bool { return obj.valid(); }); map result; @@ -2383,13 +2386,14 @@ public: vesting_balance_type vesting_type, bool broadcast /* = false */) { try { - account_object son_account = get_account(owner_account); + FC_ASSERT( !is_locked() ); + account_object user_account = get_account(owner_account); fc::optional asset_obj = get_asset(asset_symbol); FC_ASSERT(asset_obj, "Invalid asset symbol {asst}", ("asst", asset_symbol)); vesting_balance_create_operation op; - op.creator = son_account.get_id(); - op.owner = son_account.get_id(); + op.creator = user_account.get_id(); + op.owner = user_account.get_id(); op.amount = asset_obj->amount_from_string(amount); op.balance_type = vesting_type; if (op.balance_type == vesting_balance_type::son) @@ -6670,41 +6674,6 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id, return my->sign_transaction( tx, broadcast ); } -signed_transaction wallet_api::create_vesting_balance(string owner, - string amount, - string asset_symbol, - bool is_gpos, - bool broadcast) -{ - FC_ASSERT( !is_locked() ); - //Can be deleted after GPOS hardfork time - time_point_sec now = time_point::now(); - if(is_gpos && now < HARDFORK_GPOS_TIME) - FC_THROW("GPOS related functionality is not avaiable until next Spring"); - - account_object owner_account = get_account(owner); - account_id_type owner_id = owner_account.id; - - fc::optional asset_obj = get_asset(asset_symbol); - - auto type = vesting_balance_type::normal; - if(is_gpos) - type = vesting_balance_type::gpos; - - vesting_balance_create_operation op; - op.creator = owner_id; - op.owner = owner_id; - op.amount = asset_obj->amount_from_string(amount); - op.balance_type = type; - - signed_transaction trx; - trx.operations.push_back(op); - my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); - trx.validate(); - - return my->sign_transaction( trx, broadcast ); -} - // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info() { diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 19b1460d..7823fed3 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -44,11 +44,8 @@ #include #include -<<<<<<< HEAD #include #include -======= ->>>>>>> 24e7610bceb97ab361fe003622c80a79bdecf730 #include #include @@ -199,10 +196,6 @@ int main(int argc, char** argv) { elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string())); node->shutdown(); delete node; -<<<<<<< HEAD return EXIT_FAILURE; -======= - return 1; ->>>>>>> 24e7610bceb97ab361fe003622c80a79bdecf730 } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3cb14768..5162f692 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,7 +35,7 @@ target_link_libraries( betting_test graphene_chain graphene_app graphene_account file(GLOB PEERPLAYS_SIDECHAIN_TESTS "peerplays_sidechain/*.cpp") add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) @@ -50,7 +50,7 @@ add_executable( cli_test ${CLI_SOURCES} ) if(WIN32) list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32) endif() -target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) diff --git a/tests/cli/cli_fixture.cpp b/tests/cli/cli_fixture.cpp index 5b5fd7ad..8a382e0b 100644 --- a/tests/cli/cli_fixture.cpp +++ b/tests/cli/cli_fixture.cpp @@ -129,7 +129,7 @@ client_connection::client_connection( wallet_data.ws_password = ""; websocket_connection = websocket_client.connect( wallet_data.ws_server ); - api_connection = std::make_shared(websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); + api_connection = std::make_shared(*websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 505178c0..d300005b 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -23,291 +23,11 @@ */ #include "cli_fixture.hpp" -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include #include #define BOOST_TEST_MODULE Test Application #include -/***** - * Global Initialization for Windows - * ( sets up Winsock stuf ) - */ -#ifdef _WIN32 -int sockInit(void) -{ - WSADATA wsa_data; - return WSAStartup(MAKEWORD(1,1), &wsa_data); -} -int sockQuit(void) -{ - return WSACleanup(); -} -#endif - -/********************* - * Helper Methods - *********************/ - -#include "../common/genesis_file_util.hpp" - -using std::exception; -using std::cerr; - -#define INVOKE(test) ((struct test*)this)->test_method(); - -////// -/// @brief attempt to find an available port on localhost -/// @returns an available port number, or -1 on error -///// -int get_available_port() -{ - struct sockaddr_in sin; - int socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if (socket_fd == -1) - return -1; - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1) - return -1; - socklen_t len = sizeof(sin); - if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1) - return -1; -#ifdef _WIN32 - closesocket(socket_fd); -#else - close(socket_fd); -#endif - return ntohs(sin.sin_port); -} - -/////////// -/// @brief Start the application -/// @param app_dir the temporary directory to use -/// @param server_port_number to be filled with the rpc endpoint port number -/// @returns the application object -////////// -std::shared_ptr start_application(fc::temp_directory& app_dir, int& server_port_number) { - std::shared_ptr app1(new graphene::app::application{}); - - app1->register_plugin< graphene::bookie::bookie_plugin>(); - app1->register_plugin(); - app1->register_plugin< graphene::market_history::market_history_plugin >(); - app1->register_plugin< graphene::witness_plugin::witness_plugin >(); - app1->startup_plugins(); - boost::program_options::variables_map cfg; -#ifdef _WIN32 - sockInit(); -#endif - server_port_number = get_available_port(); - cfg.emplace( - "rpc-endpoint", - boost::program_options::variable_value(string("127.0.0.1:" + std::to_string(server_port_number)), false) - ); - cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); - cfg.emplace("seed-nodes", boost::program_options::variable_value(string("[]"), false)); - - app1->initialize(app_dir.path(), cfg); - - app1->initialize_plugins(cfg); - app1->startup_plugins(); - - app1->startup(); - fc::usleep(fc::milliseconds(500)); - return app1; -} - -/////////// -/// 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, 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(); - returned_block = db->generate_block( db->get_slot_time(1), - db->get_scheduled_witness(1), - committee_key, - database::skip_nothing ); - return true; - } catch (exception &e) { - return false; - } -} - -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 -/// @returns true on success -/////////// -bool generate_maintenance_block(std::shared_ptr app) { - try { - fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); - uint32_t skip = ~0; - auto db = app->chain_database(); - auto maint_time = db->get_dynamic_global_properties().next_maintenance_time; - auto slots_to_miss = db->get_slot_at_time(maint_time); - db->generate_block(db->get_slot_time(slots_to_miss), - db->get_scheduled_witness(slots_to_miss), - committee_key, - skip); - return true; - } catch (exception& e) - { - return false; - } -} - -/////////// -/// @brief a class to make connecting to the application server easier -/////////// -class client_connection -{ -public: - ///////// - // constructor - ///////// - client_connection( - std::shared_ptr app, - const fc::temp_directory& data_dir, - const int server_port_number - ) - { - wallet_data.chain_id = app->chain_database()->get_chain_id(); - wallet_data.ws_server = "ws://127.0.0.1:" + std::to_string(server_port_number); - wallet_data.ws_user = ""; - wallet_data.ws_password = ""; - websocket_connection = websocket_client.connect( wallet_data.ws_server ); - - api_connection = std::make_shared(*websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); - - remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); - BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); - - wallet_api_ptr = std::make_shared(wallet_data, remote_login_api); - wallet_filename = data_dir.path().generic_string() + "/wallet.json"; - wallet_api_ptr->set_wallet_filename(wallet_filename); - - wallet_api = fc::api(wallet_api_ptr); - - wallet_cli = std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); - for( auto& name_formatter : wallet_api_ptr->get_result_formatters() ) - wallet_cli->format_result( name_formatter.first, name_formatter.second ); - - boost::signals2::scoped_connection closed_connection(websocket_connection->closed.connect([=]{ - cerr << "Server has disconnected us.\n"; - wallet_cli->stop(); - })); - (void)(closed_connection); - } - ~client_connection() - { - // wait for everything to finish up - fc::usleep(fc::milliseconds(500)); - } -public: - fc::http::websocket_client websocket_client; - graphene::wallet::wallet_data wallet_data; - fc::http::websocket_connection_ptr websocket_connection; - std::shared_ptr api_connection; - fc::api remote_login_api; - std::shared_ptr wallet_api_ptr; - fc::api wallet_api; - std::shared_ptr wallet_cli; - std::string wallet_filename; -}; - - -/////////////////////////////// -// Cli Wallet Fixture -/////////////////////////////// - -struct cli_fixture -{ - class dummy - { - public: - ~dummy() - { - // wait for everything to finish up - fc::usleep(fc::milliseconds(500)); - } - }; - dummy dmy; - int server_port_number; - fc::temp_directory app_dir; - std::shared_ptr app1; - client_connection con; - std::vector nathan_keys; - - cli_fixture() : - server_port_number(0), - app_dir( graphene::utilities::temp_directory_path() ), - app1( start_application(app_dir, server_port_number) ), - con( app1, app_dir, server_port_number ), - nathan_keys( {"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"} ) - { - BOOST_TEST_MESSAGE("Setup cli_wallet::boost_fixture_test_case"); - - using namespace graphene::chain; - using namespace graphene::app; - - try - { - 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"); - BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); - BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } - } - - ~cli_fixture() - { - BOOST_TEST_MESSAGE("Cleanup cli_wallet::boost_fixture_test_case"); - - // wait for everything to finish up - fc::usleep(fc::seconds(1)); - - app1->shutdown(); -#ifdef _WIN32 - sockQuit(); -#endif - } -}; - /////////////////////////////// // Tests /////////////////////////////// @@ -319,17 +39,7 @@ BOOST_AUTO_TEST_CASE( cli_connect ) BOOST_TEST_MESSAGE("Testing wallet connection."); } -//////////////// -// Start a server and connect using the same calls as the CLI -// Quit wallet and be sure that file was saved correctly -//////////////// -BOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture ) -{ - BOOST_TEST_MESSAGE("Testing wallet connection and quit command."); - BOOST_CHECK_THROW( con.wallet_api_ptr->quit(), fc::canceled_exception ); -} - -BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture ) +BOOST_AUTO_TEST_CASE( upgrade_nathan_account ) { init_nathan(); } @@ -350,11 +60,8 @@ BOOST_AUTO_TEST_CASE( create_new_account ) BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - BOOST_CHECK(generate_block(app1)); - fc::usleep( fc::seconds(1) ); - - // attempt to give jmjatlanta some peerplays - BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to jmjatlanta"); + // attempt to give jmjatlanta some CORE + BOOST_TEST_MESSAGE("Transferring CORE from Nathan to jmjatlanta"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer( "nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true ); @@ -369,22 +76,19 @@ BOOST_AUTO_TEST_CASE( create_new_account ) // Vote for two witnesses, and make sure they both stay there // after a maintenance block /////////////////////// - -// Todo: Removed by GPOS, refactor test. -/* -BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) +BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) { try { BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses"); - - INVOKE(create_new_account); + + init_nathan(); // get the details for init1 witness_object init1_obj = con.wallet_api_ptr->get_witness("init1"); int init1_start_votes = init1_obj.total_votes; // Vote for a witness - signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init1", true, true); + signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true); // generate a block to get things started BOOST_CHECK(generate_block()); @@ -399,7 +103,7 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) // Vote for a 2nd witness int init2_start_votes = init2_obj.total_votes; - signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init2", true, true); + signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init2", true, true); // send another block to trigger maintenance interval BOOST_CHECK(generate_maintenance_block()); @@ -417,43 +121,6 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) throw; } } -*/ - -BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture ) -{ - try - { - INVOKE(upgrade_nathan_account); - - // register account and transfer funds - const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); - con.wallet_api_ptr->register_account( - "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true - ); - con.wallet_api_ptr->transfer("nathan", "test", "1000", "1.3.0", "", true); - - // import key and save wallet - BOOST_CHECK(con.wallet_api_ptr->import_key("test", test_bki.wif_priv_key)); - con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - - // create transaction and check expected result - auto signed_trx = con.wallet_api_ptr->transfer("test", "nathan", "10", "1.3.0", "", true); - - const auto &test_acc = con.wallet_api_ptr->get_account("test"); - flat_set expected_signers = {test_bki.pub_key}; - vector > expected_key_refs{{test_acc.id, test_acc.id}}; - - auto signers = con.wallet_api_ptr->get_transaction_signers(signed_trx); - BOOST_CHECK(signers == expected_signers); - - auto key_refs = con.wallet_api_ptr->get_key_references({test_bki.pub_key}); - BOOST_CHECK(key_refs == expected_key_refs); - - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } -} /////////////////////// // Check account history pagination @@ -464,7 +131,7 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) { INVOKE(create_new_account); - // attempt to give jmjatlanta some peerplay + // attempt to give jmjatlanta some peerplay BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta"); for(int i = 1; i <= 199; i++) { @@ -474,13 +141,13 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) BOOST_CHECK(generate_block()); - // now get account history and make sure everything is there (and no duplicates) + // now get account history and make sure everything is there (and no duplicates) std::vector history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300); BOOST_CHECK_EQUAL(201u, history.size() ); - std::set operation_ids; + std::set operation_ids; - for(auto& op : history) + for(auto& op : history) { if( operation_ids.find(op.op.id) != operation_ids.end() ) { @@ -494,286 +161,4 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) } } -BOOST_FIXTURE_TEST_CASE( cli_get_available_transaction_signers, cli_fixture ) -{ - try - { - INVOKE(upgrade_nathan_account); - - // register account - const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); - con.wallet_api_ptr->register_account( - "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true - ); - const auto &test_acc = con.wallet_api_ptr->get_account("test"); - - // create and sign transaction - signed_transaction trx; - trx.operations = {transfer_operation()}; - - // sign with test key - const auto test_privkey = wif_to_key( test_bki.wif_priv_key ); - BOOST_REQUIRE( test_privkey ); - trx.sign( *test_privkey, con.wallet_data.chain_id ); - - // sign with other keys - const auto privkey_1 = fc::ecc::private_key::generate(); - trx.sign( privkey_1, con.wallet_data.chain_id ); - - const auto privkey_2 = fc::ecc::private_key::generate(); - trx.sign( privkey_2, con.wallet_data.chain_id ); - - // verify expected result - flat_set expected_signers = {test_bki.pub_key, - privkey_1.get_public_key(), - privkey_2.get_public_key()}; - - auto signers = con.wallet_api_ptr->get_transaction_signers(trx); - BOOST_CHECK(signers == expected_signers); - - // blockchain has no references to unknown accounts (privkey_1, privkey_2) - // only test account available - vector > expected_key_refs; - expected_key_refs.push_back(vector()); - expected_key_refs.push_back(vector()); - expected_key_refs.push_back({test_acc.id, test_acc.id}); - - auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()}); - std::sort(key_refs.begin(), key_refs.end()); - - BOOST_CHECK(key_refs == expected_key_refs); - - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_FIXTURE_TEST_CASE( cli_cant_get_signers_from_modified_transaction, cli_fixture ) -{ - try - { - INVOKE(upgrade_nathan_account); - - // register account - const auto test_bki = con.wallet_api_ptr->suggest_brain_key(); - con.wallet_api_ptr->register_account( - "test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true - ); - - // create and sign transaction - signed_transaction trx; - trx.operations = {transfer_operation()}; - - // sign with test key - const auto test_privkey = wif_to_key( test_bki.wif_priv_key ); - BOOST_REQUIRE( test_privkey ); - trx.sign( *test_privkey, con.wallet_data.chain_id ); - - // modify transaction (MITM-attack) - trx.operations.clear(); - - // verify if transaction has no valid signature of test account - flat_set expected_signers_of_valid_transaction = {test_bki.pub_key}; - auto signers = con.wallet_api_ptr->get_transaction_signers(trx); - BOOST_CHECK(signers != expected_signers_of_valid_transaction); - - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } -} - -/////////////////// -// Start a server and connect using the same calls as the CLI -// Set a voting proxy and be assured that it sticks -/////////////////// -BOOST_FIXTURE_TEST_CASE( cli_set_voting_proxy, cli_fixture ) -{ - try { - INVOKE(create_new_account); - - // grab account for comparison - account_object prior_voting_account = con.wallet_api_ptr->get_account("jmjatlanta"); - // set the voting proxy to nathan - BOOST_TEST_MESSAGE("About to set voting proxy."); - signed_transaction voting_tx = con.wallet_api_ptr->set_voting_proxy("jmjatlanta", "nathan", true); - account_object after_voting_account = con.wallet_api_ptr->get_account("jmjatlanta"); - // see if it changed - BOOST_CHECK(prior_voting_account.options.voting_account != after_voting_account.options.voting_account); - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } -} - - -/////////////////////// -// Create a multi-sig account and verify that only when all signatures are -// signed, the transaction could be broadcast -/////////////////////// -BOOST_AUTO_TEST_CASE( cli_multisig_transaction ) -{ - 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 multisig account - graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr->suggest_brain_key(); - graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr->suggest_brain_key(); - graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr->suggest_brain_key(); - graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr->suggest_brain_key(); - BOOST_CHECK(!bki1.brain_priv_key.empty()); - BOOST_CHECK(!bki2.brain_priv_key.empty()); - BOOST_CHECK(!bki3.brain_priv_key.empty()); - BOOST_CHECK(!bki4.brain_priv_key.empty()); - - signed_transaction create_multisig_acct_tx; - account_create_operation account_create_op; - - account_create_op.referrer = nathan_acct_after_upgrade.id; - account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage; - account_create_op.registrar = nathan_acct_after_upgrade.id; - account_create_op.name = "cifer.test"; - account_create_op.owner = authority(1, bki1.pub_key, 1); - account_create_op.active = authority(2, bki2.pub_key, 1, bki3.pub_key, 1); - account_create_op.options.memo_key = bki4.pub_key; - account_create_op.fee = asset(1000000); // should be enough for creating account - - create_multisig_acct_tx.operations.push_back(account_create_op); - con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true); - - // attempt to give cifer.test some peerplays - BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to cifer.test"); - signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer("nathan", "cifer.test", "10000", "1.3.0", "Here are some BTS for your new account", true); - - // transfer bts from cifer.test to nathan - BOOST_TEST_MESSAGE("Transferring peerplays from cifer.test to nathan"); - auto dyn_props = app1->chain_database()->get_dynamic_global_properties(); - account_object cifer_test = con.wallet_api_ptr->get_account("cifer.test"); - - // construct a transfer transaction - signed_transaction transfer_tx2; - transfer_operation xfer_op; - xfer_op.from = cifer_test.id; - xfer_op.to = nathan_acct_after_upgrade.id; - xfer_op.amount = asset(100000000); - xfer_op.fee = asset(3000000); // should be enough for transfer - transfer_tx2.operations.push_back(xfer_op); - - // case1: sign a transaction without TaPoS and expiration fields - // expect: return a transaction with TaPoS and expiration filled - transfer_tx2 = - con.wallet_api_ptr->add_transaction_signature( transfer_tx2, false ); - BOOST_CHECK( ( transfer_tx2.ref_block_num != 0 && - transfer_tx2.ref_block_prefix != 0 ) || - ( transfer_tx2.expiration != fc::time_point_sec() ) ); - - // case2: broadcast without signature - // expect: exception with missing active authority - BOOST_CHECK_THROW(con.wallet_api_ptr->broadcast_transaction(transfer_tx2), fc::exception); - - // case3: - // import one of the private keys for this new account in the wallet file, - // sign and broadcast with partial signatures - // - // expect: exception with missing active authority - BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki2.wif_priv_key)); - BOOST_CHECK_THROW(con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true), fc::exception); - - // case4: sign again as signature exists - // expect: num of signatures not increase - // transfer_tx2 = con.wallet_api_ptr->add_transaction_signature(transfer_tx2, false); - // BOOST_CHECK_EQUAL(transfer_tx2.signatures.size(), 1); - - // case5: - // import another private key, sign and broadcast without full signatures - // - // expect: transaction broadcast successfully - BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki3.wif_priv_key)); - con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true); - auto balances = con.wallet_api_ptr->list_account_balances( "cifer.test" ); - for (auto b : balances) { - if (b.asset_id == asset_id_type()) { - BOOST_ASSERT(b == asset(900000000 - 3000000)); - } - } - - // wait for everything to finish up - fc::usleep(fc::seconds(1)); - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } - app1->shutdown(); -} - -graphene::wallet::plain_keys decrypt_keys( const std::string& password, const vector& cipher_keys ) -{ - auto pw = fc::sha512::hash( password.c_str(), password.size() ); - vector decrypted = fc::aes_decrypt( pw, cipher_keys ); - return fc::raw::unpack( decrypted ); -} - -BOOST_AUTO_TEST_CASE( saving_keys_wallet_test ) -{ - cli_fixture cli; - - cli.con.wallet_api_ptr->import_balance( "nathan", cli.nathan_keys, true ); - cli.con.wallet_api_ptr->upgrade_account( "nathan", true ); - std::string brain_key( "FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL" ); - cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true ); - - BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) ); - - std::string path( cli.app_dir.path().generic_string() + "/wallet.json" ); - graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); - BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan - BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1 - BOOST_CHECK( wallet.pending_account_registrations["account1"].size() == 2 ); // account1 active key + account1 memo key - - graphene::wallet::plain_keys pk = decrypt_keys( "supersecret", wallet.cipher_keys ); - BOOST_CHECK( pk.keys.size() == 1 ); // nathan key - - BOOST_CHECK( generate_block( cli.app1 ) ); - fc::usleep( fc::seconds(1) ); - - wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); - BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1 - BOOST_CHECK( wallet.pending_account_registrations.empty() ); - BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) ); - - pk = decrypt_keys( "supersecret", wallet.cipher_keys ); - BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key -} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/common/genesis_file_util.hpp b/tests/common/genesis_file_util.hpp index e058df02..27a2080f 100644 --- a/tests/common/genesis_file_util.hpp +++ b/tests/common/genesis_file_util.hpp @@ -1,5 +1,5 @@ #pragma once - +#include ///////// /// @brief forward declaration, using as a hack to generate a genesis.json file /// for testing From 2e8c07465551ab9c7cba880696fce90a5f01c13f Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Fri, 6 Mar 2020 22:49:26 +1100 Subject: [PATCH 48/64] SON202 - Maintenance improvements (#303) --- .../include/graphene/chain/protocol/son.hpp | 10 ++++++++- libraries/chain/son_evaluator.cpp | 15 +++++++------ .../wallet/include/graphene/wallet/wallet.hpp | 8 +++---- libraries/wallet/wallet.cpp | 14 +++++++------ tests/cli/son.cpp | 6 +++--- tests/tests/son_operations_tests.cpp | 21 ++++++++++++++++++- 6 files changed, 53 insertions(+), 21 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index dc11d5be..20353b91 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -77,6 +77,12 @@ namespace graphene { namespace chain { share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; + enum class son_maintenance_request_type + { + request_maintenance, + cancel_request_maintenance + }; + struct son_maintenance_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; @@ -84,6 +90,7 @@ namespace graphene { namespace chain { asset fee; son_id_type son_id; account_id_type owner_account; + son_maintenance_request_type request_type = son_maintenance_request_type::request_maintenance; account_id_type fee_payer()const { return owner_account; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } @@ -108,5 +115,6 @@ FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) ) +FC_REFLECT_ENUM( graphene::chain::son_maintenance_request_type, (request_maintenance)(cancel_request_maintenance) ) FC_REFLECT(graphene::chain::son_maintenance_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account) ) +FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account)(request_type) ) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index f4a7548a..908c40d5 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -66,9 +66,6 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op) void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass - // Get the current block witness signatory - witness_id_type wit_id = db().get_scheduled_witness(1); - const witness_object& current_witness = wit_id(db()); // Either owner can remove or consensus son account FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == GRAPHENE_SON_ACCOUNT)); const auto& idx = db().get_index_type().indices().get(); @@ -204,7 +201,13 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati auto itr = idx.find(op.son_id); FC_ASSERT( itr != idx.end() ); // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states - FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive SONs can't go to maintenance"); + if(op.request_type == son_maintenance_request_type::request_maintenance) { + FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance"); + } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request"); + } else { + FC_ASSERT(false, "Invalid maintenance operation"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -214,11 +217,11 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if(itr->status == son_status::active) { + if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) { db().modify(*itr, [](son_object &so) { so.status = son_status::request_maintenance; }); - } else if(itr->status == son_status::request_maintenance) { + } else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { db().modify(*itr, [](son_object &so) { so.status = son_status::active; }); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 34fc1885..364fb20b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1356,7 +1356,7 @@ class wallet_api * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction */ - signed_transaction start_son_maintenance(string owner_account, + signed_transaction request_son_maintenance(string owner_account, bool broadcast = false); /** Modify status of the SON owned by the given account back to active. @@ -1365,7 +1365,7 @@ class wallet_api * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction */ - signed_transaction stop_son_maintenance(string owner_account, + signed_transaction cancel_request_son_maintenance(string owner_account, bool broadcast = false); /** Lists all SONs in the blockchain. @@ -2237,8 +2237,8 @@ FC_API( graphene::wallet::wallet_api, (delete_son) (list_sons) (list_active_sons) - (start_son_maintenance) - (stop_son_maintenance) + (request_son_maintenance) + (cancel_request_son_maintenance) (get_active_son_wallet) (get_son_wallet_by_time_point) (get_son_wallets) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8af353e2..aca8c04f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1942,7 +1942,7 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } - signed_transaction start_son_maintenance(string owner_account, + signed_transaction request_son_maintenance(string owner_account, bool broadcast) { try { son_object son = get_son(owner_account); @@ -1950,6 +1950,7 @@ public: son_maintenance_operation op; op.owner_account = son.son_account; op.son_id = son.id; + op.request_type = son_maintenance_request_type::request_maintenance; signed_transaction tx; tx.operations.push_back( op ); @@ -1959,7 +1960,7 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account) ) } - signed_transaction stop_son_maintenance(string owner_account, + signed_transaction cancel_request_son_maintenance(string owner_account, bool broadcast) { try { son_object son = get_son(owner_account); @@ -1967,6 +1968,7 @@ public: son_maintenance_operation op; op.owner_account = son.son_account; op.son_id = son.id; + op.request_type = son_maintenance_request_type::cancel_request_maintenance; signed_transaction tx; tx.operations.push_back( op ); @@ -4463,14 +4465,14 @@ signed_transaction wallet_api::delete_son(string owner_account, return my->delete_son(owner_account, broadcast); } -signed_transaction wallet_api::start_son_maintenance(string owner_account, bool broadcast) +signed_transaction wallet_api::request_son_maintenance(string owner_account, bool broadcast) { - return my->start_son_maintenance(owner_account, broadcast); + return my->request_son_maintenance(owner_account, broadcast); } -signed_transaction wallet_api::stop_son_maintenance(string owner_account, bool broadcast) +signed_transaction wallet_api::cancel_request_son_maintenance(string owner_account, bool broadcast) { - return my->stop_son_maintenance(owner_account, broadcast); + return my->cancel_request_son_maintenance(owner_account, broadcast); } map wallet_api::list_sons(const string& lowerbound, uint32_t limit) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 7915c71e..3630000c 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -699,7 +699,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_CHECK(son_obj.status == son_status::active); // put SON in maintenance mode - con.wallet_api_ptr->start_son_maintenance(name, true); + con.wallet_api_ptr->request_son_maintenance(name, true); BOOST_CHECK(generate_block()); // check SON is in request_maintenance @@ -707,7 +707,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_CHECK(son_obj.status == son_status::request_maintenance); // restore SON activity - con.wallet_api_ptr->stop_son_maintenance(name, true); + con.wallet_api_ptr->cancel_request_son_maintenance(name, true); BOOST_CHECK(generate_block()); // check SON is active @@ -715,7 +715,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_CHECK(son_obj.status == son_status::active); // put SON in maintenance mode - con.wallet_api_ptr->start_son_maintenance(name, true); + con.wallet_api_ptr->request_son_maintenance(name, true); BOOST_CHECK(generate_block()); // check SON is in request_maintenance diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 9f3c0937..48c52feb 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -553,6 +553,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { son_maintenance_operation op; op.owner_account = alice_id; op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::request_maintenance; trx.operations.push_back(op); set_expiration(db, trx); @@ -586,10 +587,11 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { generate_block(); - // Put SON in maintenance + // Request SON Maintenance son_maintenance_operation op; op.owner_account = alice_id; op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::request_maintenance; trx.operations.push_back(op); set_expiration(db, trx); @@ -600,6 +602,23 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { BOOST_CHECK( obj->status == son_status::request_maintenance); } + { + generate_block(); + // Cancel SON Maintenance request + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::cancel_request_maintenance; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_CHECK( obj->status == son_status::active); + } + // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { From 85b81cb32b4092490a2d22f7d36a4027c9693958 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Tue, 10 Mar 2020 17:59:32 +0100 Subject: [PATCH 49/64] Quickfix, remove dead code, return result from wallet withdraw do_evaluate --- libraries/chain/son_wallet_deposit_evaluator.cpp | 1 - libraries/chain/son_wallet_withdraw_evaluator.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index 2f5a4f6b..839c5183 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -52,7 +52,6 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); FC_ASSERT(itr != idx.end(), "Son wallet transfer not found"); - //FC_ASSERT(itr->processed == false, "Son wallet transfer is already processed"); const database& d = db(); diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index d3a32a1b..baf9b06a 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -50,6 +50,7 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_ const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_process_operation& op) From f1e5171be0633e17f48cd7f87bd43676bee43fc6 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Wed, 11 Mar 2020 20:31:20 +1100 Subject: [PATCH 50/64] SON275 - ZMQ Crash on application exit (#306) * SON275 - ZMQ Crash on application exit * SON275 - Fix Indentation Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> --- libraries/chain/db_maint.cpp | 2 +- libraries/chain/include/graphene/chain/config.hpp | 1 + .../graphene/chain/protocol/chain_parameters.hpp | 5 +++++ .../sidechain_net_handler_bitcoin.cpp | 14 +++++++++----- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 16a9ff95..def394a8 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -123,7 +123,7 @@ void database::pay_sons() time_point_sec now = head_block_time(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); // Current requirement is that we have to pay every 24 hours, so the following check - if( dpo.son_budget.value > 0 && now - dpo.last_son_payout_time >= fc::days(1)) { + if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { uint64_t total_txs_signed = 0; share_type son_budget = dpo.son_budget; get_index_type().inspect_all_objects([this, &total_txs_signed](const object& o) { diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 65f4ab47..7e58bf0d 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -237,6 +237,7 @@ #define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds #define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds #define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds +#define SON_PAY_TIME (60*60*24) // 1 day #define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200)) #define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT) #define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0)) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index cd870a2e..65b0d3c0 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -46,6 +46,7 @@ namespace graphene { namespace chain { optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; + optional < uint32_t > son_pay_time; optional < uint32_t > son_deregister_time; optional < uint32_t > son_heartbeat_frequency; optional < uint32_t > son_down_time; @@ -141,6 +142,9 @@ namespace graphene { namespace chain { inline uint16_t son_pay_daily_max()const { return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX; } + inline uint16_t son_pay_time()const { + return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME; + } inline uint16_t son_deregister_time()const { return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME; } @@ -167,6 +171,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) + (son_pay_time) (son_deregister_time) (son_heartbeat_frequency) (son_down_time) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 7eaa4d44..1b1889bc 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -535,18 +535,22 @@ std::vector zmq_listener::receive_multipart() { } void zmq_listener::handle_zmq() { + int linger = 0; socket.setsockopt(ZMQ_SUBSCRIBE, "hashblock", 9); + socket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); while (true) { - auto msg = receive_multipart(); - const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); - const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - - event_received(block_hash); + try { + auto msg = receive_multipart(); + const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); + const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); + event_received(block_hash); + } catch (zmq::error_t& e) { + } } } From ea0be267896599467d4da70c01850fe0ecf9760f Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Fri, 13 Mar 2020 11:58:54 -0300 Subject: [PATCH 51/64] need to assign both name and id to stats id --- libraries/chain/db_init.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 09e45276..a6527809 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -482,7 +482,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID); FC_ASSERT(create([this](account_object& a) { a.name = "son-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT; From d22544657f8e938c7f3121412af6e0cb4a560bf5 Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Fri, 13 Mar 2020 12:00:07 -0300 Subject: [PATCH 52/64] fix unit test case failures(add gpos vesting before voting) --- .gitignore | 1 + tests/cli/main.cpp | 1 + tests/cli/son.cpp | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 90311de0..39b23163 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ moc_* hardfork.hpp build_xc data +CMakeDoxyfile.in build diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index d300005b..106f3c8c 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -88,6 +88,7 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) witness_object init1_obj = con.wallet_api_ptr->get_witness("init1"); int init1_start_votes = init1_obj.total_votes; // Vote for a witness + con.wallet_api_ptr->create_vesting_balance("nathan", "10000", "1.3.0", vesting_balance_type::gpos, true); signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true); // generate a block to get things started diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 7915c71e..3ffa83be 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -213,6 +213,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_start_votes = son2_obj.total_votes; + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); // Vote for a son1account BOOST_TEST_MESSAGE("Voting for son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); @@ -449,6 +450,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); accepted.push_back("son1account"); accepted.push_back("son2account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 2, true); BOOST_CHECK(generate_block()); @@ -469,6 +471,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) accepted.clear(); rejected.clear(); rejected.push_back("son1account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 1, true); BOOST_CHECK(generate_maintenance_block()); @@ -619,6 +622,9 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) "http://son" + fc::to_pretty_string(i), sidechain_public_keys, false); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE token for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); } BOOST_CHECK(generate_maintenance_block()); From cc7b47cf0f460f12dc34321910220fd068c89467 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Sat, 14 Mar 2020 11:08:45 +1100 Subject: [PATCH 53/64] SON276 - Fix SON proposal exceptions - I (#307) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> --- libraries/chain/db_getter.cpp | 15 -- libraries/chain/db_maint.cpp | 208 +++++++++++------- libraries/chain/db_update.cpp | 46 ++++ .../chain/include/graphene/chain/database.hpp | 6 +- libraries/chain/son_evaluator.cpp | 4 +- 5 files changed, 177 insertions(+), 102 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 72e0327f..dfd59567 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -222,21 +222,6 @@ signed_transaction database::create_signed_transaction( const fc::ecc::private_k return processed_trx; } -void database::remove_son_proposal( const proposal_object& proposal ) -{ try { - if( proposal.proposed_transaction.operations.size() == 1 && - ( proposal.proposed_transaction.operations.back().which() == operation::tag::value || - proposal.proposed_transaction.operations.back().which() == operation::tag::value) ) - { - const auto& son_proposal_idx = get_index_type().indices().get(); - auto son_proposal_itr = son_proposal_idx.find( proposal.id ); - if( son_proposal_itr == son_proposal_idx.end() ) { - return; - } - remove( *son_proposal_itr ); - } -} FC_CAPTURE_AND_RETHROW( (proposal) ) } - bool database::is_son_dereg_valid( son_id_type son_id ) { const auto& son_idx = get_index_type().indices().get< by_id >(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index def394a8..96ef6853 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -180,6 +180,128 @@ void database::update_son_metrics() } } +void database::update_son_statuses(const vector& curr_active_sons, const vector& new_active_sons) +{ + vector current_sons, new_sons; + vector sons_to_remove, sons_to_add; + const auto& idx = get_index_type().indices().get(); + + current_sons.reserve(curr_active_sons.size()); + std::transform(curr_active_sons.begin(), curr_active_sons.end(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + new_sons.reserve(new_active_sons.size()); + std::transform(new_active_sons.begin(), new_active_sons.end(), + std::inserter(new_sons, new_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + // find all cur_active_sons members that is not in new_active_sons + for_each(current_sons.begin(), current_sons.end(), + [&sons_to_remove, &new_sons](const son_id_type& si) + { + if(std::find(new_sons.begin(), new_sons.end(), si) == + new_sons.end()) + { + sons_to_remove.push_back(si); + } + } + ); + + for( const auto& sid : sons_to_remove ) + { + auto son = idx.find( sid ); + if(son == idx.end()) // SON is deleted already + continue; + // keep maintenance status for nodes becoming inactive + if(son->status == son_status::active) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::inactive; + }); + } + } + + // find all new_active_sons members that is not in cur_active_sons + for_each(new_sons.begin(), new_sons.end(), + [&sons_to_add, ¤t_sons](const son_id_type& si) + { + if(std::find(current_sons.begin(), current_sons.end(), si) == + current_sons.end()) + { + sons_to_add.push_back(si); + } + } + ); + + for( const auto& sid : sons_to_add ) + { + auto son = idx.find( sid ); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); + // keep maintenance status for new nodes + if(son->status == son_status::inactive) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::active; + }); + } + } + + ilog("New SONS"); + for(size_t i = 0; i < new_sons.size(); i++) { + auto son = idx.find( new_sons[i] ); + if(son == idx.end()) // SON is deleted already + continue; + ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) ); + } + + if( sons_to_remove.size() > 0 ) + { + remove_inactive_son_proposals(sons_to_remove); + } +} + +void database::update_son_wallet(const vector& new_active_sons) +{ + bool should_recreate_pw = true; + + // Expire for current son_wallet_object wallet, if exists + const auto& idx_swi = get_index_type().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*obj).sons; + + bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); + if (wallet_son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + } + } + + should_recreate_pw = !wallet_son_sets_equal; + + if (should_recreate_pw) { + modify(*obj, [&, obj](son_wallet_object &swo) { + swo.expires = head_block_time(); + }); + } + } + + if (should_recreate_pw) { + // Create new son_wallet_object, to initiate wallet recreation + create( [&]( son_wallet_object& obj ) { + obj.valid_from = head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); + }); + } +} + void database::pay_workers( share_type& budget ) { // ilog("Processing payroll! Available budget is ${b}", ("b", budget)); @@ -496,90 +618,8 @@ void database::update_active_sons() } else { ilog( "Active SONs set CHANGED" ); - bool should_recreate_pw = true; - - // Expire for current son_wallet_object wallet, if exists - const auto& idx_swi = get_index_type().indices().get(); - auto obj = idx_swi.rbegin(); - if (obj != idx_swi.rend()) { - // Compare current wallet SONs and to-be lists of active sons - auto cur_wallet_sons = (*obj).sons; - - bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); - if (wallet_son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); - } - } - - should_recreate_pw = !wallet_son_sets_equal; - - if (should_recreate_pw) { - modify(*obj, [&, obj](son_wallet_object &swo) { - swo.expires = head_block_time(); - }); - } - } - - if (should_recreate_pw) { - // Create new son_wallet_object, to initiate wallet recreation - create( [&]( son_wallet_object& obj ) { - obj.valid_from = head_block_time(); - obj.expires = time_point_sec::maximum(); - obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); - }); - } - - vector sons_to_remove; - // find all cur_active_sons members that is not in new_active_sons - for_each(cur_active_sons.begin(), cur_active_sons.end(), - [&sons_to_remove, &new_active_sons](const son_info& si) - { - if(std::find(new_active_sons.begin(), new_active_sons.end(), si) == - new_active_sons.end()) - { - sons_to_remove.push_back(si); - } - } - ); - const auto& idx = get_index_type().indices().get(); - for( const son_info& si : sons_to_remove ) - { - auto son = idx.find( si.son_id ); - if(son == idx.end()) // SON is deleted already - continue; - // keep maintenance status for nodes becoming inactive - if(son->status == son_status::active) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::inactive; - }); - } - } - vector sons_to_add; - // find all new_active_sons members that is not in cur_active_sons - for_each(new_active_sons.begin(), new_active_sons.end(), - [&sons_to_add, &cur_active_sons](const son_info& si) - { - if(std::find(cur_active_sons.begin(), cur_active_sons.end(), si) == - cur_active_sons.end()) - { - sons_to_add.push_back(si); - } - } - ); - for( const son_info& si : sons_to_add ) - { - auto son = idx.find( si.son_id ); - FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", si.son_id)); - // keep maintenance status for new nodes - if(son->status == son_status::inactive) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::active; - }); - } - } + update_son_wallet(new_active_sons); + update_son_statuses(cur_active_sons, new_active_sons); } modify(gpo, [&]( global_property_object& gp ){ diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index b33e3819..ed440d82 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -652,4 +652,50 @@ void database::update_betting_markets(fc::time_point_sec current_block_time) remove_completed_events(); } +void database::remove_son_proposal( const proposal_object& proposal ) +{ try { + if( proposal.proposed_transaction.operations.size() == 1 && + ( proposal.proposed_transaction.operations.back().which() == operation::tag::value || + proposal.proposed_transaction.operations.back().which() == operation::tag::value) ) + { + const auto& son_proposal_idx = get_index_type().indices().get(); + auto son_proposal_itr = son_proposal_idx.find( proposal.id ); + if( son_proposal_itr == son_proposal_idx.end() ) { + return; + } + remove( *son_proposal_itr ); + } +} FC_CAPTURE_AND_RETHROW( (proposal) ) } + +void database::remove_inactive_son_down_proposals( const vector& son_ids_to_remove ) +{ + const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); + std::vector proposals_to_remove; + + for( auto& son_proposal : son_proposal_idx ) + { + if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal) + { + auto it = std::find(son_ids_to_remove.begin(), son_ids_to_remove.end(), son_proposal.son_id); + if (it != son_ids_to_remove.end()) + { + ilog( "Removing inactive proposal ${p} for son ${s}", ("p", son_proposal.proposal_id) ("s",son_proposal.son_id)); + proposals_to_remove.push_back(son_proposal.proposal_id); + } + } + } + + for( auto& proposal_id : proposals_to_remove ) + { + const auto& proposal_obj = proposal_id(*this); + remove_son_proposal(proposal_obj); + remove(proposal_obj); + } +} + +void database::remove_inactive_son_proposals( const vector& son_ids_to_remove ) +{ + remove_inactive_son_down_proposals( son_ids_to_remove ); +} + } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 5b8952df..4727bc1d 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -304,7 +304,6 @@ namespace graphene { namespace chain { std::set get_sons_to_be_deregistered(); fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); - void remove_son_proposal( const proposal_object& proposal ); bool is_son_dereg_valid( son_id_type son_id ); time_point_sec head_block_time()const; @@ -549,6 +548,11 @@ namespace graphene { namespace chain { void update_active_committee_members(); void update_son_metrics(); void update_active_sons(); + void remove_son_proposal( const proposal_object& proposal ); + void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); + void remove_inactive_son_proposals( const vector& son_ids_to_remove ); + void update_son_statuses( const vector& cur_active_sons, const vector& new_active_sons ); + void update_son_wallet( const vector& new_active_sons ); void update_worker_votes(); template diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 908c40d5..c95c717f 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -96,10 +96,10 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op) void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); const auto& idx = db().get_index_type().indices().get(); - FC_ASSERT( idx.find(op.son_id) != idx.end() ); auto itr = idx.find(op.son_id); + FC_ASSERT( itr != idx.end() ); + FC_ASSERT(itr->son_account == op.owner_account); auto stats = itr->statistics( db() ); // Inactive SONs need not send heartbeats FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); From ff7e1baaf130f08eaf1153aa4102dece7f9bea3e Mon Sep 17 00:00:00 2001 From: obucina <11353193+obucina@users.noreply.github.com> Date: Sat, 14 Mar 2020 11:44:39 +0100 Subject: [PATCH 54/64] Add SON statistic for tracking reported sidechain transactions (#308) - Deposit and Withdrawal object extended to contain expected and received transaction reports from SON network - SON statistic object extended to contain total number of sidechain transactions reported by SON network when SON was active and number of transactions reported by single SON when he was active - Code formatting --- .../chain/protocol/son_wallet_deposit.hpp | 3 +- .../chain/protocol/son_wallet_withdraw.hpp | 3 +- .../include/graphene/chain/son_object.hpp | 16 ++++++++-- .../chain/son_wallet_deposit_object.hpp | 7 +++-- .../chain/son_wallet_withdraw_object.hpp | 8 +++-- .../chain/son_wallet_deposit_evaluator.cpp | 31 ++++++++++++++++--- .../chain/son_wallet_withdraw_evaluator.cpp | 29 +++++++++++++++-- .../sidechain_net_handler.cpp | 8 +++++ .../sidechain_net_handler_bitcoin.cpp | 2 +- 9 files changed, 90 insertions(+), 17 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp index abcc4384..ddc1ff53 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp @@ -12,6 +12,7 @@ namespace graphene { namespace chain { asset fee; account_id_type payer; + son_id_type son_id; fc::time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; std::string sidechain_uid; @@ -45,7 +46,7 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer) - (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset)) + (son_id) (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset)) FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer) (son_wallet_deposit_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp index 99c263be..4e9d4971 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp @@ -12,6 +12,7 @@ namespace graphene { namespace chain { asset fee; account_id_type payer; + son_id_type son_id; fc::time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; std::string peerplays_uid; @@ -44,7 +45,7 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer) - (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + (son_id) (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer) (son_wallet_withdraw_id)) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index 0d4a7317..ef479962 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -42,6 +42,10 @@ namespace graphene { namespace chain { fc::time_point_sec last_down_timestamp; // Last Active heartbeat timestamp fc::time_point_sec last_active_timestamp; + // Total sidechain transactions reported by SON network while SON was active + uint64_t total_sidechain_txs_reported = 0; + // Sidechain transactions reported by this SON + uint64_t sidechain_txs_reported = 0; }; /** @@ -87,14 +91,20 @@ namespace graphene { namespace chain { >; using son_index = generic_index; + struct by_owner; using son_stats_multi_index_type = multi_index_container< son_statistics_object, indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + > > >; - using son_stats_index = generic_index; + } } // graphene::chain FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintenance)(in_maintenance)(deregistered) ) @@ -111,4 +121,6 @@ FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (current_interval_downtime) (last_down_timestamp) (last_active_timestamp) + (total_sidechain_txs_reported) + (sidechain_txs_reported) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp index 668af1d1..4c30cc49 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -18,7 +18,6 @@ namespace graphene { namespace chain { time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; - int64_t confirmations; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -29,6 +28,9 @@ namespace graphene { namespace chain { chain::account_id_type peerplays_to; chain::asset peerplays_asset; + std::set expected_reports; + std::set received_reports; + bool processed; }; @@ -63,7 +65,8 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::son_wallet_deposit_object, (graphene::db::object), - (timestamp) (sidechain) (confirmations) + (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset) + (expected_reports) (received_reports) (processed) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp index 68e870e0..71245ba7 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -18,7 +18,6 @@ namespace graphene { namespace chain { time_point_sec timestamp; peerplays_sidechain::sidechain_type sidechain; - int64_t confirmations; std::string peerplays_uid; std::string peerplays_transaction_id; chain::account_id_type peerplays_from; @@ -27,6 +26,10 @@ namespace graphene { namespace chain { std::string withdraw_address; std::string withdraw_currency; safe withdraw_amount; + + std::set expected_reports; + std::set received_reports; + bool processed; }; @@ -61,7 +64,8 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::son_wallet_withdraw_object, (graphene::db::object), - (timestamp) (sidechain) (confirmations) + (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) + (expected_reports) (received_reports) (processed) ) diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index 839c5183..764f2c29 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace graphene { namespace chain { @@ -11,8 +12,9 @@ void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_de FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); - //const auto& idx = db().get_index_type().indices().get(); - //FC_ASSERT(idx.find(op.sidechain_uid) == idx.end(), "Already registered " + op.sidechain_uid); + const auto &idx = db().get_index_type().indices().get(); + FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -24,7 +26,6 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swto ){ swto.timestamp = op.timestamp; swto.sidechain = op.sidechain; - swto.confirmations = 1; swto.sidechain_uid = op.sidechain_uid; swto.sidechain_transaction_id = op.sidechain_transaction_id; swto.sidechain_from = op.sidechain_from; @@ -33,12 +34,32 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de swto.peerplays_from = op.peerplays_from; swto.peerplays_to = op.peerplays_to; swto.peerplays_asset = op.peerplays_asset; + + auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons) { + swto.expected_reports.insert(si.son_id); + + auto stats_itr = db().get_index_type().indices().get().find(si.son_id); + db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { + sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1; + if (si.son_id == op.son_id) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + } + }); + } + + swto.received_reports.insert(op.son_id); + swto.processed = false; }); return new_son_wallet_deposit_object.id; } else { db().modify(*itr, [&op](son_wallet_deposit_object &swto) { - swto.confirmations = swto.confirmations + 1; + swto.received_reports.insert(op.son_id); + }); + auto stats_itr = db().get_index_type().indices().get().find(op.son_id); + db().modify(*stats_itr, [&op](son_statistics_object &sso) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; }); return (*itr).id; } @@ -51,7 +72,7 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); - FC_ASSERT(itr != idx.end(), "Son wallet transfer not found"); + FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); const database& d = db(); diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index baf9b06a..2185e808 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace graphene { namespace chain { @@ -11,6 +12,9 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + const auto &idx = db().get_index_type().indices().get(); + FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -22,7 +26,6 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w const auto& new_son_wallet_withdraw_object = db().create( [&]( son_wallet_withdraw_object& swwo ){ swwo.timestamp = op.timestamp; swwo.sidechain = op.sidechain; - swwo.confirmations = 1; swwo.peerplays_uid = op.peerplays_uid; swwo.peerplays_transaction_id = op.peerplays_transaction_id; swwo.peerplays_from = op.peerplays_from; @@ -31,12 +34,32 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w swwo.withdraw_address = op.withdraw_address; swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_amount = op.withdraw_amount; + + auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons) { + swwo.expected_reports.insert(si.son_id); + + auto stats_itr = db().get_index_type().indices().get().find(si.son_id); + db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { + sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1; + if (si.son_id == op.son_id) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + } + }); + } + + swwo.received_reports.insert(op.son_id); + swwo.processed = false; }); return new_son_wallet_withdraw_object.id; } else { - db().modify(*itr, [&op](son_wallet_withdraw_object &swto) { - swto.confirmations = swto.confirmations + 1; + db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) { + swwo.received_reports.insert(op.son_id); + }); + auto stats_itr = db().get_index_type().indices().get().find(op.son_id); + db().modify(*stats_itr, [&op](son_statistics_object &sso) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; }); return (*itr).id; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 19188440..38dbf4f3 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -74,6 +74,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") != 0)) { son_wallet_deposit_create_operation op; op.payer = GRAPHENE_SON_ACCOUNT; + //op.son_id = ; // to be filled for each son op.timestamp = sed.timestamp; op.sidechain = sed.sidechain; op.sidechain_uid = sed.sidechain_uid; @@ -88,6 +89,9 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_active_son(son_id)) { + + op.son_id = son_id; + proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; proposal_op.proposed_ops.emplace_back(op_wrapper(op)); @@ -117,6 +121,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ son_wallet_withdraw_create_operation op; op.payer = GRAPHENE_SON_ACCOUNT; + //op.son_id = ; // to be filled for each son op.timestamp = sed.timestamp; op.sidechain = sed.sidechain; op.peerplays_uid = sed.sidechain_uid; @@ -130,6 +135,9 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_active_son(son_id)) { + + op.son_id = son_id; + proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; proposal_op.proposed_ops.emplace_back(op_wrapper(op)); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 1b1889bc..fc891054 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -549,7 +549,7 @@ void zmq_listener::handle_zmq() { const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); event_received(block_hash); - } catch (zmq::error_t& e) { + } catch (zmq::error_t &e) { } } } From 3ffcd4fdd0d82004f78af2402e079c2b6f88e31a Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Mon, 16 Mar 2020 18:24:28 -0300 Subject: [PATCH 55/64] Allow voting for son, only if GPOS vesting balance available --- libraries/wallet/wallet.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fb9d242d..5ab144f8 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2589,6 +2589,13 @@ public: bool approve, bool broadcast /* = false */) { try { + + std::vector vbo_info = get_vesting_balances(voting_account); + std::vector::iterator vbo_iter; + vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); + if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the SON account", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); + account_object voting_account_object = get_account(voting_account); account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account(son_account_id); From c0d515b93f6f6f74fa659bf49984361e95c58496 Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Mon, 16 Mar 2020 22:32:35 -0300 Subject: [PATCH 56/64] notifications of SONS should get restrict to sons functionality --- libraries/chain/db_maint.cpp | 154 ++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7207545a..9cdbb9ba 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -558,96 +558,100 @@ void database::update_active_sons() } } - if (son_sets_equal) { - ilog( "Active SONs set NOT CHANGED" ); - } else { - ilog( "Active SONs set CHANGED" ); + //restrict below code snippet to sons functionality + if(cur_active_sons.size() || new_active_sons.size()) + { + if (son_sets_equal) { + ilog( "Active SONs set NOT CHANGED" ); + } else { + ilog( "Active SONs set CHANGED" ); - bool should_recreate_pw = true; + bool should_recreate_pw = true; - // Expire for current son_wallet_object wallet, if exists - const auto& idx_swi = get_index_type().indices().get(); - auto obj = idx_swi.rbegin(); - if (obj != idx_swi.rend()) { - // Compare current wallet SONs and to-be lists of active sons - auto cur_wallet_sons = (*obj).sons; + // Expire for current son_wallet_object wallet, if exists + const auto& idx_swi = get_index_type().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*obj).sons; - bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); - if (wallet_son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); + if (wallet_son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + } + } + + should_recreate_pw = !wallet_son_sets_equal; + + if (should_recreate_pw) { + modify(*obj, [&, obj](son_wallet_object &swo) { + swo.expires = head_block_time(); + }); } } - should_recreate_pw = !wallet_son_sets_equal; - if (should_recreate_pw) { - modify(*obj, [&, obj](son_wallet_object &swo) { - swo.expires = head_block_time(); + // Create new son_wallet_object, to initiate wallet recreation + create( [&]( son_wallet_object& obj ) { + obj.valid_from = head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); }); } - } - if (should_recreate_pw) { - // Create new son_wallet_object, to initiate wallet recreation - create( [&]( son_wallet_object& obj ) { - obj.valid_from = head_block_time(); - obj.expires = time_point_sec::maximum(); - obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); - }); - } - - vector sons_to_remove; - // find all cur_active_sons members that is not in new_active_sons - for_each(cur_active_sons.begin(), cur_active_sons.end(), - [&sons_to_remove, &new_active_sons](const son_info& si) - { - if(std::find(new_active_sons.begin(), new_active_sons.end(), si) == - new_active_sons.end()) + vector sons_to_remove; + // find all cur_active_sons members that is not in new_active_sons + for_each(cur_active_sons.begin(), cur_active_sons.end(), + [&sons_to_remove, &new_active_sons](const son_info& si) { - sons_to_remove.push_back(si); + if(std::find(new_active_sons.begin(), new_active_sons.end(), si) == + new_active_sons.end()) + { + sons_to_remove.push_back(si); + } } - } - ); - const auto& idx = get_index_type().indices().get(); - for( const son_info& si : sons_to_remove ) - { - auto son = idx.find( si.son_id ); - if(son == idx.end()) // SON is deleted already - continue; - // keep maintenance status for nodes becoming inactive - if(son->status == son_status::active) + ); + const auto& idx = get_index_type().indices().get(); + for( const son_info& si : sons_to_remove ) { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::inactive; - }); + auto son = idx.find( si.son_id ); + if(son == idx.end()) // SON is deleted already + continue; + // keep maintenance status for nodes becoming inactive + if(son->status == son_status::active) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::inactive; + }); + } + } + vector sons_to_add; + // find all new_active_sons members that is not in cur_active_sons + for_each(new_active_sons.begin(), new_active_sons.end(), + [&sons_to_add, &cur_active_sons](const son_info& si) + { + if(std::find(cur_active_sons.begin(), cur_active_sons.end(), si) == + cur_active_sons.end()) + { + sons_to_add.push_back(si); + } + } + ); + for( const son_info& si : sons_to_add ) + { + auto son = idx.find( si.son_id ); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", si.son_id)); + // keep maintenance status for new nodes + if(son->status == son_status::inactive) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::active; + }); + } } } - vector sons_to_add; - // find all new_active_sons members that is not in cur_active_sons - for_each(new_active_sons.begin(), new_active_sons.end(), - [&sons_to_add, &cur_active_sons](const son_info& si) - { - if(std::find(cur_active_sons.begin(), cur_active_sons.end(), si) == - cur_active_sons.end()) - { - sons_to_add.push_back(si); - } - } - ); - for( const son_info& si : sons_to_add ) - { - auto son = idx.find( si.son_id ); - FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", si.son_id)); - // keep maintenance status for new nodes - if(son->status == son_status::inactive) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::active; - }); - } - } - } + } modify(gpo, [&]( global_property_object& gp ){ gp.active_sons.clear(); From 8a90e70f1262bbd1ed95b3055ba4d22e53f80405 Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Mon, 16 Mar 2020 22:40:35 -0300 Subject: [PATCH 57/64] update GPOS hardfork date to sons branch --- libraries/chain/hardfork.d/GPOS.hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/GPOS.hf b/libraries/chain/hardfork.d/GPOS.hf index 52e95a72..39dd7ac4 100644 --- a/libraries/chain/hardfork.d/GPOS.hf +++ b/libraries/chain/hardfork.d/GPOS.hf @@ -1,4 +1,4 @@ -// GPOS HARDFORK Monday, 6 January 2020 01:00:00 GMT +// GPOS HARDFORK Monday, 16 March 2020 19:00:00 GMT #ifndef HARDFORK_GPOS_TIME -#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1578272400 )) +#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1584385200 )) #endif From ed4ebfdb80f38d3e10076b90aef6c0d05b77e716 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Wed, 18 Mar 2020 00:01:31 +1100 Subject: [PATCH 58/64] SON127 - Add son parameter extensions to genesis, push proposal fix (#310) * SON276 - Fix SON proposal exceptions - I * SON127 - Add son parameter extensions to genesis, push proposal fix Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> --- libraries/chain/db_block.cpp | 4 ++-- programs/witness_node/genesis.json | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 7625178a..dfa6c4d1 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -336,8 +336,6 @@ processed_transaction database::validate_transaction( const signed_transaction& processed_transaction database::push_proposal(const proposal_object& proposal) { try { - FC_ASSERT( _undo_db.size() < _undo_db.max_size(), "Undo database is full!" ); - transaction_evaluation_state eval_state(this); eval_state._is_proposed_trx = true; @@ -347,6 +345,8 @@ processed_transaction database::push_proposal(const proposal_object& proposal) size_t old_applied_ops_size = _applied_ops.size(); try { + 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); for( auto& op : proposal.proposed_transaction.operations ) eval_state.operation_results.emplace_back(apply_operation(eval_state, op)); diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index ab153e7b..1e18d592 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -352,7 +352,15 @@ "maximum_tournament_start_time_in_future": 2419200, "maximum_tournament_start_delay": 604800, "maximum_tournament_number_of_wins": 100, - "extensions": {} + "extensions": { + "son_vesting_amount": 5000000, + "son_vesting_period": 172800, + "son_pay_daily_max": 20000000, + "son_pay_time": 86400, + "son_deregister_time": 43200, + "son_heartbeat_frequency": 180, + "son_down_time": 360 + } }, "initial_bts_accounts": [], "initial_accounts": [{ @@ -493,4 +501,4 @@ "num_special_accounts": 0, "num_special_assets": 0 } -} \ No newline at end of file +} From 1d66b859c910aa66571adee0db0df23230ada788 Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Wed, 18 Mar 2020 17:18:15 -0300 Subject: [PATCH 59/64] update GPOS HF to fall in before SONS HF, remove check --- libraries/chain/hardfork.d/GPOS.hf | 4 ++-- libraries/chain/vesting_balance_evaluator.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/chain/hardfork.d/GPOS.hf b/libraries/chain/hardfork.d/GPOS.hf index 39dd7ac4..8e93f80f 100644 --- a/libraries/chain/hardfork.d/GPOS.hf +++ b/libraries/chain/hardfork.d/GPOS.hf @@ -1,4 +1,4 @@ -// GPOS HARDFORK Monday, 16 March 2020 19:00:00 GMT +// GPOS HARDFORK Monday, 31 Dec 2019 00:00:00 GMT #ifndef HARDFORK_GPOS_TIME -#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1584385200 )) +#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1577750400 )) #endif diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 9f93a5ff..dc91a449 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -42,9 +42,6 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); - if(d.head_block_time() < HARDFORK_SON_TIME) // Todo: can be removed after gpos hf time pass - FC_ASSERT( op.balance_type == vesting_balance_type::normal); - if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() ); From 78c17c0ef0a71e8575bc4afa21e8c599e5f274fb Mon Sep 17 00:00:00 2001 From: pbattu123 Date: Wed, 18 Mar 2020 17:19:28 -0300 Subject: [PATCH 60/64] updated unit test cases to reflect GPOS vesting and update account id's according to sons-account --- tests/cli/son.cpp | 11 +++++++++-- tests/common/database_fixture.cpp | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 3ffa83be..e3634a54 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -332,6 +332,10 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) BOOST_TEST_MESSAGE("Voting for SONs"); for(unsigned int i = 0; i < son_number + 1; i++) { + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); } @@ -623,7 +627,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) sidechain_public_keys, false); con.wallet_api_ptr->transfer( - "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE token for your new account", true ); + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); } BOOST_CHECK(generate_maintenance_block()); @@ -697,7 +701,10 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_TEST_MESSAGE("Voting for SONs"); for(unsigned int i = 1; i < son_number + 1; i++) { - con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); } BOOST_CHECK(generate_maintenance_block()); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 5631ccaa..ba5bde4e 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -110,7 +110,7 @@ database_fixture::database_fixture() // add account tracking for ahplugin for special test case with track-account enabled if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account") { std::vector track_account; - std::string track = "\"1.2.18\""; + std::string track = "\"1.2.19\""; track_account.push_back(track); options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); options.insert(std::make_pair("partial-operations", boost::program_options::variable_value(true, false))); @@ -120,7 +120,7 @@ database_fixture::database_fixture() std::vector track_account; std::string track = "\"1.2.0\""; track_account.push_back(track); - track = "\"1.2.17\""; + track = "\"1.2.18\""; track_account.push_back(track); options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); } From 096af5e77c10fa46a512d2b7ffc1da1f428656e5 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Fri, 20 Mar 2020 18:30:32 +1100 Subject: [PATCH 61/64] [SON-24] - SON Rewards missing serialisations and end to end testing (#313) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> --- .../chain/include/graphene/chain/global_property_object.hpp | 4 +++- libraries/chain/son_evaluator.cpp | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 1d985a2d..c34265cb 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -79,7 +79,7 @@ namespace graphene { namespace chain { time_point_sec last_budget_time; share_type witness_budget; //Last SON Payout time, it can be different to the maintenance interval time - time_point_sec last_son_payout_time; + time_point_sec last_son_payout_time = HARDFORK_SON_TIME; share_type son_budget = 0; uint32_t accounts_registered_this_interval = 0; /** @@ -138,6 +138,8 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene:: (next_maintenance_time) (last_budget_time) (witness_budget) + (last_son_payout_time) + (son_budget) (accounts_registered_this_interval) (recently_missed_count) (current_aslot) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index c95c717f..4155dc6b 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -141,6 +141,8 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& { sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); sso.last_active_timestamp = op.ts; + // TODO: Remove me after sidechain tx signing is finished + sso.txs_signed = sso.txs_signed + 1; } ); db().modify(*itr, [&is_son_active](son_object &so) { @@ -154,6 +156,8 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) { sso.last_active_timestamp = op.ts; + // TODO: Remove me after sidechain tx signing is finished + sso.txs_signed = sso.txs_signed + 1; } ); } } From 19a34e910261a9397735fafc9faa2ce75ae53985 Mon Sep 17 00:00:00 2001 From: obucina <11353193+obucina@users.noreply.github.com> Date: Sat, 21 Mar 2020 09:47:46 +0100 Subject: [PATCH 62/64] Revert "Merge develop branch changes(GPOS+graphene updates) into SONs branch" --- .gitignore | 1 - .gitmodules | 3 +- CMakeDoxyfile.in | 279 ---- Doxyfile | 2 +- libraries/app/CMakeLists.txt | 4 +- libraries/app/api.cpp | 65 +- libraries/app/application.cpp | 29 +- libraries/app/database_api.cpp | 351 +--- libraries/app/impacted.cpp | 369 +++++ libraries/app/include/graphene/app/api.hpp | 60 +- .../app/include/graphene/app/application.hpp | 8 +- .../app/include/graphene/app/database_api.hpp | 101 +- .../include/graphene/app}/impacted.hpp | 4 +- libraries/chain/CMakeLists.txt | 2 - libraries/chain/account_evaluator.cpp | 83 +- libraries/chain/account_object.cpp | 19 +- libraries/chain/asset_evaluator.cpp | 72 +- libraries/chain/asset_object.cpp | 16 +- libraries/chain/balance_evaluator.cpp | 1 - .../chain/committee_member_evaluator.cpp | 10 +- libraries/chain/db_balance.cpp | 11 +- libraries/chain/db_block.cpp | 215 +-- libraries/chain/db_debug.cpp | 2 +- libraries/chain/db_getter.cpp | 32 +- libraries/chain/db_init.cpp | 94 +- libraries/chain/db_maint.cpp | 506 ++---- libraries/chain/db_management.cpp | 14 +- libraries/chain/db_market.cpp | 20 +- libraries/chain/db_notify.cpp | 28 +- libraries/chain/db_update.cpp | 102 +- libraries/chain/db_witness_schedule.cpp | 12 +- libraries/chain/genesis_state.cpp | 69 - libraries/chain/hardfork.d/GPOS.hf | 4 - .../include/graphene/chain/account_object.hpp | 96 +- .../include/graphene/chain/asset_object.hpp | 108 +- .../include/graphene/chain/balance_object.hpp | 2 - .../include/graphene/chain/block_database.hpp | 2 - .../graphene/chain/block_summary_object.hpp | 4 - .../graphene/chain/budget_record_object.hpp | 17 +- .../include/graphene/chain/buyback_object.hpp | 2 - .../graphene/chain/chain_property_object.hpp | 4 +- .../chain/committee_member_object.hpp | 5 +- .../graphene/chain/confidential_object.hpp | 9 +- .../chain/include/graphene/chain/config.hpp | 5 +- .../chain/include/graphene/chain/database.hpp | 45 +- .../include/graphene/chain/exceptions.hpp | 16 - .../include/graphene/chain/fba_object.hpp | 5 +- .../include/graphene/chain/fork_database.hpp | 5 - .../include/graphene/chain/genesis_state.hpp | 81 +- .../graphene/chain/global_property_object.hpp | 4 +- .../chain/immutable_chain_parameters.hpp | 7 +- .../include/graphene/chain/market_object.hpp | 4 - .../chain/operation_history_object.hpp | 19 +- .../graphene/chain/proposal_object.hpp | 5 +- .../graphene/chain/protocol/account.hpp | 22 +- .../graphene/chain/protocol/address.hpp | 12 +- .../graphene/chain/protocol/assert.hpp | 3 - .../include/graphene/chain/protocol/asset.hpp | 4 - .../graphene/chain/protocol/asset_ops.hpp | 27 - .../graphene/chain/protocol/authority.hpp | 3 - .../graphene/chain/protocol/balance.hpp | 4 - .../include/graphene/chain/protocol/base.hpp | 5 - .../include/graphene/chain/protocol/block.hpp | 5 - .../graphene/chain/protocol/buyback.hpp | 2 - .../chain/protocol/chain_parameters.hpp | 34 +- .../chain/protocol/committee_member.hpp | 7 - .../graphene/chain/protocol/confidential.hpp | 7 - .../graphene/chain/protocol/custom.hpp | 3 - .../include/graphene/chain/protocol/ext.hpp | 1 - .../include/graphene/chain/protocol/fba.hpp | 3 - .../graphene/chain/protocol/fee_schedule.hpp | 3 - .../graphene/chain/protocol/market.hpp | 11 +- .../include/graphene/chain/protocol/memo.hpp | 3 - .../graphene/chain/protocol/operations.hpp | 2 - .../graphene/chain/protocol/proposal.hpp | 8 - .../chain/protocol/special_authority.hpp | 2 - .../graphene/chain/protocol/transaction.hpp | 5 - .../graphene/chain/protocol/transfer.hpp | 6 - .../include/graphene/chain/protocol/types.hpp | 30 +- .../graphene/chain/protocol/vesting.hpp | 20 +- .../include/graphene/chain/protocol/vote.hpp | 9 +- .../chain/protocol/withdraw_permission.hpp | 10 - .../graphene/chain/protocol/witness.hpp | 6 - .../graphene/chain/protocol/worker.hpp | 3 - .../include/graphene/chain/pts_address.hpp | 11 +- .../chain/special_authority_object.hpp | 2 - .../graphene/chain/transaction_object.hpp | 4 +- .../chain/vesting_balance_evaluator.hpp | 1 - .../graphene/chain/vesting_balance_object.hpp | 8 +- .../chain/withdraw_permission_object.hpp | 2 - .../include/graphene/chain/witness_object.hpp | 4 +- .../chain/witness_schedule_object.hpp | 3 - .../include/graphene/chain/worker_object.hpp | 5 +- libraries/chain/proposal_evaluator.cpp | 15 +- libraries/chain/proposal_object.cpp | 4 +- libraries/chain/protocol/account.cpp | 16 - libraries/chain/protocol/address.cpp | 5 +- libraries/chain/protocol/assert.cpp | 10 +- libraries/chain/protocol/asset.cpp | 11 +- libraries/chain/protocol/asset_ops.cpp | 29 - libraries/chain/protocol/authority.cpp | 3 - libraries/chain/protocol/block.cpp | 5 - libraries/chain/protocol/committee_member.cpp | 11 - libraries/chain/protocol/confidential.cpp | 13 +- libraries/chain/protocol/custom.cpp | 5 - libraries/chain/protocol/fee_schedule.cpp | 4 - libraries/chain/protocol/market.cpp | 9 - libraries/chain/protocol/memo.cpp | 4 - libraries/chain/protocol/operations.cpp | 5 - libraries/chain/protocol/proposal.cpp | 9 - libraries/chain/protocol/small_ops.cpp | 44 - libraries/chain/protocol/tournament.cpp | 1 - libraries/chain/protocol/transaction.cpp | 5 - libraries/chain/protocol/transfer.cpp | 7 - libraries/chain/protocol/vote.cpp | 2 - .../chain/protocol/withdraw_permission.cpp | 11 +- libraries/chain/protocol/witness.cpp | 6 - libraries/chain/protocol/worker.cpp | 4 - libraries/chain/pts_address.cpp | 11 +- libraries/chain/small_objects.cpp | 72 - libraries/chain/special_authority.cpp | 5 - libraries/chain/vesting_balance_evaluator.cpp | 118 +- libraries/chain/vesting_balance_object.cpp | 40 +- libraries/chain/worker_evaluator.cpp | 2 +- libraries/egenesis/egenesis_none.cpp | 2 - libraries/fc | 2 +- libraries/net/CMakeLists.txt | 1 - .../net/include/graphene/net/message.hpp | 14 +- .../include/graphene/net/peer_connection.hpp | 7 +- .../include/graphene/net/peer_database.hpp | 8 +- libraries/net/message.cpp | 29 - libraries/net/message_oriented_connection.cpp | 34 +- libraries/net/node.cpp | 24 +- libraries/net/peer_connection.cpp | 13 +- libraries/net/peer_database.cpp | 11 - libraries/plugins/CMakeLists.txt | 2 - .../account_history_plugin.cpp | 7 +- .../accounts_list/accounts_list_plugin.cpp | 2 +- .../affiliate_stats_plugin.cpp | 2 +- libraries/plugins/bookie/bookie_plugin.cpp | 10 +- .../delayed_node/delayed_node_plugin.cpp | 2 +- .../plugins/elasticsearch/CMakeLists.txt | 23 - .../elasticsearch/elasticsearch_plugin.cpp | 622 ------- .../elasticsearch/elasticsearch_plugin.hpp | 289 ---- libraries/plugins/es_objects/CMakeLists.txt | 23 - libraries/plugins/es_objects/es_objects.cpp | 401 ----- .../graphene/es_objects/es_objects.hpp | 113 -- libraries/plugins/snapshot/CMakeLists.txt | 2 - .../include/graphene/snapshot/snapshot.hpp | 1 - libraries/plugins/snapshot/snapshot.cpp | 5 - libraries/utilities/CMakeLists.txt | 1 - libraries/utilities/elasticsearch.cpp | 190 --- .../graphene/utilities/elasticsearch.hpp | 68 - .../wallet/include/graphene/wallet/wallet.hpp | 75 +- libraries/wallet/wallet.cpp | 456 +----- programs/cli_wallet/main.cpp | 12 +- programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/genesis.json | 44 +- programs/witness_node/main.cpp | 12 +- tests/CMakeLists.txt | 24 +- tests/app/main.cpp | 1 - tests/cli/cli_fixture.cpp | 2 +- tests/cli/main.cpp | 3 +- tests/cli/son.cpp | 15 +- tests/common/database_fixture.cpp | 69 +- tests/common/genesis_file_util.hpp | 2 +- tests/elasticsearch/main.cpp | 535 ------ tests/tests/block_tests.cpp | 13 +- tests/tests/gpos_tests.cpp | 1453 ----------------- tests/tests/history_api_tests.cpp | 400 +++-- tests/tests/voting_tests.cpp | 362 +--- 171 files changed, 1455 insertions(+), 7752 deletions(-) delete mode 100644 CMakeDoxyfile.in create mode 100644 libraries/app/impacted.cpp rename libraries/{chain/include/graphene/chain => app/include/graphene/app}/impacted.hpp (96%) delete mode 100644 libraries/chain/hardfork.d/GPOS.hf delete mode 100644 libraries/chain/protocol/small_ops.cpp delete mode 100644 libraries/chain/small_objects.cpp delete mode 100644 libraries/net/message.cpp delete mode 100644 libraries/plugins/elasticsearch/CMakeLists.txt delete mode 100644 libraries/plugins/elasticsearch/elasticsearch_plugin.cpp delete mode 100644 libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp delete mode 100644 libraries/plugins/es_objects/CMakeLists.txt delete mode 100644 libraries/plugins/es_objects/es_objects.cpp delete mode 100644 libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp delete mode 100644 libraries/utilities/elasticsearch.cpp delete mode 100644 libraries/utilities/include/graphene/utilities/elasticsearch.hpp delete mode 100644 tests/elasticsearch/main.cpp delete mode 100644 tests/tests/gpos_tests.cpp diff --git a/.gitignore b/.gitignore index 39b23163..90311de0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ moc_* hardfork.hpp build_xc data -CMakeDoxyfile.in build diff --git a/.gitmodules b/.gitmodules index 4d3518d1..5572259c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,5 @@ ignore = dirty [submodule "libraries/fc"] path = libraries/fc - url = https://github.com/peerplays-network/peerplays-fc.git - branch = latest-fc + url = https://github.com/PBSA/peerplays-fc.git ignore = dirty diff --git a/CMakeDoxyfile.in b/CMakeDoxyfile.in deleted file mode 100644 index b0ed02fb..00000000 --- a/CMakeDoxyfile.in +++ /dev/null @@ -1,279 +0,0 @@ -# -# DO NOT EDIT! THIS FILE WAS GENERATED BY CMAKE! -# - -DOXYFILE_ENCODING = @DOXYGEN_DOXYFILE_ENCODING@ -PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ -PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ -PROJECT_BRIEF = @DOXYGEN_PROJECT_BRIEF@ -PROJECT_LOGO = @DOXYGEN_PROJECT_LOGO@ -OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ -CREATE_SUBDIRS = @DOXYGEN_CREATE_SUBDIRS@ -ALLOW_UNICODE_NAMES = @DOXYGEN_ALLOW_UNICODE_NAMES@ -OUTPUT_LANGUAGE = @DOXYGEN_OUTPUT_LANGUAGE@ -OUTPUT_TEXT_DIRECTION = @DOXYGEN_OUTPUT_TEXT_DIRECTION@ -BRIEF_MEMBER_DESC = @DOXYGEN_BRIEF_MEMBER_DESC@ -REPEAT_BRIEF = @DOXYGEN_REPEAT_BRIEF@ -ABBREVIATE_BRIEF = @DOXYGEN_ABBREVIATE_BRIEF@ -ALWAYS_DETAILED_SEC = @DOXYGEN_ALWAYS_DETAILED_SEC@ -INLINE_INHERITED_MEMB = @DOXYGEN_INLINE_INHERITED_MEMB@ -FULL_PATH_NAMES = @DOXYGEN_FULL_PATH_NAMES@ -STRIP_FROM_PATH = @DOXYGEN_STRIP_FROM_PATH@ -STRIP_FROM_INC_PATH = @DOXYGEN_STRIP_FROM_INC_PATH@ -SHORT_NAMES = @DOXYGEN_SHORT_NAMES@ -JAVADOC_AUTOBRIEF = @DOXYGEN_JAVADOC_AUTOBRIEF@ -JAVADOC_BANNER = @DOXYGEN_JAVADOC_BANNER@ -QT_AUTOBRIEF = @DOXYGEN_QT_AUTOBRIEF@ -MULTILINE_CPP_IS_BRIEF = @DOXYGEN_MULTILINE_CPP_IS_BRIEF@ -INHERIT_DOCS = @DOXYGEN_INHERIT_DOCS@ -SEPARATE_MEMBER_PAGES = @DOXYGEN_SEPARATE_MEMBER_PAGES@ -TAB_SIZE = @DOXYGEN_TAB_SIZE@ -ALIASES = @DOXYGEN_ALIASES@ -TCL_SUBST = @DOXYGEN_TCL_SUBST@ -OPTIMIZE_OUTPUT_FOR_C = @DOXYGEN_OPTIMIZE_OUTPUT_FOR_C@ -OPTIMIZE_OUTPUT_JAVA = @DOXYGEN_OPTIMIZE_OUTPUT_JAVA@ -OPTIMIZE_FOR_FORTRAN = @DOXYGEN_OPTIMIZE_FOR_FORTRAN@ -OPTIMIZE_OUTPUT_VHDL = @DOXYGEN_OPTIMIZE_OUTPUT_VHDL@ -OPTIMIZE_OUTPUT_SLICE = @DOXYGEN_OPTIMIZE_OUTPUT_SLICE@ -EXTENSION_MAPPING = @DOXYGEN_EXTENSION_MAPPING@ -MARKDOWN_SUPPORT = @DOXYGEN_MARKDOWN_SUPPORT@ -TOC_INCLUDE_HEADINGS = @DOXYGEN_TOC_INCLUDE_HEADINGS@ -AUTOLINK_SUPPORT = @DOXYGEN_AUTOLINK_SUPPORT@ -BUILTIN_STL_SUPPORT = @DOXYGEN_BUILTIN_STL_SUPPORT@ -CPP_CLI_SUPPORT = @DOXYGEN_CPP_CLI_SUPPORT@ -SIP_SUPPORT = @DOXYGEN_SIP_SUPPORT@ -IDL_PROPERTY_SUPPORT = @DOXYGEN_IDL_PROPERTY_SUPPORT@ -DISTRIBUTE_GROUP_DOC = @DOXYGEN_DISTRIBUTE_GROUP_DOC@ -GROUP_NESTED_COMPOUNDS = @DOXYGEN_GROUP_NESTED_COMPOUNDS@ -SUBGROUPING = @DOXYGEN_SUBGROUPING@ -INLINE_GROUPED_CLASSES = @DOXYGEN_INLINE_GROUPED_CLASSES@ -INLINE_SIMPLE_STRUCTS = @DOXYGEN_INLINE_SIMPLE_STRUCTS@ -TYPEDEF_HIDES_STRUCT = @DOXYGEN_TYPEDEF_HIDES_STRUCT@ -LOOKUP_CACHE_SIZE = @DOXYGEN_LOOKUP_CACHE_SIZE@ -EXTRACT_ALL = @DOXYGEN_EXTRACT_ALL@ -EXTRACT_PRIVATE = @DOXYGEN_EXTRACT_PRIVATE@ -EXTRACT_PRIV_VIRTUAL = @DOXYGEN_EXTRACT_PRIV_VIRTUAL@ -EXTRACT_PACKAGE = @DOXYGEN_EXTRACT_PACKAGE@ -EXTRACT_STATIC = @DOXYGEN_EXTRACT_STATIC@ -EXTRACT_LOCAL_CLASSES = @DOXYGEN_EXTRACT_LOCAL_CLASSES@ -EXTRACT_LOCAL_METHODS = @DOXYGEN_EXTRACT_LOCAL_METHODS@ -EXTRACT_ANON_NSPACES = @DOXYGEN_EXTRACT_ANON_NSPACES@ -HIDE_UNDOC_MEMBERS = @DOXYGEN_HIDE_UNDOC_MEMBERS@ -HIDE_UNDOC_CLASSES = @DOXYGEN_HIDE_UNDOC_CLASSES@ -HIDE_FRIEND_COMPOUNDS = @DOXYGEN_HIDE_FRIEND_COMPOUNDS@ -HIDE_IN_BODY_DOCS = @DOXYGEN_HIDE_IN_BODY_DOCS@ -INTERNAL_DOCS = @DOXYGEN_INTERNAL_DOCS@ -CASE_SENSE_NAMES = @DOXYGEN_CASE_SENSE_NAMES@ -HIDE_SCOPE_NAMES = @DOXYGEN_HIDE_SCOPE_NAMES@ -HIDE_COMPOUND_REFERENCE= @DOXYGEN_HIDE_COMPOUND_REFERENCE@ -SHOW_INCLUDE_FILES = @DOXYGEN_SHOW_INCLUDE_FILES@ -SHOW_GROUPED_MEMB_INC = @DOXYGEN_SHOW_GROUPED_MEMB_INC@ -FORCE_LOCAL_INCLUDES = @DOXYGEN_FORCE_LOCAL_INCLUDES@ -INLINE_INFO = @DOXYGEN_INLINE_INFO@ -SORT_MEMBER_DOCS = @DOXYGEN_SORT_MEMBER_DOCS@ -SORT_BRIEF_DOCS = @DOXYGEN_SORT_BRIEF_DOCS@ -SORT_MEMBERS_CTORS_1ST = @DOXYGEN_SORT_MEMBERS_CTORS_1ST@ -SORT_GROUP_NAMES = @DOXYGEN_SORT_GROUP_NAMES@ -SORT_BY_SCOPE_NAME = @DOXYGEN_SORT_BY_SCOPE_NAME@ -STRICT_PROTO_MATCHING = @DOXYGEN_STRICT_PROTO_MATCHING@ -GENERATE_TODOLIST = @DOXYGEN_GENERATE_TODOLIST@ -GENERATE_TESTLIST = @DOXYGEN_GENERATE_TESTLIST@ -GENERATE_BUGLIST = @DOXYGEN_GENERATE_BUGLIST@ -GENERATE_DEPRECATEDLIST= @DOXYGEN_GENERATE_DEPRECATEDLIST@ -ENABLED_SECTIONS = @DOXYGEN_ENABLED_SECTIONS@ -MAX_INITIALIZER_LINES = @DOXYGEN_MAX_INITIALIZER_LINES@ -SHOW_USED_FILES = @DOXYGEN_SHOW_USED_FILES@ -SHOW_FILES = @DOXYGEN_SHOW_FILES@ -SHOW_NAMESPACES = @DOXYGEN_SHOW_NAMESPACES@ -FILE_VERSION_FILTER = @DOXYGEN_FILE_VERSION_FILTER@ -LAYOUT_FILE = @DOXYGEN_LAYOUT_FILE@ -CITE_BIB_FILES = @DOXYGEN_CITE_BIB_FILES@ -QUIET = @DOXYGEN_QUIET@ -WARNINGS = @DOXYGEN_WARNINGS@ -WARN_IF_UNDOCUMENTED = @DOXYGEN_WARN_IF_UNDOCUMENTED@ -WARN_IF_DOC_ERROR = @DOXYGEN_WARN_IF_DOC_ERROR@ -WARN_NO_PARAMDOC = @DOXYGEN_WARN_NO_PARAMDOC@ -WARN_AS_ERROR = @DOXYGEN_WARN_AS_ERROR@ -WARN_FORMAT = @DOXYGEN_WARN_FORMAT@ -WARN_LOGFILE = @DOXYGEN_WARN_LOGFILE@ -INPUT = @DOXYGEN_INPUT@ -INPUT_ENCODING = @DOXYGEN_INPUT_ENCODING@ -FILE_PATTERNS = @DOXYGEN_FILE_PATTERNS@ -RECURSIVE = @DOXYGEN_RECURSIVE@ -EXCLUDE = @DOXYGEN_EXCLUDE@ -EXCLUDE_SYMLINKS = @DOXYGEN_EXCLUDE_SYMLINKS@ -EXCLUDE_PATTERNS = @DOXYGEN_EXCLUDE_PATTERNS@ -EXCLUDE_SYMBOLS = @DOXYGEN_EXCLUDE_SYMBOLS@ -EXAMPLE_PATH = @DOXYGEN_EXAMPLE_PATH@ -EXAMPLE_PATTERNS = @DOXYGEN_EXAMPLE_PATTERNS@ -EXAMPLE_RECURSIVE = @DOXYGEN_EXAMPLE_RECURSIVE@ -IMAGE_PATH = @DOXYGEN_IMAGE_PATH@ -INPUT_FILTER = @DOXYGEN_INPUT_FILTER@ -FILTER_PATTERNS = @DOXYGEN_FILTER_PATTERNS@ -FILTER_SOURCE_FILES = @DOXYGEN_FILTER_SOURCE_FILES@ -FILTER_SOURCE_PATTERNS = @DOXYGEN_FILTER_SOURCE_PATTERNS@ -USE_MDFILE_AS_MAINPAGE = @DOXYGEN_USE_MDFILE_AS_MAINPAGE@ -SOURCE_BROWSER = @DOXYGEN_SOURCE_BROWSER@ -INLINE_SOURCES = @DOXYGEN_INLINE_SOURCES@ -STRIP_CODE_COMMENTS = @DOXYGEN_STRIP_CODE_COMMENTS@ -REFERENCED_BY_RELATION = @DOXYGEN_REFERENCED_BY_RELATION@ -REFERENCES_RELATION = @DOXYGEN_REFERENCES_RELATION@ -REFERENCES_LINK_SOURCE = @DOXYGEN_REFERENCES_LINK_SOURCE@ -SOURCE_TOOLTIPS = @DOXYGEN_SOURCE_TOOLTIPS@ -USE_HTAGS = @DOXYGEN_USE_HTAGS@ -VERBATIM_HEADERS = @DOXYGEN_VERBATIM_HEADERS@ -CLANG_ASSISTED_PARSING = @DOXYGEN_CLANG_ASSISTED_PARSING@ -CLANG_OPTIONS = @DOXYGEN_CLANG_OPTIONS@ -CLANG_DATABASE_PATH = @DOXYGEN_CLANG_DATABASE_PATH@ -ALPHABETICAL_INDEX = @DOXYGEN_ALPHABETICAL_INDEX@ -COLS_IN_ALPHA_INDEX = @DOXYGEN_COLS_IN_ALPHA_INDEX@ -IGNORE_PREFIX = @DOXYGEN_IGNORE_PREFIX@ -GENERATE_HTML = @DOXYGEN_GENERATE_HTML@ -HTML_OUTPUT = @DOXYGEN_HTML_OUTPUT@ -HTML_FILE_EXTENSION = @DOXYGEN_HTML_FILE_EXTENSION@ -HTML_HEADER = @DOXYGEN_HTML_HEADER@ -HTML_FOOTER = @DOXYGEN_HTML_FOOTER@ -HTML_STYLESHEET = @DOXYGEN_HTML_STYLESHEET@ -HTML_EXTRA_STYLESHEET = @DOXYGEN_HTML_EXTRA_STYLESHEET@ -HTML_EXTRA_FILES = @DOXYGEN_HTML_EXTRA_FILES@ -HTML_COLORSTYLE_HUE = @DOXYGEN_HTML_COLORSTYLE_HUE@ -HTML_COLORSTYLE_SAT = @DOXYGEN_HTML_COLORSTYLE_SAT@ -HTML_COLORSTYLE_GAMMA = @DOXYGEN_HTML_COLORSTYLE_GAMMA@ -HTML_TIMESTAMP = @DOXYGEN_HTML_TIMESTAMP@ -HTML_DYNAMIC_MENUS = @DOXYGEN_HTML_DYNAMIC_MENUS@ -HTML_DYNAMIC_SECTIONS = @DOXYGEN_HTML_DYNAMIC_SECTIONS@ -HTML_INDEX_NUM_ENTRIES = @DOXYGEN_HTML_INDEX_NUM_ENTRIES@ -GENERATE_DOCSET = @DOXYGEN_GENERATE_DOCSET@ -DOCSET_FEEDNAME = @DOXYGEN_DOCSET_FEEDNAME@ -DOCSET_BUNDLE_ID = @DOXYGEN_DOCSET_BUNDLE_ID@ -DOCSET_PUBLISHER_ID = @DOXYGEN_DOCSET_PUBLISHER_ID@ -DOCSET_PUBLISHER_NAME = @DOXYGEN_DOCSET_PUBLISHER_NAME@ -GENERATE_HTMLHELP = @DOXYGEN_GENERATE_HTMLHELP@ -CHM_FILE = @DOXYGEN_CHM_FILE@ -HHC_LOCATION = @DOXYGEN_HHC_LOCATION@ -GENERATE_CHI = @DOXYGEN_GENERATE_CHI@ -CHM_INDEX_ENCODING = @DOXYGEN_CHM_INDEX_ENCODING@ -BINARY_TOC = @DOXYGEN_BINARY_TOC@ -TOC_EXPAND = @DOXYGEN_TOC_EXPAND@ -GENERATE_QHP = @DOXYGEN_GENERATE_QHP@ -QCH_FILE = @DOXYGEN_QCH_FILE@ -QHP_NAMESPACE = @DOXYGEN_QHP_NAMESPACE@ -QHP_VIRTUAL_FOLDER = @DOXYGEN_QHP_VIRTUAL_FOLDER@ -QHP_CUST_FILTER_NAME = @DOXYGEN_QHP_CUST_FILTER_NAME@ -QHP_CUST_FILTER_ATTRS = @DOXYGEN_QHP_CUST_FILTER_ATTRS@ -QHP_SECT_FILTER_ATTRS = @DOXYGEN_QHP_SECT_FILTER_ATTRS@ -QHG_LOCATION = @DOXYGEN_QHG_LOCATION@ -GENERATE_ECLIPSEHELP = @DOXYGEN_GENERATE_ECLIPSEHELP@ -ECLIPSE_DOC_ID = @DOXYGEN_ECLIPSE_DOC_ID@ -DISABLE_INDEX = @DOXYGEN_DISABLE_INDEX@ -GENERATE_TREEVIEW = @DOXYGEN_GENERATE_TREEVIEW@ -ENUM_VALUES_PER_LINE = @DOXYGEN_ENUM_VALUES_PER_LINE@ -TREEVIEW_WIDTH = @DOXYGEN_TREEVIEW_WIDTH@ -EXT_LINKS_IN_WINDOW = @DOXYGEN_EXT_LINKS_IN_WINDOW@ -FORMULA_FONTSIZE = @DOXYGEN_FORMULA_FONTSIZE@ -FORMULA_TRANSPARENT = @DOXYGEN_FORMULA_TRANSPARENT@ -USE_MATHJAX = @DOXYGEN_USE_MATHJAX@ -MATHJAX_FORMAT = @DOXYGEN_MATHJAX_FORMAT@ -MATHJAX_RELPATH = @DOXYGEN_MATHJAX_RELPATH@ -MATHJAX_EXTENSIONS = @DOXYGEN_MATHJAX_EXTENSIONS@ -MATHJAX_CODEFILE = @DOXYGEN_MATHJAX_CODEFILE@ -SEARCHENGINE = @DOXYGEN_SEARCHENGINE@ -SERVER_BASED_SEARCH = @DOXYGEN_SERVER_BASED_SEARCH@ -EXTERNAL_SEARCH = @DOXYGEN_EXTERNAL_SEARCH@ -SEARCHENGINE_URL = @DOXYGEN_SEARCHENGINE_URL@ -SEARCHDATA_FILE = @DOXYGEN_SEARCHDATA_FILE@ -EXTERNAL_SEARCH_ID = @DOXYGEN_EXTERNAL_SEARCH_ID@ -EXTRA_SEARCH_MAPPINGS = @DOXYGEN_EXTRA_SEARCH_MAPPINGS@ -GENERATE_LATEX = @DOXYGEN_GENERATE_LATEX@ -LATEX_OUTPUT = @DOXYGEN_LATEX_OUTPUT@ -LATEX_CMD_NAME = @DOXYGEN_LATEX_CMD_NAME@ -MAKEINDEX_CMD_NAME = @DOXYGEN_MAKEINDEX_CMD_NAME@ -LATEX_MAKEINDEX_CMD = @DOXYGEN_LATEX_MAKEINDEX_CMD@ -COMPACT_LATEX = @DOXYGEN_COMPACT_LATEX@ -PAPER_TYPE = @DOXYGEN_PAPER_TYPE@ -EXTRA_PACKAGES = @DOXYGEN_EXTRA_PACKAGES@ -LATEX_HEADER = @DOXYGEN_LATEX_HEADER@ -LATEX_FOOTER = @DOXYGEN_LATEX_FOOTER@ -LATEX_EXTRA_STYLESHEET = @DOXYGEN_LATEX_EXTRA_STYLESHEET@ -LATEX_EXTRA_FILES = @DOXYGEN_LATEX_EXTRA_FILES@ -PDF_HYPERLINKS = @DOXYGEN_PDF_HYPERLINKS@ -USE_PDFLATEX = @DOXYGEN_USE_PDFLATEX@ -LATEX_BATCHMODE = @DOXYGEN_LATEX_BATCHMODE@ -LATEX_HIDE_INDICES = @DOXYGEN_LATEX_HIDE_INDICES@ -LATEX_SOURCE_CODE = @DOXYGEN_LATEX_SOURCE_CODE@ -LATEX_BIB_STYLE = @DOXYGEN_LATEX_BIB_STYLE@ -LATEX_TIMESTAMP = @DOXYGEN_LATEX_TIMESTAMP@ -LATEX_EMOJI_DIRECTORY = @DOXYGEN_LATEX_EMOJI_DIRECTORY@ -GENERATE_RTF = @DOXYGEN_GENERATE_RTF@ -RTF_OUTPUT = @DOXYGEN_RTF_OUTPUT@ -COMPACT_RTF = @DOXYGEN_COMPACT_RTF@ -RTF_HYPERLINKS = @DOXYGEN_RTF_HYPERLINKS@ -RTF_STYLESHEET_FILE = @DOXYGEN_RTF_STYLESHEET_FILE@ -RTF_EXTENSIONS_FILE = @DOXYGEN_RTF_EXTENSIONS_FILE@ -RTF_SOURCE_CODE = @DOXYGEN_RTF_SOURCE_CODE@ -GENERATE_MAN = @DOXYGEN_GENERATE_MAN@ -MAN_OUTPUT = @DOXYGEN_MAN_OUTPUT@ -MAN_EXTENSION = @DOXYGEN_MAN_EXTENSION@ -MAN_SUBDIR = @DOXYGEN_MAN_SUBDIR@ -MAN_LINKS = @DOXYGEN_MAN_LINKS@ -GENERATE_XML = @DOXYGEN_GENERATE_XML@ -XML_OUTPUT = @DOXYGEN_XML_OUTPUT@ -XML_PROGRAMLISTING = @DOXYGEN_XML_PROGRAMLISTING@ -XML_NS_MEMB_FILE_SCOPE = @DOXYGEN_XML_NS_MEMB_FILE_SCOPE@ -GENERATE_DOCBOOK = @DOXYGEN_GENERATE_DOCBOOK@ -DOCBOOK_OUTPUT = @DOXYGEN_DOCBOOK_OUTPUT@ -DOCBOOK_PROGRAMLISTING = @DOXYGEN_DOCBOOK_PROGRAMLISTING@ -GENERATE_AUTOGEN_DEF = @DOXYGEN_GENERATE_AUTOGEN_DEF@ -GENERATE_PERLMOD = @DOXYGEN_GENERATE_PERLMOD@ -PERLMOD_LATEX = @DOXYGEN_PERLMOD_LATEX@ -PERLMOD_PRETTY = @DOXYGEN_PERLMOD_PRETTY@ -PERLMOD_MAKEVAR_PREFIX = @DOXYGEN_PERLMOD_MAKEVAR_PREFIX@ -ENABLE_PREPROCESSING = @DOXYGEN_ENABLE_PREPROCESSING@ -MACRO_EXPANSION = @DOXYGEN_MACRO_EXPANSION@ -EXPAND_ONLY_PREDEF = @DOXYGEN_EXPAND_ONLY_PREDEF@ -SEARCH_INCLUDES = @DOXYGEN_SEARCH_INCLUDES@ -INCLUDE_PATH = @DOXYGEN_INCLUDE_PATH@ -INCLUDE_FILE_PATTERNS = @DOXYGEN_INCLUDE_FILE_PATTERNS@ -PREDEFINED = @DOXYGEN_PREDEFINED@ -EXPAND_AS_DEFINED = @DOXYGEN_EXPAND_AS_DEFINED@ -SKIP_FUNCTION_MACROS = @DOXYGEN_SKIP_FUNCTION_MACROS@ -TAGFILES = @DOXYGEN_TAGFILES@ -GENERATE_TAGFILE = @DOXYGEN_GENERATE_TAGFILE@ -ALLEXTERNALS = @DOXYGEN_ALLEXTERNALS@ -EXTERNAL_GROUPS = @DOXYGEN_EXTERNAL_GROUPS@ -EXTERNAL_PAGES = @DOXYGEN_EXTERNAL_PAGES@ -CLASS_DIAGRAMS = @DOXYGEN_CLASS_DIAGRAMS@ -DIA_PATH = @DOXYGEN_DIA_PATH@ -HIDE_UNDOC_RELATIONS = @DOXYGEN_HIDE_UNDOC_RELATIONS@ -HAVE_DOT = @DOXYGEN_HAVE_DOT@ -DOT_NUM_THREADS = @DOXYGEN_DOT_NUM_THREADS@ -DOT_FONTNAME = @DOXYGEN_DOT_FONTNAME@ -DOT_FONTSIZE = @DOXYGEN_DOT_FONTSIZE@ -DOT_FONTPATH = @DOXYGEN_DOT_FONTPATH@ -CLASS_GRAPH = @DOXYGEN_CLASS_GRAPH@ -COLLABORATION_GRAPH = @DOXYGEN_COLLABORATION_GRAPH@ -GROUP_GRAPHS = @DOXYGEN_GROUP_GRAPHS@ -UML_LOOK = @DOXYGEN_UML_LOOK@ -UML_LIMIT_NUM_FIELDS = @DOXYGEN_UML_LIMIT_NUM_FIELDS@ -TEMPLATE_RELATIONS = @DOXYGEN_TEMPLATE_RELATIONS@ -INCLUDE_GRAPH = @DOXYGEN_INCLUDE_GRAPH@ -INCLUDED_BY_GRAPH = @DOXYGEN_INCLUDED_BY_GRAPH@ -CALL_GRAPH = @DOXYGEN_CALL_GRAPH@ -CALLER_GRAPH = @DOXYGEN_CALLER_GRAPH@ -GRAPHICAL_HIERARCHY = @DOXYGEN_GRAPHICAL_HIERARCHY@ -DIRECTORY_GRAPH = @DOXYGEN_DIRECTORY_GRAPH@ -DOT_IMAGE_FORMAT = @DOXYGEN_DOT_IMAGE_FORMAT@ -INTERACTIVE_SVG = @DOXYGEN_INTERACTIVE_SVG@ -DOT_PATH = @DOXYGEN_DOT_PATH@ -DOTFILE_DIRS = @DOXYGEN_DOTFILE_DIRS@ -MSCFILE_DIRS = @DOXYGEN_MSCFILE_DIRS@ -DIAFILE_DIRS = @DOXYGEN_DIAFILE_DIRS@ -PLANTUML_JAR_PATH = @DOXYGEN_PLANTUML_JAR_PATH@ -PLANTUML_CFG_FILE = @DOXYGEN_PLANTUML_CFG_FILE@ -PLANTUML_INCLUDE_PATH = @DOXYGEN_PLANTUML_INCLUDE_PATH@ -DOT_GRAPH_MAX_NODES = @DOXYGEN_DOT_GRAPH_MAX_NODES@ -MAX_DOT_GRAPH_DEPTH = @DOXYGEN_MAX_DOT_GRAPH_DEPTH@ -DOT_TRANSPARENT = @DOXYGEN_DOT_TRANSPARENT@ -DOT_MULTI_TARGETS = @DOXYGEN_DOT_MULTI_TARGETS@ -GENERATE_LEGEND = @DOXYGEN_GENERATE_LEGEND@ -DOT_CLEANUP = @DOXYGEN_DOT_CLEANUP@ diff --git a/Doxyfile b/Doxyfile index 18bb33e2..75931ef9 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Peerplays" +PROJECT_NAME = "Graphene" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index ea0a2c07..e6f8940c 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( graphene_app api.cpp application.cpp database_api.cpp + impacted.cpp plugin.cpp config_util.cpp ${HEADERS} @@ -13,8 +14,7 @@ add_library( graphene_app # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain graphene_elasticsearch) - +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie peerplays_sidechain ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 11d39f69..d31abe19 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -39,19 +40,8 @@ #include #include -#include #include -template class fc::api; -template class fc::api; -template class fc::api; -template class fc::api; -template class fc::api; -template class fc::api; -template class fc::api; -template class fc::api; - - namespace graphene { namespace app { login_api::login_api(application& a) @@ -113,7 +103,7 @@ namespace graphene { namespace app { } else if( api_name == "asset_api" ) { - _asset_api = std::make_shared< asset_api >( _app ); + _asset_api = std::make_shared< asset_api >( std::ref( *_app.chain_database() ) ); } else if( api_name == "debug_api" ) { @@ -546,12 +536,10 @@ namespace graphene { namespace app { } // end get_relevant_accounts( obj ) #endif - vector history_api::get_fill_order_history( std::string asset_a, std::string asset_b, uint32_t limit )const + vector history_api::get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); - asset_id_type a = database_api.get_asset_id_from_string( asset_a ); - asset_id_type b = database_api.get_asset_id_from_string( asset_b ); if( a > b ) std::swap(a,b); const auto& history_idx = db.get_index_type().indices().get(); history_key hkey; @@ -573,7 +561,7 @@ namespace graphene { namespace app { return result; } - vector history_api::get_account_history( const std::string account_id_or_name, + vector history_api::get_account_history( account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start ) const @@ -582,26 +570,12 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT( limit <= 100 ); vector result; - account_id_type account; try { - account = database_api.get_account_id_from_string(account_id_or_name); const account_transaction_history_object& node = account(db).statistics(db).most_recent_op(db); if(start == operation_history_id_type() || start.instance.value > node.operation_id.instance.value) start = node.operation_id; } catch(...) { return result; } - if(_app.is_plugin_enabled("elasticsearch")) { - auto es = _app.get_plugin("elasticsearch"); - if(es.get()->get_running_mode() != elasticsearch::mode::only_save) { - if(!_app.elasticsearch_thread) - _app.elasticsearch_thread= std::make_shared("elasticsearch"); - - return _app.elasticsearch_thread->async([&es, &account, &stop, &limit, &start]() { - return es->get_account_history(account, stop, limit, start); - }, "thread invoke for method " BOOST_PP_STRINGIZE(method_name)).wait(); - } - } - const auto& hist_idx = db.get_index_type(); const auto& by_op_idx = hist_idx.indices().get(); auto index_start = by_op_idx.begin(); @@ -620,7 +594,7 @@ namespace graphene { namespace app { return result; } - vector history_api::get_account_history_operations( const std::string account_id_or_name, + vector history_api::get_account_history_operations( account_id_type account, int operation_id, operation_history_id_type start, operation_history_id_type stop, @@ -630,11 +604,6 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT( limit <= 100 ); vector result; - account_id_type account; - try { - account = database_api.get_account_id_from_string(account_id_or_name); - } catch (...) { return result; } - const auto& stats = account(db).statistics(db); if( stats.most_recent_op == account_transaction_history_id_type() ) return result; const account_transaction_history_object* node = &stats.most_recent_op(db); @@ -661,7 +630,7 @@ namespace graphene { namespace app { } - vector history_api::get_relative_account_history( const std::string account_id_or_name, + vector history_api::get_relative_account_history( account_id_type account, uint32_t stop, unsigned limit, uint32_t start) const @@ -670,10 +639,6 @@ namespace graphene { namespace app { const auto& db = *_app.chain_database(); FC_ASSERT(limit <= 100); vector result; - account_id_type account; - try { - account = database_api.get_account_id_from_string(account_id_or_name); - } catch(...) { return result; } const auto& stats = account(db).statistics(db); if( start == 0 ) start = stats.total_ops; @@ -713,13 +678,11 @@ namespace graphene { namespace app { return hist->tracked_buckets(); } - vector history_api::get_market_history( std::string asset_a, std::string asset_b, + vector history_api::get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const { try { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); - asset_id_type a = database_api.get_asset_id_from_string( asset_a ); - asset_id_type b = database_api.get_asset_id_from_string( asset_b ); vector result; result.reserve(200); @@ -739,7 +702,7 @@ namespace graphene { namespace app { ++itr; } return result; - } FC_CAPTURE_AND_RETHROW( (asset_a)(asset_b)(bucket_seconds)(start)(end) ) } + } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } crypto_api::crypto_api(){}; @@ -798,16 +761,12 @@ namespace graphene { namespace app { } // asset_api - asset_api::asset_api(graphene::app::application& app) : - _app(app), - _db( *app.chain_database()), - database_api( std::ref(*app.chain_database())) { } + asset_api::asset_api(graphene::chain::database& db) : _db(db) { } asset_api::~asset_api() { } - vector asset_api::get_asset_holders( std::string asset, uint32_t start, uint32_t limit ) const { + vector asset_api::get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit ) const { FC_ASSERT(limit <= 100); - asset_id_type asset_id = database_api.get_asset_id_from_string( asset ); const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); @@ -838,11 +797,11 @@ namespace graphene { namespace app { return result; } // get number of asset holders. - int asset_api::get_asset_holders_count( std::string asset ) const { + int asset_api::get_asset_holders_count( asset_id_type asset_id ) const { const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); - asset_id_type asset_id = database_api.get_asset_id_from_string( asset ); auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); + int count = boost::distance(range) - 1; return count; diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index adfd8402..0f0c0690 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -226,7 +226,7 @@ namespace detail { void new_connection( const fc::http::websocket_connection_ptr& c ) { - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); auto login = std::make_shared( std::ref(*_self) ); login->enable_api("database_api"); @@ -375,11 +375,6 @@ namespace detail { } _chain_db->add_checkpoints( loaded_checkpoints ); - if( _options->count("enable-standby-votes-tracking") ) - { - _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as() ); - } - bool replay = false; std::string replay_reason = "reason not provided"; @@ -931,10 +926,6 @@ void application::set_program_options(boost::program_options::options_descriptio ("genesis-json", bpo::value(), "File to read Genesis State from") ("dbg-init-key", bpo::value(), "Block signing key to use for init witnesses, overrides genesis file") ("api-access", bpo::value(), "JSON file specifying API permissions") - ("enable-standby-votes-tracking", bpo::value()->implicit_value(true), - "Whether to enable tracking of votes of standby witnesses and committee members. " - "Set it to true to provide accurate data to API clients, set to false for slightly better performance.") - ("plugins", bpo::value(), "Space-separated list of plugins to activate") ; command_line_options.add(configuration_file_options); command_line_options.add_options() @@ -991,20 +982,9 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti wanted.push_back("witness"); wanted.push_back("account_history"); wanted.push_back("market_history"); - wanted.push_back("bookie"); } - int es_ah_conflict_counter = 0; for (auto& it : wanted) { - if(it == "account_history") - ++es_ah_conflict_counter; - if(it == "elasticsearch") - ++es_ah_conflict_counter; - - if(es_ah_conflict_counter > 1) { - elog("Can't start program with elasticsearch and account_history plugin at the same time"); - std::exit(EXIT_FAILURE); - } if (!it.empty()) enable_plugin(it); } } @@ -1029,7 +1009,9 @@ std::shared_ptr application::get_plugin(const string& name) con bool application::is_plugin_enabled(const string& name) const { - return !(my->_active_plugins.find(name) == my->_active_plugins.end()); + if(my->_active_plugins.find(name) == my->_active_plugins.end()) + return false; + return true; } net::node_ptr application::p2p_node() @@ -1069,8 +1051,7 @@ void graphene::app::application::enable_plugin(const string& name) my->_active_plugins[name]->plugin_set_app(this); } -void graphene::app::application::add_available_plugin(std::shared_ptr p) -{ +void graphene::app::application::add_available_plugin(std::shared_ptr p) { my->_available_plugins[p->plugin_name()] = p; } diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c9aba7ff..c6c8a952 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -26,15 +26,11 @@ #include #include #include -#include -#include #include #include #include -#include -#include #include #include @@ -49,8 +45,6 @@ typedef std::map< std::pair, std::vector > market_queue_type; -template class fc::api; - namespace graphene { namespace app { class database_api_impl : public std::enable_shared_from_this @@ -87,26 +81,23 @@ class database_api_impl : public std::enable_shared_from_this bool is_public_key_registered(string public_key) const; // Accounts - account_id_type get_account_id_from_string(const std::string& name_or_id)const; - vector> get_accounts(const vector& account_names_or_ids)const; + vector> get_accounts(const vector& account_ids)const; std::map get_full_accounts( const vector& names_or_ids, bool subscribe ); optional get_account_by_name( string name )const; - vector get_account_references( const std::string account_id_or_name )const; + vector get_account_references( account_id_type account_id )const; vector> lookup_account_names(const vector& account_names)const; map lookup_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_account_count()const; // Balances - vector get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const; + vector get_account_balances(account_id_type id, const flat_set& assets)const; + vector get_named_account_balances(const std::string& name, const flat_set& assets)const; vector get_balance_objects( const vector
& addrs )const; vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( const std::string account_id_or_name )const; + vector get_vesting_balances( account_id_type account_id )const; // Assets - asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const; - vector> get_assets(const vector& asset_symbols_or_ids)const; - // helper function - vector> get_assets( const vector& asset_ids )const; + vector> get_assets(const vector& asset_ids)const; vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; vector> lookup_asset_symbols(const vector& symbols_or_ids)const; uint64_t get_asset_count()const; @@ -133,13 +124,12 @@ class database_api_impl : public std::enable_shared_from_this asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const; // Markets / feeds - vector get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const; - vector get_limit_orders( const std::string& a, const std::string& b, const uint32_t limit)const; - vector get_call_orders(const std::string& a, uint32_t limit)const; - vector get_settle_orders(const std::string& a, uint32_t limit)const; - vector get_margin_positions( const std::string account_id_or_name )const; - void subscribe_to_market(std::function callback, const std::string& a, const std::string& b); - void unsubscribe_from_market(const std::string& a, const std::string& b); + vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; + vector get_call_orders(asset_id_type a, uint32_t limit)const; + vector get_settle_orders(asset_id_type a, uint32_t limit)const; + vector get_margin_positions( const account_id_type& id )const; + void subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b); + void unsubscribe_from_market(asset_id_type a, asset_id_type b); market_ticker get_ticker( const string& base, const string& quote )const; market_volume get_24_volume( const string& base, const string& quote )const; order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const; @@ -147,13 +137,13 @@ class database_api_impl : public std::enable_shared_from_this // Witnesses vector> get_witnesses(const vector& witness_ids)const; - fc::optional get_witness_by_account(const std::string account_id_or_name)const; + fc::optional get_witness_by_account(account_id_type account)const; map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_witness_count()const; // Committee members vector> get_committee_members(const vector& committee_member_ids)const; - fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; + fc::optional get_committee_member_by_account(account_id_type account)const; map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; // SON members @@ -185,10 +175,10 @@ class database_api_impl : public std::enable_shared_from_this bool verify_authority( const signed_transaction& trx )const; bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; processed_transaction validate_transaction( const signed_transaction& trx )const; - vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; + vector< fc::variant > get_required_fees( const vector& ops, asset_id_type id )const; // Proposed transactions - vector get_proposed_transactions( const std::string account_id_or_name )const; + vector get_proposed_transactions( account_id_type id )const; // Blinded balances vector get_blinded_balances( const flat_set& commitments )const; @@ -199,14 +189,8 @@ class database_api_impl : public std::enable_shared_from_this vector get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state); vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; - // gpos - gpos_info get_gpos_info(const account_id_type account) const; //private: - const account_object* get_account_from_string( const std::string& name_or_id, - bool throw_if_not_found = true ) const; - const asset_object* get_asset_from_string( const std::string& symbol_or_id, - bool throw_if_not_found = true ) const; template void subscribe_to_item( const T& i )const { @@ -646,27 +630,22 @@ bool database_api_impl::is_public_key_registered(string public_key) const // // ////////////////////////////////////////////////////////////////////// -account_id_type database_api::get_account_id_from_string(const std::string& name_or_id)const +vector> database_api::get_accounts(const vector& account_ids)const { - return my->get_account_from_string( name_or_id )->id; + return my->get_accounts( account_ids ); } -vector> database_api::get_accounts(const vector& account_names_or_ids)const +vector> database_api_impl::get_accounts(const vector& account_ids)const { - return my->get_accounts( account_names_or_ids ); -} - -vector> database_api_impl::get_accounts(const vector& account_names_or_ids)const -{ - vector> result; result.reserve(account_names_or_ids.size()); - std::transform(account_names_or_ids.begin(), account_names_or_ids.end(), std::back_inserter(result), - [this](std::string id_or_name) -> optional { - const account_object *account = get_account_from_string(id_or_name, false); - if(account == nullptr) - return {}; - - subscribe_to_item( account->id ); - return *account; + vector> result; result.reserve(account_ids.size()); + std::transform(account_ids.begin(), account_ids.end(), std::back_inserter(result), + [this](account_id_type id) -> optional { + if(auto o = _db.find(id)) + { + subscribe_to_item( id ); + return *o; + } + return {}; }); return result; } @@ -795,17 +774,16 @@ optional database_api_impl::get_account_by_name( string name )co return optional(); } -vector database_api::get_account_references( const std::string account_id_or_name )const +vector database_api::get_account_references( account_id_type account_id )const { - return my->get_account_references( account_id_or_name ); + return my->get_account_references( account_id ); } -vector database_api_impl::get_account_references( const std::string account_id_or_name )const +vector database_api_impl::get_account_references( account_id_type account_id )const { const auto& idx = _db.get_index_type(); 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); vector result; @@ -874,16 +852,13 @@ uint64_t database_api_impl::get_account_count()const // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_account_balances(const std::string& account_name_or_id, const flat_set& assets)const +vector database_api::get_account_balances(account_id_type id, const flat_set& assets)const { - return my->get_account_balances( account_name_or_id, assets ); + return my->get_account_balances( id, assets ); } -vector database_api_impl::get_account_balances( const std::string& account_name_or_id, - const flat_set& assets)const +vector database_api_impl::get_account_balances(account_id_type acnt, const flat_set& assets)const { - const account_object* account = get_account_from_string(account_name_or_id); - account_id_type acnt = account->id; vector result; if (assets.empty()) { @@ -906,7 +881,15 @@ vector database_api_impl::get_account_balances( const std::string& accoun vector database_api::get_named_account_balances(const std::string& name, const flat_set& assets)const { - return my->get_account_balances( name, assets ); + return my->get_named_account_balances( name, assets ); +} + +vector database_api_impl::get_named_account_balances(const std::string& name, const flat_set& assets) const +{ + const auto& accounts_by_name = _db.get_index_type().indices().get(); + auto itr = accounts_by_name.find(name); + FC_ASSERT( itr != accounts_by_name.end() ); + return get_account_balances(itr->get_id(), assets); } vector database_api::get_balance_objects( const vector
& addrs )const @@ -956,26 +939,24 @@ vector database_api_impl::get_vested_balances( const vector database_api::get_vesting_balances( const std::string account_id_or_name )const +vector database_api::get_vesting_balances( account_id_type account_id )const { - return my->get_vesting_balances( account_id_or_name ); + return my->get_vesting_balances( account_id ); } -vector database_api_impl::get_vesting_balances( const std::string account_id_or_name )const +vector database_api_impl::get_vesting_balances( account_id_type account_id )const { try { - const account_id_type account_id = get_account_from_string(account_id_or_name)->id; vector result; auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id); std::for_each(vesting_range.first, vesting_range.second, [&result](const vesting_balance_object& balance) { - if(balance.balance.amount > 0) - result.emplace_back(balance); + result.emplace_back(balance); }); return result; } - FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); + FC_CAPTURE_AND_RETHROW( (account_id) ); } ////////////////////////////////////////////////////////////////////// @@ -984,48 +965,9 @@ vector database_api_impl::get_vesting_balances( const st // // ////////////////////////////////////////////////////////////////////// -asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_or_id)const +vector> database_api::get_assets(const vector& asset_ids)const { - return my->get_asset_from_string( symbol_or_id )->id; -} - -const asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id, - bool throw_if_not_found ) const -{ - // TODO cache the result to avoid repeatly fetching from db - FC_ASSERT( symbol_or_id.size() > 0); - const asset_object* asset = nullptr; - if (std::isdigit(symbol_or_id[0])) - asset = _db.find(fc::variant(symbol_or_id, 1).as(1)); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(symbol_or_id); - if (itr != idx.end()) - asset = &*itr; - } - if(throw_if_not_found) - FC_ASSERT( asset, "no such asset" ); - return asset; -} - -vector> database_api::get_assets(const vector& asset_symbols_or_ids)const -{ - return my->get_assets( asset_symbols_or_ids ); -} - -vector> database_api_impl::get_assets(const vector& asset_symbols_or_ids)const -{ - vector> result; result.reserve(asset_symbols_or_ids.size()); - std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result), - [this](std::string id_or_name) -> optional { - const asset_object* asset_obj = get_asset_from_string( id_or_name, false ); - if( asset_obj == nullptr ) - return {}; - subscribe_to_item(asset_obj->id ); - return asset_object( *asset_obj ); - }); - return result; + return my->get_assets( asset_ids ); } vector> database_api_impl::get_assets(const vector& asset_ids)const @@ -1281,7 +1223,7 @@ vector database_api_impl::get_all_unmatched_bets_for_bettor(account_ // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const +vector database_api::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const { return my->get_limit_orders( a, b, limit ); } @@ -1289,22 +1231,12 @@ vector database_api::get_limit_orders(const std::string& a, /** * @return the limit orders for both sides of the book for the two assets specified up to limit number on each side. */ -vector database_api_impl::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const -{ - const asset_id_type asset_a_id = get_asset_from_string(a)->id; - const asset_id_type asset_b_id = get_asset_from_string(b)->id; - - return get_limit_orders(asset_a_id, asset_b_id, limit); -} - -vector database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b, - const uint32_t limit )const +vector database_api_impl::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const { const auto& limit_order_idx = _db.get_index_type(); const auto& limit_price_idx = limit_order_idx.indices().get(); vector result; - result.reserve(limit*2); uint32_t count = 0; auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); @@ -1328,46 +1260,45 @@ vector database_api_impl::get_limit_orders( const asset_id_t return result; } -vector database_api::get_call_orders(const std::string& a, uint32_t limit)const +vector database_api::get_call_orders(asset_id_type a, uint32_t limit)const { return my->get_call_orders( a, limit ); } -vector database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const +vector database_api_impl::get_call_orders(asset_id_type a, uint32_t limit)const { const auto& call_index = _db.get_index_type().indices().get(); - const asset_object* mia = get_asset_from_string(a); - price index_price = price::min(mia->bitasset_data(_db).options.short_backing_asset, mia->get_id()); + const asset_object& mia = _db.get(a); + price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id()); return vector(call_index.lower_bound(index_price.min()), call_index.lower_bound(index_price.max())); } -vector database_api::get_settle_orders(const std::string& a, uint32_t limit)const +vector database_api::get_settle_orders(asset_id_type a, uint32_t limit)const { return my->get_settle_orders( a, limit ); } -vector database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const +vector database_api_impl::get_settle_orders(asset_id_type a, uint32_t limit)const { const auto& settle_index = _db.get_index_type().indices().get(); - const asset_object* mia = get_asset_from_string(a); - return vector(settle_index.lower_bound(mia->get_id()), - settle_index.upper_bound(mia->get_id())); + const asset_object& mia = _db.get(a); + return vector(settle_index.lower_bound(mia.get_id()), + settle_index.upper_bound(mia.get_id())); } -vector database_api::get_margin_positions( const std::string account_id_or_name )const +vector database_api::get_margin_positions( const account_id_type& id )const { - return my->get_margin_positions( account_id_or_name ); + return my->get_margin_positions( id ); } -vector database_api_impl::get_margin_positions( const std::string account_id_or_name )const +vector database_api_impl::get_margin_positions( const account_id_type& id )const { try { const auto& idx = _db.get_index_type(); const auto& aidx = idx.indices().get(); - const account_id_type id = get_account_from_string(account_id_or_name)->id; auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) ); auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) ); vector result; @@ -1377,37 +1308,31 @@ vector database_api_impl::get_margin_positions( const std::st ++start; } return result; - } FC_CAPTURE_AND_RETHROW( (account_id_or_name) ) + } FC_CAPTURE_AND_RETHROW( (id) ) } -void database_api::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) +void database_api::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) { my->subscribe_to_market( callback, a, b ); } -void database_api_impl::subscribe_to_market(std::function callback, const std::string& a, const std::string& b) +void database_api_impl::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) { - auto asset_a_id = get_asset_from_string(a)->id; - auto asset_b_id = get_asset_from_string(b)->id; - - if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id); - FC_ASSERT(asset_a_id != asset_b_id); - _market_subscriptions[ std::make_pair(asset_a_id,asset_b_id) ] = callback; + if(a > b) std::swap(a,b); + FC_ASSERT(a != b); + _market_subscriptions[ std::make_pair(a,b) ] = callback; } -void database_api::unsubscribe_from_market(const std::string& a, const std::string& b) +void database_api::unsubscribe_from_market(asset_id_type a, asset_id_type b) { my->unsubscribe_from_market( a, b ); } -void database_api_impl::unsubscribe_from_market(const std::string& a, const std::string& b) +void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b) { - auto asset_a_id = get_asset_from_string(a)->id; - auto asset_b_id = get_asset_from_string(b)->id; - - if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id); - FC_ASSERT(asset_a_id != asset_b_id); - _market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id)); + if(a > b) std::swap(a,b); + FC_ASSERT(a != b); + _market_subscriptions.erase(std::make_pair(a,b)); } market_ticker database_api::get_ticker( const string& base, const string& quote )const @@ -1630,10 +1555,9 @@ vector> database_api::get_witnesses(const vectorget_witnesses( witness_ids ); } -vector database_api::get_workers_by_account(const std::string account_id_or_name)const +vector database_api::get_workers_by_account(account_id_type account)const { const auto& idx = my->_db.get_index_type().indices().get(); - const account_id_type account = my->get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); vector result; @@ -1659,15 +1583,14 @@ vector> database_api_impl::get_witnesses(const vector database_api::get_witness_by_account(const std::string account_id_or_name)const +fc::optional database_api::get_witness_by_account(account_id_type account)const { - return my->get_witness_by_account( account_id_or_name ); + return my->get_witness_by_account( account ); } -fc::optional database_api_impl::get_witness_by_account(const std::string account_id_or_name) const +fc::optional database_api_impl::get_witness_by_account(account_id_type account) const { const auto& idx = _db.get_index_type().indices().get(); - const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if( itr != idx.end() ) return *itr; @@ -1735,15 +1658,14 @@ vector> database_api_impl::get_committee_membe return result; } -fc::optional database_api::get_committee_member_by_account(const std::string account_id_or_name)const +fc::optional database_api::get_committee_member_by_account(account_id_type account)const { - return my->get_committee_member_by_account( account_id_or_name ); + return my->get_committee_member_by_account( account ); } -fc::optional database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const +fc::optional database_api_impl::get_committee_member_by_account(account_id_type account) const { const auto& idx = _db.get_index_type().indices().get(); - const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if( itr != idx.end() ) return *itr; @@ -2212,9 +2134,9 @@ processed_transaction database_api_impl::validate_transaction( const signed_tran return _db.validate_transaction(trx); } -vector< fc::variant > database_api::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const +vector< fc::variant > database_api::get_required_fees( const vector& ops, asset_id_type id )const { - return my->get_required_fees( ops, asset_id_or_symbol ); + return my->get_required_fees( ops, id ); } /** @@ -2273,7 +2195,7 @@ struct get_required_fees_helper uint32_t current_recursion = 0; }; -vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const +vector< fc::variant > database_api_impl::get_required_fees( const vector& ops, asset_id_type id )const { vector< operation > _ops = ops; // @@ -2283,7 +2205,7 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector result; result.reserve(ops.size()); - const asset_object& a = *get_asset_from_string(asset_id_or_symbol); + const asset_object& a = id(_db); get_required_fees_helper helper( _db.current_fee_schedule(), a.options.core_exchange_rate, @@ -2301,17 +2223,16 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector database_api::get_proposed_transactions( const std::string account_id_or_name )const +vector database_api::get_proposed_transactions( account_id_type id )const { - return my->get_proposed_transactions( account_id_or_name ); + return my->get_proposed_transactions( id ); } /** TODO: add secondary index that will accelerate this process */ -vector database_api_impl::get_proposed_transactions( const std::string account_id_or_name )const +vector database_api_impl::get_proposed_transactions( account_id_type id )const { const auto& idx = _db.get_index_type(); vector result; - const account_id_type id = get_account_from_string(account_id_or_name)->id; idx.inspect_all_objects( [&](const object& obj){ const proposal_object& p = static_cast(obj); @@ -2426,26 +2347,6 @@ vector database_api_impl::get_tournaments_by_state(tournament return result; } -const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id, - bool throw_if_not_found ) const -{ - // TODO cache the result to avoid repeatly fetching from db - FC_ASSERT( name_or_id.size() > 0); - const account_object* account = nullptr; - if (std::isdigit(name_or_id[0])) - account = _db.find(fc::variant(name_or_id, 1).as(1)); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(name_or_id); - if (itr != idx.end()) - account = &*itr; - } - if(throw_if_not_found) - FC_ASSERT( account, "no such account" ); - return account; -} - vector database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const { return my->get_registered_tournaments(account_filter, limit); @@ -2463,80 +2364,6 @@ vector database_api_impl::get_registered_tournaments(account return tournament_ids; } -////////////////////////////////////////////////////////////////////// -// // -// GPOS methods // -// // -////////////////////////////////////////////////////////////////////// - -graphene::app::gpos_info database_api::get_gpos_info(const account_id_type account) const -{ - return my->get_gpos_info(account); - -} -graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const -{ - FC_ASSERT( _db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time - gpos_info result; - - result.vesting_factor = _db.calculate_vesting_factor(account(_db)); - result.current_subperiod = _db.get_gpos_current_subperiod(); - result.last_voted_time = account(_db).statistics(_db).last_vote_time; - - const auto& dividend_data = asset_id_type()(_db).dividend_data(_db); - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db); - result.award = _db.get_balance(dividend_distribution_account, asset_id_type()(_db)); - - share_type total_amount; - auto balance_type = vesting_balance_type::gpos; -#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX - // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset - auto vesting_balances_begin = - vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type)); - auto vesting_balances_end = - vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type())); - - for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) - { - total_amount += vesting_balance_obj.balance.amount; - } -#else - const vesting_balance_index& vesting_index = _db.get_index_type(); - const auto& vesting_balances = vesting_index.indices().get(); - for (const vesting_balance_object& vesting_balance_obj : vesting_balances) - { - if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance_type == balance_type) - { - total_amount += vesting_balance_obj.balance.amount; - } - } -#endif - - vector account_vbos; - const time_point_sec now = _db.head_block_time(); - auto vesting_range = _db.get_index_type().indices().get().equal_range(account); - std::for_each(vesting_range.first, vesting_range.second, - [&account_vbos, now](const vesting_balance_object& balance) { - if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos - && balance.balance.asset_id == asset_id_type()) - account_vbos.emplace_back(balance); - }); - - share_type allowed_withdraw_amount = 0, account_vested_balance = 0; - - for (const vesting_balance_object& vesting_balance_obj : account_vbos) - { - account_vested_balance += vesting_balance_obj.balance.amount; - if(vesting_balance_obj.is_withdraw_allowed(_db.head_block_time(), vesting_balance_obj.balance.amount)) - allowed_withdraw_amount += vesting_balance_obj.balance.amount; - } - - result.total_amount = total_amount; - result.allowed_withdraw_amount = allowed_withdraw_amount; - result.account_vested_balance = account_vested_balance; - return result; -} - ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp new file mode 100644 index 00000000..90538087 --- /dev/null +++ b/libraries/app/impacted.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., 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 + +namespace graphene { namespace app { + +using namespace fc; +using namespace graphene::chain; + +// TODO: Review all of these, especially no-ops +struct get_impacted_account_visitor +{ + flat_set& _impacted; + get_impacted_account_visitor( flat_set& impact ):_impacted(impact) {} + typedef void result_type; + + void operator()( const transfer_operation& op ) + { + _impacted.insert( op.to ); + } + + void operator()( const asset_claim_fees_operation& op ){} + void operator()( const limit_order_create_operation& op ) {} + void operator()( const limit_order_cancel_operation& op ) + { + _impacted.insert( op.fee_paying_account ); + } + void operator()( const call_order_update_operation& op ) {} + void operator()( const fill_order_operation& op ) + { + _impacted.insert( op.account_id ); + } + + void operator()( const account_create_operation& op ) + { + _impacted.insert( op.registrar ); + _impacted.insert( op.referrer ); + add_authority_accounts( _impacted, op.owner ); + add_authority_accounts( _impacted, op.active ); + } + + void operator()( const account_update_operation& op ) + { + _impacted.insert( op.account ); + if( op.owner ) + add_authority_accounts( _impacted, *(op.owner) ); + if( op.active ) + add_authority_accounts( _impacted, *(op.active) ); + } + + void operator()( const account_whitelist_operation& op ) + { + _impacted.insert( op.account_to_list ); + } + + void operator()( const account_upgrade_operation& op ) {} + void operator()( const account_transfer_operation& op ) + { + _impacted.insert( op.new_owner ); + } + + void operator()( const asset_create_operation& op ) {} + void operator()( const asset_update_operation& op ) + { + if( op.new_issuer ) + _impacted.insert( *(op.new_issuer) ); + } + + void operator()( const asset_update_bitasset_operation& op ) {} + void operator()( const asset_update_dividend_operation& op ) {} + void operator()( const asset_dividend_distribution_operation& op ) + { + _impacted.insert( op.account_id ); + } + + void operator()( const asset_update_feed_producers_operation& op ) {} + + void operator()( const asset_issue_operation& op ) + { + _impacted.insert( op.issue_to_account ); + } + + void operator()( const asset_reserve_operation& op ) {} + void operator()( const asset_fund_fee_pool_operation& op ) {} + void operator()( const asset_settle_operation& op ) {} + void operator()( const asset_global_settle_operation& op ) {} + void operator()( const asset_publish_feed_operation& op ) {} + void operator()( const witness_create_operation& op ) + { + _impacted.insert( op.witness_account ); + } + void operator()( const witness_update_operation& op ) + { + _impacted.insert( op.witness_account ); + } + + void operator()( const proposal_create_operation& op ) + { + vector other; + for( const auto& proposed_op : op.proposed_ops ) + operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other ); + for( auto& o : other ) + add_authority_accounts( _impacted, o ); + } + + void operator()( const proposal_update_operation& op ) {} + void operator()( const proposal_delete_operation& op ) {} + + void operator()( const withdraw_permission_create_operation& op ) + { + _impacted.insert( op.authorized_account ); + } + + void operator()( const withdraw_permission_update_operation& op ) + { + _impacted.insert( op.authorized_account ); + } + + void operator()( const withdraw_permission_claim_operation& op ) + { + _impacted.insert( op.withdraw_from_account ); + } + + void operator()( const withdraw_permission_delete_operation& op ) + { + _impacted.insert( op.authorized_account ); + } + + void operator()( const committee_member_create_operation& op ) + { + _impacted.insert( op.committee_member_account ); + } + void operator()( const committee_member_update_operation& op ) + { + _impacted.insert( op.committee_member_account ); + } + void operator()( const committee_member_update_global_parameters_operation& op ) {} + + void operator()( const vesting_balance_create_operation& op ) + { + _impacted.insert( op.owner ); + } + + void operator()( const vesting_balance_withdraw_operation& op ) {} + void operator()( const worker_create_operation& op ) {} + void operator()( const custom_operation& op ) {} + void operator()( const assert_operation& op ) {} + void operator()( const balance_claim_operation& op ) {} + + void operator()( const override_transfer_operation& op ) + { + _impacted.insert( op.to ); + _impacted.insert( op.from ); + _impacted.insert( op.issuer ); + } + + void operator()( const transfer_to_blind_operation& op ) + { + _impacted.insert( op.from ); + for( const auto& out : op.outputs ) + add_authority_accounts( _impacted, out.owner ); + } + + void operator()( const blind_transfer_operation& op ) + { + for( const auto& in : op.inputs ) + add_authority_accounts( _impacted, in.owner ); + for( const auto& out : op.outputs ) + add_authority_accounts( _impacted, out.owner ); + } + + void operator()( const transfer_from_blind_operation& op ) + { + _impacted.insert( op.to ); + for( const auto& in : op.inputs ) + add_authority_accounts( _impacted, in.owner ); + } + + void operator()( const asset_settle_cancel_operation& op ) + { + _impacted.insert( op.account ); + } + + void operator()( const fba_distribute_operation& op ) + { + _impacted.insert( op.account_id ); + } + + void operator()( const sport_create_operation& op ) {} + void operator()( const sport_update_operation& op ) {} + void operator()( const sport_delete_operation& op ) {} + void operator()( const event_group_create_operation& op ) {} + void operator()( const event_group_update_operation& op ) {} + void operator()( const event_group_delete_operation& op ) {} + void operator()( const event_create_operation& op ) {} + void operator()( const event_update_operation& op ) {} + void operator()( const event_update_status_operation& op ) {} + void operator()( const betting_market_rules_create_operation& op ) {} + void operator()( const betting_market_rules_update_operation& op ) {} + void operator()( const betting_market_group_create_operation& op ) {} + void operator()( const betting_market_group_update_operation& op ) {} + void operator()( const betting_market_create_operation& op ) {} + void operator()( const betting_market_update_operation& op ) {} + void operator()( const betting_market_group_resolve_operation& op ) {} + void operator()( const betting_market_group_cancel_unmatched_bets_operation& op ) {} + + void operator()( const bet_place_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + void operator()( const bet_cancel_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + void operator()( const bet_canceled_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + void operator()( const bet_adjusted_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + void operator()( const bet_matched_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + void operator()( const betting_market_group_resolved_operation& op ) + { + _impacted.insert( op.bettor_id ); + } + + void operator()( const tournament_create_operation& op ) + { + _impacted.insert( op.creator ); + _impacted.insert( op.options.whitelist.begin(), op.options.whitelist.end() ); + } + void operator()( const tournament_join_operation& op ) + { + _impacted.insert( op.payer_account_id ); + _impacted.insert( op.player_account_id ); + } + void operator()( const tournament_leave_operation& op ) + { + //if account canceling registration is not the player, it must be the payer + if (op.canceling_account_id != op.player_account_id) + _impacted.erase( op.canceling_account_id ); + _impacted.erase( op.player_account_id ); + } + void operator()( const game_move_operation& op ) + { + _impacted.insert( op.player_account_id ); + } + void operator()( const tournament_payout_operation& op ) + { + _impacted.insert( op.payout_account_id ); + } + void operator()( const affiliate_payout_operation& op ) + { + _impacted.insert( op.affiliate ); + } + void operator()( const affiliate_referral_payout_operation& op ) { } + void operator()( const lottery_asset_create_operation& op) { } + void operator()( const ticket_purchase_operation& op ) + { + _impacted.insert( op.buyer ); + } + void operator()( const lottery_reward_operation& op ) { + _impacted.insert( op.winner ); + } + void operator()( const lottery_end_operation& op ) { + for( auto participant : op.participants ) { + _impacted.insert(participant.first); + } + } + void operator()( const sweeps_vesting_claim_operation& op ) { + _impacted.insert( op.account ); + } + void operator()( const son_create_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_update_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_delete_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_heartbeat_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_report_down_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_maintenance_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_wallet_recreate_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_deposit_create_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_deposit_process_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_create_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_process_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const sidechain_address_add_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_update_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const sidechain_address_delete_operation& op ){ + _impacted.insert( op.sidechain_address_account ); + } + void operator()( const bitcoin_transaction_send_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_transaction_sign_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const bitcoin_send_transaction_process_operation& op ){ + _impacted.insert( op.payer ); + } +}; + +void operation_get_impacted_accounts( const operation& op, flat_set& result ) +{ + get_impacted_account_visitor vtor = get_impacted_account_visitor( result ); + op.visit( vtor ); +} + +void transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) +{ + for( const auto& op : tx.operations ) + operation_get_impacted_accounts( op, result ); +} + +} } diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 4adf73a3..a263c4dd 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -31,8 +31,6 @@ #include #include -#include - #include #include #include @@ -97,32 +95,31 @@ namespace graphene { namespace app { class history_api { public: - history_api(application& app) - :_app(app), database_api( std::ref(*app.chain_database())) {} + history_api(application& app):_app(app){} /** * @brief Get operations relevant to the specificed account - * @param account_id_or_name The account ID or name whose history should be queried + * @param account The account whose history should be queried * @param stop ID of the earliest operation to retrieve * @param limit Maximum number of operations to retrieve (must not exceed 100) * @param start ID of the most recent operation to retrieve * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_account_history(const std::string account_id_or_name, + vector get_account_history(account_id_type account, operation_history_id_type stop = operation_history_id_type(), unsigned limit = 100, operation_history_id_type start = operation_history_id_type())const; /** * @brief Get only asked operations relevant to the specified account - * @param account_id_or_name The account ID or name whose history should be queried + * @param account The account whose history should be queried * @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...) * @param stop ID of the earliest operation to retrieve * @param limit Maximum number of operations to retrieve (must not exceed 100) * @param start ID of the most recent operation to retrieve * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_account_history_operations(const std::string account_id_or_name, + vector get_account_history_operations(account_id_type account, int operation_id, operation_history_id_type start = operation_history_id_type(), operation_history_id_type stop = operation_history_id_type(), @@ -132,7 +129,7 @@ namespace graphene { namespace app { * @breif Get operations relevant to the specified account referenced * by an event numbering specific to the account. The current number of operations * for the account can be found in the account statistics (or use 0 for start). - * @param account_id_or_name The account ID or name whose history should be queried + * @param account The account whose history should be queried * @param stop Sequence number of earliest operation. 0 is default and will * query 'limit' number of operations. * @param limit Maximum number of operations to retrieve (must not exceed 100) @@ -140,19 +137,18 @@ namespace graphene { namespace app { * 0 is default, which will start querying from the most recent operation. * @return A list of operations performed by account, ordered from most recent to oldest. */ - vector get_relative_account_history( const std::string account_id_or_name, + vector get_relative_account_history( account_id_type account, uint32_t stop = 0, unsigned limit = 100, uint32_t start = 0) const; - vector get_fill_order_history( std::string asset_a, std::string asset_b, uint32_t limit )const; - vector get_market_history( std::string asset_a, std::string asset_b, uint32_t bucket_seconds, + vector get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const; + vector get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const; vector list_core_accounts()const; flat_set get_market_history_buckets()const; private: application& _app; - graphene::app::database_api database_api; }; /** @@ -329,47 +325,17 @@ namespace graphene { namespace app { class asset_api { public: - asset_api(graphene::app::application& app); + asset_api(graphene::chain::database& db); ~asset_api(); - /** - * @brief Get asset holders for a specific asset - * @param asset The specific asset id or symbol - * @param start The start index - * @param limit Maximum limit must not exceed 100 - * @return A list of asset holders for the specified asset - */ - vector get_asset_holders( std::string asset, uint32_t start, uint32_t limit )const; - - /** - * @brief Get asset holders count for a specific asset - * @param asset The specific asset id or symbol - * @return Holders count for the specified asset - */ - int get_asset_holders_count( std::string asset )const; - - /** - * @brief Get all asset holders - * @return A list of all asset holders - */ + vector get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit )const; + int get_asset_holders_count( asset_id_type asset_id )const; vector get_all_asset_holders() const; private: - graphene::app::application& _app; graphene::chain::database& _db; - graphene::app::database_api database_api; }; -} } // graphene::app -extern template class fc::api; -extern template class fc::api; -extern template class fc::api; -extern template class fc::api; -extern template class fc::api; -extern template class fc::api; -extern template class fc::api; - -namespace graphene { namespace app { /** * @brief The login_api class implements the bottom layer of the RPC API * @@ -431,8 +397,6 @@ namespace graphene { namespace app { }} // graphene::app -extern template class fc::api; - FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation, (id)(block_num)(trx_num)(trx) ) FC_REFLECT( graphene::app::verify_range_result, diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index a436aacd..b0ace3d7 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -56,8 +56,8 @@ namespace graphene { namespace app { auto plug = std::make_shared(); plug->plugin_set_app(this); - boost::program_options::options_description plugin_cli_options(plug->plugin_name() + " plugin. " + plug->plugin_description() + "\nOptions"), plugin_cfg_options; - //boost::program_options::options_description plugin_cli_options("Options for plugin " + plug->plugin_name()), plugin_cfg_options; + string cli_plugin_desc = plug->plugin_name() + " plugin. " + plug->plugin_description() + "\nOptions"; + boost::program_options::options_description plugin_cli_options( cli_plugin_desc ), plugin_cfg_options; plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options); if( !plugin_cli_options.options().empty() ) _cli_options.add(plugin_cli_options); @@ -99,9 +99,7 @@ namespace graphene { namespace app { bool is_plugin_enabled(const string& name) const; - std::shared_ptr elasticsearch_thread; - - private: + private: void add_available_plugin( std::shared_ptr p ); std::shared_ptr my; diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index a89224b4..76ef822c 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -117,16 +117,6 @@ struct market_trade double value; }; -struct gpos_info { - double vesting_factor; - asset award; - share_type total_amount; - uint32_t current_subperiod; - fc::time_point_sec last_voted_time; - share_type allowed_withdraw_amount; - share_type account_vested_balance; -}; - /** * @brief The database_api class implements the RPC API for the chain database. * @@ -254,21 +244,13 @@ class database_api ////////////// /** - * @brief Get account object from a name or ID - * @param name_or_id name or ID of the account - * @return Account ID - * - */ - account_id_type get_account_id_from_string(const std::string& name_or_id)const; - - /** - * @brief Get a list of accounts by ID or Name + * @brief Get a list of accounts by ID * @param account_ids IDs of the accounts to retrieve * @return The accounts corresponding to the provided IDs * * This function has semantics identical to @ref get_objects */ - vector> get_accounts(const vector& account_names_or_ids)const; + vector> get_accounts(const vector& account_ids)const; /** * @brief Fetch all objects relevant to the specified accounts and subscribe to updates @@ -288,7 +270,7 @@ class database_api /** * @return all accounts that referr to the key or account id in their owner or active authorities. */ - vector get_account_references( const std::string account_name_or_id )const; + vector get_account_references( account_id_type account_id )const; /** * @brief Get a list of accounts by name @@ -317,8 +299,7 @@ class database_api * @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in * @return Balances of the account */ - vector get_account_balances( const std::string& account_name_or_id, - const flat_set& assets )const; + vector get_account_balances(account_id_type id, const flat_set& assets)const; /// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID. vector get_named_account_balances(const std::string& name, const flat_set& assets)const; @@ -328,7 +309,7 @@ class database_api vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( const std::string account_id_or_name )const; + vector get_vesting_balances( account_id_type account_id )const; /** * @brief Get the total number of accounts registered with the blockchain @@ -339,21 +320,14 @@ class database_api // Assets // //////////// - /** - * @brief Get asset ID from an asset symbol or ID - * @param symbol_or_id symbol name or ID of the asset - * @return asset ID - */ - asset_id_type get_asset_id_from_string(const std::string& symbol_or_id) const; - /** * @brief Get a list of assets by ID - * @param asset_symbols_or_ids IDs or names of the assets to retrieve + * @param asset_ids IDs of the assets to retrieve * @return The assets corresponding to the provided IDs * * This function has semantics identical to @ref get_objects */ - vector> get_assets(const vector& asset_symbols_or_ids)const; + vector> get_assets(const vector& asset_ids)const; /** * @brief Get assets alphabetically by symbol name @@ -455,47 +429,47 @@ class database_api * @param limit Maximum number of orders to retrieve * @return The limit orders, ordered from least price to greatest */ - vector get_limit_orders(const std::string& a, const std::string& b, uint32_t limit)const; + vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; /** * @brief Get call orders in a given asset - * @param a ID or name of asset being called + * @param a ID of asset being called * @param limit Maximum number of orders to retrieve * @return The call orders, ordered from earliest to be called to latest */ - vector get_call_orders(const std::string& a, uint32_t limit)const; + vector get_call_orders(asset_id_type a, uint32_t limit)const; /** * @brief Get forced settlement orders in a given asset - * @param a ID or name of asset being settled + * @param a ID of asset being settled * @param limit Maximum number of orders to retrieve * @return The settle orders, ordered from earliest settlement date to latest */ - vector get_settle_orders(const std::string& a, uint32_t limit)const; + vector get_settle_orders(asset_id_type a, uint32_t limit)const; /** * @return all open margin positions for a given account id. */ - vector get_margin_positions( const std::string account_id_or_name )const; + vector get_margin_positions( const account_id_type& id )const; /** * @brief Request notification when the active orders in the market between two assets changes * @param callback Callback method which is called when the market changes - * @param a First asset ID or name - * @param b Second asset ID or name + * @param a First asset ID + * @param b Second asset ID * * Callback will be passed a variant containing a vector>. The vector will * contain, in order, the operations which changed the market, and their results. */ void subscribe_to_market(std::function callback, - const std::string& a, const std::string& b); + asset_id_type a, asset_id_type b); /** * @brief Unsubscribe from updates to a given market - * @param a First asset ID or name - * @param b Second asset ID or name + * @param a First asset ID + * @param b Second asset ID */ - void unsubscribe_from_market( const std::string& a, const std::string& b ); + void unsubscribe_from_market( asset_id_type a, asset_id_type b ); /** * @brief Returns the ticker for the market assetA:assetB @@ -554,7 +528,7 @@ class database_api * @param account The ID of the account whose witness should be retrieved * @return The witness object, or null if the account does not have a witness */ - fc::optional get_witness_by_account(const std::string account_name_or_id)const; + fc::optional get_witness_by_account(account_id_type account)const; /** * @brief Get names and IDs for registered witnesses @@ -584,10 +558,10 @@ class database_api /** * @brief Get the committee_member owned by a given account - * @param account_id_or_name The ID or name of the account whose committee_member should be retrieved + * @param account The ID of the account whose committee_member should be retrieved * @return The committee_member object, or null if the account does not have a committee_member */ - fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; + fc::optional get_committee_member_by_account(account_id_type account)const; /** * @brief Get names and IDs for registered committee_members @@ -697,11 +671,9 @@ class database_api /// WORKERS /** - * @brief Return the worker objects associated with this account. - * @param account_id_or_name The ID or name of the account whose worker should be retrieved - * @return The worker object or null if the account does not have a worker + * Return the worker objects associated with this account. */ - vector get_workers_by_account(const std::string account_id_or_name)const; + vector get_workers_by_account(account_id_type account)const; /////////// @@ -758,7 +730,7 @@ class database_api * For each operation calculate the required fee in the specified asset type. If the asset type does * not have a valid core_exchange_rate */ - vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; + vector< fc::variant > get_required_fees( const vector& ops, asset_id_type id )const; /////////////////////////// // Proposed transactions // @@ -767,7 +739,7 @@ class database_api /** * @return the set of proposed transactions relevant to the specified account id. */ - vector get_proposed_transactions( const std::string account_id_or_name )const; + vector get_proposed_transactions( account_id_type id )const; ////////////////////// // Blinded balances // @@ -800,31 +772,17 @@ class database_api */ vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; - ////////// - // GPOS // - ////////// - /** - * @return account and network GPOS information - */ - gpos_info get_gpos_info(const account_id_type account) const; - - - -private: + private: std::shared_ptr< database_api_impl > my; }; } } -extern template class fc::api; - FC_REFLECT( graphene::app::order, (price)(quote)(base) ); FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) ); -FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount)(current_subperiod)(last_voted_time)(allowed_withdraw_amount)(account_vested_balance) ); - FC_API(graphene::app::database_api, // Objects @@ -855,7 +813,6 @@ FC_API(graphene::app::database_api, (is_public_key_registered) // Accounts - (get_account_id_from_string) (get_accounts) (get_full_accounts) (get_account_by_name) @@ -876,7 +833,6 @@ FC_API(graphene::app::database_api, (list_assets) (lookup_asset_symbols) (get_asset_count) - (get_asset_id_from_string) // Peerplays (list_sports) @@ -962,7 +918,4 @@ FC_API(graphene::app::database_api, (get_tournaments_by_state) (get_tournaments ) (get_registered_tournaments) - - // gpos - (get_gpos_info) ) diff --git a/libraries/chain/include/graphene/chain/impacted.hpp b/libraries/app/include/graphene/app/impacted.hpp similarity index 96% rename from libraries/chain/include/graphene/chain/impacted.hpp rename to libraries/app/include/graphene/app/impacted.hpp index 2a22cbd1..2e59b910 100644 --- a/libraries/chain/include/graphene/chain/impacted.hpp +++ b/libraries/app/include/graphene/app/impacted.hpp @@ -28,7 +28,7 @@ #include #include -namespace graphene { namespace chain { +namespace graphene { namespace app { void operation_get_impacted_accounts( const graphene::chain::operation& op, @@ -39,4 +39,4 @@ void transaction_get_impacted_accounts( fc::flat_set& result ); -} } // graphene::app \ No newline at end of file +} } // graphene::app diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 8fba8a43..9c068ba5 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -61,7 +61,6 @@ add_library( graphene_chain protocol/confidential.cpp protocol/vote.cpp protocol/tournament.cpp - protocol/small_ops.cpp genesis_state.cpp get_config.cpp @@ -95,7 +94,6 @@ add_library( graphene_chain fba_object.cpp proposal_object.cpp vesting_balance_object.cpp - small_objects.cpp block_database.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index ad6ac5dc..2d117f52 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -162,39 +162,33 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio if( referrer_percent > GRAPHENE_100_PERCENT ) referrer_percent = GRAPHENE_100_PERCENT; } - const auto& global_properties = d.get_global_properties(); - const auto& new_acnt_object = d.create( [&o,&d,&global_properties,referrer_percent]( account_object& obj ) - { - obj.registrar = o.registrar; - obj.referrer = o.referrer; - obj.lifetime_referrer = o.referrer(d).lifetime_referrer; + const auto& new_acnt_object = db().create( [&]( account_object& obj ){ + obj.registrar = o.registrar; + obj.referrer = o.referrer; + obj.lifetime_referrer = o.referrer(db()).lifetime_referrer; - const auto& params = global_properties.parameters; - obj.network_fee_percentage = params.network_percent_of_fee; - obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee; - obj.referrer_rewards_percentage = referrer_percent; + auto& params = db().get_global_properties().parameters; + obj.network_fee_percentage = params.network_percent_of_fee; + obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee; + obj.referrer_rewards_percentage = referrer_percent; - obj.name = o.name; - obj.owner = o.owner; - obj.active = o.active; - obj.options = o.options; - obj.statistics = d.create([&obj](account_statistics_object& s){ - s.owner = obj.id; - s.name = obj.name; - s.is_voting = obj.options.is_voting(); - }).id; + obj.name = o.name; + obj.owner = o.owner; + obj.active = o.active; + obj.options = o.options; + obj.statistics = db().create([&](account_statistics_object& s){s.owner = obj.id;}).id; - if( o.extensions.value.owner_special_authority.valid() ) - obj.owner_special_authority = *(o.extensions.value.owner_special_authority); - if( o.extensions.value.active_special_authority.valid() ) - obj.active_special_authority = *(o.extensions.value.active_special_authority); - if( o.extensions.value.buyback_options.valid() ) - { - obj.allowed_assets = o.extensions.value.buyback_options->markets; - obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy ); - } - obj.affiliate_distributions = o.extensions.value.affiliate_distributions; + if( o.extensions.value.owner_special_authority.valid() ) + obj.owner_special_authority = *(o.extensions.value.owner_special_authority); + if( o.extensions.value.active_special_authority.valid() ) + obj.active_special_authority = *(o.extensions.value.active_special_authority); + if( o.extensions.value.buyback_options.valid() ) + { + obj.allowed_assets = o.extensions.value.buyback_options->markets; + obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy ); + } + obj.affiliate_distributions = o.extensions.value.affiliate_distributions; }); if( has_small_percent ) @@ -206,18 +200,17 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio wlog( "Affected account object is ${o}", ("o", new_acnt_object) ); } - const auto& dynamic_properties = d.get_dynamic_global_properties(); - d.modify(dynamic_properties, [](dynamic_global_property_object& p) { + const auto& dynamic_properties = db().get_dynamic_global_properties(); + db().modify(dynamic_properties, [](dynamic_global_property_object& p) { ++p.accounts_registered_this_interval; }); - if( dynamic_properties.accounts_registered_this_interval % global_properties.parameters.accounts_per_fee_scale == 0 - && global_properties.parameters.account_fee_scale_bitshifts != 0 ) - { - d.modify(global_properties, [&dynamic_properties](global_property_object& p) { + const auto& global_properties = db().get_global_properties(); + if( dynamic_properties.accounts_registered_this_interval % + global_properties.parameters.accounts_per_fee_scale == 0 ) + db().modify(global_properties, [&dynamic_properties](global_property_object& p) { p.parameters.current_fees->get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); - } if( o.extensions.value.owner_special_authority.valid() || o.extensions.value.active_special_authority.valid() ) @@ -287,26 +280,18 @@ void_result account_update_evaluator::do_apply( const account_update_operation& { try { database& d = db(); - bool sa_before = acnt->has_special_authority(); - - // update account statistics if( o.new_options.valid() ) { d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso ) { - fc::optional< bool > flag = o.extensions.value.update_last_voting_time; if((o.new_options->votes != acnt->options.votes || - o.new_options->voting_account != acnt->options.voting_account) || - (flag.valid() && *flag)) + o.new_options->voting_account != acnt->options.voting_account)) aso.last_vote_time = d.head_block_time(); - - if(o.new_options->is_voting() != acnt->options.is_voting()) - aso.is_voting = !aso.is_voting; } ); } - // update account object - d.modify( *acnt, [&o](account_object& a){ + bool sa_before, sa_after; + d.modify( *acnt, [&](account_object& a){ if( o.owner ) { a.owner = *o.owner; @@ -318,6 +303,7 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.top_n_control_flags = 0; } if( o.new_options ) a.options = *o.new_options; + sa_before = a.has_special_authority(); if( o.extensions.value.owner_special_authority.valid() ) { a.owner_special_authority = *(o.extensions.value.owner_special_authority); @@ -328,10 +314,9 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.active_special_authority = *(o.extensions.value.active_special_authority); a.top_n_control_flags = 0; } + sa_after = a.has_special_authority(); }); - bool sa_after = acnt->has_special_authority(); - if( sa_before & (!sa_after) ) { const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get(); diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 71ee28de..e51e1705 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -22,9 +22,9 @@ * THE SOFTWARE. */ #include +#include #include - -#include +#include #include namespace graphene { namespace chain { @@ -46,8 +46,6 @@ void account_balance_object::adjust_balance(const asset& delta) { assert(delta.asset_id == asset_type); balance += delta.amount; - if( asset_type == asset_id_type() ) // CORE asset - maintenance_flag = true; } void account_statistics_object::process_fees(const account_object& a, database& d) const @@ -59,8 +57,8 @@ void account_statistics_object::process_fees(const account_object& a, database& // Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead. // No need to check the registrar; registrars are required to be lifetime members. if( account.referrer(d).is_basic_account(d.head_block_time()) ) - d.modify( account, [](account_object& acc) { - acc.referrer = acc.lifetime_referrer; + d.modify(account, [](account_object& a) { + a.referrer = a.lifetime_referrer; }); share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); @@ -76,8 +74,8 @@ void account_statistics_object::process_fees(const account_object& a, database& share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage); share_type referral = core_fee_total - network_cut - lifetime_cut; - d.modify( d.get_core_dynamic_data(), [network_cut](asset_dynamic_data_object& addo) { - addo.accumulated_fees += network_cut; + d.modify(asset_dynamic_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) { + d.accumulated_fees += network_cut; }); // Potential optimization: Skip some of this math and object lookups by special casing on the account type. @@ -320,8 +318,3 @@ const account_balance_object* balances_by_account_index::get_account_balance( co } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_statistics_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::pending_dividend_payout_balance_for_holder_object ) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 7a26a2cb..59b590dd 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -133,36 +133,33 @@ void asset_create_evaluator::pay_fee() object_id_type asset_create_evaluator::do_apply( const asset_create_operation& op ) { try { - database& d = db(); - // includes changes from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) bool hf_429 = fee_is_odd && db().head_block_time() > HARDFORK_CORE_429_TIME; const asset_dynamic_data_object& dyn_asset = - d.create( [hf_429,this]( asset_dynamic_data_object& a ) { + db().create( [&]( asset_dynamic_data_object& a ) { a.current_supply = 0; a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0); }); - if( fee_is_odd && !hf_429 ) - { - const auto& core_dd = d.get_core_asset().dynamic_data( d ); - d.modify( core_dd, []( asset_dynamic_data_object& dd ) { + if( fee_is_odd && !hf_429 ) + { + const auto& core_dd = db().get( asset_id_type() ).dynamic_data( db() ); + db().modify( core_dd, [=]( asset_dynamic_data_object& dd ) { dd.current_supply++; - }); - } - - auto next_asset_id = d.get_index_type().get_next_id(); + }); + } asset_bitasset_data_id_type bit_asset_id; if( op.bitasset_opts.valid() ) - bit_asset_id = d.create( [&]( asset_bitasset_data_object& a ) { + bit_asset_id = db().create( [&]( asset_bitasset_data_object& a ) { a.options = *op.bitasset_opts; a.is_prediction_market = op.is_prediction_market; - a.asset_id = next_asset_id; }).id; + auto next_asset_id = db().get_index_type().get_next_id(); + const asset_object& new_asset = - d.create( [&]( asset_object& a ) { + db().create( [&]( asset_object& a ) { a.issuer = op.issuer; a.symbol = op.symbol; a.precision = op.precision; @@ -178,7 +175,7 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o if( op.bitasset_opts.valid() ) a.bitasset_data_id = bit_asset_id; }); - FC_ASSERT( new_asset.id == next_asset_id ); + assert( new_asset.id == next_asset_id ); return new_asset.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -284,36 +281,33 @@ void lottery_asset_create_evaluator::pay_fee() object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_create_operation& op ) { try { - database& d = db(); - // includes changes from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) - bool hf_429 = fee_is_odd && d.head_block_time() > HARDFORK_CORE_429_TIME; + bool hf_429 = fee_is_odd && db().head_block_time() > HARDFORK_CORE_429_TIME; const asset_dynamic_data_object& dyn_asset = - d.create( [&]( asset_dynamic_data_object& a ) { + db().create( [&]( asset_dynamic_data_object& a ) { a.current_supply = 0; a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0); }); if( fee_is_odd && !hf_429 ) { - const auto& core_dd = d.get( asset_id_type() ).dynamic_data( db() ); - d.modify( core_dd, [=]( asset_dynamic_data_object& dd ) { + const auto& core_dd = db().get( asset_id_type() ).dynamic_data( db() ); + db().modify( core_dd, [=]( asset_dynamic_data_object& dd ) { dd.current_supply++; }); } - auto next_asset_id = d.get_index_type().get_next_id(); - asset_bitasset_data_id_type bit_asset_id; if( op.bitasset_opts.valid() ) - bit_asset_id = d.create( [&op,next_asset_id]( asset_bitasset_data_object& a ) { + bit_asset_id = db().create( [&]( asset_bitasset_data_object& a ) { a.options = *op.bitasset_opts; a.is_prediction_market = op.is_prediction_market; - a.asset_id = next_asset_id; }).id; + auto next_asset_id = db().get_index_type().get_next_id(); + const asset_object& new_asset = - d.create( [&op,next_asset_id,&dyn_asset,bit_asset_id,&d]( asset_object& a ) { + db().create( [&]( asset_object& a ) { a.issuer = op.issuer; a.symbol = op.symbol; a.precision = op.precision; @@ -322,7 +316,7 @@ object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_cre a.lottery_options = op.extensions; //a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id ); a.lottery_options->owner = a.id; - d.create([&a](lottery_balance_object& lbo) { + db().create([&](lottery_balance_object& lbo) { lbo.lottery_id = a.id; }); if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 ) @@ -333,7 +327,7 @@ object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_cre if( op.bitasset_opts.valid() ) a.bitasset_data_id = bit_asset_id; }); - FC_ASSERT( new_asset.id == next_asset_id, "Unexpected object database error, object id mismatch" ); + assert( new_asset.id == next_asset_id ); return new_asset.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -360,7 +354,7 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o ) { try { db().adjust_balance( o.issue_to_account, o.asset_to_issue ); - db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){ + db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){ data.current_supply += o.asset_to_issue.amount; }); @@ -392,7 +386,7 @@ void_result asset_reserve_evaluator::do_apply( const asset_reserve_operation& o { try { db().adjust_balance( o.payer, -o.amount_to_reserve ); - db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){ + db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){ data.current_supply -= o.amount_to_reserve.amount; }); @@ -414,7 +408,7 @@ void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_op { try { db().adjust_balance(o.from_account, -o.amount); - db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ) { + db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ) { data.fee_pool += o.amount; }); @@ -489,21 +483,7 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o) d.cancel_order(*itr); } - // For market-issued assets, if core change rate changed, update flag in bitasset data - if( asset_to_update->is_market_issued() - && asset_to_update->options.core_exchange_rate != o.new_options.core_exchange_rate ) - { - const auto& bitasset = asset_to_update->bitasset_data(d); - if( !bitasset.asset_cer_updated ) - { - d.modify( bitasset, [](asset_bitasset_data_object& b) - { - b.asset_cer_updated = true; - }); - } - } - - d.modify(*asset_to_update, [&o](asset_object& a) { + d.modify(*asset_to_update, [&](asset_object& a) { if( o.new_issuer ) a.issuer = *o.new_issuer; a.options = o.new_options; diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 88e5dfca..63df70a3 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -24,9 +24,10 @@ #include #include -#include #include +#include + using namespace graphene::chain; share_type asset_bitasset_data_object::max_force_settlement_volume(share_type current_supply) const @@ -60,15 +61,12 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time if( current_feeds.size() < options.minimum_feeds ) { //... don't calculate a median, and set a null feed - feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance current_feed_publication_time = current_time; current_feed = price_feed(); return; } if( current_feeds.size() == 1 ) { - if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate ) - feed_cer_updated = true; current_feed = std::move(current_feeds.front()); return; } @@ -87,8 +85,6 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time #undef CALCULATE_MEDIAN_VALUE // *** End Median Calculations *** - if( current_feed.core_exchange_rate != median_feed.core_exchange_rate ) - feed_cer_updated = true; current_feed = median_feed; } @@ -295,11 +291,3 @@ void sweeps_vesting_balance_object::adjust_balance( const asset& delta ) FC_ASSERT( delta.asset_id == asset_id ); balance += delta.amount.value; } - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dynamic_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_bitasset_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dividend_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::total_distributed_dividend_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::lottery_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::sweeps_vesting_balance_object ) diff --git a/libraries/chain/balance_evaluator.cpp b/libraries/chain/balance_evaluator.cpp index 817d736f..8d29c01d 100644 --- a/libraries/chain/balance_evaluator.cpp +++ b/libraries/chain/balance_evaluator.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include namespace graphene { namespace chain { diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 73d7703b..d3756698 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -77,7 +77,15 @@ void_result committee_member_update_evaluator::do_apply( const committee_member_ void_result committee_member_update_global_parameters_evaluator::do_evaluate(const committee_member_update_global_parameters_operation& o) { try { FC_ASSERT(trx_state->_is_proposed_trx); - + + if( db().head_block_time() < HARDFORK_1000_TIME ) // TODO: remove after hf + FC_ASSERT( !o.new_parameters.extensions.value.min_bet_multiplier.valid() + && !o.new_parameters.extensions.value.max_bet_multiplier.valid() + && !o.new_parameters.extensions.value.betting_rake_fee_percentage.valid() + && !o.new_parameters.extensions.value.permitted_betting_odds_increments.valid() + && !o.new_parameters.extensions.value.live_betting_delay_time.valid(), + "Parameter extensions are not allowed yet!" ); + dgpo = &db().get_global_properties(); if( o.new_parameters.extensions.value.min_bet_multiplier.valid() && !o.new_parameters.extensions.value.max_bet_multiplier.valid() ) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 55729050..7a46df17 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -77,8 +77,6 @@ void database::adjust_balance(account_id_type account, asset delta ) b.owner = account; b.asset_type = delta.asset_id; b.balance = delta.amount.value; - if( b.asset_type == asset_id_type() ) // CORE asset - b.maintenance_flag = true; }); } else { if( delta.amount < 0 ) @@ -210,7 +208,7 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b acct.get_id() == GRAPHENE_TEMP_ACCOUNT ) { // The blockchain's accounts do not get cashback; it simply goes to the reserve pool. - modify( get_core_dynamic_data(), [amount](asset_dynamic_data_object& d) { + modify(get(asset_id_type()).dynamic_asset_data_id(*this), [amount](asset_dynamic_data_object& d) { d.current_supply -= amount; }); return; @@ -225,15 +223,10 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b if( new_vbid.valid() ) { - modify( acct, [&new_vbid]( account_object& _acct ) + modify( acct, [&]( account_object& _acct ) { _acct.cashback_vb = *new_vbid; } ); - - modify( acct.statistics( *this ), []( account_statistics_object& aso ) - { - aso.has_cashback_vb = true; - } ); } return; diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index eb843b8b..dfa6c4d1 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -198,90 +197,82 @@ bool database::push_block(const signed_block& new_block, uint32_t skip) bool database::_push_block(const signed_block& new_block) { try { uint32_t skip = get_node_properties().skip_flags; - const auto now = fc::time_point::now().sec_since_epoch(); - - if( _fork_db.head() && new_block.timestamp.sec_since_epoch() > now - 86400 ) + if( !(skip&skip_fork_db) ) { + /// TODO: if the block is greater than the head block and before the next maitenance interval // verify that the block signer is in the current set of active witnesses. - shared_ptr prev_block = _fork_db.fetch_block( new_block.previous ); - GRAPHENE_ASSERT( prev_block, unlinkable_block_exception, "block does not link to known chain" ); - if( prev_block->scheduled_witnesses && !(skip&(skip_witness_schedule_check|skip_witness_signature)) ) - verify_signing_witness( new_block, *prev_block ); - } - shared_ptr new_head = _fork_db.push_block(new_block); - //If the head block from the longest chain does not build off of the current head, we need to switch forks. - if( new_head->data.previous != head_block_id() ) - { - //If the newly pushed block is the same height as head, we get head back in new_head - //Only switch forks if new_head is actually higher than head - if( new_head->data.block_num() > head_block_num() ) + shared_ptr new_head = _fork_db.push_block(new_block); + //If the head block from the longest chain does not build off of the current head, we need to switch forks. + if( new_head->data.previous != head_block_id() ) { - wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); - auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); - - // pop blocks until we hit the forked block - while( head_block_id() != branches.second.back()->data.previous ) + //If the newly pushed block is the same height as head, we get head back in new_head + //Only switch forks if new_head is actually higher than head + if( new_head->data.block_num() > head_block_num() ) { - ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); - pop_block(); + wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); + auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); + + // pop blocks until we hit the forked block + while( head_block_id() != branches.second.back()->data.previous ) + { + ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); + pop_block(); + } + + // push all blocks on the new fork + for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) + { + ilog( "pushing block from fork #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); + optional except; + try { + undo_database::session session = _undo_db.start_undo_session(); + apply_block( (*ritr)->data, skip ); + _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); + session.commit(); + } + catch ( const fc::exception& e ) { except = e; } + if( except ) + { + wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); + // remove the rest of branches.first from the fork_db, those blocks are invalid + while( ritr != branches.first.rend() ) + { + ilog( "removing block from fork_db #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); + _fork_db.remove( (*ritr)->id ); + ++ritr; + } + _fork_db.set_head( branches.second.front() ); + + // pop all blocks from the bad fork + while( head_block_id() != branches.second.back()->data.previous ) + { + ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); + pop_block(); + } + + ilog( "Switching back to fork: ${id}", ("id",branches.second.front()->data.id()) ); + // restore all blocks from the good fork + for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 ) + { + ilog( "pushing block #${n} ${id}", ("n",(*ritr2)->data.block_num())("id",(*ritr2)->id) ); + auto session = _undo_db.start_undo_session(); + apply_block( (*ritr2)->data, skip ); + _block_id_to_block.store( (*ritr2)->id, (*ritr2)->data ); + session.commit(); + } + throw *except; + } + } + return true; } - - // push all blocks on the new fork - for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) - { - ilog( "pushing block from fork #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); - optional except; - try { - undo_database::session session = _undo_db.start_undo_session(); - apply_block( (*ritr)->data, skip ); - update_witnesses( **ritr ); - _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); - session.commit(); - } - catch ( const fc::exception& e ) { except = e; } - if( except ) - { - wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); - // remove the rest of branches.first from the fork_db, those blocks are invalid - while( ritr != branches.first.rend() ) - { - ilog( "removing block from fork_db #${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->id) ); - _fork_db.remove( (*ritr)->id ); - ++ritr; - } - _fork_db.set_head( branches.second.front() ); - - // pop all blocks from the bad fork - while( head_block_id() != branches.second.back()->data.previous ) - { - ilog( "popping block #${n} ${id}", ("n",head_block_num())("id",head_block_id()) ); - pop_block(); - } - - ilog( "Switching back to fork: ${id}", ("id",branches.second.front()->data.id()) ); - // restore all blocks from the good fork - for( auto ritr2 = branches.second.rbegin(); ritr2 != branches.second.rend(); ++ritr2 ) - { - ilog( "pushing block #${n} ${id}", ("n",(*ritr2)->data.block_num())("id",(*ritr2)->id) ); - auto session = _undo_db.start_undo_session(); - apply_block( (*ritr2)->data, skip ); - _block_id_to_block.store( (*ritr2)->id, (*ritr2)->data ); - session.commit(); - } - throw *except; - } - } - return true; + else return false; } - else return false; } try { auto session = _undo_db.start_undo_session(); apply_block(new_block, skip); - if( new_block.timestamp.sec_since_epoch() > now - 86400 ) - update_witnesses( *new_head ); _block_id_to_block.store(new_block.id(), new_block); session.commit(); } catch ( const fc::exception& e ) { @@ -293,73 +284,6 @@ bool database::_push_block(const signed_block& new_block) return false; } FC_CAPTURE_AND_RETHROW( (new_block) ) } -void database::verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const -{ - FC_ASSERT( new_block.timestamp >= fork_entry.next_block_time ); - uint32_t slot_num = ( new_block.timestamp - fork_entry.next_block_time ).to_seconds() / block_interval(); - const global_property_object& gpo = get_global_properties(); - - if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) - { - uint64_t index = ( fork_entry.next_block_aslot + slot_num ) % fork_entry.scheduled_witnesses->size(); - const auto& scheduled_witness = (*fork_entry.scheduled_witnesses)[index]; - FC_ASSERT( new_block.witness == scheduled_witness.first, "Witness produced block at wrong time", - ("block witness",new_block.witness)("scheduled",scheduled_witness)("slot_num",slot_num) ); - FC_ASSERT( new_block.validate_signee( scheduled_witness.second ) ); - } - if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && - slot_num != 0 ) - { - witness_id_type wid; - const witness_schedule_object& wso = get_witness_schedule_object(); - // ask the near scheduler who goes in the given slot - bool slot_is_near = wso.scheduler.get_slot(slot_num, wid); - if(! slot_is_near) - { - // if the near scheduler doesn't know, we have to extend it to - // a far scheduler. - // n.b. instantiating it is slow, but block gaps long enough to - // need it are likely pretty rare. - - witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV); - - far_future_witness_scheduler far_scheduler = - far_future_witness_scheduler(wso.scheduler, far_rng); - if(!far_scheduler.get_slot(slot_num, wid)) - { - // no scheduled witness -- somebody set up us the bomb - // n.b. this code path is impossible, the present - // implementation of far_future_witness_scheduler - // returns true unconditionally - assert( false ); - } - } - - FC_ASSERT( new_block.witness == wid, "Witness produced block at wrong time", - ("block witness",new_block.witness)("scheduled",wid)("slot_num",slot_num) ); - FC_ASSERT( new_block.validate_signee( wid(*this).signing_key ) ); - } -} - -void database::update_witnesses( fork_item& fork_entry )const -{ - if( fork_entry.scheduled_witnesses ) return; - - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - fork_entry.next_block_aslot = dpo.current_aslot + 1; - fork_entry.next_block_time = get_slot_time( 1 ); - - const witness_schedule_object& wso = get_witness_schedule_object(); - fork_entry.scheduled_witnesses = std::make_shared< vector< pair< witness_id_type, public_key_type > > >(); - fork_entry.scheduled_witnesses->reserve( wso.current_shuffled_witnesses.size() ); - - for( size_t i = 0; i < wso.current_shuffled_witnesses.size(); ++i ) - { - const auto& witness = wso.current_shuffled_witnesses[i](*this); - fork_entry.scheduled_witnesses->emplace_back( wso.current_shuffled_witnesses[i], witness.signing_key ); - } -} - /** * Attempts to push the transaction into the pending queue * @@ -400,7 +324,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx temp_session.merge(); // notify anyone listening to pending transactions - notify_on_pending_transaction( trx ); + on_pending_transaction( trx ); return processed_trx; } @@ -669,7 +593,7 @@ void database::_apply_block( const signed_block& next_block ) const witness_object& signing_witness = validate_block_header(skip, next_block); const auto& global_props = get_global_properties(); - const auto& dynamic_global_props = get_dynamic_global_properties(); + const auto& dynamic_global_props = get(dynamic_global_property_id_type()); bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp); _current_block_num = next_block_num; @@ -677,8 +601,6 @@ void database::_apply_block( const signed_block& next_block ) _current_op_in_trx = 0; _current_virtual_op = 0; - _issue_453_affected_assets.clear(); - for( const auto& trx : next_block.transactions ) { /* We do not need to push the undo state for each transaction @@ -722,8 +644,7 @@ void database::_apply_block( const signed_block& next_block ) clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); - 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_expired_feeds(); update_withdraw_permissions(); update_tournaments(); update_betting_markets(next_block.timestamp); @@ -745,7 +666,7 @@ void database::_apply_block( const signed_block& next_block ) apply_debug_updates(); // notify observers that the block has been applied - notify_applied_block( next_block ); //emit + applied_block( next_block ); //emit _applied_ops.clear(); notify_changed_objects(); diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp index 27beb3ed..0fa5eb58 100644 --- a/libraries/chain/db_debug.cpp +++ b/libraries/chain/db_debug.cpp @@ -42,7 +42,7 @@ void database::debug_dump() const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); const auto& balance_index = db.get_index_type().indices(); - const auto& statistics_index = db.get_index_type().indices(); + const simple_index& statistics_index = db.get_index_type>(); map total_balances; map total_debts; share_type core_in_orders; diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index edc2a199..dfd59567 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -38,27 +38,22 @@ namespace graphene { namespace chain { const asset_object& database::get_core_asset() const { - return *_p_core_asset_obj; -} - -const asset_dynamic_data_object& database::get_core_dynamic_data() const -{ - return *_p_core_dynamic_data_obj; + return get(asset_id_type()); } const global_property_object& database::get_global_properties()const { - return *_p_global_prop_obj; + return get( global_property_id_type() ); } const chain_property_object& database::get_chain_properties()const { - return *_p_chain_property_obj; + return get( chain_property_id_type() ); } const dynamic_global_property_object& database::get_dynamic_global_properties() const { - return *_p_dyn_global_prop_obj; + return get( dynamic_global_property_id_type() ); } const fee_schedule& database::current_fee_schedule()const @@ -68,17 +63,17 @@ const fee_schedule& database::current_fee_schedule()const time_point_sec database::head_block_time()const { - return get_dynamic_global_properties().time; + return get( dynamic_global_property_id_type() ).time; } uint32_t database::head_block_num()const { - return get_dynamic_global_properties().head_block_number; + return get( dynamic_global_property_id_type() ).head_block_number; } block_id_type database::head_block_id()const { - return get_dynamic_global_properties().head_block_id; + return get( dynamic_global_property_id_type() ).head_block_id; } decltype( chain_parameters::block_interval ) database::block_interval( )const @@ -237,17 +232,4 @@ bool database::is_son_dereg_valid( son_id_type son_id ) return ret; } -const account_statistics_object& database::get_account_stats_by_owner( account_id_type owner )const -{ - auto& idx = get_index_type().indices().get(); - auto itr = idx.find( owner ); - FC_ASSERT( itr != idx.end(), "Can not find account statistics object for owner ${a}", ("a",owner) ); - return *itr; -} - -const witness_schedule_object& database::get_witness_schedule_object()const -{ - return *_p_witness_schedule_obj; -} - } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index a6527809..833e03e4 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -333,7 +333,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index> >(); add_index< primary_index> >(); - add_index< primary_index >(); + add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index > >(); @@ -395,19 +395,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) n.owner.weight_threshold = 1; n.active.weight_threshold = 1; n.name = "committee-account"; - n.statistics = create( [&n](account_statistics_object& s){ - s.owner = n.id; - s.name = n.name; - s.core_in_balance = GRAPHENE_MAX_SHARE_SUPPLY; - }).id; + n.statistics = create( [&](account_statistics_object& s){ s.owner = n.id; }).id; }); FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "witness-account"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT; @@ -417,10 +410,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_WITNESS_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "relaxed-committee-account"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; @@ -430,10 +420,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "null-account"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -443,10 +430,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_NULL_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "temp-account"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 0; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT; @@ -456,10 +440,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_TEMP_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "proxy-to-self"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; @@ -469,10 +450,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT); FC_ASSERT(create([this](account_object& a) { a.name = "default-dividend-distribution"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_PROXY_TO_SELF_ACCOUNT; @@ -482,10 +460,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID); FC_ASSERT(create([this](account_object& a) { a.name = "son-account"; - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT; @@ -499,12 +474,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) uint64_t id = get_index().get_next_id().instance(); if( id >= genesis_state.immutable_parameters.num_special_accounts ) break; - const account_object& acct = create([this,id](account_object& a) { + const account_object& acct = create([&](account_object& a) { a.name = "special-account-" + std::to_string(id); - a.statistics = create([&a](account_statistics_object& s){ - s.owner = a.id; - s.name = a.name; - }).id; + a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id); @@ -518,12 +490,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Create core asset const asset_dynamic_data_object& dyn_asset = - create([](asset_dynamic_data_object& a) { + create([&](asset_dynamic_data_object& a) { a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY; }); const asset_dividend_data_object& div_asset = - create([&genesis_state](asset_dividend_data_object& a) { + create([&](asset_dividend_data_object& a) { a.options.minimum_distribution_interval = 3*24*60*60; a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT; a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1); @@ -532,7 +504,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); const asset_object& core_asset = - create( [&genesis_state,&div_asset,&dyn_asset]( asset_object& a ) { + create( [&]( asset_object& a ) { a.symbol = GRAPHENE_SYMBOL; a.options.max_supply = genesis_state.max_core_supply; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -545,12 +517,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; a.dividend_data_id = div_asset.id; - }); - FC_ASSERT( dyn_asset.id == asset_dynamic_data_id_type() ); - FC_ASSERT( asset_id_type(core_asset.id) == asset().asset_id ); - FC_ASSERT( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); - _p_core_asset_obj = &core_asset; - _p_core_dynamic_data_obj = &dyn_asset; + }); + assert( asset_id_type(core_asset.id) == asset().asset_id ); + assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); #ifdef _DEFAULT_DIVIDEND_ASSET // Create default dividend asset @@ -583,7 +552,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.dynamic_asset_data_id = dyn_asset1.id; a.dividend_data_id = div_asset1.id; }); - FC_ASSERT( default_asset.id == asset_id_type(1) ); + assert( default_asset.id == asset_id_type(1) ); #endif // Create more special assets @@ -593,10 +562,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) if( id >= genesis_state.immutable_parameters.num_special_assets ) break; const asset_dynamic_data_object& dyn_asset = - create([](asset_dynamic_data_object& a) { + create([&](asset_dynamic_data_object& a) { a.current_supply = 0; }); - const asset_object& asset_obj = create( [id,&dyn_asset]( asset_object& a ) { + const asset_object& asset_obj = create( [&]( asset_object& a ) { a.symbol = "SPECIAL" + std::to_string( id ); a.options.max_supply = 0; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; @@ -616,14 +585,14 @@ void database::init_genesis(const genesis_state_type& genesis_state) chain_id_type chain_id = genesis_state.compute_chain_id(); // Create global properties - _p_global_prop_obj = & create([&genesis_state](global_property_object& p) { + create([&](global_property_object& p) { p.parameters = genesis_state.initial_parameters; // Set fees to zero initially, so that genesis initialization needs not pay them // We'll fix it at the end of the function p.parameters.current_fees->zero_all_fees(); }); - _p_dyn_global_prop_obj = & create([&genesis_state](dynamic_global_property_object& p) { + create([&](dynamic_global_property_object& p) { p.time = genesis_state.initial_timestamp; p.dynamic_flags = 0; p.witness_budget = 0; @@ -636,7 +605,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" ); FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" ); - _p_chain_property_obj = & create([chain_id,&genesis_state](chain_property_object& p) + create([&](chain_property_object& p) { p.chain_id = chain_id; p.immutable_parameters = genesis_state.immutable_parameters; @@ -760,7 +729,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) cop.active = cop.owner; account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get(); - modify( owner_account_id(*this).statistics(*this), [&collateral_rec]( account_statistics_object& o ) { + modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) { o.total_core_in_orders = collateral_rec.collateral; }); @@ -976,7 +945,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); // Set active witnesses - modify(get_global_properties(), [&genesis_state](global_property_object& p) { + modify(get_global_properties(), [&](global_property_object& p) { for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { p.active_witnesses.insert(witness_id_type(i)); @@ -984,7 +953,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); // Initialize witness schedule - _p_witness_schedule_obj = & create([this](witness_schedule_object& _wso) +#ifndef NDEBUG + const witness_schedule_object& wso = +#endif + create([&](witness_schedule_object& _wso) { // for scheduled memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size()); @@ -1008,7 +980,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) for( const witness_id_type& wid : get_global_properties().active_witnesses ) _wso.current_shuffled_witnesses.push_back( wid ); }); - FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); + assert( wso.id == witness_schedule_id_type() ); // Initialize witness schedule #ifndef NDEBUG @@ -1038,6 +1010,12 @@ void database::init_genesis(const genesis_state_type& genesis_state) p.parameters.current_fees = genesis_state.initial_parameters.current_fees; }); + // Create witness scheduler + //create([&]( witness_schedule_object& wso ) + //{ + // for( const witness_id_type& wid : get_global_properties().active_witnesses ) + // wso.current_shuffled_witnesses.push_back( wid ); + //}); // Create FBA counters create([&]( fba_accumulator_object& acc ) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 841389cb..96ef6853 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -77,44 +77,12 @@ vector> database::sort return refs; } -template -void database::perform_account_maintenance(Type tally_helper) +template +void database::perform_account_maintenance(std::tuple helpers) { - const auto& bal_idx = get_index_type< account_balance_index >().indices().get< by_maintenance_flag >(); - if( bal_idx.begin() != bal_idx.end() ) - { - auto bal_itr = bal_idx.rbegin(); - while( bal_itr->maintenance_flag ) - { - const account_balance_object& bal_obj = *bal_itr; - - modify( get_account_stats_by_owner( bal_obj.owner ), [&bal_obj](account_statistics_object& aso) { - aso.core_in_balance = bal_obj.balance; - }); - - modify( bal_obj, []( account_balance_object& abo ) { - abo.maintenance_flag = false; - }); - - bal_itr = bal_idx.rbegin(); - } - } - - const auto& stats_idx = get_index_type< account_stats_index >().indices().get< by_maintenance_seq >(); - auto stats_itr = stats_idx.lower_bound( true ); - - while( stats_itr != stats_idx.end() ) - { - const account_statistics_object& acc_stat = *stats_itr; - const account_object& acc_obj = acc_stat.owner( *this ); - ++stats_itr; - - if( acc_stat.has_some_core_voting() ) - tally_helper( acc_obj, acc_stat ); - - if( acc_stat.has_pending_fees() ) - acc_stat.process_fees( acc_obj, *this ); - } + const auto& idx = get_index_type().indices().get(); + for( const account_object& a : idx ) + detail::for_each(helpers, a, detail::gen_seq()); } /// @brief A visitor for @ref worker_type which calls pay_worker on the worker within @@ -336,13 +304,12 @@ void database::update_son_wallet(const vector& new_active_sons) void database::pay_workers( share_type& budget ) { - const auto head_time = head_block_time(); // ilog("Processing payroll! Available budget is ${b}", ("b", budget)); vector> active_workers; - // TODO optimization: add by_expiration index to avoid iterating through all objects - get_index_type().inspect_all_objects([head_time, &active_workers](const object& o) { + get_index_type().inspect_all_objects([this, &active_workers](const object& o) { const worker_object& w = static_cast(o); - if( w.is_active(head_time) && w.approving_stake() > 0 ) + auto now = head_block_time(); + if( w.is_active(now) && w.approving_stake() > 0 ) active_workers.emplace_back(w); }); @@ -356,22 +323,17 @@ void database::pay_workers( share_type& budget ) return wa.id < wb.id; }); - const auto last_budget_time = get_dynamic_global_properties().last_budget_time; - const auto passed_time_ms = head_time - last_budget_time; - const auto passed_time_count = passed_time_ms.count(); - const auto day_count = fc::days(1).count(); for( uint32_t i = 0; i < active_workers.size() && budget > 0; ++i ) { const worker_object& active_worker = active_workers[i]; share_type requested_pay = active_worker.daily_pay; - - // Note: if there is a good chance that passed_time_count == day_count, - // for better performance, can avoid the 128 bit calculation by adding a check. - // Since it's not the case on BitShares mainnet, we're not using a check here. - fc::uint128 pay(requested_pay.value); - pay *= passed_time_count; - pay /= day_count; - requested_pay = pay.to_uint64(); + if( head_block_time() - get_dynamic_global_properties().last_budget_time != fc::days(1) ) + { + fc::uint128 pay(requested_pay.value); + pay *= (head_block_time() - get_dynamic_global_properties().last_budget_time).count(); + pay /= fc::days(1).count(); + requested_pay = pay.to_uint64(); + } share_type actual_pay = std::min(budget, requested_pay); //ilog(" ==> Paying ${a} to worker ${w}", ("w", active_worker.id)("a", actual_pay)); @@ -408,27 +370,13 @@ void database::update_active_witnesses() const global_property_object& gpo = get_global_properties(); - auto update_witness_total_votes = [this]( const witness_object& wit ) { - modify( wit, [this]( witness_object& obj ) - { - obj.total_votes = _vote_tally_buffer[obj.vote_id]; - }); - }; + const auto& all_witnesses = get_index_type().indices(); - if( _track_standby_votes ) + for( const witness_object& wit : all_witnesses ) { - const auto& all_witnesses = get_index_type().indices(); - for( const witness_object& wit : all_witnesses ) - { - update_witness_total_votes( wit ); - } - } - else - { - for( const witness_object& wit : wits ) - { - update_witness_total_votes( wit ); - } + modify( wit, [&]( witness_object& obj ){ + obj.total_votes = _vote_tally_buffer[wit.vote_id]; + }); } // Update witness authority @@ -504,29 +452,13 @@ void database::update_active_committee_members() const chain_property_object& cpo = get_chain_properties(); auto committee_members = sort_votable_objects(std::max(committee_member_count*2+1, (size_t)cpo.immutable_parameters.min_committee_member_count)); - auto update_committee_member_total_votes = [this]( const committee_member_object& cm ) { - modify( cm, [this]( committee_member_object& obj ) - { - obj.total_votes = _vote_tally_buffer[obj.vote_id]; - }); - }; + for( const committee_member_object& del : committee_members ) + { + modify( del, [&]( committee_member_object& obj ){ + obj.total_votes = _vote_tally_buffer[del.vote_id]; + }); + } - if( _track_standby_votes ) - { - const auto& all_committee_members = get_index_type().indices(); - for( const committee_member_object& cm : all_committee_members ) - { - update_committee_member_total_votes( cm ); - } - } - else - { - for( const committee_member_object& cm : committee_members ) - { - update_committee_member_total_votes( cm ); - } - } - // Update committee authorities if( !committee_members.empty() ) { @@ -716,8 +648,8 @@ void database::update_active_sons() void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const asset_object& core = get_core_asset(); - const asset_dynamic_data_object& core_dd = get_core_dynamic_data(); + const asset_object& core = asset_id_type(0)(*this); + const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(*this); rec.from_initial_reserve = core.reserved(*this); rec.from_accumulated_fees = core_dd.accumulated_fees; @@ -770,7 +702,8 @@ void database::process_budget() { const global_property_object& gpo = get_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const asset_dynamic_data_object& core = get_core_dynamic_data(); + const asset_dynamic_data_object& core = + asset_id_type(0)(*this).dynamic_asset_data_id(*this); fc::time_point_sec now = head_block_time(); int64_t time_to_maint = (dpo.next_maintenance_time - now).to_seconds(); @@ -950,7 +883,8 @@ void split_fba_balance( if( fba.accumulated_fba_fees == 0 ) return; - const asset_dynamic_data_object& core_dd = db.get_core_dynamic_data(); + const asset_object& core = asset_id_type(0)(db); + const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(db); if( !fba.is_configured(db) ) { @@ -1124,154 +1058,6 @@ void deprecate_annual_members( database& db ) return; } -uint32_t database::get_gpos_current_subperiod() -{ - if(this->head_block_time() < HARDFORK_GPOS_TIME) //Can be deleted after GPOS hardfork time - return 0; - - fc::time_point_sec last_date_voted; - - const auto &gpo = this->get_global_properties(); - const auto vesting_period = gpo.parameters.gpos_period(); - const auto vesting_subperiod = gpo.parameters.gpos_subperiod(); - const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start()); - - // variables needed - const fc::time_point_sec period_end = period_start + vesting_period; - const auto number_of_subperiods = vesting_period / vesting_subperiod; - const auto now = this->head_block_time(); - auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch(); - - FC_ASSERT(period_start <= now && now <= period_end); - - // get in what sub period we are - uint32_t current_subperiod = 0; - std::list period_list(number_of_subperiods); - std::iota(period_list.begin(), period_list.end(), 1); - - std::for_each(period_list.begin(), period_list.end(),[&](uint32_t period) { - if(seconds_since_period_start >= vesting_subperiod * (period - 1) && - seconds_since_period_start < vesting_subperiod * period) - current_subperiod = period; - }); - - return current_subperiod; -} - -double database::calculate_vesting_factor(const account_object& stake_account) -{ - fc::time_point_sec last_date_voted; - // get last time voted form account stats - // check last_vote_time of proxy voting account if proxy is set - if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT) - last_date_voted = stake_account.statistics(*this).last_vote_time; - else - last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time; - - // get global data related to gpos - const auto &gpo = this->get_global_properties(); - const auto vesting_period = gpo.parameters.gpos_period(); - const auto vesting_subperiod = gpo.parameters.gpos_subperiod(); - const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start()); - - // variables needed - const auto number_of_subperiods = vesting_period / vesting_subperiod; - double vesting_factor; - - // get in what sub period we are - uint32_t current_subperiod = get_gpos_current_subperiod(); - - if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0; - - // On starting new vesting period, all votes become zero until someone votes, To avoid a situation of zero votes, - // changes were done to roll in GPOS rules, the vesting factor will be 1 for whoever votes in 6th sub-period of last vesting period - // BLOCKBACK-174 fix - if(current_subperiod == 1 && this->head_block_time() >= HARDFORK_GPOS_TIME + vesting_period) //Applicable only from 2nd vesting period - { - if(last_date_voted > period_start - vesting_subperiod) - return 1; - } - if(last_date_voted < period_start) return 0; - - double numerator = number_of_subperiods; - - if(current_subperiod > 1) { - std::list subperiod_list(current_subperiod - 1); - std::iota(subperiod_list.begin(), subperiod_list.end(), 2); - subperiod_list.reverse(); - - for(auto subperiod: subperiod_list) - { - numerator--; - - auto last_period_start = period_start + fc::seconds(vesting_subperiod * (subperiod - 1)); - auto last_period_end = period_start + fc::seconds(vesting_subperiod * (subperiod)); - - if (last_date_voted > last_period_start && last_date_voted <= last_period_end) { - numerator++; - break; - } - } - } - vesting_factor = numerator / number_of_subperiods; - return vesting_factor; -} - -share_type credit_account(database& db, const account_id_type owner_id, const std::string owner_name, - share_type remaining_amount_to_distribute, - const share_type shares_to_credit, const asset_id_type payout_asset_type, - const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index, - const asset_id_type dividend_id) { - - //wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); - if (shares_to_credit.value) { - - remaining_amount_to_distribute -= shares_to_credit; - - dlog("Crediting account ${account} with ${amount}", - ("account", owner_name) - ("amount", asset(shares_to_credit, payout_asset_type))); - auto pending_payout_iter = - pending_payout_balance_index.indices().get().find( - boost::make_tuple(dividend_id, payout_asset_type, - owner_id)); - if (pending_payout_iter == - pending_payout_balance_index.indices().get().end()) - db.create( - [&](pending_dividend_payout_balance_for_holder_object &obj) { - obj.owner = owner_id; - obj.dividend_holder_asset_type = dividend_id; - obj.dividend_payout_asset_type = payout_asset_type; - obj.pending_balance = shares_to_credit; - }); - else - db.modify(*pending_payout_iter, - [&](pending_dividend_payout_balance_for_holder_object &pending_balance) { - pending_balance.pending_balance += shares_to_credit; - }); - } - return remaining_amount_to_distribute; -} - -void rolling_period_start(database& db) -{ - if(db.head_block_time() >= HARDFORK_GPOS_TIME) - { - auto gpo = db.get_global_properties(); - auto period_start = db.get_global_properties().parameters.gpos_period_start(); - auto vesting_period = db.get_global_properties().parameters.gpos_period(); - - auto now = db.head_block_time(); - if(now.sec_since_epoch() >= (period_start + vesting_period)) - { - // roll - db.modify(db.get_global_properties(), [now](global_property_object& p) { - p.parameters.extensions.value.gpos_period_start = now.sec_since_epoch(); - }); - } - } -} - // Schedules payouts from a dividend distribution account to the current holders of the // dividend-paying asset. This takes any deposits made to the dividend distribution account // since the last time it was called, and distributes them to the current owners of the @@ -1303,42 +1089,34 @@ void schedule_pending_dividend_balances(database& db, balance_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); auto holder_balances_end = balance_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); + uint32_t holder_account_count = std::distance(holder_balances_begin, holder_balances_end); uint64_t distribution_base_fee = gpo.parameters.current_fees->get().distribution_base_fee; uint32_t distribution_fee_per_holder = gpo.parameters.current_fees->get().distribution_fee_per_holder; + // the fee, in BTS, for distributing each asset in the account + uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; std::map vesting_amounts; - - auto balance_type = vesting_balance_type::normal; - if(db.head_block_time() >= HARDFORK_GPOS_TIME) - balance_type = vesting_balance_type::gpos; - - uint32_t holder_account_count = 0; - #ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset auto vesting_balances_begin = - vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type)); + vesting_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); auto vesting_balances_end = - vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, balance_type, share_type())); - + vesting_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - ++holder_account_count; - dlog("Vesting balance for account: ${owner}, amount: ${amount}", - ("owner", vesting_balance_obj.owner(db).name) - ("amount", vesting_balance_obj.balance.amount)); + //dlog("Vesting balance for account: ${owner}, amount: ${amount}", + // ("owner", vesting_balance_obj.owner(db).name) + // ("amount", vesting_balance_obj.balance.amount)); } #else // get only once a collection of accounts that hold nonzero vesting balances of the dividend asset const auto& vesting_balances = vesting_index.indices().get(); for (const vesting_balance_object& vesting_balance_obj : vesting_balances) { - if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount && - vesting_balance_object.balance_type == balance_type) + if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - ++gpos_holder_account_count; dlog("Vesting balance for account: ${owner}, amount: ${amount}", ("owner", vesting_balance_obj.owner(db).name) ("amount", vesting_balance_obj.balance.amount)); @@ -1347,12 +1125,6 @@ void schedule_pending_dividend_balances(database& db, #endif auto current_distribution_account_balance_iter = current_distribution_account_balance_range.begin(); - if(db.head_block_time() < HARDFORK_GPOS_TIME) - holder_account_count = std::distance(holder_balances_begin, holder_balances_end); - // the fee, in BTS, for distributing each asset in the account - uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder; - - //auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first; auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first; dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}", ("current", (int64_t)std::distance(current_distribution_account_balance_range.begin(), current_distribution_account_balance_range.end())) @@ -1362,23 +1134,14 @@ void schedule_pending_dividend_balances(database& db, // accounts other than the distribution account (it would be silly to distribute dividends back to // the distribution account) share_type total_balance_of_dividend_asset; - if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // only core - for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range(vesting_balances_begin, - vesting_balances_end)) - if (holder_balance_object.owner != dividend_data.dividend_distribution_account) { - total_balance_of_dividend_asset += holder_balance_object.balance.amount; - } - } - else { - for (const account_balance_object &holder_balance_object : boost::make_iterator_range(holder_balances_begin, - holder_balances_end)) - if (holder_balance_object.owner != dividend_data.dividend_distribution_account) { - total_balance_of_dividend_asset += holder_balance_object.balance; - auto itr = vesting_amounts.find(holder_balance_object.owner); - if (itr != vesting_amounts.end()) - total_balance_of_dividend_asset += itr->second; - } - } + for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) + if (holder_balance_object.owner != dividend_data.dividend_distribution_account) + { + total_balance_of_dividend_asset += holder_balance_object.balance; + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + total_balance_of_dividend_asset += itr->second; + } // loop through all of the assets currently or previously held in the distribution account while (current_distribution_account_balance_iter != current_distribution_account_balance_range.end() || previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second) @@ -1502,68 +1265,46 @@ void schedule_pending_dividend_balances(database& db, ("total", total_balance_of_dividend_asset)); share_type remaining_amount_to_distribute = delta_balance; - if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // core only - // credit each account with their portion, don't send any back to the dividend distribution account - for (const vesting_balance_object &holder_balance_object : boost::make_iterator_range( - vesting_balances_begin, vesting_balances_end)) { - if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; + // credit each account with their portion, don't send any back to the dividend distribution account + for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end)) + { + if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; - auto vesting_factor = db.calculate_vesting_factor(holder_balance_object.owner(db)); + auto holder_balance = holder_balance_object.balance; - auto holder_balance = holder_balance_object.balance; + auto itr = vesting_amounts.find(holder_balance_object.owner); + if (itr != vesting_amounts.end()) + holder_balance += itr->second; - fc::uint128_t amount_to_credit(delta_balance.value); - amount_to_credit *= holder_balance.amount.value; - amount_to_credit /= total_balance_of_dividend_asset.value; - share_type full_shares_to_credit((int64_t) amount_to_credit.to_uint64()); - share_type shares_to_credit = (uint64_t) floor(full_shares_to_credit.value * vesting_factor); + fc::uint128_t amount_to_credit(delta_balance.value); + amount_to_credit *= holder_balance.value; + amount_to_credit /= total_balance_of_dividend_asset.value; + share_type shares_to_credit((int64_t)amount_to_credit.to_uint64()); + if (shares_to_credit.value) + { + wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset)); - if (shares_to_credit < full_shares_to_credit) { - // Todo: sending results of decay to committee account, need to change to specified account - dlog("Crediting committee_account with ${amount}", - ("amount", asset(full_shares_to_credit - shares_to_credit, payout_asset_type))); - db.adjust_balance(dividend_data.dividend_distribution_account, - -asset(full_shares_to_credit - shares_to_credit, payout_asset_type)); - db.adjust_balance(account_id_type(0), asset(full_shares_to_credit - shares_to_credit, payout_asset_type)); - } + remaining_amount_to_distribute -= shares_to_credit; - remaining_amount_to_distribute = credit_account(db, - holder_balance_object.owner, - holder_balance_object.owner(db).name, - remaining_amount_to_distribute, - shares_to_credit, - payout_asset_type, - pending_payout_balance_index, - dividend_holder_asset_obj.id); + dlog("Crediting account ${account} with ${amount}", + ("account", holder_balance_object.owner(db).name) + ("amount", asset(shares_to_credit, payout_asset_type))); + auto pending_payout_iter = + pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type, holder_balance_object.owner)); + if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) + db.create( [&]( pending_dividend_payout_balance_for_holder_object& obj ){ + obj.owner = holder_balance_object.owner; + obj.dividend_holder_asset_type = dividend_holder_asset_obj.id; + obj.dividend_payout_asset_type = payout_asset_type; + obj.pending_balance = shares_to_credit; + }); + else + db.modify(*pending_payout_iter, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){ + pending_balance.pending_balance += shares_to_credit; + }); } } - else { - // credit each account with their portion, don't send any back to the dividend distribution account - for (const account_balance_object &holder_balance_object : boost::make_iterator_range( - holder_balances_begin, holder_balances_end)) { - if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue; - auto holder_balance = holder_balance_object.balance; - - auto itr = vesting_amounts.find(holder_balance_object.owner); - if (itr != vesting_amounts.end()) - holder_balance += itr->second; - - fc::uint128_t amount_to_credit(delta_balance.value); - amount_to_credit *= holder_balance.value; - amount_to_credit /= total_balance_of_dividend_asset.value; - share_type shares_to_credit((int64_t) amount_to_credit.to_uint64()); - - remaining_amount_to_distribute = credit_account(db, - holder_balance_object.owner, - holder_balance_object.owner(db).name, - remaining_amount_to_distribute, - shares_to_credit, - payout_asset_type, - pending_payout_balance_index, - dividend_holder_asset_obj.id); - } - } for (const auto& pending_payout : pending_payout_balance_index.indices()) if (pending_payout.pending_balance.value) dlog("Pending payout: ${account_name} -> ${amount}", @@ -1826,8 +1567,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g process_dividend_assets(*this); - rolling_period_start(*this); - struct vote_tally_helper { database& d; const global_property_object& props; @@ -1842,28 +1581,24 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1); d._total_voting_stake = 0; - auto balance_type = vesting_balance_type::normal; - if(d.head_block_time() >= HARDFORK_GPOS_TIME) - balance_type = vesting_balance_type::gpos; - const vesting_balance_index& vesting_index = d.get_index_type(); #ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX auto vesting_balances_begin = - vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type(), balance_type)); + vesting_index.indices().get().lower_bound(boost::make_tuple(asset_id_type())); auto vesting_balances_end = - vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type())); + vesting_index.indices().get().upper_bound(boost::make_tuple(asset_id_type(), share_type())); for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end)) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; - dlog("Vesting balance for account: ${owner}, amount: ${amount}", - ("owner", vesting_balance_obj.owner(d).name) - ("amount", vesting_balance_obj.balance.amount)); + //dlog("Vesting balance for account: ${owner}, amount: ${amount}", + // ("owner", vesting_balance_obj.owner(d).name) + // ("amount", vesting_balance_obj.balance.amount)); } #else const auto& vesting_balances = vesting_index.indices().get(); for (const vesting_balance_object& vesting_balance_obj : vesting_balances) { - if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount && vesting_balance_obj.balance_type == balance_type) + if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount) { vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount; dlog("Vesting balance for account: ${owner}, amount: ${amount}", @@ -1874,8 +1609,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g #endif } - void operator()( const account_object& stake_account, const account_statistics_object& stats ) - { + void operator()(const account_object& stake_account) { if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) ) { // There may be a difference between the account whose stake is voting and the one specifying opinions. @@ -1892,35 +1626,13 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g const account_object& opinion_account = *opinion_account_ptr; const auto& stats = stake_account.statistics(d); - uint64_t voting_stake = 0; + uint64_t voting_stake = stats.total_core_in_orders.value + + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) + + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; auto itr = vesting_amounts.find(stake_account.id); if (itr != vesting_amounts.end()) voting_stake += itr->second.value; - - if(d.head_block_time() >= HARDFORK_GPOS_TIME) - { - if (itr == vesting_amounts.end() && d.head_block_time() >= (HARDFORK_GPOS_TIME + props.parameters.gpos_subperiod()/2)) - return; - - auto vesting_factor = d.calculate_vesting_factor(stake_account); - voting_stake = (uint64_t)floor(voting_stake * vesting_factor); - - //Include votes(based on stake) for the period of gpos_subperiod()/2 as system has zero votes on GPOS activation - if(d.head_block_time() < (HARDFORK_GPOS_TIME + props.parameters.gpos_subperiod()/2)) - { - voting_stake += stats.total_core_in_orders.value - + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value : 0) - + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; - } - } - else - { - voting_stake += stats.total_core_in_orders.value - + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value : 0) - + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value; - } - for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); @@ -1968,8 +1680,23 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } } tally_helper(*this, gpo); - - perform_account_maintenance( tally_helper ); + struct process_fees_helper { + database& d; + const global_property_object& props; + + process_fees_helper(database& d, const global_property_object& gpo) + : d(d), props(gpo) {} + + void operator()(const account_object& a) { + a.statistics(d).process_fees(a, d); + } + } fee_helper(*this, gpo); + + perform_account_maintenance(std::tie( + tally_helper, + fee_helper + )); + struct clear_canary { clear_canary(vector& target): target(target){} ~clear_canary() { target.clear(); } @@ -1987,10 +1714,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g update_active_sons(); update_worker_votes(); - const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); - - modify(gpo, [&dgpo](global_property_object& p) { + modify(gpo, [this](global_property_object& p) { // Remove scaling of account registration fee + const auto& dgpo = get_dynamic_global_properties(); p.parameters.current_fees->get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale); @@ -2006,20 +1732,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments; if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() ) p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time; - if( !p.pending_parameters->extensions.value.gpos_period_start.valid() ) - p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; - if( !p.pending_parameters->extensions.value.gpos_period.valid() ) - p.pending_parameters->extensions.value.gpos_period = p.parameters.extensions.value.gpos_period; - if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() ) - p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod; - if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() ) - p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); } }); - auto next_maintenance_time = dgpo.next_maintenance_time; + auto next_maintenance_time = get(dynamic_global_property_id_type()).next_maintenance_time; auto maintenance_interval = gpo.parameters.maintenance_interval; if( next_maintenance_time <= next_block.timestamp ) @@ -2049,6 +1767,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } + const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); + if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) ) deprecate_annual_members(*this); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 9560aae3..f6d164d2 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -24,9 +24,6 @@ #include -#include -#include -#include #include #include @@ -180,7 +177,7 @@ void database::wipe(const fc::path& data_dir, bool include_blocks) { ilog("Wiping database", ("include_blocks", include_blocks)); if (_opened) { - close(false); + close(); } object_database::wipe(data_dir); if( include_blocks ) @@ -218,15 +215,6 @@ void database::open( if( !find(global_property_id_type()) ) init_genesis(genesis_loader()); - else - { - _p_core_asset_obj = &get( asset_id_type() ); - _p_core_dynamic_data_obj = &get( asset_dynamic_data_id_type() ); - _p_global_prop_obj = &get( global_property_id_type() ); - _p_chain_property_obj = &get( chain_property_id_type() ); - _p_dyn_global_prop_obj = &get( dynamic_global_property_id_type() ); - _p_witness_schedule_obj = &get( witness_schedule_id_type() ); - } fc::optional last_block = _block_id_to_block.last_id(); if( last_block.valid() ) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index ad888532..59f77762 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -426,16 +426,14 @@ bool database::fill_order(const force_settlement_object& settle, const asset& pa * * @return true if a margin call was executed. */ -bool database::check_call_orders( const asset_object& mia, bool enable_black_swan, bool for_new_limit_order, - const asset_bitasset_data_object* bitasset_ptr ) +bool database::check_call_orders(const asset_object& mia, bool enable_black_swan) { try { if( !mia.is_market_issued() ) return false; - const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); - - if( check_for_blackswan( mia, enable_black_swan, &bitasset ) ) + if( check_for_blackswan( mia, enable_black_swan ) ) return false; + const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); if( bitasset.is_prediction_market ) return false; if( bitasset.current_feed.settlement_price.is_null() ) return false; @@ -466,12 +464,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa bool filled_limit = false; bool margin_called = false; - auto head_time = head_block_time(); - auto head_num = head_block_num(); - - bool after_hardfork_436 = ( head_time > HARDFORK_436_TIME ); - - while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) && call_itr != call_end ) + while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end ) { bool filled_call = false; price match_price; @@ -488,7 +481,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa // would be margin called, but there is no matching order #436 bool feed_protected = ( bitasset.current_feed.settlement_price > ~call_itr->call_price ); - if( feed_protected && after_hardfork_436 ) + if( feed_protected && (head_block_time() > HARDFORK_436_TIME) ) return margin_called; // would be margin called, but there is no matching order @@ -513,8 +506,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa if( usd_to_buy * match_price > call_itr->get_collateral() ) { - elog( "black swan detected on asset ${symbol} (${id}) at block ${b}", - ("id",mia.id)("symbol",mia.symbol)("b",head_num) ); + elog( "black swan detected" ); edump((enable_black_swan)); FC_ASSERT( enable_black_swan ); globally_settle_asset(mia, bitasset.current_feed.settlement_price ); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index a762fe2c..6cf7c7b0 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -33,14 +33,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include - using namespace fc; using namespace graphene::chain; @@ -349,13 +341,13 @@ struct get_impacted_account_visitor } }; -void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) +void operation_get_impacted_accounts( const operation& op, flat_set& result ) { get_impacted_account_visitor vtor = get_impacted_account_visitor( result ); op.visit( vtor ); } -void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) +void transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) { for( const auto& op : tx.operations ) operation_get_impacted_accounts( op, result ); @@ -511,16 +503,6 @@ void get_relevant_accounts( const object* obj, flat_set& accoun namespace graphene { namespace chain { -void database::notify_applied_block( const signed_block& block ) -{ - GRAPHENE_TRY_NOTIFY( applied_block, block ) -} - -void database::notify_on_pending_transaction( const signed_transaction& tx ) -{ - GRAPHENE_TRY_NOTIFY( on_pending_transaction, tx ) -} - void database::notify_changed_objects() { try { if( _undo_db.enabled() ) @@ -540,7 +522,7 @@ void database::notify_changed_objects() get_relevant_accounts(obj, new_accounts_impacted); } - GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) + new_objects(new_ids, new_accounts_impacted); } // Changed @@ -554,7 +536,7 @@ void database::notify_changed_objects() get_relevant_accounts(item.second.get(), changed_accounts_impacted); } - GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) + changed_objects(changed_ids, changed_accounts_impacted); } // Removed @@ -571,7 +553,7 @@ void database::notify_changed_objects() get_relevant_accounts(obj, removed_accounts_impacted); } - GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) + removed_objects(removed_ids, removed, removed_accounts_impacted); } } } FC_CAPTURE_AND_LOG( (0) ) } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index c89b4bd5..ed440d82 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -45,7 +45,7 @@ namespace graphene { namespace chain { void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks ) { - const dynamic_global_property_object& _dgp = get_dynamic_global_properties(); + const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this); const global_property_object& gpo = get_global_properties(); // dynamic global properties updating @@ -121,7 +121,6 @@ void database::update_last_irreversible_block() const global_property_object& gpo = get_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - // TODO for better performance, move this to db_maint, because only need to do it once per maintenance interval vector< const witness_object* > wit_objs; wit_objs.reserve( gpo.active_witnesses.size() ); for( const witness_id_type& wid : gpo.active_witnesses ) @@ -239,12 +238,11 @@ void database::clear_expired_proposals() * * A black swan occurs if MAX(HB,SP) <= LC */ -bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan, - const asset_bitasset_data_object* bitasset_ptr ) +bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan ) { if( !mia.is_market_issued() ) return false; - const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); + const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); if( bitasset.has_settlement() ) return true; // already force settled auto settle_price = bitasset.current_feed.settlement_price; if( settle_price.is_null() ) return false; // no feed @@ -469,84 +467,32 @@ void database::clear_expired_orders() void database::update_expired_feeds() { - const auto head_time = head_block_time(); - bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME ); - - const auto& idx = get_index_type().indices().get(); - auto itr = idx.begin(); - while( itr != idx.end() && itr->feed_is_expired( head_time ) ) + auto& asset_idx = get_index_type().indices().get(); + auto itr = asset_idx.lower_bound( true /** market issued */ ); + while( itr != asset_idx.end() ) { - const asset_bitasset_data_object& b = *itr; - ++itr; // not always process begin() because old code skipped updating some assets before hf 615 - bool update_cer = false; // for better performance, to only update bitasset once, also check CER in this function - const asset_object* asset_ptr = nullptr; - // update feeds, check margin calls - if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) ) + const asset_object& a = *itr; + ++itr; + assert( a.is_market_issued() ); + + const asset_bitasset_data_object& b = a.bitasset_data(*this); + bool feed_is_expired; + if( head_block_time() < HARDFORK_615_TIME ) + feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() ); + else + feed_is_expired = b.feed_is_expired( head_block_time() ); + if( feed_is_expired ) { - auto old_median_feed = b.current_feed; - modify( b, [head_time,&update_cer]( asset_bitasset_data_object& abdo ) - { - abdo.update_median_feeds( head_time ); - if( abdo.need_to_update_cer() ) - { - update_cer = true; - abdo.asset_cer_updated = false; - abdo.feed_cer_updated = false; - } + modify(b, [this](asset_bitasset_data_object& a) { + a.update_median_feeds(head_block_time()); }); - if( !b.current_feed.settlement_price.is_null() && !( b.current_feed == old_median_feed ) ) // `==` check is safe here - { - asset_ptr = &b.asset_id( *this ); - check_call_orders( *asset_ptr, true, false, &b ); - } + check_call_orders(b.current_feed.settlement_price.base.asset_id(*this)); } - // update CER - if( update_cer ) - { - if( !asset_ptr ) - asset_ptr = &b.asset_id( *this ); - if( asset_ptr->options.core_exchange_rate != b.current_feed.core_exchange_rate ) - { - modify( *asset_ptr, [&b]( asset_object& ao ) - { - ao.options.core_exchange_rate = b.current_feed.core_exchange_rate; - }); - } - } - } // for each asset whose feed is expired - - // process assets affected by bitshares-core issue 453 before hard fork 615 - if( !after_hardfork_615 ) - { - for( asset_id_type a : _issue_453_affected_assets ) - { - check_call_orders( a(*this) ); - } - } -} - -void database::update_core_exchange_rates() -{ - const auto& idx = get_index_type().indices().get(); - if( idx.begin() != idx.end() ) - { - for( auto itr = idx.rbegin(); itr->need_to_update_cer(); itr = idx.rbegin() ) - { - const asset_bitasset_data_object& b = *itr; - const asset_object& a = b.asset_id( *this ); - if( a.options.core_exchange_rate != b.current_feed.core_exchange_rate ) - { - modify( a, [&b]( asset_object& ao ) - { - ao.options.core_exchange_rate = b.current_feed.core_exchange_rate; - }); - } - modify( b, []( asset_bitasset_data_object& abdo ) - { - abdo.asset_cer_updated = false; - abdo.feed_cer_updated = false; + if( !b.current_feed.core_exchange_rate.is_null() && + a.options.core_exchange_rate != b.current_feed.core_exchange_rate ) + modify(a, [&b](asset_object& a) { + a.options.core_exchange_rate = b.current_feed.core_exchange_rate; }); - } } } diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 084c8e1d..31caad4b 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -40,14 +40,14 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const witness_schedule_object& wso = get_witness_schedule_object();; + const witness_schedule_object& wso = witness_schedule_id_type()(*this); uint64_t current_aslot = dpo.current_aslot + slot_num; return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ]; } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && slot_num != 0 ) { - const witness_schedule_object& wso = get_witness_schedule_object(); + const witness_schedule_object& wso = witness_schedule_id_type()(*this); // ask the near scheduler who goes in the given slot bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid); if(! slot_is_near) @@ -156,7 +156,7 @@ uint32_t database::get_slot_at_time(fc::time_point_sec when)const void database::update_witness_schedule() { - const witness_schedule_object& wso = get_witness_schedule_object(); + const witness_schedule_object& wso = witness_schedule_id_type()(*this); const global_property_object& gpo = get_global_properties(); if( head_block_num() % gpo.active_witnesses.size() == 0 ) @@ -226,7 +226,7 @@ void database::update_son_schedule() vector database::get_near_witness_schedule()const { - const witness_schedule_object& wso = get_witness_schedule_object(); + const witness_schedule_object& wso = witness_schedule_id_type()(*this); vector result; result.reserve(wso.scheduler.size()); @@ -243,7 +243,7 @@ void database::update_witness_schedule(const signed_block& next_block) { auto start = fc::time_point::now(); const global_property_object& gpo = get_global_properties(); - const witness_schedule_object& wso = get_witness_schedule_object(); + const witness_schedule_object& wso = get(witness_schedule_id_type()); uint32_t schedule_needs_filled = gpo.active_witnesses.size(); uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); @@ -395,7 +395,7 @@ uint32_t database::witness_participation_rate()const } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { - const witness_schedule_object& wso = get_witness_schedule_object(); + const witness_schedule_object& wso = get(witness_schedule_id_type()); return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128; } return 0; diff --git a/libraries/chain/genesis_state.cpp b/libraries/chain/genesis_state.cpp index 53311012..a278b680 100644 --- a/libraries/chain/genesis_state.cpp +++ b/libraries/chain/genesis_state.cpp @@ -36,72 +36,3 @@ chain_id_type genesis_state_type::compute_chain_id() const } } } // graphene::chain - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_account_type, BOOST_PP_SEQ_NIL, (name)(owner_key)(active_key)(is_lifetime_member)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type, BOOST_PP_SEQ_NIL, - (symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)(collateral_records)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position, BOOST_PP_SEQ_NIL, - (owner)(collateral)(debt)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_balance_type, BOOST_PP_SEQ_NIL, - (owner)(asset_symbol)(amount)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_vesting_balance_type, BOOST_PP_SEQ_NIL, - (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_witness_type, BOOST_PP_SEQ_NIL, (owner_name)(block_signing_key)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_committee_member_type, BOOST_PP_SEQ_NIL, (owner_name)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_worker_type, BOOST_PP_SEQ_NIL, (owner_name)(daily_pay)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority, BOOST_PP_SEQ_NIL, - (weight_threshold) - (account_auths) - (key_auths) - (address_auths)) -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy, BOOST_PP_SEQ_NIL, - (vesting_seconds) - (coin_seconds_earned) - (start_claim) - (coin_seconds_earned_last_update)) -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy, BOOST_PP_SEQ_NIL, - (begin_timestamp) - (vesting_cliff_seconds) - (vesting_duration_seconds) - (begin_balance)) -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance, BOOST_PP_SEQ_NIL, - (asset_symbol) - (amount) - (policy_type) - (policy)) -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type, BOOST_PP_SEQ_NIL, - (name) - (owner_authority) - (active_authority) - (core_balance) - (vesting_balances)) - -FC_REFLECT_DERIVED_NO_TYPENAME(graphene::chain::genesis_state_type, BOOST_PP_SEQ_NIL, - (initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances) - (initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates) - (initial_committee_candidates)(initial_worker_candidates) - (initial_chain_id) - (immutable_parameters)) - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_account_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_asset_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_balance_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_vesting_balance_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_witness_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_committee_member_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_worker_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type::initial_bts_account_type) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::genesis_state_type) diff --git a/libraries/chain/hardfork.d/GPOS.hf b/libraries/chain/hardfork.d/GPOS.hf deleted file mode 100644 index 8e93f80f..00000000 --- a/libraries/chain/hardfork.d/GPOS.hf +++ /dev/null @@ -1,4 +0,0 @@ -// GPOS HARDFORK Monday, 31 Dec 2019 00:00:00 GMT -#ifndef HARDFORK_GPOS_TIME -#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1577750400 )) -#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 5f24adeb..4e940326 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -22,9 +22,8 @@ * THE SOFTWARE. */ #pragma once -#include +#include #include -#include #include namespace graphene { namespace chain { @@ -47,8 +46,6 @@ namespace graphene { namespace chain { account_id_type owner; - string name; ///< redundantly store account name here for better maintenance performance - /** * Keep the most recent operation as a root pointer to a linked list of the transaction history. */ @@ -65,19 +62,6 @@ namespace graphene { namespace chain { */ share_type total_core_in_orders; - share_type core_in_balance = 0; ///< redundantly store core balance here for better maintenance performance - - bool has_cashback_vb = false; ///< redundantly store this for better maintenance performance - - bool is_voting = false; ///< redundately store whether this account is voting for better maintenance performance - - - /// Whether this account owns some CORE asset and is voting - inline bool has_some_core_voting() const - { - return is_voting && ( total_core_in_orders > 0 || core_in_balance > 0 || has_cashback_vb ); - } - /** * Tracks the total fees paid by this account for the purpose of calculating bulk discounts. */ @@ -103,12 +87,6 @@ namespace graphene { namespace chain { */ time_point_sec last_vote_time; - /// Whether this account has pending fees, no matter vested or not - inline bool has_pending_fees() const { return pending_fees > 0 || pending_vested_fees > 0; } - - /// Whether need to process this account during the maintenance interval - inline bool need_maintenance() const { return has_some_core_voting() || has_pending_fees(); } - /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees void process_fees(const account_object& a, database& d) const; @@ -134,7 +112,6 @@ namespace graphene { namespace chain { account_id_type owner; asset_id_type asset_type; share_type balance; - bool maintenance_flag = false; ///< Whether need to process this balance object in maintenance interval asset get_balance()const { return asset(balance, asset_type); } void adjust_balance(const asset& delta); @@ -411,9 +388,6 @@ namespace graphene { namespace chain { }; struct by_asset_balance; - struct by_maintenance_flag; - struct by_account_asset; - /** * @ingroup object_index */ @@ -421,15 +395,6 @@ namespace graphene { namespace chain { account_balance_object, indexed_by< 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, @@ -469,6 +434,26 @@ namespace graphene { namespace chain { */ typedef generic_index account_index; + struct by_owner; + struct by_maintenance_seq; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + account_statistics_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, + member< account_statistics_object, account_id_type, &account_statistics_object::owner > > + > + > account_stats_multi_index_type; + + /** + * @ingroup object_index + */ + typedef generic_index account_stats_index; + struct by_dividend_payout_account{}; // use when calculating pending payouts struct by_dividend_account_payout{}; // use when doing actual payouts struct by_account_dividend_payout{}; // use in get_full_accounts() @@ -512,33 +497,6 @@ namespace graphene { namespace chain { */ typedef generic_index pending_dividend_payout_balance_for_holder_object_index; - struct by_owner; - struct by_maintenance_seq; - - /** - * @ingroup object_index - */ - typedef multi_index_container< - account_statistics_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_unique< tag, - member< account_statistics_object, account_id_type, &account_statistics_object::owner > >, - ordered_unique< tag, - composite_key< - account_statistics_object, - const_mem_fun, - member - > - > - > - > account_stats_multi_index_type; - - /** - * @ingroup object_index - */ - typedef generic_index account_stats_index; - }} FC_REFLECT_DERIVED( graphene::chain::account_object, @@ -555,17 +513,14 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), - (owner)(asset_type)(balance)(maintenance_flag) ) + (owner)(asset_type)(balance) ) FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object), - (owner)(name) + (owner) (most_recent_op) (total_ops)(removed_ops) (total_core_in_orders) - (core_in_balance) - (has_cashback_vb) - (is_voting) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) (last_vote_time) @@ -575,7 +530,4 @@ FC_REFLECT_DERIVED( graphene::chain::pending_dividend_payout_balance_for_holder_ (graphene::db::object), (owner)(dividend_holder_asset_type)(dividend_payout_asset_type)(pending_balance) ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_statistics_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::pending_dividend_payout_balance_for_holder_object ) + diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 8978a6d1..f1df4681 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -22,11 +22,10 @@ * THE SOFTWARE. */ #pragma once -#include -#include -#include #include #include +#include +#include /** * @defgroup prediction_market Prediction Market @@ -39,10 +38,11 @@ */ namespace graphene { namespace chain { + class account_object; class database; class transaction_evaluation_state; using namespace graphene::db; - + /** * @brief tracks the asset information that changes frequently * @ingroup object @@ -118,9 +118,9 @@ namespace graphene { namespace chain { /// Convert an asset to a textual representation with symbol, i.e. "123.45 USD" string amount_to_pretty_string(const asset &amount)const { FC_ASSERT(amount.asset_id == id); return amount_to_pretty_string(amount.amount); } - + uint32_t get_issuer_num()const - { return issuer.instance.value; } + { return issuer.instance.value; } /// Ticker symbol for this asset, i.e. "USD" string symbol; /// Maximum number of digits after the decimal point (must be <= 12) @@ -138,7 +138,7 @@ namespace graphene { namespace chain { map< account_id_type, vector< uint16_t > > distribute_winners_part( database& db ); void distribute_sweeps_holders_part( database& db ); void end_lottery( database& db ); - + /// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently. asset_dynamic_data_id_type dynamic_asset_data_id; /// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true @@ -150,7 +150,7 @@ namespace graphene { namespace chain { optional dividend_data_id; asset_id_type get_id()const { return id; } - + void validate()const { // UIAs may not be prediction markets, have force settlement, or global settlements @@ -174,7 +174,7 @@ namespace graphene { namespace chain { { return db.get(dynamic_asset_data_id); } /** - * The total amount of an asset that is reserved for future issuance. + * The total amount of an asset that is reserved for future issuance. */ template share_type reserved( const DB& db )const @@ -193,9 +193,6 @@ namespace graphene { namespace chain { static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_asset_bitasset_data_type; - /// The asset this object belong to - asset_id_type asset_id; - /// The tunable options for BitAssets are stored in this field. bitasset_options options; @@ -233,18 +230,6 @@ namespace graphene { namespace chain { share_type settlement_fund; ///@} - /// Track whether core_exchange_rate in corresponding asset_object has updated - bool asset_cer_updated = false; - - /// Track whether core exchange rate in current feed has updated - bool feed_cer_updated = false; - - /// Whether need to update core_exchange_rate in asset_object - bool need_to_update_cer() const - { - return ( ( feed_cer_updated || asset_cer_updated ) && !current_feed.core_exchange_rate.is_null() ); - } - /// The time when @ref current_feed would expire time_point_sec feed_expiration_time()const { @@ -254,7 +239,7 @@ namespace graphene { namespace chain { else return current_feed_publication_time + options.feed_lifetime_sec; } - + bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const { return feed_expiration_time() >= current_time; } bool feed_is_expired(time_point_sec current_time)const @@ -262,34 +247,14 @@ namespace graphene { namespace chain { void update_median_feeds(time_point_sec current_time); }; - // key extractor for short backing asset - struct bitasset_short_backing_asset_extractor - { - typedef asset_id_type result_type; - result_type operator() (const asset_bitasset_data_object& obj) const - { - return obj.options.short_backing_asset; - } - }; - - struct by_short_backing_asset; struct by_feed_expiration; - struct by_cer_update; - typedef multi_index_container< asset_bitasset_data_object, indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, bitasset_short_backing_asset_extractor >, - ordered_unique< tag, - composite_key< asset_bitasset_data_object, - const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time >, - member< asset_bitasset_data_object, asset_id_type, &asset_bitasset_data_object::asset_id > - > - >, - ordered_non_unique< tag, - const_mem_fun< asset_bitasset_data_object, bool, &asset_bitasset_data_object::need_to_update_cer > - > + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, + const_mem_fun< asset_bitasset_data_object, time_point_sec, &asset_bitasset_data_object::feed_expiration_time > + > > > asset_bitasset_data_object_multi_index_type; //typedef flat_index asset_bitasset_data_index; @@ -378,7 +343,7 @@ namespace graphene { namespace chain { /// This field is reset any time the dividend_asset_options are updated fc::optional last_scheduled_payout_time; - /// The time payouts on this asset were last processed + /// The time payouts on this asset were last processed /// (this should be the maintenance interval at or after last_scheduled_payout_time) /// This can be displayed for the user fc::optional last_payout_time; @@ -405,7 +370,7 @@ namespace graphene { namespace chain { typedef generic_index asset_dividend_data_object_index; - // This tracks the balances in a dividend distribution account at the last time + // This tracks the balances in a dividend distribution account at the last time // pending dividend payouts were calculated (last maintenance interval). // At each maintenance interval, we will compare the current balance to the // balance stored here to see how much was deposited during that interval. @@ -434,9 +399,9 @@ namespace graphene { namespace chain { > > total_distributed_dividend_balance_object_multi_index_type; typedef generic_index total_distributed_dividend_balance_object_index; - - - + + + /** * @ingroup object */ @@ -445,17 +410,17 @@ namespace graphene { namespace chain { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_lottery_balance_object_type; - + asset_id_type lottery_id; asset balance; - + asset get_balance()const { return balance; } void adjust_balance(const asset& delta); }; - - + + struct by_owner; - + /** * @ingroup object_index */ @@ -468,13 +433,13 @@ namespace graphene { namespace chain { > > > lottery_balance_index_type; - + /** * @ingroup object_index */ typedef generic_index lottery_balance_index; - - + + class sweeps_vesting_balance_object : public abstract_object { public: @@ -486,7 +451,7 @@ namespace graphene { namespace chain { uint64_t balance; asset_id_type asset_id; time_point_sec last_claim_date; - + uint64_t get_balance()const { return balance; } void adjust_balance(const asset& delta); asset available_for_claim() const { return asset( balance / SWEEPS_VESTING_BALANCE_MULTIPLIER , asset_id ); } @@ -516,7 +481,6 @@ FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::o (current_supply)(sweeps_tickets_sold)(confidential_supply)(accumulated_fees)(fee_pool) ) FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::object), - (asset_id) (feeds) (current_feed) (current_feed_publication_time) @@ -525,13 +489,11 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db:: (is_prediction_market) (settlement_price) (settlement_fund) - (asset_cer_updated) - (feed_cer_updated) ) - + FC_REFLECT_DERIVED( graphene::chain::asset_dividend_data_object, (graphene::db::object), (options) - (last_scheduled_payout_time) + (last_scheduled_payout_time) (last_payout_time ) (last_scheduled_distribution_time) (last_distribution_time) @@ -561,13 +523,3 @@ FC_REFLECT_DERIVED( graphene::chain::lottery_balance_object, (graphene::db::obje FC_REFLECT_DERIVED( graphene::chain::sweeps_vesting_balance_object, (graphene::db::object), (owner)(balance)(asset_id)(last_claim_date) ) - - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dynamic_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_bitasset_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dividend_data_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::total_distributed_dividend_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::lottery_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::sweeps_vesting_balance_object ) - diff --git a/libraries/chain/include/graphene/chain/balance_object.hpp b/libraries/chain/include/graphene/chain/balance_object.hpp index 38a1a649..8d531d0c 100644 --- a/libraries/chain/include/graphene/chain/balance_object.hpp +++ b/libraries/chain/include/graphene/chain/balance_object.hpp @@ -73,5 +73,3 @@ namespace graphene { namespace chain { FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object), (owner)(balance)(vesting_policy)(last_claim_date) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::balance_object ) diff --git a/libraries/chain/include/graphene/chain/block_database.hpp b/libraries/chain/include/graphene/chain/block_database.hpp index c5cf5df9..d902cd1b 100644 --- a/libraries/chain/include/graphene/chain/block_database.hpp +++ b/libraries/chain/include/graphene/chain/block_database.hpp @@ -25,8 +25,6 @@ #include #include -#include - namespace graphene { namespace chain { class index_entry; diff --git a/libraries/chain/include/graphene/chain/block_summary_object.hpp b/libraries/chain/include/graphene/chain/block_summary_object.hpp index 9f79d43e..f002c030 100644 --- a/libraries/chain/include/graphene/chain/block_summary_object.hpp +++ b/libraries/chain/include/graphene/chain/block_summary_object.hpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #pragma once -#include #include namespace graphene { namespace chain { @@ -48,7 +47,4 @@ namespace graphene { namespace chain { } } - FC_REFLECT_DERIVED( graphene::chain::block_summary_object, (graphene::db::object), (block_id) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::block_summary_object ) diff --git a/libraries/chain/include/graphene/chain/budget_record_object.hpp b/libraries/chain/include/graphene/chain/budget_record_object.hpp index 007d46a7..63784c71 100644 --- a/libraries/chain/include/graphene/chain/budget_record_object.hpp +++ b/libraries/chain/include/graphene/chain/budget_record_object.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include #include namespace graphene { namespace chain { @@ -55,6 +56,8 @@ struct budget_record share_type supply_delta = 0; }; +class budget_record_object; + class budget_record_object : public graphene::db::abstract_object { public: @@ -67,7 +70,8 @@ class budget_record_object : public graphene::db::abstract_object buyback_index; } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::buyback_object ) diff --git a/libraries/chain/include/graphene/chain/chain_property_object.hpp b/libraries/chain/include/graphene/chain/chain_property_object.hpp index 3c7a77ff..3d2c82a6 100644 --- a/libraries/chain/include/graphene/chain/chain_property_object.hpp +++ b/libraries/chain/include/graphene/chain/chain_property_object.hpp @@ -27,6 +27,8 @@ namespace graphene { namespace chain { +class chain_property_object; + /** * Contains invariants which are set at genesis and never changed. */ @@ -46,5 +48,3 @@ FC_REFLECT_DERIVED( graphene::chain::chain_property_object, (graphene::db::objec (chain_id) (immutable_parameters) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::chain_property_object ) diff --git a/libraries/chain/include/graphene/chain/committee_member_object.hpp b/libraries/chain/include/graphene/chain/committee_member_object.hpp index fe7968d3..7b0d8e75 100644 --- a/libraries/chain/include/graphene/chain/committee_member_object.hpp +++ b/libraries/chain/include/graphene/chain/committee_member_object.hpp @@ -29,6 +29,8 @@ namespace graphene { namespace chain { using namespace graphene::db; + class account_object; + /** * @brief tracks information about a committee_member account. * @ingroup object @@ -71,8 +73,5 @@ namespace graphene { namespace chain { using committee_member_index = generic_index; } } // graphene::chain - FC_REFLECT_DERIVED( graphene::chain::committee_member_object, (graphene::db::object), (committee_member_account)(vote_id)(total_votes)(url) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_object ) diff --git a/libraries/chain/include/graphene/chain/confidential_object.hpp b/libraries/chain/include/graphene/chain/confidential_object.hpp index acdb0ba5..f98e20a9 100644 --- a/libraries/chain/include/graphene/chain/confidential_object.hpp +++ b/libraries/chain/include/graphene/chain/confidential_object.hpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -49,6 +50,8 @@ class blinded_balance_object : public graphene::db::abstract_object get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const; std::vector get_seeds( asset_id_type for_asset, uint8_t count_winners )const; uint64_t get_random_bits( uint64_t bound ); @@ -307,7 +305,6 @@ namespace graphene { namespace chain { fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); bool is_son_dereg_valid( son_id_type son_id ); - const witness_schedule_object& get_witness_schedule_object()const; time_point_sec head_block_time()const; uint32_t head_block_num()const; @@ -465,8 +462,7 @@ namespace graphene { namespace chain { bool fill_order( const call_order_object& order, const asset& pays, const asset& receives ); bool fill_order( const force_settlement_object& settle, const asset& pays, const asset& receives ); - bool check_call_orders( const asset_object& mia, bool enable_black_swan = true, bool for_new_limit_order = false, - const asset_bitasset_data_object* bitasset_ptr = nullptr ); + bool check_call_orders( const asset_object& mia, bool enable_black_swan = true ); // helpers to fill_order void pay_order( const account_object& receiver, const asset& receives, const asset& pays ); @@ -475,7 +471,7 @@ namespace graphene { namespace chain { asset pay_market_fees( const asset_object& recv_asset, const asset& receives ); - ///@{ + ///@} /** * This method validates transactions without adding it to the pending state. * @return true if the transaction would validate @@ -490,13 +486,9 @@ 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; } 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(); } - void notify_applied_block( const signed_block& block ); - void notify_on_pending_transaction( const signed_transaction& tx ); void notify_changed_objects(); private: @@ -522,8 +514,6 @@ namespace graphene { namespace chain { const witness_object& validate_block_header( uint32_t skip, const signed_block& next_block )const; const witness_object& _validate_block_header( const signed_block& next_block )const; - void verify_signing_witness( const signed_block& new_block, const fork_item& fork_entry )const; - void update_witnesses( fork_item& fork_entry )const; void create_block_summary(const signed_block& next_block); //////////////////// db_witness_schedule.cpp //////////////////// @@ -538,13 +528,11 @@ namespace graphene { namespace chain { void clear_expired_proposals(); void clear_expired_orders(); void update_expired_feeds(); - void update_core_exchange_rates(); void update_maintenance_flag( bool new_maintenance_flag ); void update_withdraw_permissions(); void update_tournaments(); void update_betting_markets(fc::time_point_sec current_block_time); - bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, - const asset_bitasset_data_object* bitasset_ptr = nullptr ); + bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true ); ///Steps performed only at maintenance intervals ///@{ @@ -566,13 +554,9 @@ namespace graphene { namespace chain { void update_son_statuses( const vector& cur_active_sons, const vector& new_active_sons ); void update_son_wallet( const vector& new_active_sons ); void update_worker_votes(); - - public: - double calculate_vesting_factor(const account_object& stake_account); - uint32_t get_gpos_current_subperiod(); - - template - void perform_account_maintenance(Type tally_helper); + + template + void perform_account_maintenance(std::tuple helpers); ///@} ///@} @@ -612,11 +596,6 @@ namespace graphene { namespace chain { flat_map _checkpoints; node_property_object _node_property_object; - - /// Whether to update votes of standby witnesses and committee members when performing chain maintenance. - /// Set it to true to provide accurate data to API clients, set to false to have better performance. - bool _track_standby_votes = true; - fc::hash_ctr_rng _random_number_generator; bool _slow_replays = false; @@ -635,18 +614,6 @@ namespace graphene { namespace chain { void initialize_db_sidechain(); protected: private: - /// Tracks assets affected by bitshares-core issue #453 before hard fork #615 in one block - flat_set _issue_453_affected_assets; - - /// Pointers to core asset object and global objects who will have immutable addresses after created - ///@{ - const asset_object* _p_core_asset_obj = nullptr; - const asset_dynamic_data_object* _p_core_dynamic_data_obj = nullptr; - const global_property_object* _p_global_prop_obj = nullptr; - const dynamic_global_property_object* _p_dyn_global_prop_obj = nullptr; - const chain_property_object* _p_chain_property_obj = nullptr; - const witness_schedule_object* _p_witness_schedule_obj = nullptr; - ///@} }; namespace detail diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index ee264029..2e07ca26 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -65,21 +65,6 @@ msg \ ) -#define GRAPHENE_TRY_NOTIFY( signal, ... ) \ - try \ - { \ - signal( __VA_ARGS__ ); \ - } \ - catch( const graphene::chain::plugin_exception& e ) \ - { \ - elog( "Caught plugin exception: ${e}", ("e", e.to_detail_string() ) ); \ - throw; \ - } \ - catch( ... ) \ - { \ - wlog( "Caught unexpected exception in plugin" ); \ - } - namespace graphene { namespace chain { FC_DECLARE_EXCEPTION( chain_exception, 3000000, "blockchain exception" ) @@ -92,7 +77,6 @@ namespace graphene { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, graphene::chain::chain_exception, 3070000, "undo database exception" ) FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, graphene::chain::chain_exception, 3080000, "unlinkable block" ) FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, graphene::chain::chain_exception, 3090000, "black swan" ) - FC_DECLARE_DERIVED_EXCEPTION( plugin_exception, graphene::chain::chain_exception, 3100000, "plugin exception" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" ) diff --git a/libraries/chain/include/graphene/chain/fba_object.hpp b/libraries/chain/include/graphene/chain/fba_object.hpp index 3d1e1be0..aec9e9cd 100644 --- a/libraries/chain/include/graphene/chain/fba_object.hpp +++ b/libraries/chain/include/graphene/chain/fba_object.hpp @@ -49,7 +49,4 @@ class fba_accumulator_object : public graphene::db::abstract_object< fba_accumul } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), - (accumulated_fba_fees)(designated_asset) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fba_accumulator_object ) +FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), (accumulated_fba_fees)(designated_asset) ) diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index 4007ca09..8ca95b5e 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -51,11 +51,6 @@ namespace graphene { namespace chain { bool invalid = false; block_id_type id; signed_block data; - - // contains witness block signing keys scheduled *after* the block has been applied - shared_ptr< vector< pair< witness_id_type, public_key_type > > > scheduled_witnesses; - uint64_t next_block_aslot = 0; - fc::time_point_sec next_block_time; }; typedef shared_ptr item_ptr; diff --git a/libraries/chain/include/graphene/chain/genesis_state.hpp b/libraries/chain/include/graphene/chain/genesis_state.hpp index b2f76118..ebd153b6 100644 --- a/libraries/chain/include/graphene/chain/genesis_state.hpp +++ b/libraries/chain/include/graphene/chain/genesis_state.hpp @@ -23,7 +23,6 @@ */ #pragma once -#include #include #include #include @@ -170,32 +169,56 @@ struct genesis_state_type { } } // namespace graphene::chain -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_account_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_balance_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_vesting_balance_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_witness_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_committee_member_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_worker_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type::initial_bts_account_type) -FC_REFLECT_TYPENAME(graphene::chain::genesis_state_type) +FC_REFLECT(graphene::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)(is_lifetime_member)) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_account_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_asset_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_balance_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_vesting_balance_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_witness_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_committee_member_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_worker_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type::initial_bts_account_type) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::genesis_state_type) +FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type, + (symbol)(issuer_name)(description)(precision)(max_supply)(accumulated_fees)(is_bitasset)(collateral_records)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_asset_type::initial_collateral_position, + (owner)(collateral)(debt)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type, + (owner)(asset_symbol)(amount)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type, + (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay)) + +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority, + (weight_threshold) + (account_auths) + (key_auths) + (address_auths)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy, + (vesting_seconds) + (coin_seconds_earned) + (start_claim) + (coin_seconds_earned_last_update)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy, + (begin_timestamp) + (vesting_cliff_seconds) + (vesting_duration_seconds) + (begin_balance)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance, + (asset_symbol) + (amount) + (policy_type) + (policy)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type, + (name) + (owner_authority) + (active_authority) + (core_balance) + (vesting_balances)) + +FC_REFLECT(graphene::chain::genesis_state_type, + (initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances) + (initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates) + (initial_committee_candidates)(initial_worker_candidates) + (initial_chain_id) + (immutable_parameters)) diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index bb607b57..c34265cb 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -130,6 +130,7 @@ namespace graphene { namespace chain { }} FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object), + (random) (head_block_number) (head_block_id) (time) @@ -155,6 +156,3 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje (active_witnesses) (active_sons) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::dynamic_global_property_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::global_property_object ) diff --git a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp index f7128889..ade1a459 100644 --- a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp @@ -23,8 +23,11 @@ */ #pragma once +#include + +#include + #include -#include namespace graphene { namespace chain { @@ -46,5 +49,3 @@ FC_REFLECT( graphene::chain::immutable_chain_parameters, (num_special_accounts) (num_special_assets) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::immutable_chain_parameters ) diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index 4bd3e048..b56f4e9c 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -217,7 +217,3 @@ FC_REFLECT_DERIVED( graphene::chain::force_settlement_object, (graphene::db::object), (owner)(balance)(settlement_date) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::force_settlement_object ) diff --git a/libraries/chain/include/graphene/chain/operation_history_object.hpp b/libraries/chain/include/graphene/chain/operation_history_object.hpp index 89199472..d8b90b58 100644 --- a/libraries/chain/include/graphene/chain/operation_history_object.hpp +++ b/libraries/chain/include/graphene/chain/operation_history_object.hpp @@ -22,10 +22,8 @@ * THE SOFTWARE. */ #pragma once - #include #include - #include namespace graphene { namespace chain { @@ -96,22 +94,15 @@ namespace graphene { namespace chain { operation_history_id_type operation_id; uint32_t sequence = 0; /// the operation position within the given account account_transaction_history_id_type next; + + //std::pair account_op()const { return std::tie( account, operation_id ); } + //std::pair account_seq()const { return std::tie( account, sequence ); } }; struct by_id; struct by_seq; struct by_op; struct by_opid; - -typedef multi_index_container< - operation_history_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > - > -> operation_history_multi_index_type; - -typedef generic_index operation_history_index; - typedef multi_index_container< account_transaction_history_object, indexed_by< @@ -141,8 +132,6 @@ typedef generic_index #include -#include namespace graphene { namespace chain { - class database; + /** * @brief tracks the approval of a partially approved transaction @@ -98,5 +97,3 @@ FC_REFLECT_DERIVED( graphene::chain::proposal_object, (graphene::chain::object), (expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals) (available_active_approvals)(required_owner_approvals)(available_owner_approvals) (available_key_approvals)(proposer)(fail_reason)) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_object ) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 50ccb8ae..496e9067 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -59,12 +59,6 @@ namespace graphene { namespace chain { /// account's balance of core asset. flat_set votes; extensions_type extensions; - - /// Whether this account is voting - inline bool is_voting() const - { - return ( voting_account != GRAPHENE_PROXY_TO_SELF_ACCOUNT || !votes.empty() ); - } void validate()const; }; @@ -149,7 +143,6 @@ namespace graphene { namespace chain { optional< void_t > null_ext; optional< special_authority > owner_special_authority; optional< special_authority > active_special_authority; - optional< bool > update_last_voting_time; }; struct fee_parameters_type @@ -305,7 +298,7 @@ FC_REFLECT( graphene::chain::account_create_operation, (name)(owner)(active)(options)(extensions) ) -FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(update_last_voting_time) ) +FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) ) FC_REFLECT( graphene::chain::account_update_operation, (fee)(account)(owner)(active)(new_options)(extensions) ) @@ -320,16 +313,5 @@ FC_REFLECT( graphene::chain::account_whitelist_operation::fee_parameters_type, ( FC_REFLECT( graphene::chain::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) ) FC_REFLECT( graphene::chain::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) ) FC_REFLECT( graphene::chain::account_transfer_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_whitelist_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_upgrade_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_whitelist_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_upgrade_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::account_transfer_operation ) +FC_REFLECT( graphene::chain::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/address.hpp b/libraries/chain/include/graphene/chain/protocol/address.hpp index 8bf0fab6..b225b42c 100644 --- a/libraries/chain/include/graphene/chain/protocol/address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/address.hpp @@ -25,10 +25,14 @@ #include #include -#include +#include #include -#include + +namespace fc { namespace ecc { + class public_key; + typedef fc::array public_key_data; +} } // fc::ecc namespace graphene { namespace chain { @@ -47,7 +51,7 @@ namespace graphene { namespace chain { class address { public: - address(){} ///< constructs empty / null address + address(); ///< constructs empty / null address explicit address( const std::string& base58str ); ///< converts to binary, validates checksum address( const fc::ecc::public_key& pub ); ///< converts to binary explicit address( const fc::ecc::public_key_data& pub ); ///< converts to binary @@ -93,5 +97,3 @@ namespace std #include FC_REFLECT( graphene::chain::address, (addr) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::address ) diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index ce758862..c9f3b277 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -113,5 +112,3 @@ FC_REFLECT( graphene::chain::block_id_predicate, (id) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::assert_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::assert_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/asset.hpp b/libraries/chain/include/graphene/chain/protocol/asset.hpp index 60bd3cd0..a938129a 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset.hpp @@ -218,7 +218,3 @@ FC_REFLECT( graphene::chain::price, (base)(quote) ) (core_exchange_rate) FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::price ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::price_feed ) diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index ae5dc211..a567c5a1 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -764,30 +764,3 @@ FC_REFLECT( graphene::chain::asset_reserve_operation, FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) ); FC_REFLECT( graphene::chain::asset_dividend_distribution_operation, (fee)(dividend_asset_id)(account_id)(amounts)(extensions) ); - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::bitasset_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_global_settle_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_dividend_distribution_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_claim_fees_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_bitasset_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_feed_producers_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_publish_feed_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_issue_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_reserve_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_global_settle_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_settle_cancel_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_fund_fee_pool_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_claim_fees_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_bitasset_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_update_feed_producers_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_publish_feed_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_issue_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::asset_reserve_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/authority.hpp b/libraries/chain/include/graphene/chain/protocol/authority.hpp index d279402d..70b674b3 100644 --- a/libraries/chain/include/graphene/chain/protocol/authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/authority.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -135,5 +134,3 @@ void add_authority_accounts( FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) ) // FC_REFLECT_TYPENAME( graphene::chain::authority::classification ) FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::authority ) diff --git a/libraries/chain/include/graphene/chain/protocol/balance.hpp b/libraries/chain/include/graphene/chain/protocol/balance.hpp index 9d0b252f..f60087a7 100644 --- a/libraries/chain/include/graphene/chain/protocol/balance.hpp +++ b/libraries/chain/include/graphene/chain/protocol/balance.hpp @@ -23,8 +23,6 @@ */ #pragma once #include -#include -#include namespace graphene { namespace chain { @@ -59,5 +57,3 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::balance_claim_operation::fee_parameters_type, ) FC_REFLECT( graphene::chain::balance_claim_operation, (fee)(deposit_to_account)(balance_to_claim)(balance_owner_key)(total_claimed) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::balance_claim_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/base.hpp b/libraries/chain/include/graphene/chain/protocol/base.hpp index 23c285d3..52240b93 100644 --- a/libraries/chain/include/graphene/chain/protocol/base.hpp +++ b/libraries/chain/include/graphene/chain/protocol/base.hpp @@ -27,13 +27,8 @@ #include #include -#include - namespace graphene { namespace chain { - struct asset; - struct authority; - /** * @defgroup operations Operations * @ingroup transactions Transactions diff --git a/libraries/chain/include/graphene/chain/protocol/block.hpp b/libraries/chain/include/graphene/chain/protocol/block.hpp index ad5b0327..46ac0f6d 100644 --- a/libraries/chain/include/graphene/chain/protocol/block.hpp +++ b/libraries/chain/include/graphene/chain/protocol/block.hpp @@ -69,8 +69,3 @@ FC_REFLECT( graphene::chain::block_header, (extensions) ) FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) ) FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) ) - - -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::block_header) -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_block_header) -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_block) diff --git a/libraries/chain/include/graphene/chain/protocol/buyback.hpp b/libraries/chain/include/graphene/chain/protocol/buyback.hpp index 4a51e8c7..6adad52d 100644 --- a/libraries/chain/include/graphene/chain/protocol/buyback.hpp +++ b/libraries/chain/include/graphene/chain/protocol/buyback.hpp @@ -50,5 +50,3 @@ struct buyback_account_options } } FC_REFLECT( graphene::chain::buyback_account_options, (asset_to_buy)(asset_to_buy_issuer)(markets) ); - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::buyback_account_options ) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 42f6af1d..65b0d3c0 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -28,8 +28,6 @@ #include #include -#include <../hardfork.d/GPOS.hf> -#include namespace graphene { namespace chain { struct fee_schedule; } } @@ -41,16 +39,10 @@ namespace graphene { namespace chain { optional< uint16_t > betting_rake_fee_percentage; optional< flat_map > permitted_betting_odds_increments; optional< uint16_t > live_betting_delay_time; - optional< uint16_t > sweeps_distribution_percentage = SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE; - optional< asset_id_type > sweeps_distribution_asset = SWEEPS_DEFAULT_DISTRIBUTION_ASSET; - optional< account_id_type > sweeps_vesting_accumulator_account= SWEEPS_ACCUMULATOR_ACCOUNT; - /* gpos parameters */ - optional < uint32_t > gpos_period = GPOS_PERIOD; - optional < uint32_t > gpos_subperiod = GPOS_SUBPERIOD; - optional < uint32_t > gpos_period_start = HARDFORK_GPOS_TIME.sec_since_epoch(); - optional < uint32_t > gpos_vesting_lockin_period = GPOS_VESTING_LOCKIN_PERIOD; - optional < uint16_t > son_count; + optional< uint16_t > sweeps_distribution_percentage; + optional< asset_id_type > sweeps_distribution_asset; + optional< account_id_type > sweeps_vesting_accumulator_account; optional < uint32_t > son_vesting_amount; optional < uint32_t > son_vesting_period; optional < uint32_t > son_pay_daily_max; @@ -162,18 +154,6 @@ namespace graphene { namespace chain { inline uint16_t son_down_time()const { return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; } - inline uint32_t gpos_period()const { - return extensions.value.gpos_period.valid() ? *extensions.value.gpos_period : GPOS_PERIOD; /// total seconds of current gpos period - } - inline uint32_t gpos_subperiod()const { - return extensions.value.gpos_subperiod.valid() ? *extensions.value.gpos_subperiod : GPOS_SUBPERIOD; /// gpos_period % gpos_subperiod = 0 - } - inline uint32_t gpos_period_start()const { - return extensions.value.gpos_period_start.valid() ? *extensions.value.gpos_period_start : HARDFORK_GPOS_TIME.sec_since_epoch(); /// current period start date - } - inline uint32_t gpos_vesting_lockin_period()const { - return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period - } }; } } // graphene::chain @@ -188,17 +168,13 @@ FC_REFLECT( graphene::chain::parameter_extension, (sweeps_distribution_percentage) (sweeps_distribution_asset) (sweeps_vesting_accumulator_account) - (gpos_period) - (gpos_subperiod) - (gpos_period_start) - (gpos_vesting_lockin_period) (son_vesting_amount) (son_vesting_period) (son_pay_daily_max) (son_pay_time) (son_deregister_time) (son_heartbeat_frequency) - (son_down_time) + (son_down_time) ) FC_REFLECT( graphene::chain::chain_parameters, @@ -247,5 +223,3 @@ FC_REFLECT( graphene::chain::chain_parameters, (maximum_tournament_number_of_wins) (extensions) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::chain_parameters ) diff --git a/libraries/chain/include/graphene/chain/protocol/committee_member.hpp b/libraries/chain/include/graphene/chain/protocol/committee_member.hpp index 8aaed748..77188367 100644 --- a/libraries/chain/include/graphene/chain/protocol/committee_member.hpp +++ b/libraries/chain/include/graphene/chain/protocol/committee_member.hpp @@ -104,10 +104,3 @@ FC_REFLECT( graphene::chain::committee_member_create_operation, FC_REFLECT( graphene::chain::committee_member_update_operation, (fee)(committee_member)(committee_member_account)(new_url) ) FC_REFLECT( graphene::chain::committee_member_update_global_parameters_operation, (fee)(new_parameters) ); - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::committee_member_update_global_parameters_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/confidential.hpp b/libraries/chain/include/graphene/chain/protocol/confidential.hpp index 697ef35b..763006ae 100644 --- a/libraries/chain/include/graphene/chain/protocol/confidential.hpp +++ b/libraries/chain/include/graphene/chain/protocol/confidential.hpp @@ -281,10 +281,3 @@ FC_REFLECT( graphene::chain::blind_transfer_operation, FC_REFLECT( graphene::chain::transfer_to_blind_operation::fee_parameters_type, (fee)(price_per_output) ) FC_REFLECT( graphene::chain::transfer_from_blind_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::blind_transfer_operation::fee_parameters_type, (fee)(price_per_output) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_to_blind_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_from_blind_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::blind_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_to_blind_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_from_blind_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::blind_transfer_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/custom.hpp b/libraries/chain/include/graphene/chain/protocol/custom.hpp index 5596aaad..e5701a4b 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom.hpp @@ -56,6 +56,3 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::custom_operation::fee_parameters_type, (fee)(price_per_kbyte) ) FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::custom_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::custom_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/ext.hpp b/libraries/chain/include/graphene/chain/protocol/ext.hpp index 6c974630..31f66506 100644 --- a/libraries/chain/include/graphene/chain/protocol/ext.hpp +++ b/libraries/chain/include/graphene/chain/protocol/ext.hpp @@ -24,7 +24,6 @@ #pragma once #include -#include #include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/fba.hpp b/libraries/chain/include/graphene/chain/protocol/fba.hpp index dc672436..7460ca8d 100644 --- a/libraries/chain/include/graphene/chain/protocol/fba.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fba.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -46,5 +45,3 @@ struct fba_distribute_operation : public base_operation FC_REFLECT( graphene::chain::fba_distribute_operation::fee_parameters_type, ) FC_REFLECT( graphene::chain::fba_distribute_operation, (fee)(account_id)(fba_id)(amount) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fba_distribute_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 9baaffc7..e250ab17 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #pragma once -#include #include namespace graphene { namespace chain { @@ -86,5 +85,3 @@ namespace graphene { namespace chain { FC_REFLECT_TYPENAME( graphene::chain::fee_parameters ) FC_REFLECT( graphene::chain::fee_schedule, (parameters)(scale) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fee_schedule ) diff --git a/libraries/chain/include/graphene/chain/protocol/market.hpp b/libraries/chain/include/graphene/chain/protocol/market.hpp index 2bff8c56..56352c60 100644 --- a/libraries/chain/include/graphene/chain/protocol/market.hpp +++ b/libraries/chain/include/graphene/chain/protocol/market.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -166,15 +165,9 @@ FC_REFLECT( graphene::chain::limit_order_cancel_operation::fee_parameters_type, FC_REFLECT( graphene::chain::call_order_update_operation::fee_parameters_type, (fee) ) /// THIS IS THE ONLY VIRTUAL OPERATION THUS FAR... FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, ) + + FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions)) FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) ) FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) ) FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fill_order_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/memo.hpp b/libraries/chain/include/graphene/chain/protocol/memo.hpp index 6c5b69fb..b126d3a7 100644 --- a/libraries/chain/include/graphene/chain/protocol/memo.hpp +++ b/libraries/chain/include/graphene/chain/protocol/memo.hpp @@ -89,6 +89,3 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::memo_message, (checksum)(text) ) FC_REFLECT( graphene::chain::memo_data, (from)(to)(nonce)(message) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::memo_message ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::memo_data ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index caca89dd..d633056f 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -191,5 +191,3 @@ namespace graphene { namespace chain { FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT( graphene::chain::op_wrapper, (op) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::op_wrapper ) diff --git a/libraries/chain/include/graphene/chain/protocol/proposal.hpp b/libraries/chain/include/graphene/chain/protocol/proposal.hpp index 141ec35f..3383b6cf 100644 --- a/libraries/chain/include/graphene/chain/protocol/proposal.hpp +++ b/libraries/chain/include/graphene/chain/protocol/proposal.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { /** @@ -180,10 +179,3 @@ FC_REFLECT( graphene::chain::proposal_update_operation, (fee)(fee_paying_account (active_approvals_to_add)(active_approvals_to_remove)(owner_approvals_to_add)(owner_approvals_to_remove) (key_approvals_to_add)(key_approvals_to_remove)(extensions) ) FC_REFLECT( graphene::chain::proposal_delete_operation, (fee)(fee_paying_account)(using_owner_authority)(proposal)(extensions) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_delete_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::proposal_delete_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/special_authority.hpp b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp index 05a80719..3ee6f15f 100644 --- a/libraries/chain/include/graphene/chain/protocol/special_authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp @@ -48,5 +48,3 @@ void validate_special_authority( const special_authority& auth ); FC_REFLECT( graphene::chain::no_special_authority, ) FC_REFLECT( graphene::chain::top_holders_special_authority, (asset)(num_top_holders) ) FC_REFLECT_TYPENAME( graphene::chain::special_authority ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::top_holders_special_authority ) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 2a9909a5..95c39961 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -230,8 +230,3 @@ FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expi // 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) ) - - -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::transaction) -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::signed_transaction) -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::chain::processed_transaction) diff --git a/libraries/chain/include/graphene/chain/protocol/transfer.hpp b/libraries/chain/include/graphene/chain/protocol/transfer.hpp index 5366a7ab..f4417bb7 100644 --- a/libraries/chain/include/graphene/chain/protocol/transfer.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transfer.hpp @@ -24,7 +24,6 @@ #pragma once #include #include -#include namespace graphene { namespace chain { @@ -106,8 +105,3 @@ FC_REFLECT( graphene::chain::override_transfer_operation::fee_parameters_type, ( FC_REFLECT( graphene::chain::override_transfer_operation, (fee)(issuer)(from)(to)(amount)(memo)(extensions) ) FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo)(extensions) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::override_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transfer_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::override_transfer_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index f31cb3cf..1caf1f9c 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -27,15 +27,13 @@ #include #include #include -#include #include #include #include #include #include #include -#include -#include +#include #include #include #include @@ -44,34 +42,10 @@ #include #include #include +#include #include #include -#define GRAPHENE_EXTERNAL_SERIALIZATION(ext, type) \ -namespace fc { \ - ext template void from_variant( const variant& v, type& vo, uint32_t max_depth ); \ - ext template void to_variant( const type& v, variant& vo, uint32_t max_depth ); \ -namespace raw { \ - ext template void pack< datastream, type >( datastream& s, const type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ - ext template void pack< datastream, type >( datastream& s, const type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ - ext template void unpack< datastream, type >( datastream& s, type& tx, uint32_t _max_depth=FC_PACK_MAX_DEPTH ); \ -} } // fc::raw - -#define FC_REFLECT_DERIVED_NO_TYPENAME( TYPE, INHERITS, MEMBERS ) \ -namespace fc { \ -template<> struct reflector {\ - typedef TYPE type; \ - typedef fc::true_type is_defined; \ - typedef fc::false_type is_enum; \ - enum member_count_enum { \ - local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\ - total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\ - }; \ - FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ -}; \ -} // fc - - namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index d3eb9560..9fcbda66 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -23,24 +23,11 @@ */ #pragma once #include -#include namespace graphene { namespace chain { enum class vesting_balance_type { normal, gpos, son }; - inline std::string get_vesting_balance_type(vesting_balance_type type) { - switch (type) { - case vesting_balance_type::normal: - return "NORMAL"; - case vesting_balance_type::son: - return "SON"; - case vesting_balance_type::gpos: - default: - return "GPOS"; - } - } - struct linear_vesting_policy_initializer { /** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */ @@ -137,9 +124,4 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vestin FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) -FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son)) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation ) +FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 8a46954d..7ef2c8a1 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -24,7 +24,12 @@ #pragma once -#include +#include +#include +#include + +#include +#include namespace graphene { namespace chain { @@ -146,5 +151,3 @@ FC_REFLECT_TYPENAME( fc::flat_set ) FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp b/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp index 7963e99f..7bc905ac 100644 --- a/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp +++ b/libraries/chain/include/graphene/chain/protocol/withdraw_permission.hpp @@ -24,7 +24,6 @@ #pragma once #include #include -#include namespace graphene { namespace chain { @@ -180,12 +179,3 @@ FC_REFLECT( graphene::chain::withdraw_permission_update_operation, (fee)(withdra FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw_permission)(withdraw_from_account)(withdraw_to_account)(amount_to_withdraw)(memo) ); FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_claim_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_delete_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_claim_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_delete_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/witness.hpp b/libraries/chain/include/graphene/chain/protocol/witness.hpp index 2b5e88b0..b096e826 100644 --- a/libraries/chain/include/graphene/chain/protocol/witness.hpp +++ b/libraries/chain/include/graphene/chain/protocol/witness.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -85,8 +84,3 @@ FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(ur FC_REFLECT( graphene::chain::witness_update_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key)(new_initial_secret) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_update_operation ) diff --git a/libraries/chain/include/graphene/chain/protocol/worker.hpp b/libraries/chain/include/graphene/chain/protocol/worker.hpp index 11e0aa05..9e6eef35 100644 --- a/libraries/chain/include/graphene/chain/protocol/worker.hpp +++ b/libraries/chain/include/graphene/chain/protocol/worker.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include namespace graphene { namespace chain { @@ -105,5 +104,3 @@ FC_REFLECT( graphene::chain::worker_create_operation::fee_parameters_type, (fee) FC_REFLECT( graphene::chain::worker_create_operation, (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(name)(url)(initializer) ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_create_operation ) diff --git a/libraries/chain/include/graphene/chain/pts_address.hpp b/libraries/chain/include/graphene/chain/pts_address.hpp index c0bc80ff..636e2f11 100644 --- a/libraries/chain/include/graphene/chain/pts_address.hpp +++ b/libraries/chain/include/graphene/chain/pts_address.hpp @@ -24,8 +24,6 @@ #pragma once #include -#include -#include #include namespace fc { namespace ecc { class public_key; } } @@ -77,11 +75,4 @@ namespace fc { void to_variant( const graphene::chain::pts_address& var, fc::variant& vo, uint32_t max_depth = 1 ); void from_variant( const fc::variant& var, graphene::chain::pts_address& vo, uint32_t max_depth = 1 ); -namespace raw { - extern template void pack( datastream& s, const graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); - extern template void pack( datastream& s, const graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); - extern template void unpack( datastream& s, graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); -} } // fc::raw +} diff --git a/libraries/chain/include/graphene/chain/special_authority_object.hpp b/libraries/chain/include/graphene/chain/special_authority_object.hpp index 75093f3a..da9ecc5e 100644 --- a/libraries/chain/include/graphene/chain/special_authority_object.hpp +++ b/libraries/chain/include/graphene/chain/special_authority_object.hpp @@ -68,5 +68,3 @@ FC_REFLECT_DERIVED( (graphene::db::object), (account) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::special_authority_object ) diff --git a/libraries/chain/include/graphene/chain/transaction_object.hpp b/libraries/chain/include/graphene/chain/transaction_object.hpp index aaaa31f1..4f76d6be 100644 --- a/libraries/chain/include/graphene/chain/transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/transaction_object.hpp @@ -22,10 +22,12 @@ * THE SOFTWARE. */ #pragma once +#include #include #include #include +#include #include #include @@ -70,5 +72,3 @@ namespace graphene { namespace chain { } } FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(trx_id) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::transaction_object ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp index 9bb7520e..fccfbb75 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp @@ -46,7 +46,6 @@ class vesting_balance_withdraw_evaluator : public evaluator, - member, member_offset //member //member_offset >, composite_key_compare< std::less< asset_id_type >, - std::less< vesting_balance_type >, std::greater< share_type > //std::less< account_id_type > > @@ -255,7 +255,3 @@ FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::objec (policy) (balance_type) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::linear_vesting_policy ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::cdd_vesting_policy ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_object ) diff --git a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp index a6fee0c5..000573bd 100644 --- a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp +++ b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp @@ -114,5 +114,3 @@ FC_REFLECT_DERIVED( graphene::chain::withdraw_permission_object, (graphene::db:: (expiration) (claimed_this_period) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::withdraw_permission_object ) diff --git a/libraries/chain/include/graphene/chain/witness_object.hpp b/libraries/chain/include/graphene/chain/witness_object.hpp index 7928b46e..2d1b7666 100644 --- a/libraries/chain/include/graphene/chain/witness_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_object.hpp @@ -29,6 +29,8 @@ namespace graphene { namespace chain { using namespace graphene::db; + class witness_object; + class witness_object : public abstract_object { public: @@ -83,5 +85,3 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object), (total_missed) (last_confirmed_block_num) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_object ) diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index b934fd01..fc7d6d10 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -153,6 +153,3 @@ FC_REFLECT_DERIVED( (recent_slots_filled) (current_shuffled_sons) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_scheduler ) -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_schedule_object ) diff --git a/libraries/chain/include/graphene/chain/worker_object.hpp b/libraries/chain/include/graphene/chain/worker_object.hpp index 5e23f0b8..1219fc1c 100644 --- a/libraries/chain/include/graphene/chain/worker_object.hpp +++ b/libraries/chain/include/graphene/chain/worker_object.hpp @@ -22,9 +22,8 @@ * THE SOFTWARE. */ #pragma once -#include +#include #include -#include namespace graphene { namespace chain { @@ -176,5 +175,3 @@ FC_REFLECT_DERIVED( graphene::chain::worker_object, (graphene::db::object), (name) (url) ) - -GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::worker_object ) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 6664476f..1a8e2ee2 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -46,7 +46,15 @@ struct proposal_operation_hardfork_visitor template void operator()(const T &v) const {} - void operator()(const committee_member_update_global_parameters_operation &op) const {} + void operator()(const committee_member_update_global_parameters_operation &op) const { + if( block_time < HARDFORK_1000_TIME ) // TODO: remove after hf + FC_ASSERT( !op.new_parameters.extensions.value.min_bet_multiplier.valid() + && !op.new_parameters.extensions.value.max_bet_multiplier.valid() + && !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid() + && !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid() + && !op.new_parameters.extensions.value.live_betting_delay_time.valid(), + "Parameter extensions are not allowed yet!" ); + } void operator()(const graphene::chain::tournament_payout_operation &o) const { // TODO: move check into tournament_payout_operation::validate after HARDFORK_999_TIME @@ -152,11 +160,6 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" ); } - void operator()(const vesting_balance_create_operation &vbco) const { - if(block_time < HARDFORK_GPOS_TIME) - FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" ); - } - // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 1d5a8706..343edce2 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -37,7 +37,7 @@ bool proposal_object::is_authorized_to_execute(database& db) const [&]( account_id_type id ){ return &id(db).active; }, [&]( account_id_type id ){ return &id(db).owner; }, db.get_global_properties().parameters.max_authority_depth, - true, /* allow committee */ + true, /* allow committeee */ available_active_approvals, available_owner_approvals ); } @@ -90,5 +90,3 @@ void required_approval_index::object_removed( const object& obj ) } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::proposal_object ) diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index 2405369a..6721bb07 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -24,9 +24,6 @@ #include #include #include - -#include - namespace graphene { namespace chain { /** @@ -284,7 +281,6 @@ void account_update_operation::validate()const || new_options.valid() || extensions.value.owner_special_authority.valid() || extensions.value.active_special_authority.valid() - || extensions.value.update_last_voting_time.valid() ); FC_ASSERT( has_action ); @@ -330,15 +326,3 @@ void account_transfer_operation::validate()const } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_whitelist_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_upgrade_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_whitelist_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_upgrade_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transfer_operation ) diff --git a/libraries/chain/protocol/address.cpp b/libraries/chain/protocol/address.cpp index f0edbd49..19bb4df5 100644 --- a/libraries/chain/protocol/address.cpp +++ b/libraries/chain/protocol/address.cpp @@ -27,10 +27,9 @@ #include #include -#include - namespace graphene { namespace chain { + address::address(){} address::address( const std::string& base58str ) { @@ -111,5 +110,3 @@ namespace fc vo = graphene::chain::address( var.as_string() ); } } - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::address ) diff --git a/libraries/chain/protocol/assert.cpp b/libraries/chain/protocol/assert.cpp index 5ce61e45..60f26e3f 100644 --- a/libraries/chain/protocol/assert.cpp +++ b/libraries/chain/protocol/assert.cpp @@ -21,11 +21,7 @@ * 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 { @@ -66,7 +62,5 @@ share_type assert_operation::calculate_fee(const fee_parameters_type& k)const return k.fee * predicates.size(); } -} } // namespace graphene::chain -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::assert_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::assert_operation ) +} } // namespace graphene::chain diff --git a/libraries/chain/protocol/asset.cpp b/libraries/chain/protocol/asset.cpp index 525e193b..e1169b0c 100644 --- a/libraries/chain/protocol/asset.cpp +++ b/libraries/chain/protocol/asset.cpp @@ -24,7 +24,6 @@ #include #include #include -#include namespace graphene { namespace chain { typedef boost::multiprecision::uint128_t uint128_t; @@ -131,11 +130,7 @@ namespace graphene { namespace chain { return ~(asset( cp.numerator().convert_to(), debt.asset_id ) / asset( cp.denominator().convert_to(), collateral.asset_id )); } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) } - bool price::is_null() const - { - // Effectively same as "return *this == price();" but perhaps faster - return ( base.asset_id == asset_id_type() && quote.asset_id == asset_id_type() ); - } + bool price::is_null() const { return *this == price(); } void price::validate() const { try { @@ -207,7 +202,3 @@ const int64_t scaled_precision_lut[19] = }; } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::price ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::price_feed ) diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index 5dfd09ee..e4942aa4 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -24,8 +24,6 @@ #include #include -#include - namespace graphene { namespace chain { /** @@ -290,30 +288,3 @@ void lottery_asset_options::validate() const } } } // namespace graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::bitasset_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_global_settle_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_claim_fees_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_bitasset_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_feed_producers_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_publish_feed_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_issue_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_reserve_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_global_settle_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_settle_cancel_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_fund_fee_pool_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_claim_fees_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_dividend_distribution_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_bitasset_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_update_feed_producers_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_publish_feed_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_issue_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::asset_reserve_operation ) diff --git a/libraries/chain/protocol/authority.cpp b/libraries/chain/protocol/authority.cpp index 6cfed2ec..97470d33 100644 --- a/libraries/chain/protocol/authority.cpp +++ b/libraries/chain/protocol/authority.cpp @@ -23,7 +23,6 @@ */ #include -#include namespace graphene { namespace chain { @@ -37,5 +36,3 @@ void add_authority_accounts( } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::authority ) diff --git a/libraries/chain/protocol/block.cpp b/libraries/chain/protocol/block.cpp index 725ea3a7..d32365dd 100644 --- a/libraries/chain/protocol/block.cpp +++ b/libraries/chain/protocol/block.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include #include #include #include @@ -91,7 +90,3 @@ namespace graphene { namespace chain { } } } - -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::block_header) -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_block_header) -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_block) diff --git a/libraries/chain/protocol/committee_member.cpp b/libraries/chain/protocol/committee_member.cpp index 1824870a..4c8c5d25 100644 --- a/libraries/chain/protocol/committee_member.cpp +++ b/libraries/chain/protocol/committee_member.cpp @@ -21,12 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include #include -#include - namespace graphene { namespace chain { void committee_member_create_operation::validate()const @@ -49,10 +45,3 @@ void committee_member_update_global_parameters_operation::validate() const } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_global_parameters_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_update_global_parameters_operation ) diff --git a/libraries/chain/protocol/confidential.cpp b/libraries/chain/protocol/confidential.cpp index 2e8fbc68..603befa1 100644 --- a/libraries/chain/protocol/confidential.cpp +++ b/libraries/chain/protocol/confidential.cpp @@ -27,6 +27,7 @@ #include #include +#include namespace graphene { namespace chain { @@ -140,6 +141,9 @@ share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k return k.fee + outputs.size() * k.price_per_output; } + + + /** * Packs *this then encodes as base58 encoded string. */ @@ -155,11 +159,6 @@ stealth_confirmation::stealth_confirmation( const std::string& base58 ) *this = fc::raw::unpack( fc::from_base58( base58 ) ); } -} } // graphene::chain -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation ) + +} } // graphene::chain diff --git a/libraries/chain/protocol/custom.cpp b/libraries/chain/protocol/custom.cpp index 72f8dd44..b69243be 100644 --- a/libraries/chain/protocol/custom.cpp +++ b/libraries/chain/protocol/custom.cpp @@ -23,8 +23,6 @@ */ #include -#include - namespace graphene { namespace chain { void custom_operation::validate()const @@ -37,6 +35,3 @@ share_type custom_operation::calculate_fee(const fee_parameters_type& k)const } } } - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::custom_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::custom_operation ) diff --git a/libraries/chain/protocol/fee_schedule.cpp b/libraries/chain/protocol/fee_schedule.cpp index 6d494e37..138d801e 100644 --- a/libraries/chain/protocol/fee_schedule.cpp +++ b/libraries/chain/protocol/fee_schedule.cpp @@ -35,8 +35,6 @@ namespace fc //template const graphene::chain::fee_schedule& smart_ref::operator*() const; } -#include - #define MAX_FEE_STABILIZATION_ITERATION 4 namespace graphene { namespace chain { @@ -210,5 +208,3 @@ namespace graphene { namespace chain { } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fee_schedule ) diff --git a/libraries/chain/protocol/market.cpp b/libraries/chain/protocol/market.cpp index ae0a3a68..923f4763 100644 --- a/libraries/chain/protocol/market.cpp +++ b/libraries/chain/protocol/market.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include namespace graphene { namespace chain { @@ -47,11 +46,3 @@ void call_order_update_operation::validate()const } FC_CAPTURE_AND_RETHROW((*this)) } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_cancel_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_cancel_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fill_order_operation ) diff --git a/libraries/chain/protocol/memo.cpp b/libraries/chain/protocol/memo.cpp index afa0b486..e04b5e43 100644 --- a/libraries/chain/protocol/memo.cpp +++ b/libraries/chain/protocol/memo.cpp @@ -23,7 +23,6 @@ */ #include #include -#include namespace graphene { namespace chain { @@ -89,6 +88,3 @@ memo_message memo_message::deserialize(const string& serial) } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::memo_message ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::memo_data ) diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 7db51078..40a37eba 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -21,10 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include -#include -#include namespace graphene { namespace chain { @@ -88,5 +85,3 @@ void operation_get_required_authorities( const operation& op, } } } // namespace graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::op_wrapper ) diff --git a/libraries/chain/protocol/proposal.cpp b/libraries/chain/protocol/proposal.cpp index c77e71e4..069824af 100644 --- a/libraries/chain/protocol/proposal.cpp +++ b/libraries/chain/protocol/proposal.cpp @@ -25,8 +25,6 @@ #include #include -#include - namespace graphene { namespace chain { proposal_create_operation proposal_create_operation::committee_proposal(const chain_parameters& global_params, fc::time_point_sec head_block_time ) @@ -107,10 +105,3 @@ void proposal_update_operation::get_required_owner_authorities( flat_set -#include -#include -#include -#include -#include -#include - -#include - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::balance_claim_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::buyback_account_options ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fba_distribute_operation ) - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_withdraw_operation ) - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::chain_parameters ) diff --git a/libraries/chain/protocol/tournament.cpp b/libraries/chain/protocol/tournament.cpp index 78ab4c01..57e80bf3 100644 --- a/libraries/chain/protocol/tournament.cpp +++ b/libraries/chain/protocol/tournament.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include namespace graphene { namespace chain { diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 093e7833..a11e3335 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -27,7 +27,6 @@ #include #include #include -#include namespace graphene { namespace chain { @@ -391,7 +390,3 @@ void signed_transaction::verify_authority( } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::transaction) -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::signed_transaction) -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::chain::processed_transaction) diff --git a/libraries/chain/protocol/transfer.cpp b/libraries/chain/protocol/transfer.cpp index 0fb0aefa..3dfe4eb7 100644 --- a/libraries/chain/protocol/transfer.cpp +++ b/libraries/chain/protocol/transfer.cpp @@ -23,8 +23,6 @@ */ #include -#include - namespace graphene { namespace chain { share_type transfer_operation::calculate_fee( const fee_parameters_type& schedule )const @@ -63,8 +61,3 @@ void override_transfer_operation::validate()const } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::override_transfer_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::override_transfer_operation ) diff --git a/libraries/chain/protocol/vote.cpp b/libraries/chain/protocol/vote.cpp index 68f476f5..f78f2b4f 100644 --- a/libraries/chain/protocol/vote.cpp +++ b/libraries/chain/protocol/vote.cpp @@ -49,5 +49,3 @@ void from_variant( const variant& var, graphene::chain::vote_id_type& vo, uint32 } } // fc - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vote_id_type ) diff --git a/libraries/chain/protocol/withdraw_permission.cpp b/libraries/chain/protocol/withdraw_permission.cpp index b36c378d..33b40c85 100644 --- a/libraries/chain/protocol/withdraw_permission.cpp +++ b/libraries/chain/protocol/withdraw_permission.cpp @@ -23,8 +23,6 @@ */ #include -#include - namespace graphene { namespace chain { void withdraw_permission_update_operation::validate()const @@ -67,13 +65,6 @@ void withdraw_permission_delete_operation::validate() const FC_ASSERT( withdraw_from_account != authorized_account ); } + } } // graphene::chain -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_claim_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_delete_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_update_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_claim_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_delete_operation ) diff --git a/libraries/chain/protocol/witness.cpp b/libraries/chain/protocol/witness.cpp index 90583cd8..82fa462a 100644 --- a/libraries/chain/protocol/witness.cpp +++ b/libraries/chain/protocol/witness.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include namespace graphene { namespace chain { @@ -40,8 +39,3 @@ void witness_update_operation::validate() const } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_update_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_create_operation ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_update_operation ) diff --git a/libraries/chain/protocol/worker.cpp b/libraries/chain/protocol/worker.cpp index 932148ec..eb133da0 100644 --- a/libraries/chain/protocol/worker.cpp +++ b/libraries/chain/protocol/worker.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include -#include namespace graphene { namespace chain { @@ -37,6 +36,3 @@ void worker_create_operation::validate() const } } } - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_create_operation::fee_parameters_type ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_create_operation ) diff --git a/libraries/chain/pts_address.cpp b/libraries/chain/pts_address.cpp index c6d74f58..27f3d256 100644 --- a/libraries/chain/pts_address.cpp +++ b/libraries/chain/pts_address.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include namespace graphene { namespace chain { @@ -98,12 +97,4 @@ namespace fc { vo = graphene::chain::pts_address( var.as_string() ); } - -namespace raw { - template void pack( datastream& s, const graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); - template void pack( datastream& s, const graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); - template void unpack( datastream& s, graphene::chain::pts_address& tx, - uint32_t _max_depth=FC_PACK_MAX_DEPTH ); -} } // fc::raw +} diff --git a/libraries/chain/small_objects.cpp b/libraries/chain/small_objects.cpp deleted file mode 100644 index a74fa116..00000000 --- a/libraries/chain/small_objects.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019 BitShares Blockchain 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::block_summary_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::budget_record ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::budget_record_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::buyback_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::immutable_chain_parameters ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::call_order_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::force_settlement_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::chain_property_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::committee_member_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blinded_balance_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::fba_accumulator_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::dynamic_global_property_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::global_property_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::operation_history_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::account_transaction_history_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::special_authority_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transaction_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::withdraw_permission_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_scheduler ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::witness_schedule_object ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::worker_object ) diff --git a/libraries/chain/special_authority.cpp b/libraries/chain/special_authority.cpp index 74889f80..ca974f30 100644 --- a/libraries/chain/special_authority.cpp +++ b/libraries/chain/special_authority.cpp @@ -25,8 +25,6 @@ #include #include -#include - namespace graphene { namespace chain { struct special_authority_validate_visitor @@ -70,6 +68,3 @@ void evaluate_special_authority( const database& db, const special_authority& a } } } // graphene::chain - - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::top_holders_special_authority ) diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index dc91a449..cc82aa3e 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -42,12 +42,12 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); + if(d.head_block_time() < HARDFORK_SON_TIME) // Todo: can be removed after gpos hf time pass + FC_ASSERT( op.balance_type == vesting_balance_type::normal); + if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() ); - if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass - FC_ASSERT( op.balance_type == vesting_balance_type::normal); - return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -103,70 +103,23 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance // If making changes to this logic, check if those changes should also be made there as well. obj.owner = op.owner; obj.balance = op.amount; - if(op.balance_type == vesting_balance_type::gpos) - { - const auto &gpo = d.get_global_properties(); - // forcing gpos policy - linear_vesting_policy p; - p.begin_timestamp = now; - p.vesting_cliff_seconds = gpo.parameters.gpos_vesting_lockin_period(); - p.vesting_duration_seconds = gpo.parameters.gpos_subperiod(); - obj.policy = p; - } - else { - op.policy.visit(init_policy_visitor(obj.policy, op.amount.amount, now)); - } obj.balance_type = op.balance_type; + op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); return vbo.id; } FC_CAPTURE_AND_RETHROW( (op) ) } -operation_result vesting_balance_withdraw_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) -{ try { - trx_state = &eval_state; - const auto& oper = op.get(); - - //check_required_authorities(op); - auto result = evaluate( oper ); - - if( apply ) result = this->apply( oper ); - return result; -} FC_CAPTURE_AND_RETHROW() } - void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balance_withdraw_operation& op ) { try { const database& d = db(); const time_point_sec now = d.head_block_time(); const vesting_balance_object& vbo = op.vesting_balance( d ); - if(vbo.balance_type == vesting_balance_type::normal) - { - FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) ); - FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "Account has insufficient ${balance_type} Vested Balance to withdraw", - ("balance_type", get_vesting_balance_type(vbo.balance_type))("now", now)("op", op)("vbo", vbo) ); - assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached - } - else if(now > HARDFORK_GPOS_TIME && vbo.balance_type == vesting_balance_type::gpos) - { - const account_id_type account_id = op.owner; - vector vbos; - auto vesting_range = d.get_index_type().indices().get().equal_range(account_id); - std::for_each(vesting_range.first, vesting_range.second, - [&vbos, now](const vesting_balance_object& balance) { - if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos - && balance.is_withdraw_allowed(now, balance.balance.amount) && balance.balance.asset_id == asset_id_type()) - vbos.emplace_back(balance); - }); + FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) ); + FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) ); + assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached - asset total_amount; - for (const vesting_balance_object& vesting_balance_obj : vbos) - { - total_amount += vesting_balance_obj.balance.amount; - } - FC_ASSERT( op.amount <= total_amount, "Account has either insufficient GPOS Vested Balance or lock-in period is not matured"); - } - - /* const account_object& owner_account = op.owner( d ); */ + /* const account_object& owner_account = */ op.owner( d ); // TODO: Check asset authorizations and withdrawals return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -174,55 +127,22 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_withdraw_operation& op ) { try { database& d = db(); - const time_point_sec now = d.head_block_time(); - //Handling all GPOS withdrawls separately from normal and SONs(future extension). - // One request/transaction would be sufficient to withdraw from multiple vesting balance ids + const vesting_balance_object& vbo = op.vesting_balance( d ); - if(vbo.balance_type == vesting_balance_type::normal) + + // Allow zero balance objects to stick around, (1) to comply + // with the chain's "objects live forever" design principle, (2) + // if it's cashback or worker, it'll be filled up again. + + d.modify( vbo, [&]( vesting_balance_object& vbo ) { - // Allow zero balance objects to stick around, (1) to comply - // with the chain's "objects live forever" design principle, (2) - // if it's cashback or worker, it'll be filled up again. + vbo.withdraw( now, op.amount ); + } ); - d.modify( vbo, [&]( vesting_balance_object& vbo ) - { - vbo.withdraw( now, op.amount ); - } ); - - d.adjust_balance( op.owner, op.amount ); - } - else if(now > HARDFORK_GPOS_TIME && vbo.balance_type == vesting_balance_type::gpos) - { - const account_id_type account_id = op.owner; - vector ids; - auto vesting_range = d.get_index_type().indices().get().equal_range(account_id); - std::for_each(vesting_range.first, vesting_range.second, - [&ids, now](const vesting_balance_object& balance) { - if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos - && balance.is_withdraw_allowed(now, balance.balance.amount) && balance.balance.asset_id == asset_id_type()) - ids.emplace_back(balance.id); - }); - - asset total_withdraw_amount = op.amount; - for (const vesting_balance_id_type& id : ids) - { - const vesting_balance_object& vbo = id( d ); - if(total_withdraw_amount.amount > vbo.balance.amount) - { - total_withdraw_amount.amount -= vbo.balance.amount; - d.adjust_balance( op.owner, vbo.balance ); - d.modify( vbo, [&]( vesting_balance_object& vbo ) {vbo.withdraw( now, vbo.balance );} ); - } - else - { - d.modify( vbo, [&]( vesting_balance_object& vbo ) {vbo.withdraw( now, total_withdraw_amount );} ); - d.adjust_balance( op.owner, total_withdraw_amount); - break; - } - } - } + d.adjust_balance( op.owner, op.amount ); + // TODO: Check asset authorizations and withdrawals return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 3334d4f6..742482ce 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -24,8 +24,6 @@ #include -#include - namespace graphene { namespace chain { inline bool sum_below_max_shares(const asset& a, const asset& b) @@ -47,33 +45,23 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& if( elapsed_seconds >= vesting_cliff_seconds ) { - // BLOCKBACK-154 fix, Begin balance for linear vesting applies only to initial account balance from genesis - // So, for any GPOS vesting, the begin balance would be 0 and should be able to withdraw balance amount based on lockin period - if(begin_balance == 0) + share_type total_vested = 0; + if( elapsed_seconds < vesting_duration_seconds ) { - allowed_withdraw = ctx.balance.amount; - return asset( allowed_withdraw, ctx.balance.asset_id ); + total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); } else { - share_type total_vested = 0; - if( elapsed_seconds < vesting_duration_seconds ) - { - total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); - } - else - { - total_vested = begin_balance; - } - assert( total_vested >= 0 ); - - const share_type withdrawn_already = begin_balance - ctx.balance.amount; - assert( withdrawn_already >= 0 ); - - allowed_withdraw = total_vested - withdrawn_already; - assert( allowed_withdraw >= 0 ); + total_vested = begin_balance; } - } + assert( total_vested >= 0 ); + + const share_type withdrawn_already = begin_balance - ctx.balance.amount; + assert( withdrawn_already >= 0 ); + + allowed_withdraw = total_vested - withdrawn_already; + assert( allowed_withdraw >= 0 ); + } } return asset( allowed_withdraw, ctx.balance.asset_id ); @@ -277,7 +265,3 @@ asset vesting_balance_object::get_allowed_withdraw(const time_point_sec& now)con } } } // graphene::chain - -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::linear_vesting_policy ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::cdd_vesting_policy ) -GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::vesting_balance_object ) diff --git a/libraries/chain/worker_evaluator.cpp b/libraries/chain/worker_evaluator.cpp index b5aea8f3..cf6f0e00 100644 --- a/libraries/chain/worker_evaluator.cpp +++ b/libraries/chain/worker_evaluator.cpp @@ -106,7 +106,7 @@ object_id_type worker_create_evaluator::do_apply(const worker_create_evaluator:: void refund_worker_type::pay_worker(share_type pay, database& db) { total_burned += pay; - db.modify( db.get_core_dynamic_data(), [pay](asset_dynamic_data_object& d) { + db.modify(db.get(asset_id_type()).dynamic_data(db), [pay](asset_dynamic_data_object& d) { d.current_supply -= pay; }); } diff --git a/libraries/egenesis/egenesis_none.cpp b/libraries/egenesis/egenesis_none.cpp index c7a0dcdd..825f7f83 100644 --- a/libraries/egenesis/egenesis_none.cpp +++ b/libraries/egenesis/egenesis_none.cpp @@ -24,8 +24,6 @@ #include -#include - namespace graphene { namespace egenesis { using namespace graphene::chain; diff --git a/libraries/fc b/libraries/fc index a76b9ff8..f13d0632 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit a76b9ff81c6887ebe1dc9fa03ef15e1433029c65 +Subproject commit f13d0632b08b9983a275304317a033914938e339 diff --git a/libraries/net/CMakeLists.txt b/libraries/net/CMakeLists.txt index 82522e5a..f7f549ed 100644 --- a/libraries/net/CMakeLists.txt +++ b/libraries/net/CMakeLists.txt @@ -5,7 +5,6 @@ set(SOURCES node.cpp core_messages.cpp peer_database.cpp peer_connection.cpp - message.cpp message_oriented_connection.cpp) add_library( graphene_net ${SOURCES} ${HEADERS} ) diff --git a/libraries/net/include/graphene/net/message.hpp b/libraries/net/include/graphene/net/message.hpp index 686fea24..cfef1519 100644 --- a/libraries/net/include/graphene/net/message.hpp +++ b/libraries/net/include/graphene/net/message.hpp @@ -22,16 +22,12 @@ * THE SOFTWARE. */ #pragma once -#include - -#include - #include #include #include -#include +#include #include -#include +#include namespace graphene { namespace net { @@ -112,10 +108,10 @@ namespace graphene { namespace net { } }; + + + } } // graphene::net FC_REFLECT( graphene::net::message_header, (size)(msg_type) ) FC_REFLECT_DERIVED( graphene::net::message, (graphene::net::message_header), (data) ) - -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::message_header) -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::message) diff --git a/libraries/net/include/graphene/net/peer_connection.hpp b/libraries/net/include/graphene/net/peer_connection.hpp index 61f1cef5..6f9a4b20 100644 --- a/libraries/net/include/graphene/net/peer_connection.hpp +++ b/libraries/net/include/graphene/net/peer_connection.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,9 @@ #include #include #include +#include #include +#include #include #include @@ -261,13 +264,13 @@ namespace graphene { namespace net fc::future accept_or_connect_task_done; firewall_check_state_data *firewall_check_state = nullptr; - - private: #ifndef NDEBUG + private: fc::thread* _thread = nullptr; unsigned _send_message_queue_tasks_running = 0; // temporary debugging #endif bool _currently_handling_message = false; // true while we're in the middle of handling a message from the remote system + private: peer_connection(peer_connection_delegate* delegate); void destroy(); public: diff --git a/libraries/net/include/graphene/net/peer_database.hpp b/libraries/net/include/graphene/net/peer_database.hpp index ff7f4036..d0a06dd9 100644 --- a/libraries/net/include/graphene/net/peer_database.hpp +++ b/libraries/net/include/graphene/net/peer_database.hpp @@ -24,14 +24,13 @@ #pragma once #include -#include - #include #include #include #include #include #include +#include namespace graphene { namespace net { @@ -119,6 +118,5 @@ namespace graphene { namespace net { } } // end namespace graphene::net -FC_REFLECT_TYPENAME( graphene::net::potential_peer_record ) - -GRAPHENE_EXTERNAL_SERIALIZATION(extern, graphene::net::potential_peer_record) +FC_REFLECT_ENUM(graphene::net::potential_peer_last_connection_disposition, (never_attempted_to_connect)(last_connection_failed)(last_connection_rejected)(last_connection_handshaking_failed)(last_connection_succeeded)) +FC_REFLECT(graphene::net::potential_peer_record, (endpoint)(last_seen_time)(last_connection_disposition)(last_connection_attempt_time)(number_of_successful_connection_attempts)(number_of_failed_connection_attempts)(last_error) ) diff --git a/libraries/net/message.cpp b/libraries/net/message.cpp deleted file mode 100644 index 6d35bfe5..00000000 --- a/libraries/net/message.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019 BitShares Blockchain 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 - -#include - -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::message_header) -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::message) diff --git a/libraries/net/message_oriented_connection.cpp b/libraries/net/message_oriented_connection.cpp index 1bc1832e..5dea08d4 100644 --- a/libraries/net/message_oriented_connection.cpp +++ b/libraries/net/message_oriented_connection.cpp @@ -62,8 +62,7 @@ namespace graphene { namespace net { fc::time_point _last_message_received_time; fc::time_point _last_message_sent_time; - std::atomic_bool _send_message_in_progress; - std::atomic_bool _read_loop_in_progress; + bool _send_message_in_progress; #ifndef NDEBUG fc::thread* _thread; #endif @@ -99,8 +98,7 @@ namespace graphene { namespace net { _delegate(delegate), _bytes_received(0), _bytes_sent(0), - _send_message_in_progress(false), - _read_loop_in_progress(false) + _send_message_in_progress(false) #ifndef NDEBUG ,_thread(&fc::thread::current()) #endif @@ -140,21 +138,6 @@ namespace graphene { namespace net { _sock.bind(local_endpoint); } - class no_parallel_execution_guard final - { - std::atomic_bool* _flag; - public: - explicit no_parallel_execution_guard(std::atomic_bool* flag) : _flag(flag) - { - bool expected = false; - FC_ASSERT( flag->compare_exchange_strong( expected, true ), "Only one thread at time can visit it"); - } - ~no_parallel_execution_guard() - { - *_flag = false; - } - }; - void message_oriented_connection_impl::read_loop() { VERIFY_CORRECT_THREAD(); @@ -162,7 +145,6 @@ namespace graphene { namespace net { const int LEFTOVER = BUFFER_SIZE - sizeof(message_header); static_assert(BUFFER_SIZE >= sizeof(message_header), "insufficient buffer"); - no_parallel_execution_guard guard( &_read_loop_in_progress ); _connected_time = fc::time_point::now(); fc::oexception exception_to_rethrow; @@ -259,7 +241,17 @@ namespace graphene { namespace net { } send_message_scope_logger(remote_endpoint); #endif #endif - no_parallel_execution_guard guard( &_send_message_in_progress ); + struct verify_no_send_in_progress { + bool& var; + verify_no_send_in_progress(bool& var) : var(var) + { + if (var) + elog("Error: two tasks are calling message_oriented_connection::send_message() at the same time"); + assert(!var); + var = true; + } + ~verify_no_send_in_progress() { var = false; } + } _verify_no_send_in_progress(_send_message_in_progress); try { diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 0fc61dde..a38199fd 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -66,7 +66,6 @@ #include #include #include -#include #include #include #include @@ -1250,7 +1249,7 @@ namespace graphene { namespace net { namespace detail { for (const peer_connection_ptr& peer : _active_connections) { // only advertise to peers who are in sync with us - idump((peer->peer_needs_sync_items_from_us)); + wdump((peer->peer_needs_sync_items_from_us)); if( !peer->peer_needs_sync_items_from_us ) { std::map > items_to_advertise_by_type; @@ -1258,7 +1257,7 @@ namespace graphene { namespace net { namespace detail { // or anything it has advertised to us // group the items we need to send by type, because we'll need to send one inventory message per type unsigned total_items_to_send_to_this_peer = 0; - idump((inventory_to_advertise)); + wdump((inventory_to_advertise)); for (const item_id& item_to_advertise : inventory_to_advertise) { auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise); @@ -1277,9 +1276,9 @@ namespace graphene { namespace net { namespace detail { else { if (adv_to_peer != peer->inventory_advertised_to_peer.end() ) - idump( (*adv_to_peer) ); + wdump( (*adv_to_peer) ); if (adv_to_us != peer->inventory_peer_advertised_to_us.end() ) - idump( (*adv_to_us) ); + wdump( (*adv_to_us) ); } } dlog("advertising ${count} new item(s) of ${types} type(s) to peer ${endpoint}", @@ -2279,7 +2278,7 @@ namespace graphene { namespace net { namespace detail { bool disconnect_from_inhibited_peer = false; // if our client doesn't have any items after the item the peer requested, it will send back // a list containing the last item the peer requested - idump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis)); + wdump((reply_message)(fetch_blockchain_item_ids_message_received.blockchain_synopsis)); if( reply_message.item_hashes_available.empty() ) originating_peer->peer_needs_sync_items_from_us = false; /* I have no items in my blockchain */ else if( !fetch_blockchain_item_ids_message_received.blockchain_synopsis.empty() && @@ -2650,6 +2649,11 @@ namespace graphene { namespace net { namespace detail { if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty()) assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back()); + // append the remaining items to the peer's list + boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received); + + originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count; + // at any given time, there's a maximum number of blocks that can possibly be out there // [(now - genesis time) / block interval]. If they offer us more blocks than that, // they must be an attacker or have a buggy client. @@ -2672,12 +2676,6 @@ namespace graphene { namespace net { namespace detail { return; } - - // append the remaining items to the peer's list - boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received); - - originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count; - uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers(); if (new_number_of_unfetched_items != _total_number_of_unfetched_items) _delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type, @@ -2937,7 +2935,7 @@ namespace graphene { namespace net { namespace detail { if( closing_connection_message_received.closing_due_to_error ) { - wlog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", + elog( "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", ( "peer", originating_peer->get_remote_endpoint() ) ( "msg", closing_connection_message_received.reason_for_closing ) ( "error", closing_connection_message_received.error ) ); diff --git a/libraries/net/peer_connection.cpp b/libraries/net/peer_connection.cpp index 9b753e6c..f1f20d3f 100644 --- a/libraries/net/peer_connection.cpp +++ b/libraries/net/peer_connection.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -261,7 +260,7 @@ namespace graphene { namespace net } catch ( fc::exception& e ) { - wlog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) ); + elog( "fatal: error connecting to peer ${remote_endpoint}: ${e}", ("remote_endpoint", remote_endpoint )("e", e.to_detail_string() ) ); throw; } } // connect_to() @@ -313,24 +312,24 @@ namespace graphene { namespace net } catch (const fc::exception& send_error) { - wlog("Error sending message: ${exception}. Closing connection.", ("exception", send_error)); + elog("Error sending message: ${exception}. Closing connection.", ("exception", send_error)); try { close_connection(); } catch (const fc::exception& close_error) { - wlog("Caught error while closing connection: ${exception}", ("exception", close_error)); + elog("Caught error while closing connection: ${exception}", ("exception", close_error)); } return; } catch (const std::exception& e) { - wlog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what())); + elog("message_oriented_exception::send_message() threw a std::exception(): ${what}", ("what", e.what())); } catch (...) { - wlog("message_oriented_exception::send_message() threw an unhandled exception"); + elog("message_oriented_exception::send_message() threw an unhandled exception"); } _queued_messages.front()->transmission_finish_time = fc::time_point::now(); _total_queued_messages_size -= _queued_messages.front()->get_size_in_queue(); @@ -346,7 +345,7 @@ namespace graphene { namespace net _queued_messages.emplace(std::move(message_to_send)); if (_total_queued_messages_size > GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES) { - wlog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)", + elog("send queue exceeded maximum size of ${max} bytes (current size ${current} bytes)", ("max", GRAPHENE_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES)("current", _total_queued_messages_size)); try { diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 76ae9c8c..2b20364e 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -274,14 +274,3 @@ namespace graphene { namespace net { } } } // end namespace graphene::net - -FC_REFLECT_ENUM( graphene::net::potential_peer_last_connection_disposition, - (never_attempted_to_connect) - (last_connection_failed)(last_connection_rejected) - (last_connection_handshaking_failed)(last_connection_succeeded) ) -FC_REFLECT_DERIVED_NO_TYPENAME( graphene::net::potential_peer_record, BOOST_PP_SEQ_NIL, - (endpoint)(last_seen_time)(last_connection_disposition) - (last_connection_attempt_time)(number_of_successful_connection_attempts) - (number_of_failed_connection_attempts)(last_error) ) - -GRAPHENE_EXTERNAL_SERIALIZATION(/*not extern*/, graphene::net::potential_peer_record) diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index d2a5be16..fb944627 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -2,7 +2,6 @@ add_subdirectory( witness ) add_subdirectory( account_history ) add_subdirectory( accounts_list ) add_subdirectory( affiliate_stats ) -add_subdirectory( elasticsearch ) add_subdirectory( market_history ) add_subdirectory( delayed_node ) add_subdirectory( bookie ) @@ -11,4 +10,3 @@ add_subdirectory( generate_uia_sharedrop_genesis ) add_subdirectory( debug_witness ) add_subdirectory( snapshot ) add_subdirectory( peerplays_sidechain ) -add_subdirectory( es_objects ) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 67322f80..81acb01e 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include @@ -128,8 +128,8 @@ void account_history_plugin_impl::update_account_histories( const signed_block& if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - graphene::chain::operation_get_impacted_accounts( op.op, impacted ); - if( op.op.which() == operation::tag< lottery_end_operation >::value ) + graphene::app::operation_get_impacted_accounts( op.op, impacted ); + if( op.op.which() == operation::tag< lottery_end_operation >::value ) { auto lop = op.op.get< lottery_end_operation >(); auto asset_object = lop.lottery( db ); @@ -137,7 +137,6 @@ void account_history_plugin_impl::update_account_histories( const signed_block& for( auto benefactor : asset_object.lottery_options->benefactors ) impacted.insert( benefactor.id ); } - for( auto& a : other ) for( auto& item : a.account_auths ) impacted.insert( item.first ); diff --git a/libraries/plugins/accounts_list/accounts_list_plugin.cpp b/libraries/plugins/accounts_list/accounts_list_plugin.cpp index 757891ea..aabf711d 100644 --- a/libraries/plugins/accounts_list/accounts_list_plugin.cpp +++ b/libraries/plugins/accounts_list/accounts_list_plugin.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include diff --git a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp index da9c8a04..438b1aca 100644 --- a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp +++ b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index 261de241..f15ac2f7 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -370,8 +370,8 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) assert(bet_iter != persistent_bets_by_bet_id.end()); if (bet_iter != persistent_bets_by_bet_id.end()) { - // ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations", - // ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id)); + ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations", + ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id)); if (is_operation_history_object_stored(op.id)) db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { obj.associated_operations.emplace_back(op.id); @@ -386,8 +386,8 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) assert(bet_iter != persistent_bets_by_bet_id.end()); if (bet_iter != persistent_bets_by_bet_id.end()) { - // ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations", - // ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id)); + ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations", + ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id)); if (is_operation_history_object_stored(op.id)) db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { obj.associated_operations.emplace_back(op.id); diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index d49129b0..3eadda34 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -65,7 +65,7 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c void delayed_node_plugin::connect() { - my->client_connection = std::make_shared(*my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); + my->client_connection = std::make_shared(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); my->database_api = my->client_connection->get_remote_api(0); my->client_connection_closed = my->client_connection->closed.connect([this] { connection_failed(); diff --git a/libraries/plugins/elasticsearch/CMakeLists.txt b/libraries/plugins/elasticsearch/CMakeLists.txt deleted file mode 100644 index f4815576..00000000 --- a/libraries/plugins/elasticsearch/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -file(GLOB HEADERS "include/graphene/elasticsearch/*.hpp") - -add_library( graphene_elasticsearch - elasticsearch_plugin.cpp - ) - -target_link_libraries( graphene_elasticsearch graphene_chain graphene_app curl ) -target_include_directories( graphene_elasticsearch - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) - -if(MSVC) - set_source_files_properties(elasticsearch_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) -endif(MSVC) - -install( TARGETS - graphene_elasticsearch - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) -INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/elasticsearch" ) - diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp deleted file mode 100644 index 484aef9c..00000000 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (c) 2017 Cryptonomex, Inc., 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 elasticsearch { - -namespace detail -{ - -class elasticsearch_plugin_impl -{ - public: - elasticsearch_plugin_impl(elasticsearch_plugin& _plugin) - : _self( _plugin ) - { curl = curl_easy_init(); } - virtual ~elasticsearch_plugin_impl(); - - bool update_account_histories( const signed_block& b ); - - graphene::chain::database& database() - { - return _self.database(); - } - - elasticsearch_plugin& _self; - primary_index< operation_history_index >* _oho_index; - - std::string _elasticsearch_node_url = "http://localhost:9200/"; - uint32_t _elasticsearch_bulk_replay = 10000; - uint32_t _elasticsearch_bulk_sync = 100; - bool _elasticsearch_visitor = false; - std::string _elasticsearch_basic_auth = ""; - std::string _elasticsearch_index_prefix = "peerplays-"; - bool _elasticsearch_operation_object = false; - uint32_t _elasticsearch_start_es_after_block = 0; - bool _elasticsearch_operation_string = true; - mode _elasticsearch_mode = mode::only_save; - CURL *curl; // curl handler - vector bulk_lines; // vector of op lines - vector prepare; - - graphene::utilities::ES es; - uint32_t limit_documents; - int16_t op_type; - operation_history_struct os; - block_struct bs; - visitor_struct vs; - bulk_struct bulk_line_struct; - std::string bulk_line; - std::string index_name; - bool is_sync = false; - fc::time_point last_sync; - private: - 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); - const account_statistics_object& getStatsObject(const account_id_type account_id); - void growStats(const account_statistics_object& stats_obj, const account_transaction_history_object& ath); - void getOperationType(const optional & oho); - void doOperationHistory(const optional & oho); - void doBlock(const optional & oho, const signed_block& b); - void doVisitor(const optional & oho); - void checkState(const fc::time_point_sec& block_time); - void cleanObjects(const account_transaction_history_object& ath, account_id_type account_id); - void createBulkLine(const account_transaction_history_object& ath); - void prepareBulk(const account_transaction_history_id_type& ath_id); - void populateESstruct(); -}; - -elasticsearch_plugin_impl::~elasticsearch_plugin_impl() -{ - if (curl) { - curl_easy_cleanup(curl); - curl = nullptr; - } - return; -} - -bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b ) -{ - checkState(b.timestamp); - index_name = graphene::utilities::generateIndexName(b.timestamp, _elasticsearch_index_prefix); - - graphene::chain::database& db = database(); - const vector >& hist = db.get_applied_operations(); - bool is_first = true; - auto skip_oho_id = [&is_first,&db,this]() { - if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo - { - db.remove( db.create( []( operation_history_object& obj) {} ) ); - is_first = false; - } - else - _oho_index->use_next_id(); - }; - for( const optional< operation_history_object >& o_op : hist ) { - optional oho; - - auto create_oho = [&]() { - is_first = false; - return optional( - db.create([&](operation_history_object &h) { - if (o_op.valid()) - { - h.op = o_op->op; - h.result = o_op->result; - h.block_num = o_op->block_num; - h.trx_in_block = o_op->trx_in_block; - h.op_in_trx = o_op->op_in_trx; - h.virtual_op = o_op->virtual_op; - } - })); - }; - - if( !o_op.valid() ) { - skip_oho_id(); - continue; - } - oho = create_oho(); - - // populate what we can before impacted loop - getOperationType(oho); - doOperationHistory(oho); - doBlock(oho, b); - if(_elasticsearch_visitor) - doVisitor(oho); - - const operation_history_object& op = *o_op; - - // get the set of accounts this operation applies to - flat_set impacted; - vector other; - operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here - - if( op.op.which() == operation::tag< account_create_operation >::value ) - impacted.insert( op.result.get() ); - else - graphene::chain::operation_get_impacted_accounts( op.op, impacted ); - - for( auto& a : other ) - for( auto& item : a.account_auths ) - impacted.insert( item.first ); - - for( auto& account_id : impacted ) - { - if(!add_elasticsearch( account_id, oho, b.block_num() )) - return false; - } - } - // we send bulk at end of block when we are in sync for better real time client experience - if(is_sync) - { - populateESstruct(); - if(es.bulk_lines.size() > 0) - { - prepare.clear(); - if(!graphene::utilities::SendBulk(es)) - return false; - else - bulk_lines.clear(); - } - } - - return true; -} - -void elasticsearch_plugin_impl::checkState(const fc::time_point_sec& block_time) -{ - fc::time_point current_time(fc::time_point::now()); - if(((current_time - block_time) < fc::seconds(30)) || (current_time - last_sync > fc::seconds(60))) - { - limit_documents = _elasticsearch_bulk_sync; - is_sync = true; - last_sync = current_time; - } - else - { - limit_documents = _elasticsearch_bulk_replay; - is_sync = false; - } -} - -void elasticsearch_plugin_impl::getOperationType(const optional & oho) -{ - if (!oho->id.is_null()) - op_type = oho->op.which(); -} - -void elasticsearch_plugin_impl::doOperationHistory(const optional & oho) -{ - os.trx_in_block = oho->trx_in_block; - os.op_in_trx = oho->op_in_trx; - os.operation_result = fc::json::to_string(oho->result); - os.virtual_op = oho->virtual_op; - - if(_elasticsearch_operation_object) { - oho->op.visit(fc::from_static_variant(os.op_object, FC_PACK_MAX_DEPTH)); - adaptor_struct adaptor; - os.op_object = adaptor.adapt(os.op_object.get_object()); - } - if(_elasticsearch_operation_string) - os.op = fc::json::to_string(oho->op); -} - -void elasticsearch_plugin_impl::doBlock(const optional & oho, const signed_block& b) -{ - std::string trx_id = ""; - if(oho->trx_in_block < b.transactions.size()) - trx_id = b.transactions[oho->trx_in_block].id().str(); - bs.block_num = b.block_num(); - bs.block_time = b.timestamp; - bs.trx_id = trx_id; -} - -void elasticsearch_plugin_impl::doVisitor(const optional & oho) -{ - operation_visitor o_v; - oho->op.visit(o_v); - - vs.fee_data.asset = o_v.fee_asset; - vs.fee_data.amount = o_v.fee_amount; - - vs.transfer_data.asset = o_v.transfer_asset_id; - vs.transfer_data.amount = o_v.transfer_amount; - vs.transfer_data.from = o_v.transfer_from; - vs.transfer_data.to = o_v.transfer_to; - - vs.fill_data.order_id = o_v.fill_order_id; - vs.fill_data.account_id = o_v.fill_account_id; - vs.fill_data.pays_asset_id = o_v.fill_pays_asset_id; - vs.fill_data.pays_amount = o_v.fill_pays_amount; - vs.fill_data.receives_asset_id = o_v.fill_receives_asset_id; - vs.fill_data.receives_amount = o_v.fill_receives_amount; - //vs.fill_data.fill_price = o_v.fill_fill_price; - //vs.fill_data.is_maker = o_v.fill_is_maker; -} - -bool elasticsearch_plugin_impl::add_elasticsearch( const account_id_type account_id, - 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); - if(block_number > _elasticsearch_start_es_after_block) { - createBulkLine(ath); - prepareBulk(ath.id); - } - cleanObjects(ath, account_id); - - if (curl && bulk_lines.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech - prepare.clear(); - populateESstruct(); - if(!graphene::utilities::SendBulk(es)) - return false; - else - bulk_lines.clear(); - } - - return true; -} - -const account_statistics_object& elasticsearch_plugin_impl::getStatsObject(const account_id_type account_id) -{ - graphene::chain::database& db = database(); - const auto &acct = db.get(account_id); - return acct.statistics(db); -} - -const account_transaction_history_object& elasticsearch_plugin_impl::addNewEntry(const account_statistics_object& stats_obj, - const account_id_type account_id, - const optional & oho) -{ - graphene::chain::database& db = database(); - const auto &ath = db.create([&](account_transaction_history_object &obj) { - obj.operation_id = oho->id; - obj.account = account_id; - obj.sequence = stats_obj.total_ops + 1; - obj.next = stats_obj.most_recent_op; - }); - - return ath; -} - -void elasticsearch_plugin_impl::growStats(const account_statistics_object& stats_obj, - const account_transaction_history_object& ath) -{ - graphene::chain::database& db = database(); - db.modify(stats_obj, [&](account_statistics_object &obj) { - obj.most_recent_op = ath.id; - obj.total_ops = ath.sequence; - }); -} - -void elasticsearch_plugin_impl::createBulkLine(const account_transaction_history_object& ath) -{ - bulk_line_struct.account_history = ath; - bulk_line_struct.operation_history = os; - bulk_line_struct.operation_type = op_type; - bulk_line_struct.operation_id_num = ath.operation_id.instance.value; - bulk_line_struct.block_data = bs; - if(_elasticsearch_visitor) - bulk_line_struct.additional_data = vs; - bulk_line = fc::json::to_string(bulk_line_struct); -} - -void elasticsearch_plugin_impl::prepareBulk(const account_transaction_history_id_type& ath_id) -{ - const std::string _id = fc::json::to_string(ath_id); - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = index_name; - bulk_header["_type"] = "data"; - bulk_header["_id"] = fc::to_string(ath_id.space_id) + "." + fc::to_string(ath_id.type_id) + "." + ath_id.instance; - prepare = graphene::utilities::createBulk(bulk_header, bulk_line); - bulk_lines.insert(bulk_lines.end(), prepare.begin(), prepare.end()); -} - -void elasticsearch_plugin_impl::cleanObjects(const account_transaction_history_object& ath, account_id_type account_id) -{ - graphene::chain::database& db = database(); - // remove everything except current object from ath - const auto &his_idx = db.get_index_type(); - const auto &by_seq_idx = his_idx.indices().get(); - auto itr = by_seq_idx.lower_bound(boost::make_tuple(account_id, 0)); - if (itr != by_seq_idx.end() && itr->account == account_id && itr->id != ath.id) { - // if found, remove the entry - const auto remove_op_id = itr->operation_id; - const auto itr_remove = itr; - ++itr; - db.remove( *itr_remove ); - // modify previous node's next pointer - // this should be always true, but just have a check here - if( itr != by_seq_idx.end() && itr->account == account_id ) - { - db.modify( *itr, [&]( account_transaction_history_object& obj ){ - obj.next = account_transaction_history_id_type(); - }); - } - // do the same on oho - const auto &by_opid_idx = his_idx.indices().get(); - if (by_opid_idx.find(remove_op_id) == by_opid_idx.end()) { - db.remove(remove_op_id(db)); - } - } -} - -void elasticsearch_plugin_impl::populateESstruct() -{ - es.curl = curl; - es.bulk_lines = bulk_lines; - es.elasticsearch_url = _elasticsearch_node_url; - es.auth = _elasticsearch_basic_auth; -} - -} // end namespace detail - -elasticsearch_plugin::elasticsearch_plugin() : - my( new detail::elasticsearch_plugin_impl(*this) ) -{ -} - -elasticsearch_plugin::~elasticsearch_plugin() -{ -} - -std::string elasticsearch_plugin::plugin_name()const -{ - return "elasticsearch"; -} -std::string elasticsearch_plugin::plugin_description()const -{ - return "Stores account history data in elasticsearch database(EXPERIMENTAL)."; -} - -void elasticsearch_plugin::plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg - ) -{ - cli.add_options() - ("elasticsearch-node-url", boost::program_options::value(), - "Elastic Search database node url(http://localhost:9200/)") - ("elasticsearch-bulk-replay", boost::program_options::value(), - "Number of bulk documents to index on replay(10000)") - ("elasticsearch-bulk-sync", boost::program_options::value(), - "Number of bulk documents to index on a syncronied chain(100)") - ("elasticsearch-visitor", boost::program_options::value(), - "Use visitor to index additional data(slows down the replay(false))") - ("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(peerplays-)") - ("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)") - ("elasticsearch-operation-string", boost::program_options::value(), - "Save operation as string. Needed to serve history api calls(true)") - ("elasticsearch-mode", boost::program_options::value(), - "Mode of operation: only_save(0), only_query(1), all(2) - Default: 0") - ; - cfg.add(cli); -} - -void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ - my->_oho_index = database().add_index< primary_index< operation_history_index > >(); - database().add_index< primary_index< account_transaction_history_index > >(); - - if (options.count("elasticsearch-node-url")) { - my->_elasticsearch_node_url = options["elasticsearch-node-url"].as(); - } - if (options.count("elasticsearch-bulk-replay")) { - my->_elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as(); - } - if (options.count("elasticsearch-bulk-sync")) { - my->_elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as(); - } - if (options.count("elasticsearch-visitor")) { - my->_elasticsearch_visitor = options["elasticsearch-visitor"].as(); - } - if (options.count("elasticsearch-basic-auth")) { - my->_elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as(); - } - if (options.count("elasticsearch-index-prefix")) { - my->_elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as(); - } - 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(); - } - if (options.count("elasticsearch-operation-string")) { - my->_elasticsearch_operation_string = options["elasticsearch-operation-string"].as(); - } - if (options.count("elasticsearch-mode")) { - const auto option_number = options["elasticsearch-mode"].as(); - if(option_number > mode::all) - FC_THROW_EXCEPTION(fc::exception, "Elasticsearch mode not valid"); - my->_elasticsearch_mode = static_cast(options["elasticsearch-mode"].as()); - } - - if(my->_elasticsearch_mode != mode::only_query) { - if (my->_elasticsearch_mode == mode::all && !my->_elasticsearch_operation_string) - FC_THROW_EXCEPTION(fc::exception, - "If elasticsearch-mode is set to all then elasticsearch-operation-string need to be true"); - - database().applied_block.connect([this](const signed_block &b) { - if (!my->update_account_histories(b)) - FC_THROW_EXCEPTION(fc::exception, - "Error populating ES database, we are going to keep trying."); - }); - } -} - -void elasticsearch_plugin::plugin_startup() -{ - graphene::utilities::ES es; - es.curl = my->curl; - es.elasticsearch_url = my->_elasticsearch_node_url; - es.auth = my->_elasticsearch_basic_auth; - - if(!graphene::utilities::checkES(es)) - FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_elasticsearch_node_url)); - ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() begin"); -} - -operation_history_object elasticsearch_plugin::get_operation_by_id(operation_history_id_type id) -{ - const string operation_id_string = std::string(object_id_type(id)); - - const string query = R"( - { - "query": { - "match": - { - "account_history.operation_id": )" + operation_id_string + R"(" - } - } - } - )"; - - auto es = prepareHistoryQuery(query); - const auto response = graphene::utilities::simpleQuery(es); - variant variant_response = fc::json::from_string(response); - const auto source = variant_response["hits"]["hits"][size_t(0)]["_source"]; - return fromEStoOperation(source); -} - -vector elasticsearch_plugin::get_account_history( - const account_id_type account_id, - operation_history_id_type stop = operation_history_id_type(), - unsigned limit = 100, - operation_history_id_type start = operation_history_id_type()) -{ - const string account_id_string = std::string(object_id_type(account_id)); - - const auto stop_number = stop.instance.value; - const auto start_number = start.instance.value; - - string range = ""; - if(stop_number == 0) - range = " AND operation_id_num: ["+fc::to_string(stop_number)+" TO "+fc::to_string(start_number)+"]"; - else if(stop_number > 0) - range = " AND operation_id_num: {"+fc::to_string(stop_number)+" TO "+fc::to_string(start_number)+"]"; - - const string query = R"( - { - "size": )" + fc::to_string(limit) + R"(, - "sort" : [{ "operation_id_num" : {"order" : "desc"}}], - "query": { - "bool": { - "must": [ - { - "query_string": { - "query": "account_history.account: )" + account_id_string + range + R"(" - } - } - ] - } - } - } - )"; - - auto es = prepareHistoryQuery(query); - - vector result; - - if(!graphene::utilities::checkES(es)) - return result; - - const auto response = graphene::utilities::simpleQuery(es); - variant variant_response = fc::json::from_string(response); - - const auto hits = variant_response["hits"]["total"]["value"]; - uint32_t size; - if( hits.is_object() ) // ES-7 ? - size = static_cast(hits["value"].as_uint64()); - else // probably ES-6 - size = static_cast(hits.as_uint64()); - - size = std::min( size, limit ); - - for(unsigned i=0; i_elasticsearch_node_url; - es.index_prefix = my->_elasticsearch_index_prefix; - es.endpoint = es.index_prefix + "*/data/_search"; - es.query = query; - - return es; -} - -mode elasticsearch_plugin::get_running_mode() -{ - return my->_elasticsearch_mode; -} - - -} } diff --git a/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp b/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp deleted file mode 100644 index 01a83244..00000000 --- a/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2017 Cryptonomex, Inc., 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 - -namespace graphene { namespace elasticsearch { - using namespace chain; - -// -// Plugins should #define their SPACE_ID's so plugins with -// conflicting SPACE_ID assignments can be compiled into the -// same binary (by simply re-assigning some of the conflicting #defined -// SPACE_ID's in a build script). -// -// Assignment of SPACE_ID's cannot be done at run-time because -// various template automagic depends on them being known at compile -// time. -// -#ifndef ELASTICSEARCH_SPACE_ID -#define ELASTICSEARCH_SPACE_ID 6 -#endif - -namespace detail -{ - class elasticsearch_plugin_impl; -} - -enum mode { only_save = 0 , only_query = 1, all = 2 }; - -class elasticsearch_plugin : public graphene::app::plugin -{ - public: - elasticsearch_plugin(); - virtual ~elasticsearch_plugin(); - - std::string plugin_name()const override; - std::string plugin_description()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) override; - virtual void plugin_initialize(const boost::program_options::variables_map& options) override; - virtual void plugin_startup() override; - - operation_history_object get_operation_by_id(operation_history_id_type id); - vector get_account_history(const account_id_type account_id, - operation_history_id_type stop, unsigned limit, operation_history_id_type start); - mode get_running_mode(); - - friend class detail::elasticsearch_plugin_impl; - std::unique_ptr my; - - private: - operation_history_object fromEStoOperation(variant source); - graphene::utilities::ES prepareHistoryQuery(string query); -}; - - -struct operation_visitor -{ - typedef void result_type; - - share_type fee_amount; - asset_id_type fee_asset; - - asset_id_type transfer_asset_id; - share_type transfer_amount; - account_id_type transfer_from; - account_id_type transfer_to; - - void operator()( const graphene::chain::transfer_operation& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - - transfer_asset_id = o.amount.asset_id; - transfer_amount = o.amount.amount; - transfer_from = o.from; - transfer_to = o.to; - } - - object_id_type fill_order_id; - account_id_type fill_account_id; - asset_id_type fill_pays_asset_id; - share_type fill_pays_amount; - asset_id_type fill_receives_asset_id; - share_type fill_receives_amount; - //double fill_fill_price; - //bool fill_is_maker; - - void operator()( const graphene::chain::fill_order_operation& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - - fill_order_id = o.order_id; - fill_account_id = o.account_id; - fill_pays_asset_id = o.pays.asset_id; - fill_pays_amount = o.pays.amount; - fill_receives_asset_id = o.receives.asset_id; - fill_receives_amount = o.receives.amount; - //fill_fill_price = o.fill_price.to_real(); - //fill_is_maker = o.is_maker; - } - - template - void operator()( const T& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - } -}; - -struct operation_history_struct { - int trx_in_block; - int op_in_trx; - std::string operation_result; - int virtual_op; - std::string op; - variant op_object; -}; - -struct block_struct { - int block_num; - fc::time_point_sec block_time; - std::string trx_id; -}; - -struct fee_struct { - asset_id_type asset; - share_type amount; -}; - -struct transfer_struct { - asset_id_type asset; - share_type amount; - account_id_type from; - account_id_type to; -}; - -struct fill_struct { - object_id_type order_id; - account_id_type account_id; - asset_id_type pays_asset_id; - share_type pays_amount; - asset_id_type receives_asset_id; - share_type receives_amount; - double fill_price; - bool is_maker; -}; - -struct visitor_struct { - fee_struct fee_data; - transfer_struct transfer_data; - fill_struct fill_data; -}; - -struct bulk_struct { - account_transaction_history_object account_history; - operation_history_struct operation_history; - int operation_type; - int operation_id_num; - block_struct block_data; - optional additional_data; -}; - -struct adaptor_struct { - variant adapt(const variant_object& op) - { - fc::mutable_variant_object o(op); - 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("memo") != o.end()) - { - auto& memo = o["memo"]; - if (memo.is_string()) - { - o["memo_"] = o["memo"]; - o.erase("memo"); - } - else if (memo.is_object()) - { - fc::mutable_variant_object tmp(memo.get_object()); - if (tmp.find("nonce") != tmp.end()) - { - tmp["nonce"] = tmp["nonce"].as_string(); - o["memo"] = tmp; - } - } - } - if (o.find("new_parameters") != o.end()) - { - auto& tmp = o["new_parameters"]; - if (tmp.is_object()) - { - fc::mutable_variant_object tmp2(tmp.get_object()); - if (tmp2.find("current_fees") != tmp2.end()) - { - tmp2.erase("current_fees"); - o["new_parameters"] = tmp2; - } - } - } - if (o.find("owner") != o.end() && o["owner"].is_string()) - { - o["owner_"] = o["owner"].as_string(); - o.erase("owner"); - } - if (o.find("proposed_ops") != o.end()) - { - o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]); - } - if (o.find("initializer") != o.end()) - { - o["initializer"] = fc::json::to_string(o["initializer"]); - } - - 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::elasticsearch - -FC_REFLECT_ENUM( graphene::elasticsearch::mode, (only_save)(only_query)(all) ) -FC_REFLECT( graphene::elasticsearch::operation_history_struct, (trx_in_block)(op_in_trx)(operation_result)(virtual_op)(op)(op_object) ) -FC_REFLECT( graphene::elasticsearch::block_struct, (block_num)(block_time)(trx_id) ) -FC_REFLECT( graphene::elasticsearch::fee_struct, (asset)(amount) ) -FC_REFLECT( graphene::elasticsearch::transfer_struct, (asset)(amount)(from)(to) ) -FC_REFLECT( graphene::elasticsearch::fill_struct, (order_id)(account_id)(pays_asset_id)(pays_amount)(receives_asset_id)(receives_amount)(fill_price)(is_maker)) -FC_REFLECT( graphene::elasticsearch::visitor_struct, (fee_data)(transfer_data)(fill_data) ) -FC_REFLECT( graphene::elasticsearch::bulk_struct, (account_history)(operation_history)(operation_type)(operation_id_num)(block_data)(additional_data) ) diff --git a/libraries/plugins/es_objects/CMakeLists.txt b/libraries/plugins/es_objects/CMakeLists.txt deleted file mode 100644 index 92e3d150..00000000 --- a/libraries/plugins/es_objects/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -file(GLOB HEADERS "include/graphene/es_objects/*.hpp") - -add_library( graphene_es_objects - es_objects.cpp - ) - -target_link_libraries( graphene_es_objects graphene_chain graphene_app curl ) -target_include_directories( graphene_es_objects - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) - -if(MSVC) - set_source_files_properties(es_objects.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) -endif(MSVC) - -install( TARGETS - graphene_es_objects - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) -INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/es_objects" ) - diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp deleted file mode 100644 index b9083cbc..00000000 --- a/libraries/plugins/es_objects/es_objects.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage, 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 -#include -#include -#include - -#include - -namespace graphene { namespace es_objects { - -namespace detail -{ - -class es_objects_plugin_impl -{ - public: - es_objects_plugin_impl(es_objects_plugin& _plugin) - : _self( _plugin ) - { curl = curl_easy_init(); } - virtual ~es_objects_plugin_impl(); - - bool index_database(const vector& ids, std::string action); - bool genesis(); - void remove_from_database(object_id_type id, std::string index); - - es_objects_plugin& _self; - std::string _es_objects_elasticsearch_url = "http://localhost:9200/"; - std::string _es_objects_auth = ""; - uint32_t _es_objects_bulk_replay = 10000; - uint32_t _es_objects_bulk_sync = 100; - bool _es_objects_proposals = true; - bool _es_objects_accounts = true; - bool _es_objects_assets = true; - bool _es_objects_balances = true; - bool _es_objects_limit_orders = true; - bool _es_objects_asset_bitasset = true; - std::string _es_objects_index_prefix = "ppobjects-"; - uint32_t _es_objects_start_es_after_block = 0; - CURL *curl; // curl handler - vector bulk; - vector prepare; - - bool _es_objects_keep_only_current = true; - - uint32_t block_number; - fc::time_point_sec block_time; - - private: - template - void prepareTemplate(T blockchain_object, string index_name); -}; - -bool es_objects_plugin_impl::genesis() -{ - - ilog("elasticsearch OBJECTS: inserting data from genesis"); - - graphene::chain::database &db = _self.database(); - - block_number = db.head_block_num(); - block_time = db.head_block_time(); - - if (_es_objects_accounts) { - auto &index_accounts = db.get_index(1, 2); - index_accounts.inspect_all_objects([this, &db](const graphene::db::object &o) { - auto obj = db.find_object(o.id); - auto a = static_cast(obj); - prepareTemplate(*a, "account"); - }); - } - if (_es_objects_assets) { - auto &index_assets = db.get_index(1, 3); - index_assets.inspect_all_objects([this, &db](const graphene::db::object &o) { - auto obj = db.find_object(o.id); - auto a = static_cast(obj); - prepareTemplate(*a, "asset"); - }); - } - if (_es_objects_balances) { - auto &index_balances = db.get_index(2, 5); - index_balances.inspect_all_objects([this, &db](const graphene::db::object &o) { - auto obj = db.find_object(o.id); - auto b = static_cast(obj); - prepareTemplate(*b, "balance"); - }); - } - - graphene::utilities::ES es; - es.curl = curl; - es.bulk_lines = bulk; - es.elasticsearch_url = _es_objects_elasticsearch_url; - es.auth = _es_objects_auth; - if (!graphene::utilities::SendBulk(es)) - FC_THROW_EXCEPTION(fc::exception, "Error inserting genesis data."); - else - bulk.clear(); - - return true; -} - -bool es_objects_plugin_impl::index_database(const vector& ids, std::string action) -{ - graphene::chain::database &db = _self.database(); - - block_time = db.head_block_time(); - block_number = db.head_block_num(); - - if(block_number > _es_objects_start_es_after_block) { - - // check if we are in replay or in sync and change number of bulk documents accordingly - uint32_t limit_documents = 0; - if ((fc::time_point::now() - block_time) < fc::seconds(30)) - limit_documents = _es_objects_bulk_sync; - else - limit_documents = _es_objects_bulk_replay; - - - for (auto const &value: ids) { - if (value.is() && _es_objects_proposals) { - auto obj = db.find_object(value); - auto p = static_cast(obj); - if (p != nullptr) { - if (action == "delete") - remove_from_database(p->id, "proposal"); - else - prepareTemplate(*p, "proposal"); - } - } else if (value.is() && _es_objects_accounts) { - auto obj = db.find_object(value); - auto a = static_cast(obj); - if (a != nullptr) { - if (action == "delete") - remove_from_database(a->id, "account"); - else - prepareTemplate(*a, "account"); - } - } else if (value.is() && _es_objects_assets) { - auto obj = db.find_object(value); - auto a = static_cast(obj); - if (a != nullptr) { - if (action == "delete") - remove_from_database(a->id, "asset"); - else - prepareTemplate(*a, "asset"); - } - } else if (value.is() && _es_objects_balances) { - auto obj = db.find_object(value); - auto b = static_cast(obj); - if (b != nullptr) { - if (action == "delete") - remove_from_database(b->id, "balance"); - else - prepareTemplate(*b, "balance"); - } - } else if (value.is() && _es_objects_limit_orders) { - auto obj = db.find_object(value); - auto l = static_cast(obj); - if (l != nullptr) { - if (action == "delete") - remove_from_database(l->id, "limitorder"); - else - prepareTemplate(*l, "limitorder"); - } - } else if (value.is() && _es_objects_asset_bitasset) { - auto obj = db.find_object(value); - auto ba = static_cast(obj); - if (ba != nullptr) { - if (action == "delete") - remove_from_database(ba->id, "bitasset"); - else - prepareTemplate(*ba, "bitasset"); - } - } - } - - if (curl && bulk.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech - - graphene::utilities::ES es; - es.curl = curl; - es.bulk_lines = bulk; - es.elasticsearch_url = _es_objects_elasticsearch_url; - es.auth = _es_objects_auth; - - if (!graphene::utilities::SendBulk(es)) - return false; - else - bulk.clear(); - } - } - - return true; -} - -void es_objects_plugin_impl::remove_from_database( object_id_type id, std::string index) -{ - if(_es_objects_keep_only_current) - { - fc::mutable_variant_object delete_line; - delete_line["_id"] = string(id); - delete_line["_index"] = _es_objects_index_prefix + index; - delete_line["_type"] = "data"; - fc::mutable_variant_object final_delete_line; - final_delete_line["delete"] = delete_line; - prepare.push_back(fc::json::to_string(final_delete_line)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); - } -} - -template -void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_name) -{ - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + index_name; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(blockchain_object.id); - } - - adaptor_struct adaptor; - 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()); - - o["object_id"] = string(blockchain_object.id); - o["block_time"] = block_time; - o["block_number"] = block_number; - - string data = fc::json::to_string(o); - - 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() -{ - if (curl) { - curl_easy_cleanup(curl); - curl = nullptr; - } - return; -} - -} // end namespace detail - -es_objects_plugin::es_objects_plugin() : - my( new detail::es_objects_plugin_impl(*this) ) -{ -} - -es_objects_plugin::~es_objects_plugin() -{ -} - -std::string es_objects_plugin::plugin_name()const -{ - return "es_objects"; -} -std::string es_objects_plugin::plugin_description()const -{ - return "Stores blockchain objects in ES database. Experimental."; -} - -void es_objects_plugin::plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg - ) -{ - cli.add_options() - ("es-objects-elasticsearch-url", boost::program_options::value(), "Elasticsearch node url(http://localhost:9200/)") - ("es-objects-auth", boost::program_options::value(), "Basic auth username:password('')") - ("es-objects-bulk-replay", boost::program_options::value(), "Number of bulk documents to index on replay(10000)") - ("es-objects-bulk-sync", boost::program_options::value(), "Number of bulk documents to index on a synchronized chain(100)") - ("es-objects-proposals", boost::program_options::value(), "Store proposal objects(true)") - ("es-objects-accounts", boost::program_options::value(), "Store account objects(true)") - ("es-objects-assets", boost::program_options::value(), "Store asset objects(true)") - ("es-objects-balances", boost::program_options::value(), "Store balances objects(true)") - ("es-objects-limit-orders", boost::program_options::value(), "Store limit order objects(true)") - ("es-objects-asset-bitasset", boost::program_options::value(), "Store feed data(true)") - ("es-objects-index-prefix", boost::program_options::value(), "Add a prefix to the index(ppobjects-)") - ("es-objects-keep-only-current", boost::program_options::value(), "Keep only current state of the objects(true)") - ("es-objects-start-es-after-block", boost::program_options::value(), "Start doing ES job after block(0)") - ; - cfg.add(cli); -} - -void es_objects_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ - database().applied_block.connect([this](const signed_block &b) { - if(b.block_num() == 1) { - if (!my->genesis()) - FC_THROW_EXCEPTION(fc::exception, "Error populating genesis data."); - } - }); - - database().new_objects.connect([this]( const vector& ids, const flat_set& impacted_accounts ) { - if(!my->index_database(ids, "create")) - { - FC_THROW_EXCEPTION(fc::exception, "Error creating object from ES database, we are going to keep trying."); - } - }); - database().changed_objects.connect([this]( const vector& ids, const flat_set& impacted_accounts ) { - if(!my->index_database(ids, "update")) - { - FC_THROW_EXCEPTION(fc::exception, "Error updating object from ES database, we are going to keep trying."); - } - }); - database().removed_objects.connect([this](const vector& ids, const vector& objs, const flat_set& impacted_accounts) { - if(!my->index_database(ids, "delete")) - { - FC_THROW_EXCEPTION(fc::exception, "Error deleting object from ES database, we are going to keep trying."); - } - }); - - - if (options.count("es-objects-elasticsearch-url")) { - my->_es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as(); - } - if (options.count("es-objects-auth")) { - my->_es_objects_auth = options["es-objects-auth"].as(); - } - if (options.count("es-objects-bulk-replay")) { - my->_es_objects_bulk_replay = options["es-objects-bulk-replay"].as(); - } - if (options.count("es-objects-bulk-sync")) { - my->_es_objects_bulk_sync = options["es-objects-bulk-sync"].as(); - } - if (options.count("es-objects-proposals")) { - my->_es_objects_proposals = options["es-objects-proposals"].as(); - } - if (options.count("es-objects-accounts")) { - my->_es_objects_accounts = options["es-objects-accounts"].as(); - } - if (options.count("es-objects-assets")) { - my->_es_objects_assets = options["es-objects-assets"].as(); - } - if (options.count("es-objects-balances")) { - my->_es_objects_balances = options["es-objects-balances"].as(); - } - if (options.count("es-objects-limit-orders")) { - my->_es_objects_limit_orders = options["es-objects-limit-orders"].as(); - } - if (options.count("es-objects-asset-bitasset")) { - my->_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as(); - } - if (options.count("es-objects-index-prefix")) { - my->_es_objects_index_prefix = options["es-objects-index-prefix"].as(); - } - if (options.count("es-objects-keep-only-current")) { - my->_es_objects_keep_only_current = options["es-objects-keep-only-current"].as(); - } - if (options.count("es-objects-start-es-after-block")) { - my->_es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as(); - } -} - -void es_objects_plugin::plugin_startup() -{ - graphene::utilities::ES es; - es.curl = my->curl; - es.elasticsearch_url = my->_es_objects_elasticsearch_url; - es.auth = my->_es_objects_auth; - es.auth = my->_es_objects_index_prefix; - - if(!graphene::utilities::checkES(es)) - FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_es_objects_elasticsearch_url)); - ilog("elasticsearch OBJECTS: plugin_startup() begin"); -} - -} } 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 deleted file mode 100644 index fa91e3bd..00000000 --- a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage, 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 - -namespace graphene { namespace es_objects { - -using namespace chain; - -namespace detail -{ - class es_objects_plugin_impl; -} - -class es_objects_plugin : public graphene::app::plugin -{ - public: - es_objects_plugin(); - virtual ~es_objects_plugin(); - - std::string plugin_name()const override; - std::string plugin_description()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description& cli, - boost::program_options::options_description& cfg) override; - virtual void plugin_initialize(const boost::program_options::variables_map& options) override; - virtual void plugin_startup() override; - - friend class detail::es_objects_plugin_impl; - std::unique_ptr my; -}; - -struct adaptor_struct { - 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) { - 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"]); - } - - return o; - } - - 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 diff --git a/libraries/plugins/snapshot/CMakeLists.txt b/libraries/plugins/snapshot/CMakeLists.txt index 728740de..227c3860 100644 --- a/libraries/plugins/snapshot/CMakeLists.txt +++ b/libraries/plugins/snapshot/CMakeLists.txt @@ -15,5 +15,3 @@ install( TARGETS LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) - -INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/snapshot" ) diff --git a/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp b/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp index eb8d3a16..b3ee30c6 100644 --- a/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp +++ b/libraries/plugins/snapshot/include/graphene/snapshot/snapshot.hpp @@ -35,7 +35,6 @@ class snapshot_plugin : public graphene::app::plugin { ~snapshot_plugin() {} std::string plugin_name()const override; - std::string plugin_description()const override; virtual void plugin_set_program_options( boost::program_options::options_description &command_line_options, diff --git a/libraries/plugins/snapshot/snapshot.cpp b/libraries/plugins/snapshot/snapshot.cpp index f74ad589..fe856ecb 100644 --- a/libraries/plugins/snapshot/snapshot.cpp +++ b/libraries/plugins/snapshot/snapshot.cpp @@ -54,11 +54,6 @@ std::string snapshot_plugin::plugin_name()const return "snapshot"; } -std::string snapshot_plugin::plugin_description()const -{ - return "Create snapshots at a specified time or block number."; -} - void snapshot_plugin::plugin_initialize(const boost::program_options::variables_map& options) { try { ilog("snapshot plugin: plugin_initialize() begin"); diff --git a/libraries/utilities/CMakeLists.txt b/libraries/utilities/CMakeLists.txt index 98086b10..f2d646d5 100644 --- a/libraries/utilities/CMakeLists.txt +++ b/libraries/utilities/CMakeLists.txt @@ -14,7 +14,6 @@ set(sources string_escape.cpp tempdir.cpp words.cpp - elasticsearch.cpp ${HEADERS}) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp" @ONLY) diff --git a/libraries/utilities/elasticsearch.cpp b/libraries/utilities/elasticsearch.cpp deleted file mode 100644 index 11a9561b..00000000 --- a/libraries/utilities/elasticsearch.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage, 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 - -size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) -{ - ((std::string*)userp)->append((char*)contents, size * nmemb); - return size * nmemb; -} - -namespace graphene { namespace utilities { - -bool checkES(ES& es) -{ - graphene::utilities::CurlRequest curl_request; - curl_request.handler = es.curl; - curl_request.url = es.elasticsearch_url + "_nodes"; - curl_request.auth = es.auth; - curl_request.type = "GET"; - - if(doCurl(curl_request).empty()) - return false; - return true; - -} -const std::string simpleQuery(ES& es) -{ - graphene::utilities::CurlRequest curl_request; - curl_request.handler = es.curl; - curl_request.url = es.elasticsearch_url + es.endpoint; - curl_request.auth = es.auth; - curl_request.type = "POST"; - curl_request.query = es.query; - - return doCurl(curl_request); -} - -bool SendBulk(ES& es) -{ - std::string bulking = joinBulkLines(es.bulk_lines); - - graphene::utilities::CurlRequest curl_request; - curl_request.handler = es.curl; - curl_request.url = es.elasticsearch_url + "_bulk"; - curl_request.auth = es.auth; - curl_request.type = "POST"; - curl_request.query = bulking; - - auto curlResponse = doCurl(curl_request); - - if(handleBulkResponse(getResponseCode(curl_request.handler), curlResponse)) - return true; - return false; -} - -const std::string joinBulkLines(const std::vector& bulk) -{ - auto bulking = boost::algorithm::join(bulk, "\n"); - bulking = bulking + "\n"; - - return bulking; -} -long getResponseCode(CURL *handler) -{ - long http_code = 0; - curl_easy_getinfo (handler, CURLINFO_RESPONSE_CODE, &http_code); - return http_code; -} - -bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer) -{ - if(http_code == 200) { - // all good, but check errors in response - fc::variant j = fc::json::from_string(CurlReadBuffer); - bool errors = j["errors"].as_bool(); - if(errors == true) { - return false; - } - } - else { - if(http_code == 413) { - elog( "413 error: Can be low disk space" ); - } - else if(http_code == 401) { - elog( "401 error: Unauthorized" ); - } - else { - elog( std::to_string(http_code) + " error: Unknown error" ); - } - return false; - } - return true; -} - -const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data) -{ - std::vector bulk; - fc::mutable_variant_object final_bulk_header; - final_bulk_header["index"] = bulk_header; - bulk.push_back(fc::json::to_string(final_bulk_header)); - bulk.push_back(data); - - return bulk; -} - -bool deleteAll(ES& es) -{ - graphene::utilities::CurlRequest curl_request; - curl_request.handler = es.curl; - curl_request.url = es.elasticsearch_url + es.index_prefix + "*"; - curl_request.auth = es.auth; - curl_request.type = "DELETE"; - - auto curl_response = doCurl(curl_request); - if(curl_response.empty()) - return false; - else - return true; -} -const std::string getEndPoint(ES& es) -{ - graphene::utilities::CurlRequest curl_request; - curl_request.handler = es.curl; - curl_request.url = es.elasticsearch_url + es.endpoint; - curl_request.auth = es.auth; - curl_request.type = "GET"; - - return doCurl(curl_request); -} - -const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix) -{ - auto block_date_string = block_date.to_iso_string(); - std::vector parts; - boost::split(parts, block_date_string, boost::is_any_of("-")); - std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1]; - return index_name; -} - -const std::string doCurl(CurlRequest& curl) -{ - std::string CurlReadBuffer; - struct curl_slist *headers = NULL; - headers = curl_slist_append(headers, "Content-Type: application/json"); - - curl_easy_setopt(curl.handler, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl.handler, CURLOPT_URL, curl.url.c_str()); - curl_easy_setopt(curl.handler, CURLOPT_CUSTOMREQUEST, curl.type.c_str()); - if(curl.type == "POST") - { - curl_easy_setopt(curl.handler, CURLOPT_POST, true); - curl_easy_setopt(curl.handler, CURLOPT_POSTFIELDS, curl.query.c_str()); - } - curl_easy_setopt(curl.handler, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl.handler, CURLOPT_WRITEDATA, (void *)&CurlReadBuffer); - curl_easy_setopt(curl.handler, CURLOPT_USERAGENT, "libcrp/0.1"); - if(!curl.auth.empty()) - curl_easy_setopt(curl.handler, CURLOPT_USERPWD, curl.auth.c_str()); - curl_easy_perform(curl.handler); - - return CurlReadBuffer; -} - -} } // end namespace graphene::utilities diff --git a/libraries/utilities/include/graphene/utilities/elasticsearch.hpp b/libraries/utilities/include/graphene/utilities/elasticsearch.hpp deleted file mode 100644 index 898464b1..00000000 --- a/libraries/utilities/include/graphene/utilities/elasticsearch.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage, 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 - -size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp); - -namespace graphene { namespace utilities { - - class ES { - public: - CURL *curl; - std::vector bulk_lines; - std::string elasticsearch_url; - std::string index_prefix; - std::string auth; - std::string endpoint; - std::string query; - }; - class CurlRequest { - public: - CURL *handler; - std::string url; - std::string type; - std::string auth; - std::string query; - }; - - bool SendBulk(ES& es); - const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data); - bool checkES(ES& es); - const std::string simpleQuery(ES& es); - bool deleteAll(ES& es); - bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer); - const std::string getEndPoint(ES& es); - const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix); - const std::string doCurl(CurlRequest& curl); - const std::string joinBulkLines(const std::vector& bulk); - long getResponseCode(CURL *handler); - -} } // end namespace graphene::utilities diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index b051346d..364fb20b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -498,11 +498,6 @@ class wallet_api * @ingroup Transaction Builder API */ signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true); - /** Broadcast signed transaction - * @param tx signed transaction - * @returns the transaction ID along with the signed transaction. - */ - pair broadcast_transaction(signed_transaction tx); /** * @ingroup Transaction Builder API */ @@ -602,12 +597,6 @@ class wallet_api */ bool load_wallet_file(string wallet_filename = ""); - /** Quitting from Peerplays wallet. - * - * The current wallet will be closed. - */ - void quit(); - /** Saves the current wallet to the given filename. * * @warning This does not change the wallet filename that will be used for future @@ -1307,12 +1296,6 @@ class wallet_api */ witness_object get_witness(string owner_account); - /** Returns true if the account is witness, false otherwise - * @param owner_account the name or id of the witness account owner, or the id of the witness - * @returns true if account is witness, false otherwise - */ - bool is_witness(string owner_account); - /** Returns information about the given committee_member. * @param owner_account the name or id of the committee_member account owner, or the id of the committee_member * @returns the information about the committee_member stored in the block chain @@ -1587,7 +1570,7 @@ class wallet_api vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); /** - * Withdraw a normal(old) vesting balance. + * Withdraw a vesting balance. * * @param witness_name The account name of the witness, also accepts account ID or vesting balance ID type. * @param amount The amount to withdraw. @@ -1600,20 +1583,6 @@ class wallet_api string asset_symbol, bool broadcast = false); - /** - * Withdraw a GPOS vesting balance. - * - * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type. - * @param amount The amount to withdraw. - * @param asset_symbol The symbol of the asset to withdraw. - * @param broadcast true if you wish to broadcast the transaction - */ - signed_transaction withdraw_GPOS_vesting_balance( - string account_name, - string amount, - string asset_symbol, - bool broadcast = false); - /** Vote for a given committee_member. * * An account can publish a list of all committee_memberes they approve of. This @@ -1802,37 +1771,6 @@ class wallet_api */ signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); - /** Get transaction signers. - * - * Returns information about who signed the transaction, specifically, - * the corresponding public keys of the private keys used to sign the transaction. - * @param tx the signed transaction - * @return the set of public_keys - */ - flat_set get_transaction_signers(const signed_transaction &tx) const; - - /** Get key references. - * - * Returns accounts related to given public keys. - * @param keys public keys to search for related accounts - * @return the set of related accounts - */ - vector> get_key_references(const vector &keys) const; - - /** Signs a transaction. - * - * Given a fully-formed transaction with or without signatures, signs - * the transaction with the owned keys and optionally broadcasts the - * transaction. - * - * @param tx the unsigned transaction - * @param broadcast true if you wish to broadcast the transaction - * - * @return the signed transaction - */ - signed_transaction add_transaction_signature( signed_transaction tx, - bool broadcast = false ); - /** Returns an uninitialized object representing a given blockchain operation. * * This returns a default-initialized object of the given type; it can be used @@ -2171,8 +2109,6 @@ class wallet_api } } -extern template class fc::api; - FC_REFLECT( graphene::wallet::key_label, (label)(key) ) FC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) ) FC_REFLECT( graphene::wallet::blind_confirmation::output, (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) ) @@ -2242,7 +2178,6 @@ FC_API( graphene::wallet::wallet_api, (set_fees_on_builder_transaction) (preview_builder_transaction) (sign_builder_transaction) - (broadcast_transaction) (propose_builder_transaction) (propose_builder_transaction2) (remove_builder_transaction) @@ -2294,7 +2229,6 @@ FC_API( graphene::wallet::wallet_api, (create_committee_member) (get_son) (get_witness) - (is_witness) (get_committee_member) (list_witnesses) (list_committee_members) @@ -2319,9 +2253,9 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) + (create_vesting_balance) (get_vesting_balances) (withdraw_vesting) - (withdraw_GPOS_vesting_balance) (vote_for_committee_member) (vote_for_son) (update_son_votes) @@ -2350,9 +2284,6 @@ FC_API( graphene::wallet::wallet_api, (save_wallet_file) (serialize_transaction) (sign_transaction) - (get_transaction_signers) - (get_key_references) - (add_transaction_signature) (get_prototype_operation) (propose_parameter_change) (propose_fee_change) @@ -2408,7 +2339,6 @@ FC_API( graphene::wallet::wallet_api, (tournament_join) (tournament_leave) (rps_throw) - (create_vesting_balance) (get_upcoming_tournaments) (get_tournaments) (get_tournaments_by_state) @@ -2420,5 +2350,4 @@ FC_API( graphene::wallet::wallet_api, (get_matched_bets_for_bettor) (get_all_matched_bets_for_bettor) (buy_ticket) - (quit) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index f836fb70..aca8c04f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include @@ -91,8 +90,6 @@ # include #endif -template class fc::api; - #define BRAIN_KEY_WORD_COUNT 16 namespace graphene { namespace wallet { @@ -278,7 +275,6 @@ public: private: void claim_registered_account(const account_object& account) { - bool import_keys = false; auto it = _wallet.pending_account_registrations.find( account.name ); FC_ASSERT( it != _wallet.pending_account_registrations.end() ); for (const std::string& wif_key : it->second) @@ -294,13 +290,8 @@ private: // possibility of migrating to a fork where the // name is available, the user can always // manually re-register) - } else { - import_keys = true; } _wallet.pending_account_registrations.erase( it ); - - if (import_keys) - save_wallet_file(); } // after a son registration succeeds, this saves the private key in the wallet permanently @@ -376,8 +367,7 @@ private: for( const fc::optional& optional_account : owner_account_objects ) if (optional_account) { - std::string account_id = account_id_to_string(optional_account->id); - fc::optional witness_obj = _remote_db->get_witness_by_account(account_id); + fc::optional witness_obj = _remote_db->get_witness_by_account(optional_account->id); if (witness_obj) claim_registered_witness(optional_account->name); } @@ -645,13 +635,6 @@ public: fc::async([this, object]{subscribed_object_changed(object);}, "Object changed"); } - void quit() - { - ilog( "Quitting Cli Wallet ..." ); - - throw fc::canceled_exception(); - } - bool copy_wallet_file( string destination_filename ) { fc::path src_path = get_wallet_filename(); @@ -770,17 +753,9 @@ public: { return _remote_db->get_dynamic_global_properties(); } - std::string account_id_to_string(account_id_type id) const - { - std::string account_id = fc::to_string(id.space_id) - + "." + fc::to_string(id.type_id) - + "." + fc::to_string(id.instance.value); - return account_id; - } account_object get_account(account_id_type id) const { - std::string account_id = account_id_to_string(id); - auto rec = _remote_db->get_accounts({account_id}).front(); + auto rec = _remote_db->get_accounts({id}).front(); FC_ASSERT(rec); return *rec; } @@ -802,16 +777,9 @@ public: { return get_account(account_name_or_id).get_id(); } - std::string asset_id_to_string(asset_id_type id) const - { - std::string asset_id = fc::to_string(id.space_id) + - "." + fc::to_string(id.type_id) + - "." + fc::to_string(id.instance.value); - return asset_id; - } optional find_asset(asset_id_type id)const { - auto rec = _remote_db->get_assets({asset_id_to_string(id)}).front(); + auto rec = _remote_db->get_assets({id}).front(); if( rec ) _asset_cache[id] = *rec; return rec; @@ -1075,7 +1043,7 @@ public: ("chain_id", _chain_id) ); size_t account_pagination = 100; - vector< std::string > account_ids_to_send; + vector< account_id_type > account_ids_to_send; size_t n = _wallet.my_accounts.size(); account_ids_to_send.reserve( std::min( account_pagination, n ) ); auto it = _wallet.my_accounts.begin(); @@ -1090,8 +1058,7 @@ public: { assert( it != _wallet.my_accounts.end() ); old_accounts.push_back( *it ); - std::string account_id = account_id_to_string(old_accounts.back().id); - account_ids_to_send.push_back( account_id ); + account_ids_to_send.push_back( old_accounts.back().id ); ++it; } std::vector< optional< account_object > > accounts = _remote_db->get_accounts(account_ids_to_send); @@ -1123,7 +1090,6 @@ public: return true; } - void save_wallet_file(string wallet_filename = "") { // @@ -1138,7 +1104,7 @@ public: if( wallet_filename == "" ) wallet_filename = _wallet_filename; - ilog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); + wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); string data = fc::json::to_pretty_string( _wallet ); try @@ -1150,38 +1116,14 @@ public: // // http://en.wikipedia.org/wiki/Most_vexing_parse // - std::string tmp_wallet_filename = wallet_filename + ".tmp"; - fc::ofstream outfile{ fc::path( tmp_wallet_filename ) }; + fc::ofstream outfile{ fc::path( wallet_filename ) }; outfile.write( data.c_str(), data.length() ); outfile.flush(); outfile.close(); - - ilog( "saved successfully wallet to tmp file ${fn}", ("fn", tmp_wallet_filename) ); - - std::string wallet_file_content; - fc::read_file_contents(tmp_wallet_filename, wallet_file_content); - - if (wallet_file_content == data) { - dlog( "validated successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); - fc::rename( tmp_wallet_filename, wallet_filename ); - dlog( "renamed successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); - } - else - { - FC_THROW("tmp wallet file cannot be validated ${fn}", ("fn", tmp_wallet_filename) ); - } - - ilog( "successfully saved wallet to file ${fn}", ("fn", wallet_filename) ); - disable_umask_protection(); } catch(...) { - string ws_password = _wallet.ws_password; - _wallet.ws_password = ""; - elog("wallet file content is: ${data}", ("data", fc::json::to_pretty_string( _wallet ) ) ); - _wallet.ws_password = ws_password; - disable_umask_protection(); throw; } @@ -1243,20 +1185,6 @@ public: return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast); } - - pair broadcast_transaction(signed_transaction tx) - { - try { - _remote_net_broadcast->broadcast_transaction(tx); - } - catch (const fc::exception& e) { - elog("Caught exception while broadcasting tx ${id}: ${e}", - ("id", tx.id().str())("e", e.to_detail_string())); - throw; - } - return std::make_pair(tx.id(),tx); - } - signed_transaction propose_builder_transaction( transaction_handle_type handle, time_point_sec expiration = time_point::now() + fc::minutes(1), @@ -1817,7 +1745,7 @@ public: committee_member_create_operation committee_member_create_op; committee_member_create_op.committee_member_account = get_account_id(owner_account); committee_member_create_op.url = url; - if (_remote_db->get_committee_member_by_account(owner_account)) + if (_remote_db->get_committee_member_by_account(committee_member_create_op.committee_member_account)) FC_THROW("Account ${owner_account} is already a committee_member", ("owner_account", owner_account)); signed_transaction tx; @@ -1847,7 +1775,7 @@ public: // then maybe it's the owner account try { - std::string owner_account_id = account_id_to_string(get_account_id(owner_account)); + account_id_type owner_account_id = get_account_id(owner_account); fc::optional witness = _remote_db->get_witness_by_account(owner_account_id); if (witness) return *witness; @@ -1898,42 +1826,6 @@ public: FC_CAPTURE_AND_RETHROW( (owner_account) ) } - bool is_witness(string owner_account) - { - try - { - fc::optional witness_id = maybe_id(owner_account); - if (witness_id) - { - std::vector ids_to_get; - ids_to_get.push_back(*witness_id); - std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get); - if (witness_objects.front()) - return true; - else - return false; - } - else - { - // then maybe it's the owner account - try - { - std::string owner_account_id = account_id_to_string(get_account_id(owner_account)); - fc::optional witness = _remote_db->get_witness_by_account(owner_account_id); - if (witness) - return true; - else - return false; - } - catch (const fc::exception&) - { - return false; - } - } - } - FC_CAPTURE_AND_RETHROW( (owner_account) ) - } - committee_member_object get_committee_member(string owner_account) { try @@ -1953,7 +1845,8 @@ public: // then maybe it's the owner account try { - fc::optional committee_member = _remote_db->get_committee_member_by_account(owner_account); + account_id_type owner_account_id = get_account_id(owner_account); + fc::optional committee_member = _remote_db->get_committee_member_by_account(owner_account_id); if (committee_member) return *committee_member; else @@ -2096,16 +1989,13 @@ public: return swi.son_id; }); std::vector> son_objects = _remote_db->get_sons(son_ids); - vector owners; + vector owners; for(auto obj: son_objects) { if (obj) - { - std::string acc_id = account_id_to_string(obj->son_account); - owners.push_back(acc_id); - } + owners.push_back(obj->son_account); } - vector< optional< account_object> > accs = _remote_db->get_accounts(owners); + vector> accs = _remote_db->get_accounts(owners); std::remove_if(son_objects.begin(), son_objects.end(), [](const fc::optional& obj) -> bool { return obj.valid(); }); map result; @@ -2228,7 +2118,7 @@ public: witness_create_op.initial_secret = enc.result(); - if (_remote_db->get_witness_by_account(account_id_to_string(witness_create_op.witness_account))) + if (_remote_db->get_witness_by_account(witness_create_op.witness_account)) FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account)); signed_transaction tx; @@ -2388,14 +2278,13 @@ public: vesting_balance_type vesting_type, bool broadcast /* = false */) { try { - FC_ASSERT( !is_locked() ); - account_object user_account = get_account(owner_account); + account_object son_account = get_account(owner_account); fc::optional asset_obj = get_asset(asset_symbol); FC_ASSERT(asset_obj, "Invalid asset symbol {asst}", ("asst", asset_symbol)); vesting_balance_create_operation op; - op.creator = user_account.get_id(); - op.owner = user_account.get_id(); + op.creator = son_account.get_id(); + op.owner = son_account.get_id(); op.amount = asset_obj->amount_from_string(amount); op.balance_type = vesting_type; if (op.balance_type == vesting_balance_type::son) @@ -2421,7 +2310,12 @@ public: return result; } - vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( account_name ); + // try casting to avoid a round-trip if we were given an account ID + fc::optional acct_id = maybe_id( account_name ); + if( !acct_id ) + acct_id = get_account( account_name ).id; + + vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( *acct_id ); if( vbos.size() == 0 ) return result; @@ -2442,21 +2336,12 @@ public: fc::optional vbid = maybe_id(witness_name); if( !vbid ) { - if (is_witness(witness_name)) - { - witness_object wit = get_witness( witness_name ); - FC_ASSERT( wit.pay_vb, "Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); - vbid = wit.pay_vb; - } - else - FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); + witness_object wit = get_witness( witness_name ); + FC_ASSERT( wit.pay_vb ); + vbid = wit.pay_vb; } vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid ); - - if(vbo.balance_type != vesting_balance_type::normal) - FC_THROW("Allowed to withdraw only Normal type vest balances with this method"); - vesting_balance_withdraw_operation vesting_balance_withdraw_op; vesting_balance_withdraw_op.vesting_balance = *vbid; @@ -2472,100 +2357,21 @@ public: } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) ) } - signed_transaction withdraw_GPOS_vesting_balance( - string account_name, - string amount, - string asset_symbol, - bool broadcast = false) - { try { - - //Can be deleted after GPOS hardfork time - time_point_sec now = time_point::now(); - if(now < HARDFORK_GPOS_TIME) - FC_THROW("GPOS related functionality is not avaiable until next Spring"); - - asset_object asset_obj = get_asset( asset_symbol ); - vector< vesting_balance_object > vbos; - vesting_balance_object vbo; - fc::optional vbid = maybe_id(account_name); - if( !vbid ) - { - vbos = _remote_db->get_vesting_balances( account_name ); - if( vbos.size() == 0 ) - FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name)); - } - - //whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types - if(!vbos.size()) - vbos.emplace_back( get_object(*vbid) ); - - for (const vesting_balance_object& vesting_balance_obj: vbos) { - if(vesting_balance_obj.balance_type == vesting_balance_type::gpos) - { - vbo = vesting_balance_obj; - break; - } - } - - vesting_balance_withdraw_operation vesting_balance_withdraw_op; - - vesting_balance_withdraw_op.vesting_balance = vbo.id; - vesting_balance_withdraw_op.owner = vbo.owner; - vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount); - - signed_transaction tx; - tx.operations.push_back( vesting_balance_withdraw_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (account_name)(amount) ) - } - signed_transaction vote_for_committee_member(string voting_account, string committee_member, bool approve, bool broadcast /* = false */) { try { - std::vector vbo_info = get_vesting_balances(voting_account); - - time_point_sec now = time_point::now(); - if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass - { - std::vector::iterator vbo_iter; - vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); - if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) - FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the committee member", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); - } - account_object voting_account_object = get_account(voting_account); - fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member); + account_id_type committee_member_owner_account_id = get_account_id(committee_member); + fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id); if (!committee_member_obj) FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member)); - - bool update_vote_time = false; - if (approve) { auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id); - if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass - { - account_id_type stake_account = get_account_id(voting_account); - const auto gpos_info = _remote_db->get_gpos_info(stake_account); - const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); - const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); - const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; - - if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) - FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member)); - else - update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF) - } - else - { - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); - } + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); } else { @@ -2576,7 +2382,6 @@ public: account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; - account_update_op.extensions.value.update_last_voting_time = update_vote_time; signed_transaction tx; tx.operations.push_back( account_update_op ); @@ -2591,13 +2396,6 @@ public: bool approve, bool broadcast /* = false */) { try { - - std::vector vbo_info = get_vesting_balances(voting_account); - std::vector::iterator vbo_iter; - vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); - if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) - FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the SON account", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); - account_object voting_account_object = get_account(voting_account); account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account(son_account_id); @@ -2674,57 +2472,26 @@ public: bool approve, bool broadcast /* = false */) { try { - std::vector vbo_info = get_vesting_balances(voting_account); - - time_point_sec now = time_point::now(); - if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass - { - std::vector::iterator vbo_iter; - vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); - if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) - FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the witness", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); - } - account_object voting_account_object = get_account(voting_account); - - fc::optional witness_obj = _remote_db->get_witness_by_account(witness); + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); - - bool update_vote_time = false; if (approve) { auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); - if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass - { - account_id_type stake_account = get_account_id(voting_account); - const auto gpos_info = _remote_db->get_gpos_info(stake_account); - const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); - const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); - const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; - - if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) - FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness)); - else - update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF) - } - else - { - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); - } + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); } else { unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); if (!votes_removed) - FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness)); + FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); } - account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; - account_update_op.extensions.value.update_last_voting_time = update_vote_time; signed_transaction tx; tx.operations.push_back( account_update_op ); @@ -2743,7 +2510,8 @@ public: account_object voting_account_object = get_account(voting_account); for (const std::string& witness : witnesses_to_approve) { - fc::optional witness_obj = _remote_db->get_witness_by_account(witness); + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); @@ -2752,7 +2520,8 @@ public: } for (const std::string& witness : witnesses_to_reject) { - fc::optional witness_obj = _remote_db->get_witness_by_account(witness); + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); if (!witness_obj) FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); @@ -2893,84 +2662,6 @@ public: return tx; } - flat_set get_transaction_signers(const signed_transaction &tx) const - { - return tx.get_signature_keys(_chain_id); - } - - vector> get_key_references(const vector &keys) const - { - return _remote_db->get_key_references(keys); - } - - /** - * Get the required public keys to sign the transaction which had been - * owned by us - * - * NOTE, if `erase_existing_sigs` set to true, the original trasaction's - * signatures will be erased - * - * @param tx The transaction to be signed - * @param erase_existing_sigs - * The transaction could have been partially signed already, - * if set to false, the corresponding public key of existing - * signatures won't be returned. - * If set to true, the existing signatures will be erased and - * all required keys returned. - */ - set get_owned_required_keys( signed_transaction &tx, - bool erase_existing_sigs = true) - { - set pks = _remote_db->get_potential_signatures( tx ); - flat_set owned_keys; - owned_keys.reserve( pks.size() ); - std::copy_if( pks.begin(), pks.end(), - std::inserter( owned_keys, owned_keys.end() ), - [this]( const public_key_type &pk ) { - return _keys.find( pk ) != _keys.end(); - } ); - - if ( erase_existing_sigs ) - tx.signatures.clear(); - - return _remote_db->get_required_signatures( tx, owned_keys ); - } - - signed_transaction add_transaction_signature( signed_transaction tx, - bool broadcast ) - { - set approving_key_set = get_owned_required_keys(tx, false); - - if ( ( ( tx.ref_block_num == 0 && tx.ref_block_prefix == 0 ) || - tx.expiration == fc::time_point_sec() ) && - tx.signatures.empty() ) - { - auto dyn_props = get_dynamic_global_properties(); - auto parameters = get_global_properties().parameters; - fc::time_point_sec now = dyn_props.time; - tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( now + parameters.maximum_time_until_expiration ); - } - for ( const public_key_type &key : approving_key_set ) - tx.sign( get_private_key( key ), _chain_id ); - - if ( broadcast ) - { - try - { - _remote_net_broadcast->broadcast_transaction( tx ); - } - catch ( const fc::exception &e ) - { - elog( "Caught exception while broadcasting tx ${id}: ${e}", - ( "id", tx.id().str() )( "e", e.to_detail_string() ) ); - FC_THROW( "Caught exception while broadcasting tx" ); - } - } - - return tx; - } - signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, @@ -4136,8 +3827,8 @@ map wallet_api::list_accounts(const string& lowerbound, vector wallet_api::list_account_balances(const string& id) { if( auto real_id = detail::maybe_id(id) ) - return my->_remote_db->get_account_balances(id, flat_set()); - return my->_remote_db->get_account_balances(id, flat_set()); + return my->_remote_db->get_account_balances(*real_id, flat_set()); + return my->_remote_db->get_account_balances(get_account(id).id, flat_set()); } vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const @@ -4173,7 +3864,8 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const vector wallet_api::get_account_history(string name, int limit) const { vector result; - + auto account_id = get_account(name).get_id(); + while (limit > 0) { bool skip_first_row = false; @@ -4193,7 +3885,7 @@ vector wallet_api::get_account_history(string name, int limit) int page_limit = skip_first_row ? std::min(100, limit + 1) : std::min(100, limit); - vector current = my->_remote_hist->get_account_history(name, operation_history_id_type(), + vector current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(), page_limit, start); bool first_row = true; for (auto &o : current) @@ -4228,10 +3920,11 @@ vector wallet_api::get_relative_account_history(string name, u FC_ASSERT( start > 0 || limit <= 100 ); vector result; + auto account_id = get_account(name).get_id(); while( limit > 0 ) { - vector current = my->_remote_hist->get_relative_account_history(name, stop, std::min(100, limit), start); + vector current = my->_remote_hist->get_relative_account_history(account_id, stop, std::min(100, limit), start); for (auto &o : current) { std::stringstream ss; auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); @@ -4252,22 +3945,22 @@ vector wallet_api::list_core_accounts()const vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket , fc::time_point_sec start, fc::time_point_sec end )const { - return my->_remote_hist->get_market_history( symbol1, symbol2, bucket, start, end ); + return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, start, end ); } vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const { - return my->_remote_db->get_limit_orders(a, b, limit); + return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); } vector wallet_api::get_call_orders(string a, uint32_t limit)const { - return my->_remote_db->get_call_orders(a, limit); + return my->_remote_db->get_call_orders(get_asset(a).id, limit); } vector wallet_api::get_settle_orders(string a, uint32_t limit)const { - return my->_remote_db->get_settle_orders(a, limit); + return my->_remote_db->get_settle_orders(get_asset(a).id, limit); } brain_key_info wallet_api::suggest_brain_key()const @@ -4364,11 +4057,6 @@ signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type return my->sign_builder_transaction(transaction_handle, broadcast); } -pair wallet_api::broadcast_transaction(signed_transaction tx) -{ - return my->broadcast_transaction(tx); -} - signed_transaction wallet_api::propose_builder_transaction( transaction_handle_type handle, time_point_sec expiration, @@ -4738,11 +4426,6 @@ witness_object wallet_api::get_witness(string owner_account) return my->get_witness(owner_account); } -bool wallet_api::is_witness(string owner_account) -{ - return my->is_witness(owner_account); -} - committee_member_object wallet_api::get_committee_member(string owner_account) { return my->get_committee_member(owner_account); @@ -4911,20 +4594,11 @@ signed_transaction wallet_api::withdraw_vesting( string witness_name, string amount, string asset_symbol, - bool broadcast) + bool broadcast /* = false */) { return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast ); } -signed_transaction wallet_api::withdraw_GPOS_vesting_balance( - string account_name, - string amount, - string asset_symbol, - bool broadcast) -{ - return my->withdraw_GPOS_vesting_balance( account_name, amount, asset_symbol, broadcast ); -} - signed_transaction wallet_api::vote_for_committee_member(string voting_account, string witness, bool approve, @@ -4993,22 +4667,6 @@ signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broa return my->sign_transaction( tx, broadcast); } FC_CAPTURE_AND_RETHROW( (tx) ) } -signed_transaction wallet_api::add_transaction_signature( signed_transaction tx, - bool broadcast ) -{ - return my->add_transaction_signature( tx, broadcast ); -} - -flat_set wallet_api::get_transaction_signers(const signed_transaction &tx) const -{ try { - return my->get_transaction_signers(tx); -} FC_CAPTURE_AND_RETHROW( (tx) ) } - -vector> wallet_api::get_key_references(const vector &keys) const -{ try { - return my->get_key_references(keys); -} FC_CAPTURE_AND_RETHROW( (keys) ) } - operation wallet_api::get_prototype_operation(string operation_name) { return my->get_prototype_operation( operation_name ); @@ -5196,11 +4854,6 @@ bool wallet_api::load_wallet_file( string wallet_filename ) return my->load_wallet_file( wallet_filename ); } -void wallet_api::quit() -{ - my->quit(); -} - void wallet_api::save_wallet_file( string wallet_filename ) { my->save_wallet_file( wallet_filename ); @@ -6738,10 +6391,7 @@ vesting_balance_object_with_info::vesting_balance_object_with_info( const vestin : vesting_balance_object( vbo ) { allowed_withdraw = get_allowed_withdraw( now ); - if(vbo.balance_type == vesting_balance_type::gpos) - allowed_withdraw_time = vbo.policy.get().begin_timestamp + vbo.policy.get().vesting_cliff_seconds; - else - allowed_withdraw_time = now; + allowed_withdraw_time = now; } } } // graphene::wallet diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index b7abdabe..d68f25b8 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -113,13 +113,11 @@ int main( int argc, char** argv ) cfg.appenders.push_back(fc::appender_config( "rpc", "file", fc::variant(ac, 5))); cfg.loggers = { fc::logger_config("default"), fc::logger_config( "rpc") }; - cfg.loggers.front().level = fc::log_level::warn; + cfg.loggers.front().level = fc::log_level::info; cfg.loggers.front().appenders = {"default"}; - cfg.loggers.back().level = fc::log_level::info; + cfg.loggers.back().level = fc::log_level::debug; cfg.loggers.back().appenders = {"rpc"}; - fc::configure_logging( cfg ); - fc::ecc::private_key committee_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); idump( (key_to_wif( committee_private_key ) ) ); @@ -176,7 +174,7 @@ int main( int argc, char** argv ) fc::http::websocket_client client; idump((wdata.ws_server)); auto con = client.connect( wdata.ws_server ); - auto apic = std::make_shared(*con, GRAPHENE_MAX_NESTED_OBJECTS); + auto apic = std::make_shared(con, GRAPHENE_MAX_NESTED_OBJECTS); auto remote_api = apic->get_remote_api< login_api >(1); edump((wdata.ws_user)(wdata.ws_password) ); @@ -215,7 +213,7 @@ int main( int argc, char** argv ) _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){ std::cout << "here... \n"; wlog("." ); - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); @@ -232,7 +230,7 @@ int main( int argc, char** argv ) if( options.count("rpc-tls-endpoint") ) { _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 44048602..e43172a0 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc graphene_snapshot graphene_es_objects peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index 12f2581e..1e18d592 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -1,5 +1,5 @@ { - "initial_timestamp": "2020-01-13T06:03:15", + "initial_timestamp": "2019-05-14T18:47:51", "max_core_supply": "1000000000000000", "initial_parameters": { "current_fees": { @@ -307,27 +307,6 @@ 75,{} ],[ 76,{} - ],[ - 77,{ - "lottery_asset": 2000000, - "price_per_kbyte": 10 - } - ],[ - 78,{ - "fee": 0 - } - ],[ - 79,{ - "fee": 0 - } - ],[ - 80,{ - "fee": 0 - } - ],[ - 81,{ - "fee": 2000000 - } ] ], "scale": 10000 @@ -374,20 +353,13 @@ "maximum_tournament_start_delay": 604800, "maximum_tournament_number_of_wins": 100, "extensions": { - "sweeps_distribution_percentage": 200, - "sweeps_distribution_asset": "1.3.0", - "sweeps_vesting_accumulator_account": "1.2.0", - "gpos_period": 15552000, - "gpos_subperiod": 2592000, - "gpos_period_start": 1601528400, - "gpos_vesting_lockin_period": 2592000, - "son_vesting_amount": 5000000, - "son_vesting_period": 172800, - "son_pay_daily_max": 20000000, - "son_pay_time": 86400, - "son_deregister_time": 43200, - "son_heartbeat_frequency": 180, - "son_down_time": 360 + "son_vesting_amount": 5000000, + "son_vesting_period": 172800, + "son_pay_daily_max": 20000000, + "son_pay_time": 86400, + "son_deregister_time": 43200, + "son_heartbeat_frequency": 180, + "son_down_time": 360 } }, "initial_bts_accounts": [], diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 7823fed3..6675ee87 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -25,11 +25,8 @@ #include #include -#include #include #include -#include -#include #include //#include //#include @@ -37,7 +34,7 @@ #include #include #include -#include +//#include #include #include @@ -87,10 +84,7 @@ int main(int argc, char** argv) { "Space-separated list of plugins to activate"); auto witness_plug = node->register_plugin(); - auto debug_witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); - auto elasticsearch_plug = node->register_plugin(); - auto es_objects_plug = node->register_plugin(); auto market_history_plug = node->register_plugin(); //auto generate_genesis_plug = node->register_plugin(); //auto generate_uia_sharedrop_genesis_plug = node->register_plugin(); @@ -98,7 +92,7 @@ int main(int argc, char** argv) { auto affiliate_stats_plug = node->register_plugin(); auto bookie_plug = node->register_plugin(); auto peerplays_sidechain = node->register_plugin(); - auto snapshot_plug = node->register_plugin(); +// auto snapshot_plug = node->register_plugin(); // add plugin options to config try @@ -177,7 +171,7 @@ int main(int argc, char** argv) { exit_promise->set_value(signal); }, SIGTERM); - ilog("Started Peerplays node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); + ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) ); int signal = exit_promise->wait(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5162f692..b49e089e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,55 +8,51 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie peerplays_sidechain graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) file(GLOB PERFORMANCE_TESTS "performance/*.cpp") add_executable( performance_test ${PERFORMANCE_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB BENCH_MARKS "benchmarks/*.cpp") add_executable( chain_bench ${BENCH_MARKS} ${COMMON_SOURCES} ) -target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) -target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_elasticsearch graphene_es_objects graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( app_test graphene_app graphene_account_history graphene_witness graphene_bookie graphene_net graphene_chain graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB INTENSE_SOURCES "intense/*.cpp") add_executable( intense_test ${INTENSE_SOURCES} ${COMMON_SOURCES} ) -target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB BETTING_TESTS "betting/*.cpp") add_executable( betting_test ${BETTING_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB PEERPLAYS_SIDECHAIN_TESTS "peerplays_sidechain/*.cpp") add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB RANDOM_SOURCES "random/*.cpp") add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} ) -target_link_libraries( random_test graphene_chain graphene_app graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB CLI_SOURCES "cli/*.cpp") add_executable( cli_test ${CLI_SOURCES} ) if(WIN32) list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32) endif() -target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) -file(GLOB ES_SOURCES "elasticsearch/*.cpp") -add_executable( es_test ${ES_SOURCES} ${COMMON_SOURCES} ) -target_link_libraries( es_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) - add_subdirectory( generate_empty_blocks ) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 68b9e1f0..b3775b1f 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -63,7 +63,6 @@ BOOST_AUTO_TEST_CASE( two_node_network ) app1.register_plugin(); boost::program_options::variables_map cfg; cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); - cfg.emplace("plugins", boost::program_options::variable_value(string(" "), false)); app1.initialize(app_dir.path(), cfg); cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); diff --git a/tests/cli/cli_fixture.cpp b/tests/cli/cli_fixture.cpp index 8a382e0b..5b5fd7ad 100644 --- a/tests/cli/cli_fixture.cpp +++ b/tests/cli/cli_fixture.cpp @@ -129,7 +129,7 @@ client_connection::client_connection( wallet_data.ws_password = ""; websocket_connection = websocket_client.connect( wallet_data.ws_server ); - api_connection = std::make_shared(*websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); + api_connection = std::make_shared(websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 106f3c8c..cc155979 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -88,7 +88,6 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses ) witness_object init1_obj = con.wallet_api_ptr->get_witness("init1"); int init1_start_votes = init1_obj.total_votes; // Vote for a witness - con.wallet_api_ptr->create_vesting_balance("nathan", "10000", "1.3.0", vesting_balance_type::gpos, true); signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true); // generate a block to get things started @@ -162,4 +161,4 @@ BOOST_AUTO_TEST_CASE( account_history_pagination ) } } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 7b1f395e..3630000c 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -213,7 +213,6 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_start_votes = son2_obj.total_votes; - con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); // Vote for a son1account BOOST_TEST_MESSAGE("Voting for son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); @@ -332,10 +331,6 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) BOOST_TEST_MESSAGE("Voting for SONs"); for(unsigned int i = 0; i < son_number + 1; i++) { - con.wallet_api_ptr->transfer( - "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); - con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); - std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); } @@ -454,7 +449,6 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); accepted.push_back("son1account"); accepted.push_back("son2account"); - con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 2, true); BOOST_CHECK(generate_block()); @@ -475,7 +469,6 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) accepted.clear(); rejected.clear(); rejected.push_back("son1account"); - con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, 1, true); BOOST_CHECK(generate_maintenance_block()); @@ -626,9 +619,6 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) "http://son" + fc::to_pretty_string(i), sidechain_public_keys, false); - con.wallet_api_ptr->transfer( - "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); - con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); } BOOST_CHECK(generate_maintenance_block()); @@ -701,10 +691,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_TEST_MESSAGE("Voting for SONs"); for(unsigned int i = 1; i < son_number + 1; i++) { - con.wallet_api_ptr->transfer( - "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); - con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); - con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); } BOOST_CHECK(generate_maintenance_block()); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index ba5bde4e..4e171b14 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -29,9 +29,11 @@ #include #include #include -#include -#include +#include + +#include +#include #include #include #include @@ -52,6 +54,7 @@ #include #include #include +#include #include "database_fixture.hpp" @@ -79,7 +82,7 @@ database_fixture::database_fixture() std::cout << "running test " << boost::unit_test::framework::current_test_case().p_name << std::endl; } - //auto ahplugin = app.register_plugin(); + auto ahplugin = app.register_plugin(); auto mhplugin = app.register_plugin(); auto bookieplugin = app.register_plugin(); auto affiliateplugin = app.register_plugin(); @@ -110,7 +113,7 @@ database_fixture::database_fixture() // add account tracking for ahplugin for special test case with track-account enabled if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account") { std::vector track_account; - std::string track = "\"1.2.19\""; + std::string track = "\"1.2.18\""; track_account.push_back(track); options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); options.insert(std::make_pair("partial-operations", boost::program_options::variable_value(true, false))); @@ -120,63 +123,14 @@ database_fixture::database_fixture() std::vector track_account; std::string track = "\"1.2.0\""; track_account.push_back(track); - track = "\"1.2.18\""; + track = "\"1.2.17\""; track_account.push_back(track); options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); } - // standby votes tracking - if( boost::unit_test::framework::current_test_case().p_name.value == "track_votes_witnesses_disabled" || - boost::unit_test::framework::current_test_case().p_name.value == "track_votes_committee_disabled") { - app.chain_database()->enable_standby_votes_tracking( false ); - } - // app.initialize(); - - auto test_name = boost::unit_test::framework::current_test_case().p_name.value; - if(test_name == "elasticsearch_account_history" || test_name == "elasticsearch_suite" || - test_name == "elasticsearch_history_api") { - auto esplugin = app.register_plugin(); - esplugin->plugin_set_app(&app); - - options.insert(std::make_pair("elasticsearch-node-url", boost::program_options::variable_value(string("http://localhost:9200/"), false))); - options.insert(std::make_pair("elasticsearch-bulk-replay", boost::program_options::variable_value(uint32_t(2), false))); - options.insert(std::make_pair("elasticsearch-bulk-sync", boost::program_options::variable_value(uint32_t(2), false))); - options.insert(std::make_pair("elasticsearch-start-es-after-block", boost::program_options::variable_value(uint32_t(0), false))); - options.insert(std::make_pair("elasticsearch-visitor", boost::program_options::variable_value(false, false))); - options.insert(std::make_pair("elasticsearch-operation-object", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("elasticsearch-operation-string", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("elasticsearch-mode", boost::program_options::variable_value(uint16_t(2), false))); - - esplugin->plugin_initialize(options); - esplugin->plugin_startup(); - } - else { - auto ahplugin = app.register_plugin(); - app.enable_plugin("affiliate_stats"); - ahplugin->plugin_set_app(&app); - ahplugin->plugin_initialize(options); - ahplugin->plugin_startup(); - } - - if(test_name == "elasticsearch_objects" || test_name == "elasticsearch_suite") { - auto esobjects_plugin = app.register_plugin(); - esobjects_plugin->plugin_set_app(&app); - - options.insert(std::make_pair("es-objects-elasticsearch-url", boost::program_options::variable_value(string("http://localhost:9200/"), false))); - options.insert(std::make_pair("es-objects-bulk-replay", boost::program_options::variable_value(uint32_t(2), false))); - options.insert(std::make_pair("es-objects-bulk-sync", boost::program_options::variable_value(uint32_t(2), false))); - options.insert(std::make_pair("es-objects-proposals", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("es-objects-accounts", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("es-objects-assets", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("es-objects-balances", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("es-objects-limit-orders", boost::program_options::variable_value(true, false))); - options.insert(std::make_pair("es-objects-asset-bitasset", boost::program_options::variable_value(true, false))); - - esobjects_plugin->plugin_initialize(options); - esobjects_plugin->plugin_startup(); - } - + ahplugin->plugin_set_app(&app); + ahplugin->plugin_initialize(options); mhplugin->plugin_set_app(&app); mhplugin->plugin_initialize(options); bookieplugin->plugin_set_app(&app); @@ -184,6 +138,7 @@ database_fixture::database_fixture() affiliateplugin->plugin_set_app(&app); affiliateplugin->plugin_initialize(options); + ahplugin->plugin_startup(); mhplugin->plugin_startup(); bookieplugin->plugin_startup(); affiliateplugin->plugin_startup(); @@ -239,7 +194,7 @@ void database_fixture::verify_asset_supplies( const database& db ) const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); BOOST_CHECK(core_asset_data.fee_pool == 0); - const auto& statistics_index = db.get_index_type().indices(); + const simple_index& statistics_index = db.get_index_type>(); const auto& balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); const auto& tournaments_index = db.get_index_type().indices(); diff --git a/tests/common/genesis_file_util.hpp b/tests/common/genesis_file_util.hpp index 27a2080f..e058df02 100644 --- a/tests/common/genesis_file_util.hpp +++ b/tests/common/genesis_file_util.hpp @@ -1,5 +1,5 @@ #pragma once -#include + ///////// /// @brief forward declaration, using as a hack to generate a genesis.json file /// for testing diff --git a/tests/elasticsearch/main.cpp b/tests/elasticsearch/main.cpp deleted file mode 100644 index 28d3522c..00000000 --- a/tests/elasticsearch/main.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage 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 - -#include "../common/database_fixture.hpp" - -#define BOOST_TEST_MODULE Elastic Search Database Tests -#include - -using namespace graphene::chain; -using namespace graphene::chain::test; -using namespace graphene::app; - -BOOST_FIXTURE_TEST_SUITE( elasticsearch_tests, database_fixture ) - -BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { - try { - - CURL *curl; // curl handler - curl = curl_easy_init(); - - graphene::utilities::ES es; - es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; - //es.auth = "elastic:changeme"; - - // delete all first - auto delete_account_history = graphene::utilities::deleteAll(es); - fc::usleep(fc::milliseconds(1000)); // this is because index.refresh_interval, nothing to worry - - if(delete_account_history) { // all records deleted - - //account_id_type() do 3 ops - create_bitasset("USD", account_id_type()); - auto dan = create_account("dan"); - auto bob = create_account("bob"); - - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - // for later use - //int asset_crobjeate_op_id = operation::tag::value; - //int account_create_op_id = operation::tag::value; - - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - es.endpoint = es.index_prefix + "*/data/_count"; - es.query = query; - - auto res = graphene::utilities::simpleQuery(es); - variant j = fc::json::from_string(res); - auto total = j["count"].as_string(); - BOOST_CHECK_EQUAL(total, "5"); - - es.endpoint = es.index_prefix + "*/data/_search"; - res = graphene::utilities::simpleQuery(es); - j = fc::json::from_string(res); - auto first_id = j["hits"]["hits"][size_t(0)]["_id"].as_string(); - BOOST_CHECK_EQUAL(first_id, "2.9.0"); - - generate_block(); - auto willie = create_account("willie"); - generate_block(); - - fc::usleep(fc::milliseconds(1000)); // index.refresh_interval - - es.endpoint = es.index_prefix + "*/data/_count"; - res = graphene::utilities::simpleQuery(es); - j = fc::json::from_string(res); - - total = j["count"].as_string(); - BOOST_CHECK_EQUAL(total, "7"); - - // do some transfers in 1 block - transfer(account_id_type()(db), bob, asset(100)); - transfer(account_id_type()(db), bob, asset(200)); - transfer(account_id_type()(db), bob, asset(300)); - - generate_block(); - fc::usleep(fc::milliseconds(1000)); // index.refresh_interval - - res = graphene::utilities::simpleQuery(es); - j = fc::json::from_string(res); - - total = j["count"].as_string(); - BOOST_CHECK_EQUAL(total, "13"); - - // check the visitor data - auto block_date = db.head_block_time(); - std::string index_name = graphene::utilities::generateIndexName(block_date, "peerplays-"); - - es.endpoint = index_name + "/data/2.9.12"; // we know last op is a transfer of amount 300 - res = graphene::utilities::getEndPoint(es); - j = fc::json::from_string(res); - auto last_transfer_amount = j["_source"]["operation_history"]["op_object"]["amount_"]["amount"].as_string(); - BOOST_CHECK_EQUAL(last_transfer_amount, "300"); - } - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(elasticsearch_objects) { - try { - - CURL *curl; // curl handler - curl = curl_easy_init(); - - graphene::utilities::ES es; - es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "ppobjects-"; - //es.auth = "elastic:changeme"; - - // delete all first - auto delete_objects = graphene::utilities::deleteAll(es); - - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - if(delete_objects) { // all records deleted - - // asset and bitasset - create_bitasset("USD", account_id_type()); - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - es.endpoint = es.index_prefix + "*/data/_count"; - es.query = query; - - auto res = graphene::utilities::simpleQuery(es); - variant j = fc::json::from_string(res); - auto total = j["count"].as_string(); - BOOST_CHECK_EQUAL(total, "2"); - - es.endpoint = es.index_prefix + "asset/data/_search"; - res = graphene::utilities::simpleQuery(es); - j = fc::json::from_string(res); - auto first_id = j["hits"]["hits"][size_t(0)]["_source"]["symbol"].as_string(); - BOOST_CHECK_EQUAL(first_id, "USD"); - - auto bitasset_data_id = j["hits"]["hits"][size_t(0)]["_source"]["bitasset_data_id"].as_string(); - es.endpoint = es.index_prefix + "bitasset/data/_search"; - es.query = "{ \"query\" : { \"bool\": { \"must\" : [{ \"term\": { \"object_id\": \""+bitasset_data_id+"\"}}] } } }"; - res = graphene::utilities::simpleQuery(es); - j = fc::json::from_string(res); - auto bitasset_object_id = j["hits"]["hits"][size_t(0)]["_source"]["object_id"].as_string(); - BOOST_CHECK_EQUAL(bitasset_object_id, bitasset_data_id); - } - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(elasticsearch_suite) { - try { - - CURL *curl; // curl handler - curl = curl_easy_init(); - - graphene::utilities::ES es; - es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; - auto delete_account_history = graphene::utilities::deleteAll(es); - fc::usleep(fc::milliseconds(1000)); - es.index_prefix = "ppobjects-"; - auto delete_objects = graphene::utilities::deleteAll(es); - fc::usleep(fc::milliseconds(1000)); - - if(delete_account_history && delete_objects) { // all records deleted - - - } - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(elasticsearch_history_api) { - try { - CURL *curl; // curl handler - curl = curl_easy_init(); - - graphene::utilities::ES es; - es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; - - auto delete_account_history = graphene::utilities::deleteAll(es); - - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - if(delete_account_history) { - - create_bitasset("USD", account_id_type()); // create op 0 - const account_object& dan = create_account("dan"); // create op 1 - create_bitasset("CNY", dan.id); // create op 2 - create_bitasset("BTC", account_id_type()); // create op 3 - create_bitasset("XMR", dan.id); // create op 4 - create_bitasset("EUR", account_id_type()); // create op 5 - create_bitasset("OIL", dan.id); // create op 6 - - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - graphene::app::history_api hist_api(app); - app.enable_plugin("elasticsearch"); - - // f(A, 0, 4, 9) = { 5, 3, 1, 0 } - auto histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(9)); - - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // f(A, 0, 4, 6) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(6)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // f(A, 0, 4, 5) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(5)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // f(A, 0, 4, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(4)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); - - // f(A, 0, 4, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(3)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); - - // f(A, 0, 4, 2) = { 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // f(A, 0, 4, 1) = { 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // f(A, 0, 4, 0) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 4, operation_history_id_type()); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // f(A, 1, 5, 9) = { 5, 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(9)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // f(A, 1, 5, 6) = { 5, 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(6)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // f(A, 1, 5, 5) = { 5, 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(5)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // f(A, 1, 5, 4) = { 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(4)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - - // f(A, 1, 5, 3) = { 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(3)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - - // f(A, 1, 5, 2) = { } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(A, 1, 5, 1) = { } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(A, 1, 5, 0) = { 5, 3 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(1), 5, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // f(A, 0, 3, 9) = { 5, 3, 1 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(9)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(A, 0, 3, 6) = { 5, 3, 1 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(6)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(A, 0, 3, 5) = { 5, 3, 1 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(5)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(A, 0, 3, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(4)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); - - // f(A, 0, 3, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(3)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); - - // f(A, 0, 3, 2) = { 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // f(A, 0, 3, 1) = { 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // f(A, 0, 3, 0) = { 5, 3, 1 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(), 3, operation_history_id_type()); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(B, 0, 4, 9) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(9)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); - - // f(B, 0, 4, 6) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(6)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); - - // f(B, 0, 4, 5) = { 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(5)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(B, 0, 4, 4) = { 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(4)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - - // f(B, 0, 4, 3) = { 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(3)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - - // f(B, 0, 4, 2) = { 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); - - // f(B, 0, 4, 1) = { 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); - - // f(B, 0, 4, 0) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type()); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); - - // f(B, 2, 4, 9) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(9)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - - // f(B, 2, 4, 6) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(6)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - - // f(B, 2, 4, 5) = { 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(5)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - - // f(B, 2, 4, 4) = { 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(4)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - - // f(B, 2, 4, 3) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(3)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(B, 2, 4, 2) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(B, 2, 4, 1) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(B, 2, 4, 0) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - - // 0 limits - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 0, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(3), 0, operation_history_id_type(9)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // non existent account - histories = hist_api.get_account_history("1.2.18", operation_history_id_type(0), 4, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // create a new account C = alice { 7 } - auto alice = create_account("alice"); - - generate_block(); - fc::usleep(fc::milliseconds(1000)); - - // f(C, 0, 4, 10) = { 7 } - histories = hist_api.get_account_history("alice", operation_history_id_type(0), 4, operation_history_id_type(10)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); - - // f(C, 8, 4, 10) = { } - histories = hist_api.get_account_history("alice", operation_history_id_type(8), 4, operation_history_id_type(10)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 } - histories = hist_api.get_account_history("1.2.0", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 5u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[4].id.instance(), 0u); - } - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index b7ed69fe..9f74a34c 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -745,8 +745,6 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } - - generate_block(); transfer(account_id_type()(db), nathan, asset(5000)); generate_blocks(maintenence_time - initial_properties.parameters.block_interval); @@ -961,23 +959,18 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) processed_transaction ptx; account_object committee_account_object = committee_account(db); - generate_block(skip_flags); // transfer from committee account to Sam account transfer(committee_account_object, sam_account_object, core.amount(100000)); generate_block(skip_flags); - private_key_type charlie_key = generate_private_key("charlie"); - create_account("charlie", charlie_key); + create_account("alice"); generate_block(skip_flags); + create_account("bob"); generate_block(skip_flags); - private_key_type bob_key = generate_private_key("bob"); - create_account("bob", bob_key); - generate_block(skip_flags); - + db.pop_block(); db.pop_block(); - } catch(const fc::exception& e) { edump( (e.to_detail_string()) ); throw; diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp deleted file mode 100644 index aa9969ee..00000000 --- a/tests/tests/gpos_tests.cpp +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * Copyright (c) 2018 oxarbitrage 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 -#include -#include -#include - -#include "../common/database_fixture.hpp" - -#include - -using namespace graphene::chain; -using namespace graphene::chain::test; - -struct gpos_fixture: database_fixture -{ - const worker_object& create_worker( const account_id_type owner, const share_type daily_pay, - const fc::microseconds& duration ) { - worker_create_operation op; - op.owner = owner; - op.daily_pay = daily_pay; - op.initializer = vesting_balance_worker_initializer(1); - op.work_begin_date = db.head_block_time(); - op.work_end_date = op.work_begin_date + duration; - trx.operations.push_back(op); - set_expiration(db, trx); - trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); - trx.clear(); - return db.get(ptx.operation_results[0].get()); - } - const vesting_balance_object& create_vesting(const account_id_type owner, const asset amount, - const vesting_balance_type type) - { - vesting_balance_create_operation op; - op.creator = owner; - op.owner = owner; - op.amount = amount; - op.balance_type = type; - - trx.operations.push_back(op); - set_expiration(db, trx); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - return db.get(ptx.operation_results[0].get()); - } - - void withdraw_gpos_vesting(const vesting_balance_id_type v_bid, const account_id_type owner, const asset amount, - /*const vesting_balance_type type, */const fc::ecc::private_key& key) - { - vesting_balance_withdraw_operation op; - op.vesting_balance = v_bid; - op.owner = owner; - op.amount = amount; - //op.balance_type = type; - - trx.operations.push_back(op); - set_expiration(db, trx); - trx.validate(); - sign(trx, key); - PUSH_TX(db, trx); - trx.clear(); - } - - void update_payout_interval(std::string asset_name, fc::time_point start, uint32_t interval) - { - auto dividend_holder_asset_object = get_asset(asset_name); - asset_update_dividend_operation op; - op.issuer = dividend_holder_asset_object.issuer; - op.asset_to_update = dividend_holder_asset_object.id; - op.new_options.next_payout_time = start; - op.new_options.payout_interval = interval; - trx.operations.push_back(op); - set_expiration(db, trx); - PUSH_TX(db, trx, ~0); - trx.operations.clear(); - } - - void update_gpos_global(uint32_t vesting_period, uint32_t vesting_subperiod, fc::time_point_sec period_start) - { - db.modify(db.get_global_properties(), [vesting_period, vesting_subperiod, period_start](global_property_object& p) { - p.parameters.extensions.value.gpos_period = vesting_period; - p.parameters.extensions.value.gpos_subperiod = vesting_subperiod; - p.parameters.extensions.value.gpos_period_start = period_start.sec_since_epoch(); - p.parameters.extensions.value.gpos_vesting_lockin_period = vesting_subperiod; - }); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), vesting_period); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), vesting_subperiod); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), period_start.sec_since_epoch()); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_vesting_lockin_period(), vesting_subperiod); - } - - void update_maintenance_interval(uint32_t new_interval) - { - db.modify(db.get_global_properties(), [new_interval](global_property_object& p) { - p.parameters.maintenance_interval = new_interval; - }); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, new_interval); - } - - void vote_for(const account_id_type account_id, const vote_id_type vote_for, const fc::ecc::private_key& key) - { - account_update_operation op; - op.account = account_id; - op.new_options = account_id(db).options; - op.new_options->votes.insert(vote_for); - op.extensions.value.update_last_voting_time = true; - trx.operations.push_back(op); - set_expiration(db, trx); - trx.validate(); - sign(trx, key); - PUSH_TX(db, trx); - trx.clear(); - } - void fill_reserve_pool(const account_id_type account_id, asset amount) - { - asset_reserve_operation op; - op.payer = account_id; - op.amount_to_reserve = amount; - trx.operations.push_back(op); - trx.validate(); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.clear(); - } - - void advance_x_maint(int periods) - { - for(int i=0; i(ptx.operation_results[0].get()); - - // check created vesting amount and policy - BOOST_CHECK_EQUAL(alice_vesting.balance.amount.value, 100); - BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_duration_seconds, - db.get_global_properties().parameters.gpos_subperiod()); - BOOST_CHECK_EQUAL(alice_vesting.policy.get().vesting_cliff_seconds, - db.get_global_properties().parameters.gpos_subperiod()); - - // bob creates a gpos vesting with his custom policy - { - vesting_balance_create_operation op; - op.creator = bob_id; - op.owner = bob_id; - op.amount = core.amount(200); - op.balance_type = vesting_balance_type::gpos; - op.policy = cdd_vesting_policy_initializer{ 60*60*24 }; - - trx.operations.push_back(op); - set_expiration(db, trx); - ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - } - auto bob_vesting = db.get(ptx.operation_results[0].get()); - - generate_block(); - - // policy is not the one defined by the user but default - BOOST_CHECK_EQUAL(bob_vesting.balance.amount.value, 200); - BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_duration_seconds, - db.get_global_properties().parameters.gpos_subperiod()); - BOOST_CHECK_EQUAL(bob_vesting.policy.get().vesting_cliff_seconds, - db.get_global_properties().parameters.gpos_subperiod()); - - } - catch (fc::exception& e) - { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( dividends ) -{ - ACTORS((alice)(bob)); - try - { - // move to 1 week before hardfork - generate_blocks( HARDFORK_GPOS_TIME - fc::days(7) ); - generate_block(); - - const auto& core = asset_id_type()(db); - - // all core coins are in the committee_account - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 1000000000000000); - - // transfer half of the total stake to alice so not all the dividends will go to the committee_account - transfer( committee_account, alice_id, core.amount( 500000000000000 ) ); - generate_block(); - - // send some to bob - transfer( committee_account, bob_id, core.amount( 1000 ) ); - generate_block(); - - // committee balance - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999999000); - - // alice balance - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000000); - - // bob balance - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000); - - // get core asset object - const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); - - // by default core token pays dividends once per month - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days - - // update the payout interval for speed purposes of the test - update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day - - generate_block(); - - BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 86400); // 1 day now - - // get the dividend distribution account - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - - // transfering some coins to distribution account. - // simulating the blockchain haves some dividends to pay. - transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); - generate_block(); - - // committee balance - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998900 ); - - // distribution account balance - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); - - // get when is the next payout time as we need to advance there - auto next_payout_time = dividend_data.options.next_payout_time; - - // advance to next payout - generate_blocks(*next_payout_time); - wdump((*next_payout_time)); - - // advance to next maint after payout time arrives - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // check balances now, dividends are paid "normally" - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998949 ); - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 ); - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 1); - - // advance to hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - - // advance to next maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // send 99 to the distribution account so it will have 100 PPY again to share - transfer( committee_account, dividend_distribution_account.id, core.amount( 99 ) ); - generate_block(); - - // get when is the next payout time as we need to advance there - next_payout_time = dividend_data.options.next_payout_time; - - // advance to next payout - generate_blocks(*next_payout_time); - - // advance to next maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // make sure no dividends were paid "normally" - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999998850 ); - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 500000000000050 ); - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); - - // create vesting balance - create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); - - // need to vote to get paid - auto witness1 = witness_id_type(1)(db); - vote_for(bob_id, witness1.vote_id, bob_private_key); - - generate_block(); - - // check balances - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 900 ); - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 100); - - // advance to next payout - generate_blocks(*next_payout_time); - - // advance to next maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // check balances, dividends paid to bob - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 1000 ); - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, core), 0); - } - catch (fc::exception& e) - { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( gpos_basic_dividend_distribution_to_core_asset ) -{ - using namespace graphene; - ACTORS((alice)(bob)(carol)(dave)); - try { - const auto& core = asset_id_type()(db); - BOOST_TEST_MESSAGE("Creating test asset"); - { - asset_create_operation creator; - creator.issuer = account_id_type(); - creator.fee = asset(); - creator.symbol = "TESTB"; - creator.common_options.max_supply = 100000000; - creator.precision = 2; - creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/ - creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; - creator.common_options.flags = charge_market_fee; - creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))}); - trx.operations.push_back(std::move(creator)); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - } - - // pass hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - generate_block(); - - const auto& dividend_holder_asset_object = asset_id_type(0)(db); - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - const account_object& alice = get_account("alice"); - const account_object& bob = get_account("bob"); - const account_object& carol = get_account("carol"); - const account_object& dave = get_account("dave"); - const auto& test_asset_object = get_asset("TESTB"); - - auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue) - { - asset_issue_operation op; - op.issuer = asset_to_issue.issuer; - op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id); - op.issue_to_account = destination_account.id; - trx.operations.push_back( op ); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - }; - - auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) { - int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id, - holder_account_obj.id, - payout_asset_obj.id); - BOOST_CHECK_EQUAL(pending_balance, expected_balance); - }; - - auto advance_to_next_payout_time = [&]() { - // Advance to the next upcoming payout time - BOOST_REQUIRE(dividend_data.options.next_payout_time); - fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time; - idump((next_payout_scheduled_time)); - // generate blocks up to the next scheduled time - generate_blocks(next_payout_scheduled_time); - // if the scheduled time fell on a maintenance interval, then we should have paid out. - // if not, we need to advance to the next maintenance interval to trigger the payout - if (dividend_data.options.next_payout_time) - { - // we know there was a next_payout_time set when we entered this, so if - // it has been cleared, we must have already processed payouts, no need to - // further advance time. - BOOST_REQUIRE(dividend_data.options.next_payout_time); - if (*dividend_data.options.next_payout_time == next_payout_scheduled_time) - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); // get the maintenance skip slots out of the way - } - idump((db.head_block_time())); - }; - - // the first test will be testing pending balances, so we need to hit a - // maintenance interval that isn't the payout interval. Payout is - // every 3 days, maintenance interval is every 1 day. - advance_to_next_payout_time(); - - // Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total - // supply of the core asset. - // Then deposit 400 TEST in the distribution account, and see that they - // each are credited 100 TEST. - transfer( committee_account(db), alice, asset( 250000000000000 ) ); - transfer( committee_account(db), bob, asset( 250000000000000 ) ); - transfer( committee_account(db), carol, asset( 250000000000000 ) ); - transfer( committee_account(db), dave, asset( 250000000000000 ) ); - - // create vesting balance - // bob has not vested anything - create_vesting(alice_id, core.amount(25000000), vesting_balance_type::gpos); - create_vesting(carol_id, core.amount(25000000), vesting_balance_type::gpos); - create_vesting(dave_id, core.amount(25000000), vesting_balance_type::gpos); - - // need to vote to get paid - // carol doesn't participate in voting - auto witness1 = witness_id_type(1)(db); - vote_for(alice_id, witness1.vote_id, alice_private_key); - vote_for(bob_id, witness1.vote_id, bob_private_key); - vote_for(dave_id, witness1.vote_id, dave_private_key); - - // issuing 30000 TESTB to the dividend account - // alice and dave should receive 10000 TESTB as they have gpos vesting and - // participated in voting - // bob should not receive any TESTB as he doesn't have gpos vested - // carol should not receive any TESTB as she doesn't participated in voting - // remaining 10000 TESTB should be deposited in commitee_accoount. - BOOST_TEST_MESSAGE("Issuing 30000 TESTB to the dividend account"); - issue_asset_to_account(test_asset_object, dividend_distribution_account, 30000); - - generate_block(); - - 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 - - verify_pending_balance(alice, test_asset_object, 10000); - verify_pending_balance(bob, test_asset_object, 0); - verify_pending_balance(carol, test_asset_object, 0); - verify_pending_balance(dave, test_asset_object, 10000); - - advance_to_next_payout_time(); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); // get the maintenance skip slots out of the way - - auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout) - { - BOOST_TEST_MESSAGE("Verifying the virtual op was created"); - const account_transaction_history_index& hist_idx = db.get_index_type(); - auto account_history_range = hist_idx.indices().get().equal_range(boost::make_tuple(destination_account.id)); - BOOST_REQUIRE(account_history_range.first != account_history_range.second); - const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db); - const asset_dividend_distribution_operation& distribution_operation = history_object.op.get(); - BOOST_CHECK(distribution_operation.account_id == destination_account.id); - BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout) - != distribution_operation.amounts.end()); - }; - - BOOST_TEST_MESSAGE("Verifying the payouts"); - BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 10000); - verify_dividend_payout_operations(alice, asset(10000, test_asset_object.id)); - verify_pending_balance(alice, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0); - verify_pending_balance(bob, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0); - verify_pending_balance(carol, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 10000); - verify_dividend_payout_operations(dave, asset(10000, test_asset_object.id)); - verify_pending_balance(dave, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(account_id_type(0)(db), test_asset_object), 10000); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( votes_on_gpos_activation ) -{ - ACTORS((alice)(bob)); - try { - const auto& core = asset_id_type()(db); - - // send some asset to alice and bob - transfer( committee_account, alice_id, core.amount( 1000 ) ); - transfer( committee_account, bob_id, core.amount( 1000 ) ); - generate_block(); - - // update default gpos - auto now = db.head_block_time(); - // 5184000 = 60x60x24x6 = 6 days - // 864000 = 60x60x24x1 = 1 days - update_gpos_global(518400, 86400, HARDFORK_GPOS_TIME); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch()); - // no votes for witness 1 - auto witness1 = witness_id_type(1)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 0); - - // no votes for witness 2 - auto witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness2.total_votes, 0); - - // vote for witness1 and witness2 - this before GPOS period starts - vote_for(alice_id, witness1.vote_id, alice_private_key); - vote_for(bob_id, witness2.vote_id, bob_private_key); - - // go to maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // vote is the same as amount in the first subperiod since voting - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 1000); - BOOST_CHECK_EQUAL(witness2.total_votes, 1000); - - update_maintenance_interval(3600); //update maintenance interval to 1hr to evaluate sub-periods - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 3600); - - // move to hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - generate_block(); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 1000); - BOOST_CHECK_EQUAL(witness2.total_votes, 1000); - - // add some vesting to alice and don't add anything for Bob - create_vesting(alice_id, core.amount(99), vesting_balance_type::gpos); - generate_block(); - vote_for(alice_id, witness1.vote_id, alice_private_key); - generate_block(); - - advance_x_maint(1); - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - //System needs to consider votes based on both regular balance + GPOS balance for 1/2 sub-period on GPOS activation - BOOST_CHECK_EQUAL(witness1.total_votes, 1000); - BOOST_CHECK_EQUAL(witness2.total_votes, 1000); - - advance_x_maint(6); - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 1000); - BOOST_CHECK_EQUAL(witness2.total_votes, 1000); - - advance_x_maint(5); - generate_block(); - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - //Since Alice has votes, votes should be based on GPOS balance i.e 99 - //Since Bob not voted after GPOS activation, witness2 votes should be 0 after crossing 1/2 sub-period(12 maintanence intervals in this case) - BOOST_CHECK_EQUAL(witness1.total_votes, 99); - BOOST_CHECK_EQUAL(witness2.total_votes, 0); - - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( voting ) -{ - ACTORS((alice)(bob)); - try { - // move to hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - generate_block(); - - const auto& core = asset_id_type()(db); - - // send some asset to alice and bob - transfer( committee_account, alice_id, core.amount( 1000 ) ); - transfer( committee_account, bob_id, core.amount( 1000 ) ); - generate_block(); - - // default maintenance_interval is 1 day - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 86400); - - // add some vesting to alice and bob - create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); - create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); - generate_block(); - - // default gpos values - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 15552000); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 2592000); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch()); - - // update default gpos for test speed - auto now = db.head_block_time(); - // 5184000 = 60x60x24x60 = 60 days - // 864000 = 60x60x24x10 = 10 days - update_gpos_global(5184000, 864000, now); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 5184000); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 864000); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); - // end global changes - - generate_block(); - - // no votes for witness 1 - auto witness1 = witness_id_type(1)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 0); - - // no votes for witness 2 - auto witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness2.total_votes, 0); - - // vote for witness1 and witness2 - sub-period 1 - vote_for(alice_id, witness1.vote_id, alice_private_key); - vote_for(bob_id, witness2.vote_id, bob_private_key); - - // go to maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // need to consider both gpos and regular balance for first 1/2 sub period - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 1000); - BOOST_CHECK_EQUAL(witness2.total_votes, 1000); - - advance_x_maint(6); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 100); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(4); - - //Bob votes for witness2 - sub-period 2 - vote_for(bob_id, witness2.vote_id, bob_private_key); - // go to maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // vote decay as time pass - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 83); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(10); - //Bob votes for witness2 - sub-period 3 - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // decay more - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 66); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(10); - - // Bob votes for witness2 - sub-period 4 - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // decay more - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 50); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(10); - - // Bob votes for witness2 - sub-period 5 - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // decay more - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 33); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(10); - - // Bob votes for witness2 - sub-period 6 - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // decay more - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 16); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - // we are still in gpos period 1 - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); - - advance_x_maint(5); - // a new GPOS period is in but vote from user is before the start. Whoever votes in 6th sub-period, votes will carry - now = db.head_block_time(); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); - - generate_block(); - - // we are in the second GPOS period, at subperiod 1, - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 0); - //It's critical here, since bob votes in 6th sub-period of last vesting period, witness2 should retain his votes - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - - // lets vote here from alice to generate votes for witness 1 - //vote from bob to reatin VF 1 - vote_for(alice_id, witness1.vote_id, alice_private_key); - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_block(); - - // go to maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 100); - BOOST_CHECK_EQUAL(witness2.total_votes, 100); - - advance_x_maint(10); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 83); - BOOST_CHECK_EQUAL(witness2.total_votes, 83); - - vote_for(bob_id, witness2.vote_id, bob_private_key); - generate_block(); - - advance_x_maint(10); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 66); - BOOST_CHECK_EQUAL(witness2.total_votes, 83); - - // alice votes again, now for witness 2, her vote worth 100 now - vote_for(alice_id, witness2.vote_id, alice_private_key); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - witness1 = witness_id_type(1)(db); - witness2 = witness_id_type(2)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 100); - BOOST_CHECK_EQUAL(witness2.total_votes, 183); - - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( rolling_period_start ) -{ - // period start rolls automatically after HF - try { - // update default gpos global parameters to make this thing faster - update_gpos_global(518400, 86400, HARDFORK_GPOS_TIME); - generate_blocks(HARDFORK_GPOS_TIME); - update_maintenance_interval(3600); //update maintenance interval to 1hr to evaluate sub-periods - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 3600); - - auto vesting_period_1 = db.get_global_properties().parameters.gpos_period_start(); - - auto now = db.head_block_time(); - // moving outside period: - while( db.head_block_time() <= now + fc::days(6) ) - { - generate_block(); - } - generate_block(); - auto vesting_period_2 = db.get_global_properties().parameters.gpos_period_start(); - - //difference between start of two consecutive vesting periods should be 6 days - BOOST_CHECK_EQUAL(vesting_period_1 + 518400, vesting_period_2); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( worker_dividends_voting ) -{ - try { - // advance to HF - generate_blocks(HARDFORK_GPOS_TIME); - generate_block(); - - // update default gpos global parameters to 4 days - auto now = db.head_block_time(); - update_gpos_global(345600, 86400, now); - - generate_block(); - set_expiration(db, trx); - const auto& core = asset_id_type()(db); - - // get core asset object - const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); - - // by default core token pays dividends once per month - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 2592000); // 30 days - - // update the payout interval to 1 day for speed purposes of the test - update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day - - generate_block(); - - // get the dividend distribution account - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - - // transfering some coins to distribution account. - transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); - generate_block(); - - ACTORS((nathan)(voter1)(voter2)(voter3)); - - transfer( committee_account, nathan_id, core.amount( 1000 ) ); - transfer( committee_account, voter1_id, core.amount( 1000 ) ); - transfer( committee_account, voter2_id, core.amount( 1000 ) ); - - generate_block(); - - upgrade_to_lifetime_member(nathan_id); - - auto worker = create_worker(nathan_id, 10, fc::days(6)); - - // add some vesting to voter1 - create_vesting(voter1_id, core.amount(100), vesting_balance_type::gpos); - - // add some vesting to voter2 - create_vesting(voter2_id, core.amount(100), vesting_balance_type::gpos); - - generate_block(); - - // vote for worker - vote_for(voter1_id, worker.vote_for, voter1_private_key); - - // first maint pass, coefficient will be 1 - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - worker = worker_id_type()(db); - BOOST_CHECK_EQUAL(worker.total_votes_for, 100); - - // here dividends are paid to voter1 and voter2 - // voter1 get paid full dividend share as coefficent is at 1 here - BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 950); - - // voter2 didnt voted so he dont get paid - BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900); - - // send some asset to the reserve pool so the worker can get paid - fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2)); - - BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 0); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // worker is getting paid - BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().balance(db).balance.amount.value, 10); - BOOST_CHECK_EQUAL(worker.worker.get().balance(db).balance.amount.value, 10); - - // second maint pass, coefficient will be 0.75 - worker = worker_id_type()(db); - BOOST_CHECK_EQUAL(worker.total_votes_for, 75); - - // more decay - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - worker = worker_id_type()(db); - BOOST_CHECK_EQUAL(worker.total_votes_for, 50); - - transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); - generate_block(); - - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996850); - - // more decay - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - worker = worker_id_type()(db); - BOOST_CHECK_EQUAL(worker.total_votes_for, 25); - - // here voter1 get paid again but less money by vesting coefficient - BOOST_CHECK_EQUAL(get_balance(voter1_id(db), core), 962); - BOOST_CHECK_EQUAL(get_balance(voter2_id(db), core), 900); - - // remaining dividends not paid by coeffcient are sent to committee account - BOOST_CHECK_EQUAL(get_balance(committee_account(db), core), 499999999996938); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( account_multiple_vesting ) -{ - try { - // advance to HF - generate_blocks(HARDFORK_GPOS_TIME); - generate_block(); - set_expiration(db, trx); - - // update default gpos global parameters to 4 days - auto now = db.head_block_time(); - update_gpos_global(345600, 86400, now); - - ACTORS((sam)(patty)); - - const auto& core = asset_id_type()(db); - - transfer( committee_account, sam_id, core.amount( 300 ) ); - transfer( committee_account, patty_id, core.amount( 100 ) ); - - // add some vesting to sam - create_vesting(sam_id, core.amount(100), vesting_balance_type::gpos); - - // have another balance with 200 more - create_vesting(sam_id, core.amount(200), vesting_balance_type::gpos); - - // patty also have vesting balance - create_vesting(patty_id, core.amount(100), vesting_balance_type::gpos); - - // get core asset object - const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - - // update the payout interval - update_payout_interval(core.symbol, db.head_block_time() + fc::minutes(1), 60 * 60 * 24); // 1 day - - // get the dividend distribution account - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - - // transfering some coins to distribution account. - transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); - generate_block(); - - // vote for a votable object - auto witness1 = witness_id_type(1)(db); - vote_for(sam_id, witness1.vote_id, sam_private_key); - vote_for(patty_id, witness1.vote_id, patty_private_key); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // amount in vested balanced will sum up as voting power - witness1 = witness_id_type(1)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 400); - - // sam get paid dividends - BOOST_CHECK_EQUAL(get_balance(sam_id(db), core), 75); - - // patty also - BOOST_CHECK_EQUAL(get_balance(patty_id(db), core), 25); - - // total vote not decaying - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - witness1 = witness_id_type(1)(db); - - BOOST_CHECK_EQUAL(witness1.total_votes, 300); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( Withdraw_gpos_vesting_balance ) -{ - try { - // advance to HF - generate_blocks(HARDFORK_GPOS_TIME); - generate_block(); - set_expiration(db, trx); - - // update default gpos global parameters to 4 days - auto now = db.head_block_time(); - update_gpos_global(345600, 86400, now); - - ACTORS((alice)(bob)); - - graphene::app::database_api db_api1(db); - const auto& core = asset_id_type()(db); - - - transfer( committee_account, alice_id, core.amount( 500 ) ); - transfer( committee_account, bob_id, core.amount( 99 ) ); - - // add some vesting to Alice, Bob - vesting_balance_object vbo1, vbo2; - vbo1 = create_vesting(alice_id, core.amount(150), vesting_balance_type::gpos); - vbo2 = create_vesting(bob_id, core.amount(99), vesting_balance_type::gpos); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - generate_block(); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_blocks(db.get_global_properties().parameters.gpos_vesting_lockin_period()); - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 350); - withdraw_gpos_vesting(vbo1.id, alice_id, core.amount(50), /*vesting_balance_type::gpos, */alice_private_key); - withdraw_gpos_vesting(vbo2.id, bob_id, core.amount(99), /*vesting_balance_type::gpos, */bob_private_key); - generate_block(); - // verify charles balance - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 400); - BOOST_CHECK_EQUAL(get_balance(bob_id(db), core), 99); - - // Add more 50 and 73 vesting objects and withdraw 90 from - // total vesting balance of user - vbo1 = create_vesting(alice_id, core.amount(50), vesting_balance_type::gpos); - vbo2 = create_vesting(alice_id, core.amount(73), vesting_balance_type::gpos); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - generate_block(); - - vector vbos = db_api1.get_vesting_balances("alice"); - asset total_vesting; - for (const vesting_balance_object& vbo : vbos) - { - if (vbo.balance_type == vesting_balance_type::gpos && vbo.balance.asset_id == asset_id_type()) - total_vesting += vbo.balance; - } - // total vesting balance of alice - BOOST_CHECK_EQUAL(total_vesting.amount.value, core.amount(223).amount.value); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_blocks(db.get_global_properties().parameters.gpos_vesting_lockin_period()); - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 277); - withdraw_gpos_vesting(vbo1.id, alice_id, core.amount(90), /*vesting_balance_type::gpos,*/ alice_private_key); - generate_block(); - // verify alice balance - BOOST_CHECK_EQUAL(get_balance(alice_id(db), core), 367); - - // verify remaining vesting balance - vbos = db_api1.get_vesting_balances("alice"); - asset remaining_vesting; - for (const vesting_balance_object& vbo : vbos) - { - if (vbo.balance_type == vesting_balance_type::gpos && vbo.balance.asset_id == asset_id_type()) - remaining_vesting += vbo.balance; - } - // remaining vesting balance of alice - BOOST_CHECK_EQUAL(remaining_vesting.amount.value, core.amount(133).amount.value); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -/* -BOOST_AUTO_TEST_CASE( competing_proposals ) -{ - try { - // advance to HF - generate_blocks(HARDFORK_GPOS_TIME); - generate_block(); - set_expiration(db, trx); - - ACTORS((voter1)(voter2)(worker1)(worker2)); - - const auto& core = asset_id_type()(db); - - transfer( committee_account, worker1_id, core.amount( 1000 ) ); - transfer( committee_account, worker2_id, core.amount( 1000 ) ); - transfer( committee_account, voter1_id, core.amount( 1000 ) ); - transfer( committee_account, voter2_id, core.amount( 1000 ) ); - - create_vesting(voter1_id, core.amount(200), vesting_balance_type::gpos); - create_vesting(voter2_id, core.amount(300), vesting_balance_type::gpos); - - generate_block(); - - auto now = db.head_block_time(); - update_gpos_global(518400, 86400, now); - - update_payout_interval(core.symbol, fc::time_point::now() + fc::minutes(1), 60 * 60 * 24); // 1 day - - upgrade_to_lifetime_member(worker1_id); - upgrade_to_lifetime_member(worker2_id); - - // create 2 competing proposals asking a lot of token - // todo: maybe a refund worker here so we can test with smaller numbers - auto w1 = create_worker(worker1_id, 100000000000, fc::days(10)); - auto w1_id_instance = w1.id.instance(); - auto w2 = create_worker(worker2_id, 100000000000, fc::days(10)); - auto w2_id_instance = w2.id.instance(); - - fill_reserve_pool(account_id_type(), asset(GRAPHENE_MAX_SHARE_SUPPLY/2)); - - // vote for the 2 workers - vote_for(voter1_id, w1.vote_for, voter1_private_key); - vote_for(voter2_id, w2.vote_for, voter2_private_key); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - w1 = worker_id_type(w1_id_instance)(db); - w2 = worker_id_type(w2_id_instance)(db); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // only w2 is getting paid as it haves more votes and money is only enough for 1 - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 100000000000); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 150000000000); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - w1 = worker_id_type(w1_id_instance)(db); - w2 = worker_id_type(w2_id_instance)(db); - - // as votes decay w1 is still getting paid as it always have more votes than w1 - BOOST_CHECK_EQUAL(w1.total_votes_for, 100); - BOOST_CHECK_EQUAL(w2.total_votes_for, 150); - - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 200000000000); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - w1 = worker_id_type(w1_id_instance)(db); - w2 = worker_id_type(w2_id_instance)(db); - - BOOST_CHECK_EQUAL(w1.total_votes_for, 66); - BOOST_CHECK_EQUAL(w2.total_votes_for, 100); - - // worker is sil getting paid as days pass - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 250000000000); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - w1 = worker_id_type(w1_id_instance)(db); - w2 = worker_id_type(w2_id_instance)(db); - - BOOST_CHECK_EQUAL(w1.total_votes_for, 33); - BOOST_CHECK_EQUAL(w2.total_votes_for, 50); - - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - w1 = worker_id_type(w1_id_instance)(db); - w2 = worker_id_type(w2_id_instance)(db); - - // worker2 will not get paid anymore as it haves 0 votes - BOOST_CHECK_EQUAL(w1.total_votes_for, 0); - BOOST_CHECK_EQUAL(w2.total_votes_for, 0); - - BOOST_CHECK_EQUAL(w1.worker.get().balance(db).balance.amount.value, 0); - BOOST_CHECK_EQUAL(w2.worker.get().balance(db).balance.amount.value, 300000000000); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} -*/ -BOOST_AUTO_TEST_CASE( proxy_voting ) -{ - ACTORS((alice)(bob)); - try { - // move to hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - generate_block(); - - // database api - graphene::app::database_api db_api(db); - - const auto& core = asset_id_type()(db); - - // send some asset to alice and bob - transfer( committee_account, alice_id, core.amount( 1000 ) ); - transfer( committee_account, bob_id, core.amount( 1000 ) ); - generate_block(); - - // add some vesting to alice and bob - create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); - generate_block(); - - // total balance is 100 rest of data at 0 - auto gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100); - - create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); - generate_block(); - - gpos_info = db_api.get_gpos_info(bob_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - - auto now = db.head_block_time(); - update_gpos_global(518400, 86400, now); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); - - // alice assign bob as voting account - graphene::chain::account_update_operation op; - op.account = alice_id; - op.new_options = alice_id(db).options; - op.new_options->voting_account = bob_id; - trx.operations.push_back(op); - set_expiration(db, trx); - trx.validate(); - sign(trx, alice_private_key); - PUSH_TX( db, trx, ~0 ); - trx.clear(); - - generate_block(); - - // vote for witness1 - auto witness1 = witness_id_type(1)(db); - vote_for(bob_id, witness1.vote_id, bob_private_key); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // check vesting factor of current subperiod - BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1); - BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // GPOS 2nd subperiod started. - // vesting factor decay - BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337); - BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // GPOS 3rd subperiod started - // vesting factor decay - BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.66666666666666663); - BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.66666666666666663); - - // vote for witness2 - auto witness2 = witness_id_type(2)(db); - vote_for(bob_id, witness2.vote_id, bob_private_key); - - // vesting factor should be 1 for both alice and bob for the current subperiod - BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1); - BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1); - - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // vesting factor decay - BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337); - BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( no_proposal ) -{ - try { - - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( database_api ) -{ - ACTORS((alice)(bob)); - try { - // move to hardfork - generate_blocks( HARDFORK_GPOS_TIME ); - generate_block(); - - // database api - graphene::app::database_api db_api(db); - - const auto& core = asset_id_type()(db); - - // send some asset to alice and bob - transfer( committee_account, alice_id, core.amount( 1000 ) ); - transfer( committee_account, bob_id, core.amount( 1000 ) ); - generate_block(); - - // add some vesting to alice and bob - create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos); - generate_block(); - - // total balance is 100 rest of data at 0 - auto gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100); - - create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos); - generate_block(); - - // total gpos balance is now 200 - gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - - // update default gpos and dividend interval to 10 days - auto now = db.head_block_time(); - update_gpos_global(5184000, 864000, now); // 10 days subperiods - update_payout_interval(core.symbol, now + fc::minutes(1), 60 * 60 * 24 * 10); // 10 days - - generate_block(); - - // no votes for witness 1 - auto witness1 = witness_id_type(1)(db); - BOOST_CHECK_EQUAL(witness1.total_votes, 0); - - // no votes for witness 2 - auto witness2 = witness_id_type(2)(db); - BOOST_CHECK_EQUAL(witness2.total_votes, 0); - - // transfering some coins to distribution account. - const auto& dividend_holder_asset_object = get_asset(GRAPHENE_SYMBOL); - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - transfer( committee_account, dividend_distribution_account.id, core.amount( 100 ) ); - generate_block(); - - // award balance is now 100 - gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 100); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - - // vote for witness1 - vote_for(alice_id, witness1.vote_id, alice_private_key); - vote_for(bob_id, witness1.vote_id, bob_private_key); - - // go to maint - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // payment for alice and bob is done, distribution account is back in 0 - gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 1); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - - advance_x_maint(10); - - // alice vesting coeffcient decay - gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.83333333333333337); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - - advance_x_maint(10); - - // vesting factor for alice decaying more - gpos_info = db_api.get_gpos_info(alice_id); - BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0.66666666666666663); - BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0); - BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200); - } - catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp index 943b8265..4cbcda89 100644 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -55,25 +55,25 @@ BOOST_AUTO_TEST_CASE(get_account_history) { int account_create_op_id = operation::tag::value; //account_id_type() did 3 ops and includes id0 - vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 100, operation_history_id_type()); + vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); BOOST_CHECK_EQUAL(histories[2].op.which(), asset_create_op_id); // 1 account_create op larger than id1 - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 100, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK(histories[0].id.instance() != 0); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // Limit 2 returns 2 result - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 2, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 2, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK(histories[1].id.instance() != 0); BOOST_CHECK_EQUAL(histories[1].op.which(), account_create_op_id); // bob has 1 op - histories = hist_api.get_account_history("bob", operation_history_id_type(), 100, operation_history_id_type()); + histories = hist_api.get_account_history(bob_acc.get_id(), operation_history_id_type(), 100, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); } FC_LOG_AND_RETHROW() @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(zero_id_object) { graphene::app::history_api hist_api(app); // no history at all in the chain - vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 4, operation_history_id_type(0)); + vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); create_bitasset("USD", account_id_type()); // create op 0 @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(zero_id_object) { fc::usleep(fc::milliseconds(2000)); // what if the account only has one history entry and it is 0? - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); } FC_LOG_AND_RETHROW() @@ -107,13 +107,13 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { // account_id_type() and dan share operation id 1(account create) - share can be also in id 0 // no history at all in the chain - vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 4, operation_history_id_type(0)); + vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); create_bitasset("USD", account_id_type()); // create op 0 generate_block(); // what if the account only has one history entry and it is 0? - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { generate_block(); // f(A, 0, 4, 9) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 6) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 5) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -152,33 +152,33 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 0, 4, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 4, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 4, 2) = { 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 4, 1) = { 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 4, 0) = { 5, 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); @@ -186,103 +186,103 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); // f(A, 1, 5, 9) = { 5, 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(9)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 6) = { 5, 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(6)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 5) = { 5, 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(5)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 1, 5, 4) = { 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(4)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); // f(A, 1, 5, 3) = { 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(3)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); // f(A, 1, 5, 2) = { } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(2)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 1, 5, 1) = { } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(1)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 1, 5, 0) = { 5, 3 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 5, operation_history_id_type(0)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // f(A, 0, 3, 9) = { 5, 3, 1 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(9)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 6) = { 5, 3, 1 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(6)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 5) = { 5, 3, 1 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(5)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(A, 0, 3, 4) = { 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(4)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 3, 3) = { 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(3)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u); // f(A, 0, 3, 2) = { 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(2)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 3, 1) = { 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type(1)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); // f(A, 0, 3, 0) = { 5, 3, 1 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(), 3, operation_history_id_type()); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 9) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 0, 4, 6) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -298,38 +298,38 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 0, 4, 5) = { 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 4) = { 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 3u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); // f(B, 0, 4, 3) = { 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); // f(B, 0, 4, 2) = { 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u); // f(B, 0, 4, 1) = { 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u); // f(B, 0, 4, 0) = { 6, 4, 2, 1 } - histories = hist_api.get_account_history("dan", operation_history_id_type(), 4, operation_history_id_type()); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type()); BOOST_CHECK_EQUAL(histories.size(), 4u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); @@ -337,49 +337,49 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u); // f(B, 2, 4, 9) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(9)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // f(B, 2, 4, 6) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(6)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(6)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // f(B, 2, 4, 5) = { 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(5)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(5)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); // f(B, 2, 4, 4) = { 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(4)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(4)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); // f(B, 2, 4, 3) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(3)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(3)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 2) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(2)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(2)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 1) = { } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(1)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(1)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(B, 2, 4, 0) = { 6, 4 } - histories = hist_api.get_account_history("dan", operation_history_id_type(2), 4, operation_history_id_type(0)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); // 0 limits - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 0, operation_history_id_type(0)); + histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(0), 0, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("committee-account", operation_history_id_type(3), 0, operation_history_id_type(9)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(3), 0, operation_history_id_type(9)); BOOST_CHECK_EQUAL(histories.size(), 0u); // create a new account C = alice { 7 } @@ -388,16 +388,16 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { generate_block(); // f(C, 0, 4, 10) = { 7 } - histories = hist_api.get_account_history("alice", operation_history_id_type(0), 4, operation_history_id_type(10)); + histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(0), 4, operation_history_id_type(10)); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); // f(C, 8, 4, 10) = { } - histories = hist_api.get_account_history("alice", operation_history_id_type(8), 4, operation_history_id_type(10)); + histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(8), 4, operation_history_id_type(10)); BOOST_CHECK_EQUAL(histories.size(), 0u); // f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 } - histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); + histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); BOOST_CHECK_EQUAL(histories.size(), 5u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u); BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u); @@ -407,155 +407,148 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(track_account) { - try { - graphene::app::history_api hist_api(app); +//BOOST_AUTO_TEST_CASE(track_account) { +// try { +// graphene::app::history_api hist_api(app); +// +// // account_id_type() is not tracked +// +// // account_id_type() creates alice(not tracked account) +// const account_object& alice = create_account("alice"); +// auto alice_id = alice.id; +// +// //account_id_type() creates some ops +// create_bitasset("CNY", account_id_type()); +// create_bitasset("USD", account_id_type()); +// +// // account_id_type() creates dan(account tracked) +// const account_object& dan = create_account("dan"); +// auto dan_id = dan.id; +// +// // dan makes 1 op +// create_bitasset("EUR", dan_id); +// +// generate_block( ~database::skip_fork_db ); +// +// // anything against account_id_type() should be {} +// vector histories = +// hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// // anything against alice should be {} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// // dan should have history +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 2u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); +// +// // create more ops, starting with an untracked account +// create_bitasset( "BTC", account_id_type() ); +// create_bitasset( "GBP", dan_id ); +// +// generate_block( ~database::skip_fork_db ); +// +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 3u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); +// +// db.pop_block(); +// +// // Try again, should result in same object IDs +// create_bitasset( "BTC", account_id_type() ); +// create_bitasset( "GBP", dan_id ); +// +// generate_block(); +// +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 3u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); +// } catch (fc::exception &e) { +// edump((e.to_detail_string())); +// throw; +// } +//} - // account_id_type() is not tracked - - // account_id_type() creates alice(not tracked account) - const account_object& alice = create_account("alice"); - auto alice_id = alice.id; - - //account_id_type() creates some ops - create_bitasset("CNY", account_id_type()); - create_bitasset("USD", account_id_type()); - - // account_id_type() creates dan(account tracked) - const account_object& dan = create_account("dan"); - auto dan_id = dan.id; - - // dan makes 1 op - create_bitasset("EUR", dan_id); - - generate_block( ~database::skip_fork_db ); - - // anything against account_id_type() should be {} - vector histories = - hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("committee-account", operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // anything against alice should be {} - histories = hist_api.get_account_history("alice", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("alice", operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("alice", operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - // dan should have history - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); - - // create more ops, starting with an untracked account - create_bitasset( "BTC", account_id_type() ); - create_bitasset( "GBP", dan_id ); - - generate_block( ~database::skip_fork_db ); - - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); - - db.pop_block(); - - // Try again, should result in same object IDs - create_bitasset( "BTC", account_id_type() ); - create_bitasset( "GBP", dan_id ); - - generate_block(); - - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 3u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u); - } catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(track_account2) { - try { - graphene::app::history_api hist_api(app); - - // account_id_type() is tracked - - // account_id_type() creates alice(tracked account) - const account_object& alice = create_account("alice"); - auto alice_id = alice.id; - - //account_id_type() creates some ops - create_bitasset("CNY", account_id_type()); - create_bitasset("USD", account_id_type()); - - // alice makes 1 op - create_bitasset("EUR", alice_id); - - // account_id_type() creates dan(account not tracked) - const account_object& dan = create_account("dan"); - auto dan_id = dan.id; - - generate_block(); - - // all account_id_type() should have 4 ops {4,2,1,0} - vector histories = hist_api.get_account_history("committee-account", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 4u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); - BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); - BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); - - // all alice account should have 2 ops {3, 0} - histories = hist_api.get_account_history("alice", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 2u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); - - // alice first op should be {0} - histories = hist_api.get_account_history("alice", operation_history_id_type(0), 1, operation_history_id_type(1)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); - - // alice second op should be {3} - histories = hist_api.get_account_history("alice", operation_history_id_type(1), 1, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 1u); - BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); - - // anything against dan should be {} - histories = hist_api.get_account_history("dan", operation_history_id_type(0), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("dan", operation_history_id_type(1), 10, operation_history_id_type(0)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - histories = hist_api.get_account_history("dan", operation_history_id_type(1), 1, operation_history_id_type(2)); - BOOST_CHECK_EQUAL(histories.size(), 0u); - - } catch (fc::exception &e) { - edump((e.to_detail_string())); - throw; - } -} +//BOOST_AUTO_TEST_CASE(track_account2) { +// try { +// graphene::app::history_api hist_api(app); +// +// // account_id_type() is tracked +// +// // account_id_type() creates alice(tracked account) +// const account_object& alice = create_account("alice"); +// auto alice_id = alice.id; +// +// //account_id_type() creates some ops +// create_bitasset("CNY", account_id_type()); +// create_bitasset("USD", account_id_type()); +// +// // alice makes 1 op +// create_bitasset("EUR", alice_id); +// +// // account_id_type() creates dan(account not tracked) +// const account_object& dan = create_account("dan"); +// auto dan_id = dan.id; +// +// generate_block(); +// +// // all account_id_type() should have 4 ops {4,2,1,0} +// vector histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 4u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u); +// BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u); +// BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u); +// +// // all alice account should have 2 ops {3, 0} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 2u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); +// BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u); +// +// // alice first op should be {0} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1)); +// BOOST_CHECK_EQUAL(histories.size(), 1u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); +// +// // alice second op should be {3} +// histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 1u); +// BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u); +// +// // anything against dan should be {} +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2)); +// BOOST_CHECK_EQUAL(histories.size(), 0u); +// +// } catch (fc::exception &e) { +// edump((e.to_detail_string())); +// throw; +// } +//} BOOST_AUTO_TEST_CASE(get_account_history_operations) { try { graphene::app::history_api hist_api(app); - int asset_create_op_id = operation::tag::value; - int account_create_op_id = operation::tag::value; - - // no asset_create operation on account_id_type() should not throw any exception - vector histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); - BOOST_CHECK_EQUAL(histories.size(), 0u); - //account_id_type() do 3 ops create_bitasset("CNY", account_id_type()); create_account("sam"); @@ -564,28 +557,31 @@ BOOST_AUTO_TEST_CASE(get_account_history_operations) { generate_block(); fc::usleep(fc::milliseconds(2000)); + int asset_create_op_id = operation::tag::value; + int account_create_op_id = operation::tag::value; + //account_id_type() did 1 asset_create op - histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); + vector histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u); BOOST_CHECK_EQUAL(histories[0].op.which(), asset_create_op_id); //account_id_type() did 2 account_create ops - histories = hist_api.get_account_history_operations("committee-account", account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); + histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 2u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // No asset_create op larger than id1 - histories = hist_api.get_account_history_operations("committee-account", asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100); + histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100); BOOST_CHECK_EQUAL(histories.size(), 0u); // Limit 1 returns 1 result - histories = hist_api.get_account_history_operations("committee-account", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1); + histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); // alice has 1 op - histories = hist_api.get_account_history_operations("alice", account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100); + histories = hist_api.get_account_history_operations(get_account("alice").id, account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100); BOOST_CHECK_EQUAL(histories.size(), 1u); BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id); @@ -595,4 +591,4 @@ BOOST_AUTO_TEST_CASE(get_account_history_operations) { } } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index 79f80e1f..b88f485a 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(last_voting_date) // 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); + auto stats_obj = alice_id(db).statistics(db); BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), 0); // alice votes @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(last_voting_date) 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); + stats_obj = alice_id(db).statistics(db); BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), now); } FC_LOG_AND_RETHROW() @@ -163,360 +163,4 @@ BOOST_AUTO_TEST_CASE(last_voting_date_proxy) } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(put_my_witnesses) -{ - try - { - graphene::app::database_api db_api1(db); - - ACTORS( (witness0) - (witness1) - (witness2) - (witness3) - (witness4) - (witness5) - (witness6) - (witness7) - (witness8) - (witness9) - (witness10) - (witness11) - (witness12) - (witness13) ); - - // Upgrade all accounts to LTM - upgrade_to_lifetime_member(witness0_id); - upgrade_to_lifetime_member(witness1_id); - upgrade_to_lifetime_member(witness2_id); - upgrade_to_lifetime_member(witness3_id); - upgrade_to_lifetime_member(witness4_id); - upgrade_to_lifetime_member(witness5_id); - upgrade_to_lifetime_member(witness6_id); - upgrade_to_lifetime_member(witness7_id); - upgrade_to_lifetime_member(witness8_id); - upgrade_to_lifetime_member(witness9_id); - upgrade_to_lifetime_member(witness10_id); - upgrade_to_lifetime_member(witness11_id); - upgrade_to_lifetime_member(witness12_id); - upgrade_to_lifetime_member(witness13_id); - - // Create all the witnesses - const witness_id_type witness0_witness_id = create_witness(witness0_id, witness0_private_key).id; - const witness_id_type witness1_witness_id = create_witness(witness1_id, witness1_private_key).id; - const witness_id_type witness2_witness_id = create_witness(witness2_id, witness2_private_key).id; - const witness_id_type witness3_witness_id = create_witness(witness3_id, witness3_private_key).id; - const witness_id_type witness4_witness_id = create_witness(witness4_id, witness4_private_key).id; - const witness_id_type witness5_witness_id = create_witness(witness5_id, witness5_private_key).id; - const witness_id_type witness6_witness_id = create_witness(witness6_id, witness6_private_key).id; - const witness_id_type witness7_witness_id = create_witness(witness7_id, witness7_private_key).id; - const witness_id_type witness8_witness_id = create_witness(witness8_id, witness8_private_key).id; - const witness_id_type witness9_witness_id = create_witness(witness9_id, witness9_private_key).id; - const witness_id_type witness10_witness_id = create_witness(witness10_id, witness10_private_key).id; - const witness_id_type witness11_witness_id = create_witness(witness11_id, witness11_private_key).id; - const witness_id_type witness12_witness_id = create_witness(witness12_id, witness12_private_key).id; - const witness_id_type witness13_witness_id = create_witness(witness13_id, witness13_private_key).id; - - // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time - const vector private_keys = { - witness0_private_key, - witness1_private_key, - witness2_private_key, - witness3_private_key, - witness4_private_key, - witness5_private_key, - witness6_private_key, - witness7_private_key, - witness8_private_key, - witness9_private_key, - witness10_private_key, - witness11_private_key, - witness12_private_key, - witness13_private_key - - }; - - // create a map with account id and witness id of the first 11 witnesses - const flat_map witness_map = { - {witness0_id, witness0_witness_id}, - {witness1_id, witness1_witness_id}, - {witness2_id, witness2_witness_id}, - {witness3_id, witness3_witness_id}, - {witness4_id, witness4_witness_id}, - {witness5_id, witness5_witness_id}, - {witness6_id, witness6_witness_id}, - {witness7_id, witness7_witness_id}, - {witness8_id, witness8_witness_id}, - {witness9_id, witness9_witness_id}, - {witness10_id, witness10_witness_id}, - {witness11_id, witness11_witness_id}, - {witness12_id, witness12_witness_id}, - {witness13_id, witness13_witness_id} - }; - - // Check current default witnesses, default chain is configured with 10 witnesses - auto witnesses = db.get_global_properties().active_witnesses; - BOOST_CHECK_EQUAL(witnesses.size(), 10); - BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1); - BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2); - BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3); - BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 4); - BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 5); - BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 6); - BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7); - BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8); - BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9); - BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 10); - - // Activate all witnesses - // Each witness is voted with incremental stake so last witness created will be the ones with more votes - int c = 0; - for (auto l : witness_map) { - int stake = 100 + c + 10; - transfer(committee_account, l.first, asset(stake)); - { - set_expiration(db, trx); - account_update_operation op; - op.account = l.first; - op.new_options = l.first(db).options; - op.new_options->votes.insert(l.second(db).vote_id); - - trx.operations.push_back(op); - sign(trx, private_keys.at(c)); - PUSH_TX(db, trx); - trx.clear(); - } - ++c; - } - - // Trigger the new witnesses - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // Check my witnesses are now in control of the system - witnesses = db.get_global_properties().active_witnesses; - BOOST_CHECK_EQUAL(witnesses.size(), 11); - BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 14); - BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 15); - BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 16); - BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 17); - BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 18); - BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 19); - BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 20); - BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 21); - BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 22); - BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 23); - BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 24); - - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled) -{ - try - { - graphene::app::database_api db_api1(db); - - INVOKE(put_my_witnesses); - - const account_id_type witness1_id= get_account("witness1").id; - auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name); - BOOST_CHECK_EQUAL(witness1_object->total_votes, 111); - - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_CASE(track_votes_witnesses_disabled) -{ - try - { - graphene::app::database_api db_api1(db); - - INVOKE(put_my_witnesses); - - const account_id_type witness1_id= get_account("witness1").id; - auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name); - BOOST_CHECK_EQUAL(witness1_object->total_votes, 0); - - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_CASE(put_my_committee_members) -{ - try - { - graphene::app::database_api db_api1(db); - - ACTORS( (committee0) - (committee1) - (committee2) - (committee3) - (committee4) - (committee5) - (committee6) - (committee7) - (committee8) - (committee9) - (committee10) - (committee11) - (committee12) - (committee13) ); - - // Upgrade all accounts to LTM - upgrade_to_lifetime_member(committee0_id); - upgrade_to_lifetime_member(committee1_id); - upgrade_to_lifetime_member(committee2_id); - upgrade_to_lifetime_member(committee3_id); - upgrade_to_lifetime_member(committee4_id); - upgrade_to_lifetime_member(committee5_id); - upgrade_to_lifetime_member(committee6_id); - upgrade_to_lifetime_member(committee7_id); - upgrade_to_lifetime_member(committee8_id); - upgrade_to_lifetime_member(committee9_id); - upgrade_to_lifetime_member(committee10_id); - upgrade_to_lifetime_member(committee11_id); - upgrade_to_lifetime_member(committee12_id); - upgrade_to_lifetime_member(committee13_id); - - // Create all the committee - const committee_member_id_type committee0_committee_id = create_committee_member(committee0_id(db)).id; - const committee_member_id_type committee1_committee_id = create_committee_member(committee1_id(db)).id; - const committee_member_id_type committee2_committee_id = create_committee_member(committee2_id(db)).id; - const committee_member_id_type committee3_committee_id = create_committee_member(committee3_id(db)).id; - const committee_member_id_type committee4_committee_id = create_committee_member(committee4_id(db)).id; - const committee_member_id_type committee5_committee_id = create_committee_member(committee5_id(db)).id; - const committee_member_id_type committee6_committee_id = create_committee_member(committee6_id(db)).id; - const committee_member_id_type committee7_committee_id = create_committee_member(committee7_id(db)).id; - const committee_member_id_type committee8_committee_id = create_committee_member(committee8_id(db)).id; - const committee_member_id_type committee9_committee_id = create_committee_member(committee9_id(db)).id; - const committee_member_id_type committee10_committee_id = create_committee_member(committee10_id(db)).id; - const committee_member_id_type committee11_committee_id = create_committee_member(committee11_id(db)).id; - const committee_member_id_type committee12_committee_id = create_committee_member(committee12_id(db)).id; - const committee_member_id_type committee13_committee_id = create_committee_member(committee13_id(db)).id; - - // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time - const vector private_keys = { - committee0_private_key, - committee1_private_key, - committee2_private_key, - committee3_private_key, - committee4_private_key, - committee5_private_key, - committee6_private_key, - committee7_private_key, - committee8_private_key, - committee9_private_key, - committee10_private_key, - committee11_private_key, - committee12_private_key, - committee13_private_key - }; - - // create a map with account id and committee id of the first 11 witnesses - const flat_map committee_map = { - {committee0_id, committee0_committee_id}, - {committee1_id, committee1_committee_id}, - {committee2_id, committee2_committee_id}, - {committee3_id, committee3_committee_id}, - {committee4_id, committee4_committee_id}, - {committee5_id, committee5_committee_id}, - {committee6_id, committee6_committee_id}, - {committee7_id, committee7_committee_id}, - {committee8_id, committee8_committee_id}, - {committee9_id, committee9_committee_id}, - {committee10_id, committee10_committee_id}, - {committee11_id, committee11_committee_id}, - {committee12_id, committee12_committee_id}, - {committee13_id, committee13_committee_id} - }; - - // Check current default witnesses, default chain is configured with 10 witnesses - auto committee_members = db.get_global_properties().active_committee_members; - - BOOST_CHECK_EQUAL(committee_members.size(), 10); - BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 0); - BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 1); - BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 2); - BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 3); - BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 4); - BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 5); - BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 6); - BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 7); - BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 8); - BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 9); - - // Activate all committee - // Each witness is voted with incremental stake so last witness created will be the ones with more votes - int c = 0; - for (auto committee : committee_map) { - int stake = 100 + c + 10; - transfer(committee_account, committee.first, asset(stake)); - { - set_expiration(db, trx); - account_update_operation op; - op.account = committee.first; - op.new_options = committee.first(db).options; - op.new_options->votes.insert(committee.second(db).vote_id); - - trx.operations.push_back(op); - sign(trx, private_keys.at(c)); - PUSH_TX(db, trx); - trx.clear(); - } - ++c; - } - - // Trigger the new committee - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - // Check my witnesses are now in control of the system - committee_members = db.get_global_properties().active_committee_members; - BOOST_CHECK_EQUAL(committee_members.size(), 11); - - /* TODO we are not in full control, seems to committee members have votes by default - BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 14); - BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 15); - BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 16); - BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 17); - BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 18); - BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 19); - BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 20); - BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 21); - BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 22); - BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 23); - BOOST_CHECK_EQUAL(committee_members.begin()[10].instance.value, 24); - */ - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_CASE(track_votes_committee_enabled) -{ - try - { - graphene::app::database_api db_api1(db); - - INVOKE(put_my_committee_members); - - const account_id_type committee1_id= get_account("committee1").id; - auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name); - BOOST_CHECK_EQUAL(committee1_object->total_votes, 111); - - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_CASE(track_votes_committee_disabled) -{ - try - { - graphene::app::database_api db_api1(db); - - INVOKE(put_my_committee_members); - - const account_id_type committee1_id= get_account("committee1").id; - auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name); - BOOST_CHECK_EQUAL(committee1_object->total_votes, 0); - - } FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() From 964aed0bdfdd772adc051d68885d00eeafcfa250 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Wed, 25 Mar 2020 20:21:09 +1100 Subject: [PATCH 63/64] [SON-122] - SON Statistics improvements and consensus account creation (#318) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> --- libraries/chain/db_getter.cpp | 2 +- libraries/chain/db_init.cpp | 10 -- libraries/chain/db_maint.cpp | 120 ++++++++++++------ libraries/chain/get_config.cpp | 1 - .../chain/include/graphene/chain/config.hpp | 2 - .../chain/include/graphene/chain/database.hpp | 2 +- .../chain/protocol/chain_parameters.hpp | 5 + .../include/graphene/chain/son_object.hpp | 3 + .../chain/sidechain_transaction_evaluator.cpp | 4 +- libraries/chain/son_evaluator.cpp | 4 +- .../chain/son_wallet_deposit_evaluator.cpp | 4 +- libraries/chain/son_wallet_evaluator.cpp | 4 +- .../chain/son_wallet_withdraw_evaluator.cpp | 4 +- .../peerplays_sidechain_plugin.cpp | 2 +- .../sidechain_net_handler.cpp | 12 +- .../sidechain_net_handler_bitcoin.cpp | 4 +- .../sidechain_net_handler_peerplays.cpp | 2 +- tests/tests/sidechain_transaction_tests.cpp | 6 +- tests/tests/son_operations_tests.cpp | 12 +- tests/tests/son_wallet_tests.cpp | 4 +- 20 files changed, 123 insertions(+), 84 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index dfd59567..9e8bfa80 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -197,7 +197,7 @@ std::set database::get_sons_being_reported_down() fc::optional database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ) { son_delete_operation son_dereg_op; - son_dereg_op.payer = GRAPHENE_SON_ACCOUNT; + son_dereg_op.payer = get_global_properties().parameters.son_account(); son_dereg_op.son_id = son_id; proposal_create_operation proposal_op; diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 833e03e4..781146af 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -458,16 +458,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.network_fee_percentage = 0; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT; }).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID); - FC_ASSERT(create([this](account_object& a) { - a.name = "son-account"; - a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; - a.owner.weight_threshold = 1; - a.active.weight_threshold = 0; - a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_SON_ACCOUNT; - a.membership_expiration_date = time_point_sec::maximum(); - a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - }).get_id() == GRAPHENE_SON_ACCOUNT); // Create more special accounts while( true ) { diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 96ef6853..498a61ac 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -138,7 +138,8 @@ void database::pay_sons() if(s.txs_signed > 0){ auto son_params = get_global_properties().parameters; share_type pay = (s.txs_signed * son_budget.value)/total_txs_signed; - + // TODO: Remove me after QA + ilog( "pay ${p} to ${s} for ${t} transactions signed", ("p", pay.value)("s", s.id)("t",s.txs_signed) ); const auto& idx = get_index_type().indices().get(); auto son_obj = idx.find( s.owner ); modify( *son_obj, [&]( son_object& _son_obj) @@ -166,16 +167,30 @@ void database::pay_sons() } } -void database::update_son_metrics() +void database::update_son_metrics(const vector& curr_active_sons) { + vector current_sons; + + current_sons.reserve(curr_active_sons.size()); + std::transform(curr_active_sons.begin(), curr_active_sons.end(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + const auto& son_idx = get_index_type().indices().get< by_id >(); for( auto& son : son_idx ) { auto& stats = son.statistics(*this); - modify( stats, [&]( son_statistics_object& _stats) + bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); + modify( stats, [&]( son_statistics_object& _stats ) { _stats.total_downtime += _stats.current_interval_downtime; _stats.current_interval_downtime = 0; + if(is_active_son) + { + _stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval; + } }); } } @@ -555,44 +570,48 @@ void database::update_active_sons() } // Update SON authority - modify( get(GRAPHENE_SON_ACCOUNT), [&]( account_object& a ) + if( gpo.parameters.son_account() != GRAPHENE_NULL_ACCOUNT ) { - if( head_block_time() < HARDFORK_533_TIME ) + modify( get(gpo.parameters.son_account()), [&]( account_object& a ) { - uint64_t total_votes = 0; - map weights; - a.active.weight_threshold = 0; - a.active.account_auths.clear(); - - for( const son_object& son : sons ) + if( head_block_time() < HARDFORK_533_TIME ) { - weights.emplace(son.son_account, _vote_tally_buffer[son.vote_id]); - total_votes += _vote_tally_buffer[son.vote_id]; - } + uint64_t total_votes = 0; + map weights; + a.active.weight_threshold = 0; + a.active.account_auths.clear(); - // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, - // then I want to keep the most significant 16 bits of what's left. - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& weight : weights ) - { - // Ensure that everyone has at least one vote. Zero weights aren't allowed. - uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); - a.active.account_auths[weight.first] += votes; - a.active.weight_threshold += votes; + for( const son_object& son : sons ) + { + weights.emplace(son.son_account, _vote_tally_buffer[son.vote_id]); + total_votes += _vote_tally_buffer[son.vote_id]; + } + + // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, + // then I want to keep the most significant 16 bits of what's left. + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + for( const auto& weight : weights ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); + a.active.account_auths[weight.first] += votes; + a.active.weight_threshold += votes; + } + + a.active.weight_threshold *= 2; + a.active.weight_threshold /= 3; + a.active.weight_threshold += 1; } + else + { + vote_counter vc; + for( const son_object& son : sons ) + vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) ); + vc.finish_2_3( a.active ); + } + } ); + } - a.active.weight_threshold *= 2; - a.active.weight_threshold /= 3; - a.active.weight_threshold += 1; - } - else - { - vote_counter vc; - for( const son_object& son : sons ) - vc.add( son.son_account, std::max(_vote_tally_buffer[son.vote_id], UINT64_C(1)) ); - vc.finish_2_3( a.active ); - } - } ); // Compare current and to-be lists of active sons auto cur_active_sons = gpo.active_sons; @@ -622,6 +641,9 @@ void database::update_active_sons() update_son_statuses(cur_active_sons, new_active_sons); } + // Update son performance metrics + update_son_metrics(cur_active_sons); + modify(gpo, [&]( global_property_object& gp ){ gp.active_sons.clear(); gp.active_sons.reserve(new_active_sons.size()); @@ -640,9 +662,6 @@ void database::update_active_sons() }); _sso.scheduler.update(active_sons); }); - - update_son_metrics(); - } FC_CAPTURE_AND_RETHROW() } void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const @@ -1558,6 +1577,30 @@ void process_dividend_assets(database& db) } } FC_CAPTURE_AND_RETHROW() } +void perform_son_tasks(database& db) +{ + const global_property_object& gpo = db.get_global_properties(); + if(gpo.parameters.son_account() == GRAPHENE_NULL_ACCOUNT && db.head_block_time() >= HARDFORK_SON_TIME) + { + const auto& son_account = db.create([&](account_object& a) { + a.name = "son-account"; + a.statistics = db.create([&](account_statistics_object& s){s.owner = a.id;}).id; + a.owner.weight_threshold = 1; + a.active.weight_threshold = 0; + a.registrar = a.lifetime_referrer = a.referrer = a.id; + a.membership_expiration_date = time_point_sec::maximum(); + a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + }); + + db.modify( gpo, [&]( global_property_object& gpo ) { + gpo.parameters.extensions.value.son_account = son_account.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.son_account = son_account.get_id(); + }); + } +} + void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) { try { const auto& gpo = get_global_properties(); @@ -1566,6 +1609,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g create_buyback_orders(*this); process_dividend_assets(*this); + perform_son_tasks(*this); struct vote_tally_helper { database& d; diff --git a/libraries/chain/get_config.cpp b/libraries/chain/get_config.cpp index 68d0c951..245d6598 100644 --- a/libraries/chain/get_config.cpp +++ b/libraries/chain/get_config.cpp @@ -110,7 +110,6 @@ fc::variant_object get_config() result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); - result[ "GRAPHENE_SON_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 7e58bf0d..dfd80f03 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -175,8 +175,6 @@ #define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5)) /// #define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6)) -/// -#define GRAPHENE_SON_ACCOUNT (graphene::chain::account_id_type(7)) /// Sentinel value used in the scheduler. #define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0)) ///@} diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 4727bc1d..c4c00627 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -546,7 +546,7 @@ namespace graphene { namespace chain { void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); - void update_son_metrics(); + void update_son_metrics( const vector& curr_active_sons ); void update_active_sons(); void remove_son_proposal( const proposal_object& proposal ); void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 65b0d3c0..4b091726 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -50,6 +50,7 @@ namespace graphene { namespace chain { optional < uint32_t > son_deregister_time; optional < uint32_t > son_heartbeat_frequency; optional < uint32_t > son_down_time; + optional < account_id_type > son_account; }; struct chain_parameters @@ -154,6 +155,9 @@ namespace graphene { namespace chain { inline uint16_t son_down_time()const { return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; } + inline account_id_type son_account() const { + return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT; + } }; } } // graphene::chain @@ -175,6 +179,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_deregister_time) (son_heartbeat_frequency) (son_down_time) + (son_account) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index ef479962..ec346185 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -34,6 +34,8 @@ namespace graphene { namespace chain { uint64_t total_txs_signed = 0; // Transactions signed since the last son payouts uint64_t txs_signed = 0; + // Total Voted Active time i.e. duration selected as part of voted active SONs + uint64_t total_voted_time = 0; // Total Downtime barring the current down time in seconds, used for stats to present to user uint64_t total_downtime = 0; // Current Interval Downtime since last maintenance @@ -117,6 +119,7 @@ FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, (owner) (total_txs_signed) (txs_signed) + (total_voted_time) (total_downtime) (current_interval_downtime) (last_down_timestamp) diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index a77c7f64..a016ce27 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -16,7 +16,7 @@ void_result bitcoin_transaction_send_evaluator::do_evaluate(const bitcoin_transa try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); return void_result(); } FC_CAPTURE_AND_RETHROW((op)) @@ -109,7 +109,7 @@ void_result bitcoin_send_transaction_process_evaluator::do_evaluate(const bitcoi try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& btidx = db().get_index_type().indices().get(); const auto btobj = btidx.find(op.bitcoin_transaction_id); FC_ASSERT(btobj != btidx.end(), "Bitcoin Transaction Object not found"); diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 4155dc6b..021d8d20 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -67,7 +67,7 @@ void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass // Either owner can remove or consensus son account - FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == GRAPHENE_SON_ACCOUNT)); + FC_ASSERT(op.payer == db().get(op.son_id).son_account || (db().is_son_dereg_valid(op.son_id) && op.payer == db().get_global_properties().parameters.son_account())); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); return void_result(); @@ -167,7 +167,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - FC_ASSERT(op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer."); + FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); auto itr = idx.find(op.son_id); diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index 764f2c29..c0f95e78 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -10,7 +10,7 @@ namespace graphene { namespace chain { void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_create_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto &idx = db().get_index_type().indices().get(); FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists"); @@ -68,7 +68,7 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_process_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 15a1d120..0baed1cb 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -8,7 +8,7 @@ namespace graphene { namespace chain { void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); auto itr = idx.rbegin(); @@ -53,7 +53,7 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index 2185e808..d0611b7c 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -10,7 +10,7 @@ namespace graphene { namespace chain { void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto &idx = db().get_index_type().indices().get(); FC_ASSERT(idx.find(op.son_id) != idx.end(), "Statistic object for a given SON ID does not exists"); @@ -68,7 +68,7 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." ); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 2ef59f39..88089580 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -438,7 +438,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { const chain::global_property_object &gpo = d.get_global_properties(); chain::son_report_down_operation son_down_op; - son_down_op.payer = GRAPHENE_SON_ACCOUNT; + son_down_op.payer = d.get_global_properties().parameters.son_account(); son_down_op.son_id = son_id; son_down_op.down_ts = last_active_ts; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 38dbf4f3..9d4329b0 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -71,9 +71,9 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ const chain::global_property_object &gpo = database.get_global_properties(); // Deposit request - if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") != 0)) { + if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("1.3.0") != 0)) { son_wallet_deposit_create_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = gpo.parameters.son_account(); //op.son_id = ; // to be filled for each son op.timestamp = sed.timestamp; op.sidechain = sed.sidechain; @@ -112,7 +112,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } // Withdrawal request - if ((sed.peerplays_to == GRAPHENE_SON_ACCOUNT) && (sed.sidechain_currency.compare("1.3.0") == 0)) { + if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("1.3.0") == 0)) { // BTC Payout only (for now) const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin)); @@ -120,7 +120,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ return; son_wallet_withdraw_create_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = gpo.parameters.son_account(); //op.son_id = ; // to be filled for each son op.timestamp = sed.timestamp; op.sidechain = sed.sidechain; @@ -173,7 +173,7 @@ void sidechain_net_handler::process_deposits() { const chain::global_property_object &gpo = plugin.database().get_global_properties(); son_wallet_deposit_process_operation p_op; - p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.payer = gpo.parameters.son_account(); p_op.son_wallet_deposit_id = swdo.id; proposal_create_operation proposal_op; @@ -207,7 +207,7 @@ void sidechain_net_handler::process_withdrawals() { const chain::global_property_object &gpo = plugin.database().get_global_properties(); son_wallet_withdraw_process_operation p_op; - p_op.payer = GRAPHENE_SON_ACCOUNT; + p_op.payer = gpo.parameters.son_account(); p_op.son_wallet_withdraw_id = swwo.id; proposal_create_operation proposal_op; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index fc891054..e7b9a70c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -634,7 +634,7 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); son_wallet_update_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = gpo.parameters.son_account(); op.son_wallet_id = (*active_sw).id; op.sidechain = sidechain_type::bitcoin; op.address = res.str(); @@ -876,7 +876,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) sed.sidechain_currency = "BTC"; sed.sidechain_amount = v.out.amount; sed.peerplays_from = addr_itr->sidechain_address_account; - sed.peerplays_to = GRAPHENE_SON_ACCOUNT; + sed.peerplays_to = database.get_global_properties().parameters.son_account(); sed.peerplays_asset = asset(sed.sidechain_amount / 1000); // For Bitcoin, the exchange rate is 1:1, for others, get the exchange rate from market sidechain_event_data_received(sed); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index a30dc4d7..18f60b7a 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -62,7 +62,7 @@ void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { operation_index = operation_index + 1; if (op.which() == operation::tag::value) { transfer_operation transfer_op = op.get(); - if (transfer_op.to != GRAPHENE_SON_ACCOUNT) { + if (transfer_op.to != plugin.database().get_global_properties().parameters.son_account()) { continue; } diff --git a/tests/tests/sidechain_transaction_tests.cpp b/tests/tests/sidechain_transaction_tests.cpp index 25e319f0..d1132605 100644 --- a/tests/tests/sidechain_transaction_tests.cpp +++ b/tests/tests/sidechain_transaction_tests.cpp @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) generate_block(); const auto& acc_idx = db.get_index_type().indices().get(); - auto acc_itr = acc_idx.find(GRAPHENE_SON_ACCOUNT); + auto acc_itr = acc_idx.find(db.get_global_properties().parameters.son_account()); BOOST_REQUIRE(acc_itr != acc_idx.end()); db.modify(*acc_itr, [&](account_object &obj) { obj.active.account_auths.clear(); @@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) bitcoin_transaction_send_operation send_op; - send_op.payer = GRAPHENE_SON_ACCOUNT; + send_op.payer = gpo.parameters.son_account(); proposal_create_operation proposal_op; proposal_op.fee_paying_account = alice_id; @@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) bitcoin_send_transaction_process_operation process_op; process_op.bitcoin_transaction_id = bitcoin_transaction_id_type(0); - process_op.payer = GRAPHENE_SON_ACCOUNT; + process_op.payer = db.get_global_properties().parameters.son_account(); proposal_create_operation proposal_op; proposal_op.fee_paying_account = alice_id; diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 48c52feb..a917e550 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -220,7 +220,7 @@ try { son_delete_operation op; op.owner_account = alice_id; op.son_id = son_id_type(0); - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); trx.operations.push_back(op); sign(trx, bob_private_key); @@ -729,7 +729,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); @@ -742,7 +742,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { } { - // Check that transaction fails if payer is not GRAPHENE_SON_ACCOUNT. + // Check that transaction fails if payer is not db.get_global_properties().parameters.son_account(). generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; @@ -759,11 +759,11 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { } { - // Check that transaction succeeds after getting enough approvals on GRAPHENE_SON_ACCOUNT. + // Check that transaction succeeds after getting enough approvals on db.get_global_properties().parameters.son_account(). generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); op.down_ts = son_stats_obj->last_active_timestamp; @@ -783,7 +783,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); // Send Report Down Operation for an active status SON son_report_down_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); op.down_ts = son_stats_obj->last_active_timestamp; diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index 486f46ed..39e53269 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { son_wallet_recreate_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); { son_info si; @@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE( son_wallet_update_test ) { son_wallet_update_operation op; - op.payer = GRAPHENE_SON_ACCOUNT; + op.payer = db.get_global_properties().parameters.son_account(); op.son_wallet_id = son_wallet_id_type(0); op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; op.address = "bitcoin address"; From 0f97241f1b1418d11c0b6b4cd25afff5bbc774e4 Mon Sep 17 00:00:00 2001 From: obucina <11353193+obucina@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:44:22 +0100 Subject: [PATCH 64/64] Replace raw with psbt transactions to support parital tx signing (#311) * RPC calls for PSBT, raw transactions replaced with PSBT * Fix estimatesmartfeerate, extensive RPC calls logging for debugging purposes * Remove dead code * Partial signing functional for deposit and withdrawal * Fix sidechain_type declarations * Depositing Peerplays asset refactored * Partial signing functional for primary wallet funds moving * Prettier logs * Refactor multiple SON support processing * Serialize field complete from sidechain_transaction_sign_operation * Refactor transaction signing in particular order, BTC only (maybe) need it * Add number of required signatures parameter for addmultisigaddress * Change default bitcoin node parameters * Transaction signing only by scheduled son * Removed scheduling log * Prevent PW funds moving to the same address * Refactor sidechain_transaction_object processing, code cleanup * Remove obsolete tests * Decrease logging * Code readability * When updated, import son wallet bitcoin address to bitcoin wallet * When updated, recreate son wallet bitcoin address on each node * Refactor on_changed_objects, move it into task * Add check to prevent deposit/withdrawal double processing * Improved check for sidechain transaction object creation * Single sidechain transaction signature per block allowed only * Unlock wallet on addmultisigaddress * Import both address and redeem script on primary wallet change, fix some compiler warnings * Fix invalid list of signers for PW funds transfer --- libraries/app/impacted.cpp | 6 +- libraries/chain/CMakeLists.txt | 2 +- libraries/chain/db_init.cpp | 8 +- libraries/chain/db_notify.cpp | 8 +- .../chain/include/graphene/chain/database.hpp | 2 +- .../graphene/chain/protocol/operations.hpp | 6 +- .../chain/protocol/sidechain_address.hpp | 2 +- .../chain/protocol/sidechain_transaction.hpp | 59 +- .../include/graphene/chain/protocol/son.hpp | 2 +- .../chain/protocol/son_wallet_deposit.hpp | 10 +- .../chain/protocol/son_wallet_withdraw.hpp | 12 +- .../include/graphene/chain/protocol/types.hpp | 10 +- .../chain/sidechain_address_object.hpp | 3 +- .../include/graphene/chain/sidechain_defs.hpp | 26 + .../chain/sidechain_transaction_evaluator.hpp | 25 +- .../chain/sidechain_transaction_object.hpp | 62 +- .../chain/include/graphene/chain/son_info.hpp | 2 +- .../include/graphene/chain/son_object.hpp | 2 +- .../chain/son_wallet_deposit_object.hpp | 9 +- .../graphene/chain/son_wallet_object.hpp | 4 +- .../chain/son_wallet_withdraw_object.hpp | 11 +- .../chain/sidechain_transaction_evaluator.cpp | 232 +++-- .../chain/son_wallet_deposit_evaluator.cpp | 90 +- .../chain/son_wallet_withdraw_evaluator.cpp | 9 +- .../graphene/peerplays_sidechain/defs.hpp | 10 +- .../peerplays_sidechain_plugin.hpp | 6 +- .../sidechain_net_handler.hpp | 22 +- .../sidechain_net_handler_bitcoin.hpp | 46 +- .../sidechain_net_handler_peerplays.hpp | 11 +- .../sidechain_net_manager.hpp | 6 +- .../peerplays_sidechain_plugin.cpp | 197 +++-- .../sidechain_net_handler.cpp | 206 +++-- .../sidechain_net_handler_bitcoin.cpp | 821 ++++++++++++++---- .../sidechain_net_handler_peerplays.cpp | 33 +- .../sidechain_net_manager.cpp | 14 +- libraries/wallet/wallet.cpp | 2 +- tests/tests/sidechain_addresses_test.cpp | 2 +- tests/tests/sidechain_transaction_tests.cpp | 386 -------- tests/tests/son_wallet_tests.cpp | 2 +- 39 files changed, 1327 insertions(+), 1039 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/sidechain_defs.hpp delete mode 100644 tests/tests/sidechain_transaction_tests.cpp diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 90538087..e95817aa 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -343,13 +343,13 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } - void operator()( const bitcoin_transaction_send_operation& op ){ + void operator()( const sidechain_transaction_create_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const bitcoin_transaction_sign_operation& op ){ + void operator()( const sidechain_transaction_sign_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const bitcoin_send_transaction_process_operation& op ){ + void operator()( const sidechain_transaction_send_operation& op ){ _impacted.insert( op.payer ); } }; diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 9c068ba5..73db113d 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -130,7 +130,7 @@ add_library( graphene_chain ) add_dependencies( graphene_chain build_hardfork_hpp ) -target_link_libraries( graphene_chain fc graphene_db peerplays_sidechain ) +target_link_libraries( graphene_chain fc graphene_db ) target_include_directories( graphene_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 781146af..b3311c2d 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -269,9 +269,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); - register_evaluator(); - register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -321,7 +321,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 6cf7c7b0..db245f0a 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -330,13 +330,13 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } - void operator()( const bitcoin_transaction_send_operation& op ) { + void operator()( const sidechain_transaction_create_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const bitcoin_transaction_sign_operation& op ) { + void operator()( const sidechain_transaction_sign_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const bitcoin_send_transaction_process_operation& op ) { + void operator()( const sidechain_transaction_send_operation& op ) { _impacted.insert( op.payer ); } }; @@ -443,6 +443,8 @@ void get_relevant_accounts( const object* obj, flat_set& accoun assert( aobj != nullptr ); accounts.insert( aobj->sidechain_address_account ); break; + } case sidechain_transaction_object_type:{ + break; } } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c4c00627..857c6dab 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,7 +40,7 @@ #include -#include +#include #include diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index d633056f..24bb7ba5 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -157,9 +157,9 @@ namespace graphene { namespace chain { sidechain_address_add_operation, sidechain_address_update_operation, sidechain_address_delete_operation, - bitcoin_transaction_send_operation, - bitcoin_transaction_sign_operation, - bitcoin_send_transaction_process_operation + sidechain_transaction_create_operation, + sidechain_transaction_sign_operation, + sidechain_transaction_send_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp index 7418f55e..e79f65de 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp index eb7e942d..4ddbd7ce 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -1,60 +1,71 @@ #pragma once #include #include -#include +#include namespace graphene { namespace chain { - struct bitcoin_transaction_send_operation : public base_operation + struct sidechain_transaction_create_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - // TODO: BTC Transaction Structs go here - fc::flat_map> signatures; + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector signers; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; - struct bitcoin_transaction_sign_operation : public base_operation + struct sidechain_transaction_sign_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; - proposal_id_type proposal_id; - std::vector signatures; + asset fee; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + std::string transaction; + block_id_type block; + bool complete; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee( const fee_parameters_type& k )const { return 0; } }; - struct bitcoin_send_transaction_process_operation : public base_operation + struct sidechain_transaction_send_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - bitcoin_transaction_id_type bitcoin_transaction_id; + sidechain_transaction_id_type sidechain_transaction_id; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee( const fee_parameters_type& k )const { return 0; } }; } } // graphene::chain -FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(signatures) ) +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer) + (sidechain) + (object_id) + (transaction) + (signers) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) ) +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(payer) + (sidechain_transaction_id) + (transaction) + (block) + (complete) ) -FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation, (fee)(payer)(bitcoin_transaction_id) ) \ No newline at end of file +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer) + (sidechain_transaction_id) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 20353b91..3dfbc52d 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp index ddc1ff53..a1b44fac 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include @@ -14,7 +15,7 @@ namespace graphene { namespace chain { son_id_type son_id; fc::time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -46,7 +47,10 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer) - (son_id) (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset)) + (son_id) (timestamp) (sidechain) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) + (peerplays_from) (peerplays_to) (peerplays_asset) ) + FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer) - (son_wallet_deposit_id)) + (son_wallet_deposit_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp index 4e9d4971..353d695e 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include @@ -14,12 +15,12 @@ namespace graphene { namespace chain { son_id_type son_id; fc::time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string peerplays_uid; std::string peerplays_transaction_id; chain::account_id_type peerplays_from; chain::asset peerplays_asset; - peerplays_sidechain::sidechain_type withdraw_sidechain; + sidechain_type withdraw_sidechain; std::string withdraw_address; std::string withdraw_currency; safe withdraw_amount; @@ -45,7 +46,10 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer) - (son_id) (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + (son_id) (timestamp) (sidechain) + (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) + (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer) - (son_wallet_withdraw_id)) + (son_wallet_withdraw_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 1caf1f9c..63863ca1 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -151,7 +151,7 @@ namespace graphene { namespace chain { son_wallet_deposit_object_type, son_wallet_withdraw_object_type, sidechain_address_object_type, - bitcoin_transaction_object_type, + sidechain_transaction_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -219,7 +219,7 @@ namespace graphene { namespace chain { class son_wallet_deposit_object; class son_wallet_withdraw_object; class sidechain_address_object; - class bitcoin_transaction_object; + class sidechain_transaction_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; @@ -252,7 +252,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type; typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; - typedef object_id< protocol_ids, bitcoin_transaction_object_type,bitcoin_transaction_object> bitcoin_transaction_id_type; + typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type; // implementation types class global_property_object; @@ -443,7 +443,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (son_wallet_deposit_object_type) (son_wallet_withdraw_object_type) (sidechain_address_object_type) - (bitcoin_transaction_object_type) + (sidechain_transaction_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -521,7 +521,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::bitcoin_transaction_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index 1a8b6967..86e1b16d 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -3,7 +3,8 @@ #include #include -#include +#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp new file mode 100644 index 00000000..f51b9eaf --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace graphene { namespace chain { + +enum class sidechain_type { + bitcoin, + ethereum, + eos, + peerplays +}; + +} } + +namespace graphene { namespace peerplays_sidechain { + +using sidechain_type = graphene::chain::sidechain_type; + +} } + +FC_REFLECT_ENUM(graphene::chain::sidechain_type, + (bitcoin) + (ethereum) + (eos) + (peerplays) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp index aac04698..3bc64bfa 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp @@ -4,32 +4,31 @@ namespace graphene { namespace chain { -class bitcoin_transaction_send_evaluator : public evaluator +class sidechain_transaction_create_evaluator : public evaluator { public: - typedef bitcoin_transaction_send_operation operation_type; + typedef sidechain_transaction_create_operation operation_type; - void_result do_evaluate(const bitcoin_transaction_send_operation& o); - object_id_type do_apply(const bitcoin_transaction_send_operation& o); + void_result do_evaluate(const sidechain_transaction_create_operation& o); + object_id_type do_apply(const sidechain_transaction_create_operation& o); }; -class bitcoin_transaction_sign_evaluator : public evaluator +class sidechain_transaction_sign_evaluator : public evaluator { public: - typedef bitcoin_transaction_sign_operation operation_type; + typedef sidechain_transaction_sign_operation operation_type; - void_result do_evaluate(const bitcoin_transaction_sign_operation& o); - object_id_type do_apply(const bitcoin_transaction_sign_operation& o); - void update_proposal( const bitcoin_transaction_sign_operation& o ); + void_result do_evaluate(const sidechain_transaction_sign_operation& o); + object_id_type do_apply(const sidechain_transaction_sign_operation& o); }; -class bitcoin_send_transaction_process_evaluator : public evaluator +class sidechain_transaction_send_evaluator : public evaluator { public: - typedef bitcoin_send_transaction_process_operation operation_type; + typedef sidechain_transaction_send_operation operation_type; - void_result do_evaluate(const bitcoin_send_transaction_process_operation& o); - object_id_type do_apply(const bitcoin_send_transaction_process_operation& o); + void_result do_evaluate(const sidechain_transaction_send_operation& o); + object_id_type do_apply(const sidechain_transaction_send_operation& o); }; } } // namespace graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp index 95417852..f4f596cf 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -1,39 +1,69 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; /** - * @class bitcoin_transaction_object - * @brief tracks state of bitcoin transaction. + * @class sidechain_transaction_object + * @brief tracks state of sidechain transaction during signing process. * @ingroup object */ - class bitcoin_transaction_object : public abstract_object + class sidechain_transaction_object : public abstract_object { public: static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = bitcoin_transaction_object_type; - // Bitcoin structs go here. - bool processed = false; - fc::flat_map> signatures; + static const uint8_t type_id = sidechain_transaction_object_type; + + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector> signers; + + block_id_type block; + bool valid = false; + bool complete = false; + bool sent = false; }; - struct by_processed; - using bitcoin_transaction_multi_index_type = multi_index_container< - bitcoin_transaction_object, + struct by_object_id; + struct by_sidechain_and_complete; + struct by_sidechain_and_complete_and_sent; + using sidechain_transaction_multi_index_type = multi_index_container< + sidechain_transaction_object, indexed_by< ordered_unique< tag, member >, - ordered_non_unique< tag, - member + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member + > + >, + ordered_non_unique< tag, + composite_key, + member, + member + > > > >; - using bitcoin_transaction_index = generic_index; + using sidechain_transaction_index = generic_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::db::object), - (processed)(signatures) ) +FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ), + (sidechain) + (object_id) + (transaction) + (signers) + (block) + (valid) + (complete) + (sent) ) diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp index d30f0f6b..bc0c823b 100644 --- a/libraries/chain/include/graphene/chain/son_info.hpp +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index ec346185..5aee1265 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp index 4c30cc49..6ddc44c2 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -17,7 +18,7 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_deposit_object_type; time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -45,7 +46,7 @@ namespace graphene { namespace chain { member >, ordered_non_unique< tag, - member + member >, ordered_unique< tag, member @@ -55,7 +56,7 @@ namespace graphene { namespace chain { >, ordered_non_unique< tag, composite_key, + member, member > > diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index aec28342..315def33 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -20,7 +20,7 @@ namespace graphene { namespace chain { time_point_sec valid_from; time_point_sec expires; - flat_map addresses; + flat_map addresses; vector sons; }; diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp index 71245ba7..ef39aadf 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -17,12 +18,12 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_withdraw_object_type; time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string peerplays_uid; std::string peerplays_transaction_id; chain::account_id_type peerplays_from; chain::asset peerplays_asset; - peerplays_sidechain::sidechain_type withdraw_sidechain; + sidechain_type withdraw_sidechain; std::string withdraw_address; std::string withdraw_currency; safe withdraw_amount; @@ -47,14 +48,14 @@ namespace graphene { namespace chain { member >, ordered_non_unique< tag, - member + member >, ordered_non_unique< tag, member >, ordered_non_unique< tag, composite_key, + member, member > > diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index a016ce27..3c72b9e9 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -1,140 +1,128 @@ #include +#include #include +#include #include #include -#include -#include -namespace graphene -{ -namespace chain -{ +namespace graphene { namespace chain { -void_result bitcoin_transaction_send_evaluator::do_evaluate(const bitcoin_transaction_send_operation &op) -{ - try - { - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} +void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); -object_id_type bitcoin_transaction_send_evaluator::do_apply(const bitcoin_transaction_send_operation &op) -{ - try - { - const auto &new_bitcoin_transaction_object = db().create([&](bitcoin_transaction_object &obj) { - obj.processed = false; - obj.signatures = op.signatures; + FC_ASSERT((op.object_id.is() || op.object_id.is() || op.object_id.is()), "Invalid object id"); + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.object_id); + FC_ASSERT(sto_obj == sto_idx.end(), "Sidechain transaction for a given object is already created"); + + FC_ASSERT(!op.transaction.empty(), "Sidechain transaction data not set"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) +{ try { + const auto &new_sidechain_transaction_object = db().create([&](sidechain_transaction_object &sto) { + sto.sidechain = op.sidechain; + sto.object_id = op.object_id; + sto.transaction = op.transaction; + std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signers, sto.signers.end()), [](const son_id_type son_id) { + return std::make_pair(son_id, false); }); - return new_bitcoin_transaction_object.id; - } - FC_CAPTURE_AND_RETHROW((op)) -} + sto.block = db().head_block_id(); + sto.valid = true; + sto.complete = false; + sto.sent = false; + }); + return new_sidechain_transaction_object.id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } -void_result bitcoin_transaction_sign_evaluator::do_evaluate(const bitcoin_transaction_sign_operation &op) -{ - try - { - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - const auto &proposal_idx = db().get_index_type().indices().get(); - const auto &proposal_itr = proposal_idx.find(op.proposal_id); - FC_ASSERT(proposal_idx.end() != proposal_itr, "proposal not found"); - // Checks can this SON approve this proposal - auto can_this_son_approve_this_proposal = [&]() { - const auto &sidx = db().get_index_type().indices().get(); - auto son_obj = sidx.find(op.payer); - if (son_obj == sidx.end()) - { - return false; - } - // TODO: Check if the SON is included in the PW script. - return true; - }; +void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_transaction_sign_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - FC_ASSERT(can_this_son_approve_this_proposal(), "Invalid approval received"); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); -object_id_type bitcoin_transaction_sign_evaluator::do_apply(const bitcoin_transaction_sign_operation &op) -{ - try - { - const auto &proposal = op.proposal_id(db()); - const auto &sidx = db().get_index_type().indices().get(); - auto son_obj = sidx.find(op.payer); + const auto &son_idx = db().get_index_type().indices().get(); + const auto &son_obj = son_idx.find(op.payer); + FC_ASSERT(son_obj != son_idx.end(), "SON object not found"); - db().modify(proposal, [&](proposal_object &po) { - auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get(); - bitcoin_transaction_send_op.signatures[son_obj->id] = op.signatures; - po.proposed_transaction.operations[0] = bitcoin_transaction_send_op; - }); - - db().modify( son_obj->statistics( db() ), [&]( son_statistics_object& sso ) { - sso.txs_signed += 1; - } ); - - update_proposal(op); - } - FC_CAPTURE_AND_RETHROW((op)) -} - -void bitcoin_transaction_sign_evaluator::update_proposal(const bitcoin_transaction_sign_operation &op) -{ - database &d = db(); - proposal_update_operation update_op; - - update_op.fee_paying_account = op.payer; - update_op.proposal = op.proposal_id; - update_op.active_approvals_to_add = {op.payer}; - - bool skip_fee_old = trx_state->skip_fee; - bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check; - trx_state->skip_fee = true; - trx_state->skip_fee_schedule_check = true; - - d.apply_operation(*trx_state, update_op); - - trx_state->skip_fee = skip_fee_old; - trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old; -} - -void_result bitcoin_send_transaction_process_evaluator::do_evaluate(const bitcoin_send_transaction_process_operation &op) -{ - try - { - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - const auto& btidx = db().get_index_type().indices().get(); - const auto btobj = btidx.find(op.bitcoin_transaction_id); - FC_ASSERT(btobj != btidx.end(), "Bitcoin Transaction Object not found"); - FC_ASSERT(btobj->processed == false, "Bitcoin Transaction already processed"); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} - -object_id_type bitcoin_send_transaction_process_evaluator::do_apply(const bitcoin_send_transaction_process_operation &op) -{ - try - { - const auto &btidx = db().get_index_type().indices().get(); - auto btobj = btidx.find(op.bitcoin_transaction_id); - if (btobj != btidx.end()) - { - db().modify(*btobj, [&op](bitcoin_transaction_object &bto) { - bto.processed = true; - }); + bool expected = false; + for (auto signer : sto_obj->signers) { + if (signer.first == son_obj->id) { + expected = !signer.second; } - return op.bitcoin_transaction_id; } - FC_CAPTURE_AND_RETHROW((op)) -} + FC_ASSERT(expected, "Signer not expected"); + + FC_ASSERT(sto_obj->block == op.block, "Sidechain transaction already signed in this block"); + + FC_ASSERT(sto_obj->valid, "Transaction not valid"); + FC_ASSERT(!sto_obj->complete, "Transaction signing completed"); + FC_ASSERT(!sto_obj->sent, "Transaction already sent"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_transaction_sign_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + const auto &son_idx = db().get_index_type().indices().get(); + auto son_obj = son_idx.find(op.payer); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.transaction = op.transaction; + sto.block = db().head_block_id(); + sto.complete = op.complete; + for (size_t i = 0; i < sto.signers.size(); i++) { + if (sto.signers.at(i).first == son_obj->id) { + sto.signers.at(i).second = true; + } + } + }); + + db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) { + sso.txs_signed += 1; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_transaction_send_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); + + FC_ASSERT(sto_obj->valid, "Transaction not valid"); + FC_ASSERT(sto_obj->complete, "Transaction signing not complete"); + FC_ASSERT(!sto_obj->sent, "Transaction already sent"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_transaction_send_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.block = db().head_block_id(); + sto.sent = true; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } } // namespace chain } // namespace graphene diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index c0f95e78..e2734ea4 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -23,21 +23,22 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de const auto& idx = db().get_index_type().indices().get(); auto itr = idx.find(op.sidechain_uid); if (itr == idx.end()) { - const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swto ){ - swto.timestamp = op.timestamp; - swto.sidechain = op.sidechain; - swto.sidechain_uid = op.sidechain_uid; - swto.sidechain_transaction_id = op.sidechain_transaction_id; - swto.sidechain_from = op.sidechain_from; - swto.sidechain_to = op.sidechain_to; - swto.sidechain_amount = op.sidechain_amount; - swto.peerplays_from = op.peerplays_from; - swto.peerplays_to = op.peerplays_to; - swto.peerplays_asset = op.peerplays_asset; + const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swdo ){ + swdo.timestamp = op.timestamp; + swdo.sidechain = op.sidechain; + swdo.sidechain_uid = op.sidechain_uid; + swdo.sidechain_transaction_id = op.sidechain_transaction_id; + swdo.sidechain_from = op.sidechain_from; + swdo.sidechain_to = op.sidechain_to; + swdo.sidechain_currency = op.sidechain_currency; + swdo.sidechain_amount = op.sidechain_amount; + swdo.peerplays_from = op.peerplays_from; + swdo.peerplays_to = op.peerplays_to; + swdo.peerplays_asset = op.peerplays_asset; auto &gpo = db().get_global_properties(); for (auto &si : gpo.active_sons) { - swto.expected_reports.insert(si.son_id); + swdo.expected_reports.insert(si.son_id); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { @@ -48,14 +49,14 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de }); } - swto.received_reports.insert(op.son_id); + swdo.received_reports.insert(op.son_id); - swto.processed = false; + swdo.processed = false; }); return new_son_wallet_deposit_object.id; } else { - db().modify(*itr, [&op](son_wallet_deposit_object &swto) { - swto.received_reports.insert(op.son_id); + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.received_reports.insert(op.son_id); }); auto stats_itr = db().get_index_type().indices().get().find(op.son_id); db().modify(*stats_itr, [&op](son_statistics_object &sso) { @@ -73,47 +74,8 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); - - const database& d = db(); - - const account_object& from_account = itr->peerplays_to(d); // reversed, for deposit - const account_object& to_account = itr->peerplays_from(d); // reversed, for deposit - const asset_object& asset_type = itr->peerplays_asset.asset_id(d); - - try { - - GRAPHENE_ASSERT( - is_authorized_asset( d, from_account, asset_type ), - transfer_from_account_not_whitelisted, - "'from' account ${from} is not whitelisted for asset ${asset}", - ("from",from_account.id) - ("asset",itr->peerplays_asset.asset_id) - ); - GRAPHENE_ASSERT( - is_authorized_asset( d, to_account, asset_type ), - transfer_to_account_not_whitelisted, - "'to' account ${to} is not whitelisted for asset ${asset}", - ("to",to_account.id) - ("asset",itr->peerplays_asset.asset_id) - ); - - if( asset_type.is_transfer_restricted() ) - { - GRAPHENE_ASSERT( - from_account.id == asset_type.issuer || to_account.id == asset_type.issuer, - transfer_restricted_transfer_asset, - "Asset {asset} has transfer_restricted flag enabled", - ("asset", itr->peerplays_asset.asset_id) - ); - } - - bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_asset.amount; - FC_ASSERT( insufficient_balance, - "Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'", - ("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_asset))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) ); - - return void_result(); - } FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_asset))("f",from_account.name)("t",to_account.name) ); + FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op) @@ -122,17 +84,9 @@ object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_d auto itr = idx.find(op.son_wallet_deposit_id); if(itr != idx.end()) { - if (itr->processed == false) { - db().modify(*itr, [&op](son_wallet_deposit_object &swto) { - swto.processed = true; - }); - - const account_id_type from_account = itr->peerplays_to; // reversed, for deposit - const account_id_type to_account = itr->peerplays_from; // reversed, for deposit - - db().adjust_balance( from_account, -itr->peerplays_asset ); - db().adjust_balance( to_account, itr->peerplays_asset ); - } + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.processed = true; + }); } return op.son_wallet_deposit_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index d0611b7c..7a3930b1 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -73,6 +73,7 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_ const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); + FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -82,11 +83,9 @@ object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_ auto itr = idx.find(op.son_wallet_withdraw_id); if(itr != idx.end()) { - if (itr->processed == false) { - db().modify(*itr, [&op](son_wallet_withdraw_object &swto) { - swto.processed = true; - }); - } + db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) { + swwo.processed = true; + }); } return op.son_wallet_withdraw_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index b0484efd..6dd49252 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -10,16 +10,10 @@ #include #include +#include namespace graphene { namespace peerplays_sidechain { -enum class sidechain_type { - bitcoin, - ethereum, - eos, - peerplays -}; - using bytes = std::vector; struct prev_out { @@ -76,5 +70,3 @@ struct sidechain_event_data { }; }} // namespace graphene::peerplays_sidechain - -FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin)(ethereum)(eos)(peerplays)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 0d39b855..c69efaf4 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -5,6 +5,7 @@ #include namespace graphene { namespace peerplays_sidechain { + using namespace chain; namespace detail { @@ -26,8 +27,9 @@ public: std::unique_ptr my; std::set &get_sons(); - son_id_type &get_current_son_id(); - son_object get_son_object(son_id_type son_id); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); bool is_active_son(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index f3a7aa41..5814b208 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -5,6 +5,11 @@ #include #include +#include +#include +#include +#include +#include #include #include @@ -15,7 +20,7 @@ public: sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); virtual ~sidechain_net_handler(); - graphene::peerplays_sidechain::sidechain_type get_sidechain(); + sidechain_type get_sidechain(); std::vector get_sidechain_deposit_addresses(); std::vector get_sidechain_withdraw_addresses(); std::string get_private_key(std::string public_key); @@ -23,23 +28,22 @@ public: void sidechain_event_data_received(const sidechain_event_data &sed); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); virtual void recreate_primary_wallet() = 0; - virtual void process_deposit(const son_wallet_deposit_object &swdo) = 0; - virtual void process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; + virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0; + virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; + virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) = 0; + virtual bool send_sidechain_transaction(const sidechain_transaction_object &sto) = 0; protected: peerplays_sidechain_plugin &plugin; graphene::chain::database &database; - graphene::peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::map private_keys; - virtual std::string create_multisignature_wallet(const std::vector public_keys) = 0; - virtual std::string transfer(const std::string &from, const std::string &to, const uint64_t amount) = 0; - virtual std::string sign_transaction(const std::string &transaction) = 0; - virtual std::string send_transaction(const std::string &transaction) = 0; - private: }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index ec9689f6..637a5254 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -7,8 +7,6 @@ #include #include -#include -#include namespace graphene { namespace peerplays_sidechain { @@ -22,23 +20,27 @@ public: class bitcoin_rpc_client { public: bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password); - bool connection_is_not_defined() const; - std::string addmultisigaddress(const std::vector public_keys); + std::string addmultisigaddress(const uint32_t nrequired, const std::vector public_keys); + std::string createpsbt(const std::vector &ins, const fc::flat_map outs); std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); std::string createwallet(const std::string &wallet_name); + std::string decodepsbt(std::string const &tx_psbt); + std::string decoderawtransaction(std::string const &tx_hex); std::string encryptwallet(const std::string &passphrase); uint64_t estimatesmartfee(); + std::string finalizepsbt(std::string const &tx_psbt); + std::string getaddressinfo(const std::string &address); std::string getblock(const std::string &block_hash, int32_t verbosity = 2); void importaddress(const std::string &address_or_script); std::vector listunspent(); std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount); std::string loadwallet(const std::string &filename); - void sendrawtransaction(const std::string &tx_hex); - std::string signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key); + bool sendrawtransaction(const std::string &tx_hex); std::string signrawtransactionwithwallet(const std::string &tx_hash); std::string unloadwallet(const std::string &filename); std::string walletlock(); + std::string walletprocesspsbt(std::string const &tx_psbt); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: @@ -59,9 +61,6 @@ private: class zmq_listener { public: zmq_listener(std::string _ip, uint32_t _zmq); - bool connection_is_not_defined() const { - return zmq_port == 0; - } fc::signal event_received; @@ -84,8 +83,10 @@ public: virtual ~sidechain_net_handler_bitcoin(); void recreate_primary_wallet(); - void process_deposit(const son_wallet_deposit_object &swdo); - void process_withdrawal(const son_wallet_withdraw_object &swwo); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete); + bool send_sidechain_transaction(const sidechain_transaction_object &sto); private: std::string ip; @@ -99,17 +100,24 @@ private: std::unique_ptr bitcoin_client; std::unique_ptr listener; - std::string create_multisignature_wallet(const std::vector public_keys); - std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); - std::string sign_transaction(const std::string &transaction); - std::string send_transaction(const std::string &transaction); - std::string sign_and_send_transaction_with_wallet(const std::string &tx_json); - std::string transfer_all_btc(const std::string &from_address, const std::string &to_address); - std::string transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo); - std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); + fc::future on_changed_objects_task; + + std::string create_transaction(const std::vector &inputs, const fc::flat_map outputs); + std::string sign_transaction(const std::string &tx, bool &complete); + bool send_transaction(const std::string &tx); + + std::string create_transaction_raw(const std::vector &inputs, const fc::flat_map outputs); + std::string create_transaction_psbt(const std::vector &inputs, const fc::flat_map outputs); + std::string create_transaction_standalone(const std::vector &inputs, const fc::flat_map outputs); + + std::string sign_transaction_raw(const std::string &tx, bool &complete); + std::string sign_transaction_psbt(const std::string &tx, bool &complete); + std::string sign_transaction_standalone(const std::string &tx, bool &complete); void handle_event(const std::string &event_data); std::vector extract_info_from_block(const std::string &_block); + void on_changed_objects(const vector &ids, const flat_set &accounts); + void on_changed_objects_cb(const vector &ids, const flat_set &accounts); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp index 943c3a90..157dc421 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -14,15 +14,12 @@ public: virtual ~sidechain_net_handler_peerplays(); void recreate_primary_wallet(); - void process_deposit(const son_wallet_deposit_object &swdo); - void process_withdrawal(const son_wallet_withdraw_object &swwo); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete); + bool send_sidechain_transaction(const sidechain_transaction_object &sto); private: - std::string create_multisignature_wallet(const std::vector public_keys); - std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); - std::string sign_transaction(const std::string &transaction); - std::string send_transaction(const std::string &transaction); - void on_applied_block(const signed_block &b); }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index 8e246bce..29d9c7f1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -15,10 +15,12 @@ public: sidechain_net_manager(peerplays_sidechain_plugin &_plugin); virtual ~sidechain_net_manager(); - bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map &options); + bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options); void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); private: peerplays_sidechain_plugin &plugin; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 88089580..deda02b3 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -33,8 +33,9 @@ public: void plugin_startup(); std::set &get_sons(); - son_id_type &get_current_son_id(); - son_object get_son_object(son_id_type son_id); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); bool is_active_son(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); @@ -49,6 +50,8 @@ public: void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); private: peerplays_sidechain_plugin &plugin; @@ -67,6 +70,7 @@ private: fc::future _heartbeat_task; fc::future _son_processing_task; + bool first_block_skipped; void on_applied_block(const signed_block &b); }; @@ -76,7 +80,8 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec config_ready_bitcoin(false), config_ready_peerplays(false), current_son_id(son_id_type(std::numeric_limits().max())), - net_manager(nullptr) { + net_manager(nullptr), + first_block_skipped(false) { } peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { @@ -110,9 +115,9 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()); cli.add_options()("peerplays-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), "Tuple of [PublicKey, WIF private key] (may specify multiple times)"); - cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node"); + cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); - cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); @@ -222,11 +227,15 @@ std::set &peerplays_sidechain_plugin_impl::get_sons() { return sons; } -son_id_type &peerplays_sidechain_plugin_impl::get_current_son_id() { +const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { return current_son_id; } -son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { +const son_object peerplays_sidechain_plugin_impl::get_current_son_object() { + return get_son_object(current_son_id); +} + +const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { const auto &idx = plugin.database().get_index_type().indices().get(); auto son_obj = idx.find(son_id); if (son_obj == idx.end()) @@ -285,7 +294,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { for (son_id_type son_id : sons) { if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { - ilog("peerplays_sidechain_plugin: sending heartbeat for SON ${son}", ("son", son_id)); + ilog("Sending heartbeat for SON ${son}", ("son", son_id)); chain::son_heartbeat_operation op; op.owner_account = get_son_object(son_id).son_account; op.son_id = son_id; @@ -298,7 +307,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}", ("e", e.what())); + elog("Sending heartbeat failed with exception ${e}", ("e", e.what())); return false; } }); @@ -324,34 +333,56 @@ void peerplays_sidechain_plugin_impl::son_processing() { return; } - chain::son_id_type next_son_id = plugin.database().get_scheduled_son(1); - ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}", ("son", next_son_id)); + fc::time_point now_fine = fc::time_point::now(); + fc::time_point_sec now = now_fine + fc::microseconds(500000); + if (plugin.database().get_slot_time(1) < now) { + return; // Not synced + } - // Tasks that are executed by all active SONs, no matter if scheduled - // E.g. sending approvals and signing - approve_proposals(); + chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1); + ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ", + ("scheduled_son_id", scheduled_son_id)("now", now)); - // Tasks that are executed by scheduled and active SON - if (sons.find(next_son_id) != sons.end()) { + for (son_id_type son_id : plugin.get_sons()) { - current_son_id = next_son_id; + if (plugin.is_active_son(son_id)) { - create_son_down_proposals(); + current_son_id = son_id; - create_son_deregister_proposals(); + // Tasks that are executed by all active SONs, no matter if scheduled + // E.g. sending approvals and signing (only signing that can be done in parallel) + approve_proposals(); - recreate_primary_wallet(); + // Tasks that are executed by scheduled and active SON + if (current_son_id == scheduled_son_id) { - process_deposits(); + create_son_down_proposals(); - process_withdrawals(); + create_son_deregister_proposals(); + + recreate_primary_wallet(); + + process_deposits(); + + process_withdrawals(); + + process_sidechain_transactions(); + + send_sidechain_transactions(); + } + } else { + // Tasks that are executed by previously active SONs + // E.g. sending approvals and signing that SON was required to do while it was active + //approve_leftover_proposals(); ??? + //process_leftover_sidechain_transactions(); ??? + } } } void peerplays_sidechain_plugin_impl::approve_proposals() { auto approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_id_type &proposal_id) { - ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id)); + ilog("Sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id)); chain::proposal_update_operation puo; puo.fee_paying_account = get_son_object(son_id).son_account; puo.proposal = proposal_id; @@ -364,7 +395,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}", ("e", e.what())); + elog("Sending approval failed with exception ${e}", ("e", e.what())); return false; } }); @@ -378,57 +409,71 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } for (const auto proposal_id : proposals) { - for (son_id_type son_id : sons) { - if (!is_active_son(son_id)) { + + const object *obj = plugin.database().find_object(proposal_id); + const chain::proposal_object *proposal_ptr = dynamic_cast(obj); + if (proposal_ptr == nullptr) { + continue; + } + const proposal_object proposal = *proposal_ptr; + + if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1) { + int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which(); + + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - const object *obj = plugin.database().find_object(proposal_id); - const chain::proposal_object *proposal_ptr = dynamic_cast(obj); - if (proposal_ptr == nullptr) { - continue; - } - const proposal_object proposal = *proposal_ptr; - - if (proposal.available_active_approvals.find(get_son_object(son_id).son_account) != proposal.available_active_approvals.end()) { + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); - continue; - } - - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); - continue; - } - - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } } + + if (proposal.proposed_transaction.operations.size() == 2) { + int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which(); + int32_t op_idx_1 = proposal.proposed_transaction.operations[1].which(); + + if ((op_idx_0 == chain::operation::tag::value) && + (op_idx_1 == chain::operation::tag::value)) { + approve_proposal(get_current_son_id(), proposal.id); + continue; + } + } + + ilog("=================================================="); + ilog("Proposal not approved ${proposal}", ("proposal", proposal)); + ilog("=================================================="); } } @@ -443,8 +488,8 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { son_down_op.down_ts = last_active_ts; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.push_back(op_wrapper(son_down_op)); + proposal_op.fee_paying_account = get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); return proposal_op; @@ -467,7 +512,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { int64_t down_threshold = gpo.parameters.son_down_time(); if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { - ilog("peerplays_sidechain_plugin: sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); + ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); fc::future fut = fc::async([&]() { @@ -477,7 +522,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}", ("e", e.what())); + elog("Sending son down proposal failed with exception ${e}", ("e", e.what())); return false; } }); @@ -502,7 +547,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); if (op.valid()) { // Signing and pushing into the txs to be included in the block - ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); + ilog("Sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); fc::future fut = fc::async([&]() { try { @@ -511,7 +556,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}", ("e", e.what())); + elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what())); return false; } }); @@ -534,11 +579,23 @@ void peerplays_sidechain_plugin_impl::process_withdrawals() { net_manager->process_withdrawals(); } -void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { - schedule_son_processing(); +void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { + net_manager->process_sidechain_transactions(); } -} // end namespace detail +void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { + net_manager->send_sidechain_transactions(); +} + +void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { + if (first_block_skipped) { + schedule_son_processing(); + } else { + first_block_skipped = true; + } +} + +} // namespace detail peerplays_sidechain_plugin::peerplays_sidechain_plugin() : my(new detail::peerplays_sidechain_plugin_impl(*this)) { @@ -559,24 +616,30 @@ void peerplays_sidechain_plugin::plugin_set_program_options( } void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) { - ilog("peerplays sidechain plugin: plugin_initialize()"); + ilog("peerplays sidechain plugin: plugin_initialize() begin"); my->plugin_initialize(options); + ilog("peerplays sidechain plugin: plugin_initialize() end"); } void peerplays_sidechain_plugin::plugin_startup() { - ilog("peerplays sidechain plugin: plugin_startup()"); + ilog("peerplays sidechain plugin: plugin_startup() begin"); my->plugin_startup(); + ilog("peerplays sidechain plugin: plugin_startup() end"); } std::set &peerplays_sidechain_plugin::get_sons() { return my->get_sons(); } -son_id_type &peerplays_sidechain_plugin::get_current_son_id() { +const son_id_type peerplays_sidechain_plugin::get_current_son_id() { return my->get_current_son_id(); } -son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { +const son_object peerplays_sidechain_plugin::get_current_son_object() { + return my->get_current_son_object(); +} + +const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 9d4329b0..c6eccd12 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -1,10 +1,7 @@ #include -#include -#include -#include - #include +#include namespace graphene { namespace peerplays_sidechain { @@ -16,7 +13,7 @@ sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin sidechain_net_handler::~sidechain_net_handler() { } -graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidechain() { +sidechain_type sidechain_net_handler::get_sidechain() { return sidechain; } @@ -94,17 +91,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.proposed_ops.emplace_back(op); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + elog("Sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -140,17 +137,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.proposed_ops.emplace_back(op); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + elog("Sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -161,83 +158,170 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } void sidechain_net_handler::process_deposits() { - const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx = database.get_index_type().indices().get(); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - std::for_each(idx_range.first, idx_range.second, - [&](const son_wallet_deposit_object &swdo) { - ilog("Deposit to process: ${swdo}", ("swdo", swdo)); + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { + ilog("Deposit to process: ${swdo}", ("swdo", swdo)); - process_deposit(swdo); + bool process_deposit_result = process_deposit(swdo); - const chain::global_property_object &gpo = plugin.database().get_global_properties(); + if (!process_deposit_result) { + wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); + return; + } - son_wallet_deposit_process_operation p_op; - p_op.payer = gpo.parameters.son_account(); - p_op.son_wallet_deposit_id = swdo.id; + const chain::global_property_object &gpo = database.get_global_properties(); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if (plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); - } - }); + transfer_operation t_op; + t_op.fee = asset(2000000); + t_op.from = swdo.peerplays_to; // gpo.parameters.son_account() + t_op.to = swdo.peerplays_from; + t_op.amount = swdo.peerplays_asset; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(swdp_op); + proposal_op.proposed_ops.emplace_back(t_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + }); } void sidechain_net_handler::process_withdrawals() { - const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx = database.get_index_type().indices().get(); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - std::for_each(idx_range.first, idx_range.second, - [&](const son_wallet_withdraw_object &swwo) { - ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { + ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); - process_withdrawal(swwo); + bool process_withdrawal_result = process_withdrawal(swwo); - const chain::global_property_object &gpo = plugin.database().get_global_properties(); + if (!process_withdrawal_result) { + wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); + return; + } - son_wallet_withdraw_process_operation p_op; - p_op.payer = gpo.parameters.son_account(); - p_op.son_wallet_withdraw_id = swwo.id; + const chain::global_property_object &gpo = database.get_global_properties(); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); - if (plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); - } - }); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(swwp_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::process_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + ilog("Sidechain transaction to process: ${sto}", ("sto", sto)); + + bool complete = false; + std::string processed_sidechain_tx = process_sidechain_transaction(sto, complete); + + if (processed_sidechain_tx.empty()) { + wlog("Sidechain transaction not processed: ${sto}", ("sto", sto)); + return; + } + + sidechain_transaction_sign_operation sts_op; + sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.sidechain_transaction_id = sto.id; + sts_op.transaction = processed_sidechain_tx; + sts_op.block = sto.block; + sts_op.complete = complete; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::send_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + ilog("Sidechain transaction to send: ${sto}", ("sto", sto)); + + bool sent = send_sidechain_transaction(sto); + + if (!sent) { + wlog("Sidechain transaction not sent: ${sto}", ("sto", sto)); + return; + } + + sidechain_transaction_send_operation sts_op; + sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.sidechain_transaction_id = sto.id; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what())); + } + }); } void sidechain_net_handler::recreate_primary_wallet() { FC_ASSERT(false, "recreate_primary_wallet not implemented"); } -void sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) { FC_ASSERT(false, "process_deposit not implemented"); } -void sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) { FC_ASSERT(false, "process_withdrawal not implemented"); } +std::string sidechain_net_handler::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + FC_ASSERT(false, "process_sidechain_transaction not implemented"); +} + +bool sidechain_net_handler::send_sidechain_transaction(const sidechain_transaction_object &sto) { + FC_ASSERT(false, "send_sidechain_transaction not implemented"); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index e7b9a70c..a788b04f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -32,14 +31,10 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::stri authorization.val = "Basic " + fc::base64_encode(user + ":" + password); } -bool bitcoin_rpc_client::connection_is_not_defined() const { - return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); -} - -std::string bitcoin_rpc_client::addmultisigaddress(const std::vector public_keys) { +std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector public_keys) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " "\"method\": \"addmultisigaddress\", \"params\": ["); - std::string params = "2, ["; + std::string params = std::to_string(nrequired) + ", ["; std::string pubkeys = ""; for (std::string pubkey : public_keys) { if (!pubkeys.empty()) { @@ -50,11 +45,11 @@ std::string bitcoin_rpc_client::addmultisigaddress(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " + "\"method\": \"createpsbt\", \"params\": ["); + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " "\"method\": \"createrawtransaction\", \"params\": ["); @@ -92,11 +131,11 @@ std::string bitcoin_rpc_client::createrawtransaction(const std::vector("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } - return std::string(); + return ""; } std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { @@ -121,7 +163,63 @@ std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": " + "\"decodepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": " + "\"decoderawtransaction\", \"params\": [\"" + + tx_hex + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -149,7 +247,7 @@ std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -175,7 +273,7 @@ uint64_t bitcoin_rpc_client::estimatesmartfee() { "\"method\": \"estimatesmartfee\", \"params\": [") + std::to_string(confirmation_target_blocks) + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -186,13 +284,79 @@ uint64_t bitcoin_rpc_client::estimatesmartfee() { boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - if (json.count("result")) - if (json.get_child("result").count("feerate")) { - auto feerate_str = json.get_child("result").get_child("feerate").get_value(); - feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); - return std::stoll(feerate_str); + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.find("feerate") != json_result.not_found()) { + auto feerate_str = json_result.get("feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } + + if (json_result.find("errors") != json_result.not_found()) { + wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } } - return 0; + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return 20000; +} + +std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " + "\"finalizepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": " + "\"getaddressinfo\", \"params\": [\"" + + address + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; } std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { @@ -204,7 +368,7 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -228,7 +392,7 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script) { "\"method\": \"importaddress\", \"params\": [") + std::string("\"") + address_or_script + std::string("\"") + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -240,7 +404,6 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script) { boost::property_tree::read_json(ss, json); if (reply.status == 200) { - idump((address_or_script)(ss.str())); return; } else if (json.count("error") && !json.get_child("error").empty()) { wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); @@ -326,7 +489,7 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -345,16 +508,16 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { return ""; } -void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { +bool bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " "\"method\": \"sendrawtransaction\", \"params\": [") + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; + return false; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -362,18 +525,14 @@ void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { boost::property_tree::read_json(ss, json); if (reply.status == 200) { - return; + return true; } else if (json.count("error") && !json.get_child("error").empty()) { const auto error_code = json.get_child("error").get_child("code").get_value(); if (error_code == -27) // transaction already in block chain - return; - + return true; wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } -} - -std::string bitcoin_rpc_client::signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key) { - return ""; + return false; } std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { @@ -382,11 +541,11 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & std::string params = "\"" + tx_hash + "\""; body = body + params + std::string("]}"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -412,7 +571,7 @@ std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -439,7 +598,7 @@ std::string bitcoin_rpc_client::walletlock() { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -458,6 +617,32 @@ std::string bitcoin_rpc_client::walletlock() { return ""; } +std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " + "\"walletprocesspsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " "\"walletpassphrase\", \"params\": [\"" + @@ -497,10 +682,10 @@ fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool sho fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); if (show_log) { - ilog("Request URL: ${url}", ("url", url)); - ilog("Request: ${body}", ("body", body)); + ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request: ${body}", ("body", body)); std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - ilog("Response: ${ss}", ("ss", ss.str())); + ilog("### Response: ${ss}", ("ss", ss.str())); } return reply; @@ -603,9 +788,22 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain listener->event_received.connect([this](const std::string &event_data) { std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); }); + + database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { + on_changed_objects(ids, accounts); + }); } sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { + try { + if (on_changed_objects_task.valid()) { + on_changed_objects_task.cancel_and_wait(__FUNCTION__); + } + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } } void sidechain_net_handler_bitcoin::recreate_primary_wallet() { @@ -623,7 +821,12 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { for (const son_info &si : active_sons) { son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); } - string reply_str = create_multisignature_wallet(son_pubkeys_bitcoin); + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1; + string reply_str = bitcoin_client->addmultisigaddress(nrequired, son_pubkeys_bitcoin); std::stringstream active_pw_ss(reply_str); boost::property_tree::ptree active_pw_pt; @@ -635,13 +838,13 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { son_wallet_update_operation op; op.payer = gpo.parameters.son_account(); - op.son_wallet_id = (*active_sw).id; + op.son_wallet_id = active_sw->id; op.sidechain = sidechain_type::bitcoin; op.address = res.str(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(op); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -651,7 +854,7 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; } @@ -664,109 +867,80 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { std::string active_pw_address = active_pw_pt.get_child("result").get("address"); std::string prev_pw_address = prev_sw_pt.get("address"); - transfer_all_btc(prev_pw_address, active_pw_address); + if (prev_pw_address == active_pw_address) { + elog("BTC previous and new primary wallet addresses are same. No funds moving needed [from ${prev_sw} to ${active_sw}]", ("prev_sw", prev_sw->id)("active_sw", active_sw->id)); + return; + } + + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now + double total_amount = 0.0; + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); + + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", prev_pw_address)); + return; + } else { + for (const auto &utx : inputs) { + total_amount += utx.amount_; + } + + if (min_amount >= total_amount) { + elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address)); + return; + } + } + + fc::flat_map outputs; + outputs[active_pw_address] = total_amount - min_amount; + + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + + auto signer_sons = prev_sw->sons; + std::vector signers; + for (const son_info &si : signer_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = prev_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(stc_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for withdrawal sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + } } } } } } -void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { - transfer_deposit_to_primary_wallet(swdo); -} - -void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { - transfer_withdrawal_from_primary_wallet(swwo); -} - -std::string sidechain_net_handler_bitcoin::create_multisignature_wallet(const std::vector public_keys) { - return bitcoin_client->addmultisigaddress(public_keys); -} - -std::string sidechain_net_handler_bitcoin::transfer(const std::string &from, const std::string &to, const uint64_t amount) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::sign_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::send_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet(const std::string &tx_json) { - std::string reply_str = tx_json; - - std::stringstream ss_utx(reply_str); - boost::property_tree::ptree pt; - boost::property_tree::read_json(ss_utx, pt); - - if (!(pt.count("error") && pt.get_child("error").empty()) || !pt.count("result")) { - return ""; - } - - if (!wallet_password.empty()) { - bitcoin_client->walletpassphrase(wallet_password, 60); - } - - std::string unsigned_tx_hex = pt.get("result"); - - reply_str = bitcoin_client->signrawtransactionwithwallet(unsigned_tx_hex); - std::stringstream ss_stx(reply_str); - boost::property_tree::ptree stx_json; - boost::property_tree::read_json(ss_stx, stx_json); - - //if (!wallet_password.empty()) { - // bitcoin_client->walletlock(); - //} - - if (!(stx_json.count("error") && stx_json.get_child("error").empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex")) { - return ""; - } - - std::string signed_tx_hex = stx_json.get("result.hex"); - - bitcoin_client->sendrawtransaction(signed_tx_hex); - - return reply_str; -} - -std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &from_address, const std::string &to_address) { - uint64_t fee_rate = bitcoin_client->estimatesmartfee(); - uint64_t min_fee_rate = 1000; - fee_rate = std::max(fee_rate, min_fee_rate); - - double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now - double total_amount = 0.0; - std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(from_address, 0); - - if (unspent_utxo.size() == 0) { - wlog("Failed to find UTXOs to spend for ${pw}", ("pw", from_address)); - return ""; - } else { - for (const auto &utx : unspent_utxo) { - total_amount += utx.amount_; - } - - if (min_amount >= total_amount) { - wlog("Failed not enough BTC to transfer from ${fa}", ("fa", from_address)); - return ""; - } - } - - fc::flat_map outs; - outs[to_address] = total_amount - min_amount; - - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); -} - -std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { - return ""; + return false; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -787,26 +961,61 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(co deposit_amount -= fee_rate; // Deduct minimum relay fee double transfer_amount = (double)deposit_amount / 100000000.0; - std::vector ins; - fc::flat_map outs; + std::vector inputs; + fc::flat_map outputs; btc_txout utxo; utxo.txid_ = txid; utxo.out_num_ = std::stoul(nvout); - ins.push_back(utxo); + inputs.push_back(utxo); - outs[pw_address] = transfer_amount; + outputs[pw_address] = transfer_amount; - std::string reply_str = bitcoin_client->createrawtransaction(ins, outs); - return sign_and_send_transaction_with_wallet(reply_str); + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + std::vector signers; + for (const son_info &si : active_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swdo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(stc_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; } -std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { - return ""; + return false; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -823,30 +1032,267 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall double min_amount = ((double)(swwo.withdraw_amount.value + fee_rate) / 100000000.0); // Account only for relay fee for now double total_amount = 0.0; - std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); - if (unspent_utxo.size() == 0) { - wlog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); return ""; } else { - for (const auto &utx : unspent_utxo) { + for (const auto &utx : inputs) { total_amount += utx.amount_; } if (min_amount > total_amount) { - wlog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); + elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); return ""; } } - fc::flat_map outs; - outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; + fc::flat_map outputs; + outputs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; if ((total_amount - min_amount) > 0.0) { - outs[pw_address] = total_amount - min_amount; + outputs[pw_address] = total_amount - min_amount; } - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + std::vector signers; + for (const son_info &si : active_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swwo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(stc_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + trx.validate(); + try { + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +std::string sidechain_net_handler_bitcoin::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + + //// Uncomment to get signing in order from sto.signers + //son_id_type invalid_signer = son_id_type(0xFFFFFFFF); + //son_id_type next_signer = invalid_signer; + //for (auto &signer : sto.signers) { + // if (signer.second == false) { + // next_signer = signer.first; + // break; + // } + //} + // + //if ((next_signer == invalid_signer) || (next_signer != plugin.get_current_son_id())) { + // return ""; + //} + + return sign_transaction(sto.transaction, complete); +} + +bool sidechain_net_handler_bitcoin::send_sidechain_transaction(const sidechain_transaction_object &sto) { + return send_transaction(sto.transaction); +} + +// Creates transaction in any format +// Function to actually create transaction should return transaction string, or empty string in case of failure +std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector &inputs, const fc::flat_map outputs) { + std::string new_tx = ""; + //new_tx = create_transaction_raw(inputs, outputs); + new_tx = create_transaction_psbt(inputs, outputs); + //new_tx = create_transaction_standalone(inputs, outputs); + return new_tx; +} + +// Adds signature to transaction +// Function to actually add signature should return transaction with added signature string, or empty string in case of failure +std::string sidechain_net_handler_bitcoin::sign_transaction(const std::string &tx_str, bool &complete) { + std::string new_tx = ""; + //new_tx = sign_transaction_raw(tx, complete); + new_tx = sign_transaction_psbt(tx_str, complete); + //new_tx = sign_transaction_standalone(tx, complete); + return new_tx; +} + +bool sidechain_net_handler_bitcoin::send_transaction(const std::string &tx_str) { + return bitcoin_client->sendrawtransaction(tx_str); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_raw(const std::vector &inputs, const fc::flat_map outputs) { + return bitcoin_client->createrawtransaction(inputs, outputs); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_psbt(const std::vector &inputs, const fc::flat_map outputs) { + return bitcoin_client->createpsbt(inputs, outputs); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const std::vector &inputs, const fc::flat_map outputs) { + // Examples + + // Transaction with no inputs and outputs + //bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" createrawtransaction '[]' '[]' + //02000000000000000000 + //bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" decoderawtransaction 02000000000000000000 + //{ + // "txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + // "hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + // "version": 2, + // "size": 10, + // "vsize": 10, + // "weight": 40, + // "locktime": 0, + // "vin": [ + // ], + // "vout": [ + // ] + //} + + // Transaction with input and output + //{ + // "txid": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b", + // "hash": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b", + // "version": 2, + // "size": 83, + // "vsize": 83, + // "weight": 332, + // "locktime": 0, + // "vin": [ + // { + // "txid": "3d322dc2640239a2e68e182b254d19c88e5172a61947f94a105c3f57618092ff", + // "vout": 0, + // "scriptSig": { + // "asm": "", + // "hex": "" + // }, + // "sequence": 4294967295 + // } + // ], + // "vout": [ + // { + // "value": 1.00000000, + // "n": 0, + // "scriptPubKey": { + // "asm": "OP_HASH160 b87c323018cae236eb03a1f63000c85b672270f6 OP_EQUAL", + // "hex": "a914b87c323018cae236eb03a1f63000c85b672270f687", + // "reqSigs": 1, + // "type": "scripthash", + // "addresses": [ + // "2NA4h6sc9oZ4ogfNKU9Wp6fkqPZLZPqqpgf" + // ] + // } + // } + // ] + //} + + return ""; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_raw(const std::string &tx_str, bool &complete) { + if (tx_str.empty()) { + elog("Signing failed, tx string is empty"); + return ""; + } + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string reply_str = bitcoin_client->signrawtransactionwithwallet(tx_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to process raw transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_raw = json_res.get("hex"); + bool complete_raw = json_res.get("complete"); + + if (complete_raw) { + complete = true; + return new_tx_raw; + } + return new_tx_raw; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_psbt(const std::string &tx_str, bool &complete) { + if (tx_str.empty()) { + elog("Signing failed, tx string is empty"); + return ""; + } + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string reply_str = bitcoin_client->walletprocesspsbt(tx_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("psbt") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to process psbt transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_psbt = json_res.get("psbt"); + bool complete_psbt = json_res.get("complete"); + + if (complete_psbt) { + std::string reply_str = bitcoin_client->finalizepsbt(new_tx_psbt); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to finalize psbt transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_raw = json_res.get("hex"); + bool complete_raw = json_res.get("complete"); + + if (complete_raw) { + complete = true; + return new_tx_raw; + } + } + return new_tx_psbt; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_standalone(const std::string &tx_str, bool &complete) { + complete = true; + return ""; } void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { @@ -867,7 +1313,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) std::string sidechain_uid = ss.str(); sidechain_event_data sed; - sed.timestamp = plugin.database().head_block_time(); + sed.timestamp = database.head_block_time(); sed.sidechain = addr_itr->sidechain; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = v.out.hash_tx; @@ -916,6 +1362,55 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block return result; } +void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_changed_objects_processing = 5000; + + fc::time_point next_wakeup(now + fc::microseconds(time_to_next_changed_objects_processing)); + + on_changed_objects_task = fc::schedule([this, ids, accounts] { + on_changed_objects_cb(ids, accounts); + }, + next_wakeup, "SON Processing"); +} + +void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector &ids, const flat_set &accounts) { + for (auto id : ids) { + if (id.is()) { + const auto &swi = database.get_index_type().indices().get(); + auto swo = swi.find(id); + if (swo != swi.end()) { + std::stringstream pw_ss(swo->addresses.at(sidechain)); + boost::property_tree::ptree pw_pt; + boost::property_tree::read_json(pw_ss, pw_pt); + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string pw_address = ""; + if (pw_pt.count("address")) { + pw_address = pw_pt.get("address"); + bitcoin_client->importaddress(pw_address); + } + + std::string pw_redeem_script = ""; + if (pw_pt.count("redeemScript")) { + pw_redeem_script = pw_pt.get("redeemScript"); + bitcoin_client->importaddress(pw_redeem_script); + } + + vector son_pubkeys_bitcoin; + for (const son_info &si : swo->sons) { + son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + } + uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1; + bitcoin_client->addmultisigaddress(nrequired, son_pubkeys_bitcoin); + } + } + } +} + // ============================================================================= }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 18f60b7a..8dd39b22 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -22,7 +21,7 @@ namespace graphene { namespace peerplays_sidechain { sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::peerplays; - plugin.database().applied_block.connect([&](const signed_block &b) { + database.applied_block.connect([&](const signed_block &b) { on_applied_block(b); }); } @@ -31,28 +30,24 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { } void sidechain_net_handler_peerplays::recreate_primary_wallet() { + return; } -void sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { + return true; } -void sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { + return true; } -std::string sidechain_net_handler_peerplays::create_multisignature_wallet(const std::vector public_keys) { - return ""; +std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + complete = true; + return sto.transaction; } -std::string sidechain_net_handler_peerplays::transfer(const std::string &from, const std::string &to, const uint64_t amount) { - return ""; -} - -std::string sidechain_net_handler_peerplays::sign_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_peerplays::send_transaction(const std::string &transaction) { - return ""; +bool sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) { + return true; } void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { @@ -72,18 +67,18 @@ void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { std::string sidechain_uid = ss.str(); sidechain_event_data sed; - sed.timestamp = plugin.database().head_block_time(); + sed.timestamp = database.head_block_time(); sed.sidechain = sidechain_type::peerplays; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); - sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(plugin.database()).symbol; + sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(database).symbol; sed.sidechain_amount = transfer_op.amount.amount; sed.peerplays_from = transfer_op.from; sed.peerplays_to = transfer_op.to; // We should calculate exchange rate between CORE/TEST and other Peerplays asset - sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(plugin.database()).options.core_exchange_rate.quote.amount); + sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(database).options.core_exchange_rate.quote.amount); sidechain_event_data_received(sed); } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index 05b6b5b8..826e5d91 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -15,7 +15,7 @@ sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin sidechain_net_manager::~sidechain_net_manager() { } -bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map &options) { +bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) { bool ret_val = false; @@ -57,4 +57,16 @@ void sidechain_net_manager::process_withdrawals() { } } +void sidechain_net_manager::process_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_sidechain_transactions(); + } +} + +void sidechain_net_manager::send_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->send_sidechain_transactions(); + } +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index aca8c04f..ea2434f6 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -73,7 +73,7 @@ #include #include -#include +#include #include #include diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index bee7cec8..e65cf11e 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using namespace graphene::chain; using namespace graphene::chain::test; diff --git a/tests/tests/sidechain_transaction_tests.cpp b/tests/tests/sidechain_transaction_tests.cpp deleted file mode 100644 index d1132605..00000000 --- a/tests/tests/sidechain_transaction_tests.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include - -#include "../common/database_fixture.hpp" - -#include -#include -#include -#include -#include - -using namespace graphene; -using namespace graphene::chain; -using namespace graphene::chain::test; - -BOOST_FIXTURE_TEST_SUITE(sidechain_transaction_tests, database_fixture) - -BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) -{ - - try - { - BOOST_TEST_MESSAGE("bitcoin_transaction_send_test"); - - generate_blocks(HARDFORK_SON_TIME); - generate_block(); - set_expiration(db, trx); - - ACTORS((alice)(bob)); - - upgrade_to_lifetime_member(alice); - upgrade_to_lifetime_member(bob); - - transfer(committee_account, alice_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION)); - transfer(committee_account, bob_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION)); - - generate_block(); - set_expiration(db, trx); - - std::string test_url = "https://create_son_test"; - - // create deposit vesting - vesting_balance_id_type deposit_alice; - { - vesting_balance_create_operation op; - op.creator = alice_id; - op.owner = alice_id; - op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::son; - op.policy = dormant_vesting_policy_initializer{}; - trx.operations.push_back(op); - sign(trx, alice_private_key); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - deposit_alice = ptx.operation_results[0].get(); - } - generate_block(); - set_expiration(db, trx); - - // create payment normal vesting - vesting_balance_id_type payment_alice; - { - vesting_balance_create_operation op; - op.creator = alice_id; - op.owner = alice_id; - op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::normal; - trx.operations.push_back(op); - sign(trx, alice_private_key); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - payment_alice = ptx.operation_results[0].get(); - } - - generate_block(); - set_expiration(db, trx); - - // alice becomes son - { - flat_map sidechain_public_keys; - sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; - - son_create_operation op; - op.owner_account = alice_id; - op.url = test_url; - op.deposit = deposit_alice; - op.pay_vb = payment_alice; - op.signing_key = alice_public_key; - op.sidechain_public_keys = sidechain_public_keys; - trx.operations.push_back(op); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - set_expiration(db, trx); - - // create deposit vesting - vesting_balance_id_type deposit_bob; - { - vesting_balance_create_operation op; - op.creator = bob_id; - op.owner = bob_id; - op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::son; - op.policy = dormant_vesting_policy_initializer{}; - trx.operations.push_back(op); - sign(trx, bob_private_key); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - deposit_bob = ptx.operation_results[0].get(); - } - generate_block(); - set_expiration(db, trx); - - // create payment normal vesting - vesting_balance_id_type payment_bob; - { - vesting_balance_create_operation op; - op.creator = bob_id; - op.owner = bob_id; - op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); - op.balance_type = vesting_balance_type::normal; - trx.operations.push_back(op); - sign(trx, bob_private_key); - processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); - payment_bob = ptx.operation_results[0].get(); - } - generate_block(); - set_expiration(db, trx); - - // bob becomes son - { - flat_map sidechain_public_keys; - sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; - - son_create_operation op; - op.owner_account = bob_id; - op.url = test_url; - op.deposit = deposit_bob; - op.pay_vb = payment_bob; - op.signing_key = bob_public_key; - op.sidechain_public_keys = sidechain_public_keys; - trx.operations.push_back(op); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - - generate_block(); - - const auto& acc_idx = db.get_index_type().indices().get(); - auto acc_itr = acc_idx.find(db.get_global_properties().parameters.son_account()); - BOOST_REQUIRE(acc_itr != acc_idx.end()); - db.modify(*acc_itr, [&](account_object &obj) { - obj.active.account_auths.clear(); - obj.active.add_authority(bob_id, 1); - obj.active.add_authority(alice_id, 1); - obj.active.weight_threshold = 2; - obj.owner.account_auths.clear(); - obj.owner.add_authority(bob_id, 1); - obj.owner.add_authority(alice_id, 1); - obj.owner.weight_threshold = 2; - }); - - /*const auto &son_btc_account = db.create([&](account_object &obj) { - obj.name = "son_btc_account"; - obj.statistics = db.create([&](account_statistics_object &acc_stat) { acc_stat.owner = obj.id; }).id; - obj.membership_expiration_date = time_point_sec::maximum(); - obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - - obj.owner.add_authority(bob_id, 1); - obj.active.add_authority(bob_id, 1); - obj.owner.add_authority(alice_id, 1); - obj.active.add_authority(alice_id, 1); - obj.active.weight_threshold = 2; - obj.owner.weight_threshold = 2; - }); - - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - _gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); - if( _gpo.pending_parameters ) - _gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); - });*/ - - generate_block(); - - const global_property_object &gpo = db.get_global_properties(); - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_send_operation"); - - bitcoin_transaction_send_operation send_op; - - send_op.payer = gpo.parameters.son_account(); - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = alice_id; - proposal_op.proposed_ops.push_back(op_wrapper(send_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); - - trx.operations.push_back(proposal_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - - BOOST_TEST_MESSAGE("Check proposal results"); - - const auto &idx = db.get_index_type().indices().get(); - BOOST_REQUIRE(idx.size() == 1); - auto obj = idx.find(proposal_id_type(0)); - BOOST_REQUIRE(obj != idx.end()); - - const auto& btidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(btidx.size() == 0); - - std::vector a1 = {'a', 'l', 'i', 'c', 'e', '1'}; - std::vector a2 = {'a', 'l', 'i', 'c', 'e', '2'}; - std::vector a3 = {'a', 'l', 'i', 'c', 'e', '3'}; - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); - - bitcoin_transaction_sign_operation sign_op; - - sign_op.payer = alice_id; - sign_op.proposal_id = proposal_id_type(0); - sign_op.signatures.push_back(a1); - sign_op.signatures.push_back(a2); - sign_op.signatures.push_back(a3); - - trx.operations.push_back(sign_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - - BOOST_REQUIRE(idx.size() == 1); - BOOST_REQUIRE(btidx.size() == 0); - auto pobj = idx.find(proposal_id_type(0)); - BOOST_REQUIRE(pobj != idx.end()); - - const auto& sidx = db.get_index_type().indices().get(); - const auto son_obj1 = sidx.find( alice_id ); - BOOST_REQUIRE(son_obj1 != sidx.end()); - - auto bitcoin_transaction_send_op = pobj->proposed_transaction.operations[0].get(); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures.size() == 1); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][0] == a1); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][1] == a2); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][2] == a3); - - std::vector b1 = {'b', 'o', 'b', '1'}; - std::vector b2 = {'b', 'o', 'b', '2'}; - std::vector b3 = {'b', 'o', 'b', '3'}; - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); - - bitcoin_transaction_sign_operation sign_op; - - sign_op.payer = bob_id; - sign_op.proposal_id = proposal_id_type(0); - sign_op.signatures.push_back(b1); - sign_op.signatures.push_back(b2); - sign_op.signatures.push_back(b3); - - trx.operations.push_back(sign_op); - set_expiration(db, trx); - sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - - BOOST_REQUIRE(idx.size() == 0); - - const auto son_obj2 = sidx.find( bob_id ); - BOOST_REQUIRE(son_obj2 != sidx.end()); - - BOOST_REQUIRE(btidx.size() == 1); - - const auto btobj = btidx.find(bitcoin_transaction_id_type(0)); - BOOST_REQUIRE(btobj != btidx.end()); - - BOOST_REQUIRE(btobj->processed == false); - - auto stats1 = son_obj1->statistics( db ); - auto stats2 = son_obj2->statistics( db ); - - BOOST_REQUIRE(stats1.txs_signed == 1); - BOOST_REQUIRE(stats2.txs_signed == 1); - - auto sigs = btobj->signatures; - - BOOST_REQUIRE(sigs[son_obj1->id][0] == a1); - BOOST_REQUIRE(sigs[son_obj1->id][1] == a2); - BOOST_REQUIRE(sigs[son_obj1->id][2] == a3); - - BOOST_REQUIRE(sigs[son_obj2->id][0] == b1); - BOOST_REQUIRE(sigs[son_obj2->id][1] == b2); - BOOST_REQUIRE(sigs[son_obj2->id][2] == b3); - - { - BOOST_TEST_MESSAGE("Send bitcoin_send_transaction_process_operation"); - - bitcoin_send_transaction_process_operation process_op; - process_op.bitcoin_transaction_id = bitcoin_transaction_id_type(0); - process_op.payer = db.get_global_properties().parameters.son_account(); - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = alice_id; - proposal_op.proposed_ops.push_back(op_wrapper(process_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); - - trx.operations.push_back(proposal_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - BOOST_REQUIRE(idx.size() == 1); - obj = idx.find(proposal_id_type(1)); - BOOST_REQUIRE(obj != idx.end()); - - - { - BOOST_TEST_MESSAGE("Send proposal_update_operation"); - - proposal_update_operation puo; - puo.fee_paying_account = bob_id; - puo.proposal = obj->id; - puo.active_approvals_to_add = { bob_id }; - - trx.operations.push_back(puo); - set_expiration(db, trx); - sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - BOOST_REQUIRE(idx.size() == 1); - obj = idx.find(proposal_id_type(1)); - BOOST_REQUIRE(obj != idx.end()); - - BOOST_REQUIRE(btobj != btidx.end()); - BOOST_REQUIRE(btobj->processed == false); - - { - BOOST_TEST_MESSAGE("Send proposal_update_operation"); - - proposal_update_operation puo; - puo.fee_paying_account = alice_id; - puo.proposal = obj->id; - puo.active_approvals_to_add = { alice_id }; - - trx.operations.push_back(puo); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - BOOST_REQUIRE(idx.size() == 0); - - BOOST_REQUIRE(btobj != btidx.end()); - BOOST_REQUIRE(btobj->processed == true); - } - FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index 39e53269..7ef3abc0 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using namespace graphene; using namespace graphene::chain;