From 2f7bd63d446e292217057c4bd740dc3846eaa367 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Mon, 15 Jun 2015 15:33:53 -0500 Subject: [PATCH 001/354] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f4e8fbd3..e15ad7fe 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,11 @@ This is a quick introduction to get new developers up to speed on Graphene. Starting Graphene ----------------- +For Ubuntu 14.04 LTS users, see this link first: + https://github.com/cryptonomex/graphene/wiki/build-ubuntu + +and then proceed with: + git clone https://github.com/cryptonomex/graphene.git cd graphene git submodule update --init --recursive From 81c6338dfe93a86b49338ef78e95d51ee591bd08 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 12 Jun 2015 11:15:11 -0400 Subject: [PATCH 002/354] Begin test for cashback rewards --- .../include/graphene/chain/operations.hpp | 2 +- libraries/chain/operations.cpp | 3 +- tests/common/database_fixture.hpp | 6 +- tests/tests/fee_tests.cpp | 83 +++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 tests/tests/fee_tests.cpp diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index b670be1d..7be5368a 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -132,7 +132,7 @@ namespace graphene { namespace chain { account_id_type referrer; /// Of the fee split between registrar and referrer, this percentage goes to the referrer. The rest goes to the /// registrar. - uint8_t referrer_percent = 0; + uint16_t referrer_percent = 0; string name; authority owner; diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index c08027e2..5e88efed 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -201,8 +201,7 @@ void account_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( is_valid_name( name ) ); - FC_ASSERT( referrer_percent >= 0 ); - FC_ASSERT( referrer_percent <= 100 ); + FC_ASSERT( referrer_percent <= GRAPHENE_100_PERCENT ); FC_ASSERT( !owner.auths.empty() ); auto pos = name.find( '/' ); if( pos != string::npos ) diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 66b2ceeb..a841aaf1 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -67,9 +67,11 @@ using namespace graphene::db; #define PUSH_TX( tx, skip_flags ) \ _push_transaction( tx, skip_flags, __FILE__, __LINE__ ) -#define ACTOR(name) \ +#define PREP_ACTOR(name) \ fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ - key_id_type name ## _key_id = register_key(name ## _private_key.get_public_key()).get_id(); \ + key_id_type name ## _key_id = register_key(name ## _private_key.get_public_key()).get_id(); +#define ACTOR(name) \ + PREP_ACTOR(name) \ account_id_type name ## _id = create_account(BOOST_PP_STRINGIZE(name), name ## _key_id).id; #define GET_ACTOR(name) \ fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp new file mode 100644 index 00000000..03816ecb --- /dev/null +++ b/tests/tests/fee_tests.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; + +BOOST_FIXTURE_TEST_SUITE( fee_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( cashback_test ) +{ try { + /* Account Structure used in this test * + * * + * /-----------------\ /-------------------\ * + * | life (Lifetime) | | reggie (Lifetime) | * + * \-----------------/ \-------------------/ * + * | Refers & | Refers | Registers | Registers * + * v Registers v v | * + * /----------------\ /----------------\ | * + * | ann (Annual) | | dumy (basic) | | * + * \----------------/ \----------------/ |-------------. * + * | Refers & L--------------------------------. | | * + * v Registers Refers v v | * + * /----------------\ /----------------\ | * + * | scud (basic) | | stud (basic) | | * + * \----------------/ | (Upgrades to | | * + * | Lifetime) | v * + * \----------------/ /--------------\ * + * L------->| pleb (Basic) | * + * \--------------/ * + * * + */ + ACTOR(life); + ACTOR(reggie); + PREP_ACTOR(ann); + PREP_ACTOR(scud); + PREP_ACTOR(dumy); + PREP_ACTOR(stud); + PREP_ACTOR(pleb); + transfer(account_id_type(), life_id, asset(100000000)); + transfer(account_id_type(), reggie_id, asset(100000000)); + upgrade_to_lifetime_member(life_id); + upgrade_to_lifetime_member(reggie_id); + enable_fees(10000); + const auto& fees = db.get_global_properties().parameters.current_fees; + +#define CustomRegisterActor(actor_name, registrar_name, referrer_name, referrer_rate) \ + { \ + account_create_operation op; \ + op.registrar = registrar_name ## _id; \ + op.referrer = referrer_name ## _id; \ + op.referrer_percent = referrer_rate*GRAPHENE_1_PERCENT; \ + op.name = BOOST_PP_STRINGIZE(actor_name); \ + op.memo_key = actor_name ## _key_id; \ + op.active = authority(1, actor_name ## _key_id, 1); \ + op.owner = op.active; \ + op.fee = op.calculate_fee(fees); \ + trx.operations = {op}; \ + trx.sign(registrar_name ## _key_id, registrar_name ## _private_key); \ + db.push_transaction(trx); \ + } + + CustomRegisterActor(ann, life, life, 75); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_SUITE_END() From ce4846e81b4848f680f4924bb488ff247355dd30 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Mon, 15 Jun 2015 17:28:20 -0400 Subject: [PATCH 003/354] Progress #31: Add some testing --- libraries/chain/account_evaluator.cpp | 2 + libraries/chain/account_object.cpp | 75 +++++++++++++++++++ libraries/chain/db_maint.cpp | 71 +----------------- .../include/graphene/chain/account_object.hpp | 13 +++- .../graphene/chain/vesting_balance_object.hpp | 3 +- tests/common/database_fixture.cpp | 31 +++++++- tests/common/database_fixture.hpp | 6 +- tests/tests/fee_tests.cpp | 36 +++++++-- 8 files changed, 152 insertions(+), 85 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 0e9aed33..c8ef51f8 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -224,6 +224,7 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: if( o.upgrade_to_lifetime_member ) { // Upgrade to lifetime member. I don't care what the account was before. + a.statistics(d).process_fees(a, d); a.membership_expiration_date = time_point_sec::maximum(); a.referrer = a.registrar = a.lifetime_referrer = a.get_id(); a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage; @@ -234,6 +235,7 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: a.membership_expiration_date += fc::days(365); } else { // Upgrade from basic account. + a.statistics(d).process_fees(a, d); assert(a.is_basic_account(d.head_block_time())); a.membership_expiration_date = d.head_block_time() + fc::days(365); } diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 59ba30aa..512974e6 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -17,10 +17,24 @@ */ #include #include +#include #include namespace graphene { namespace chain { +share_type cut_fee(share_type a, uint16_t p) +{ + if( a == 0 || p == 0 ) + return 0; + if( p == GRAPHENE_100_PERCENT ) + return a; + + fc::uint128 r(a.value); + r *= p; + r /= GRAPHENE_100_PERCENT; + return r.to_uint64(); +} + bool account_object::is_authorized_asset(const asset_object& asset_obj) const { for( const auto id : blacklisting_accounts ) if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) return false; @@ -56,4 +70,65 @@ uint16_t account_statistics_object::calculate_bulk_discount_percent(const chain_ return bulk_discount_percent; } +void account_statistics_object::process_fees(const account_object& a, database& d) const +{ + if( pending_fees > 0 || pending_vested_fees > 0 ) + { + const auto& props = d.get_global_properties(); + + auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting) + { + share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); + assert( network_cut <= core_fee_total ); + share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); + share_type accumulated = network_cut - burned; + assert( accumulated + burned == network_cut ); + 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(dynamic_asset_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. + // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to + // it directly. + share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage); + share_type registrar_cut = referral - referrer_cut; + + d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting); + d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting); + d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting); + + assert( referrer_cut + registrar_cut + accumulated + burned + lifetime_cut == core_fee_total ); + }; + + share_type vesting_fee_subtotal(pending_fees); + share_type vested_fee_subtotal(pending_vested_fees); + share_type vesting_cashback, vested_cashback; + + if( lifetime_fees_paid > props.parameters.bulk_discount_threshold_min && + a.is_member(d.head_block_time()) ) + { + auto bulk_discount_rate = calculate_bulk_discount_percent(props.parameters); + vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate); + vesting_fee_subtotal -= vesting_cashback; + + vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate); + vested_fee_subtotal -= vested_cashback; + } + + pay_out_fees(a, vesting_fee_subtotal, true); + d.deposit_cashback(a, vesting_cashback, true); + pay_out_fees(a, vested_fee_subtotal, false); + d.deposit_cashback(a, vested_cashback, false); + + d.modify(*this, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) { + s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal; + s.pending_fees = 0; + s.pending_vested_fees = 0; + }); + } +} + } } // graphene::chain diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 0db0b6f6..23f16537 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -357,77 +357,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g process_fees_helper(database& d, const global_property_object& gpo) : d(d), props(gpo) {} - share_type cut_fee(share_type a, uint16_t p)const - { - if( a == 0 || p == 0 ) - return 0; - if( p == GRAPHENE_100_PERCENT ) - return a; - - fc::uint128 r(a.value); - r *= p; - r /= GRAPHENE_100_PERCENT; - return r.to_uint64(); - } - - void pay_out_fees(const account_object& account, share_type core_fee_total, bool require_vesting) - { - share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); - assert( network_cut <= core_fee_total ); - share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); - share_type accumulated = network_cut - burned; - assert( accumulated + burned == network_cut ); - 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(dynamic_asset_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. - // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to - // it directly. - share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage); - share_type registrar_cut = referral - referrer_cut; - - d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting); - d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting); - d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting); - - assert( referrer_cut + registrar_cut + accumulated + burned + lifetime_cut == core_fee_total ); - } - void operator()(const account_object& a) { - const account_statistics_object& stats = a.statistics(d); - - if( stats.pending_fees > 0 ) - { - share_type vesting_fee_subtotal(stats.pending_fees); - share_type vested_fee_subtotal(stats.pending_vested_fees); - share_type vesting_cashback, vested_cashback; - - if( stats.lifetime_fees_paid > props.parameters.bulk_discount_threshold_min && - a.is_member(d.head_block_time()) ) - { - auto bulk_discount_rate = stats.calculate_bulk_discount_percent(props.parameters); - vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate); - vesting_fee_subtotal -= vesting_cashback; - - vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate); - vested_fee_subtotal -= vested_cashback; - } - - pay_out_fees(a, vesting_fee_subtotal, true); - d.deposit_cashback(a, vesting_cashback, true); - pay_out_fees(a, vested_fee_subtotal, false); - d.deposit_cashback(a, vested_cashback, false); - - d.modify(stats, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) { - s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal; - s.pending_fees = 0; - s.pending_vested_fees = 0; - }); - } + a.statistics(d).process_fees(a, d); } } fee_helper(*this, gpo); diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index c41a15fc..0a3497d6 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -22,6 +22,7 @@ #include namespace graphene { namespace chain { +class database; /** * @class account_statistics_object @@ -64,8 +65,8 @@ namespace graphene { namespace chain { * them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid * doing massive amounts of uint128 arithmetic on each and every operation. * - *These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance - *interval. + * These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance + * interval. */ share_type pending_fees; /** @@ -76,6 +77,8 @@ namespace graphene { namespace chain { /// @brief Calculate the percentage discount this user receives on his fees uint16_t calculate_bulk_discount_percent(const chain_parameters& params)const; + /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees + void process_fees(const account_object& a, database& d) const; }; /** @@ -189,6 +192,12 @@ namespace graphene { namespace chain { * Vesting balance which receives cashback_reward deposits. */ optional cashback_vb; + template + const vesting_balance_object& cashback_balance(const DB& db)const + { + FC_ASSERT(cashback_vb); + return db.get(*cashback_vb); + } /// @return true if this is a lifetime member account; false otherwise. bool is_lifetime_member()const diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index c0e32347..3d6ff6fb 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -98,8 +98,7 @@ namespace graphene { namespace chain { > vesting_policy; /** - * Timelocked balance object is a balance that is locked by the - * blockchain for a period of time. + * Vesting balance object is a balance that is locked by the blockchain for a period of time. */ class vesting_balance_object : public abstract_object { diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 27d57445..61a5c8cf 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -270,11 +270,17 @@ void database_fixture::generate_blocks( uint32_t block_count ) generate_block(); } -void database_fixture::generate_blocks( fc::time_point_sec timestamp ) +void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks) { + if( miss_intermediate_blocks ) + { + auto slots_to_miss = db.get_slot_at_time(timestamp) - 1; + assert(slots_to_miss > 0); + generate_block(~0, generate_private_key("genesis"), slots_to_miss); + return; + } while( db.head_block_time() < timestamp ) generate_block(); - return; } account_create_operation database_fixture::make_account( @@ -683,14 +689,33 @@ void database_fixture::upgrade_to_lifetime_member( const account_object& account account_upgrade_operation op; op.account_to_upgrade = account.get_id(); op.upgrade_to_lifetime_member = true; + op.fee = op.calculate_fee(db.get_global_properties().parameters.current_fees); trx.operations = {op}; - db.push_transaction( trx, ~0 ); + db.push_transaction(trx, ~0); FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() ); trx.clear(); } FC_CAPTURE_AND_RETHROW((account)) } +void database_fixture::upgrade_to_annual_member(account_id_type account) +{ + upgrade_to_annual_member(account(db)); +} + +void database_fixture::upgrade_to_annual_member(const account_object& account) +{ + try { + account_upgrade_operation op; + op.account_to_upgrade = account.get_id(); + op.fee = op.calculate_fee(db.get_global_properties().parameters.current_fees); + trx.operations = {op}; + db.push_transaction(trx, ~0); + FC_ASSERT( op.account_to_upgrade(db).is_member(db.head_block_time()) ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW((account)) +} + void database_fixture::print_market( const string& syma, const string& symb )const { const auto& limit_idx = db.get_index_type(); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index a841aaf1..7010a29f 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -119,13 +119,13 @@ struct database_fixture { * @brief Generates block_count blocks * @param block_count number of blocks to generate */ - void generate_blocks( uint32_t block_count ); + void generate_blocks(uint32_t block_count); /** * @brief Generates blocks until the head block time matches or exceeds timestamp * @param timestamp target time to generate blocks until */ - void generate_blocks( fc::time_point_sec timestamp ); + void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = false); account_create_operation make_account( const std::string& name = "nathan", @@ -206,6 +206,8 @@ struct database_fixture { void enable_fees( share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION ); void upgrade_to_lifetime_member( account_id_type account ); void upgrade_to_lifetime_member( const account_object& account ); + void upgrade_to_annual_member( account_id_type account ); + void upgrade_to_annual_member( const account_object& account ); void print_market( const string& syma, const string& symb )const; string pretty( const asset& a )const; void print_short_order( const short_order_object& cur )const; diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 03816ecb..e72ca4d0 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -16,6 +16,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include + #include #include "../common/database_fixture.hpp" @@ -36,15 +38,15 @@ BOOST_AUTO_TEST_CASE( cashback_test ) * /----------------\ /----------------\ | * * | ann (Annual) | | dumy (basic) | | * * \----------------/ \----------------/ |-------------. * - * | Refers & L--------------------------------. | | * - * v Registers Refers v v | * + * | Refers L--------------------------------. | | * + * v Refers v v | * * /----------------\ /----------------\ | * - * | scud (basic) | | stud (basic) | | * - * \----------------/ | (Upgrades to | | * + * | scud (basic) |<------------------------| stud (basic) | | * + * \----------------/ Registers | (Upgrades to | | * * | Lifetime) | v * * \----------------/ /--------------\ * * L------->| pleb (Basic) | * - * \--------------/ * + * Refers \--------------/ * * * */ ACTOR(life); @@ -62,6 +64,7 @@ BOOST_AUTO_TEST_CASE( cashback_test ) const auto& fees = db.get_global_properties().parameters.current_fees; #define CustomRegisterActor(actor_name, registrar_name, referrer_name, referrer_rate) \ + account_id_type actor_name ## _id; \ { \ account_create_operation op; \ op.registrar = registrar_name ## _id; \ @@ -74,10 +77,31 @@ BOOST_AUTO_TEST_CASE( cashback_test ) op.fee = op.calculate_fee(fees); \ trx.operations = {op}; \ trx.sign(registrar_name ## _key_id, registrar_name ## _private_key); \ - db.push_transaction(trx); \ + actor_name ## _id = db.push_transaction(trx).operation_results.front().get(); \ + trx.clear(); \ } CustomRegisterActor(ann, life, life, 75); + + transfer(life_id, ann_id, asset(1000000)); + upgrade_to_annual_member(ann_id); + + CustomRegisterActor(dumy, reggie, life, 75); + CustomRegisterActor(stud, reggie, ann, 80); + + transfer(life_id, stud_id, asset(1000000)); + upgrade_to_lifetime_member(stud_id); + + CustomRegisterActor(pleb, reggie, stud, 95); + CustomRegisterActor(scud, stud, ann, 80); + + generate_block(); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true); + + BOOST_CHECK_EQUAL(life_id(db).cashback_balance(db).balance.amount.value, 78000); + BOOST_CHECK_EQUAL(reggie_id(db).cashback_balance(db).balance.amount.value, 34000); + BOOST_CHECK_EQUAL(ann_id(db).cashback_balance(db).balance.amount.value, 40000); + BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 8000); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From e8bbdc08f65dbdaae4d294ba1247ce575190c509 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 16:41:52 -0400 Subject: [PATCH 004/354] Update docs submodule --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index 1b53a8ec..71ed2984 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 1b53a8eca77783d073ce7cc95991447c3f34b927 +Subproject commit 71ed2984b71d57cab13cdf12074cff150edc1d3d From 9dbf78c891f32e5f0f9728106a783c2171e19d20 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 09:52:33 -0400 Subject: [PATCH 005/354] start refactoring short --- .../chain/include/graphene/chain/asset.hpp | 25 +- .../include/graphene/chain/operations.hpp | 92 +----- .../graphene/chain/short_order_evaluator.hpp | 27 +- .../graphene/chain/short_order_object.hpp | 66 ----- .../chain/include/graphene/chain/types.hpp | 9 +- libraries/chain/operations.cpp | 38 +-- libraries/chain/short_order_evaluator.cpp | 276 +++++------------- 7 files changed, 84 insertions(+), 449 deletions(-) diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index bee277ec..aac46ed1 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -121,7 +121,7 @@ namespace graphene { namespace chain { /** * @class price_feed - * @brief defines market parameters for shorts and margin positions + * @brief defines market parameters for margin positions */ struct price_feed { @@ -131,10 +131,6 @@ namespace graphene { namespace chain { * unreasonable prices. */ price call_limit; - /** - * Short orders will only be matched against bids above this price. - */ - price short_limit; /** * Forced settlements will evaluate using this price. */ @@ -144,20 +140,6 @@ namespace graphene { namespace chain { */ uint32_t max_margin_period_sec = GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC; - /** - * Required maintenance collateral is defined - * as a fixed point number with a maximum value of 10.000 - * and a minimum value of 1.000. - * - * This value must be greater than required_maintenance_collateral or - * a margin call would be triggered immediately. - * - * Default requirement is $2 of collateral per $1 of debt based - * upon the premise that both parties to every trade should bring - * equal value to the table. - */ - uint16_t required_initial_collateral = GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO; - /** * Required maintenance collateral is defined * as a fixed point number with a maximum value of 10.000 @@ -191,6 +173,7 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::asset, (amount)(asset_id) ) FC_REFLECT( graphene::chain::price, (base)(quote) ) -#define GRAPHENE_PRICE_FEED_FIELDS (call_limit)(short_limit)(settlement_price)(max_margin_period_sec)\ - (required_initial_collateral)(required_maintenance_collateral) + +#define GRAPHENE_PRICE_FEED_FIELDS (call_limit)(settlement_price)(max_margin_period_sec)(required_maintenance_collateral) + FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index b670be1d..ba876c9a 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -827,89 +827,6 @@ namespace graphene { namespace chain { } }; - /** - * @ingroup operations - * - * Define a new short order, if it is filled it will - * be merged with existing call orders for the same - * account. If maintenance_collateral_ratio is set - * it will update any existing open call orders to - * use the new maintenance level. - * - * When shorting you specify the total amount to sell - * and the amount of collateral along with the initial - * ratio. The price it will sell at is (amount_to_sell/(collateral*initial_collateral_ratio/2000)) - */ - struct short_order_create_operation - { - /// The account placing a short order (this account must sign the transaction) - account_id_type seller; - /// The amount of market-issued asset to short sell - asset amount_to_sell; - /// The fee paid by seller - asset fee; - /// The amount of collateral to withdraw from the seller - asset collateral; - /// Fixed point representation of initial collateral ratio, with three digits of precision - /// Must be greater than or equal to the minimum specified by price feed - uint16_t initial_collateral_ratio = GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO; - /// Fixed point representation of maintenance collateral ratio, with three digits of precision - /// Must be greater than or equal to the minimum specified by price feed - uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO; - /// Expiration time for this order. Any unfilled portion of this order which is on the books at or past this time - /// will automatically be canceled. - time_point_sec expiration = time_point_sec::maximum(); - - account_id_type fee_payer()const { return seller; } - void get_required_auth(flat_set& active_auth_set, flat_set&)const; - void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - - pair get_market()const - { - return amount_to_sell.asset_id < collateral.asset_id ? - std::make_pair( amount_to_sell.asset_id, collateral.asset_id ) : - std::make_pair( collateral.asset_id, amount_to_sell.asset_id ); - } - - /** convention: amount_to_sell / amount_to_receive */ - price sell_price()const { return ~price::call_price(amount_to_sell, collateral, initial_collateral_ratio); } - - /** convention: amount_to_sell / amount_to_receive means we are - * selling collateral to receive debt - **/ - price call_price() const { return price::call_price(amount_to_sell, collateral, maintenance_collateral_ratio); } - - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( seller, -collateral ); - } - }; - - /** - * @ingroup operations - * Cancel the short order and return the balance to the - * order->seller account. - */ - struct short_order_cancel_operation - { - short_order_id_type order; - account_id_type fee_paying_account; ///< Must be order->seller - asset fee; ///< paid by order->seller - - account_id_type fee_payer()const { return fee_paying_account; } - void get_required_auth(flat_set& active_auth_set, flat_set&)const; - void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - - void get_balance_delta( balance_accumulator& acc, const operation_result& result )const - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( fee_payer(), result.get() ); - } - }; - /** * @ingroup operations @@ -924,13 +841,15 @@ namespace graphene { namespace chain { * have appropriate asset_ids, even if the amount is zero. * * @note this operation can be used to force a market order using the collateral without requiring outside funds. + * + * @note this operation can be used to issue new bitassets provided there is sufficient collateral */ struct call_order_update_operation { account_id_type funding_account; ///< pays fee, collateral, and cover asset fee; ///< paid by funding_account asset collateral_to_add; ///< the amount of collateral to add to the margin position - asset amount_to_cover; ///< the amount of the debt to be paid off + asset amount_to_cover; ///< the amount of the debt to be paid off, may be negative to issue new debt uint16_t maintenance_collateral_ratio = 0; ///< 0 means don't change, 1000 means feed account_id_type fee_payer()const { return funding_account; } @@ -1393,9 +1312,7 @@ namespace graphene { namespace chain { typedef fc::static_variant< transfer_operation, limit_order_create_operation, - short_order_create_operation, limit_order_cancel_operation, - short_order_cancel_operation, call_order_update_operation, key_create_operation, account_create_operation, @@ -1594,9 +1511,6 @@ FC_REFLECT( graphene::chain::limit_order_create_operation, ) FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) ) FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order) ) -FC_REFLECT( graphene::chain::short_order_cancel_operation,(fee)(fee_paying_account)(order) ) -FC_REFLECT( graphene::chain::short_order_create_operation, (fee)(seller)(amount_to_sell)(collateral) - (initial_collateral_ratio)(maintenance_collateral_ratio)(expiration) ) FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(collateral_to_add)(amount_to_cover)(maintenance_collateral_ratio) ) FC_REFLECT( graphene::chain::transfer_operation, diff --git a/libraries/chain/include/graphene/chain/short_order_evaluator.hpp b/libraries/chain/include/graphene/chain/short_order_evaluator.hpp index acf1acd4..485424fb 100644 --- a/libraries/chain/include/graphene/chain/short_order_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/short_order_evaluator.hpp @@ -22,32 +22,6 @@ namespace graphene { namespace chain { - class short_order_create_evaluator : public evaluator - { - public: - typedef short_order_create_operation operation_type; - - object_id_type do_evaluate( const short_order_create_operation& o ); - object_id_type do_apply( const short_order_create_operation& o ); - - const short_order_create_operation* _op = nullptr; - const account_object* _seller = nullptr; - const asset_object* _sell_asset = nullptr; - const asset_object* _receive_asset = nullptr; - share_type _priority_fee = 0; - }; - - class short_order_cancel_evaluator : public evaluator - { - public: - typedef short_order_cancel_operation operation_type; - - asset do_evaluate( const short_order_cancel_operation& o ); - asset do_apply( const short_order_cancel_operation& o ); - - const short_order_object* _order; - }; - class call_order_update_evaluator : public evaluator { public: @@ -60,6 +34,7 @@ namespace graphene { namespace chain { const asset_object* _debt_asset = nullptr; const account_object* _paying_account = nullptr; const call_order_object* _order = nullptr; + const bitasset_data_object* _bitasset_data = nullptr; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/short_order_object.hpp b/libraries/chain/include/graphene/chain/short_order_object.hpp index acbc59d8..1036b743 100644 --- a/libraries/chain/include/graphene/chain/short_order_object.hpp +++ b/libraries/chain/include/graphene/chain/short_order_object.hpp @@ -26,47 +26,6 @@ namespace graphene { namespace chain { using namespace graphene::db; - /** - * @class short_order_object - * @brief maintains state about requests to short an asset - * - * Short orders are only valid if their sell price is above the - * fair market value of the asset at the feed price. Users can - * place shorts at any price but their order will be ignored - * beyond the feed. - * - * All shorts have a minimial initial collateral ratio requirement that is - * defined by the network, but individuals may choose to have a higher - * initial collateral to avoid the risk of being margin called. - * - * All shorts have a maintenance collateral ratio that must be kept or - * the network will automatically cover the short order. Users can - * specify a higher maintenance collateral ratio as a form of "stop loss" - * and to potentially get ahead of a short squeeze. - */ - class short_order_object : public abstract_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = short_order_object_type; - - time_point_sec expiration; - account_id_type seller; - share_type for_sale; - share_type available_collateral; ///< asset_id == sell_price.quote.asset_id - price sell_price; ///< the price the short is currently at = min(limit_price,feed) - price call_price; ///< the price that will be used to trigger margin calls after match, must be 1:1 if prediction market - uint16_t initial_collateral_ratio = 0; ///< may be higher than the network requires - uint16_t maintenance_collateral_ratio = 0; ///< may optionally be higher than the network requires - - asset get_collateral()const { return asset( available_collateral, sell_price.quote.asset_id ); } - /** if the initial_collateral_ratio is 0, then this is a prediction market order which means the - * amount for sale depends upon price and available collateral. - */ - asset amount_for_sale()const { return asset( for_sale, sell_price.base.asset_id ); } - asset amount_to_receive()const { return amount_for_sale() * sell_price; } - }; - /** * @class call_order_object * @brief tracks debt and call price information @@ -115,27 +74,6 @@ namespace graphene { namespace chain { { return balance.asset_id; } }; - struct by_id; - struct by_price; - struct by_account; - struct by_expiration; - struct by_collateral; - typedef multi_index_container< - short_order_object, - indexed_by< - hashed_unique< tag, - member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member< short_order_object, time_point_sec, &short_order_object::expiration> >, - ordered_unique< tag, - composite_key< short_order_object, - member< short_order_object, price, &short_order_object::sell_price>, - member< object, object_id_type, &object::id> - >, - composite_key_compare< std::greater, std::less > - > - > - > short_order_multi_index_type; - typedef multi_index_container< call_order_object, indexed_by< @@ -187,10 +125,6 @@ namespace graphene { namespace chain { typedef generic_index force_settlement_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::short_order_object, (graphene::db::object), - (expiration)(seller)(for_sale)(available_collateral)(sell_price) - (call_price)(initial_collateral_ratio)(maintenance_collateral_ratio) - ) FC_REFLECT_DERIVED( graphene::chain::call_order_object, (graphene::db::object), (borrower)(collateral)(debt)(call_price)(maintenance_collateral_ratio) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 7a7d15d1..a4d2b089 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -108,7 +108,6 @@ namespace graphene { namespace chain { delegate_object_type, witness_object_type, limit_order_object_type, - short_order_object_type, call_order_object_type, custom_object_type, proposal_object_type, @@ -152,7 +151,6 @@ namespace graphene { namespace chain { class force_settlement_object; class key_object; class limit_order_object; - class short_order_object; class call_order_object; class custom_object; class proposal_object; @@ -169,7 +167,6 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, delegate_object_type, delegate_object> delegate_id_type; typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type; typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type; - typedef object_id< protocol_ids, short_order_object_type, short_order_object> short_order_id_type; typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type; typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type; typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type; @@ -358,7 +355,7 @@ namespace graphene { namespace chain { uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay uint32_t transfer_fee; ///< fee for transferring some asset uint32_t limit_order_fee; ///< fee for placing a limit order in the markets - uint32_t short_order_fee; ///< fee for placing a short order in the markets + uint32_t call_order_fee; ///< fee for placing a call order in the markets uint32_t publish_feed_fee; ///< fee for publishing a price feed uint32_t asset_create_fee; ///< the cost to register the cheapest asset uint32_t asset_update_fee; ///< the cost to modify a registered asset @@ -487,7 +484,6 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (delegate_object_type) (witness_object_type) (limit_order_object_type) - (short_order_object_type) (call_order_object_type) (custom_object_type) (proposal_object_type) @@ -532,7 +528,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (witness_withdraw_pay_fee) (transfer_fee) (limit_order_fee) - (short_order_fee) + (call_order_fee) (publish_feed_fee) (asset_create_fee) (asset_update_fee) @@ -587,7 +583,6 @@ FC_REFLECT_TYPENAME( graphene::chain::force_settlement_id_type ) FC_REFLECT_TYPENAME( graphene::chain::delegate_id_type ) FC_REFLECT_TYPENAME( graphene::chain::witness_id_type ) FC_REFLECT_TYPENAME( graphene::chain::limit_order_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::short_order_id_type ) FC_REFLECT_TYPENAME( graphene::chain::call_order_id_type ) FC_REFLECT_TYPENAME( graphene::chain::custom_id_type ) FC_REFLECT_TYPENAME( graphene::chain::proposal_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index c08027e2..ce9c2942 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -393,37 +393,6 @@ share_type limit_order_cancel_operation::calculate_fee(const fee_schedule_type& return k.limit_order_fee; } -void short_order_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const -{ - active_auth_set.insert(seller); -} - -void short_order_create_operation::validate()const -{ - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( initial_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); - FC_ASSERT( initial_collateral_ratio > maintenance_collateral_ratio ); - FC_ASSERT( initial_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); -} - -share_type short_order_create_operation::calculate_fee(const fee_schedule_type& k) const -{ - return k.short_order_fee; -} -void short_order_cancel_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const -{ - active_auth_set.insert(fee_paying_account); -} - -void short_order_cancel_operation::validate()const -{ - FC_ASSERT( fee.amount >= 0 ); -} - -share_type short_order_cancel_operation::calculate_fee(const fee_schedule_type& k) const -{ - return k.short_order_fee; -} void call_order_update_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { @@ -433,18 +402,13 @@ void call_order_update_operation::get_required_auth(flat_set& a void call_order_update_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( collateral_to_add.amount > 0 || amount_to_cover.amount > 0 || maintenance_collateral_ratio > 0 ); - if( amount_to_cover.amount == 0 ) FC_ASSERT( collateral_to_add.amount >= 0 ); - if( collateral_to_add.amount.value <= 0 ) FC_ASSERT( amount_to_cover.amount.value > 0 ); - - FC_ASSERT( amount_to_cover.amount >= 0 ); FC_ASSERT( amount_to_cover.asset_id != collateral_to_add.asset_id ); FC_ASSERT( maintenance_collateral_ratio == 0 || maintenance_collateral_ratio >= 1000 ); } share_type call_order_update_operation::calculate_fee(const fee_schedule_type& k) const { - return k.short_order_fee; + return k.call_order_fee; } proposal_create_operation proposal_create_operation::genesis_proposal(const database& db) diff --git a/libraries/chain/short_order_evaluator.cpp b/libraries/chain/short_order_evaluator.cpp index ab05611a..743fe634 100644 --- a/libraries/chain/short_order_evaluator.cpp +++ b/libraries/chain/short_order_evaluator.cpp @@ -23,139 +23,6 @@ #include namespace graphene { namespace chain { -object_id_type short_order_create_evaluator::do_evaluate( const short_order_create_operation& op ) -{ - database& d = db(); - - FC_ASSERT( op.expiration >= d.head_block_time() ); - - const asset_object& base_asset = op.amount_to_sell.asset_id(d); - const asset_object& quote_asset = op.collateral.asset_id(d); - - FC_ASSERT( base_asset.is_market_issued() ); - FC_ASSERT( quote_asset.id == base_asset.bitasset_data(d).options.short_backing_asset ); - _seller = fee_paying_account; - _receive_asset = "e_asset; - _sell_asset = &base_asset; - - - FC_ASSERT( !(base_asset.options.flags & white_list) || _seller->is_authorized_asset(base_asset) ); - FC_ASSERT( !(quote_asset.options.flags & white_list) || _seller->is_authorized_asset(quote_asset) ); - - const asset_bitasset_data_object& bitasset_data = _sell_asset->bitasset_data(d); - if( bitasset_data.is_prediction_market ) - { - FC_ASSERT( op.initial_collateral_ratio == 0 ); - FC_ASSERT( op.maintenance_collateral_ratio == 0 ); - auto p = op.sell_price(); - - // the maximum price is 1:1, it does not make sense to charge more than - // the collateral backing the position. - FC_ASSERT( p.base.amount < p.quote.amount ); - } - else - { - FC_ASSERT( op.initial_collateral_ratio >= bitasset_data.current_feed.required_initial_collateral ); - FC_ASSERT( op.maintenance_collateral_ratio >= bitasset_data.current_feed.required_maintenance_collateral ); - FC_ASSERT( op.sell_price() >= bitasset_data.current_feed.short_limit ); - } - - return object_id_type(); -} - -object_id_type short_order_create_evaluator::do_apply( const short_order_create_operation& op ) -{ - db().adjust_balance(op.seller, -op.collateral); - - const auto& new_order_object = db().create( [&]( short_order_object& obj ){ - obj.seller = _seller->id; - obj.for_sale = op.amount_to_sell.amount; - obj.available_collateral = op.collateral.amount; - obj.sell_price = op.sell_price(); - obj.call_price = op.call_price(); - obj.initial_collateral_ratio = op.initial_collateral_ratio; - obj.maintenance_collateral_ratio = op.maintenance_collateral_ratio; - obj.expiration = op.expiration; - }); - short_order_id_type new_id = new_order_object.id; - - if( op.collateral.asset_id == asset_id_type() ) - { - auto& bal_obj = fee_paying_account->statistics(db()); - db().modify( bal_obj, [&]( account_statistics_object& obj ){ - obj.total_core_in_orders += op.collateral.amount; - }); - } - - // Possible optimization: We only need to check calls if both are true: - // - The new order is at the front of the book - // - The new order is below the call limit price - db().check_call_orders(*_sell_asset); - - if( !db().find(new_id) ) // then we were filled by call order - return new_id; - - const auto& limit_order_idx = db().get_index_type(); - const auto& limit_price_idx = limit_order_idx.indices().get(); - - auto min_limit_price = ~op.sell_price(); - - auto itr = limit_price_idx.lower_bound( min_limit_price.max() ); - auto end = limit_price_idx.upper_bound( min_limit_price ); - - while( itr != end ) - { - auto old_itr = itr; - ++itr; - if( db().match( *old_itr, new_order_object, old_itr->sell_price ) != 1 ) - break; // 1 means ONLY old iter filled - } - - //Possible optimization: only check calls if the new order completely filled some old order - //Do I need to check both assets? - db().check_call_orders(*_sell_asset); - db().check_call_orders(*_receive_asset); - - return new_id; -} // short_order_evaluator::do_apply - - -asset short_order_cancel_evaluator::do_evaluate( const short_order_cancel_operation& o ) -{ - database& d = db(); - - _order = &o.order(d); - FC_ASSERT( _order->seller == o.fee_paying_account ); - - return _order->get_collateral(); -} - -asset short_order_cancel_evaluator::do_apply( const short_order_cancel_operation& o ) -{ - database& d = db(); - - auto refunded = _order->get_collateral(); - d.adjust_balance(o.fee_paying_account, refunded); - auto base_asset = _order->sell_price.base.asset_id; - auto quote_asset = _order->sell_price.quote.asset_id; - - d.remove( *_order ); - - if( refunded.asset_id == asset_id_type() ) - { - auto& stats_obj = fee_paying_account->statistics(d); - d.modify( stats_obj, [&]( account_statistics_object& obj ){ - obj.total_core_in_orders -= refunded.amount; - }); - } - - // Possible optimization: order can be called by canceling a short order iff the canceled order was at the top of the book. - // Do I need to check calls in both assets? - db().check_call_orders(base_asset(d)); - db().check_call_orders(quote_asset(d)); - - return refunded; -} asset call_order_update_evaluator::do_evaluate(const call_order_update_operation& o) { try { @@ -164,101 +31,104 @@ asset call_order_update_evaluator::do_evaluate(const call_order_update_operation _paying_account = &o.funding_account(d); _debt_asset = &o.amount_to_cover.asset_id(d); - const asset_bitasset_data_object& bitasset_data = _debt_asset->bitasset_data(d); - FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a market-issued asset.", + _bitasset_data = &_debt_asset->bitasset_data(d); + FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); - FC_ASSERT( o.collateral_to_add.asset_id == bitasset_data.options.short_backing_asset ); + FC_ASSERT( o.collateral_to_add.asset_id == _bitasset_data->options.short_backing_asset ); - if( bitasset_data.is_prediction_market ) + if( _bitasset_data->is_prediction_market ) { - FC_ASSERT( o.collateral_to_add.amount <= 0 ); - FC_ASSERT( -o.collateral_to_add.amount == o.amount_to_cover.amount ); + FC_ASSERT( o.collateral_to_add.amount == -o.amount_to_cover.amount ); FC_ASSERT( o.maintenance_collateral_ratio == 0 ); } else { FC_ASSERT( o.maintenance_collateral_ratio == 0 || - o.maintenance_collateral_ratio > bitasset_data.current_feed.required_maintenance_collateral ); + o.maintenance_collateral_ratio > _bitasset_data->current_feed.required_maintenance_collateral ); } - FC_ASSERT( d.get_balance(*_paying_account, *_debt_asset) >= o.amount_to_cover, - "Cannot cover by ${c} when payer has ${b}", - ("c", o.amount_to_cover.amount)("b", d.get_balance(*_paying_account, *_debt_asset).amount) ); - FC_ASSERT( d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)) >= o.collateral_to_add, - "Cannot increase collateral by ${c} when payer has ${b}", ("c", o.amount_to_cover.amount) - ("b", d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)).amount) ); - - auto& call_idx = d.get_index_type().indices().get(); - auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.amount_to_cover.asset_id) ); - FC_ASSERT( itr != call_idx.end(), "Could not find call order for ${sym} belonging to ${acct}.", - ("sym", _debt_asset->symbol)("acct", _paying_account->name) ); - _order = &*itr; - - FC_ASSERT( o.amount_to_cover.asset_id == _order->debt_type() ); - - FC_ASSERT( o.amount_to_cover.amount <= _order->get_debt().amount ); - - if( o.amount_to_cover.amount < _order->get_debt().amount ) + if( o.amount_to_cover > 0 ) { - FC_ASSERT( (_order->get_debt() - o.amount_to_cover) * - price::call_price(_order->get_debt() - o.amount_to_cover, - _order->get_collateral() + o.collateral_to_add, - o.maintenance_collateral_ratio? o.maintenance_collateral_ratio - : _order->maintenance_collateral_ratio) - < _order->get_collateral(), - "Order would be called immediately following this update. Refusing to apply update." ); - FC_ASSERT( o.amount_to_cover < _order->get_debt(), "Cover amount is greater than debt." ); - } else { - _closing_order = true; - FC_ASSERT( o.collateral_to_add.amount == -_order->get_collateral().amount, "", - ("collateral", _order->get_collateral()) ); - return _order->get_collateral(); + FC_ASSERT( d.get_balance(*_paying_account, *_debt_asset) >= o.amount_to_cover, + "Cannot cover by ${c} when payer only has ${b}", + ("c", o.amount_to_cover.amount)("b", d.get_balance(*_paying_account, *_debt_asset).amount) ); } + if( o.collateral_to_add > 0 ) + { + FC_ASSERT( d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)) >= o.collateral_to_add, + "Cannot increase collateral by ${c} when payer only has ${b}", ("c", o.amount_to_cover.amount) + ("b", d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)).amount) ); + } + return asset(); } FC_CAPTURE_AND_RETHROW( (o) ) } + asset call_order_update_evaluator::do_apply(const call_order_update_operation& o) { database& d = db(); + asset collateral_returned = -o.collateral_to_add; - d.adjust_balance(_paying_account->get_id(), -o.amount_to_cover); + d.adjust_balance( o.funding_account, -o.amount_to_cover); + d.adjust_balance( o.funding_account, -o.collateral_to_add); // Deduct the debt paid from the total supply of the debt asset. - d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { - dynamic_asset.current_supply -= o.amount_to_cover.amount; - assert(dynamic_asset.current_supply >= 0); - }); - - asset collateral_returned; - if( _closing_order ) + if( o.amount_to_cover != 0 ) { - collateral_returned = _order->get_collateral(); - // Credit the account's balances for his returned collateral. - d.adjust_balance(_paying_account->get_id(), collateral_returned); - d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { - if( _order->get_collateral().asset_id == asset_id_type() ) - stats.total_core_in_orders -= collateral_returned.amount; + d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { + dynamic_asset.current_supply -= o.amount_to_cover.amount; + assert(dynamic_asset.current_supply >= 0); }); - // Remove the call order. - d.remove(*_order); - } else { - // Update the call order. - d.modify(*_order, [&o](call_order_object& call) { - call.debt -= o.amount_to_cover.amount; - call.collateral += o.collateral_to_add.amount; - if( o.maintenance_collateral_ratio ) - call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; - call.update_call_price(); - }); - if( o.collateral_to_add.amount > 0 ) - // Deduct the added collateral from the account. - d.adjust_balance(_paying_account->get_id(), -o.collateral_to_add); - d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { - if( o.collateral_to_add.asset_id == asset_id_type() ) - stats.total_core_in_orders += o.collateral_to_add.amount; - }); } + auto& call_idx = d.get_index_type().indices().get(); + auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.amount_to_cover.asset_id) ); + if( itr == call_idx.end() ) + { + FC_ASSERT( o.collateral_to_add.amount > 0 ); + FC_ASSERT( o.amount_to_cover.amount < 0 ); + d.create( [&](call_order_object& call ){ + call.owner = o.funding_account; + call.collateral = o.collateral_to_add.amount; + call.debt = -o.amount_to_cover.amount; + call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; + // TODO: this is only necessary for non-prediction markets + call.update_call_price(); + FC_ASSERT( call.call_price < _bitasset_data->current_feed.settlement_price ); + }); + } + else + { + if( itr->debt - o.amount_to_cover == 0 ) + { + FC_ASSERT( o.collateral_to_add == 0 ); + collateral_returned = itr->get_collateral(); + d.adjust_balance( o.funding_account, call. + d.remove( *itr ); + } + else + { + FC_ASSERT( (itr->debt - o.amount_to_cover.amount) >= 0 ); + FC_ASSERT( (itr->collateral + o.collateral_to_add.amount) >= 0 ); + + d.modify( *itr, [&]( call_order_object& call ){ + call.collateral += o.collateral_to_add.amount; + call.debt -= o.amount_to_cover.amount; + if( o.maintenance_collateral_ratio ) + call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; + + // TODO: this is only necessary for non-prediction markets + call.update_call_price(); + FC_ASSERT( call.call_price < _bitasset_data->current_feed.settlement_price ); + }); + } + } + if( collateral_returned.asset_id == asset_id_type() && collateral_returned.amount != 0 ) + { + d.modify(_paying_account->statistics(d), [&](account_statistics_object& stats) { + stats.total_core_in_orders -= collateral_returned.amount; + }); + } return collateral_returned; } From 4ca3bb9f065bc5a3f7e984dac0cd9c2adbad6628 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 09:53:40 -0400 Subject: [PATCH 006/354] rename short to call --- .../{short_order_evaluator.cpp => call_order_evaluator.cpp} | 0 .../{short_order_evaluator.hpp => call_order_evaluator.hpp} | 0 .../chain/{short_order_object.hpp => call_order_object.hpp} | 1 - 3 files changed, 1 deletion(-) rename libraries/chain/{short_order_evaluator.cpp => call_order_evaluator.cpp} (100%) rename libraries/chain/include/graphene/chain/{short_order_evaluator.hpp => call_order_evaluator.hpp} (100%) rename libraries/chain/include/graphene/chain/{short_order_object.hpp => call_order_object.hpp} (98%) diff --git a/libraries/chain/short_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp similarity index 100% rename from libraries/chain/short_order_evaluator.cpp rename to libraries/chain/call_order_evaluator.cpp diff --git a/libraries/chain/include/graphene/chain/short_order_evaluator.hpp b/libraries/chain/include/graphene/chain/call_order_evaluator.hpp similarity index 100% rename from libraries/chain/include/graphene/chain/short_order_evaluator.hpp rename to libraries/chain/include/graphene/chain/call_order_evaluator.hpp diff --git a/libraries/chain/include/graphene/chain/short_order_object.hpp b/libraries/chain/include/graphene/chain/call_order_object.hpp similarity index 98% rename from libraries/chain/include/graphene/chain/short_order_object.hpp rename to libraries/chain/include/graphene/chain/call_order_object.hpp index 1036b743..5c1f8b67 100644 --- a/libraries/chain/include/graphene/chain/short_order_object.hpp +++ b/libraries/chain/include/graphene/chain/call_order_object.hpp @@ -120,7 +120,6 @@ namespace graphene { namespace chain { > force_settlement_object_multi_index_type; - typedef generic_index short_order_index; typedef generic_index call_order_index; typedef generic_index force_settlement_index; } } // graphene::chain From 663036f2aa98ef650f31cf0afaaf115e235f5a4e Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 10:14:10 -0400 Subject: [PATCH 007/354] it builds --- libraries/chain/CMakeLists.txt | 2 +- libraries/chain/asset.cpp | 11 +- libraries/chain/asset_evaluator.cpp | 2 +- libraries/chain/call_order_evaluator.cpp | 22 +-- libraries/chain/db_debug.cpp | 9 +- libraries/chain/db_init.cpp | 7 +- libraries/chain/db_market.cpp | 148 +----------------- libraries/chain/db_update.cpp | 12 +- libraries/chain/evaluator.cpp | 2 +- .../graphene/chain/call_order_evaluator.hpp | 2 +- .../graphene/chain/call_order_object.hpp | 4 +- .../chain/include/graphene/chain/database.hpp | 2 - libraries/chain/limit_order_evaluator.cpp | 42 +---- 13 files changed, 35 insertions(+), 230 deletions(-) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 17635897..100fd8a3 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -16,7 +16,7 @@ add_library( graphene_chain asset_evaluator.cpp transfer_evaluator.cpp proposal_evaluator.cpp - short_order_evaluator.cpp + call_order_evaluator.cpp limit_order_evaluator.cpp vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index cb1f6fdb..bb36910d 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -122,18 +122,11 @@ namespace graphene { namespace chain { { try { if( !call_limit.is_null() ) call_limit.validate(); - if( !short_limit.is_null() ) - short_limit.validate(); if( !settlement_price.is_null() ) settlement_price.validate(); - FC_ASSERT( call_limit.is_null() == short_limit.is_null() ); - FC_ASSERT( call_limit.base.asset_id == short_limit.quote.asset_id ); - FC_ASSERT( call_limit.quote.asset_id == short_limit.base.asset_id ); FC_ASSERT( max_margin_period_sec > 0 ); - FC_ASSERT( required_maintenance_collateral < required_initial_collateral ); FC_ASSERT( required_maintenance_collateral >= 1000 ); - FC_ASSERT( call_limit.is_null() || call_limit < ~short_limit ); - } FC_CAPTURE_AND_RETHROW( (call_limit.is_null())(short_limit.is_null())(call_limit)(short_limit) - (max_margin_period_sec)(required_maintenance_collateral)(required_initial_collateral) ) } + } FC_CAPTURE_AND_RETHROW( (call_limit.is_null())(call_limit) + (max_margin_period_sec)(required_maintenance_collateral) ) } } } // graphene::chain diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 096d05db..1bda2aed 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index 743fe634..45005365 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -16,9 +16,9 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include +#include #include -#include +#include #include #include @@ -47,17 +47,17 @@ asset call_order_update_evaluator::do_evaluate(const call_order_update_operation o.maintenance_collateral_ratio > _bitasset_data->current_feed.required_maintenance_collateral ); } - if( o.amount_to_cover > 0 ) + if( o.amount_to_cover.amount > 0 ) { FC_ASSERT( d.get_balance(*_paying_account, *_debt_asset) >= o.amount_to_cover, "Cannot cover by ${c} when payer only has ${b}", ("c", o.amount_to_cover.amount)("b", d.get_balance(*_paying_account, *_debt_asset).amount) ); } - if( o.collateral_to_add > 0 ) + if( o.collateral_to_add.amount > 0 ) { - FC_ASSERT( d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)) >= o.collateral_to_add, + FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= o.collateral_to_add, "Cannot increase collateral by ${c} when payer only has ${b}", ("c", o.amount_to_cover.amount) - ("b", d.get_balance(*_paying_account, bitasset_data.options.short_backing_asset(d)).amount) ); + ("b", d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)).amount) ); } return asset(); @@ -73,7 +73,7 @@ asset call_order_update_evaluator::do_apply(const call_order_update_operation& o d.adjust_balance( o.funding_account, -o.collateral_to_add); // Deduct the debt paid from the total supply of the debt asset. - if( o.amount_to_cover != 0 ) + if( o.amount_to_cover.amount != 0 ) { d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { dynamic_asset.current_supply -= o.amount_to_cover.amount; @@ -88,7 +88,7 @@ asset call_order_update_evaluator::do_apply(const call_order_update_operation& o FC_ASSERT( o.collateral_to_add.amount > 0 ); FC_ASSERT( o.amount_to_cover.amount < 0 ); d.create( [&](call_order_object& call ){ - call.owner = o.funding_account; + call.borrower = o.funding_account; call.collateral = o.collateral_to_add.amount; call.debt = -o.amount_to_cover.amount; call.maintenance_collateral_ratio = o.maintenance_collateral_ratio; @@ -99,11 +99,11 @@ asset call_order_update_evaluator::do_apply(const call_order_update_operation& o } else { - if( itr->debt - o.amount_to_cover == 0 ) + if( itr->debt - o.amount_to_cover.amount == 0 ) { - FC_ASSERT( o.collateral_to_add == 0 ); + FC_ASSERT( o.collateral_to_add.amount == 0 ); collateral_returned = itr->get_collateral(); - d.adjust_balance( o.funding_account, call. + d.adjust_balance( o.funding_account, collateral_returned ); d.remove( *itr ); } else diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp index d7d8f4e0..34f12b58 100644 --- a/libraries/chain/db_debug.cpp +++ b/libraries/chain/db_debug.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -60,13 +60,6 @@ void database::debug_dump() if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount; total_balances[for_sale.asset_id] += for_sale.amount; } - for( const short_order_object& o : db.get_index_type().indices() ) - { - idump(("short_order")(o)); - auto col = o.get_collateral(); - if( col.asset_id == asset_id_type() ) core_in_orders += col.amount; - total_balances[col.asset_id] += col.amount; - } for( const call_order_object& o : db.get_index_type().indices() ) { idump(("call_order")(o)); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 57c3cd37..f4ae2196 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -75,8 +75,6 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); - register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -108,7 +106,6 @@ 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 >(); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 22fb778d..0ffb9d31 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include @@ -225,10 +225,6 @@ int database::match( const limit_order_object& bid, const limit_order_object& as return match( bid, ask, match_price ); } -int database::match( const limit_order_object& bid, const short_order_object& ask, const price& match_price ) -{ - return match( bid, ask, match_price ); -} asset database::match( const call_order_object& call, const force_settlement_object& settle, const price& match_price, asset max_settlement ) @@ -290,107 +286,6 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c } } -bool database::fill_order( const short_order_object& order, const asset& pays, const asset& receives ) -{ try { - assert( order.amount_for_sale().asset_id == pays.asset_id ); - assert( pays.asset_id != receives.asset_id ); - - const call_order_index& call_index = get_index_type(); - - const account_object& seller = order.seller(*this); - const asset_object& recv_asset = receives.asset_id(*this); - const asset_object& pays_asset = pays.asset_id(*this); - assert( pays_asset.is_market_issued() ); - - auto issuer_fees = pay_market_fees( recv_asset, receives ); - - bool filled = pays == order.amount_for_sale(); - asset seller_to_collateral; - if( (*pays_asset.bitasset_data_id)(*this).is_prediction_market ) - { - assert( pays.amount >= receives.amount ); - seller_to_collateral = pays.amount - receives.amount; - } - else - { - seller_to_collateral = filled ? order.get_collateral() : pays * order.sell_price; - } - auto buyer_to_collateral = receives - issuer_fees; - - if( receives.asset_id == asset_id_type() ) - { - const auto& statistics = seller.statistics(*this); - modify( statistics, [&]( account_statistics_object& b ){ - b.total_core_in_orders += buyer_to_collateral.amount; - }); - } - - modify( pays_asset.dynamic_asset_data_id(*this), [&]( asset_dynamic_data_object& obj ){ - obj.current_supply += pays.amount; - }); - - const auto& call_account_index = call_index.indices().get(); - auto call_itr = call_account_index.find( boost::make_tuple(order.seller, pays.asset_id) ); - if( call_itr == call_account_index.end() ) - { - create( [&]( call_order_object& c ){ - c.borrower = seller.id; - c.collateral = seller_to_collateral.amount + buyer_to_collateral.amount; - c.debt = pays.amount; - c.maintenance_collateral_ratio = order.maintenance_collateral_ratio; - c.call_price = price::max(seller_to_collateral.asset_id, pays.asset_id); - c.update_call_price(); - }); - } - else - { - modify( *call_itr, [&]( call_order_object& c ){ - c.debt += pays.amount; - c.collateral += seller_to_collateral.amount + buyer_to_collateral.amount; - c.maintenance_collateral_ratio = order.maintenance_collateral_ratio; - c.update_call_price(); - }); - } - - if( filled ) - { - remove( order ); - } - else - { - modify( order, [&]( short_order_object& b ) { - b.for_sale -= pays.amount; - b.available_collateral -= seller_to_collateral.amount; - assert( b.available_collateral > 0 ); - assert( b.for_sale > 0 ); - }); - - /** - * There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we - * have hit the limit where the seller is asking for nothing in return. When this - * happens we must refund any balance back to the seller, it is too small to be - * sold at the sale price. - */ - if( order.amount_to_receive().amount == 0 ) - { - adjust_balance(seller.get_id(), order.get_collateral()); - if( order.get_collateral().asset_id == asset_id_type() ) - { - const auto& statistics = seller.statistics(*this); - modify( statistics, [&]( account_statistics_object& b ){ - b.total_core_in_orders -= order.available_collateral; - }); - } - - remove( order ); - filled = true; - } - } - - push_applied_operation( fill_order_operation{ order.id, order.seller, pays, receives, issuer_fees } ); - - return filled; -} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) } bool database::fill_order( const call_order_object& order, const asset& pays, const asset& receives ) { try { @@ -483,11 +378,6 @@ bool database::check_call_orders( const asset_object& mia ) const limit_order_index& limit_index = get_index_type(); const auto& limit_price_index = limit_index.indices().get(); - const short_order_index& short_index = get_index_type(); - const auto& short_price_index = short_index.indices().get(); - - auto short_itr = short_price_index.lower_bound( price::max( mia.id, bitasset.options.short_backing_asset ) ); - auto short_end = short_price_index.upper_bound( ~bitasset.current_feed.call_limit ); auto limit_itr = limit_price_index.lower_bound( price::max( mia.id, bitasset.options.short_backing_asset ) ); auto limit_end = limit_price_index.upper_bound( ~bitasset.current_feed.call_limit ); @@ -499,33 +389,14 @@ bool database::check_call_orders( const asset_object& mia ) while( call_itr != call_end ) { - bool current_is_limit = true; bool filled_call = false; price match_price; asset usd_for_sale; if( limit_itr != limit_end ) { assert( limit_itr != limit_price_index.end() ); - if( short_itr != short_end && limit_itr->sell_price < short_itr->sell_price ) - { - assert( short_itr != short_price_index.end() ); - current_is_limit = false; - match_price = short_itr->sell_price; - usd_for_sale = short_itr->amount_for_sale(); - } - else - { - current_is_limit = true; - match_price = limit_itr->sell_price; - usd_for_sale = limit_itr->amount_for_sale(); - } - } - else if( short_itr != short_end ) - { - assert( short_itr != short_price_index.end() ); - current_is_limit = false; - match_price = short_itr->sell_price; - usd_for_sale = short_itr->amount_for_sale(); + match_price = limit_itr->sell_price; + usd_for_sale = limit_itr->amount_for_sale(); } else return filled_short_or_limit; @@ -569,16 +440,9 @@ bool database::check_call_orders( const asset_object& mia ) auto old_call_itr = call_itr; if( filled_call ) ++call_itr; fill_order( *old_call_itr, call_pays, call_receives ); - if( current_is_limit ) - { - auto old_limit_itr = !filled_call ? limit_itr++ : limit_itr; - fill_order( *old_limit_itr, order_pays, order_receives ); - } - else - { - auto old_short_itr = !filled_call ? short_itr++ : short_itr; - fill_order( *old_short_itr, order_pays, order_receives ); - } + + auto old_limit_itr = !filled_call ? limit_itr++ : limit_itr; + fill_order( *old_limit_itr, order_pays, order_receives ); } // whlie call_itr != call_end return filled_short_or_limit; diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 1ba5b95e..cd4dc03c 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -131,16 +131,6 @@ void database::clear_expired_orders() apply_operation(cancel_context, canceler); } - //Cancel expired short orders - auto& short_index = get_index_type().indices().get(); - while( !short_index.empty() && short_index.begin()->expiration <= head_block_time() ) - { - const short_order_object& order = *short_index.begin(); - short_order_cancel_operation canceler; - canceler.fee_paying_account = order.seller; - canceler.order = order.id; - apply_operation(cancel_context, canceler); - } //Process expired force settlement orders auto& settlement_index = get_index_type().indices().get(); diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 46117211..61803be7 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include diff --git a/libraries/chain/include/graphene/chain/call_order_evaluator.hpp b/libraries/chain/include/graphene/chain/call_order_evaluator.hpp index 485424fb..d7d6cc0c 100644 --- a/libraries/chain/include/graphene/chain/call_order_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/call_order_evaluator.hpp @@ -34,7 +34,7 @@ namespace graphene { namespace chain { const asset_object* _debt_asset = nullptr; const account_object* _paying_account = nullptr; const call_order_object* _order = nullptr; - const bitasset_data_object* _bitasset_data = nullptr; + const asset_bitasset_data_object* _bitasset_data = nullptr; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/call_order_object.hpp b/libraries/chain/include/graphene/chain/call_order_object.hpp index 5c1f8b67..becbc781 100644 --- a/libraries/chain/include/graphene/chain/call_order_object.hpp +++ b/libraries/chain/include/graphene/chain/call_order_object.hpp @@ -74,6 +74,9 @@ namespace graphene { namespace chain { { return balance.asset_id; } }; + struct by_collateral; + struct by_account; + struct by_price; typedef multi_index_container< call_order_object, indexed_by< @@ -101,7 +104,6 @@ namespace graphene { namespace chain { > > call_order_multi_index_type; - struct by_account; struct by_expiration; typedef multi_index_container< force_settlement_object, diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 469a78ad..ba6a3a14 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -281,7 +281,6 @@ namespace graphene { namespace chain { template int match( const limit_order_object& bid, const OrderType& ask, const price& match_price ); int match( const limit_order_object& bid, const limit_order_object& ask, const price& trade_price ); - int match( const limit_order_object& bid, const short_order_object& ask, const price& trade_price ); /// @return the amount of asset settled asset match(const call_order_object& call, const force_settlement_object& settle, @@ -293,7 +292,6 @@ namespace graphene { namespace chain { * @return true if the order was completely filled and thus freed. */ bool fill_order( const limit_order_object& order, const asset& pays, const asset& receives ); - bool fill_order( const short_order_object& order, const asset& pays, const asset& receives ); 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 ); diff --git a/libraries/chain/limit_order_evaluator.cpp b/libraries/chain/limit_order_evaluator.cpp index d3429567..0e8a0d2e 100644 --- a/libraries/chain/limit_order_evaluator.cpp +++ b/libraries/chain/limit_order_evaluator.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include namespace graphene { namespace chain { @@ -104,44 +103,13 @@ object_id_type limit_order_create_evaluator::do_apply( const limit_order_create_ if( converted_some && !db().find(result) ) // then we were filled by call order return result; } - const auto& short_order_idx = db().get_index_type(); - const auto& sell_price_idx = short_order_idx.indices().get(); - - FC_ASSERT( max_price.max() >= max_price ); - auto short_itr = sell_price_idx.lower_bound( max_price.max() ); - auto short_end = sell_price_idx.upper_bound( max_price ); - - while( !filled ) - { - if( limit_itr != limit_end ) - { - if( short_itr != short_end && limit_itr->sell_price < short_itr->sell_price ) - { - auto old_short_itr = short_itr; - ++short_itr; - filled = (db().match( new_order_object, *old_short_itr, old_short_itr->sell_price ) != 2 ); - } - else - { - auto old_limit_itr = limit_itr; - ++limit_itr; - filled = (db().match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 ); - } - } - else if( short_itr != short_end ) - { - auto old_short_itr = short_itr; - ++short_itr; - filled = (db().match( new_order_object, *old_short_itr, old_short_itr->sell_price ) != 2 ); - } - else break; - } } - else while( !filled && limit_itr != limit_end ) + + while( !filled && limit_itr != limit_end ) { - auto old_itr = limit_itr; - ++limit_itr; - filled = (db().match( new_order_object, *old_itr, old_itr->sell_price ) != 2); + auto old_limit_itr = limit_itr; + ++limit_itr; + filled = (db().match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 ); } //Possible optimization: only check calls if the new order completely filled some old order From 4cee2ca608f3accdf04eceea174fe7937da35e71 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 11:22:28 -0400 Subject: [PATCH 008/354] Update plugins and wallet to remove shorts --- libraries/app/api.cpp | 20 -------- libraries/app/include/graphene/app/api.hpp | 10 +--- .../account_history_plugin.cpp | 2 - libraries/wallet/cache.cpp | 4 +- .../wallet/include/graphene/wallet/wallet.hpp | 10 ++-- libraries/wallet/wallet.cpp | 46 +++++-------------- 6 files changed, 20 insertions(+), 72 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 98d25611..241678b3 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -215,22 +215,6 @@ namespace graphene { namespace app { return result; } - vector database_api::get_short_orders(asset_id_type a, uint32_t limit)const - { - const auto& short_order_idx = _db.get_index_type(); - const auto& sell_price_idx = short_order_idx.indices().get(); - const asset_object& mia = _db.get(a); - - FC_ASSERT( mia.is_market_issued(), "must be a market issued asset" ); - - price index_price = price::min(mia.get_id(), mia.bitasset_data(_db).options.short_backing_asset); - - auto short_itr = sell_price_idx.lower_bound(index_price.max()); - auto short_end = sell_price_idx.upper_bound(index_price.min()); - - return vector(short_itr, short_end); - } - vector database_api::get_call_orders(asset_id_type a, uint32_t limit)const { const auto& call_index = _db.get_index_type().indices().get(); @@ -358,15 +342,11 @@ namespace graphene { namespace app { case operation::tag::value: market = op.op.get().get_market(); break; - case operation::tag::value: - market = op.op.get().get_market(); - break; case operation::tag::value: market = op.op.get().get_market(); break; /* case operation::tag::value: - case operation::tag::value: */ default: break; } diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index e34f290c..e4729c3d 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -142,13 +142,6 @@ namespace graphene { namespace app { * @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; - /** - * @brief Get short orders in a given asset - * @param a ID of asset being sold - * @param limit Maximum number of orders to retrieve - * @return The short orders, ordered from least price to greatest - */ - vector get_short_orders(asset_id_type a, uint32_t limit)const; /** * @brief Get call orders in a given asset * @param a ID of asset being called @@ -342,7 +335,6 @@ FC_API(graphene::app::database_api, (get_named_account_balances) (lookup_asset_symbols) (get_limit_orders) - (get_short_orders) (get_call_orders) (get_settle_orders) (list_assets) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index d8e1148e..0fd4fac5 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -141,9 +141,7 @@ struct operation_get_impacted_accounts } void operator()( const limit_order_create_operation& o )const { } - void operator()( const short_order_create_operation& o )const { } void operator()( const limit_order_cancel_operation& o )const { } - void operator()( const short_order_cancel_operation& o )const { } void operator()( const call_order_update_operation& o )const { } void operator()( const key_create_operation& o )const { } void operator()( const custom_operation& o )const { } diff --git a/libraries/wallet/cache.cpp b/libraries/wallet/cache.cpp index d5c3f382..18b1d7b0 100644 --- a/libraries/wallet/cache.cpp +++ b/libraries/wallet/cache.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -72,8 +72,6 @@ object* create_object( const variant& v ) return create_object_of_type< witness_object >( v ); case limit_order_object_type: return create_object_of_type< limit_order_object >( v ); - case short_order_object_type: - return create_object_of_type< short_order_object >( v ); case call_order_object_type: return create_object_of_type< call_order_object >( v ); /* diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index fc1f0a9f..3359e2b5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -114,7 +114,6 @@ class wallet_api vector list_assets(const string& lowerbound, uint32_t limit)const; vector get_account_history(string name, int limit)const; vector get_limit_orders(string a, string b, uint32_t limit)const; - vector get_short_orders(string a, uint32_t limit)const; vector get_call_orders(string a, uint32_t limit)const; vector get_settle_orders(string a, uint32_t limit)const; global_property_object get_global_properties() const; @@ -204,7 +203,11 @@ class wallet_api bool fill_or_kill = false, bool broadcast = false); - signed_transaction short_sell_asset(string seller_name, string amount_to_sell, string asset_symbol, + /** + * This method will create a transaction with two operations, the first one will borrow amount_to_sell + * given amount of collateral + */ + signed_transaction borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast = false); signed_transaction create_asset(string issuer, @@ -272,7 +275,7 @@ FC_API( graphene::wallet::wallet_api, (upgrade_account) (create_account_with_brain_key) (sell_asset) - (short_sell_asset) + (borrow_asset) (transfer) (create_asset) (issue_asset) @@ -289,7 +292,6 @@ FC_API( graphene::wallet::wallet_api, (load_wallet_file) (normalize_brain_key) (get_limit_orders) - (get_short_orders) (get_call_orders) (get_settle_orders) (save_wallet_file) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2d0d6dec..9f2d255a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1011,7 +1011,7 @@ public: return sign_transaction( tx, broadcast ); } - signed_transaction short_sell_asset(string seller_name, string amount_to_sell, string asset_symbol, + signed_transaction borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast = false) { account_object seller = get_account(seller_name); @@ -1019,11 +1019,10 @@ public: FC_ASSERT(mia.is_market_issued()); asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset); - short_order_create_operation op; - op.seller = seller.id; - op.expiration = fc::time_point::now() + fc::days(365*10); - op.amount_to_sell = mia.amount_from_string(amount_to_sell); - op.collateral = collateral.amount_from_string(amount_of_collateral); + call_order_update_operation op; + op.funding_account = seller.id; + op.amount_to_cover = -mia.amount_from_string(amount_to_sell); + op.collateral_to_add = collateral.amount_from_string(amount_of_collateral); signed_transaction trx; trx.operations = {op}; @@ -1040,27 +1039,11 @@ public: FC_ASSERT(order_id.space() == protocol_ids, "Invalid order ID ${id}", ("id", order_id)); signed_transaction trx; - switch( order_id.type() ) - { - case short_order_object_type: { - short_order_cancel_operation op; - op.fee_paying_account = get_object(order_id).seller; - op.order = order_id; - op.fee = op.calculate_fee(_remote_db->get_global_properties().parameters.current_fees); - trx.operations = {op}; - break; - } - case limit_order_object_type: { - limit_order_cancel_operation op; - op.fee_paying_account = get_object(order_id).seller; - op.order = order_id; - op.fee = op.calculate_fee(_remote_db->get_global_properties().parameters.current_fees); - trx.operations = {op}; - break; - } - default: - FC_THROW("Invalid order ID ${id}", ("id", order_id)); - } + limit_order_cancel_operation op; + op.fee_paying_account = get_object(order_id).seller; + op.order = order_id; + op.fee = op.calculate_fee(_remote_db->get_global_properties().parameters.current_fees); + trx.operations = {op}; trx.validate(); return sign_transaction(trx, broadcast); @@ -1384,11 +1367,6 @@ vector wallet_api::get_limit_orders(string a, string b, uint return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); } -vector wallet_api::get_short_orders(string a, uint32_t limit)const -{ - return my->_remote_db->get_short_orders(get_asset(a).id, 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); @@ -1740,11 +1718,11 @@ signed_transaction wallet_api::sell_asset(string seller_account, symbol_to_receive, expiration, fill_or_kill, broadcast); } -signed_transaction wallet_api::short_sell_asset(string seller_name, string amount_to_sell, +signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast) { FC_ASSERT(!is_locked()); - return my->short_sell_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); + return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); } } } From 2ae6ce9cdce9075bd12ee8a48497cb1bc193bed7 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 12:17:09 -0400 Subject: [PATCH 009/354] updating tests to remove references to short --- docs | 2 +- .../db/include/graphene/db/level_map.hpp | 1 + programs/js_operation_serializer/main.cpp | 2 +- tests/common/database_fixture.cpp | 113 +-- tests/common/database_fixture.hpp | 17 - tests/tests/block_tests.cpp | 52 +- tests/tests/operation_tests.cpp | 693 +----------------- tests/tests/operation_tests2.cpp | 19 +- 8 files changed, 39 insertions(+), 860 deletions(-) diff --git a/docs b/docs index 1b53a8ec..71ed2984 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 1b53a8eca77783d073ce7cc95991447c3f34b927 +Subproject commit 71ed2984b71d57cab13cdf12074cff150edc1d3d diff --git a/libraries/db/include/graphene/db/level_map.hpp b/libraries/db/include/graphene/db/level_map.hpp index 55ecb99b..ec9df5d1 100644 --- a/libraries/db/include/graphene/db/level_map.hpp +++ b/libraries/db/include/graphene/db/level_map.hpp @@ -45,6 +45,7 @@ namespace graphene { namespace db { public: void open( const fc::path& dir, bool create = true, size_t cache_size = 0 ) { try { + idump( (dir)(create)); FC_ASSERT( !is_open(), "Database is already open!" ); ldb::Options opts; diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 21db9e66..eab7181c 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 27d57445..fda2bc10 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -126,12 +126,6 @@ void database_fixture::verify_asset_supplies( )const if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount; total_balances[for_sale.asset_id] += for_sale.amount; } - for( const short_order_object& o : db.get_index_type().indices() ) - { - asset col = o.get_collateral(); - if( col.asset_id == asset_id_type() ) core_in_orders += col.amount; - total_balances[col.asset_id] += col.amount; - } for( const call_order_object& o : db.get_index_type().indices() ) { asset col = o.get_collateral(); @@ -409,32 +403,6 @@ void database_fixture::issue_uia( const account_object& recipient, asset amount return; } -const short_order_object*database_fixture::create_short(account_id_type seller, const asset& amount_to_sell, const asset& collateral_provided, uint16_t initial_collateral_ratio, uint16_t maintenance_collateral_ratio) -{ - return create_short(seller(db), amount_to_sell, collateral_provided, initial_collateral_ratio, maintenance_collateral_ratio); -} - -const short_order_object* database_fixture::create_short( - const account_object& seller, - const asset& amount_to_sell, - const asset& collateral_provided, - uint16_t initial_collateral_ratio /* = 2000 */, - uint16_t maintenance_collateral_ratio /* = 1750 */ - ) -{ - short_order_create_operation op; - op.seller = seller.id; - op.amount_to_sell = amount_to_sell; - op.collateral = collateral_provided; - op.initial_collateral_ratio = initial_collateral_ratio; - op.maintenance_collateral_ratio = maintenance_collateral_ratio; - trx.operations.push_back(std::move(op)); - trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); - trx.operations.clear(); - return db.find(ptx.operation_results[0].get()); -} - const account_object& database_fixture::create_account( const string& name, const key_id_type& key /* = key_id_type() */ @@ -603,18 +571,6 @@ asset database_fixture::cancel_limit_order( const limit_order_object& order ) return processed.operation_results[0].get(); } -asset database_fixture::cancel_short_order( const short_order_object& order ) -{ - short_order_cancel_operation cancel_order; - cancel_order.fee_paying_account = order.seller; - cancel_order.order = order.id; - trx.operations.push_back(cancel_order); - for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); - trx.validate(); - auto processed = db.push_transaction(trx, ~0); - trx.operations.clear(); - return processed.operation_results[0].get(); -} void database_fixture::transfer( account_id_type from, @@ -727,15 +683,6 @@ string database_fixture::pretty( const asset& a )const return ss.str(); } -void database_fixture::print_short_order( const short_order_object& cur )const -{ - std::cout << std::setw(10) << cur.seller(db).name << " "; - std::cout << std::setw(10) << "SHORT" << " "; - std::cout << std::setw(16) << pretty( cur.amount_for_sale() ) << " "; - std::cout << std::setw(16) << pretty( cur.amount_to_receive() ) << " "; - std::cout << std::setw(16) << (~cur.sell_price).to_real() << " "; -} - void database_fixture::print_limit_order( const limit_order_object& cur )const { std::cout << std::setw(10) << cur.seller(db).name << " "; @@ -783,68 +730,16 @@ void database_fixture::print_joint_market( const string& syma, const string& sym const auto& limit_idx = db.get_index_type(); const auto& limit_price_idx = limit_idx.indices().get(); - const auto& short_idx = db.get_index_type(); - const auto& sell_price_idx = short_idx.indices().get(); auto limit_itr = limit_price_idx.begin(); - auto short_itr = sell_price_idx.rbegin(); - while( true ) + while( limit_itr != limit_price_idx.end() ) { std::cout << std::endl; - if( limit_itr != limit_price_idx.end() ) - { - if( short_itr != sell_price_idx.rend() && limit_itr->sell_price > ~short_itr->sell_price ) - { - print_short_order( *short_itr ); - ++short_itr; - } - else // print the limit - { - print_limit_order( *limit_itr ); - ++limit_itr; - } - } - else if( short_itr != sell_price_idx.rend() ) - { - print_short_order( *short_itr ); - ++short_itr; - } - else - break; + print_limit_order( *limit_itr ); + ++limit_itr; } } -void database_fixture::print_short_market( const string& syma, const string& symb )const -{ - const auto& limit_idx = db.get_index_type(); - const auto& price_idx = limit_idx.indices().get(); - - cout << std::fixed; - cout.precision(5); - cout << std::setw(10) << std::left << "NAME" << " "; - cout << std::setw(16) << std::right << "FOR SHORT" << " "; - cout << std::setw(16) << std::right << "COLLATERAL" << " "; - cout << std::setw(10) << std::right << "PRICE" << " "; - cout << std::setw(10) << std::right << "1/PRICE" << " "; - cout << std::setw(10) << std::right << "CALL PRICE" << " "; - cout << std::setw(10) << std::right << "I-Ratio" << " "; - cout << std::setw(10) << std::right << "M-Ratio" << "\n"; - cout << string(100, '=') << std::endl; - auto cur = price_idx.begin(); - while( cur != price_idx.end() ) - { - cout << std::setw( 10 ) << std::left << cur->seller(db).name << " "; - cout << std::setw( 16 ) << std::right << pretty( cur->amount_for_sale() ) << " "; - cout << std::setw( 16 ) << std::right << pretty( cur->get_collateral() ) << " "; - cout << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " "; - cout << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " "; - cout << std::setw( 10 ) << std::right << (cur->call_price).to_real() << " "; - cout << std::setw( 10 ) << std::right << (cur->initial_collateral_ratio)/double(1000) << " "; - cout << std::setw( 10 ) << std::right << (cur->maintenance_collateral_ratio)/double(1000) << " "; - cout << "\n"; - ++cur; - } -} int64_t database_fixture::get_balance( account_id_type account, asset_id_type a )const { diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 66b2ceeb..803fc6ac 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -147,20 +147,6 @@ struct database_fixture { const asset_object& create_user_issued_asset( const string& name ); void issue_uia( const account_object& recipient, asset amount ); - const short_order_object* create_short( - account_id_type seller, - const asset& amount_to_sell, - const asset& collateral_provided, - uint16_t initial_collateral_ratio = 2000, - uint16_t maintenance_collateral_ratio = 1750 - ); - const short_order_object* create_short( - const account_object& seller, - const asset& amount_to_sell, - const asset& collateral_provided, - uint16_t initial_collateral_ratio = 2000, - uint16_t maintenance_collateral_ratio = 1750 - ); const account_object& create_account( const string& name, @@ -197,7 +183,6 @@ struct database_fixture { const limit_order_object* create_sell_order( account_id_type user, const asset& amount, const asset& recv ); const limit_order_object* create_sell_order( const account_object& user, const asset& amount, const asset& recv ); asset cancel_limit_order( const limit_order_object& order ); - asset cancel_short_order( const short_order_object& order ); void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() ); void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); @@ -206,11 +191,9 @@ struct database_fixture { void upgrade_to_lifetime_member( const account_object& account ); void print_market( const string& syma, const string& symb )const; string pretty( const asset& a )const; - void print_short_order( const short_order_object& cur )const; void print_limit_order( const limit_order_object& cur )const; void print_call_orders( )const; void print_joint_market( const string& syma, const string& symb )const; - void print_short_market( const string& syma, const string& symb )const; 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; }; diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index c74e182d..31d3b98d 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -464,53 +464,6 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) } } -/** - * Orders should specify a valid expiration time and they will ba automatically canceled if not filled by that time. - * This feature allows people to safely submit orders that have a limited lifetime, which is essential to some - * traders. - */ -BOOST_FIXTURE_TEST_CASE( short_order_expiration, database_fixture ) -{ try { - //Get a sane head block time - generate_block(); - - auto* test = &create_bitasset("TEST"); - auto* core = &asset_id_type()(db); - auto* nathan = &create_account("nathan"); - auto* genesis = &account_id_type()(db); - - transfer(*genesis, *nathan, core->amount(50000)); - - BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 ); - - short_order_create_operation op; - op.seller = nathan->id; - op.amount_to_sell = test->amount(500); - op.collateral = core->amount(500); - op.expiration = db.head_block_time() + fc::seconds(10); - trx.operations.push_back(op); - auto ptrx = db.push_transaction(trx, ~0); - - BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); - - auto ptrx_id = ptrx.operation_results.back().get(); - auto short_index = db.get_index_type().indices(); - auto short_itr = short_index.begin(); - BOOST_REQUIRE( short_itr != short_index.end() ); - BOOST_REQUIRE( short_itr->id == ptrx_id ); - BOOST_REQUIRE( db.find_object(short_itr->id) ); - BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); - auto id = short_itr->id; - - generate_blocks(op.expiration); - test = &get_asset("TEST"); - core = &asset_id_type()(db); - nathan = &get_account("nathan"); - genesis = &account_id_type()(db); - - BOOST_CHECK(db.find_object(id) == nullptr); - BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 50000 ); -} FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture ) { try { @@ -606,6 +559,8 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) { try { + FC_ASSERT( "TODO" ); + /* auto private_key = generate_private_key("genesis"); account_id_type nathan_id = create_account("nathan").get_id(); account_id_type shorter1_id = create_account("shorter1").get_id(); @@ -726,6 +681,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) BOOST_CHECK(db.find(settle_id)); BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 5878); BOOST_CHECK(!db.get_index_type().indices().empty()); + */ } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index e3bc8c00..525aed07 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -314,18 +314,12 @@ BOOST_AUTO_TEST_CASE( update_mia ) pop.publisher = account_id_type(1); price_feed feed; feed.call_limit = price(bit_usd.amount(5), bit_usd.amount(5)); - feed.short_limit = feed.call_limit; REQUIRE_THROW_WITH_VALUE(pop, feed, feed); feed.call_limit = price(bit_usd.amount(5), asset(5)); - feed.short_limit = ~feed.call_limit; REQUIRE_THROW_WITH_VALUE(pop, feed, feed); - feed.short_limit = price(asset(4), bit_usd.amount(5)); - REQUIRE_THROW_WITH_VALUE(pop, feed, feed); - std::swap(feed.call_limit, feed.short_limit); pop.feed = feed; REQUIRE_THROW_WITH_VALUE(pop, feed.max_margin_period_sec, 0); REQUIRE_THROW_WITH_VALUE(pop, feed.required_maintenance_collateral, 0); - REQUIRE_THROW_WITH_VALUE(pop, feed.required_initial_collateral, 500); trx.operations.back() = pop; db.push_transaction(trx, ~0); } @@ -349,129 +343,6 @@ BOOST_AUTO_TEST_CASE( update_mia ) } } -BOOST_AUTO_TEST_CASE( create_short_test ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 100 ) ); // 1:1 price - BOOST_REQUIRE( first_short != nullptr ); - BOOST_REQUIRE( create_short( shorter_account, bitusd.amount(100), asset( 200 ) ) ); // 1:2 price - BOOST_REQUIRE( create_short( shorter_account, bitusd.amount(100), asset( 300 ) ) ); // 1:3 price - BOOST_REQUIRE_EQUAL( get_balance(shorter_account, asset_id_type()(db) ), 10000-600 ); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} -BOOST_AUTO_TEST_CASE( cancel_short_test ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 100 ) ); // 1:1 price - BOOST_REQUIRE( first_short != nullptr ); - BOOST_REQUIRE( create_short( shorter_account, bitusd.amount(100), asset( 200 ) ) ); // 1:2 price - BOOST_REQUIRE( create_short( shorter_account, bitusd.amount(100), asset( 300 ) ) ); // 1:3 price - BOOST_REQUIRE_EQUAL( get_balance(shorter_account, asset_id_type()(db) ), 10000-600 ); - auto refund = cancel_short_order( *first_short ); - BOOST_REQUIRE_EQUAL( get_balance(shorter_account, asset_id_type()(db) ), 10000-500 ); - FC_ASSERT( refund == asset(100) ); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( match_short_now_exact ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - auto buy_order = create_sell_order( buyer_account, asset(200), bitusd.amount(100) ); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - BOOST_REQUIRE( first_short == nullptr ); - print_call_orders(); - //print_short_market("",""); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( dont_match_short ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - //print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - //print_short_market("",""); - BOOST_REQUIRE( first_short && second_short && third_short ); - //print_joint_market("",""); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( match_all_short_with_surplus_collaterl ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - //auto buy_order = create_sell_order( buyer_account, asset(200), bitusd.amount(101) ); - auto buy_order = create_sell_order( buyer_account, asset(300), bitusd.amount(100) ); - print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - print_short_market("",""); - BOOST_REQUIRE( !first_short ); - //print_short_market("",""); - print_call_orders(); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - BOOST_AUTO_TEST_CASE( create_uia ) { @@ -916,7 +787,6 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) asset_publish_feed_operation op({asset(), active_witnesses[0]}); op.asset_id = bit_usd.get_id(); op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(10)); // We'll expire margins after a month op.feed.max_margin_period_sec = fc::days(30).to_seconds(); // Accept defaults for required collateral @@ -938,38 +808,29 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db); BOOST_CHECK(bitasset.current_feed.call_limit.to_real() == GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = active_witnesses[1]; op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(20)); op.feed.max_margin_period_sec = fc::days(10).to_seconds(); trx.operations.back() = op; db.push_transaction(trx, ~0); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 20.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = active_witnesses[2]; op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(10)); op.feed.max_margin_period_sec = fc::days(100).to_seconds(); // But this delegate is an idiot. - op.feed.required_initial_collateral = 1001; op.feed.required_maintenance_collateral = 1000; trx.operations.back() = op; db.push_transaction(trx, ~0); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); } catch (const fc::exception& e) { edump((e.to_detail_string())); @@ -977,241 +838,12 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) } } -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( limit_match_existing_short_exact ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - //print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - //print_short_market("",""); - BOOST_REQUIRE( first_short && second_short && third_short ); - //print_joint_market("",""); - auto unmatched_order = create_sell_order( buyer_account, asset(200), bitusd.amount(100) ); - //print_joint_market("",""); - BOOST_REQUIRE( !unmatched_order ); - // now it shouldn't fill - unmatched_order = create_sell_order( buyer_account, asset(200), bitusd.amount(100) ); - //print_joint_market("",""); - BOOST_REQUIRE( unmatched_order ); - BOOST_CHECK( unmatched_order->amount_for_sale() == asset(200) ); - BOOST_CHECK( unmatched_order->amount_to_receive() == bitusd.amount(100) ); - BOOST_CHECK( second_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( third_short->amount_for_sale() == bitusd.amount(100) ); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( limit_match_existing_short_partial_exact_price ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - //print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - //print_short_market("",""); - BOOST_REQUIRE( first_short && second_short && third_short ); - //print_joint_market("",""); - auto unmatched_order = create_sell_order( buyer_account, asset(100), bitusd.amount(50) ); - //print_joint_market("",""); - BOOST_REQUIRE( !unmatched_order ); - BOOST_CHECK( first_short->amount_for_sale() == bitusd.amount(50) ); - BOOST_CHECK( first_short->get_collateral() == asset(100) ); - BOOST_CHECK( second_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( third_short->amount_for_sale() == bitusd.amount(100) ); - - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( limit_match_existing_short_partial_over_price ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - //print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - BOOST_REQUIRE( first_short && second_short && third_short ); - //print_joint_market("",""); - auto unmatched_order = create_sell_order( buyer_account, asset(100), bitusd.amount(40) ); - //print_joint_market("",""); - BOOST_REQUIRE( !unmatched_order ); - BOOST_CHECK( first_short->amount_for_sale() == bitusd.amount(50) ); - BOOST_CHECK( first_short->get_collateral() == asset(100) ); - BOOST_CHECK( second_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( third_short->amount_for_sale() == bitusd.amount(100) ); - - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( limit_match_multiple_existing_short_partial_over_price ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - //print_market("",""); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto next_short = create_short( shorter_account, bitusd.amount(100), asset( 210 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - //print_short_market("",""); - BOOST_REQUIRE( first_short && second_short && third_short ); - auto unmatched_order = create_sell_order( buyer_account, asset(200+115), bitusd.amount(150) ); - // print_joint_market("",""); - BOOST_REQUIRE( !unmatched_order ); - //wdump( (next_short->amount_for_sale().amount)(next_short->get_collateral().amount) ); - BOOST_CHECK( next_short->amount_for_sale() == bitusd.amount(46) ); - BOOST_CHECK( next_short->get_collateral() == asset(97) ); - BOOST_CHECK( second_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( third_short->amount_for_sale() == bitusd.amount(100) ); - print_call_orders(); - - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -/** - * Assume there exists an offer to buy BITUSD - * Create a short that exactly matches that offer at a price 2:1 - */ -BOOST_AUTO_TEST_CASE( limit_dont_match_existing_short_partial_over_price ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter_account = create_account( "shorter" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - create_sell_order( buyer_account, asset(125), bitusd.amount(100) ); - create_sell_order( buyer_account, asset(150), bitusd.amount(100) ); - auto buy_order = create_sell_order( buyer_account, asset(100), bitusd.amount(100) ); - BOOST_REQUIRE( buy_order ); - auto first_short = create_short( shorter_account, bitusd.amount(100), asset( 200 ) ); // 1:1 price - auto second_short = create_short( shorter_account, bitusd.amount(100), asset( 300 ) ); // 1:1 price - auto third_short = create_short( shorter_account, bitusd.amount(100), asset( 400 ) ); // 1:1 price - //print_short_market("",""); - BOOST_REQUIRE( first_short && second_short && third_short ); - //print_joint_market("",""); - auto unmatched_order = create_sell_order( buyer_account, asset(100), bitusd.amount(60) ); - BOOST_REQUIRE( unmatched_order ); - BOOST_CHECK( first_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( first_short->get_collateral() == asset(200) ); - BOOST_CHECK( second_short->amount_for_sale() == bitusd.amount(100) ); - BOOST_CHECK( third_short->amount_for_sale() == bitusd.amount(100) ); - }catch ( const fc::exception& e ) - { - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; - } -} - -BOOST_AUTO_TEST_CASE( multiple_shorts_matching_multiple_bids_in_order ) -{ try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const account_object& shorter1_account = create_account( "shorter1" ); - const account_object& shorter2_account = create_account( "shorter2" ); - const account_object& shorter3_account = create_account( "shorter3" ); - const account_object& buyer_account = create_account( "buyer" ); - transfer( genesis_account(db), shorter1_account, asset( 10000 ) ); - transfer( genesis_account(db), shorter2_account, asset( 10000 ) ); - transfer( genesis_account(db), shorter3_account, asset( 10000 ) ); - transfer( genesis_account(db), buyer_account, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer_account, asset(125), bitusd.amount(100) ) ); - BOOST_REQUIRE( create_sell_order( buyer_account, asset(150), bitusd.amount(100) ) ); - BOOST_REQUIRE( create_sell_order( buyer_account, asset(200), bitusd.amount(100) ) ); - print_joint_market("",""); - BOOST_REQUIRE( !create_short( shorter1_account, bitusd.amount(100), asset( 200 ) ) ); - BOOST_REQUIRE( !create_short( shorter2_account, bitusd.amount(100), asset( 150 ) ) ); - BOOST_REQUIRE( !create_short( shorter3_account, bitusd.amount(100), asset( 125 ) ) ); - print_call_orders(); - - auto& index = db.get_index_type().indices().get(); - BOOST_CHECK(index.find(boost::make_tuple(buyer_account.id, bitusd.id)) == index.end()); - BOOST_CHECK(index.find(boost::make_tuple(shorter1_account.id, bitusd.id)) != index.end()); - BOOST_CHECK(index.find(boost::make_tuple(shorter1_account.id, bitusd.id))->get_debt() == bitusd.amount(100) ); - BOOST_CHECK(index.find(boost::make_tuple(shorter1_account.id, bitusd.id))->call_price == price(asset(300), bitusd.amount(100)) ); - BOOST_CHECK(index.find(boost::make_tuple(shorter2_account.id, bitusd.id)) != index.end()); - BOOST_CHECK(index.find(boost::make_tuple(shorter2_account.id, bitusd.id))->get_debt() == bitusd.amount(100) ); - BOOST_CHECK(index.find(boost::make_tuple(shorter3_account.id, bitusd.id)) != index.end()); - BOOST_CHECK(index.find(boost::make_tuple(shorter3_account.id, bitusd.id))->get_debt() == bitusd.amount(100) ); -}catch ( const fc::exception& e ) -{ - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; -} } BOOST_AUTO_TEST_CASE( full_cover_test ) { try { - INVOKE(multiple_shorts_matching_multiple_bids_in_order); + FC_ASSERT( "Reimplement with new borrowing semantics" ); + /* const asset_object& bit_usd = get_asset("BITUSD"); const asset_object& core = asset_id_type()(db); const account_object& debt_holder = get_account("shorter1"); @@ -1239,6 +871,7 @@ BOOST_AUTO_TEST_CASE( full_cover_test ) BOOST_CHECK_EQUAL(get_balance(debt_holder, bit_usd), 0); BOOST_CHECK(index.find(boost::make_tuple(debt_holder.id, bit_usd.id)) == index.end()); + */ } catch( fc::exception& e) { edump((e.to_detail_string())); throw; @@ -1248,7 +881,7 @@ BOOST_AUTO_TEST_CASE( full_cover_test ) BOOST_AUTO_TEST_CASE( partial_cover_test ) { try { - INVOKE(multiple_shorts_matching_multiple_bids_in_order); + FC_ASSERT( "Reimplement with new borrowing semantics" ); const asset_object& bit_usd = get_asset("BITUSD"); const asset_object& core = asset_id_type()(db); const account_object& debt_holder = get_account("shorter1"); @@ -1319,251 +952,6 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) } } -BOOST_AUTO_TEST_CASE( limit_order_matching_mix_of_shorts_and_limits ) -{ try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& shorter3 = create_account( "shorter3" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - const account_object& buyer3 = create_account( "buyer3" ); - - transfer( genesis_account(db), shorter1, core.amount( 10000 ) ); - transfer( genesis_account(db), shorter2, core.amount( 10000 ) ); - transfer( genesis_account(db), shorter3, core.amount( 10000 ) ); - transfer( genesis_account(db), buyer1, core.amount( 10000 ) ); - transfer( genesis_account(db), buyer2, core.amount( 10000 ) ); - transfer( genesis_account(db), buyer3, core.amount( 10000 ) ); - - // create some BitUSD - BOOST_REQUIRE( create_sell_order( buyer1, core.amount(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), core.amount(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - // create a mixture of BitUSD sells and shorts - BOOST_REQUIRE( create_short( shorter1, bitusd.amount(100), core.amount(125) ) ); - BOOST_REQUIRE( create_sell_order( buyer1, bitusd.amount(100), core.amount(150) ) ); - BOOST_REQUIRE( create_short( shorter2, bitusd.amount(100), core.amount(200) ) ); - BOOST_REQUIRE( create_sell_order( buyer1, bitusd.amount(100), core.amount(225) ) ); - BOOST_REQUIRE( create_short( shorter3, bitusd.amount(100), core.amount(250) ) ); - - print_joint_market("",""); // may have bugs - - // buy up everything but the highest order - auto unfilled_order = create_sell_order( buyer2, core.amount(700), bitusd.amount(311) ); - if( unfilled_order ) wdump((*unfilled_order)); - print_joint_market("",""); - if( unfilled_order ) wdump((*unfilled_order)); - BOOST_REQUIRE( !unfilled_order ); - BOOST_REQUIRE_EQUAL( get_balance(buyer2, bitusd), 396 ); - - print_joint_market("",""); - print_call_orders(); - -}catch ( const fc::exception& e ) -{ - elog( "${e}", ("e", e.to_detail_string() ) ); - throw; -} } - -BOOST_AUTO_TEST_CASE( big_short ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - const account_object& buyer3 = create_account( "buyer3" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - transfer( genesis_account(db), buyer3, asset( 10000 ) ); - - create_sell_order(buyer1, core.amount(500), bitusd.amount(500)); - create_sell_order(buyer2, core.amount(500), bitusd.amount(600)); - auto unmatched_buy3 = create_sell_order(buyer3, core.amount(500), bitusd.amount(700)); - - auto unmatched = create_short(shorter1, bitusd.amount(1300), core.amount(800)); - if( unmatched ) wdump((*unmatched)); - - BOOST_CHECK( !unmatched ); - BOOST_CHECK( unmatched_buy3 ); - BOOST_CHECK_EQUAL( unmatched_buy3->amount_for_sale().amount.value, 358); - // The extra 1 is rounding leftovers; it has to go somewhere. - BOOST_CHECK_EQUAL( unmatched_buy3->amount_to_receive().amount.value, 501); - // All three buyers offered 500 CORE for varying numbers of dollars. - BOOST_CHECK_EQUAL(get_balance(buyer1, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer2, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer3, core), 9500); - // Sans the 1% market fee, buyer1 got 500 USD, buyer2 got 600 USD - BOOST_CHECK_EQUAL(get_balance(buyer1, bitusd), 495); - BOOST_CHECK_EQUAL(get_balance(buyer2, bitusd), 594); - // Buyer3 wanted 700 USD, but the shorter only had 1300-500-600=200 left, so buyer3 got 200. - BOOST_CHECK_EQUAL(get_balance(buyer3, bitusd), 198); - // Shorter1 never had any USD, so he shouldn't have any now. He paid 800 CORE, so he should have 9200 left. - BOOST_CHECK_EQUAL(get_balance(shorter1, bitusd), 0); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9200); - - const auto& call_index = db.get_index_type().indices().get(); - const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); - BOOST_CHECK(call_itr != call_index.end()); - const call_order_object& call_object = *call_itr; - BOOST_CHECK(call_object.borrower == shorter1.id); - // 800 from shorter1, 500 from buyer1 and buyer2 each, 500/700*200 from buyer3 totals 1942 - BOOST_CHECK_EQUAL(call_object.collateral.value, 1942); - // Shorter1 sold 1300 USD. Make sure that's recorded accurately. - BOOST_CHECK_EQUAL(call_object.debt.value, 1300); - // 13 USD was paid in market fees. - BOOST_CHECK_EQUAL(bitusd.dynamic_asset_data_id(db).accumulated_fees.value, 13); - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( big_short2 ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - const account_object& buyer3 = create_account( "buyer3" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - transfer( genesis_account(db), buyer3, asset( 10000 ) ); - - create_sell_order(buyer1, core.amount(500), bitusd.amount(500)); - create_sell_order(buyer2, core.amount(500), bitusd.amount(600)); - auto unmatched_buy3 = create_sell_order(buyer3, core.amount(500), bitusd.amount(700)); - - //We want to perfectly match the first two orders, so that's 1100 USD at 500/600 = 916 - auto unmatched = create_short(shorter1, bitusd.amount(1100), core.amount(916)); - if( unmatched ) wdump((*unmatched)); - - BOOST_CHECK( !unmatched ); - BOOST_CHECK( unmatched_buy3 ); - BOOST_CHECK_EQUAL( unmatched_buy3->amount_for_sale().amount.value, 500); - // The extra 1 is rounding leftovers; it has to go somewhere. - BOOST_CHECK_EQUAL( unmatched_buy3->amount_to_receive().amount.value, 700); - // All three buyers offered 500 CORE for varying numbers of dollars. - BOOST_CHECK_EQUAL(get_balance(buyer1, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer2, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer3, core), 9500); - // Sans the 1% market fee, buyer1 got 500 USD, buyer2 got 600 USD - BOOST_CHECK_EQUAL(get_balance(buyer1, bitusd), 495); - BOOST_CHECK_EQUAL(get_balance(buyer2, bitusd), 594); - // Buyer3's order wasn't matched. He should have no USD. - BOOST_CHECK_EQUAL(get_balance(buyer3, bitusd), 0); - // Shorter1 never had any USD, so he shouldn't have any now. He paid 916 CORE, so he should have 9084 left. - BOOST_CHECK_EQUAL(get_balance(shorter1, bitusd), 0); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9084); - - const auto& call_index = db.get_index_type().indices().get(); - const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); - BOOST_CHECK(call_itr != call_index.end()); - const call_order_object& call_object = *call_itr; - BOOST_CHECK(call_object.borrower == shorter1.id); - // 916 from shorter1, 500 from buyer1 and buyer2 each adds to 1916 - BOOST_CHECK_EQUAL(call_object.collateral.value, 1916); - // Shorter1 sold 1100 USD. Make sure that's recorded accurately. - BOOST_CHECK_EQUAL(call_object.debt.value, 1100); - // 11 USD was paid in market fees. - BOOST_CHECK_EQUAL(bitusd.dynamic_asset_data_id(db).accumulated_fees.value, 11); - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( big_short3 ) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - const account_object& buyer3 = create_account( "buyer3" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - transfer( genesis_account(db), buyer3, asset( 10000 ) ); - - create_short(shorter1, bitusd.amount(1300), core.amount(800)); - - print_joint_market("",""); - - create_sell_order(buyer1, core.amount(500), bitusd.amount(500)); - create_sell_order(buyer2, core.amount(500), bitusd.amount(600)); - auto unmatched_buy3 = create_sell_order(buyer3, core.amount(500), bitusd.amount(700)); - - print_joint_market("",""); - - BOOST_CHECK( unmatched_buy3 ); - BOOST_CHECK_EQUAL( unmatched_buy3->amount_for_sale().amount.value, 500); - BOOST_CHECK_EQUAL( unmatched_buy3->amount_to_receive().amount.value, 700); - BOOST_CHECK_EQUAL(get_balance(buyer1, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer2, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer3, core), 9500); - BOOST_CHECK_EQUAL(get_balance(buyer1, bitusd), 804); - BOOST_CHECK_EQUAL(get_balance(buyer2, bitusd), 484); - BOOST_CHECK_EQUAL(get_balance(buyer3, bitusd), 0); - BOOST_CHECK_EQUAL(get_balance(shorter1, bitusd), 0); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9200); - - const auto& call_index = db.get_index_type().indices().get(); - const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); - BOOST_CHECK(call_itr != call_index.end()); - const call_order_object& call_object = *call_itr; - BOOST_CHECK(call_object.borrower == shorter1.id); - BOOST_CHECK_EQUAL(call_object.collateral.value, 1600); - BOOST_CHECK_EQUAL(call_object.debt.value, 1300); - BOOST_CHECK_EQUAL(bitusd.dynamic_asset_data_id(db).accumulated_fees.value, 12); - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -/** - * Originally, this test exposed a bug in vote tallying causing the total number of votes to exceed the number of - * voting shares. This bug was resolved in commit 489b0dafe981c3b96b17f23cfc9ddc348173c529 - */ -BOOST_AUTO_TEST_CASE(break_vote_count) -{ - try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& buyer1 = create_account( "buyer1" ); - - transfer( genesis_account(db), shorter1, asset( 100000000 ) ); - transfer( genesis_account(db), buyer1, asset( 100000000 ) ); - - create_short(shorter1, bitusd.amount(1300), core.amount(800)); - - create_sell_order(buyer1, core.amount(500), bitusd.amount(500)); - - BOOST_CHECK_EQUAL(get_balance(buyer1, core), 99999500); - BOOST_CHECK_EQUAL(get_balance(buyer1, bitusd), 804); - BOOST_CHECK_EQUAL(get_balance(shorter1, bitusd), 0); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 99999200); - - create_sell_order(shorter1, core.amount(90000000), bitusd.amount(1)); - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} /** * Create an order such that when the trade executes at the @@ -1600,6 +988,8 @@ BOOST_AUTO_TEST_CASE( trade_amount_equals_zero ) BOOST_AUTO_TEST_CASE( margin_call_limit_test ) { try { + FC_ASSERT( "TODO - Reimplement with new short semantics" ); + /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1663,6 +1053,7 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) BOOST_CHECK_THROW(db.get_object(below_id), fc::exception); BOOST_CHECK(call.get_debt() == bitusd.amount(210)); BOOST_CHECK(call.get_collateral() == core.amount(803)); + */ } catch( const fc::exception& e) { edump((e.to_detail_string())); throw; @@ -1671,6 +1062,8 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) { try { + FC_ASSERT( "TODO - Reimplement with new short semantics" ); + /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1698,6 +1091,7 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) ); if( unmatched ) edump((*unmatched)); BOOST_REQUIRE( unmatched ); + */ } catch( const fc::exception& e) { edump((e.to_detail_string())); @@ -1707,6 +1101,8 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) { try { + FC_ASSERT( "TODO - Reimplement with new short semantics" ); + /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1733,6 +1129,7 @@ BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) ); if( unmatched ) edump((*unmatched)); BOOST_REQUIRE( unmatched ); + */ } catch( const fc::exception& e) { edump((e.to_detail_string())); @@ -1742,6 +1139,8 @@ BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) BOOST_AUTO_TEST_CASE( margin_call_short_test ) { try { + FC_ASSERT( "TODO - Reimplement with new short semantics" ); + /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1769,6 +1168,7 @@ BOOST_AUTO_TEST_CASE( margin_call_short_test ) auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); if( unmatched ) edump((*unmatched)); BOOST_REQUIRE( !unmatched ); + */ } catch( const fc::exception& e) { edump((e.to_detail_string())); @@ -1778,6 +1178,8 @@ BOOST_AUTO_TEST_CASE( margin_call_short_test ) BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) { try { + FC_ASSERT( "TODO - Reimplement with new short semantics" ); + /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1805,6 +1207,7 @@ BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); if( unmatched ) edump((*unmatched)); BOOST_REQUIRE( unmatched ); + */ } catch( const fc::exception& e) { edump((e.to_detail_string())); @@ -2032,66 +1435,12 @@ BOOST_AUTO_TEST_CASE( unimp_bulk_discount_test ) */ BOOST_AUTO_TEST_CASE( margin_call_black_swan ) { try { - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(30) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - verify_asset_supplies(); - ilog( "=================================== START===================================\n\n"); - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover, except the cover does not - // have enough collateral and thus a black swan event should occur. - auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(5000) ); - if( unmatched ) edump((*unmatched)); - /** black swans should cause all of the bitusd to be converted into backing - * asset at the price of the least collateralized call position at the time. This - * means that this sell order would be removed. - */ - BOOST_REQUIRE( !unmatched ); - + FC_ASSERT( "TODO - Reimplement with new short semantics" ); } catch( const fc::exception& e) { edump((e.to_detail_string())); throw; } } -/** - * This test sets up a far more complex blackswan scenerio where the - * BitUSD exists in the following places: - * - * 0) Limit Orders for the BitAsset - * 1) Limit Orders for UIA Assets - * 2) Short Orders for BitAsset backed by BitUSD - * 3) Call Orders for BitAsset backed by BitUSD - * 4) Issuer Fees - * - * This test should fail until the black swan handling code can - * perform a recursive blackswan for any other BitAssets that use - * BitUSD as collateral. - */ -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_advanced_black_swan, 1 ) -BOOST_AUTO_TEST_CASE( unimp_advanced_black_swan ) -{ - BOOST_FAIL( "not implemented" ); -} - - /** * Assume the referrer gets 99% of transaction fee diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 67f11d19..1d1db4b1 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -337,7 +337,6 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) asset_publish_feed_operation op({asset(), vikram_id}); op.asset_id = bit_usd_id; op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(10)); // We'll expire margins after a month op.feed.max_margin_period_sec = fc::days(30).to_seconds(); // Accept defaults for required collateral @@ -346,37 +345,28 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db); BOOST_CHECK(bitasset.current_feed.call_limit.to_real() == GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = ben_id; op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(20)); op.feed.max_margin_period_sec = fc::days(10).to_seconds(); trx.operations.back() = op; db.push_transaction(trx, ~0); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 20.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = dan_id; op.feed.call_limit = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); - op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(10)); op.feed.max_margin_period_sec = fc::days(100).to_seconds(); - op.feed.required_initial_collateral = 1001; op.feed.required_maintenance_collateral = 1000; trx.operations.back() = op; db.push_transaction(trx, ~0); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); - BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.max_margin_period_sec == fc::days(30).to_seconds()); - BOOST_CHECK(bitasset.current_feed.required_initial_collateral == GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO); BOOST_CHECK(bitasset.current_feed.required_maintenance_collateral == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = nathan_id; @@ -451,6 +441,8 @@ BOOST_AUTO_TEST_CASE( witness_create ) BOOST_AUTO_TEST_CASE( global_settle_test ) { try { ACTORS((nathan)(ben)(valentine)(dan)); + FC_ASSERT( !"TODO - Reimplement this" ); + /* asset_id_type bit_usd_id = create_bitasset("BITUSD", nathan_id, 100, global_settle | charge_market_fee).get_id(); transfer(genesis_account, ben_id, asset(10000)); transfer(genesis_account, valentine_id, asset(10000)); @@ -489,6 +481,7 @@ BOOST_AUTO_TEST_CASE( global_settle_test ) BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10091); BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9850); + */ } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( worker_create_test ) @@ -680,6 +673,8 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) { try { + FC_ASSERT( !"TODO - Reimplement this" ); + /* auto private_key = generate_private_key("genesis"); account_id_type nathan_id = create_account("nathan").get_id(); account_id_type shorter1_id = create_account("shorter1").get_id(); @@ -735,7 +730,6 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) price_feed feed; feed.settlement_price = price(asset(1),asset(1, bit_usd)); feed.call_limit = price::min(0, bit_usd); - feed.short_limit = price::min(bit_usd, 0); pop.feed = feed; trx.operations.push_back(pop); } @@ -793,6 +787,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) BOOST_CHECK(db.get_index_type().indices().empty()); BOOST_CHECK_EQUAL(get_balance(nathan_id, bit_usd), bit_usd(db).dynamic_data(db).current_supply.value); } + */ } FC_LOG_AND_RETHROW() } // TODO: Write linear VBO tests From 377432453fd41f4ee12a7ad7a9b66c64c2d3f600 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Tue, 16 Jun 2015 14:42:02 -0400 Subject: [PATCH 010/354] Lay to rest #16 It's finally done. --- libraries/chain/account_evaluator.cpp | 82 ++++++++----------- libraries/chain/account_object.cpp | 15 ++++ libraries/chain/db_init.cpp | 6 +- libraries/chain/db_maint.cpp | 15 ++-- libraries/chain/evaluator.cpp | 10 ++- .../include/graphene/chain/account_object.hpp | 42 ++++++---- .../chain/include/graphene/chain/config.hpp | 1 - .../include/graphene/chain/evaluator.hpp | 1 + .../include/graphene/chain/operations.hpp | 33 ++++---- libraries/chain/operations.cpp | 11 ++- .../account_history_plugin.cpp | 2 +- libraries/utilities/git_revision.cpp.in | 4 +- libraries/wallet/wallet.cpp | 22 ++--- tests/common/database_fixture.cpp | 16 ++-- tests/tests/authority_tests.cpp | 27 +++--- tests/tests/block_tests.cpp | 4 +- tests/tests/fee_tests.cpp | 2 +- tests/tests/operation_tests.cpp | 18 ++-- tests/tests/operation_tests2.cpp | 22 ++--- 19 files changed, 183 insertions(+), 150 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index c8ef51f8..9d059393 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -25,18 +25,19 @@ namespace graphene { namespace chain { void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) { try { database& d = db(); - FC_ASSERT( d.find_object(op.voting_account) ); - FC_ASSERT( is_relative(op.memo_key) || d.find_object(op.memo_key) ); + FC_ASSERT( d.find_object(op.options.voting_account) ); + FC_ASSERT( is_relative(op.options.memo_key) || d.find_object(op.options.memo_key) ); FC_ASSERT( fee_paying_account->is_lifetime_member() ); FC_ASSERT( op.referrer(d).is_member(d.head_block_time()) ); const auto& global_props = d.get_global_properties(); - uint32_t max_vote_id = global_props.next_available_vote_id; const auto& chain_params = global_props.parameters; - FC_ASSERT( op.num_witness <= chain_params.maximum_witness_count ); - FC_ASSERT( op.num_committee <= chain_params.maximum_committee_count ); + FC_ASSERT( op.owner.auths.size() <= chain_params.maximum_authority_membership ); FC_ASSERT( op.active.auths.size() <= chain_params.maximum_authority_membership ); + check_relative_ids(op.owner); + check_relative_ids(op.active); + FC_ASSERT( d.find(key_id_type(get_relative_id(op.options.memo_key))) ); for( auto id : op.owner.auths ) { FC_ASSERT( is_relative(id.first) || d.find_object(id.first) ); @@ -45,18 +46,22 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio { FC_ASSERT( is_relative(id.first) || d.find_object(id.first) ); } + + uint32_t max_vote_id = global_props.next_available_vote_id; + FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count ); + FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count ); safe counts[vote_id_type::VOTE_TYPE_COUNT]; - for( auto id : op.vote ) + for( auto id : op.options.votes ) { FC_ASSERT( id < max_vote_id ); counts[id.type()]++; } - FC_ASSERT(counts[vote_id_type::witness] <= op.num_witness, + FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness, "", - ("count", counts[vote_id_type::witness])("num", op.num_witness)); - FC_ASSERT(counts[vote_id_type::committee] <= op.num_committee, + ("count", counts[vote_id_type::witness])("num", op.options.num_witness)); + FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee, "", - ("count", counts[vote_id_type::committee])("num", op.num_committee)); + ("count", counts[vote_id_type::committee])("num", op.options.num_committee)); auto& acnt_indx = d.get_index_type(); if( op.name.size() ) @@ -65,17 +70,6 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio FC_ASSERT( current_account_itr == acnt_indx.indices().get().end() ); } - // TODO: this check can be removed after GRAPHENE_LEGACY_NAME_IMPORT_PERIOD - // legacy account check - if( d.get_dynamic_global_properties().head_block_number < GRAPHENE_LEGACY_NAME_IMPORT_PERIOD ) - { - auto legacy_account_itr = acnt_indx.indices().get().find( "bts-"+op.name ); - if( legacy_account_itr != acnt_indx.indices().get().end() ) - { - FC_ASSERT( fee_paying_account->id == legacy_account_itr->id ); - } - } - // verify child account authority auto pos = op.name.find( '/' ); if( pos != string::npos ) @@ -93,9 +87,6 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio object_id_type account_create_evaluator::do_apply( const account_create_operation& o ) { try { - auto owner = resolve_relative_ids( o.owner ); - auto active = resolve_relative_ids( o.active ); - const auto& stats_obj = db().create( [&]( account_statistics_object& ){ }); @@ -110,14 +101,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.referrer_rewards_percentage = o.referrer_percent; obj.name = o.name; - obj.owner = owner; - obj.active = active; + obj.owner = resolve_relative_ids(o.owner); + obj.active = resolve_relative_ids(o.active); obj.statistics = stats_obj.id; - obj.memo_key = get_relative_id(o.memo_key); - obj.voting_account = o.voting_account; - obj.votes = o.vote; - obj.num_witness = o.num_witness; - obj.num_committee = o.num_committee; + obj.options = o.options; + obj.options.memo_key = get_relative_id(obj.options.memo_key); }); return new_acnt_object.id; @@ -128,14 +116,12 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio { database& d = db(); - FC_ASSERT( !o.memo_key || is_relative(*o.memo_key) || db().find_object(*o.memo_key) ); - const auto& chain_params = db().get_global_properties().parameters; - FC_ASSERT( o.num_witness <= chain_params.maximum_witness_count ); - FC_ASSERT( o.num_committee <= chain_params.maximum_committee_count ); + if( o.owner ) { FC_ASSERT( o.owner->auths.size() <= chain_params.maximum_authority_membership ); + check_relative_ids(*o.owner); for( auto id : o.owner->auths ) { FC_ASSERT( is_relative(id.first) || db().find(id.first) ); @@ -144,6 +130,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio if( o.active ) { FC_ASSERT( o.active->auths.size() <= chain_params.maximum_authority_membership ); + check_relative_ids(*o.active); for( auto id : o.active->auths ) { FC_ASSERT( is_relative(id.first) || db().find(id.first) ); @@ -152,10 +139,13 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); - if( o.vote ) + if( o.new_options ) { + FC_ASSERT( d.find(key_id_type(get_relative_id(o.new_options->memo_key))) ); + FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count ); + FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count ); uint32_t max_vote_id = d.get_global_properties().next_available_vote_id; - for( auto id : *o.vote ) + for( auto id : o.new_options->votes ) { FC_ASSERT( id < max_vote_id ); } @@ -165,15 +155,15 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio } void_result account_update_evaluator::do_apply( const account_update_operation& o ) { - db().modify( *acnt, [&]( account_object& a ){ - if( o.owner ) a.owner = *o.owner; - if( o.active ) a.active = *o.active; - if( o.voting_account ) a.voting_account = *o.voting_account; - if( o.memo_key ) a.memo_key = *o.memo_key; - if( o.vote ) a.votes = *o.vote; - a.num_witness = o.num_witness; - a.num_committee = o.num_committee; - }); + db().modify( *acnt, [&](account_object& a){ + if( o.owner ) a.owner = resolve_relative_ids(*o.owner); + if( o.active ) a.active = resolve_relative_ids(*o.active); + if( o.new_options ) + { + a.options = *o.new_options; + a.options.memo_key = get_relative_id(a.options.memo_key); + } + }); return void_result(); } diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 512974e6..37f07785 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -131,4 +131,19 @@ void account_statistics_object::process_fees(const account_object& a, database& } } +void account_object::options_type::validate() const +{ + auto needed_witnesses = num_witness; + auto needed_committee = num_committee; + + 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; + + FC_ASSERT( needed_witnesses == 0 && needed_committee == 0, + "May not specify fewer witnesses or committee members than the number voted for."); +} + } } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 57c3cd37..95530b79 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -151,7 +151,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation) n.owner.add_authority(genesis_key.get_id(), 1); n.owner.weight_threshold = 1; n.active = n.owner; - n.memo_key = genesis_key.id; + n.options.memo_key = genesis_key.id; n.statistics = genesis_statistics.id; }); @@ -262,7 +262,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation) auto mangle_to_name = [](const fc::static_variant& key) { string addr = string(key.which() == std::decay::type::tag
::value? key.get
() : key.get()); - string result = "bts"; + string result = "import"; string key_string = string(addr).substr(sizeof(GRAPHENE_ADDRESS_PREFIX)-1); for( char c : key_string ) { @@ -295,7 +295,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation) cop.registrar = account_id_type(1); cop.active = account_authority; cop.owner = account_authority; - cop.memo_key = key_id; + cop.options.memo_key = key_id; trx.operations.push_back(cop); trx.validate(); auto ptrx = apply_transaction(trx, ~0); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 23f16537..55a531ab 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -307,15 +307,16 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // Usually they're the same, but if the stake account has specified a voting_account, that account is the one // specifying the opinions. const account_object& opinion_account = - (stake_account.voting_account == account_id_type())? stake_account - : d.get(stake_account.voting_account); + (stake_account.options.voting_account == + account_id_type())? stake_account + : d.get(stake_account.options.voting_account); 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; - for( vote_id_type id : opinion_account.votes ) + for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); // if they somehow managed to specify an illegal offset, ignore it. @@ -323,9 +324,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._vote_tally_buffer[ offset ] += voting_stake; } - if( opinion_account.num_witness <= props.parameters.maximum_witness_count ) + if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count ) { - uint16_t offset = std::min(size_t(opinion_account.num_witness/2), + uint16_t offset = std::min(size_t(opinion_account.options.num_witness/2), d._witness_count_histogram_buffer.size() - 1); // votes for a number greater than maximum_witness_count // are turned into votes for maximum_witness_count. @@ -335,9 +336,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // parameter was lowered. d._witness_count_histogram_buffer[ offset ] += voting_stake; } - if( opinion_account.num_committee <= props.parameters.maximum_committee_count ) + if( opinion_account.options.num_committee <= props.parameters.maximum_committee_count ) { - uint16_t offset = std::min(size_t(opinion_account.num_committee/2), + uint16_t offset = std::min(size_t(opinion_account.options.num_committee/2), d._committee_count_histogram_buffer.size() - 1); // votes for a number greater than maximum_committee_count // are turned into votes for maximum_committee_count. diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 46117211..e390890e 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -115,7 +115,15 @@ namespace graphene { namespace chain { return rel_id; } - authority generic_evaluator::resolve_relative_ids( const authority& a )const + void generic_evaluator::check_relative_ids(const authority& a)const + { + for( const auto& item : a.auths ) + { + auto id = get_relative_id( item.first ); + FC_ASSERT( id.type() == key_object_type || id.type() == account_object_type ); + } + } + authority generic_evaluator::resolve_relative_ids(const authority& a)const { authority result; result.auths.reserve( a.auths.size() ); diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 0a3497d6..c1d7376a 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -144,6 +144,7 @@ class database; /// The account's name. This name must be unique among all account names on the graph. May not be empty. string name; + /** * The owner authority represents absolute control over the account. Usually the keys in this authority will * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes @@ -154,19 +155,30 @@ class database; /// The owner authority contains the hot keys of the account. This authority has control over nearly all /// operations the account may perform. authority active; - /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- - /// validated account activities. This field is here to prevent confusion if the active authority has zero or - /// multiple keys in it. - key_id_type memo_key; - /// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake - /// will be counted as voting for the referenced account's selected votes instead. - account_id_type voting_account; - uint16_t num_witness = 0; - uint16_t num_committee = 0; - /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this - /// account's balance of core asset. - flat_set votes; + /// These are the fields which can be updated by the active authority. + struct options_type { + /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- + /// validated account activities. This field is here to prevent confusion if the active authority has zero or + /// multiple keys in it. + object_id_type memo_key = key_id_type(); + key_id_type get_memo_key()const { return memo_key; } + /// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake + /// will be counted as voting for the referenced account's selected votes instead. + account_id_type voting_account; + + /// The number of active witnesses this account votes the blockchain should appoint + /// Must not exceed the actual number of witnesses voted for in @ref votes + uint16_t num_witness = 0; + /// The number of active committee members this account votes the blockchain should appoint + /// Must not exceed the actual number of committee members voted for in @ref votes + uint16_t num_committee = 0; + /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this + /// account's balance of core asset. + flat_set votes; + + void validate()const; + } options; /// The reference implementation records the account's statistics in a separate object. This field contains the /// ID of that object. @@ -293,8 +305,10 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (graphene::db::annotated_object), (membership_expiration_date)(registrar)(referrer)(lifetime_referrer) (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage) - (name)(owner)(active)(memo_key)(voting_account)(num_witness)(num_committee)(votes) - (statistics)(whitelisting_accounts)(blacklisting_accounts)(cashback_vb) ) + (name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts) + (cashback_vb) ) + +FC_REFLECT(graphene::chain::account_object::options_type, (memo_key)(voting_account)(num_witness)(num_committee)(votes)) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 507b5641..066a18e6 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -112,7 +112,6 @@ #define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 ) #define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 ) -#define GRAPHENE_LEGACY_NAME_IMPORT_PERIOD 3000000 /** 3 million blocks */ /** * Reserved Account IDs with special meaning diff --git a/libraries/chain/include/graphene/chain/evaluator.hpp b/libraries/chain/include/graphene/chain/evaluator.hpp index 922a056d..4e3996d4 100644 --- a/libraries/chain/include/graphene/chain/evaluator.hpp +++ b/libraries/chain/include/graphene/chain/evaluator.hpp @@ -102,6 +102,7 @@ namespace graphene { namespace chain { object_id_type get_relative_id( object_id_type rel_id )const; + void check_relative_ids(const authority& a)const; authority resolve_relative_ids( const authority& a )const; asset fee_from_account; diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 7be5368a..5d5f787c 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -137,12 +138,8 @@ namespace graphene { namespace chain { string name; authority owner; authority active; - account_id_type voting_account; - object_id_type memo_key = key_id_type(); - uint16_t num_witness = 0; - uint16_t num_committee = 0; - flat_set vote; + account_object::options_type options; account_id_type fee_payer()const { return registrar; } void get_required_auth(flat_set& active_auth_set , flat_set&)const; @@ -203,15 +200,17 @@ namespace graphene { namespace chain { */ struct account_update_operation { - asset fee; - account_id_type account; - optional owner; - optional active; - optional voting_account; - optional memo_key; - optional> vote; - uint16_t num_witness = 0; - uint16_t num_committee = 0; + asset fee; + /// The account to update + account_id_type account; + + /// New owner authority. If set, this operation requires owner authority to execute. + optional owner; + /// New active authority. If set, this operation requires owner authority to execute. + optional active; + + /// New account options + optional new_options; account_id_type fee_payer()const { return account; } void get_required_auth(flat_set& active_auth_set , flat_set& owner_auth_set)const; @@ -1566,13 +1565,11 @@ FC_REFLECT( graphene::chain::key_create_operation, FC_REFLECT( graphene::chain::account_create_operation, (fee)(registrar) (referrer)(referrer_percent) - (name) - (owner)(active)(voting_account)(memo_key) - (num_witness)(num_committee)(vote) + (name)(owner)(active)(options) ) FC_REFLECT( graphene::chain::account_update_operation, - (fee)(account)(owner)(active)(voting_account)(memo_key)(num_witness)(num_committee)(vote) + (fee)(account)(owner)(active)(new_options) ) FC_REFLECT( graphene::chain::account_upgrade_operation, (fee)(account_to_upgrade)(upgrade_to_lifetime_member) ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 5e88efed..a90ebb52 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -149,9 +149,11 @@ void account_update_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( account != account_id_type() ); - FC_ASSERT( owner || active || voting_account || memo_key || vote ); -} + FC_ASSERT( owner || active || new_options ); + if( new_options ) + new_options->validate(); +} share_type asset_create_operation::calculate_fee( const fee_schedule_type& schedule )const { @@ -209,10 +211,7 @@ void account_create_operation::validate()const FC_ASSERT( owner.weight_threshold == 1 ); FC_ASSERT( owner.auths.size() == 1 ); } - FC_ASSERT( num_witness + num_committee >= num_witness ); // no overflow - FC_ASSERT( num_witness + num_committee <= vote.size() ); - // FC_ASSERT( (num_witness == 0) || (num_witness&0x01) == 0, "must be odd number" ); - // FC_ASSERT( (num_committee == 0) || (num_committee&0x01) == 0, "must be odd number" ); + options.validate(); } diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index d8e1148e..1912cc84 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -448,7 +448,7 @@ flat_set account_history_plugin_impl::get_keys_for_account( const a flat_set key_id_set; key_id_set.reserve(owner_auths.size() + active_auths.size() + 2); - key_id_set.insert(acct.memo_key); + key_id_set.insert(acct.options.memo_key); // we don't use get_keys() here to avoid an intermediate copy operation for( const pair& item : active_auths ) diff --git a/libraries/utilities/git_revision.cpp.in b/libraries/utilities/git_revision.cpp.in index 411e454a..b615f1ba 100644 --- a/libraries/utilities/git_revision.cpp.in +++ b/libraries/utilities/git_revision.cpp.in @@ -5,10 +5,10 @@ #define GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP @GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP@ #define GRAPHENE_GIT_REVISION_DESCRIPTION "@GRAPHENE_GIT_REVISION_DESCRIPTION@" -namespace bts { namespace utilities { +namespace graphene { namespace utilities { const char* const git_revision_sha = GRAPHENE_GIT_REVISION_SHA; const uint32_t git_revision_unix_timestamp = GRAPHENE_GIT_REVISION_UNIX_TIMESTAMP; const char* const git_revision_description = GRAPHENE_GIT_REVISION_DESCRIPTION; -} } // end namespace bts::utilities +} } // end namespace graphene::utilities diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2d0d6dec..6dc9e67a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -724,7 +724,7 @@ public: account_create_op.name = name; account_create_op.owner = authority(1, owner_rkid, 1); account_create_op.active = authority(1, active_rkid, 1); - account_create_op.memo_key = active_rkid; + account_create_op.options.memo_key = active_rkid; signed_transaction tx; @@ -820,7 +820,7 @@ public: account_create_op.name = account_name; account_create_op.owner = authority(1, owner_rkid, 1); account_create_op.active = authority(1, active_rkid, 1); - account_create_op.memo_key = active_rkid; + account_create_op.options.memo_key = active_rkid; // current_fee_schedule() // find_account(pay_from_account) @@ -1087,10 +1087,10 @@ public: if( memo.size() ) { xfer_op.memo = memo_data(); - xfer_op.memo->from = from_account.memo_key; - xfer_op.memo->to = to_account.memo_key; - xfer_op.memo->set_message(get_private_key(from_account.memo_key), - get_public_key(to_account.memo_key), memo); + xfer_op.memo->from = from_account.options.memo_key; + xfer_op.memo->to = to_account.options.memo_key; + xfer_op.memo->set_message(get_private_key(from_account.options.memo_key), + get_public_key(to_account.options.memo_key), memo); } signed_transaction tx; @@ -1117,10 +1117,10 @@ public: if( memo.size() ) { issue_op.memo = memo_data(); - issue_op.memo->from = issuer.memo_key; - issue_op.memo->to = to.memo_key; - issue_op.memo->set_message(get_private_key(issuer.memo_key), - get_public_key(to.memo_key), memo); + issue_op.memo->from = issuer.options.memo_key; + issue_op.memo->to = to.options.memo_key; + issue_op.memo->set_message(get_private_key(issuer.options.memo_key), + get_public_key(to.options.memo_key), memo); } signed_transaction tx; @@ -1200,7 +1200,7 @@ void dbg_make_mia(string creator, string symbol) void flood_network(string prefix, uint32_t number_of_transactions) { - const account_object& master = *_wallet.my_accounts.get().lower_bound("bts"); + const account_object& master = *_wallet.my_accounts.get().lower_bound("import"); int number_of_accounts = number_of_transactions / 3; number_of_transactions -= number_of_accounts; auto key = derive_private_key("floodshill", 0); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 61a5c8cf..d396826b 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -193,7 +193,7 @@ void database_fixture::verify_account_history_plugin_index( )const if( auth.first.type() == key_object_type ) acct_addresses.insert( key_id_type( auth.first )(db).key_address() ); } - acct_addresses.insert( acct.memo_key(db).key_address() ); + acct_addresses.insert( acct.options.get_memo_key()(db).key_address() ); for( const address& addr : acct_addresses ) tuples_from_db.emplace_back( account_id, addr ); } @@ -294,7 +294,7 @@ account_create_operation database_fixture::make_account( create_account.name = name; create_account.owner = authority(123, key, 123); create_account.active = authority(321, key, 321); - create_account.memo_key = key; + create_account.options.memo_key = key; auto& active_delegates = db.get_global_properties().active_delegates; if( active_delegates.size() > 0 ) @@ -305,9 +305,9 @@ account_create_operation database_fixture::make_account( votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); - create_account.vote = flat_set(votes.begin(), votes.end()); + create_account.options.votes = flat_set(votes.begin(), votes.end()); } - create_account.num_committee = create_account.vote.size(); + create_account.options.num_committee = create_account.options.votes.size(); create_account.fee = create_account.calculate_fee(db.current_fee_schedule()); return create_account; @@ -332,7 +332,7 @@ account_create_operation database_fixture::make_account( create_account.name = name; create_account.owner = authority(123, key, 123); create_account.active = authority(321, key, 321); - create_account.memo_key = key; + create_account.options.memo_key = key; const vector& active_delegates = db.get_global_properties().active_delegates; if( active_delegates.size() > 0 ) @@ -343,9 +343,9 @@ account_create_operation database_fixture::make_account( votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); votes.insert(active_delegates[rand() % active_delegates.size()](db).vote_id); - create_account.vote = flat_set(votes.begin(), votes.end()); + create_account.options.votes = flat_set(votes.begin(), votes.end()); } - create_account.num_committee = create_account.vote.size(); + create_account.options.num_committee = create_account.options.votes.size(); create_account.fee = create_account.calculate_fee(db.current_fee_schedule()); return create_account; @@ -499,7 +499,7 @@ const account_object& database_fixture::create_account( account_create_op.name = name; account_create_op.owner = authority(1234, key_rkid, 1234); account_create_op.active = authority(5678, key_rkid, 5678); - account_create_op.memo_key = key_rkid; + account_create_op.options.memo_key = key_rkid; trx.operations.push_back( account_create_op ); trx.validate(); diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 03585f4b..4ce9740d 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -474,7 +474,8 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) //Oh noes! Nathan votes for a whole new slate of delegates! account_update_operation op; op.account = nathan->id; - op.vote = delegates; + op.new_options = nathan->options; + op.new_options->votes = delegates; trx.operations.push_back(op); trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); db.push_transaction(trx, ~0); @@ -874,7 +875,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture ) anon_create_op.owner = owner_auth; anon_create_op.active = active_auth; anon_create_op.registrar = sam_account_object.id; - anon_create_op.memo_key = sam_account_object.memo_key; + anon_create_op.options.memo_key = sam_account_object.options.memo_key; anon_create_op.name = generate_anon_acct_name(); tx.operations.push_back( anon_create_op ); @@ -913,10 +914,10 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) account_object bob_account_object = create_account( "bob", bob_key ); account_object charlie_account_object = create_account( "charlie", charlie_key ); - key_id_type alice_key_id = alice_account_object.memo_key; + key_id_type alice_key_id = alice_account_object.options.memo_key; // unneeded, comment it out to silence compiler warning //key_id_type bob_key_id = bob_account_object.memo_key; - key_id_type charlie_key_id = charlie_account_object.memo_key; + key_id_type charlie_key_id = charlie_account_object.options.memo_key; uint32_t skip = database::skip_transaction_dupe_check; @@ -994,9 +995,10 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) { account_update_operation op; op.account = nathan_id; - op.voting_account = vikram_id; - op.vote = flat_set{nathan_delegate(db).vote_id}; - op.num_committee = 1; + op.new_options = nathan_id(db).options; + op.new_options->voting_account = vikram_id; + op.new_options->votes = flat_set{nathan_delegate(db).vote_id}; + op.new_options->num_committee = 1; trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); db.push_transaction(trx); @@ -1005,11 +1007,16 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) { account_update_operation op; op.account = vikram_id; - op.vote = vikram_id(db).votes; - op.vote->insert(vikram_delegate(db).vote_id); - op.num_committee = 11; + op.new_options = vikram_id(db).options; + op.new_options->votes.insert(vikram_delegate(db).vote_id); + op.new_options->num_committee = 11; trx.operations.push_back(op); trx.sign(vikram_key_id, vikram_private_key); + // Fails because num_committee is larger than the cardinality of committee members being voted for + BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + op.new_options->num_committee = 3; + trx.operations = {op}; + trx.sign(vikram_key_id, vikram_private_key); db.push_transaction(trx); trx.clear(); } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index c74e182d..8704850a 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -429,8 +429,8 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) { account_update_operation op; op.account = nathan.id; - op.vote = nathan.votes; - op.vote->insert(nathans_delegate.vote_id); + op.new_options = nathan.options; + op.new_options->votes.insert(nathans_delegate.vote_id); trx.operations.push_back(op); db.push_transaction(trx, ~0); trx.operations.clear(); diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index e72ca4d0..f5b429d7 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE( cashback_test ) op.referrer = referrer_name ## _id; \ op.referrer_percent = referrer_rate*GRAPHENE_1_PERCENT; \ op.name = BOOST_PP_STRINGIZE(actor_name); \ - op.memo_key = actor_name ## _key_id; \ + op.options.memo_key = actor_name ## _key_id; \ op.active = authority(1, actor_name ## _key_id, 1); \ op.owner = op.active; \ op.fee = op.calculate_fee(fees); \ diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index e3bc8c00..f2c503be 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -57,8 +57,8 @@ BOOST_AUTO_TEST_CASE( create_account_test ) REQUIRE_THROW_WITH_VALUE(op, name, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); REQUIRE_THROW_WITH_VALUE(op, name, "aaaa."); REQUIRE_THROW_WITH_VALUE(op, name, ".aaaa"); - REQUIRE_THROW_WITH_VALUE(op, voting_account, account_id_type(999999999)); - REQUIRE_THROW_WITH_VALUE(op, memo_key, key_id_type(999999999)); + REQUIRE_THROW_WITH_VALUE(op, options.voting_account, account_id_type(999999999)); + REQUIRE_THROW_WITH_VALUE(op, options.memo_key, key_id_type(999999999)); auto auth_bak = op.owner; op.owner.add_authority(account_id_type(9999999999), 10); @@ -85,8 +85,8 @@ BOOST_AUTO_TEST_CASE( create_account_test ) BOOST_CHECK(nathan_account.owner.auths.at(genesis_key) == 123); BOOST_REQUIRE(nathan_account.active.auths.size() == 1); BOOST_CHECK(nathan_account.active.auths.at(genesis_key) == 321); - BOOST_CHECK(nathan_account.voting_account == account_id_type()); - BOOST_CHECK(nathan_account.memo_key == genesis_key); + BOOST_CHECK(nathan_account.options.voting_account == account_id_type()); + BOOST_CHECK(nathan_account.options.memo_key == genesis_key); const account_statistics_object& statistics = nathan_account.statistics(db); BOOST_CHECK(statistics.id.space() == implementation_ids); @@ -159,13 +159,13 @@ BOOST_AUTO_TEST_CASE( update_account ) op.account = nathan.id; op.owner = authority(2, key_id, 1, key_id_type(), 1); op.active = authority(2, key_id, 1, key_id_type(), 1); - //op.voting_account = key_id; - op.vote = flat_set({active_delegates[0](db).vote_id, active_delegates[5](db).vote_id}); + op.new_options = nathan.options; + op.new_options->votes = flat_set({active_delegates[0](db).vote_id, active_delegates[5](db).vote_id}); + op.new_options->num_committee = 2; trx.operations.back() = op; db.push_transaction(trx, ~0); - //BOOST_CHECK(nathan.voting_key == key_id); - BOOST_CHECK(nathan.memo_key == key_id_type()); + BOOST_CHECK(nathan.options.memo_key == key_id_type()); BOOST_CHECK(nathan.active.weight_threshold == 2); BOOST_CHECK(nathan.active.auths.size() == 2); BOOST_CHECK(nathan.active.auths.at(key_id) == 1); @@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE( update_account ) BOOST_CHECK(nathan.owner.auths.size() == 2); BOOST_CHECK(nathan.owner.auths.at(key_id) == 1); BOOST_CHECK(nathan.owner.auths.at(key_id_type()) == 1); - BOOST_CHECK(nathan.votes.size() == 2); + BOOST_CHECK(nathan.options.votes.size() == 2); /** these votes are no longer tallied in real time BOOST_CHECK(active_delegates[0](db).vote(db).total_votes == 30000); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 67f11d19..7e72bbbf 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -399,10 +399,12 @@ BOOST_AUTO_TEST_CASE( witness_create ) { account_update_operation op; op.account = nathan_id; - op.vote = nathan_id(db).votes; - op.vote->insert(nathan_witness_id(db).vote_id); - op.num_witness = std::count_if(op.vote->begin(), op.vote->end(), [](vote_id_type id) { return id.type() == vote_id_type::witness; }); - op.num_committee = std::count_if(op.vote->begin(), op.vote->end(), [](vote_id_type id) { return id.type() == vote_id_type::committee; }); + op.new_options = nathan_id(db).options; + op.new_options->votes.insert(nathan_witness_id(db).vote_id); + op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(), + [](vote_id_type id) { return id.type() == vote_id_type::witness; }); + op.new_options->num_committee = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(), + [](vote_id_type id) { return id.type() == vote_id_type::committee; }); trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); db.push_transaction(trx); @@ -540,8 +542,8 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) { account_update_operation op; op.account = nathan_id; - op.vote = nathan_id(db).votes; - op.vote->insert(worker_id_type()(db).vote_for); + op.new_options = nathan_id(db).options; + op.new_options->votes.insert(worker_id_type()(db).vote_for); trx.operations.push_back(op); db.push_transaction(trx, ~0); trx.clear(); @@ -580,8 +582,8 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) { account_update_operation op; op.account = nathan_id; - op.vote = nathan_id(db).votes; - op.vote->erase(worker_id_type()(db).vote_for); + op.new_options = nathan_id(db).options; + op.new_options->votes.erase(worker_id_type()(db).vote_for); trx.operations.push_back(op); db.push_transaction(trx, ~0); trx.clear(); @@ -653,8 +655,8 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) { account_update_operation op; op.account = nathan_id; - op.vote = nathan_id(db).votes; - op.vote->insert(worker_id_type()(db).vote_for); + op.new_options = nathan_id(db).options; + op.new_options->votes.insert(worker_id_type()(db).vote_for); trx.operations.push_back(op); db.push_transaction(trx, ~0); trx.clear(); From ca89d5057c69dab4dd0a88b441cf35996ad9e18d Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 16 Jun 2015 15:56:13 -0400 Subject: [PATCH 011/354] remove dependency on level db, bugs left to fix --- .gitmodules | 4 - CMakeLists.txt | 28 -- libraries/app/include/graphene/app/plugin.hpp | 1 + libraries/chain/CMakeLists.txt | 3 +- libraries/chain/block_database.cpp | 159 ++++++ libraries/chain/db_block.cpp | 14 +- libraries/chain/db_management.cpp | 17 +- libraries/chain/db_update.cpp | 2 + .../include/graphene/chain/block_database.hpp | 26 + .../chain/include/graphene/chain/database.hpp | 5 +- libraries/db/CMakeLists.txt | 4 +- libraries/db/include/graphene/db/index.hpp | 52 +- .../db/include/graphene/db/level_map.hpp | 462 ------------------ .../db/include/graphene/db/level_pod_map.hpp | 309 ------------ .../include/graphene/db/object_database.hpp | 4 - libraries/db/object_database.cpp | 62 +-- libraries/db/upgrade_leveldb.cpp | 114 ----- libraries/leveldb | 1 - libraries/net/CMakeLists.txt | 2 +- libraries/net/peer_database.cpp | 22 +- tests/common/database_fixture.hpp | 1 + 21 files changed, 274 insertions(+), 1018 deletions(-) create mode 100644 libraries/chain/block_database.cpp create mode 100644 libraries/chain/include/graphene/chain/block_database.hpp delete mode 100644 libraries/db/include/graphene/db/level_map.hpp delete mode 100644 libraries/db/include/graphene/db/level_pod_map.hpp delete mode 100644 libraries/db/upgrade_leveldb.cpp delete mode 160000 libraries/leveldb diff --git a/.gitmodules b/.gitmodules index 85a89fba..ee296306 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,7 +5,3 @@ path = libraries/fc url = https://github.com/cryptonomex/fc.git ignore = dirty -[submodule "libraries/leveldb"] - path = libraries/leveldb - url = https://github.com/bitcoin/leveldb.git - ignore = dirty diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c6e7652..d60399d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,21 +65,6 @@ IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)") SET(Boost_LIBRARIES ${BOOST_LIBRARIES_TEMP} ${Boost_LIBRARIES}) ENDIF() -set( LEVEL_DB_DIR "${CMAKE_SOURCE_DIR}/libraries/leveldb" ) - -file( GLOB LEVEL_DB_SOURCES "${LEVEL_DB_DIR}/db/*.cc" - "${LEVEL_DB_DIR}/helpers/memenv/memenv.cc" - "${LEVEL_DB_DIR}/table/*.cc" - "${LEVEL_DB_DIR}/util/*.cc" ) -foreach( filename ${LEVEL_DB_SOURCES} ) - if( ${filename} MATCHES ".*_test.cc" OR ${filename} MATCHES ".*_bench.cc" OR ${filename} MATCHES ".*_main.cc" ) - list( REMOVE_ITEM LEVEL_DB_SOURCES ${filename} ) - endif() -endforeach() -set(LEVELDB_BUILD_DEFINES) -set(LEVELDB_BUILD_LIBRARIES) -set(LEVELDB_BUILD_PRIVATE_INCLUDES "${LEVEL_DB_DIR}") - if( WIN32 ) message( STATUS "Configuring Graphene on WIN32") set( DB_VERSION 60 ) @@ -112,13 +97,7 @@ if( WIN32 ) SET(TCL_LIBS "${TCL_LIBS}${TCL_LIB_PATH}/${TCL_LIB_NAME}g${TCL_LIB_EXT}") SET(TCL_LIBRARY ${TCL_LIBS}) - SET(LEVELDB_PORT_FILE "${LEVEL_DB_DIR}/port/port_win.cc" ) - list(APPEND LEVELDB_BUILD_DEFINES OS_WINDOWS LEVELDB_PLATFORM_WINDOWS ) - list(APPEND LEVELDB_BUILD_LIBRARIES shlwapi.lib) - list(INSERT LEVELDB_BUILD_PRIVATE_INCLUDES 0 "${CMAKE_SOURCE_DIR}/libraries/leveldb-msvc/include") else( WIN32 ) # Apple AND Linux - SET(LEVELDB_PORT_FILE "${LEVEL_DB_DIR}/port/port_posix.cc" ) - list(APPEND LEVELDB_BUILD_DEFINES LEVELDB_PLATFORM_POSIX LEVELDB_ATOMIC_PRESENT) if( APPLE ) list(APPEND LEVELDB_BUILD_DEFINES OS_MACOSX) @@ -169,13 +148,6 @@ else( WIN32 ) # Apple AND Linux endif( WIN32 ) -list(APPEND LEVEL_DB_SOURCES "${LEVELDB_PORT_FILE}") -add_library( leveldb ${LEVEL_DB_SOURCES} ) -target_link_libraries( leveldb ${LEVELDB_BUILD_LIBRARIES} ) -target_include_directories( leveldb PRIVATE ${LEVELDB_BUILD_PRIVATE_INCLUDES} - PUBLIC "${LEVEL_DB_DIR}/include" ) -set_target_properties(leveldb PROPERTIES COMPILE_DEFINITIONS "${LEVELDB_BUILD_DEFINES}") - find_package( BerkeleyDB ) set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build Graphene for code coverage analysis") diff --git a/libraries/app/include/graphene/app/plugin.hpp b/libraries/app/include/graphene/app/plugin.hpp index 253492ad..5065679d 100644 --- a/libraries/app/include/graphene/app/plugin.hpp +++ b/libraries/app/include/graphene/app/plugin.hpp @@ -20,6 +20,7 @@ #include #include +#include namespace graphene { namespace app { namespace bpo = boost::program_options; diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 17635897..47c05498 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -34,6 +34,7 @@ add_library( graphene_chain transaction_evaluation_state.cpp fork_database.cpp + block_database.cpp db_balance.cpp db_block.cpp @@ -49,7 +50,7 @@ add_library( graphene_chain ${HEADERS} ) -target_link_libraries( graphene_chain fc graphene_db leveldb ) +target_link_libraries( graphene_chain fc graphene_db ) target_include_directories( graphene_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp new file mode 100644 index 00000000..94389571 --- /dev/null +++ b/libraries/chain/block_database.cpp @@ -0,0 +1,159 @@ +#include + +namespace graphene { namespace chain { + +struct index_entry +{ + uint64_t block_pos = 0; + uint32_t block_size = 0; + block_id_type block_id; +}; + +void block_database::open( const fc::path& dbdir ) +{ + _block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary ); + _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary ); +} + + +bool block_database::is_open()const +{ + return _blocks.is_open(); +} + + +void block_database::close() +{ + _blocks.close(); + _block_num_to_pos.close(); +} + +void block_database::flush() +{ + _blocks.flush(); + _block_num_to_pos.flush(); +} + + +void block_database::store( const block_id_type& id, const signed_block& b ) +{ + auto num = block_header::num_from_id(id); + _block_num_to_pos.seekp( sizeof( index_entry ) * num ); + index_entry e; + _blocks.seekp( 0, _blocks.end ); + auto vec = fc::raw::pack( b ); + e.block_pos = _blocks.tellp(); + e.block_size = vec.size(); + e.block_id = id; + _blocks.write( vec.data(), vec.size() ); + _block_num_to_pos.write( (char*)&e, sizeof(e) ); +} + + +void block_database::remove( const block_id_type& id ) +{ + index_entry e; + auto index_pos = sizeof(e)*block_header::num_from_id(id); + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + FC_ASSERT( e.block_id == id ); + + e.block_size = 0; + _block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) ); + _block_num_to_pos.write( (char*)&e, sizeof(e) ); +} + + + + + +bool block_database::contains( const block_id_type& id )const +{ + index_entry e; + auto index_pos = sizeof(e)*block_header::num_from_id(id); + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + return e.block_id == id; +} + + +block_id_type block_database::fetch_block_id( uint32_t block_num )const +{ + index_entry e; + auto index_pos = sizeof(e)*block_num; + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + return e.block_id; +} + + +optional block_database::fetch_optional( const block_id_type& id )const +{ + index_entry e; + auto index_pos = sizeof(e)*block_header::num_from_id(id); + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + if( e.block_id != id ) return optional(); + + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + return fc::raw::unpack(data); +} + + +optional block_database::fetch_by_number( uint32_t block_num )const +{ + index_entry e; + auto index_pos = sizeof(e)*block_num; + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + return fc::raw::unpack(data); +} + + +optional block_database::last()const +{ + index_entry e; + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + + if( _block_num_to_pos.tellp() < sizeof(index_entry) ) + return optional(); + + _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + if( e.block_size == 0 ) + return optional(); + + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + return fc::raw::unpack(data); +} + + +} } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index cfdd63f8..e8493820 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -30,7 +30,7 @@ namespace graphene { namespace chain { bool database::is_known_block( const block_id_type& id )const { - return _fork_db.is_known_block(id) || _block_id_to_block.find(id).valid(); + return _fork_db.is_known_block(id) || _block_id_to_block.contains(id); } /** * Only return true *if* the transaction has not expired or been invalidated. If this @@ -45,10 +45,7 @@ bool database::is_known_transaction( const transaction_id_type& id )const block_id_type database::get_block_id_for_num( uint32_t block_num )const { try { - block_id_type lb; lb._hash[0] = htonl(block_num); - auto itr = _block_id_to_block.lower_bound( lb ); - FC_ASSERT( itr.valid() && itr.key()._hash[0] == lb._hash[0] ); - return itr.key(); + return _block_id_to_block.fetch_block_id( block_num ); } FC_CAPTURE_AND_RETHROW( (block_num) ) } optional database::fetch_block_by_id( const block_id_type& id )const @@ -65,12 +62,7 @@ optional database::fetch_block_by_number( uint32_t num )const if( results.size() == 1 ) return results[0]->data; else - { - block_id_type lb; lb._hash[0] = htonl(num); - auto itr = _block_id_to_block.lower_bound( lb ); - if( itr.valid() && itr.key()._hash[0] == lb._hash[0] ) - return itr.value(); - } + return _block_id_to_block.fetch_by_number(num); return optional(); } diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index de45ecda..5651fe1e 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -46,9 +46,9 @@ void database::open( const fc::path& data_dir, const genesis_allocation& initial _pending_block.previous = head_block_id(); _pending_block.timestamp = head_block_time(); - auto last_block_itr = _block_id_to_block.last(); - if( last_block_itr.valid() ) - _fork_db.start_block( last_block_itr.value() ); + auto last_block= _block_id_to_block.last(); + if( last_block.valid() ) + _fork_db.start_block( *last_block ); } FC_CAPTURE_AND_RETHROW( (data_dir) ) } @@ -58,19 +58,22 @@ void database::reindex(fc::path data_dir, const genesis_allocation& initial_allo open(data_dir, initial_allocation); auto start = fc::time_point::now(); - auto itr = _block_id_to_block.begin(); + auto last_block = _block_id_to_block.last(); + if( !last_block ) return; + + const auto last_block_num = last_block->block_num(); + // TODO: disable undo tracking durring reindex, this currently causes crashes in the benchmark test //_undo_db.disable(); - while( itr.valid() ) + for( uint32_t i = 1; i <= last_block_num; ++i ) { - apply_block( itr.value(), skip_delegate_signature | + apply_block( *_block_id_to_block.fetch_by_number(i), skip_delegate_signature | skip_transaction_signatures | skip_undo_block | skip_undo_transaction | skip_transaction_dupe_check | skip_tapos_check | skip_authority_check ); - ++itr; } //_undo_db.enable(); auto end = fc::time_point::now(); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 1ba5b95e..35b265db 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -187,9 +187,11 @@ void database::clear_expired_orders() max_settlement_volume = mia_object.amount(mia.max_force_settlement_volume(mia_object.dynamic_data(*this).current_supply)); if( mia.force_settled_volume >= max_settlement_volume.amount ) { + /* ilog("Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume}", ("asset", mia_object.symbol)("settlement_price_null",mia.current_feed.settlement_price.is_null()) ("settled_volume", mia.force_settled_volume)("max_volume", max_settlement_volume)); + */ if( next_asset() ) continue; break; diff --git a/libraries/chain/include/graphene/chain/block_database.hpp b/libraries/chain/include/graphene/chain/block_database.hpp new file mode 100644 index 00000000..66af6ece --- /dev/null +++ b/libraries/chain/include/graphene/chain/block_database.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + class block_database + { + public: + void open( const fc::path& dbdir ); + bool is_open()const; + void flush(); + void close(); + + void store( const block_id_type& id, const signed_block& b ); + void remove( const block_id_type& id ); + + bool contains( const block_id_type& id )const; + block_id_type fetch_block_id( uint32_t block_num )const; + optional fetch_optional( const block_id_type& id )const; + optional fetch_by_number( uint32_t block_num )const; + optional last()const; + private: + mutable std::fstream _blocks; + mutable std::fstream _block_num_to_pos; + }; +} } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 469a78ad..58e7827b 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -23,11 +23,10 @@ #include #include #include +#include #include #include -#include -#include #include #include @@ -376,7 +375,7 @@ namespace graphene { namespace chain { * until the fork is resolved. This should make maintaining * the fork tree relatively simple. */ - graphene::db::level_map _block_id_to_block; + block_database _block_id_to_block; /** * Contains the set of ops that are in the process of being applied from diff --git a/libraries/db/CMakeLists.txt b/libraries/db/CMakeLists.txt index 68407487..91e26cc6 100644 --- a/libraries/db/CMakeLists.txt +++ b/libraries/db/CMakeLists.txt @@ -1,4 +1,4 @@ file(GLOB HEADERS "include/graphene/db/*.hpp") -add_library( graphene_db undo_database.cpp index.cpp object_database.cpp upgrade_leveldb.cpp ${HEADERS} ) -target_link_libraries( graphene_db fc leveldb ) +add_library( graphene_db undo_database.cpp index.cpp object_database.cpp ${HEADERS} ) +target_link_libraries( graphene_db fc ) target_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index 2c21d392..af95414d 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -17,10 +17,13 @@ */ #pragma once #include -#include +#include +#include +#include namespace graphene { namespace db { class object_database; + using fc::path; /** * @class index_observer @@ -80,9 +83,12 @@ namespace graphene { namespace db { virtual const object& create( const std::function& constructor ) = 0; /** - * Opens the index loading objects from a level_db database + * Opens the index loading objects from a file */ - virtual void open( const shared_ptr >>& db ){} + virtual void open( const fc::path& db ) = 0; + virtual void save( const fc::path& db ) = 0; + + /** @return the object with id or nullptr if not found */ virtual const object* find( object_id_type id )const = 0; @@ -170,22 +176,40 @@ namespace graphene { namespace db { virtual void use_next_id()override { ++_next_id.number; } virtual void set_next_id( object_id_type id )override { _next_id = id; } + virtual void open( const path& db )override + { + if( !fc::exists( db ) ) return; + fc::file_mapping fm( db.generic_string().c_str(), fc::read_only ); + fc::mapped_region mr( fm, fc::read_only, 0, fc::file_size(db) ); + fc::datastream ds( (const char*)mr.get_address(), mr.get_size() ); + fc::raw::unpack(ds, _next_id); + try { + vector tmp; + while( true ) + { + fc::raw::unpack( ds, tmp ); + load( tmp ); + } + } catch ( const fc::exception& ){} + } + + virtual void save( const path& db ) override + { + std::ofstream out( db.generic_string(), std::ofstream::binary ); + out.write( (char*)&_next_id, sizeof(_next_id) ); + this->inspect_all_objects( [&]( const object& o ) { + auto vec = fc::raw::pack( static_cast(o) ); + auto packed_vec = fc::raw::pack( vec ); + out.write( packed_vec.data(), packed_vec.size() ); + }); + } + virtual const object& load( const std::vector& data )override { return DerivedIndex::insert( fc::raw::unpack( data ) ); } - virtual void open( const shared_ptr >>& db )override - { - auto first = object_id_type( DerivedIndex::object_type::space_id, DerivedIndex::object_type::type_id, 0 ); - auto last = object_id_type( DerivedIndex::object_type::space_id, DerivedIndex::object_type::type_id+1, 0 ); - auto itr = db->lower_bound( first ); - while( itr.valid() && itr.key() < last ) - { - load( itr.value() ); - ++itr; - } - } + virtual const object& create(const std::function& constructor )override { const auto& result = DerivedIndex::create( constructor ); diff --git a/libraries/db/include/graphene/db/level_map.hpp b/libraries/db/include/graphene/db/level_map.hpp deleted file mode 100644 index 55ecb99b..00000000 --- a/libraries/db/include/graphene/db/level_map.hpp +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -namespace graphene { namespace db { - - namespace ldb = leveldb; - - /** - * @brief implements a high-level API on top of Level DB that stores items using fc::raw / reflection - */ - template - class level_map - { - public: - void open( const fc::path& dir, bool create = true, size_t cache_size = 0 ) - { try { - FC_ASSERT( !is_open(), "Database is already open!" ); - - ldb::Options opts; - opts.comparator = &_comparer; - opts.create_if_missing = create; - opts.max_open_files = 64; - opts.compression = leveldb::kNoCompression; - - if( cache_size > 0 ) - { - opts.write_buffer_size = cache_size / 4; // up to two write buffers may be held in memory simultaneously - _cache.reset( leveldb::NewLRUCache( cache_size / 2 ) ); - opts.block_cache = _cache.get(); - } - - if( ldb::kMajorVersion > 1 || ( leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16 ) ) - { - // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error - // on corruption in later versions. - opts.paranoid_checks = true; - } - - _read_options.verify_checksums = true; - _iter_options.verify_checksums = true; - _iter_options.fill_cache = false; - _sync_options.sync = true; - - // Given path must exist to succeed toNativeAnsiPath - fc::create_directories( dir ); - std::string ldbPath = dir.to_native_ansi_path(); - - ldb::DB* ndb = nullptr; - const auto ntrxstat = ldb::DB::Open( opts, ldbPath.c_str(), &ndb ); - if( !ntrxstat.ok() ) - { - elog( "Failure opening database: ${db}\nStatus: ${msg}", ("db",dir)("msg",ntrxstat.ToString()) ); - FC_THROW_EXCEPTION( level_map_open_failure, "Failure opening database: ${db}\nStatus: ${msg}", - ("db",dir)("msg",ntrxstat.ToString()) ); - } - _db.reset( ndb ); - - try_upgrade_db( dir, ndb, fc::get_typename::name(), sizeof( Value ) ); - } FC_CAPTURE_AND_RETHROW( (dir)(create)(cache_size) ) } - - bool is_open()const - { - return !!_db; - } - - void close() - { - _db.reset(); - _cache.reset(); - } - - fc::optional fetch_optional( const Key& k )const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - auto itr = find( k ); - if( itr.valid() ) return itr.value(); - return fc::optional(); - } FC_RETHROW_EXCEPTIONS( warn, "" ) } - - Value fetch( const Key& k ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::vector kslice = fc::raw::pack( k ); - ldb::Slice ks( kslice.data(), kslice.size() ); - std::string value; - auto status = _db->Get( _read_options, ks, &value ); - if( status.IsNotFound() ) - { - FC_THROW_EXCEPTION( fc::key_not_found_exception, "unable to find key ${key}", ("key",k) ); - } - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - fc::datastream ds(value.c_str(), value.size()); - Value tmp; - fc::raw::unpack(ds, tmp); - return tmp; - } FC_RETHROW_EXCEPTIONS( warn, "failure fetching key ${key}", ("key",k) ); } - - class iterator - { - public: - iterator(){} - bool valid()const - { - return _it && _it->Valid(); - } - - Key key()const - { - Key tmp_key; - fc::datastream ds2( _it->key().data(), _it->key().size() ); - fc::raw::unpack( ds2, tmp_key ); - return tmp_key; - } - - Value value()const - { - Value tmp_val; - fc::datastream ds( _it->value().data(), _it->value().size() ); - fc::raw::unpack( ds, tmp_val ); - return tmp_val; - } - - iterator& operator++() { _it->Next(); return *this; } - iterator& operator--() { _it->Prev(); return *this; } - - protected: - friend class level_map; - iterator( ldb::Iterator* it ) - :_it(it){} - - std::shared_ptr _it; - }; - - iterator begin() const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->SeekToFirst(); - - if( itr._it->status().IsNotFound() ) - { - FC_THROW_EXCEPTION( fc::key_not_found_exception, "" ); - } - if( !itr._it->status().ok() ) - { - FC_THROW_EXCEPTION( level_map_failure, "database error: ${msg}", ("msg", itr._it->status().ToString() ) ); - } - - if( itr.valid() ) - { - return itr; - } - return iterator(); - } FC_RETHROW_EXCEPTIONS( warn, "error seeking to first" ) } - - iterator find( const Key& key )const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice key_slice; - - /** avoid dynamic memory allocation at this step if possible, most - * keys should be relatively small in size and not require dynamic - * memory allocation to seralize the key. - */ - fc::array stack_buffer; - - size_t pack_size = fc::raw::pack_size(key); - if( pack_size <= stack_buffer.size() ) - { - fc::datastream ds( stack_buffer.data, stack_buffer.size() ); - fc::raw::pack( ds ,key ); - key_slice = ldb::Slice( stack_buffer.data, pack_size ); - } - else - { - auto kslice = fc::raw::pack( key ); - key_slice = ldb::Slice( kslice.data(), kslice.size() ); - } - - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->Seek( key_slice ); - if( itr.valid() && itr.key() == key ) - { - return itr; - } - return iterator(); - } FC_RETHROW_EXCEPTIONS( warn, "error finding ${key}", ("key",key) ) } - - iterator lower_bound( const Key& key )const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::vector kslice = fc::raw::pack( key ); - ldb::Slice key_slice( kslice.data(), kslice.size() ); - - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->Seek( key_slice ); - return itr; - } FC_RETHROW_EXCEPTIONS( warn, "error finding ${key}", ("key",key) ) } - - iterator last( )const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->SeekToLast(); - return itr; - } FC_RETHROW_EXCEPTIONS( warn, "error finding last" ) } - - bool last( Key& k ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::unique_ptr it( _db->NewIterator( _iter_options ) ); - FC_ASSERT( it != nullptr ); - it->SeekToLast(); - if( !it->Valid() ) - { - return false; - } - fc::datastream ds2( it->key().data(), it->key().size() ); - fc::raw::unpack( ds2, k ); - return true; - } FC_RETHROW_EXCEPTIONS( warn, "error reading last item from database" ); } - - bool last( Key& k, Value& v ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::unique_ptr it( _db->NewIterator( _iter_options ) ); - FC_ASSERT( it != nullptr ); - it->SeekToLast(); - if( !it->Valid() ) - { - return false; - } - fc::datastream ds( it->value().data(), it->value().size() ); - fc::raw::unpack( ds, v ); - - fc::datastream ds2( it->key().data(), it->key().size() ); - fc::raw::unpack( ds2, k ); - return true; - } FC_RETHROW_EXCEPTIONS( warn, "error reading last item from database" ); } - - /** this class allows batched, atomic database writes. - * usage: - * { - * write_batch batch = _db.create_batch(); - * batch.store(key1, value1); - * batch.store(key2, value2); - * } - * when the batch goes out of scope, the operations are commited to the database - */ - class write_batch - { - private: - leveldb::WriteBatch _batch; - level_map* _map = nullptr; - leveldb::WriteOptions _write_options; - - friend class level_map; - write_batch( level_map* map, bool sync = false ) : _map(map) - { - _write_options.sync = sync; - } - public: - ~write_batch() - { - try - { - commit(); - } - catch (const fc::canceled_exception&) - { - throw; - } - catch (const fc::exception&) - { - // we're in a destructor, nothing we can do... - } - } - - void commit() - { - try - { - FC_ASSERT(_map->is_open(), "Database is not open!"); - - ldb::Status status = _map->_db->Write( _write_options, &_batch ); - if (!status.ok()) - FC_THROW_EXCEPTION(level_map_failure, "database error while applying batch: ${msg}", ("msg", status.ToString())); - _batch.Clear(); - } - FC_RETHROW_EXCEPTIONS(warn, "error applying batch"); - } - - void abort() - { - _batch.Clear(); - } - - void store( const Key& k, const Value& v ) - { - std::vector kslice = fc::raw::pack(k); - ldb::Slice ks(kslice.data(), kslice.size()); - - auto vec = fc::raw::pack(v); - ldb::Slice vs(vec.data(), vec.size()); - - _batch.Put(ks, vs); - } - - void remove( const Key& k ) - { - std::vector kslice = fc::raw::pack(k); - ldb::Slice ks(kslice.data(), kslice.size()); - _batch.Delete(ks); - } - }; - - write_batch create_batch( bool sync = false ) - { - FC_ASSERT( is_open(), "Database is not open!" ); - return write_batch( this, sync ); - } - - void store(const Key& k, const Value& v, bool sync = false) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::vector kslice = fc::raw::pack( k ); - ldb::Slice ks( kslice.data(), kslice.size() ); - - auto vec = fc::raw::pack(v); - ldb::Slice vs( vec.data(), vec.size() ); - - auto status = _db->Put( sync ? _sync_options : _write_options, ks, vs ); - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - } FC_RETHROW_EXCEPTIONS( warn, "error storing ${key} = ${value}", ("key",k)("value",v) ); } - - void remove( const Key& k, bool sync = false ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::vector kslice = fc::raw::pack( k ); - ldb::Slice ks( kslice.data(), kslice.size() ); - auto status = _db->Delete( sync ? _sync_options : _write_options, ks ); - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - } FC_RETHROW_EXCEPTIONS( warn, "error removing ${key}", ("key",k) ); } - - void export_to_json( const fc::path& path )const - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - FC_ASSERT( !fc::exists( path ) ); - - std::ofstream fs( path.string() ); - fs.write( "[\n", 2 ); - - auto iter = begin(); - while( iter.valid() ) - { - auto str = fc::json::to_pretty_string( std::make_pair( iter.key(), iter.value() ) ); - if( (++iter).valid() ) str += ","; - str += "\n"; - fs.write( str.c_str(), str.size() ); - } - - fs.write( "]", 1 ); - } FC_CAPTURE_AND_RETHROW( (path) ) } - - // note: this loops through all the items in the database, so it's not exactly fast. it's intended for debugging, nothing else. - size_t size() const - { - FC_ASSERT( is_open(), "Database is not open!" ); - - iterator it = begin(); - size_t count = 0; - while (it.valid()) - { - ++count; - ++it; - } - return count; - } - - private: - class key_compare : public leveldb::Comparator - { - public: - int Compare( const leveldb::Slice& a, const leveldb::Slice& b )const - { - Key ak,bk; - fc::datastream dsa( a.data(), a.size() ); - fc::raw::unpack( dsa, ak ); - fc::datastream dsb( b.data(), b.size() ); - fc::raw::unpack( dsb, bk ); - - if( ak < bk ) return -1; - if( ak == bk ) return 0; - return 1; - } - - const char* Name()const { return "key_compare"; } - void FindShortestSeparator( std::string*, const leveldb::Slice& )const{} - void FindShortSuccessor( std::string* )const{}; - }; - - std::unique_ptr _db; - std::unique_ptr _cache; - key_compare _comparer; - - ldb::ReadOptions _read_options; - ldb::ReadOptions _iter_options; - ldb::WriteOptions _write_options; - ldb::WriteOptions _sync_options; - }; - -} } // graphene::db diff --git a/libraries/db/include/graphene/db/level_pod_map.hpp b/libraries/db/include/graphene/db/level_pod_map.hpp deleted file mode 100644 index 2883356f..00000000 --- a/libraries/db/include/graphene/db/level_pod_map.hpp +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace graphene { namespace db { - - namespace ldb = leveldb; - - /** - * @brief implements a high-level API on top of Level DB that stores items using fc::raw / reflection - * @note Key must be a POD type - */ - template - class level_pod_map - { - public: - void open( const fc::path& dir, bool create = true, size_t cache_size = 0 ) - { try { - FC_ASSERT( !is_open(), "Database is already open!" ); - - ldb::Options opts; - opts.comparator = &_comparer; - opts.create_if_missing = create; - opts.max_open_files = 64; - opts.compression = leveldb::kNoCompression; - - if( cache_size > 0 ) - { - opts.write_buffer_size = cache_size / 4; // up to two write buffers may be held in memory simultaneously - _cache.reset( leveldb::NewLRUCache( cache_size / 2 ) ); - opts.block_cache = _cache.get(); - } - - if( ldb::kMajorVersion > 1 || ( leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16 ) ) - { - // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error - // on corruption in later versions. - opts.paranoid_checks = true; - } - - _read_options.verify_checksums = true; - _iter_options.verify_checksums = true; - _iter_options.fill_cache = false; - _sync_options.sync = true; - - // Given path must exist to succeed toNativeAnsiPath - fc::create_directories( dir ); - std::string ldbPath = dir.to_native_ansi_path(); - - ldb::DB* ndb = nullptr; - const auto ntrxstat = ldb::DB::Open( opts, ldbPath.c_str(), &ndb ); - if( !ntrxstat.ok() ) - { - elog( "Failure opening database: ${db}\nStatus: ${msg}", ("db",dir)("msg",ntrxstat.ToString()) ); - FC_THROW_EXCEPTION( level_pod_map_open_failure, "Failure opening database: ${db}\nStatus: ${msg}", - ("db",dir)("msg",ntrxstat.ToString()) ); - } - _db.reset( ndb ); - - try_upgrade_db( dir, ndb, fc::get_typename::name(), sizeof( Value ) ); - } FC_CAPTURE_AND_RETHROW( (dir)(create)(cache_size) ) } - - bool is_open()const - { - return !!_db; - } - - void close() - { - _db.reset(); - _cache.reset(); - } - - fc::optional fetch_optional( const Key& k ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - auto itr = find( k ); - if( itr.valid() ) return itr.value(); - return fc::optional(); - } FC_RETHROW_EXCEPTIONS( warn, "" ) } - - Value fetch( const Key& key ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice key_slice( (char*)&key, sizeof(key) ); - std::string value; - auto status = _db->Get( _read_options, key_slice, &value ); - if( status.IsNotFound() ) - { - FC_THROW_EXCEPTION( fc::key_not_found_exception, "unable to find key ${key}", ("key",key) ); - } - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_pod_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - fc::datastream datastream(value.c_str(), value.size()); - Value tmp; - fc::raw::unpack(datastream, tmp); - return tmp; - } FC_RETHROW_EXCEPTIONS( warn, "error fetching key ${key}", ("key",key) ); } - - class iterator - { - public: - iterator(){} - bool valid()const - { - return _it && _it->Valid(); - } - - Key key()const - { - FC_ASSERT( sizeof(Key) == _it->key().size() ); - return *((Key*)_it->key().data()); - } - - Value value()const - { - Value tmp_val; - fc::datastream ds( _it->value().data(), _it->value().size() ); - fc::raw::unpack( ds, tmp_val ); - return tmp_val; - } - - iterator& operator++() { _it->Next(); return *this; } - iterator& operator--() { _it->Prev(); return *this; } - - protected: - friend class level_pod_map; - iterator( ldb::Iterator* it ) - :_it(it){} - - std::shared_ptr _it; - }; - iterator begin() - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->SeekToFirst(); - - if( itr._it->status().IsNotFound() ) - { - FC_THROW_EXCEPTION( fc::key_not_found_exception, "" ); - } - if( !itr._it->status().ok() ) - { - FC_THROW_EXCEPTION( level_pod_map_failure, "database error: ${msg}", ("msg", itr._it->status().ToString() ) ); - } - - if( itr.valid() ) - { - return itr; - } - return iterator(); - } FC_RETHROW_EXCEPTIONS( warn, "error seeking to first" ) } - - iterator find( const Key& key ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice key_slice( (char*)&key, sizeof(key) ); - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->Seek( key_slice ); - if( itr.valid() && itr.key() == key ) - { - return itr; - } - return iterator(); - } FC_RETHROW_EXCEPTIONS( warn, "error finding ${key}", ("key",key) ) } - - iterator lower_bound( const Key& key ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice key_slice( (char*)&key, sizeof(key) ); - iterator itr( _db->NewIterator( _iter_options ) ); - itr._it->Seek( key_slice ); - if( itr.valid() ) - { - return itr; - } - return iterator(); - } FC_RETHROW_EXCEPTIONS( warn, "error finding ${key}", ("key",key) ) } - - bool last( Key& k ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::unique_ptr it( _db->NewIterator( _iter_options ) ); - FC_ASSERT( it != nullptr ); - it->SeekToLast(); - if( !it->Valid() ) - { - return false; - } - FC_ASSERT( sizeof( Key) == it->key().size() ); - k = *((Key*)it->key().data()); - return true; - } FC_RETHROW_EXCEPTIONS( warn, "error reading last item from database" ); } - - bool last( Key& k, Value& v ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - std::unique_ptr it( _db->NewIterator( _iter_options ) ); - FC_ASSERT( it != nullptr ); - it->SeekToLast(); - if( !it->Valid() ) - { - return false; - } - fc::datastream ds( it->value().data(), it->value().size() ); - fc::raw::unpack( ds, v ); - - FC_ASSERT( sizeof( Key) == it->key().size() ); - k = *((Key*)it->key().data()); - return true; - } FC_RETHROW_EXCEPTIONS( warn, "error reading last item from database" ); } - - void store( const Key& k, const Value& v, bool sync = false ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice ks( (char*)&k, sizeof(k) ); - auto vec = fc::raw::pack(v); - ldb::Slice vs( vec.data(), vec.size() ); - - auto status = _db->Put( sync ? _sync_options : _write_options, ks, vs ); - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_pod_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - } FC_RETHROW_EXCEPTIONS( warn, "error storing ${key} = ${value}", ("key",k)("value",v) ); } - - void remove( const Key& k, bool sync = false ) - { try { - FC_ASSERT( is_open(), "Database is not open!" ); - - ldb::Slice ks( (char*)&k, sizeof(k) ); - auto status = _db->Delete( sync ? _sync_options : _write_options, ks ); - if( status.IsNotFound() ) - { - FC_THROW_EXCEPTION( fc::key_not_found_exception, "unable to find key ${key}", ("key",k) ); - } - if( !status.ok() ) - { - FC_THROW_EXCEPTION( level_pod_map_failure, "database error: ${msg}", ("msg", status.ToString() ) ); - } - } FC_RETHROW_EXCEPTIONS( warn, "error removing ${key}", ("key",k) ); } - - private: - class key_compare : public leveldb::Comparator - { - public: - int Compare( const leveldb::Slice& a, const leveldb::Slice& b )const - { - FC_ASSERT( (a.size() == sizeof(Key)) && (b.size() == sizeof( Key )) ); - Key* ak = (Key*)a.data(); - Key* bk = (Key*)b.data(); - if( *ak < *bk ) return -1; - if( *ak == *bk ) return 0; - return 1; - } - - const char* Name()const { return "key_compare"; } - void FindShortestSeparator( std::string*, const leveldb::Slice& )const{} - void FindShortSuccessor( std::string* )const{}; - }; - - std::unique_ptr _db; - std::unique_ptr _cache; - key_compare _comparer; - - ldb::ReadOptions _read_options; - ldb::ReadOptions _iter_options; - ldb::WriteOptions _write_options; - ldb::WriteOptions _sync_options; - }; - -} } // graphene::db diff --git a/libraries/db/include/graphene/db/object_database.hpp b/libraries/db/include/graphene/db/object_database.hpp index 2a27bbdb..e070125e 100644 --- a/libraries/db/include/graphene/db/object_database.hpp +++ b/libraries/db/include/graphene/db/object_database.hpp @@ -20,9 +20,6 @@ #include #include -#include -#include - #include #include @@ -150,7 +147,6 @@ namespace graphene { namespace db { fc::path _data_dir; vector< vector< unique_ptr > > _index; - shared_ptr >> _object_id_to_object; }; } } // graphene::db diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index d8627e53..9b649c81 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -28,19 +28,12 @@ object_database::object_database() { _index.resize(255); _undo_db.enable(); - - _object_id_to_object = std::make_shared>>(); } object_database::~object_database(){} void object_database::close() { - if( _object_id_to_object->is_open() ) - { - flush(); - _object_id_to_object->close(); - } } const object* object_database::find_object( object_id_type id )const @@ -72,20 +65,14 @@ index& object_database::get_mutable_index(uint8_t space_id, uint8_t type_id) void object_database::flush() { - if( !_object_id_to_object->is_open() ) - return; - - vector next_ids; - for( auto& space : _index ) - for( const unique_ptr& type_index : space ) - if( type_index ) - { - type_index->inspect_all_objects([&] (const object& obj) { - _object_id_to_object->store(obj.id, obj.pack()); - }); - next_ids.push_back( type_index->get_next_id() ); - } - _object_id_to_object->store( object_id_type(), fc::raw::pack(next_ids) ); + ilog("Save object_database in ${d}", ("d", _data_dir)); + for( uint32_t space = 0; space < _index.size(); ++space ) + { + const auto types = _index[space].size(); + for( uint32_t type = 0; type < types; ++type ) + if( _index[space][type] ) + _index[space][type]->save( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + } } void object_database::wipe(const fc::path& data_dir) @@ -99,35 +86,12 @@ void object_database::wipe(const fc::path& data_dir) void object_database::open( const fc::path& data_dir ) { try { ilog("Open object_database in ${d}", ("d", data_dir)); - - _object_id_to_object->open( data_dir / "object_database" / "objects" ); - - for( auto& space : _index ) - { - for( auto& type_index : space ) - { - if( type_index ) - { - type_index->open( _object_id_to_object ); - } - } - } - try { - auto next_ids = fc::raw::unpack>( _object_id_to_object->fetch( object_id_type() ) ); - wdump((next_ids)); - for( auto id : next_ids ) - { - try { - get_mutable_index( id ).set_next_id( id ); - } FC_CAPTURE_AND_RETHROW( (id) ); - } - } - catch ( const fc::exception& e ) - { - // dlog( "unable to fetch next ids, must be new object_database\n ${e}", ("e",e.to_detail_string()) ); - } - _data_dir = data_dir; + for( uint32_t space = 0; space < _index.size(); ++space ) + for( uint32_t type = 0; type < _index[space].size(); ++type ) + if( _index[space][type] ) + _index[space][type]->open( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + } FC_CAPTURE_AND_RETHROW( (data_dir) ) } diff --git a/libraries/db/upgrade_leveldb.cpp b/libraries/db/upgrade_leveldb.cpp deleted file mode 100644 index e3e326a9..00000000 --- a/libraries/db/upgrade_leveldb.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include - -namespace graphene { namespace db { - - upgrade_db_mapper& upgrade_db_mapper::instance() - { - static upgrade_db_mapper mapper; - return mapper; - } - - int32_t upgrade_db_mapper::add_type( const std::string& type_name, const upgrade_db_function& function) - { - _upgrade_db_function_registry[type_name] = function; - return 0; - } - - - // this code has no graphene dependencies, and it - // could be moved to fc, if fc ever adds a leveldb dependency - void try_upgrade_db( const fc::path& dir, leveldb::DB* dbase, const char* record_type, size_t record_type_size ) - { - size_t old_record_type_size = 0; - std::string old_record_type; - fc::path record_type_filename = dir / "RECORD_TYPE"; - //if no RECORD_TYPE file exists - if ( !boost::filesystem::exists( record_type_filename ) ) - { - //must be original type for the database - old_record_type = record_type; - int last_char = old_record_type.length() - 1; - //strip version number from current_record_name and append 0 to set old_record_type (e.g. mytype0) - while (last_char >= 0 && isdigit(old_record_type[last_char])) - { - --last_char; - } - - //upgradeable record types should always end with version number - if( 'v' != old_record_type[last_char] ) - { - //ilog("Database ${db} is not upgradeable",("db",dir.to_native_ansi_path())); - return; - } - - ++last_char; - old_record_type[last_char] = '0'; - old_record_type.resize(last_char+1); - } - else //read record type from file - { - boost::filesystem::ifstream is(record_type_filename); - char buffer[120]; - is.getline(buffer,120); - old_record_type = buffer; - is >> old_record_type_size; - } - if (old_record_type != record_type) - { - //check if upgrade function in registry - auto upgrade_function_itr = upgrade_db_mapper::instance()._upgrade_db_function_registry.find( old_record_type ); - if (upgrade_function_itr != upgrade_db_mapper::instance()._upgrade_db_function_registry.end()) - { - ilog("Upgrading database ${db} from ${old} to ${new}",("db",dir.preferred_string()) - ("old",old_record_type) - ("new",record_type)); - //update database's RECORD_TYPE to new record type name - boost::filesystem::ofstream os(record_type_filename); - os << record_type << std::endl; - os << record_type_size; - //upgrade the database using upgrade function - upgrade_function_itr->second(dbase); - } - else - { - elog("In ${db}, record types ${old} and ${new} do not match, but no upgrade function found!", - ("db",dir.preferred_string())("old",old_record_type)("new",record_type)); - } - } - else if (old_record_type_size == 0) //if record type file never created, create it now - { - boost::filesystem::ofstream os(record_type_filename); - os << record_type << std::endl; - os << record_type_size; - } - else if (old_record_type_size != record_type_size) - { - elog("In ${db}, record type matches ${new}, but record sizes do not match!", - ("db",dir.preferred_string())("new",record_type)); - - } - } -} } // namespace graphene::db diff --git a/libraries/leveldb b/libraries/leveldb deleted file mode 160000 index 7d41e6f8..00000000 --- a/libraries/leveldb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7d41e6f89ff04ce9e6a742932924796f69c6e23d diff --git a/libraries/net/CMakeLists.txt b/libraries/net/CMakeLists.txt index b85b581e..7cce05cf 100644 --- a/libraries/net/CMakeLists.txt +++ b/libraries/net/CMakeLists.txt @@ -10,7 +10,7 @@ set(SOURCES node.cpp add_library( graphene_net ${SOURCES} ${HEADERS} ) target_link_libraries( graphene_net - PUBLIC fc graphene_db leveldb ) + PUBLIC fc graphene_db ) target_include_directories( graphene_net PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" PRIVATE "${CMAKE_SOURCE_DIR}/libraries/chain/include" diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 204ec638..4ba0273b 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -28,7 +28,7 @@ #include #include -#include +//#include @@ -76,8 +76,8 @@ namespace graphene { namespace net { > > potential_peer_set; //private: - typedef graphene::db::level_pod_map potential_peer_leveldb; - potential_peer_leveldb _leveldb; + //typedef graphene::db::level_pod_map potential_peer_leveldb; + //potential_peer_leveldb _leveldb; potential_peer_set _potential_peer_set; @@ -109,6 +109,7 @@ namespace graphene { namespace net { void peer_database_impl::open(const fc::path& databaseFilename) { + /* try { _leveldb.open(databaseFilename); @@ -135,16 +136,18 @@ namespace graphene { namespace net { iter = _potential_peer_set.erase(iter); } } + */ } void peer_database_impl::close() { - _leveldb.close(); + //_leveldb.close(); _potential_peer_set.clear(); } void peer_database_impl::clear() { + /* auto iter = _leveldb.begin(); while (iter.valid()) { @@ -159,6 +162,7 @@ namespace graphene { namespace net { // shouldn't happen, and if it does there's not much we can do } } + */ _potential_peer_set.clear(); } @@ -167,7 +171,7 @@ namespace graphene { namespace net { auto iter = _potential_peer_set.get().find(endpointToErase); if (iter != _potential_peer_set.get().end()) { - _leveldb.remove(iter->database_key); + //_leveldb.remove(iter->database_key); _potential_peer_set.get().erase(iter); } } @@ -178,16 +182,16 @@ namespace graphene { namespace net { if (iter != _potential_peer_set.get().end()) { _potential_peer_set.get().modify(iter, [&updatedRecord](potential_peer_database_entry& entry) { entry.peer_record = updatedRecord; }); - _leveldb.store(iter->database_key, updatedRecord); + //_leveldb.store(iter->database_key, updatedRecord); } else { uint32_t last_database_key; - _leveldb.last(last_database_key); + //_leveldb.last(last_database_key); uint32_t new_database_key = last_database_key + 1; potential_peer_database_entry new_database_entry(new_database_key, updatedRecord); _potential_peer_set.get().insert(new_database_entry); - _leveldb.store(new_database_key, updatedRecord); + //_leveldb.store(new_database_key, updatedRecord); } } @@ -314,12 +318,14 @@ namespace graphene { namespace net { std::vector peer_database::get_all()const { std::vector results; + /* auto itr = my->_leveldb.begin(); while( itr.valid() ) { results.push_back( itr.value() ); ++itr; } + */ return results; } diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 7010a29f..f17e7cd2 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -20,6 +20,7 @@ #include #include #include +#include using namespace graphene::db; From 109d95cbda947be90616d622ab718184b329f5ff Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Tue, 16 Jun 2015 17:02:13 -0400 Subject: [PATCH 012/354] Fix build --- tests/intense/block_tests.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/intense/block_tests.cpp b/tests/intense/block_tests.cpp index 993cf074..1a971b29 100644 --- a/tests/intense/block_tests.cpp +++ b/tests/intense/block_tests.cpp @@ -166,7 +166,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) // size() < num_active_keys is possible when some keys are duplicates create_op.active.weight_threshold = create_op.active.auths.size(); - create_op.memo_key = key_ids[ *(it++) ] ; + create_op.options.memo_key = key_ids[ *(it++) ] ; create_op.registrar = sam_account_object.id; trx.operations.push_back( create_op ); // trx.sign( sam_key ); @@ -191,6 +191,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) update_op.account = alice_account_id; update_op.owner = authority(); update_op.active = authority(); + update_op.new_options = create_op.options; for( int owner_index=0; owner_indexauths[ key_ids[ *(it++) ] ] = 1; @@ -200,7 +201,8 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) update_op.active->auths[ key_ids[ *(it++) ] ] = 1; // size() < num_active_keys is possible when some keys are duplicates update_op.active->weight_threshold = update_op.active->auths.size(); - update_op.memo_key = key_ids[ *(it++) ] ; + FC_ASSERT( update_op.new_options.valid() ); + update_op.new_options->memo_key = key_ids[ *(it++) ] ; trx.operations.push_back( update_op ); for( int i=0; i Date: Tue, 16 Jun 2015 18:45:33 -0400 Subject: [PATCH 013/354] tests pass again --- libraries/chain/block.cpp | 1 + libraries/chain/block_database.cpp | 71 ++++++++++++------ libraries/chain/db_block.cpp | 6 +- libraries/chain/db_management.cpp | 4 ++ libraries/db/include/graphene/db/index.hpp | 6 +- libraries/db/object_database.cpp | 1 + tests/tests/block_tests.cpp | 84 +++++++++++++++++++--- 7 files changed, 139 insertions(+), 34 deletions(-) diff --git a/libraries/chain/block.cpp b/libraries/chain/block.cpp index ad731b84..47ab8d37 100644 --- a/libraries/chain/block.cpp +++ b/libraries/chain/block.cpp @@ -53,6 +53,7 @@ namespace graphene { namespace chain { checksum_type signed_block::calculate_merkle_root()const { if( transactions.size() == 0 ) return checksum_type(); + wdump((transactions.size())); vector ids; ids.resize( ((transactions.size() + 1)/2)*2 ); diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 94389571..16cf76db 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -8,12 +8,29 @@ struct index_entry uint32_t block_size = 0; block_id_type block_id; }; + }} +FC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) ); + +namespace graphene { namespace chain { void block_database::open( const fc::path& dbdir ) -{ - _block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary ); - _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary ); -} +{ try { + idump((sizeof(index_entry)) ); + fc::create_directories(dbdir); + _block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit); + _blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit); + + if( !fc::exists( dbdir/"index" ) ) + { + _block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc); + _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc); + } + else + { + _block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out ); + _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out ); + } +} FC_CAPTURE_AND_RETHROW( (dbdir) ) } bool block_database::is_open()const @@ -51,7 +68,7 @@ void block_database::store( const block_id_type& id, const signed_block& b ) void block_database::remove( const block_id_type& id ) -{ +{ try { index_entry e; auto index_pos = sizeof(e)*block_header::num_from_id(id); _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); @@ -60,12 +77,13 @@ void block_database::remove( const block_id_type& id ) _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); - FC_ASSERT( e.block_id == id ); - - e.block_size = 0; - _block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) ); - _block_num_to_pos.write( (char*)&e, sizeof(e) ); -} + if( e.block_id == id ) + { + e.block_size = 0; + _block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) ); + _block_num_to_pos.write( (char*)&e, sizeof(e) ); + } +} FC_CAPTURE_AND_RETHROW( (id) ) } @@ -100,7 +118,7 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const optional block_database::fetch_optional( const block_id_type& id )const -{ +{ try { index_entry e; auto index_pos = sizeof(e)*block_header::num_from_id(id); _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); @@ -114,25 +132,31 @@ optional block_database::fetch_optional( const block_id_type& id ) vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); -} + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; +} FC_CAPTURE_AND_RETHROW( (id) ) } optional block_database::fetch_by_number( uint32_t block_num )const -{ +{ try { index_entry e; auto index_pos = sizeof(e)*block_num; _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); - _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); + wdump((int64_t(_block_num_to_pos.tellg())) ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); + wdump((block_num)(e)); vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); -} + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; +} FC_CAPTURE_AND_RETHROW( (block_num) ) } optional block_database::last()const @@ -145,14 +169,21 @@ optional block_database::last()const _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); - + while( e.block_size == 0 && _blocks.tellg() > 0 ) + { + _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + } + if( e.block_size == 0 ) return optional(); vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); - return fc::raw::unpack(data); + auto result = fc::raw::unpack(data); + wdump((result)); + return result; } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e8493820..d82dde79 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -84,7 +84,6 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) { try { if( !(skip&skip_fork_db) ) { - wdump((new_block.id())(new_block.previous)); auto 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() ) @@ -115,12 +114,13 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) try { auto session = _undo_db.start_undo_session(); apply_block( (*ritr)->data, skip ); - _block_id_to_block.store( new_block.id(), (*ritr)->data ); + _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); session.commit(); } catch ( const fc::exception& e ) { except = e; } if( except ) { + wdump((except->to_detail_string())); elog( "Encountered error when switching to a longer fork at id ${id}. Going back.", ("id", (*ritr)->id) ); // remove the rest of branches.first from the fork_db, those blocks are invalid @@ -325,7 +325,7 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) { try { _applied_ops.clear(); - FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root() ); + FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) ); const witness_object& signing_witness = validate_block_header(skip, next_block); const auto& global_props = get_global_properties(); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 5651fe1e..5ab3c837 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -41,7 +41,10 @@ void database::open( const fc::path& data_dir, const genesis_allocation& initial _block_id_to_block.open( data_dir / "database" / "block_num_to_block" ); if( !find(global_property_id_type()) ) + { + ilog( "Init Genesis State" ); init_genesis(initial_allocation); + } _pending_block.previous = head_block_id(); _pending_block.timestamp = head_block_time(); @@ -96,6 +99,7 @@ void database::close(uint32_t blocks_to_rewind) for(uint32_t i = 0; i < blocks_to_rewind && head_block_num() > 0; ++i) pop_block(); + object_database::flush(); object_database::close(); if( _block_id_to_block.is_open() ) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index af95414d..84c4103a 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -177,7 +177,7 @@ namespace graphene { namespace db { virtual void set_next_id( object_id_type id )override { _next_id = id; } virtual void open( const path& db )override - { + { if( !fc::exists( db ) ) return; fc::file_mapping fm( db.generic_string().c_str(), fc::read_only ); fc::mapped_region mr( fm, fc::read_only, 0, fc::file_size(db) ); @@ -195,7 +195,9 @@ namespace graphene { namespace db { virtual void save( const path& db ) override { - std::ofstream out( db.generic_string(), std::ofstream::binary ); + std::ofstream out( db.generic_string(), + std::ofstream::binary | std::ofstream::out | std::ofstream::trunc ); + FC_ASSERT( out ); out.write( (char*)&_next_id, sizeof(_next_id) ); this->inspect_all_objects( [&]( const object& o ) { auto vec = fc::raw::pack( static_cast(o) ); diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index 9b649c81..dc5cba58 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -68,6 +68,7 @@ void object_database::flush() ilog("Save object_database in ${d}", ("d", _data_dir)); for( uint32_t space = 0; space < _index.size(); ++space ) { + fc::create_directories( _data_dir / "object_database" / fc::to_string(space) ); const auto types = _index[space].size(); for( uint32_t type = 0; type < types; ++type ) if( _index[space][type] ) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index c74e182d..3ff4ea90 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -37,6 +37,73 @@ using namespace graphene::chain; BOOST_AUTO_TEST_SUITE(block_tests) +BOOST_AUTO_TEST_CASE( block_database_test ) +{ + try { + fc::temp_directory data_dir; + + block_database bdb; + bdb.open( data_dir.path() ); + FC_ASSERT( bdb.is_open() ); + bdb.close(); + FC_ASSERT( !bdb.is_open() ); + bdb.open( data_dir.path() ); + + signed_block b; + for( uint32_t i = 0; i < 5; ++i ) + { + if( i > 0 ) b.previous = b.id(); + b.witness = witness_id_type(i+1); + edump((b)); + bdb.store( b.id(), b ); + + auto fetch = bdb.fetch_by_number( b.block_num() ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + fetch = bdb.fetch_by_number( i+1 ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + fetch = bdb.fetch_optional( b.id() ); + idump((fetch)); + FC_ASSERT( fetch.valid() ); + FC_ASSERT( fetch->witness == b.witness ); + } + ilog("-----------" ); + + for( uint32_t i = 1; i < 5; ++i ) + { + auto blk = bdb.fetch_by_number( i ); + FC_ASSERT( blk.valid() ); + idump((blk)(i)); + FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); + } + + auto last = bdb.last(); + FC_ASSERT( last ); + FC_ASSERT( last->id() == b.id() ); + + bdb.close(); + bdb.open( data_dir.path() ); + last = bdb.last(); + FC_ASSERT( last ); + FC_ASSERT( last->id() == b.id() ); + + for( uint32_t i = 0; i < 5; ++i ) + { + auto blk = bdb.fetch_by_number( i+1 ); + FC_ASSERT( blk.valid() ); + idump((blk)(i)); + FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); + } + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( generate_empty_blocks ) { try { @@ -142,7 +209,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) db2.open( data_dir2.path(), genesis_allocation() ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); - for( uint32_t i = 0; i < 20; ++i ) + for( uint32_t i = 0; i < 10; ++i ) { now += db1.block_interval(); auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -150,20 +217,19 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) db2.push_block(b); } FC_CAPTURE_AND_RETHROW( ("db2") ); } - for( uint32_t i = 20; i < 23; ++i ) + for( uint32_t i = 10; i < 13; ++i ) { now += db1.block_interval(); auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); } string db1_tip = db1.head_block_id().str(); - for( uint32_t i = 23; i < 26; ++i ) + for( uint32_t i = 13; i < 16; ++i ) { now += db2.block_interval(); auto b = db2.generate_block( now, db2.get_scheduled_witness( db2.get_slot_at_time( now ) ).first, delegate_priv_key ); // notify both databases of the new block. // only db2 should switch to the new fork, db1 should not db1.push_block(b); - db2.push_block(b); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str()); } @@ -171,8 +237,8 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) //The two databases are on distinct forks now, but at the same height. Make a block on db2, make it invalid, then //pass it to db1 and assert that db1 doesn't switch to the new fork. signed_block good_block; - BOOST_CHECK_EQUAL(db1.head_block_num(), 23); - BOOST_CHECK_EQUAL(db2.head_block_num(), 23); + BOOST_CHECK_EQUAL(db1.head_block_num(), 13); + BOOST_CHECK_EQUAL(db2.head_block_num(), 13); { now += db2.block_interval(); auto b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -180,14 +246,14 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) b.transactions.emplace_back(signed_transaction()); b.transactions.back().operations.emplace_back(transfer_operation()); b.sign(delegate_priv_key); - BOOST_CHECK_EQUAL(b.block_num(), 24); + BOOST_CHECK_EQUAL(b.block_num(), 14); BOOST_CHECK_THROW(db1.push_block(b), fc::exception); } - BOOST_CHECK_EQUAL(db1.head_block_num(), 23); + BOOST_CHECK_EQUAL(db1.head_block_num(), 13); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); // assert that db1 switches to new fork with good block - BOOST_CHECK_EQUAL(db2.head_block_num(), 24); + BOOST_CHECK_EQUAL(db2.head_block_num(), 14); db1.push_block(good_block); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str()); } catch (fc::exception& e) { From 57e569a6722cf4576799febf6772f71b4f5ba7b4 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Wed, 17 Jun 2015 11:09:59 -0400 Subject: [PATCH 014/354] Update fc to fix win32 build error, fixes #51 --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index c09035db..dd1c77b3 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 +Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 From c9328cc7f44771597fcf8b2ce2a68b7cdd79ea43 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 17 Jun 2015 11:47:50 -0400 Subject: [PATCH 015/354] Resolve #52 --- libraries/app/application.cpp | 11 +++--- libraries/chain/db_init.cpp | 22 ++++++------ libraries/chain/db_management.cpp | 4 +-- .../chain/include/graphene/chain/database.hpp | 36 ++++++++++++++++--- tests/app/main.cpp | 2 +- tests/benchmarks/genesis_allocation.cpp | 11 +++--- tests/tests/block_tests.cpp | 18 +++++----- 7 files changed, 67 insertions(+), 37 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 13a62f20..281fa8bf 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -149,9 +149,10 @@ namespace detail { fc::create_directories(_data_dir / "blockchain/dblock"); auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); - genesis_allocation initial_allocation = {{graphene::chain::public_key_type(nathan_key.get_public_key()), 1}}; + genesis_state_type initial_state; + initial_state.allocation_targets.emplace_back("nathan", address(public_key_type(nathan_key.get_public_key())), 1); if( _options->count("genesis-json") ) - initial_allocation = fc::json::from_file(_options->at("genesis-json").as()).as(); + initial_state = fc::json::from_file(_options->at("genesis-json").as()).as(); else dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key))); @@ -161,12 +162,12 @@ namespace detail { if( _options->count("replay-blockchain") ) { ilog("Replaying blockchain on user request."); - _chain_db->reindex(_data_dir/"blockchain", initial_allocation); + _chain_db->reindex(_data_dir/"blockchain", initial_state); } else if( clean ) - _chain_db->open(_data_dir / "blockchain", initial_allocation); + _chain_db->open(_data_dir / "blockchain", initial_state); else { wlog("Detected unclean shutdown. Replaying blockchain..."); - _chain_db->reindex(_data_dir / "blockchain", initial_allocation); + _chain_db->reindex(_data_dir / "blockchain", initial_state); } reset_p2p_node(_data_dir); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 95530b79..7f4d3b90 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -127,7 +127,7 @@ void database::initialize_indexes() add_index< primary_index< simple_index< witness_schedule_object > > >(); } -void database::init_genesis(const genesis_allocation& initial_allocation) +void database::init_genesis(const genesis_state_type& genesis_state) { try { _undo_db.disable(); @@ -222,7 +222,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation) for( const witness_id_type& wit : init_witnesses ) p.active_witnesses.insert( wit ); p.next_available_vote_id = delegates_and_witnesses * 2; - p.chain_id = fc::digest(initial_allocation); + p.chain_id = fc::digest(genesis_state); }); (void)properties; @@ -253,11 +253,11 @@ void database::init_genesis(const genesis_allocation& initial_allocation) assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); (void)core_asset; - if( !initial_allocation.empty() ) + if( !genesis_state.allocation_targets.empty() ) { share_type total_allocation = 0; - for( const auto& handout : initial_allocation ) - total_allocation += handout.second; + for( const auto& handout : genesis_state.allocation_targets ) + total_allocation += handout.weight; auto mangle_to_name = [](const fc::static_variant& key) { string addr = string(key.which() == std::decay::type::tag
::value? key.get
() @@ -276,22 +276,22 @@ void database::init_genesis(const genesis_allocation& initial_allocation) fc::time_point start_time = fc::time_point::now(); - for( const auto& handout : initial_allocation ) + for( const auto& handout : genesis_state.allocation_targets ) { - asset amount(handout.second); + asset amount(handout.weight); amount.amount = ((fc::uint128(amount.amount.value) * GRAPHENE_INITIAL_SUPPLY)/total_allocation.value).to_uint64(); if( amount.amount == 0 ) { - wlog("Skipping zero allocation to ${k}", ("k", handout.first)); + wlog("Skipping zero allocation to ${k}", ("k", handout.name)); continue; } signed_transaction trx; - trx.operations.emplace_back(key_create_operation({asset(), genesis_account.id, handout.first})); + trx.operations.emplace_back(key_create_operation({asset(), genesis_account.id, handout.addr})); relative_key_id_type key_id(0); authority account_authority(1, key_id, 1); account_create_operation cop; - cop.name = mangle_to_name(handout.first); + cop.name = handout.name; cop.registrar = account_id_type(1); cop.active = account_authority; cop.owner = account_authority; @@ -325,7 +325,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation) fc::microseconds duration = fc::time_point::now() - start_time; ilog("Finished allocating to ${n} accounts in ${t} milliseconds.", - ("n", initial_allocation.size())("t", duration.count() / 1000)); + ("n", genesis_state.allocation_targets.size())("t", duration.count() / 1000)); } _undo_db.enable(); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 5ab3c837..56d09dc5 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -33,7 +33,7 @@ database::~database(){ _pending_block_session->commit(); } -void database::open( const fc::path& data_dir, const genesis_allocation& initial_allocation ) +void database::open( const fc::path& data_dir, const genesis_state_type& initial_allocation ) { try { ilog("Open database in ${d}", ("d", data_dir)); object_database::open( data_dir ); @@ -55,7 +55,7 @@ void database::open( const fc::path& data_dir, const genesis_allocation& initial } FC_CAPTURE_AND_RETHROW( (data_dir) ) } -void database::reindex(fc::path data_dir, const genesis_allocation& initial_allocation) +void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation) { try { wipe(data_dir, false); open(data_dir, initial_allocation); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 58e7827b..b1b170af 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -38,7 +38,30 @@ namespace graphene { namespace chain { using graphene::db::abstract_object; using graphene::db::object; - typedef vector, share_type >> genesis_allocation; + struct genesis_state_type { + struct allocation_target_type { + allocation_target_type(const string& name = string(), const address& addr = address(), share_type weight = share_type()) + : name(name), addr(addr), weight(weight){} + string name; + address addr; + share_type weight; + }; + struct initial_witness_type { + /// Must correspond to one of the allocation targets. + string owner_name; + public_key_type block_signing_key; + secret_hash_type initial_secret; + }; + struct initial_committee_member_type { + /// Must correspond to one of the allocation targets. + string owner_name; + }; + + chain_parameters initial_parameters; + vector allocation_targets; + vector initial_witnesses; + vector initial_committee; + }; /** * @class database @@ -67,14 +90,14 @@ namespace graphene { namespace chain { skip_merkle_check = 0x200 ///< used while reindexing }; - void open(const fc::path& data_dir, const genesis_allocation& initial_allocation = genesis_allocation()); + void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type()); /** * @brief Rebuild object graph from block history and open detabase * * This method may be called after or instead of @ref database::open, and will rebuild the object graph by * replaying blockchain history. When this method exits successfully, the database will be open. */ - void reindex(fc::path data_dir, const genesis_allocation& initial_allocation = genesis_allocation()); + void reindex(fc::path data_dir, const genesis_state_type& initial_allocation = genesis_state_type()); /** * @brief wipe Delete database from disk, and potentially the raw chain as well. @@ -205,7 +228,7 @@ namespace graphene { namespace chain { void initialize_evaluators(); /// Reset the object graph in-memory void initialize_indexes(); - void init_genesis(const genesis_allocation& initial_allocation = genesis_allocation()); + void init_genesis(const genesis_state_type& genesis_state = genesis_state_type()); template void register_evaluator() @@ -423,3 +446,8 @@ namespace graphene { namespace chain { } } } + +FC_REFLECT(graphene::chain::genesis_state_type::allocation_target_type, (name)(addr)(weight)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)(initial_secret)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name)) +FC_REFLECT(graphene::chain::genesis_state_type, (initial_parameters)(allocation_targets)(initial_witnesses)(initial_committee)) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 36278486..d7630550 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) fc::temp_directory app_dir; fc::temp_directory app2_dir; fc::temp_file genesis_json; - fc::json::save_to_file(genesis_allocation(), genesis_json.path()); + fc::json::save_to_file(genesis_state_type(), genesis_json.path()); fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); diff --git a/tests/benchmarks/genesis_allocation.cpp b/tests/benchmarks/genesis_allocation.cpp index febc5709..a9d73f1f 100644 --- a/tests/benchmarks/genesis_allocation.cpp +++ b/tests/benchmarks/genesis_allocation.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE( operation_sanity_check ) BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench ) { try { - genesis_allocation allocation; + genesis_state_type genesis_state; #ifdef NDEBUG ilog("Running in release mode."); @@ -57,14 +57,15 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench ) #endif for( int i = 0; i < account_count; ++i ) - allocation.emplace_back(public_key_type(fc::ecc::private_key::regenerate(fc::digest(i)).get_public_key()), - GRAPHENE_INITIAL_SUPPLY / account_count); + genesis_state.allocation_targets.emplace_back("target"+fc::to_string(i), + public_key_type(fc::ecc::private_key::regenerate(fc::digest(i)).get_public_key()), + GRAPHENE_INITIAL_SUPPLY / account_count); fc::temp_directory data_dir(fc::current_path()); { database db; - db.open(data_dir.path(), allocation); + db.open(data_dir.path(), genesis_state); for( int i = 11; i < account_count + 11; ++i) BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_INITIAL_SUPPLY / account_count); @@ -110,7 +111,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench ) auto start_time = fc::time_point::now(); wlog( "about to start reindex..." ); - db.reindex(data_dir.path(), allocation); + db.reindex(data_dir.path(), genesis_state); ilog("Replayed database in ${t} milliseconds.", ("t", (fc::time_point::now() - start_time).count() / 1000)); for( int i = 0; i < blocks_to_produce; ++i ) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 958b30d2..169fb2f7 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); { database db; - db.open(data_dir.path(), genesis_allocation() ); + db.open(data_dir.path(), genesis_state_type() ); b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); for( uint32_t i = 1; i < 200; ++i ) @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE( undo_block ) fc::temp_directory data_dir; { database db; - db.open(data_dir.path(), genesis_allocation() ); + db.open(data_dir.path(), genesis_state_type() ); fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); @@ -204,15 +204,15 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); database db1; - db1.open( data_dir1.path(), genesis_allocation() ); + db1.open(data_dir1.path(), genesis_state_type()); database db2; - db2.open( data_dir2.path(), genesis_allocation() ); + db2.open(data_dir2.path(), genesis_state_type()); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); for( uint32_t i = 0; i < 10; ++i ) { now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); try { db2.push_block(b); } FC_CAPTURE_AND_RETHROW( ("db2") ); @@ -220,13 +220,13 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) for( uint32_t i = 10; i < 13; ++i ) { now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); } string db1_tip = db1.head_block_id().str(); for( uint32_t i = 13; i < 16; ++i ) { now += db2.block_interval(); - auto b = db2.generate_block( now, db2.get_scheduled_witness( db2.get_slot_at_time( now ) ).first, delegate_priv_key ); + auto b = db2.generate_block(now, db2.get_scheduled_witness(db2.get_slot_at_time(now)).first, delegate_priv_key); // notify both databases of the new block. // only db2 should switch to the new fork, db1 should not db1.push_block(b); @@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) BOOST_CHECK_EQUAL(db2.head_block_num(), 13); { now += db2.block_interval(); - auto b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key); good_block = b; b.transactions.emplace_back(signed_transaction()); b.transactions.back().operations.emplace_back(transfer_operation()); @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) BOOST_AUTO_TEST_CASE( undo_pending ) { try { - fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); + fc::time_point_sec now(GRAPHENE_GENESIS_TIMESTAMP); fc::temp_directory data_dir; { database db; From 8f739ac7678ba0a06bedf6f7f9c989e521aff346 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 17 Jun 2015 12:14:28 -0400 Subject: [PATCH 016/354] Dramatically accelerate chain_test, fix core asset burn bug When generating blocks until a timestamp, we now generate one block immediately, then skip blocks until the timestamp, and generate a final block then. Also, this exposed a bug in the witness budget handling which caused the undesired burning of core asset. This bug is now fixed. --- libraries/chain/db_maint.cpp | 9 +++++---- tests/common/database_fixture.cpp | 3 ++- tests/common/database_fixture.hpp | 2 +- tests/tests/block_tests.cpp | 4 ++-- tests/tests/operation_tests2.cpp | 3 +++ 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 55a531ab..5c09b395 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -272,8 +272,7 @@ void database::process_budget() }); modify(dpo, [&]( dynamic_global_property_object& _dpo ) { - // Should this be +=? - _dpo.witness_budget = witness_budget; + _dpo.witness_budget += witness_budget; _dpo.last_budget_time = now; }); @@ -406,8 +405,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g next_maintenance_time = time_point_sec() + (((next_block.timestamp.sec_since_epoch() / maintenance_interval) + 1) * maintenance_interval); else - next_maintenance_time += maintenance_interval; - assert( next_maintenance_time > next_block.timestamp ); + // It's possible we have missed blocks for at least a maintenance interval. + // In this case, we'll need to bump the next maintenance time more than once. + do next_maintenance_time += maintenance_interval; + while( next_maintenance_time < head_block_time() ); } modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) { diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index d396826b..fcecaa43 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -274,8 +274,9 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i { if( miss_intermediate_blocks ) { + generate_block(); auto slots_to_miss = db.get_slot_at_time(timestamp) - 1; - assert(slots_to_miss > 0); + if( slots_to_miss <= 0 ) return; generate_block(~0, generate_private_key("genesis"), slots_to_miss); return; } diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index f17e7cd2..649df558 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -126,7 +126,7 @@ struct database_fixture { * @brief Generates blocks until the head block time matches or exceeds timestamp * @param timestamp target time to generate blocks until */ - void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = false); + void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true); account_create_operation make_account( const std::string& name = "nathan", diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 169fb2f7..0fed7963 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -568,7 +568,7 @@ BOOST_FIXTURE_TEST_CASE( short_order_expiration, database_fixture ) BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); auto id = short_itr->id; - generate_blocks(op.expiration); + generate_blocks(op.expiration, false); test = &get_asset("TEST"); core = &asset_id_type()(db); nathan = &get_account("nathan"); @@ -611,7 +611,7 @@ BOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture ) BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); auto id = limit_itr->id; - generate_blocks(op.expiration); + generate_blocks(op.expiration, false); test = &get_asset("TEST"); core = &asset_id_type()(db); nathan = &get_account("nathan"); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 7e72bbbf..aaebbdc1 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -671,9 +671,12 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) } // auto supply = asset_id_type()(db).dynamic_data(db).current_supply; + verify_asset_supplies(); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + verify_asset_supplies(); BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().total_burned.value, 1000); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + verify_asset_supplies(); BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().total_burned.value, 2000); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); BOOST_CHECK(!db.get(worker_id_type()).is_active(db.head_block_time())); From 34388fabee6989cf18eb4838fc6fc063c7f00f38 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Wed, 17 Jun 2015 12:33:38 -0400 Subject: [PATCH 017/354] Further cleanup after LevelDB removal --- CMakeLists.txt | 8 +- libraries/chain/block.cpp | 1 - libraries/chain/block_database.cpp | 24 +- .../include/graphene/chain/block_database.hpp | 17 ++ .../include/graphene/db/cached_level_map.hpp | 224 ------------------ .../db/include/graphene/db/fast_level_map.hpp | 143 ----------- .../include/graphene/db/upgrade_leveldb.hpp | 132 ----------- libraries/leveldb-msvc/include/leveldb/db.h | 6 - libraries/leveldb-msvc/include/unistd.h | 1 - libraries/leveldb-msvc/readme.txt | 5 - 10 files changed, 37 insertions(+), 524 deletions(-) delete mode 100644 libraries/db/include/graphene/db/cached_level_map.hpp delete mode 100644 libraries/db/include/graphene/db/fast_level_map.hpp delete mode 100644 libraries/db/include/graphene/db/upgrade_leveldb.hpp delete mode 100644 libraries/leveldb-msvc/include/leveldb/db.h delete mode 100644 libraries/leveldb-msvc/include/unistd.h delete mode 100644 libraries/leveldb-msvc/readme.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index d60399d2..b07ef54c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)") ENDIF() if( WIN32 ) + message( STATUS "Configuring Graphene on WIN32") set( DB_VERSION 60 ) set( BDB_STATIC_LIBS 1 ) @@ -98,13 +99,6 @@ if( WIN32 ) SET(TCL_LIBRARY ${TCL_LIBS}) else( WIN32 ) # Apple AND Linux - list(APPEND LEVELDB_BUILD_DEFINES LEVELDB_PLATFORM_POSIX LEVELDB_ATOMIC_PRESENT) - if( APPLE ) - list(APPEND LEVELDB_BUILD_DEFINES OS_MACOSX) - else() # Linux - list(APPEND LEVELDB_BUILD_DEFINES OS_LINUX) - list(APPEND LEVELDB_BUILD_LIBRARIES pthread) - endif() find_library(READLINE_LIBRARIES NAMES readline) find_path(READLINE_INCLUDE_DIR readline/readline.h) diff --git a/libraries/chain/block.cpp b/libraries/chain/block.cpp index 47ab8d37..ad731b84 100644 --- a/libraries/chain/block.cpp +++ b/libraries/chain/block.cpp @@ -53,7 +53,6 @@ namespace graphene { namespace chain { checksum_type signed_block::calculate_merkle_root()const { if( transactions.size() == 0 ) return checksum_type(); - wdump((transactions.size())); vector ids; ids.resize( ((transactions.size() + 1)/2)*2 ); diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 16cf76db..2b151f7b 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #include namespace graphene { namespace chain { @@ -8,7 +25,7 @@ struct index_entry uint32_t block_size = 0; block_id_type block_id; }; - }} + }} FC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) ); namespace graphene { namespace chain { @@ -146,9 +163,7 @@ optional block_database::fetch_by_number( uint32_t block_num )cons FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); - wdump((int64_t(_block_num_to_pos.tellg())) ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); - wdump((block_num)(e)); vector data( e.block_size ); _blocks.seekg( e.block_pos ); @@ -175,14 +190,13 @@ optional block_database::last()const _block_num_to_pos.read( (char*)&e, sizeof(e) ); } - if( e.block_size == 0 ) + if( e.block_size == 0 ) return optional(); vector data( e.block_size ); _blocks.seekg( e.block_pos ); _blocks.read( data.data(), e.block_size ); auto result = fc::raw::unpack(data); - wdump((result)); return result; } diff --git a/libraries/chain/include/graphene/chain/block_database.hpp b/libraries/chain/include/graphene/chain/block_database.hpp index 66af6ece..0ca9bc9c 100644 --- a/libraries/chain/include/graphene/chain/block_database.hpp +++ b/libraries/chain/include/graphene/chain/block_database.hpp @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #pragma once #include #include diff --git a/libraries/db/include/graphene/db/cached_level_map.hpp b/libraries/db/include/graphene/db/cached_level_map.hpp deleted file mode 100644 index 0118f62c..00000000 --- a/libraries/db/include/graphene/db/cached_level_map.hpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once -#include -#include -#include - -namespace graphene { namespace db { - - template> - class cached_level_map - { - public: - void open( const fc::path& dir, bool create = true, size_t leveldb_cache_size = 0, bool write_through = true, bool sync_on_write = false ) - { try { - _db.open( dir, create, leveldb_cache_size ); - for( auto itr = _db.begin(); itr.valid(); ++itr ) - _cache.emplace_hint( _cache.end(), itr.key(), itr.value() ); - _write_through = write_through; - _sync_on_write = sync_on_write; - } FC_CAPTURE_AND_RETHROW( (dir)(create)(leveldb_cache_size)(write_through)(sync_on_write) ) } - - void close() - { try { - if( _db.is_open() ) flush(); - _db.close(); - _cache.clear(); - _dirty_store.clear(); - _dirty_remove.clear(); - } FC_CAPTURE_AND_RETHROW() } - - void set_write_through( bool write_through ) - { try { - if( write_through == _write_through ) - return; - - if( write_through ) - flush(); - - _write_through = write_through; - } FC_CAPTURE_AND_RETHROW( (write_through) ) } - - void flush() - { try { - typename level_map::write_batch batch = _db.create_batch( _sync_on_write ); - for( const auto& key : _dirty_store ) - batch.store( key, _cache.at( key ) ); - for( const auto& key : _dirty_remove ) - batch.remove( key ); - batch.commit(); - - _dirty_store.clear(); - _dirty_remove.clear(); - } FC_CAPTURE_AND_RETHROW() } - - fc::optional fetch_optional( const Key& key )const - { try { - const auto itr = _cache.find( key ); - if( itr != _cache.end() ) - return itr->second; - return fc::optional(); - } FC_CAPTURE_AND_RETHROW( (key) ) } - - Value fetch( const Key& key )const - { try { - const auto itr = _cache.find( key ); - if( itr != _cache.end() ) - return itr->second; - FC_CAPTURE_AND_THROW( fc::key_not_found_exception, (key) ); - } FC_CAPTURE_AND_RETHROW( (key) ) } - - void store( const Key& key, const Value& value ) - { try { - _cache[ key ] = value; - if( _write_through ) - { - _db.store( key, value, _sync_on_write ); - } - else - { - _dirty_store.insert( key ); - _dirty_remove.erase( key ); - } - } FC_CAPTURE_AND_RETHROW( (key)(value) ) } - - void remove( const Key& key ) - { try { - _cache.erase( key ); - if( _write_through ) - { - _db.remove( key, _sync_on_write ); - } - else - { - _dirty_store.erase( key ); - _dirty_remove.insert( key ); - } - } FC_CAPTURE_AND_RETHROW( (key) ) } - - size_t size()const - { try { - return _cache.size(); - } FC_CAPTURE_AND_RETHROW() } - - bool last( Key& key )const - { try { - const auto ritr = _cache.crbegin(); - if( ritr != _cache.crend() ) - { - key = ritr->first; - return true; - } - return false; - } FC_CAPTURE_AND_RETHROW( (key) ) } - - bool last( Key& key, Value& value ) - { try { - const auto ritr = _cache.crbegin(); - if( ritr != _cache.crend() ) - { - key = ritr->first; - value = ritr->second; - return true; - } - return false; - } FC_CAPTURE_AND_RETHROW( (key)(value) ) } - - class iterator - { - public: - iterator(){} - bool valid()const { return _it != _end; } - - Key key()const { return _it->first; } - Value value()const { return _it->second; } - - iterator& operator++() { ++_it; return *this; } - iterator operator++(int) { - auto backup = *this; - ++_it; - return backup; - } - - iterator& operator--() - { - if( _it == _begin ) - _it = _end; - else - --_it; - return *this; - } - - iterator operator--(int) { - auto backup = *this; - operator--(); - return backup; - } - - void reset() { _it = _end; } - - protected: - friend class cached_level_map; - iterator( typename CacheType::const_iterator it, typename CacheType::const_iterator begin, typename CacheType::const_iterator end ) - :_it(it),_begin(begin),_end(end) - { } - - typename CacheType::const_iterator _it; - typename CacheType::const_iterator _begin; - typename CacheType::const_iterator _end; - }; - - iterator begin()const - { - return iterator( _cache.begin(), _cache.begin(), _cache.end() ); - } - - iterator last()const - { - if( _cache.empty() ) - return iterator( _cache.end(), _cache.begin(), _cache.end() ); - return iterator( --_cache.end(), _cache.begin(), _cache.end() ); - } - - iterator find( const Key& key )const - { - return iterator( _cache.find(key), _cache.begin(), _cache.end() ); - } - - iterator lower_bound( const Key& key )const - { - return iterator( _cache.lower_bound(key), _cache.begin(), _cache.end() ); - } - - // TODO: Iterate over cache instead - void export_to_json( const fc::path& path )const - { try { - _db.export_to_json( path ); - } FC_CAPTURE_AND_RETHROW( (path) ) } - - private: - level_map _db; - CacheType _cache; - std::set _dirty_store; - std::set _dirty_remove; - bool _write_through = true; - bool _sync_on_write = false; - }; - -} } diff --git a/libraries/db/include/graphene/db/fast_level_map.hpp b/libraries/db/include/graphene/db/fast_level_map.hpp deleted file mode 100644 index 882d6326..00000000 --- a/libraries/db/include/graphene/db/fast_level_map.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once -#include - -namespace graphene { namespace db { - -template -class fast_level_map -{ - level_map _ldb; - fc::optional _ldb_path; - bool _ldb_enabled = true; - - std::unordered_map _cache; - -public: - - ~fast_level_map() - { - close(); - } - - void open( const fc::path& path ) - { try { - FC_ASSERT( !_ldb_path.valid() ); - _ldb_path = path; - _ldb.open( *_ldb_path ); - _cache.reserve( _ldb.size() ); - for( auto iter = _ldb.begin(); iter.valid(); ++iter ) - _cache.emplace( iter.key(), iter.value() ); - } FC_CAPTURE_AND_RETHROW( (path) ) } - - void close() - { try { - if( _ldb_path.valid() ) - { - if( !_ldb_enabled ) toggle_leveldb( true ); - _ldb.close(); - _ldb_path = fc::optional(); - } - _cache.clear(); - } FC_CAPTURE_AND_RETHROW() } - - void toggle_leveldb( const bool enabled ) - { try { - FC_ASSERT( _ldb_path.valid() ); - if( enabled == _ldb_enabled ) - return; - - if( enabled ) - { - _ldb.open( *_ldb_path ); - auto batch = _ldb.create_batch(); - for( const auto& item : _cache ) - batch.store( item.first, item.second ); - batch.commit(); - } - else - { - _ldb.close(); - fc::remove_all( *_ldb_path ); - } - - _ldb_enabled = enabled; - } FC_CAPTURE_AND_RETHROW( (enabled) ) } - - void store( const K& key, const V& value ) - { try { - _cache[ key ] = value; - if( _ldb_enabled ) - _ldb.store( key, value ); - } FC_CAPTURE_AND_RETHROW( (key)(value) ) } - - void remove( const K& key ) - { try { - _cache.erase( key ); - if( _ldb_enabled ) - _ldb.remove( key ); - } FC_CAPTURE_AND_RETHROW( (key) ) } - - auto empty()const -> decltype( _cache.empty() ) - { - return _cache.empty(); - } - - auto size()const -> decltype( _cache.size() ) - { - return _cache.size(); - } - - auto count( const K& key )const -> decltype( _cache.count( key ) ) - { - return _cache.count( key ); - } - - auto unordered_begin()const -> decltype( _cache.cbegin() ) - { - return _cache.cbegin(); - } - - auto unordered_end()const -> decltype( _cache.cend() ) - { - return _cache.cend(); - } - - auto unordered_find( const K& key )const -> decltype( _cache.find( key ) ) - { - return _cache.find( key ); - } - - auto ordered_first()const -> decltype( _ldb.begin() ) - { try { - return _ldb.begin(); - } FC_CAPTURE_AND_RETHROW() } - - auto ordered_last()const -> decltype( _ldb.last() ) - { try { - return _ldb.last(); - } FC_CAPTURE_AND_RETHROW() } - - auto ordered_lower_bound( const K& key )const -> decltype( _ldb.lower_bound( key ) ) - { try { - return _ldb.lower_bound( key ); - } FC_CAPTURE_AND_RETHROW( (key) ) } -}; - -} } // graphene::db diff --git a/libraries/db/include/graphene/db/upgrade_leveldb.hpp b/libraries/db/include/graphene/db/upgrade_leveldb.hpp deleted file mode 100644 index 22b8013e..00000000 --- a/libraries/db/include/graphene/db/upgrade_leveldb.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace fc { class path; } - -/** - * This code has no graphene dependencies, and it - * could be moved to fc, if fc ever adds a leveldb dependency - * - * This code enables legacy databases files created by older programs to - * be upgraded to the current database formats. Whenever a database is first opened, - * this code checks if the database is stored in an old format and looks for an - * upgrade function to upgrade it to the current format. If found, the objects - * in the database will be immediately upgraded to the current format. - * - * Upgrades are performed by executing a series of chained copy constructors - * from the legacy object format to the current object format. This means - * that only one new copy constructor typically needs to be written to support - * upgrading any previous version of the object when an object type is modified. - * - * - Database versioning is only supported for changes to database value types - * (databases with modified key types cannot currently be upgraded). - * - The database versioning code requires that fc::get_typename is defined for - * all value types which are to be versioned. - */ - -/* - Below is a simple example of how client code needs to be written to support - database versioning. Originally, a database stored values of record0, and - record was typedef'd to be record0. A new type record1 was created to add - "new_field" to record type, and record was typedef'd to record1. The typedef - is used to minimize required changes to the client code that references - record objects. - - @code - - struct record0 - { - record0() : points(0) {} - double points; - }; - - FC_REFLECT( record0, (points) ) - REGISTER_DB_OBJECT(record,0) //This creates an upgrade function for record0 databases - - struct record1 - { - record1() : points(0), new_field("EMPTY") {} - - record1(const record0& r0) //convert from record0 to record1 for legacy files - { - key = r0.key; - new_field = "EMPTY"; - } - std::string new_field; - double points; - }; - FC_REFLECT( record1, (points)(new_field) ) - - typedef record1 record; //current databases store record1 objects - - @endcode -*/ - -namespace graphene { namespace db { - - typedef std::function upgrade_db_function; - - class upgrade_db_mapper - { - public: - static upgrade_db_mapper& instance(); - int32_t add_type( const std::string& type_name, const upgrade_db_function& function); - - std::map _upgrade_db_function_registry; - }; - - #define REGISTER_DB_OBJECT(TYPE,VERSIONNUM) \ - void UpgradeDb ## TYPE ## VERSIONNUM(leveldb::DB* dbase) \ - { \ - std::unique_ptr dbase_itr( dbase->NewIterator(leveldb::ReadOptions()) ); \ - dbase_itr->SeekToFirst(); \ - if( dbase_itr->status().IsNotFound() ) /*if empty database, do nothing*/ \ - return; \ - if( !dbase_itr->status().ok() ) \ - FC_THROW_EXCEPTION( exception, "database error: ${msg}", ("msg", dbase_itr->status().ToString() ) ); \ - while( dbase_itr->Valid() ) /* convert dbase objects from legacy TypeVersionNum to current Type */ \ - { \ - TYPE ## VERSIONNUM old_value; /*load old record type*/ \ - fc::datastream dstream( dbase_itr->value().data(), dbase_itr->value().size() ); \ - fc::raw::unpack( dstream, old_value ); \ - TYPE new_value(old_value); /*convert to new record type*/ \ - leveldb::Slice key_slice = dbase_itr->key(); \ - auto vec = fc::raw::pack(new_value); \ - leveldb::Slice value_slice( vec.data(), vec.size() ); \ - auto status = dbase->Put( leveldb::WriteOptions(), key_slice, value_slice ); \ - if( !status.ok() ) \ - { \ - FC_THROW_EXCEPTION( exception, "database error: ${msg}", ("msg", status.ToString() ) ); \ - } \ - dbase_itr->Next(); \ - } /*while*/ \ - } \ - static int dummyResult ## TYPE ## VERSIONNUM = \ - upgrade_db_mapper::instance()->add_type(fc::get_typename::name(), UpgradeDb ## TYPE ## VERSIONNUM); - - void try_upgrade_db( const fc::path& dir, leveldb::DB* dbase, const char* record_type, size_t record_type_size ); - -} } // namespace db diff --git a/libraries/leveldb-msvc/include/leveldb/db.h b/libraries/leveldb-msvc/include/leveldb/db.h deleted file mode 100644 index 983367f1..00000000 --- a/libraries/leveldb-msvc/include/leveldb/db.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -typedef SSIZE_T ssize_t; - -#include "../../../leveldb/include/leveldb/db.h" \ No newline at end of file diff --git a/libraries/leveldb-msvc/include/unistd.h b/libraries/leveldb-msvc/include/unistd.h deleted file mode 100644 index ea97c3b5..00000000 --- a/libraries/leveldb-msvc/include/unistd.h +++ /dev/null @@ -1 +0,0 @@ -/* empty unistd to allow leveldb to compile with msvc which is missing this file */ \ No newline at end of file diff --git a/libraries/leveldb-msvc/readme.txt b/libraries/leveldb-msvc/readme.txt deleted file mode 100644 index e46afc4a..00000000 --- a/libraries/leveldb-msvc/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -This directory contains the files needed to make the stock LevelDB distribution -from https://github.com/bitcoin/leveldb.git compile on Windows with Visual C++. -Add this 'include' directory to yur include path before the regular includes -only when building leveldb (it isn't needed when compiling code that uses -leveldb). \ No newline at end of file From 427d1a89774ed219639011bd2d494cfdb00135be Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 20:37:52 -0400 Subject: [PATCH 018/354] evaluator.cpp: Remove obsolete verify_signature function --- libraries/chain/evaluator.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index e390890e..b56b900e 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -95,13 +95,6 @@ namespace graphene { namespace chain { } } - /* - bool generic_evaluator::verify_signature( const key_object& k ) - { - return trx_state->_skip_signature_check || trx_state->signed_by( k.id ); - } - */ - object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const { if( rel_id.space() == relative_protocol_ids ) From a219bee55d1ff8685ecd51ca27160a767d44cca0 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 16:07:50 -0400 Subject: [PATCH 019/354] tests: implement new PUSH_TX / PUSH_BLOCK macros for tests --- tests/common/database_fixture.cpp | 19 ++++++++++++++----- tests/common/database_fixture.hpp | 15 +++++++++++---- tests/tests/authority_tests.cpp | 16 ++++++++-------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index fcecaa43..dd48a508 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -92,11 +92,6 @@ string database_fixture::generate_anon_acct_name() return "anon-acct-x" + std::to_string( anon_acct_count++ ); } -void database_fixture::_push_transaction( const signed_transaction& tx, uint32_t skip_flags, const char* file, int line ) -{ - db.push_transaction( tx, skip_flags ); -} - void database_fixture::verify_asset_supplies( )const { wlog("*** Begin asset supply verification ***"); @@ -882,4 +877,18 @@ int64_t database_fixture::get_balance( const account_object& account, const asse return db.get_balance(account.get_id(), a.get_id()).amount.value; } +namespace test { + +bool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = 0 */ ) +{ + return db.push_block( b, skip_flags); +} + +processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ ) +{ + return db.push_transaction( tx, skip_flags ); +} + +} // graphene::chain::test + } } // graphene::chain diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 649df558..8ca1e1b6 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -24,6 +24,12 @@ using namespace graphene::db; +#define PUSH_TX \ + graphene::chain::test::_push_transaction + +#define PUSH_BLOCK \ + graphene::chain::test::_push_block + // See below #define REQUIRE_OP_VALIDATION_SUCCESS( op, field, value ) \ { \ @@ -65,9 +71,6 @@ using namespace graphene::db; /// i.e. This allows a test on update_account to begin with the database at the end state of create_account. #define INVOKE(test) ((struct test*)this)->test_method(); trx.clear() -#define PUSH_TX( tx, skip_flags ) \ - _push_transaction( tx, skip_flags, __FILE__, __LINE__ ) - #define PREP_ACTOR(name) \ fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ key_id_type name ## _key_id = register_key(name ## _private_key.get_public_key()).get_id(); @@ -108,7 +111,6 @@ struct database_fixture { static fc::ecc::private_key generate_private_key(string seed); string generate_anon_acct_name(); - void _push_transaction( const signed_transaction& tx, uint32_t skip_flags, const char* file, int line ); void verify_asset_supplies( )const; void verify_account_history_plugin_index( )const; void open_database(); @@ -220,4 +222,9 @@ struct database_fixture { int64_t get_balance( const account_object& account, const asset_object& a )const; }; +namespace test { +bool _push_block( database& db, const signed_block& b, uint32_t skip_flags = 0 ); +processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags = 0 ); +} + } } diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 4ce9740d..f6bee06f 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -943,38 +943,38 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) wdump( (active_set)(owner_set)(alice_key_id) (alice_account_object) ); - PUSH_TX( trx, skip ); + db.push_transaction( trx, skip ); trx.operations.push_back( xfer_op ); // Alice's signature is now invalid edump((trx)); - BOOST_REQUIRE_THROW( PUSH_TX( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); // Re-sign, now OK (sig is replaced) trx.sign( alice_key_id, alice_key ); - PUSH_TX( trx, skip ); + db.push_transaction( trx, skip ); trx.signatures.clear(); trx.sign( charlie_key_id, alice_key ); // Sign with Alice's key (valid) claiming to be Charlie - BOOST_REQUIRE_THROW( PUSH_TX( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); // and with Charlie's key (invalid) claiming to be Alice trx.sign( charlie_key_id, alice_key ); - BOOST_REQUIRE_THROW( PUSH_TX( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); trx.signatures.clear(); // okay, now sign ONLY with Charlie's key claiming to be Alice trx.sign( charlie_key_id, alice_key ); - BOOST_REQUIRE_THROW( PUSH_TX( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); trx.signatures.clear(); trx.operations.pop_back(); trx.sign( alice_key_id, alice_key ); trx.sign( charlie_key_id, charlie_key ); // Signed by third-party Charlie (irrelevant key, not in authority) - PUSH_TX( trx, skip ); + db.push_transaction( trx, skip ); trx.operations.push_back( xfer_op ); trx.sign( alice_key_id, alice_key ); // Alice's sig is valid but Charlie's is invalid - BOOST_REQUIRE_THROW( PUSH_TX( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); } FC_LOG_AND_RETHROW() } From 616a7cf4d745e057ff4b0161c443874ee0d7797a Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 20:04:47 -0400 Subject: [PATCH 020/354] tests: Use new macros Generated by applying these commands to parent commit: sed -i -e 's/\([a-z0-9]\+\).push_transaction[(]\([^()]*\)[)]/PUSH_TX( \1, \2 )/' tests/tests/*.cpp sed -i -e 's/\([a-z0-9]\+\).push_block[(]\([^()]*\)[)]/PUSH_BLOCK( \1, \2 )/' tests/tests/*.cpp --- tests/tests/authority_tests.cpp | 140 +++++++++++++++---------------- tests/tests/block_tests.cpp | 66 +++++++-------- tests/tests/fee_tests.cpp | 2 +- tests/tests/operation_tests.cpp | 94 ++++++++++----------- tests/tests/operation_tests2.cpp | 66 +++++++-------- tests/tests/uia_tests.cpp | 34 ++++---- 6 files changed, 201 insertions(+), 201 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index f6bee06f..cad13bb7 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE( simple_single_signature ) transfer_operation op = {asset(),nathan.id, account_id_type(), core.amount(500)}; trx.operations.push_back(op); sign(trx, key.id, nathan_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500); } catch (fc::exception& e) { @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE( any_two_of_three ) op.owner = *op.active; trx.operations.push_back(op); sign(trx, key1.id,nathan_key1); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); trx.operations.clear(); trx.signatures.clear(); } FC_CAPTURE_AND_RETHROW ((nathan.active)(key1)) @@ -88,27 +88,27 @@ BOOST_AUTO_TEST_CASE( any_two_of_three ) transfer_operation op = {asset(), nathan.id, account_id_type(), core.amount(500)}; trx.operations.push_back(op); sign(trx, key1.id,nathan_key1); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, key2.id,nathan_key2); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500); trx.signatures.clear(); sign(trx, key2.id,nathan_key2); sign(trx, key3.id,nathan_key3); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1000); trx.signatures.clear(); sign(trx, key1.id,nathan_key1); sign(trx, key3.id,nathan_key3); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500); trx.signatures.clear(); //sign(trx, fc::ecc::private_key::generate()); sign(trx, key3.id,nathan_key3); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) make_child_op.owner = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1); make_child_op.active = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1); trx.operations.push_back(make_child_op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } @@ -142,17 +142,17 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) transfer_operation op = {asset(), child.id, account_id_type(), core.amount(500)}; trx.operations.push_back(op); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, key1.id,parent1_key); sign(trx, key1.id,parent1_key); sign(trx, key1.id,parent1_key); sign(trx, key1.id,parent1_key); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); trx.signatures.clear(); sign(trx, key2.id,parent2_key); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, key1.id,parent1_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 500); trx.operations.clear(); trx.signatures.clear(); @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) trx.operations.push_back(op); sign(trx, key1.id,parent1_key); sign(trx, key2.id,parent2_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_REQUIRE_EQUAL(child.active.auths.size(), 3); trx.operations.clear(); trx.signatures.clear(); @@ -175,18 +175,18 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) op = {asset(),child.id, account_id_type(), core.amount(500)}; trx.operations.push_back(op); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, key1.id,parent1_key); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); trx.signatures.clear(); sign(trx, key2.id,parent2_key); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, key1.id, parent1_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1000); trx.signatures.clear(); sign(trx, child_key_obj.id, child_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1500); trx.operations.clear(); trx.signatures.clear(); @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) op.active = authority(1, grandparent_key_obj.get_id(), 1); op.owner = *op.active; trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); trx.signatures.clear(); } @@ -212,9 +212,9 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) trx.operations.push_back(op); sign(trx, key1.id, parent1_key); sign(trx, key2.id, parent2_key); - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, grandparent_key_obj.id, grandparent_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2000); trx.operations.clear(); trx.signatures.clear(); @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) op.active = authority(1, account_id_type(), 1); op.owner = *op.active; trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); trx.signatures.clear(); } @@ -235,9 +235,9 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) sign(trx, grandparent_key_obj.id,grandparent_key); sign(trx, key_id_type(), generate_private_key("genesis")); //Fails due to recursion depth. - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); sign(trx, child_key_obj.id, child_key); - db.push_transaction(trx, database::skip_transaction_dupe_check); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2500); trx.operations.clear(); trx.signatures.clear(); @@ -248,7 +248,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) op.active = authority(1, account_id_type(child.id), 1); op.owner = *op.active; trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); trx.signatures.clear(); } @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) trx.operations.push_back(op); sign(trx, key2.id, parent2_key); //Fails due to recursion depth. - BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; @@ -301,7 +301,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) trx.operations.push_back(op); trx.set_expiration(db.head_block_id()); sign(trx, this->genesis_key, genesis_key); - const proposal_object& proposal = db.get(db.push_transaction(trx).operation_results.front().get()); + const proposal_object& proposal = db.get(PUSH_TX( db, trx ).operation_results.front().get()); BOOST_CHECK_EQUAL(proposal.required_active_approvals.size(), 1); BOOST_CHECK_EQUAL(proposal.available_active_approvals.size(), 0); @@ -312,11 +312,11 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) trx.operations = {proposal_update_operation{account_id_type(), asset(), proposal.id,{nathan.id},flat_set{},flat_set{},flat_set{},flat_set{},flat_set{}}}; trx.sign( this->genesis_key, genesis_key ); //Genesis may not add nathan's approval. - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); trx.operations = {proposal_update_operation{account_id_type(), asset(), proposal.id,{account_id_type()},flat_set{},flat_set{},flat_set{},flat_set{},flat_set{}}}; trx.sign( key_id_type(), genesis_key ); //Genesis has no stake in the transaction. - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); trx.signatures.clear(); trx.operations = {proposal_update_operation{nathan.id, asset(), proposal.id,{nathan.id},flat_set{},flat_set{},flat_set{},flat_set{},flat_set{}}}; @@ -326,7 +326,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) //trx.signatures = {nathan_key3.sign_compact(trx.digest()), nathan_key2.sign_compact(trx.digest())}; BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value - 100); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan.id, asset(100000)})); sign(trx, key_id_type(), genesis_key); - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); auto sign = [&] { trx.signatures.clear(); trx.sign(nathan_key_id,nathan_key); }; @@ -366,16 +366,16 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) sign(); // The review period isn't set yet. Make sure it throws. - BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); pop.review_period_seconds = global_params.genesis_proposal_review_period / 2; trx.operations.back() = pop; sign(); // The review period is too short. Make sure it throws. - BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); pop.review_period_seconds = global_params.genesis_proposal_review_period; trx.operations.back() = pop; sign(); - proposal_object prop = db.get(db.push_transaction(trx).operation_results.front().get()); + proposal_object prop = db.get(PUSH_TX( db, trx ).operation_results.front().get()); BOOST_REQUIRE(db.find_object(prop.id)); BOOST_CHECK(prop.expiration_time == pop.expiration_time); @@ -396,7 +396,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) uop.key_approvals_to_add.emplace(); trx.operations.push_back(uop); trx.sign(key_id_type(), genesis_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0); BOOST_CHECK(db.get(prop.id).is_authorized_to_execute(&db)); @@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) trx.operations.back() = uop; trx.sign(key_id_type(), genesis_key); // Should throw because the transaction is now in review. - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); generate_blocks(prop.expiration_time); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000); @@ -445,7 +445,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) pop.proposed_ops.emplace_back(transfer_operation({asset(),account_id_type(), nathan->id, asset(100000)})); trx.operations.push_back(pop); sign(trx, key_id_type(), genesis_key); - const proposal_object& prop = db.get(db.push_transaction(trx).operation_results.front().get()); + const proposal_object& prop = db.get(PUSH_TX( db, trx ).operation_results.front().get()); proposal_id_type pid = prop.id; BOOST_CHECK(!prop.is_authorized_to_execute(&db)); @@ -456,7 +456,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) uop.key_approvals_to_add.emplace(); trx.operations.back() = uop; trx.sign(key_id_type(), genesis_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(prop.is_authorized_to_execute(&db)); //Time passes... the proposal is now in its review period. @@ -478,7 +478,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) op.new_options->votes = delegates; trx.operations.push_back(op); trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } @@ -524,7 +524,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture ) pop.expiration_time = db.head_block_time() + fc::days(1); trx.operations.push_back(pop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -541,7 +541,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture ) uop.fee_paying_account = nathan.get_id(); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(db.find_object(pid) != nullptr); @@ -550,9 +550,9 @@ BOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture ) uop.active_approvals_to_add = {dan.get_id()}; trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); trx.sign(dan_key_obj.id,dan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(db.find_object(pid) == nullptr); } @@ -588,7 +588,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture ) pop.expiration_time = db.head_block_time() + fc::days(1); trx.operations.push_back(pop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -604,7 +604,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture ) uop.active_approvals_to_add.insert(nathan.get_id()); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 1); @@ -612,7 +612,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture ) std::swap(uop.active_approvals_to_add, uop.active_approvals_to_remove); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 0); @@ -625,7 +625,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture ) dop.proposal = pid; trx.operations.push_back(dop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(db.find_object(pid) == nullptr); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000); } @@ -666,7 +666,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture ) pop.expiration_time = db.head_block_time() + fc::days(1); trx.operations.push_back(pop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -682,7 +682,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture ) uop.owner_approvals_to_add.insert(nathan.get_id()); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 1); @@ -690,7 +690,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture ) std::swap(uop.owner_approvals_to_add, uop.owner_approvals_to_remove); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 0); @@ -704,7 +704,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture ) dop.using_owner_authority = true; trx.operations.push_back(dop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(db.find_object(pid) == nullptr); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000); } @@ -745,7 +745,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture ) pop.expiration_time = db.head_block_time() + fc::days(1); trx.operations.push_back(pop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -763,7 +763,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture ) trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); trx.sign(dan_key_obj.id,dan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1); @@ -772,7 +772,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture ) trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); trx.sign(dan_key_obj.id,dan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 0); @@ -783,7 +783,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture ) trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); trx.sign(dan_key_obj.id,dan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(!prop.is_authorized_to_execute(&db)); BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1); @@ -792,7 +792,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture ) uop.owner_approvals_to_add.insert(nathan.get_id()); trx.operations.push_back(uop); trx.sign(nathan_key_obj.id,nathan_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK(db.find_object(pid) == nullptr); } @@ -842,7 +842,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture ) kc_op.key_data = public_key_type( privkey.get_public_key() ); tx.operations.push_back( kc_op ); } - ptx = db.push_transaction(tx, ~0); + ptx = PUSH_TX( db, tx, ~0 ); vector key_ids; @@ -882,11 +882,11 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture ) if( num_keys > max_authority_membership ) { - BOOST_REQUIRE_THROW(db.push_transaction(tx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, tx, ~0 ), fc::exception); } else { - db.push_transaction(tx, ~0); + PUSH_TX( db, tx, ~0 ); } return; }; @@ -943,38 +943,38 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) wdump( (active_set)(owner_set)(alice_key_id) (alice_account_object) ); - db.push_transaction( trx, skip ); + PUSH_TX( db, trx, skip ); trx.operations.push_back( xfer_op ); // Alice's signature is now invalid edump((trx)); - BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); // Re-sign, now OK (sig is replaced) trx.sign( alice_key_id, alice_key ); - db.push_transaction( trx, skip ); + PUSH_TX( db, trx, skip ); trx.signatures.clear(); trx.sign( charlie_key_id, alice_key ); // Sign with Alice's key (valid) claiming to be Charlie - BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); // and with Charlie's key (invalid) claiming to be Alice trx.sign( charlie_key_id, alice_key ); - BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); trx.signatures.clear(); // okay, now sign ONLY with Charlie's key claiming to be Alice trx.sign( charlie_key_id, alice_key ); - BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); trx.signatures.clear(); trx.operations.pop_back(); trx.sign( alice_key_id, alice_key ); trx.sign( charlie_key_id, charlie_key ); // Signed by third-party Charlie (irrelevant key, not in authority) - db.push_transaction( trx, skip ); + PUSH_TX( db, trx, skip ); trx.operations.push_back( xfer_op ); trx.sign( alice_key_id, alice_key ); // Alice's sig is valid but Charlie's is invalid - BOOST_REQUIRE_THROW( db.push_transaction( trx, skip ), fc::exception ); + BOOST_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); } FC_LOG_AND_RETHROW() } @@ -1001,7 +1001,7 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) op.new_options->num_committee = 1; trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } { @@ -1013,11 +1013,11 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) trx.operations.push_back(op); trx.sign(vikram_key_id, vikram_private_key); // Fails because num_committee is larger than the cardinality of committee members being voted for - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); op.new_options->num_committee = 3; trx.operations = {op}; trx.sign(vikram_key_id, vikram_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 0fed7963..f0d5afdb 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) now += db1.block_interval(); auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); try { - db2.push_block(b); + PUSH_BLOCK( db2, b ); } FC_CAPTURE_AND_RETHROW( ("db2") ); } for( uint32_t i = 10; i < 13; ++i ) @@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) auto b = db2.generate_block(now, db2.get_scheduled_witness(db2.get_slot_at_time(now)).first, delegate_priv_key); // notify both databases of the new block. // only db2 should switch to the new fork, db1 should not - db1.push_block(b); + PUSH_BLOCK( db1, b ); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); BOOST_CHECK_EQUAL(db2.head_block_id().str(), b.id().str()); } @@ -247,14 +247,14 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) b.transactions.back().operations.emplace_back(transfer_operation()); b.sign(delegate_priv_key); BOOST_CHECK_EQUAL(b.block_num(), 14); - BOOST_CHECK_THROW(db1.push_block(b), fc::exception); + BOOST_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception); } BOOST_CHECK_EQUAL(db1.head_block_num(), 13); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); // assert that db1 switches to new fork with good block BOOST_CHECK_EQUAL(db2.head_block_num(), 14); - db1.push_block(good_block); + PUSH_BLOCK( db1, good_block ); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str()); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE( undo_pending ) signed_transaction trx; trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.push_back(transfer_operation({asset(), account_id_type(), account_id_type(1), asset(10000000)})); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); now += db.block_interval(); auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, ~0 ); @@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE( undo_pending ) cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); trx.sign( key_id_type(), delegate_priv_key ); - db.push_transaction(trx); + PUSH_TX( db, trx ); now += db.block_interval(); auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -304,12 +304,12 @@ BOOST_AUTO_TEST_CASE( undo_pending ) trx.set_expiration(db.head_block_time() + db.get_global_properties().parameters.maximum_time_until_expiration-1); trx.operations.push_back(transfer_operation({asset(1),account_id_type(1), nathan_id, asset(5000)})); trx.sign( key_id_type(), delegate_priv_key ); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); trx.set_expiration(db.head_block_time() + db.get_global_properties().parameters.maximum_time_until_expiration-2); trx.operations.push_back(transfer_operation({asset(1),account_id_type(1), nathan_id, asset(5000)})); trx.sign( key_id_type(), delegate_priv_key ); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000); db.clear_pending(); @@ -344,7 +344,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); trx.sign( key_id_type(), delegate_priv_key ); - db1.push_transaction(trx); + PUSH_TX( db1, trx ); auto aw = db1.get_global_properties().active_witnesses; now += db1.block_interval(); @@ -355,20 +355,20 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); now += db2.block_interval(); b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); - db1.push_block(b); + PUSH_BLOCK( db1, b ); aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); - db1.push_block(b); + PUSH_BLOCK( db1, b ); BOOST_CHECK_THROW(nathan_id(db1), fc::exception); - db2.push_transaction(trx); + PUSH_TX( db2, trx ); aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); - db1.push_block(b); + PUSH_BLOCK( db1, b ); BOOST_CHECK(nathan_id(db1).name == "nathan"); BOOST_CHECK(nathan_id(db2).name == "nathan"); @@ -402,22 +402,22 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions ) cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); trx.sign( key_id_type(), delegate_priv_key ); - db1.push_transaction(trx, skip_sigs); + PUSH_TX( db1, trx, skip_sigs ); trx = decltype(trx)(); trx.set_expiration(db1.head_block_time() + fc::minutes(1)); trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(500)})); trx.sign( key_id_type(), delegate_priv_key ); - db1.push_transaction(trx, skip_sigs); + PUSH_TX( db1, trx, skip_sigs ); - BOOST_CHECK_THROW(db1.push_transaction(trx, skip_sigs), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception); now += db1.block_interval(); auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, skip_sigs ); - db2.push_block(b, skip_sigs); + PUSH_BLOCK( db2, b, skip_sigs ); - BOOST_CHECK_THROW(db1.push_transaction(trx, skip_sigs), fc::exception); - BOOST_CHECK_THROW(db2.push_transaction(trx, skip_sigs), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db1, trx, skip_sigs ), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db2, trx, skip_sigs ), fc::exception); BOOST_CHECK_EQUAL(db1.get_balance(nathan_id, asset_id_type()).amount.value, 500); BOOST_CHECK_EQUAL(db2.get_balance(nathan_id, asset_id_type()).amount.value, 500); } catch (fc::exception& e) { @@ -459,7 +459,7 @@ BOOST_AUTO_TEST_CASE( tapos ) trx.signatures.clear(); trx.sign( key_id_type(), delegate_priv_key ); - db1.push_transaction(trx); + PUSH_TX( db1, trx ); now += db1.block_interval(); b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); @@ -469,11 +469,11 @@ BOOST_AUTO_TEST_CASE( tapos ) trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)})); trx.sign( key_id_type(), delegate_priv_key ); //relative_expiration is 1, but ref block is 2 blocks old, so this should fail. - BOOST_REQUIRE_THROW(db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception); trx.set_expiration(db1.head_block_id(), 2); trx.signatures.clear(); trx.sign( key_id_type(), delegate_priv_key ); - db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check); + PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; @@ -498,7 +498,7 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) op.new_options = nathan.options; op.new_options->votes.insert(nathans_delegate.vote_id); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } transfer(account_id_type()(db), nathan, asset(5000)); @@ -555,7 +555,7 @@ BOOST_FIXTURE_TEST_CASE( short_order_expiration, database_fixture ) op.collateral = core->amount(500); op.expiration = db.head_block_time() + fc::seconds(10); trx.operations.push_back(op); - auto ptrx = db.push_transaction(trx, ~0); + auto ptrx = PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); @@ -598,7 +598,7 @@ BOOST_FIXTURE_TEST_CASE( limit_order_expiration, database_fixture ) op.min_to_receive = test->amount(500); op.expiration = db.head_block_time() + fc::seconds(10); trx.operations.push_back(op); - auto ptrx = db.push_transaction(trx, ~0); + auto ptrx = PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL( get_balance(*nathan, *core), 49500 ); @@ -638,7 +638,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); trx.sign(key_id_type(),generate_private_key("genesis")); - db.push_transaction(trx); + PUSH_TX( db, trx ); } { proposal_update_operation uop; @@ -647,7 +647,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) account_id_type(5), account_id_type(6), account_id_type(7), account_id_type(8)}; trx.operations.push_back(uop); trx.sign(key_id_type(),generate_private_key("genesis")); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(&db)); } @@ -690,7 +690,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) op.new_options.maximum_force_settlement_volume = 9000; trx.clear(); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } generate_block(); @@ -731,7 +731,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) trx.operations.push_back(pop); } trx.sign(key_id_type(),private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); asset_settle_operation sop; @@ -743,7 +743,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) trx.sign(key_id_type(),private_key); //Partially settle a call - force_settlement_id_type settle_id = db.push_transaction(trx).operation_results.front().get(); + force_settlement_id_type settle_id = PUSH_TX( db, trx ).operation_results.front().get(); trx.clear(); call_order_id_type call_id = db.get_index_type().indices().get().begin()->id; BOOST_CHECK_EQUAL(settle_id(db).balance.amount.value, 50); @@ -761,10 +761,10 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) trx.operations.push_back(sop); trx.sign(key_id_type(),private_key); //Trx has expired by now. Make sure it throws. - BOOST_CHECK_THROW(settle_id = db.push_transaction(trx).operation_results.front().get(), fc::exception); + BOOST_CHECK_THROW(settle_id = PUSH_TX( db, trx ).operation_results.front().get(), fc::exception); trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.sign(key_id_type(),private_key); - settle_id = db.push_transaction(trx).operation_results.front().get(); + settle_id = PUSH_TX( db, trx ).operation_results.front().get(); trx.clear(); generate_blocks(settle_id(db).settlement_date); @@ -777,7 +777,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) sop.amount = db.get_balance(nathan_id, bit_usd); trx.operations.push_back(sop); trx.sign(key_id_type(),private_key); - settle_id = db.push_transaction(trx).operation_results.front().get(); + settle_id = PUSH_TX( db, trx ).operation_results.front().get(); trx.clear(); generate_blocks(settle_id(db).settlement_date); diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index f5b429d7..b98c9930 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE( cashback_test ) op.fee = op.calculate_fee(fees); \ trx.operations = {op}; \ trx.sign(registrar_name ## _key_id, registrar_name ## _private_key); \ - actor_name ## _id = db.push_transaction(trx).operation_results.front().get(); \ + actor_name ## _id = PUSH_TX( db, trx ).operation_results.front().get(); \ trx.clear(); \ } diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index f2c503be..71f34b2b 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -64,17 +64,17 @@ BOOST_AUTO_TEST_CASE( create_account_test ) op.owner.add_authority(account_id_type(9999999999), 10); trx.operations.back() = op; op.owner = auth_bak; - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); op.owner = auth_bak; op.owner.add_authority(key_id_type(9999999999), 10); trx.operations.back() = op; - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); op.owner = auth_bak; trx.operations.back() = op; trx.sign( key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))) ); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const account_object& nathan_account = *db.get_index_type().indices().get().find("nathan"); BOOST_CHECK(nathan_account.id.space() == protocol_ids); @@ -124,15 +124,15 @@ BOOST_AUTO_TEST_CASE( child_account ) trx.operations.emplace_back(op); sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")))); - BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); sign(trx, nathan_key.id,nathan_private_key); - BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); trx.signatures.clear(); op.owner = authority(1, account_id_type(nathan.id), 1); trx.operations = {op}; sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")))); sign(trx, nathan_key.id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); BOOST_CHECK( get_account("nathan/child").active.auths == op.active.auths ); } catch (fc::exception& e) { @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE( update_account ) transfer(account_id_type()(db), nathan, asset(30000)); trx.operations.emplace_back(key_create_operation({asset(),nathan.id,address(nathan_new_key.get_public_key())})); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); account_update_operation op; op.account = nathan.id; @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE( update_account ) op.new_options->votes = flat_set({active_delegates[0](db).vote_id, active_delegates[5](db).vote_id}); op.new_options->num_committee = 2; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(nathan.options.memo_key == key_id_type()); BOOST_CHECK(nathan.active.weight_threshold == 2); @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE( update_account ) op.upgrade_to_lifetime_member = true; op.fee = op.calculate_fee(db.get_global_properties().parameters.current_fees); trx.operations = {op}; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); } BOOST_CHECK( nathan.is_lifetime_member() ); @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE( transfer_core_asset ) asset fee = trx.operations.front().get().fee; trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(account_id_type()(db), asset_id_type()(db)), (genesis_balance.amount - 10000 - fee.amount).value); @@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE( transfer_core_asset ) fee = trx.operations.front().get().fee; trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 8000 - fee.amount.value); BOOST_CHECK_EQUAL(get_balance(account_id_type()(db), asset_id_type()(db)), genesis_balance.amount.value + 2000); @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE( create_delegate ) trx.operations.back() = op; delegate_id_type delegate_id = db.get_index_type>>().get_next_id(); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const delegate_object& d = delegate_id(db); BOOST_CHECK(d.delegate_account == account_id_type()); @@ -302,11 +302,11 @@ BOOST_AUTO_TEST_CASE( update_mia ) trx.operations.emplace_back(op); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); std::swap(op.new_options.flags, op.new_options.issuer_permissions); op.new_issuer = account_id_type(); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); { asset_publish_feed_operation pop; @@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE( update_mia ) REQUIRE_THROW_WITH_VALUE(pop, feed.required_maintenance_collateral, 0); REQUIRE_THROW_WITH_VALUE(pop, feed.required_initial_collateral, 500); trx.operations.back() = pop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); } trx.operations.clear(); @@ -335,13 +335,13 @@ BOOST_AUTO_TEST_CASE( update_mia ) op.issuer = account_id_type(); op.new_issuer = nathan.id; trx.operations.emplace_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(bit_usd.issuer == nathan.id); op.issuer = nathan.id; op.new_issuer = account_id_type(); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(bit_usd.issuer == account_id_type()); } catch ( const fc::exception& e ) { elog( "${e}", ("e", e.to_detail_string() ) ); @@ -488,7 +488,7 @@ BOOST_AUTO_TEST_CASE( create_uia ) creator.common_options.flags = charge_market_fee; creator.common_options.core_exchange_rate = price({asset(2),asset(1,1)}); trx.operations.push_back(std::move(creator)); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const asset_object& test_asset = test_asset_id(db); BOOST_CHECK(test_asset.symbol == "TEST"); @@ -497,7 +497,7 @@ BOOST_AUTO_TEST_CASE( create_uia ) BOOST_CHECK(test_asset.options.max_supply == 100000000); BOOST_CHECK(!test_asset.bitasset_data_id.valid()); BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100); - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); const asset_dynamic_data_object& test_asset_dynamic_data = test_asset.dynamic_asset_data_id(db); BOOST_CHECK(test_asset_dynamic_data.current_supply == 0); @@ -547,22 +547,22 @@ BOOST_AUTO_TEST_CASE( update_uia ) op.new_options.core_exchange_rate = price(asset(3), test.amount(5)); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); REQUIRE_THROW_WITH_VALUE(op, new_options.core_exchange_rate, price()); op.new_options.core_exchange_rate = test.options.core_exchange_rate; op.new_issuer = nathan.id; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); op.issuer = nathan.id; op.new_issuer.reset(); op.new_options.flags = transfer_restricted | white_list; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); REQUIRE_THROW_WITH_VALUE(op, new_options.issuer_permissions, test.options.issuer_permissions & ~white_list); op.new_options.issuer_permissions = test.options.issuer_permissions & ~white_list; op.new_options.flags = 0; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); op.new_options.issuer_permissions = test.options.issuer_permissions; op.new_options.flags = test.options.flags; BOOST_CHECK(!(test.options.issuer_permissions & white_list)); @@ -570,9 +570,9 @@ BOOST_AUTO_TEST_CASE( update_uia ) REQUIRE_THROW_WITH_VALUE(op, new_options.flags, white_list); op.new_issuer = account_id_type(); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); op.issuer = account_id_type(); - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); op.new_issuer.reset(); } catch(fc::exception& e) { edump((e.to_detail_string())); @@ -597,7 +597,7 @@ BOOST_AUTO_TEST_CASE( issue_uia ) REQUIRE_THROW_WITH_VALUE(op, issue_to_account, account_id_type(999999999)); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const asset_dynamic_data_object& test_dynamic_data = test_asset.dynamic_asset_data_id(db); BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), 5000000); @@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE( issue_uia ) BOOST_CHECK(test_dynamic_data.accumulated_fees == 0); BOOST_CHECK(test_dynamic_data.fee_pool == 0); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), 10000000); BOOST_CHECK(test_dynamic_data.current_supply == 10000000); @@ -628,11 +628,11 @@ BOOST_AUTO_TEST_CASE( transfer_uia ) BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000); trx.operations.push_back(transfer_operation({asset(),nathan.id, genesis.id, uia.amount(5000)})); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000 - 5000); BOOST_CHECK_EQUAL(get_balance(genesis, uia), 5000); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan, uia), 10000000 - 10000); BOOST_CHECK_EQUAL(get_balance(genesis, uia), 10000); } catch(fc::exception& e) { @@ -830,7 +830,7 @@ BOOST_AUTO_TEST_CASE( uia_fees ) BOOST_CHECK(fee.amount > 0); asset core_fee = fee*test_asset.options.core_exchange_rate; trx.operations.push_back(std::move(op)); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), (old_balance - fee - test_asset.amount(100)).amount.value); @@ -839,7 +839,7 @@ BOOST_AUTO_TEST_CASE( uia_fees ) BOOST_CHECK(asset_dynamic.fee_pool == 1000000 - core_fee.amount); //Do it again, for good measure. - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), (old_balance - fee - fee - test_asset.amount(200)).amount.value); BOOST_CHECK_EQUAL(get_balance(genesis_account, test_asset), 200); @@ -856,7 +856,7 @@ BOOST_AUTO_TEST_CASE( uia_fees ) BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 20); trx.operations.emplace_back(std::move(op)); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan_account, asset_id_type()(db)), 0); BOOST_CHECK_EQUAL(get_balance(nathan_account, test_asset), @@ -903,7 +903,7 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) asset_update_operation uop(get_asset("BITUSD")); uop.new_issuer = account_id_type(); trx.operations.push_back(uop); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } generate_block(); @@ -921,7 +921,7 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) op.feed.max_margin_period_sec = fc::days(30).to_seconds(); // Accept defaults for required collateral trx.operations.emplace_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); { //Dumb sanity check of some operators. Only here to improve code coverage. :D @@ -948,7 +948,7 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(20)); op.feed.max_margin_period_sec = fc::days(10).to_seconds(); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 20.0 / GRAPHENE_BLOCKCHAIN_PRECISION); @@ -964,7 +964,7 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) op.feed.required_initial_collateral = 1001; op.feed.required_maintenance_collateral = 1000; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); @@ -1235,7 +1235,7 @@ BOOST_AUTO_TEST_CASE( full_cover_test ) REQUIRE_THROW_WITH_VALUE(op, collateral_to_add, bit_usd.amount(20)); REQUIRE_THROW_WITH_VALUE(op, maintenance_collateral_ratio, 2); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(debt_holder, bit_usd), 0); BOOST_CHECK(index.find(boost::make_tuple(debt_holder.id, bit_usd.id)) == index.end()); @@ -1269,7 +1269,7 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) op.collateral_to_add = core.amount(0); op.amount_to_cover = bit_usd.amount(50); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(debt_holder, bit_usd), 0); BOOST_CHECK(index.find(boost::make_tuple(debt_holder.id, bit_usd.id)) != index.end()); @@ -1280,7 +1280,7 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) op.collateral_to_add = core.amount(52); op.amount_to_cover = bit_usd.amount(0); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); ilog("..." ); BOOST_CHECK(debt.call_price == price(core.amount(339), bit_usd.amount(50))); @@ -1292,7 +1292,7 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) REQUIRE_THROW_WITH_VALUE(op, maintenance_collateral_ratio, 2500); op.collateral_to_add = core.amount(8); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(debt.call_price == price(core.amount(368), bit_usd.amount(50))); @@ -1300,7 +1300,7 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) op.collateral_to_add.amount = 0; trx.operations.back() = op; BOOST_CHECK_EQUAL(get_balance(debt_holder, bit_usd), 0); - BOOST_CHECK_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); trx.operations.clear(); ilog("..." ); @@ -1310,7 +1310,7 @@ BOOST_AUTO_TEST_CASE( partial_cover_test ) op.validate(); ilog("..." ); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(index.find(boost::make_tuple(debt_holder.id, bit_usd.id)) == index.end()); } catch( fc::exception& e) { @@ -1831,10 +1831,10 @@ BOOST_AUTO_TEST_CASE( limit_order_fill_or_kill ) trx.operations.clear(); trx.operations.push_back(op); - BOOST_CHECK_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); op.fill_or_kill = false; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); } FC_LOG_AND_RETHROW() } /// Shameless code coverage plugging. Otherwise, these calls never happen. @@ -1882,7 +1882,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); trx.sign(key_id_type(),generate_private_key("genesis")); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 8950000000); @@ -1961,7 +1961,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); trx.sign(key_id_type(),generate_private_key("genesis")); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); BOOST_CHECK_EQUAL(get_balance(witness->witness_account(db), *core), witness_ppb - 1/*fee*/); @@ -2245,7 +2245,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) create_op.vesting_seconds = vesting_seconds; tx.operations.push_back( create_op ); - processed_transaction ptx = db.push_transaction( tx, ~0 ); + processed_transaction ptx = PUSH_TX( db, tx, ~0 ); const vesting_balance_object& vbo = vesting_balance_id_type( ptx.operation_results[0].get())(db); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index aaebbdc1..1dd13d6c 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create ) } trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) op.amount_to_withdraw = asset(1); trx.operations.push_back(op); //Throws because we haven't entered the first withdrawal period yet. - BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); //Get to the actual withdrawal period generate_blocks(permit(db).period_start_time); @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) trx.clear(); trx.operations.push_back(op); trx.sign(dan_key_id, dan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); // would be legal on its own, but doesn't work because trx already withdrew REQUIRE_THROW_WITH_VALUE(op, amount_to_withdraw, asset(5)); @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) trx.operations.back() = op; // withdraw 1 trx.ref_block_prefix++; // make it different from previous trx so it's non-duplicate trx.sign(dan_key_id, dan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -162,11 +162,11 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) trx.operations.push_back(op); trx.sign(dan_key_id, dan_private_key); //Throws because nathan doesn't have the money - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); op.amount_to_withdraw = asset(1); trx.operations.back() = op; trx.sign(dan_key_id, dan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); } BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 0); @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) trx.operations.push_back(op); trx.sign(dan_key_id, dan_private_key); //Throws because the permission has expired - BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx ), fc::exception); } } FC_LOG_AND_RETHROW() } @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_nominal_case ) // so tx's have different txid's trx.ref_block_prefix++; trx.sign(dan_key_id, dan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); // tx's involving withdraw_permissions can't delete it even // if no further withdrawals are possible BOOST_CHECK(db.find_object(permit) != nullptr); @@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_update ) REQUIRE_THROW_WITH_VALUE(op, period_start_time, db.head_block_time() - 50); trx.operations.back() = op; trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); } { @@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_delete ) trx.set_expiration(db.head_block_id()); trx.operations.push_back(op); trx.sign(get_account("nathan").active.auths.begin()->first, generate_private_key("nathan")); - db.push_transaction(trx); + PUSH_TX( db, trx ); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( mia_feeds ) @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.new_issuer = nathan_id; op.new_options = obj.options; trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); generate_block(); trx.clear(); } @@ -324,7 +324,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.new_feed_producers = {dan_id, ben_id, vikram_id}; trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); generate_block(database::skip_nothing); } { @@ -342,7 +342,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.feed.max_margin_period_sec = fc::days(30).to_seconds(); // Accept defaults for required collateral trx.operations.emplace_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db); BOOST_CHECK(bitasset.current_feed.call_limit.to_real() == GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); @@ -356,7 +356,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.feed.short_limit = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(20)); op.feed.max_margin_period_sec = fc::days(10).to_seconds(); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 20.0 / GRAPHENE_BLOCKCHAIN_PRECISION); @@ -371,7 +371,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.feed.required_initial_collateral = 1001; op.feed.required_maintenance_collateral = 1000; trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(bitasset.current_feed.call_limit.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); BOOST_CHECK_EQUAL(bitasset.current_feed.short_limit.to_real(), 10.0 / GRAPHENE_BLOCKCHAIN_PRECISION); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.publisher = nathan_id; trx.operations.back() = op; - BOOST_CHECK_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); } } FC_LOG_AND_RETHROW() } @@ -407,7 +407,7 @@ BOOST_AUTO_TEST_CASE( witness_create ) [](vote_id_type id) { return id.type() == vote_id_type::committee; }); trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -482,7 +482,7 @@ BOOST_AUTO_TEST_CASE( global_settle_test ) REQUIRE_THROW_WITH_VALUE(op, issuer, account_id_type(2)); trx.operations.back() = op; trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); } BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 0); @@ -515,7 +515,7 @@ BOOST_AUTO_TEST_CASE( worker_create_test ) REQUIRE_THROW_WITH_VALUE(op, work_end_date, op.work_begin_date); trx.operations.back() = op; trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); } const worker_object& worker = worker_id_type()(db); @@ -545,7 +545,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) op.new_options = nathan_id(db).options; op.new_options->votes.insert(worker_id_type()(db).vote_for); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } { @@ -553,7 +553,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) op.payer = account_id_type(); op.amount_to_burn = asset(GRAPHENE_INITIAL_SUPPLY/2); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } @@ -570,7 +570,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) trx.set_expiration(db.head_block_id()); trx.operations.push_back(op); trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.signatures.clear(); REQUIRE_THROW_WITH_VALUE(op, amount, asset(1)); trx.clear(); @@ -585,7 +585,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) op.new_options = nathan_id(db).options; op.new_options->votes.erase(worker_id_type()(db).vote_for); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } @@ -605,7 +605,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test ) REQUIRE_THROW_WITH_VALUE(op, amount, asset(501)); trx.operations.back() = op; trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.signatures.clear(); trx.clear(); } @@ -638,7 +638,7 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) REQUIRE_THROW_WITH_VALUE(op, work_end_date, op.work_begin_date); trx.operations.back() = op; trx.sign(nathan_key_id, nathan_private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); } @@ -658,7 +658,7 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) op.new_options = nathan_id(db).options; op.new_options->votes.insert(worker_id_type()(db).vote_for); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } { @@ -666,7 +666,7 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) op.payer = account_id_type(); op.amount_to_burn = asset(GRAPHENE_INITIAL_SUPPLY/2); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } @@ -704,7 +704,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) op.new_options.maximum_force_settlement_volume = 9000; trx.clear(); trx.operations.push_back(op); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.clear(); } generate_block(); @@ -745,7 +745,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) trx.operations.push_back(pop); } trx.sign(key_id_type(),private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.clear(); asset_settle_operation sop; @@ -753,7 +753,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) sop.amount = asset(50, bit_usd); trx.operations = {sop}; //Force settlement is disabled; check that it fails - BOOST_CHECK_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); { //Enable force settlement asset_update_operation op; @@ -763,7 +763,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) op.new_options.flags &= ~disable_force_settle; trx.operations = {op}; trx.sign(key_id_type(), private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); trx.operations = {sop}; } REQUIRE_THROW_WITH_VALUE(sop, amount, asset(999999, bit_usd)); @@ -771,7 +771,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) trx.sign(key_id_type(),private_key); //Partially settle a call - force_settlement_id_type settle_id = db.push_transaction(trx).operation_results.front().get(); + force_settlement_id_type settle_id = PUSH_TX( db, trx ).operation_results.front().get(); trx.clear(); call_order_id_type call_id = db.get_index_type().indices().get().begin()->id; BOOST_CHECK_EQUAL(settle_id(db).balance.amount.value, 50); @@ -793,7 +793,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) trx.operations.push_back(op); trx.set_expiration(db.head_block_id()); trx.sign(key_id_type(), private_key); - db.push_transaction(trx); + PUSH_TX( db, trx ); //Check that force settlements were all canceled BOOST_CHECK(db.get_index_type().indices().empty()); BOOST_CHECK_EQUAL(get_balance(nathan_id, bit_usd), bit_usd(db).dynamic_data(db).current_supply.value); diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index c896d7fb..da3bed74 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) creator.common_options.core_exchange_rate = price({asset(2),asset(1,1)}); creator.common_options.whitelist_authorities = creator.common_options.blacklist_authorities = {account_id_type()}; trx.operations.push_back(std::move(creator)); - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); const asset_object& test_asset = test_asset_id(db); BOOST_CHECK(test_asset.symbol == "ADVANCED"); @@ -82,16 +82,16 @@ BOOST_AUTO_TEST_CASE( issue_whitelist_uia ) asset_issue_operation op({asset(), advanced.issuer, advanced.amount(1000), nathan.id}); trx.operations.emplace_back(op); //Fail because nathan is not whitelisted. - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); account_whitelist_operation wop({asset(), account_id_type(), nathan.id, account_whitelist_operation::white_listed}); trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(nathan.is_authorized_asset(advanced)); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1000); } catch(fc::exception& e) { @@ -113,13 +113,13 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) transfer_operation op({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)}); trx.operations.push_back(op); //Fail because dan is not whitelisted. - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); account_whitelist_operation wop({asset(), account_id_type(), dan.id, account_whitelist_operation::white_listed}); trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 900); BOOST_CHECK_EQUAL(get_balance(dan, advanced), 100); @@ -127,16 +127,16 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) wop.new_listing |= account_whitelist_operation::black_listed; wop.account_to_list = nathan.id; trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); op.amount = advanced.amount(50); trx.operations.back() = op; //Fail because nathan is blacklisted - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); std::swap(op.from, op.to); trx.operations.back() = op; //Fail because nathan is blacklisted - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); { asset_update_operation op; @@ -145,12 +145,12 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) op.new_options.blacklist_authorities.clear(); op.new_options.blacklist_authorities.insert(dan.id); trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK(advanced.options.blacklist_authorities.find(dan.id) != advanced.options.blacklist_authorities.end()); } trx.operations.back() = op; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 950); BOOST_CHECK_EQUAL(get_balance(dan, advanced), 50); @@ -158,29 +158,29 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) wop.account_to_list = nathan.id; wop.new_listing = account_whitelist_operation::black_listed; trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.back() = op; //Fail because nathan is blacklisted BOOST_CHECK(!nathan.is_authorized_asset(advanced)); - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); //Remove nathan from genesis' whitelist, add him to dan's. This should not authorize him to hold ADVANCED. wop.authorizing_account = account_id_type(); wop.account_to_list = nathan.id; wop.new_listing = account_whitelist_operation::no_listing; trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); wop.authorizing_account = dan.id; wop.account_to_list = nathan.id; wop.new_listing = account_whitelist_operation::white_listed; trx.operations.back() = wop; - db.push_transaction(trx, ~0); + PUSH_TX( db, trx, ~0 ); trx.operations.back() = op; //Fail because nathan is not whitelisted BOOST_CHECK(!nathan.is_authorized_asset(advanced)); - BOOST_REQUIRE_THROW(db.push_transaction(trx, ~0), fc::exception); + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); } catch(fc::exception& e) { edump((e.to_detail_string())); throw; From 8e9b60dd9c240b73ca302627b4d046ac500251de Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 23:22:37 -0400 Subject: [PATCH 021/354] block_tests.cpp: Explicitly specify skip_flags in all places --- tests/tests/block_tests.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index f0d5afdb..4a395a34 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) { database db; db.open(data_dir.path(), genesis_state_type() ); - b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); for( uint32_t i = 1; i < 200; ++i ) { @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first; // TODO: Uncomment this when witness scheduling implemented BOOST_CHECK( cur_witness != prev_witness ); - b = db.generate_block( now, cur_witness, delegate_priv_key ); + b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing ); BOOST_CHECK( b.witness == cur_witness ); } db.close(); @@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first; // TODO: Uncomment this when witness scheduling implemented BOOST_CHECK( cur_witness != prev_witness ); - b = db.generate_block( now, cur_witness, delegate_priv_key ); + b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing ); } BOOST_CHECK_EQUAL( db.head_block_num(), 400 ); } @@ -168,7 +168,7 @@ BOOST_AUTO_TEST_CASE( undo_block ) for( uint32_t i = 0; i < 5; ++i ) { now += db.block_interval(); - auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); } BOOST_CHECK( db.head_block_num() == 5 ); db.pop_block(); @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE( undo_block ) for( uint32_t i = 0; i < 5; ++i ) { now += db.block_interval(); - auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); } BOOST_CHECK( db.head_block_num() == 7 ); } @@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) for( uint32_t i = 0; i < 10; ++i ) { now += db1.block_interval(); - auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); + auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); try { PUSH_BLOCK( db2, b ); } FC_CAPTURE_AND_RETHROW( ("db2") ); @@ -220,13 +220,13 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) for( uint32_t i = 10; i < 13; ++i ) { now += db1.block_interval(); - auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); + auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); } string db1_tip = db1.head_block_id().str(); for( uint32_t i = 13; i < 16; ++i ) { now += db2.block_interval(); - auto b = db2.generate_block(now, db2.get_scheduled_witness(db2.get_slot_at_time(now)).first, delegate_priv_key); + auto b = db2.generate_block(now, db2.get_scheduled_witness(db2.get_slot_at_time(now)).first, delegate_priv_key, database::skip_nothing); // notify both databases of the new block. // only db2 should switch to the new fork, db1 should not PUSH_BLOCK( db1, b ); @@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) BOOST_CHECK_EQUAL(db2.head_block_num(), 13); { now += db2.block_interval(); - auto b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key); + auto b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); good_block = b; b.transactions.emplace_back(signed_transaction()); b.transactions.back().operations.emplace_back(transfer_operation()); @@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE( undo_pending ) PUSH_TX( db, trx ); now += db.block_interval(); - auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); BOOST_CHECK(nathan_id(db).name == "nathan"); @@ -348,17 +348,17 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) auto aw = db1.get_global_properties().active_witnesses; now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); BOOST_CHECK(nathan_id(db1).name == "nathan"); now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); PUSH_BLOCK( db1, b ); aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); PUSH_BLOCK( db1, b ); BOOST_CHECK_THROW(nathan_id(db1), fc::exception); @@ -367,7 +367,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); PUSH_BLOCK( db1, b ); BOOST_CHECK(nathan_id(db1).name == "nathan"); @@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE( tapos ) const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); signed_transaction trx; //This transaction must be in the next block after its reference, or it is invalid. @@ -462,7 +462,7 @@ BOOST_AUTO_TEST_CASE( tapos ) PUSH_TX( db1, trx ); now += db1.block_interval(); - b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key, database::skip_nothing ); trx.operations.clear(); trx.signatures.clear(); From e6246bf9a0938c298b118f5371b000b5999859f5 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 16 Jun 2015 14:43:52 -0400 Subject: [PATCH 022/354] Implement node_property_object --- .../chain/include/graphene/chain/database.hpp | 4 +- .../graphene/chain/node_property_object.hpp | 41 +++++++++++++++++++ .../chain/transaction_evaluation_state.hpp | 5 +-- libraries/chain/proposal_evaluator.cpp | 15 ++++--- .../chain/transaction_evaluation_state.cpp | 4 +- 5 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/node_property_object.hpp diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index b1b170af..20b9f726 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -215,6 +216,7 @@ namespace graphene { namespace chain { const asset_object& get_core_asset()const; const global_property_object& get_global_properties()const; const dynamic_global_property_object& get_dynamic_global_properties()const; + const node_property_object& get_node_properties()const; const fee_schedule_type& current_fee_schedule()const; time_point_sec head_block_time()const; @@ -353,7 +355,7 @@ namespace graphene { namespace chain { ///Steps involved in applying a new block ///@{ - 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 create_block_summary(const signed_block& next_block); //////////////////// db_update.cpp //////////////////// diff --git a/libraries/chain/include/graphene/chain/node_property_object.hpp b/libraries/chain/include/graphene/chain/node_property_object.hpp new file mode 100644 index 00000000..b06099ef --- /dev/null +++ b/libraries/chain/include/graphene/chain/node_property_object.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#include + +namespace graphene { namespace chain { + + /** + * @brief Contains per-node database configuration. + * + * Transactions are evaluated differently based on per-node state. + * Settings here may change based on whether the node is syncing or up-to-date. + * Or whether the node is a witness node. Or if we're processing a + * transaction in a witness-signed block vs. a fresh transaction + * from the p2p network. Or configuration-specified tradeoffs of + * performance/hardfork resilience vs. paranoia. + */ + class node_property_object + { + public: + node_property_object() : skip_flags(0) {} + ~node_property_object(){} + + bool skip_authority_check; + }; +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/transaction_evaluation_state.hpp b/libraries/chain/include/graphene/chain/transaction_evaluation_state.hpp index 20faadf5..e520e4c8 100644 --- a/libraries/chain/include/graphene/chain/transaction_evaluation_state.hpp +++ b/libraries/chain/include/graphene/chain/transaction_evaluation_state.hpp @@ -33,8 +33,8 @@ namespace graphene { namespace chain { class transaction_evaluation_state { public: - transaction_evaluation_state( database* db = nullptr, bool skip_authority_check = false ) - :_db(db),_skip_authority_check(skip_authority_check){} + transaction_evaluation_state( database* db = nullptr ) + :_db(db){} bool check_authority( const account_object&, authority::classification auth_class = authority::active, int depth = 0 ); @@ -55,7 +55,6 @@ namespace graphene { namespace chain { const signed_transaction* _trx = nullptr; database* _db = nullptr; - bool _skip_authority_check = false; bool _is_proposed_trx = false; }; } } // namespace graphene::chain diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index f365263f..cbf1613b 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -93,13 +93,16 @@ void_result proposal_update_evaluator::do_evaluate(const proposal_update_operati FC_ASSERT( _proposal->available_owner_approvals.find(id) != _proposal->available_owner_approvals.end(), "", ("id", id)("available", _proposal->available_owner_approvals) ); } - for( key_id_type id : o.key_approvals_to_add ) + if( (d.get_node_properties().skip_flags & database::skip_authority_check) == 0 ) { - FC_ASSERT( trx_state->signed_by(id) || trx_state->_skip_authority_check ); - } - for( key_id_type id : o.key_approvals_to_remove ) - { - FC_ASSERT( trx_state->signed_by(id) || trx_state->_skip_authority_check ); + for( key_id_type id : o.key_approvals_to_add ) + { + FC_ASSERT( trx_state->signed_by(id) ); + } + for( key_id_type id : o.key_approvals_to_remove ) + { + FC_ASSERT( trx_state->signed_by(id) ); + } } return void_result(); diff --git a/libraries/chain/transaction_evaluation_state.cpp b/libraries/chain/transaction_evaluation_state.cpp index e1271384..35d3973f 100644 --- a/libraries/chain/transaction_evaluation_state.cpp +++ b/libraries/chain/transaction_evaluation_state.cpp @@ -25,7 +25,9 @@ namespace graphene { namespace chain { bool transaction_evaluation_state::check_authority( const account_object& account, authority::classification auth_class, int depth ) { - if( _skip_authority_check || approved_by.find(make_pair(account.id, auth_class)) != approved_by.end() ) + if( (!_is_proposed_trx) && (_db->get_node_properties().skip_flags & database::skip_authority_check) ) + return true; + if( approved_by.find(make_pair(account.id, auth_class)) != approved_by.end() ) return true; FC_ASSERT( account.id.instance() != 0 || _is_proposed_trx ); From 2ec92c669572a02d9dac04c37faf108b637610f7 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 22:22:54 -0400 Subject: [PATCH 023/354] database: Make functions which don't take skip_flags arg, use them to implement old API which does --- libraries/chain/db_block.cpp | 64 ++++++++++++++++++- libraries/chain/db_getter.cpp | 10 +++ .../chain/include/graphene/chain/database.hpp | 61 +++++++++++++++++- .../graphene/chain/node_property_object.hpp | 2 +- 4 files changed, 131 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index d82dde79..905abb21 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -81,7 +81,18 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_ * @return true if we switched forks as a result of this push. */ bool database::push_block( const signed_block& new_block, uint32_t skip ) +{ + bool result; + with_skip_flags( skip, [&]() + { + result = _push_block( new_block ); + } ); + return result; +} + +bool database::_push_block( const signed_block& new_block ) { try { + uint32_t skip = get_node_properties().skip_flags; if( !(skip&skip_fork_db) ) { auto new_head = _fork_db.push_block( new_block ); @@ -182,12 +193,23 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) */ processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) { + processed_transaction result; + with_skip_flags( skip, [&]() + { + result = _push_transaction( trx ); + } ); + return result; +} + +processed_transaction database::_push_transaction( const signed_transaction& trx ) +{ + uint32_t skip = get_node_properties().skip_flags; //wdump((trx.digest())(trx.id())); // If this is the first transaction pushed after applying a block, start a new undo session. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. if( !_pending_block_session ) _pending_block_session = _undo_db.start_undo_session(); auto session = _undo_db.start_undo_session(); - auto processed_trx = apply_transaction( trx, skip ); + auto processed_trx = _apply_transaction( trx ); _pending_block.transactions.push_back(processed_trx); FC_ASSERT( (skip & skip_block_size_check) || @@ -240,8 +262,23 @@ signed_block database::generate_block( const fc::ecc::private_key& block_signing_private_key, uint32_t skip /* = 0 */ ) +{ + signed_block result; + with_skip_flags( skip, [&]() + { + result = _generate_block( when, witness_id, block_signing_private_key ); + } ); + return result; +} + +signed_block database::_generate_block( + fc::time_point_sec when, + witness_id_type witness_id, + const fc::ecc::private_key& block_signing_private_key + ) { try { + uint32_t skip = get_node_properties().skip_flags; uint32_t slot_num = get_slot_at_time( when ); witness_id_type scheduled_witness = get_scheduled_witness( slot_num ).first; FC_ASSERT( scheduled_witness == witness_id ); @@ -322,7 +359,17 @@ const vector& database::get_applied_operations() const //////////////////// private methods //////////////////// void database::apply_block( const signed_block& next_block, uint32_t skip ) +{ + with_skip_flags( skip, [&]() + { + _apply_block( next_block ); + } ); + return; +} + +void database::_apply_block( const signed_block& next_block ) { try { + uint32_t skip = get_node_properties().skip_flags; _applied_ops.clear(); FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) ); @@ -375,16 +422,27 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) update_pending_block(next_block, current_block_interval); -} FC_CAPTURE_AND_RETHROW( (next_block.block_num())(skip) ) } +} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } processed_transaction database::apply_transaction( const signed_transaction& trx, uint32_t skip ) +{ + processed_transaction result; + with_skip_flags( skip, [&]() + { + result = _apply_transaction( trx ); + } ); + return result; +} + +processed_transaction database::_apply_transaction( const signed_transaction& trx ) { try { + uint32_t skip = get_node_properties().skip_flags; trx.validate(); auto& trx_idx = get_mutable_index_type(); auto trx_id = trx.id(); FC_ASSERT( (skip & skip_transaction_dupe_check) || trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end() ); - transaction_evaluation_state eval_state(this, skip&skip_authority_check ); + transaction_evaluation_state eval_state(this); const chain_parameters& chain_parameters = get_global_properties().parameters; eval_state._trx = &trx; diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 24b3465f..7268fcf3 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -63,4 +63,14 @@ decltype( chain_parameters::block_interval ) database::block_interval( )const return get_global_properties().parameters.block_interval; } +const node_property_object& database::get_node_properties()const +{ + return _node_property_object; +} + +node_property_object& database::node_properties() +{ + return _node_property_object; +} + } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 20b9f726..b149b324 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -64,6 +64,31 @@ namespace graphene { namespace chain { vector initial_committee; }; + namespace detail + { + /** + * Class used to help the with_skip_flags implementation. + * It must be defined in this header because it must be + * available to the with_skip_flags implementation, + * which is a template and therefore must also be defined + * in this header. + */ + struct skip_flags_restorer + { + skip_flags_restorer( node_property_object& npo, uint32_t old_skip_flags ) + : _npo( npo ), _old_skip_flags( old_skip_flags ) + {} + + ~skip_flags_restorer() + { + _npo.skip_flags = _old_skip_flags; + } + + node_property_object& _npo; + uint32_t _old_skip_flags; + }; + } + /** * @class database * @brief tracks the blockchain state in an extensible manner @@ -124,6 +149,9 @@ namespace graphene { namespace chain { bool push_block( const signed_block& b, uint32_t skip = skip_nothing ); processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + bool _push_block( const signed_block& b ); + processed_transaction _push_transaction( const signed_transaction& trx ); + ///@throws fc::exception if the proposed transaction fails to apply. processed_transaction push_proposal( const proposal_object& proposal ); @@ -131,7 +159,12 @@ namespace graphene { namespace chain { const fc::time_point_sec when, witness_id_type witness_id, const fc::ecc::private_key& block_signing_private_key, - uint32_t skip = 0 + uint32_t skip + ); + signed_block _generate_block( + const fc::time_point_sec when, + witness_id_type witness_id, + const fc::ecc::private_key& block_signing_private_key ); void pop_block(); @@ -225,6 +258,25 @@ namespace graphene { namespace chain { decltype( chain_parameters::block_interval ) block_interval( )const; + node_property_object& node_properties(); + + /** + * Set the skip_flags to the given value, call callback, + * then reset skip_flags to their previous value after + * callback is done. + */ + template< typename Lambda > + void with_skip_flags( + uint32_t skip_flags, + Lambda callback ) + { + node_property_object& npo = node_properties(); + detail::skip_flags_restorer restorer( npo, npo.skip_flags ); + npo.skip_flags = skip_flags; + callback(); + return; + } + //////////////////// db_init.cpp //////////////////// void initialize_evaluators(); @@ -350,12 +402,15 @@ namespace graphene { namespace chain { void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing ); processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + void _apply_block( const signed_block& next_block ); + processed_transaction _apply_transaction( const signed_transaction& trx ); operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op ); ///Steps involved in applying a new block ///@{ - const witness_object& validate_block_header( const signed_block& next_block )const; + 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 create_block_summary(const signed_block& next_block); //////////////////// db_update.cpp //////////////////// @@ -419,6 +474,8 @@ namespace graphene { namespace chain { vector _witness_count_histogram_buffer; vector _committee_count_histogram_buffer; uint64_t _total_voting_stake; + + node_property_object _node_property_object; }; namespace detail diff --git a/libraries/chain/include/graphene/chain/node_property_object.hpp b/libraries/chain/include/graphene/chain/node_property_object.hpp index b06099ef..92addff9 100644 --- a/libraries/chain/include/graphene/chain/node_property_object.hpp +++ b/libraries/chain/include/graphene/chain/node_property_object.hpp @@ -36,6 +36,6 @@ namespace graphene { namespace chain { node_property_object() : skip_flags(0) {} ~node_property_object(){} - bool skip_authority_check; + uint32_t skip_flags; }; } } // graphene::chain From c65fb6b3662658e21e84d1bfc74695bd452e935c Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 23:06:32 -0400 Subject: [PATCH 024/354] Use with_skip_flags() to handle virtual ops in clear_expired_orders --- libraries/chain/db_update.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 35b265db..9ba15e22 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -118,7 +118,10 @@ void database::clear_expired_proposals() void database::clear_expired_orders() { - transaction_evaluation_state cancel_context(this, true); + with_skip_flags( + get_node_properties().skip_flags | skip_authority_check, [&]() + { + transaction_evaluation_state cancel_context(this); //Cancel expired limit orders auto& limit_index = get_index_type().indices().get(); @@ -141,6 +144,7 @@ void database::clear_expired_orders() canceler.order = order.id; apply_operation(cancel_context, canceler); } + } ); //Process expired force settlement orders auto& settlement_index = get_index_type().indices().get(); From 1fb1e80a2e2af8c3bdf66e3034de8371d375f2e2 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 17 Jun 2015 23:58:45 -0400 Subject: [PATCH 025/354] check skip_flags restored to 0 after all tests --- tests/common/database_fixture.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index dd48a508..997be4a4 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -72,6 +72,8 @@ database_fixture::~database_fixture() verify_account_history_plugin_index(); } + BOOST_CHECK( db.get_node_properties().skip_flags == database::skip_nothing ); + if( data_dir ) db.close(); return; From f29ca98b9e8ed21238335e387dddf17a7f057893 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 18 Jun 2015 11:34:42 -0400 Subject: [PATCH 026/354] authority_tests.cpp: Insert additional balance checks --- tests/tests/authority_tests.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index cad13bb7..2df5550f 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -468,6 +468,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) BOOST_CHECK(pid(db).is_authorized_to_execute(&db)); nathan = &get_account("nathan"); + // no money yet BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000); { @@ -481,17 +482,21 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } + // still no money + BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000); //Time passes... the set of active delegates gets updated. generate_blocks(maintenance_time); //The proposal is no longer authorized, because the active delegates got changed. BOOST_CHECK(!pid(db).is_authorized_to_execute(&db)); + // still no money + BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000); + //Time passes... the proposal has now expired. generate_blocks(pid(db).expiration_time); BOOST_CHECK(db.find(pid) == nullptr); - //Nathan didn't get any more money because the proposal was rejected. - nathan = &get_account("nathan"); + //Nathan never got any more money because the proposal was rejected. BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000); } FC_LOG_AND_RETHROW() } From 53e144ae6b53e730d35b111a857cf1e013c9b55f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 18 Jun 2015 12:44:26 -0400 Subject: [PATCH 027/354] witness.cpp: Explicitly specify skip_flags --- libraries/plugins/witness/witness.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 71fcafa5..d0ae52f9 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -176,7 +177,8 @@ void witness_plugin::block_production_loop() auto block = db.generate_block( scheduled_time, scheduled_witness, - _private_keys[ scheduled_key ] + _private_keys[ scheduled_key ], + graphene::chain::database::skip_nothing ); ilog("Generated block #${n} with timestamp ${t} at time ${c}", ("n", block.block_num())("t", block.timestamp)("c", now)); From f44bb5d1eb9ba8bc6ed8021d43bfa684354005e1 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Thu, 18 Jun 2015 14:28:39 -0400 Subject: [PATCH 028/354] Fix test build --- tests/app/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index d7630550..a3d0a8c3 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) now += GRAPHENE_DEFAULT_BLOCK_INTERVAL; app2.p2p_node()->broadcast(graphene::net::block_message(db2->generate_block( - now, db2->get_scheduled_witness( 1 ).first, genesis_key ))); + now, db2->get_scheduled_witness( 1 ).first, genesis_key, database::skip_nothing ))); fc::usleep(fc::milliseconds(500)); BOOST_CHECK_EQUAL(app1.p2p_node()->get_connection_count(), 1); From 52a1db37003ff5dca40a65aaba61a8e2faffc6c6 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 18 Jun 2015 14:47:53 -0400 Subject: [PATCH 029/354] Fix block_database::fetch_optional() and friends, which were throwing in some cases where the requested block was not available instead of returing an invalid optional. --- libraries/chain/block_database.cpp | 134 +++++++++++++++++------------ 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 2b151f7b..fefa3039 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -135,70 +135,98 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const optional block_database::fetch_optional( const block_id_type& id )const -{ try { - index_entry e; - auto index_pos = sizeof(e)*block_header::num_from_id(id); - _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); - - _block_num_to_pos.seekg( index_pos ); - _block_num_to_pos.read( (char*)&e, sizeof(e) ); - - if( e.block_id != id ) return optional(); - - vector data( e.block_size ); - _blocks.seekg( e.block_pos ); - _blocks.read( data.data(), e.block_size ); - auto result = fc::raw::unpack(data); - FC_ASSERT( result.id() == e.block_id ); - return result; -} FC_CAPTURE_AND_RETHROW( (id) ) } - +{ + try + { + index_entry e; + auto index_pos = sizeof(e)*block_header::num_from_id(id); + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + + _block_num_to_pos.seekg( index_pos ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + + if( e.block_id != id ) return optional(); + + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; + } + catch (const fc::exception&) + { + } + catch (const std::exception&) + { + } + return optional(); +} optional block_database::fetch_by_number( uint32_t block_num )const -{ try { - index_entry e; - auto index_pos = sizeof(e)*block_num; - _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); +{ + try + { + index_entry e; + auto index_pos = sizeof(e)*block_num; + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); - _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); - _block_num_to_pos.read( (char*)&e, sizeof(e) ); + _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); - vector data( e.block_size ); - _blocks.seekg( e.block_pos ); - _blocks.read( data.data(), e.block_size ); - auto result = fc::raw::unpack(data); - FC_ASSERT( result.id() == e.block_id ); - return result; -} FC_CAPTURE_AND_RETHROW( (block_num) ) } + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + auto result = fc::raw::unpack(data); + FC_ASSERT( result.id() == e.block_id ); + return result; + } + catch (const fc::exception&) + { + } + catch (const std::exception&) + { + } + return optional(); +} optional block_database::last()const { - index_entry e; - _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - - if( _block_num_to_pos.tellp() < sizeof(index_entry) ) - return optional(); - - _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end ); - _block_num_to_pos.read( (char*)&e, sizeof(e) ); - while( e.block_size == 0 && _blocks.tellg() > 0 ) + try { - _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur ); + index_entry e; + _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); + + if( _block_num_to_pos.tellp() < sizeof(index_entry) ) + return optional(); + + _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); + while( e.block_size == 0 && _blocks.tellg() > 0 ) + { + _block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.cur ); + _block_num_to_pos.read( (char*)&e, sizeof(e) ); + } + + if( e.block_size == 0 ) + return optional(); + + vector data( e.block_size ); + _blocks.seekg( e.block_pos ); + _blocks.read( data.data(), e.block_size ); + auto result = fc::raw::unpack(data); + return result; } - - if( e.block_size == 0 ) - return optional(); - - vector data( e.block_size ); - _blocks.seekg( e.block_pos ); - _blocks.read( data.data(), e.block_size ); - auto result = fc::raw::unpack(data); - return result; + catch (const fc::exception&) + { + } + catch (const std::exception&) + { + } + return optional(); } - } } From 929125021431860146278e21595ff1389443ba5b Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 09:19:14 -0400 Subject: [PATCH 030/354] Issue #52: working my way out of the rabbit hole Evils abound. And tests still fail. --- libraries/chain/db_init.cpp | 264 ++++++++---------- libraries/chain/db_maint.cpp | 2 +- libraries/chain/db_management.cpp | 4 +- .../include/graphene/chain/account_object.hpp | 4 +- .../chain/include/graphene/chain/database.hpp | 8 +- libraries/db/object_database.cpp | 2 +- libraries/plugins/witness/witness.cpp | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 6 +- libraries/wallet/wallet.cpp | 6 +- programs/cli_wallet/main.cpp | 2 +- tests/app/main.cpp | 2 +- tests/benchmarks/genesis_allocation.cpp | 2 +- tests/common/database_fixture.cpp | 21 +- tests/common/database_fixture.hpp | 9 +- tests/intense/block_tests.cpp | 2 +- tests/performance/performance_tests.cpp | 2 +- tests/tests/authority_tests.cpp | 14 +- tests/tests/block_tests.cpp | 80 +++--- tests/tests/operation_tests.cpp | 12 +- tests/tests/operation_tests2.cpp | 2 +- 20 files changed, 230 insertions(+), 216 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 7f4d3b90..e65cdf94 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -129,112 +129,45 @@ void database::initialize_indexes() void database::init_genesis(const genesis_state_type& genesis_state) { try { - _undo_db.disable(); + FC_ASSERT(genesis_state.initial_witnesses.size() > 0, + "Cannot start a chain with zero witnesses."); - fc::ecc::private_key genesis_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); - const key_object& genesis_key = - create( [&genesis_private_key](key_object& k) { - k.key_data = public_key_type(genesis_private_key.get_public_key()); - }); - const account_statistics_object& genesis_statistics = - create( [&](account_statistics_object& b){ - }); + _undo_db.disable(); + transaction_evaluation_state genesis_eval_state(this, true); + + // Create initial accounts + fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); + create( [&null_private_key](key_object& k) { + k.key_data = public_key_type(null_private_key.get_public_key()); + }); create( [](account_balance_object& b) { b.balance = GRAPHENE_INITIAL_SUPPLY; }); - const account_object& genesis_account = + const account_object& committee_account = create( [&](account_object& n) { n.membership_expiration_date = time_point_sec::maximum(); n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - n.name = "genesis"; - n.owner.add_authority(genesis_key.get_id(), 1); - n.owner.weight_threshold = 1; + n.name = "committee-account"; n.active = n.owner; - n.options.memo_key = genesis_key.id; - n.statistics = genesis_statistics.id; + n.statistics = create( [&](account_statistics_object& b){}).id; }); - - vector init_delegates; - vector init_witnesses; - flat_set init_witness_set; - - auto delegates_and_witnesses = std::max(GRAPHENE_MIN_WITNESS_COUNT, GRAPHENE_MIN_DELEGATE_COUNT); - for( int i = 0; i < delegates_and_witnesses; ++i ) - { - const account_statistics_object& stats_obj = - create( [&](account_statistics_object&){ - }); - const account_object& delegate_account = - create( [&](account_object& a) { - a.active = a.owner = genesis_account.owner; - a.referrer = account_id_type(i); - a.registrar = account_id_type(i); - a.lifetime_referrer = account_id_type(i); - a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - a.membership_expiration_date = fc::time_point_sec::maximum(); - a.name = string("init") + fc::to_string(i); - a.statistics = stats_obj.id; - }); - const delegate_object& init_delegate = create( [&](delegate_object& d) { - d.delegate_account = delegate_account.id; - d.vote_id = i * 2; - }); - init_delegates.push_back(init_delegate.id); - - const witness_object& init_witness = create( [&](witness_object& d) { - d.witness_account = delegate_account.id; - d.vote_id = i * 2 + 1; - secret_hash_type::encoder enc; - fc::raw::pack( enc, genesis_private_key ); - fc::raw::pack( enc, d.last_secret ); - d.next_secret = secret_hash_type::hash(enc.result()); - }); - init_witnesses.push_back(init_witness.id); - init_witness_set.insert(init_witness.id); - - } - create( [&](block_summary_object& p) { + create([this](account_object& a) { + a.name = "null-account"; + a.statistics = create([](account_statistics_object&){}).id; + a.owner = authority(1, key_id_type(), 1); + a.active = a.owner; + a.registrar = a.lifetime_referrer = a.referrer = account_id_type(1); + 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; }); - const witness_schedule_object& wso = - create( [&]( witness_schedule_object& _wso ) - { - memset( _wso.rng_seed.begin(), 0, _wso.rng_seed.size() ); - - witness_scheduler_rng rng( _wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV ); - - _wso.scheduler = witness_scheduler(); - _wso.scheduler._min_token_count = init_witnesses.size() / 2; - _wso.scheduler.update( init_witness_set ); - - for( size_t i=0; i( [&](global_property_object& p) { - p.active_delegates = init_delegates; - for( const witness_id_type& wit : init_witnesses ) - p.active_witnesses.insert( wit ); - p.next_available_vote_id = delegates_and_witnesses * 2; - p.chain_id = fc::digest(genesis_state); - }); - (void)properties; - - create( [&](dynamic_global_property_object& p) { - p.time = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); - }); - + // Create core asset const asset_dynamic_data_object& dyn_asset = create( [&]( asset_dynamic_data_object& a ) { a.current_supply = GRAPHENE_INITIAL_SUPPLY; }); - const asset_object& core_asset = create( [&]( asset_object& a ) { a.symbol = GRAPHENE_SYMBOL; @@ -242,7 +175,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; a.options.flags = 0; a.options.issuer_permissions = 0; - a.issuer = genesis_account.id; + a.issuer = committee_account.id; a.options.core_exchange_rate.base.amount = 1; a.options.core_exchange_rate.base.asset_id = 0; a.options.core_exchange_rate.quote.amount = 1; @@ -253,74 +186,71 @@ void database::init_genesis(const genesis_state_type& genesis_state) assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); (void)core_asset; + // Create global properties + create([&](global_property_object& p) { + p.chain_id = fc::digest(genesis_state); + }); + create( [&](dynamic_global_property_object& p) { + p.time = fc::time_point_sec(GRAPHENE_GENESIS_TIMESTAMP); + }); + create([&](block_summary_object& p) { + }); + + // Create user accounts, apply initial stake allocation if( !genesis_state.allocation_targets.empty() ) { share_type total_allocation = 0; for( const auto& handout : genesis_state.allocation_targets ) total_allocation += handout.weight; - auto mangle_to_name = [](const fc::static_variant& key) { - string addr = string(key.which() == std::decay::type::tag
::value? key.get
() - : key.get()); - string result = "import"; - string key_string = string(addr).substr(sizeof(GRAPHENE_ADDRESS_PREFIX)-1); - for( char c : key_string ) - { - if( isupper(c) ) - result += string("-") + char(tolower(c)); - else - result += c; - } - return result; - }; - fc::time_point start_time = fc::time_point::now(); for( const auto& handout : genesis_state.allocation_targets ) { asset amount(handout.weight); - amount.amount = ((fc::uint128(amount.amount.value) * GRAPHENE_INITIAL_SUPPLY)/total_allocation.value).to_uint64(); - if( amount.amount == 0 ) - { - wlog("Skipping zero allocation to ${k}", ("k", handout.name)); - continue; - } - signed_transaction trx; - trx.operations.emplace_back(key_create_operation({asset(), genesis_account.id, handout.addr})); - relative_key_id_type key_id(0); - authority account_authority(1, key_id, 1); + key_id_type key_id = apply_operation(genesis_eval_state, key_create_operation({asset(), committee_account.id, handout.addr})).get(); account_create_operation cop; cop.name = handout.name; cop.registrar = account_id_type(1); - cop.active = account_authority; - cop.owner = account_authority; + cop.active = authority(1, key_id, 1); + cop.owner = cop.active; cop.options.memo_key = key_id; - trx.operations.push_back(cop); - trx.validate(); - auto ptrx = apply_transaction(trx, ~0); - trx = signed_transaction(); - account_id_type account_id(ptrx.operation_results.back().get()); - trx.operations.emplace_back(transfer_operation({ asset(), - genesis_account.id, - account_id, - amount, - memo_data()//vector() - })); - trx.validate(); - apply_transaction(trx, ~0); + account_id_type account_id(apply_operation(genesis_eval_state, cop).get()); + + if( handout.is_lifetime_member ) + { + account_upgrade_operation op; + op.account_to_upgrade = account_id; + op.upgrade_to_lifetime_member = true; + apply_operation(genesis_eval_state, op); + } + + if( amount.amount > 0 ) + { + amount.amount = ((fc::uint128(amount.amount.value) * GRAPHENE_INITIAL_SUPPLY)/total_allocation.value).to_uint64(); + apply_operation(genesis_eval_state, transfer_operation({asset(), + committee_account.id, + account_id, + amount, + memo_data() + })); + } } - asset leftovers = get_balance(account_id_type(), asset_id_type()); - if( leftovers.amount > 0 ) + if( total_allocation != 0 ) { - modify(*get_index_type().indices().get().find(boost::make_tuple(account_id_type(), asset_id_type())), - [](account_balance_object& b) { - b.adjust_balance(-b.get_balance()); - }); - modify(core_asset.dynamic_asset_data_id(*this), [&leftovers](asset_dynamic_data_object& d) { - d.accumulated_fees += leftovers.amount; - }); + asset leftovers = get_balance(account_id_type(), asset_id_type()); + if( leftovers.amount > 0 ) + { + modify(*get_index_type().indices().get().find(boost::make_tuple(account_id_type(), asset_id_type())), + [](account_balance_object& b) { + b.adjust_balance(-b.get_balance()); + }); + modify(core_asset.dynamic_asset_data_id(*this), [&leftovers](asset_dynamic_data_object& d) { + d.accumulated_fees += leftovers.amount; + }); + } } fc::microseconds duration = fc::time_point::now() - start_time; @@ -328,6 +258,60 @@ void database::init_genesis(const genesis_state_type& genesis_state) ("n", genesis_state.allocation_targets.size())("t", duration.count() / 1000)); } + flat_set init_delegates; + flat_set init_witnesses; + const auto& accounts_by_name = get_index_type().indices().get(); + + // Create initial witnesses and delegates + std::for_each(genesis_state.initial_witnesses.begin(), genesis_state.initial_witnesses.end(), + [&](const genesis_state_type::initial_witness_type& witness) { + const account_object& witness_account = *accounts_by_name.find(witness.owner_name); + const key_object& signing_key = create([&witness](key_object& k) { k.key_data = witness.block_signing_key; }); + + witness_create_operation op; + op.block_signing_key = signing_key.get_id(); + op.initial_secret = witness.initial_secret; + op.witness_account = witness_account.get_id(); + witness_id_type id = apply_operation(genesis_eval_state, op).get(); + init_witnesses.emplace(id); + }); + std::for_each(genesis_state.initial_committee.begin(), genesis_state.initial_committee.end(), + [&](const genesis_state_type::initial_committee_member_type& member) { + const account_object& member_account = *accounts_by_name.find(member.owner_name); + delegate_create_operation op; + op.delegate_account = member_account.get_id(); + delegate_id_type id = apply_operation(genesis_eval_state, op).get(); + init_delegates.emplace(id); + }); + + // Set initial witnesses and committee as active + modify(get_global_properties(), [&](global_property_object& p) { + p.active_delegates = vector(init_delegates.begin(), init_delegates.end()); + p.active_witnesses = init_witnesses; + std::transform(p.active_witnesses.begin(), p.active_witnesses.end(), + std::inserter(p.witness_accounts, p.witness_accounts.begin()), + [&](witness_id_type id) { return get(id).witness_account; }); + }); + + // Initialize witness schedule + const witness_schedule_object& wso = + create([&](witness_schedule_object& _wso) + { + memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size()); + + witness_scheduler_rng rng(_wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + _wso.scheduler = witness_scheduler(); + _wso.scheduler._min_token_count = init_witnesses.size() / 2; + _wso.scheduler.update(init_witnesses); + + for( size_t i=0; i, member< object, object_id_type, &object::id > >, ordered_non_unique< tag, member > > - > account_object_multi_index_type; + > account_multi_index_type; /** * @ingroup object_index */ - typedef generic_index account_index; + typedef generic_index account_index; }} diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index b1b170af..5f691b52 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,11 +40,15 @@ namespace graphene { namespace chain { struct genesis_state_type { struct allocation_target_type { - allocation_target_type(const string& name = string(), const address& addr = address(), share_type weight = share_type()) - : name(name), addr(addr), weight(weight){} + allocation_target_type(const string& name = string(), + const address& addr = address(), + share_type weight = share_type(), + bool is_lifetime_member = false) + : name(name), addr(addr), weight(weight),is_lifetime_member(is_lifetime_member){} string name; address addr; share_type weight; + bool is_lifetime_member; }; struct initial_witness_type { /// Must correspond to one of the allocation targets. diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index dc5cba58..1949a0c1 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -84,7 +84,7 @@ void object_database::wipe(const fc::path& data_dir) assert(!fc::exists(data_dir / "object_database")); } -void object_database::open( const fc::path& data_dir ) +void object_database::open(const fc::path& data_dir) { try { ilog("Open object_database in ${d}", ("d", data_dir)); _data_dir = data_dir; diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 71fcafa5..ff4cd1a9 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -35,7 +35,7 @@ void witness_plugin::plugin_set_program_options( ("witness-id,w", bpo::value>()->composing()->multitoken(), "ID of witness controlled by this node (e.g. \"1.7.0\", quotes are required, may specify multiple times)") ("private-key", bpo::value>()->composing()->multitoken()-> - DEFAULT_VALUE_VECTOR(std::make_pair(chain::key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("genesis"))))), + DEFAULT_VALUE_VECTOR(std::make_pair(chain::key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("null_key"))))), "Tuple of [key ID, private key] (may specify multiple times)") ; config_file_options.add(command_line_options); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index fc1f0a9f..2d339cd6 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -26,8 +26,8 @@ using namespace graphene::utilities; using namespace std; namespace fc{ -void to_variant(const account_object_multi_index_type& accts, variant& vo); -void from_variant(const variant &var, account_object_multi_index_type &vo); +void to_variant(const account_multi_index_type& accts, variant& vo); +void from_variant(const variant &var, account_multi_index_type &vo); } namespace graphene { namespace wallet { @@ -49,7 +49,7 @@ struct plain_keys struct wallet_data { - account_object_multi_index_type my_accounts; + account_multi_index_type my_accounts; /// @return IDs of all accounts in @ref my_accounts vector my_account_ids()const { diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6dc9e67a..6c7108a8 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1748,13 +1748,13 @@ signed_transaction wallet_api::short_sell_asset(string seller_name, string amoun } } } -void fc::to_variant(const account_object_multi_index_type& accts, fc::variant& vo) +void fc::to_variant(const account_multi_index_type& accts, fc::variant& vo) { vo = vector(accts.begin(), accts.end()); } -void fc::from_variant(const fc::variant& var, account_object_multi_index_type& vo) +void fc::from_variant(const fc::variant& var, account_multi_index_type& vo) { const vector& v = var.as>(); - vo = account_object_multi_index_type(v.begin(), v.end()); + vo = account_multi_index_type(v.begin(), v.end()); } diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index e167bbb1..c3a89df7 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -103,7 +103,7 @@ int main( int argc, char** argv ) //fc::configure_logging( cfg ); - fc::ecc::private_key genesis_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); + fc::ecc::private_key genesis_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); idump( (key_to_wif( genesis_private_key ) ) ); diff --git a/tests/app/main.cpp b/tests/app/main.cpp index d7630550..da1bf2c3 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) ilog("Connected!"); fc::ecc::private_key nathan_key = fc::ecc::private_key::generate(); - fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); + fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); graphene::chain::signed_transaction trx; trx.set_expiration(now + fc::seconds(30)); std::shared_ptr db2 = app2.chain_database(); diff --git a/tests/benchmarks/genesis_allocation.cpp b/tests/benchmarks/genesis_allocation.cpp index a9d73f1f..92e64dd9 100644 --- a/tests/benchmarks/genesis_allocation.cpp +++ b/tests/benchmarks/genesis_allocation.cpp @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE( genesis_and_persistence_bench ) BOOST_CHECK(db.get_balance(account_id_type(i), asset_id_type()).amount == GRAPHENE_INITIAL_SUPPLY / account_count); int blocks_out = 0; - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto aw = db.get_global_properties().active_witnesses; auto b = db.generate_block( db.get_slot_time( 1 ), db.get_scheduled_witness( 1 ).first, delegate_priv_key, ~0 ); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index fcecaa43..d92d123a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -53,7 +53,18 @@ database_fixture::database_fixture() ahplugin->plugin_set_app( &app ); ahplugin->plugin_initialize( options ); - db.init_genesis(); + secret_hash_type::encoder enc; + fc::raw::pack(enc, delegate_priv_key); + fc::raw::pack(enc, secret_hash_type()); + for( int i = 0; i < 10; ++i ) + { + genesis_state.allocation_targets.emplace_back("init"+fc::to_string(i), delegate_priv_key.get_public_key(), 0, true); + genesis_state.initial_committee.push_back({"init"+fc::to_string(i)}); + } + genesis_state.initial_witnesses = vector(10, {"committee-account", + delegate_priv_key.get_public_key(), + secret_hash_type::hash(enc.result())}); + db.init_genesis(genesis_state); ahplugin->plugin_startup(); generate_block(); @@ -79,8 +90,8 @@ database_fixture::~database_fixture() fc::ecc::private_key database_fixture::generate_private_key(string seed) { - static const fc::ecc::private_key genesis = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); - if( seed == "genesis" ) + static const fc::ecc::private_key genesis = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); + if( seed == "null_key" ) return genesis; return fc::ecc::private_key::regenerate(fc::sha256::hash(seed)); } @@ -250,7 +261,7 @@ void database_fixture::open_database() { if( !data_dir ) { data_dir = fc::temp_directory(); - db.open(data_dir->path()); + db.open(data_dir->path(), genesis_state); } } @@ -277,7 +288,7 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i generate_block(); auto slots_to_miss = db.get_slot_at_time(timestamp) - 1; if( slots_to_miss <= 0 ) return; - generate_block(~0, generate_private_key("genesis"), slots_to_miss); + generate_block(~0, delegate_priv_key, slots_to_miss); return; } while( db.head_block_time() < timestamp ) diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 649df558..92cf0b2e 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -88,12 +88,13 @@ struct database_fixture { // the reason we use an app is to exercise the indexes of built-in // plugins graphene::app::application app; + genesis_state_type genesis_state; chain::database &db; signed_transaction trx; key_id_type genesis_key; account_id_type genesis_account; fc::ecc::private_key private_key = fc::ecc::private_key::generate(); - fc::ecc::private_key delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + fc::ecc::private_key delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); fc::time_point_sec genesis_time = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); fc::time_point_sec now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); const key_object* key1= nullptr; @@ -113,7 +114,7 @@ struct database_fixture { void verify_account_history_plugin_index( )const; void open_database(); signed_block generate_block(uint32_t skip = ~0, - const fc::ecc::private_key& key = generate_private_key("genesis"), + const fc::ecc::private_key& key = generate_private_key("null_key"), int miss_blocks = 0); /** @@ -189,10 +190,10 @@ struct database_fixture { const delegate_object& create_delegate( const account_object& owner ); const witness_object& create_witness(account_id_type owner, key_id_type signing_key = key_id_type(), - const fc::ecc::private_key& signing_private_key = generate_private_key("genesis")); + const fc::ecc::private_key& signing_private_key = generate_private_key("null_key")); const witness_object& create_witness(const account_object& owner, key_id_type signing_key = key_id_type(), - const fc::ecc::private_key& signing_private_key = generate_private_key("genesis")); + const fc::ecc::private_key& signing_private_key = generate_private_key("null_key")); const key_object& register_key( const public_key_type& key ); const key_object& register_address( const address& addr ); uint64_t fund( const account_object& account, const asset& amount = asset(500000) ); diff --git a/tests/intense/block_tests.cpp b/tests/intense/block_tests.cpp index 1a971b29..bcee36cd 100644 --- a/tests/intense/block_tests.cpp +++ b/tests/intense/block_tests.cpp @@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) ; // Sam is the creator of accounts - private_key_type genesis_key = generate_private_key("genesis"); + private_key_type genesis_key = delegate_priv_key; private_key_type sam_key = generate_private_key("sam"); // diff --git a/tests/performance/performance_tests.cpp b/tests/performance/performance_tests.cpp index 7c3c4697..4f6f98c6 100644 --- a/tests/performance/performance_tests.cpp +++ b/tests/performance/performance_tests.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE( transfer_benchmark ) { fc::ecc::private_key nathan_key = fc::ecc::private_key::generate(); const key_object& key = register_key(nathan_key.get_public_key()); - const auto& genesis = account_id_type()(db); //get_account( "genesis" ); + const auto& genesis = account_id_type()(db); auto start = fc::time_point::now(); for( uint32_t i = 0; i < 1000*1000; ++i ) { diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 4ce9740d..18ffa16c 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts ) trx.operations.push_back(op); sign(trx, key2.id,parent2_key); sign(trx, grandparent_key_obj.id,grandparent_key); - sign(trx, key_id_type(), generate_private_key("genesis")); + sign(trx, key_id_type(), delegate_priv_key); //Fails due to recursion depth. BOOST_CHECK_THROW(db.push_transaction(trx, database::skip_transaction_dupe_check), fc::exception); sign(trx, child_key_obj.id, child_key); @@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) try { INVOKE(any_two_of_three); - fc::ecc::private_key genesis_key = generate_private_key("genesis"); + fc::ecc::private_key genesis_key = delegate_priv_key; fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3")); @@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) BOOST_AUTO_TEST_CASE( genesis_authority ) { try { fc::ecc::private_key nathan_key = fc::ecc::private_key::generate(); - fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); + fc::ecc::private_key genesis_key = delegate_priv_key; const auto& nathan_key_obj = register_key(nathan_key.get_public_key()); key_id_type nathan_key_id = nathan_key_obj.get_id(); const account_object nathan = create_account("nathan", nathan_key_obj.id); @@ -415,7 +415,7 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) { try { generate_block(); - fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))); + fc::ecc::private_key genesis_key = delegate_priv_key; fc::ecc::private_key delegate_key = fc::ecc::private_key::generate(); auto delegate_key_object = register_key(delegate_key.get_public_key()); @@ -812,7 +812,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture ) transaction tx; processed_transaction ptx; - private_key_type genesis_key = generate_private_key("genesis"); + private_key_type genesis_key = delegate_priv_key; // Sam is the creator of accounts private_key_type sam_key = generate_private_key("sam"); @@ -903,7 +903,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) { try { - private_key_type genesis_key = generate_private_key("genesis"); + private_key_type genesis_key = delegate_priv_key; // Sam is the creator of accounts private_key_type alice_key = generate_private_key("alice"); private_key_type bob_key = generate_private_key("bob"); @@ -987,8 +987,10 @@ BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) delegate_id_type nathan_delegate = create_delegate(nathan_id(db)).id; delegate_id_type vikram_delegate = create_delegate(vikram_id(db)).id; + wdump((db.get_balance(account_id_type(), asset_id_type()))); generate_block(); + wdump((db.get_balance(account_id_type(), asset_id_type()))); transfer(account_id_type(), nathan_id, asset(1000000)); transfer(account_id_type(), vikram_id, asset(100)); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 0fed7963..08557fb6 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -35,6 +35,23 @@ using namespace graphene::chain; +genesis_state_type make_genesis() { + genesis_state_type genesis_state; + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); + secret_hash_type::encoder enc; + fc::raw::pack(enc, delegate_priv_key); + fc::raw::pack(enc, secret_hash_type()); + for( int i = 0; i < 10; ++i ) + { + genesis_state.allocation_targets.emplace_back("init"+fc::to_string(i), delegate_priv_key.get_public_key(), 0, true); + genesis_state.initial_committee.push_back({"init"+fc::to_string(i)}); + } + genesis_state.initial_witnesses = vector(10, {"committee-account", + delegate_priv_key.get_public_key(), + secret_hash_type::hash(enc.result())}); + return genesis_state; +} + BOOST_AUTO_TEST_SUITE(block_tests) BOOST_AUTO_TEST_CASE( block_database_test ) @@ -113,10 +130,10 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) now += GRAPHENE_DEFAULT_BLOCK_INTERVAL; // TODO: Don't generate this here - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); { database db; - db.open(data_dir.path(), genesis_state_type() ); + db.open(data_dir.path(), make_genesis() ); b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); for( uint32_t i = 1; i < 200; ++i ) @@ -161,10 +178,10 @@ BOOST_AUTO_TEST_CASE( undo_block ) fc::temp_directory data_dir; { database db; - db.open(data_dir.path(), genesis_state_type() ); + db.open(data_dir.path(), make_genesis() ); fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); for( uint32_t i = 0; i < 5; ++i ) { now += db.block_interval(); @@ -204,11 +221,11 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); database db1; - db1.open(data_dir1.path(), genesis_state_type()); + db1.open(data_dir1.path(), make_genesis()); database db2; - db2.open(data_dir2.path(), genesis_state_type()); + db2.open(data_dir2.path(), make_genesis()); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); for( uint32_t i = 0; i < 10; ++i ) { now += db1.block_interval(); @@ -269,9 +286,9 @@ BOOST_AUTO_TEST_CASE( undo_pending ) fc::temp_directory data_dir; { database db; - db.open(data_dir.path()); + db.open(data_dir.path(), make_genesis()); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); const graphene::db::index& account_idx = db.get_index(protocol_ids, account_object_type); { @@ -328,11 +345,11 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) dir2; database db1, db2; - db1.open(dir1.path()); - db2.open(dir2.path()); + db1.open(dir1.path(), make_genesis()); + db2.open(dir2.path(), make_genesis()); fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); signed_transaction trx; @@ -386,12 +403,12 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions ) dir2; database db1, db2; - db1.open(dir1.path()); - db2.open(dir2.path()); + db1.open(dir1.path(), make_genesis()); + db2.open(dir2.path(), make_genesis()); auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check; - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); signed_transaction trx; @@ -434,16 +451,16 @@ BOOST_AUTO_TEST_CASE( tapos ) dir2; database db1, db2; - db1.open(dir1.path()); - db2.open(dir2.path()); + db1.open(dir1.path(), make_genesis()); + db2.open(dir2.path(), make_genesis()); const account_object& init1 = *db1.get_index_type().indices().get().find("init1"); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); const graphene::db::index& account_idx = db1.get_index(protocol_ids, account_object_type); now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); signed_transaction trx; //This transaction must be in the next block after its reference, or it is invalid. @@ -455,24 +472,19 @@ BOOST_AUTO_TEST_CASE( tapos ) cop.name = "nathan"; cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); - trx.sign( key_id_type(), delegate_priv_key ); - - trx.signatures.clear(); - trx.sign( key_id_type(), delegate_priv_key ); + trx.sign(key_id_type(2), delegate_priv_key); db1.push_transaction(trx); - now += db1.block_interval(); - b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key); + trx.clear(); - trx.operations.clear(); - trx.signatures.clear(); trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)})); - trx.sign( key_id_type(), delegate_priv_key ); + trx.sign(key_id_type(2), delegate_priv_key); //relative_expiration is 1, but ref block is 2 blocks old, so this should fail. BOOST_REQUIRE_THROW(db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check), fc::exception); trx.set_expiration(db1.head_block_id(), 2); trx.signatures.clear(); - trx.sign( key_id_type(), delegate_priv_key ); + trx.sign(key_id_type(2), delegate_priv_key); db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -637,7 +649,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) uop.new_parameters.block_interval = 1; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - trx.sign(key_id_type(),generate_private_key("genesis")); + trx.sign(key_id_type(),delegate_priv_key); db.push_transaction(trx); } { @@ -646,7 +658,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) uop.active_approvals_to_add = {account_id_type(1), account_id_type(2), account_id_type(3), account_id_type(4), account_id_type(5), account_id_type(6), account_id_type(7), account_id_type(8)}; trx.operations.push_back(uop); - trx.sign(key_id_type(),generate_private_key("genesis")); + trx.sign(key_id_type(),delegate_priv_key); db.push_transaction(trx); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(&db)); } @@ -672,7 +684,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) { try { - auto private_key = generate_private_key("genesis"); + auto private_key = delegate_priv_key; account_id_type nathan_id = create_account("nathan").get_id(); account_id_type shorter1_id = create_account("shorter1").get_id(); account_id_type shorter2_id = create_account("shorter2").get_id(); @@ -818,7 +830,7 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) const asset_object& core = asset_id_type()(db); // Sam is the creator of accounts - private_key_type genesis_key = generate_private_key("genesis"); + private_key_type genesis_key = delegate_priv_key; private_key_type sam_key = generate_private_key("sam"); account_object sam_account_object = create_account( "sam", sam_key ); @@ -866,7 +878,7 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture ) near_schedule = db.get_near_witness_schedule(); idump((db.head_block_time())); - generate_block(0, generate_private_key("genesis"), 2); + generate_block(0, delegate_priv_key, 2); idump((db.head_block_time())); BOOST_CHECK(db.get_dynamic_global_properties().current_witness == near_schedule[2]); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index f2c503be..11f5a955 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE( create_account_test ) op.owner = auth_bak; trx.operations.back() = op; - trx.sign( key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))) ); + trx.sign(key_id_type(), delegate_priv_key); trx.validate(); db.push_transaction(trx, ~0); @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE( child_account ) op.owner = authority(1, child_key.get_id(), 1); op.active = authority(1, child_key.get_id(), 1); trx.operations.emplace_back(op); - sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")))); + trx.sign({}, delegate_priv_key); BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception); sign(trx, nathan_key.id,nathan_private_key); @@ -130,8 +130,8 @@ BOOST_AUTO_TEST_CASE( child_account ) trx.signatures.clear(); op.owner = authority(1, account_id_type(nathan.id), 1); trx.operations = {op}; - sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis")))); - sign(trx, nathan_key.id, nathan_private_key); + trx.sign({}, delegate_priv_key); + trx.sign(nathan_key.id, nathan_private_key); db.push_transaction(trx); BOOST_CHECK( get_account("nathan/child").active.auths == op.active.auths ); @@ -1881,7 +1881,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.operations.push_back(uop); trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); - trx.sign(key_id_type(),generate_private_key("genesis")); + trx.sign(key_id_type(),delegate_priv_key); db.push_transaction(trx); trx.clear(); BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 8950000000); @@ -1960,7 +1960,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.operations.back() = wop; trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); - trx.sign(key_id_type(),generate_private_key("genesis")); + trx.sign(key_id_type(),delegate_priv_key); db.push_transaction(trx); trx.clear(); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index aaebbdc1..1ff69a82 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -685,7 +685,7 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) { try { - auto private_key = generate_private_key("genesis"); + auto private_key = delegate_priv_key; account_id_type nathan_id = create_account("nathan").get_id(); account_id_type shorter1_id = create_account("shorter1").get_id(); account_id_type shorter2_id = create_account("shorter2").get_id(); From d47c2ee2a28cf4dd815bcbdf517be70f7ab7294a Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 15:02:42 -0400 Subject: [PATCH 031/354] Progress #52: The tests pass again. --- libraries/chain/db_init.cpp | 43 ++++++++++++--- .../chain/include/graphene/chain/config.hpp | 4 +- .../chain/transaction_evaluation_state.cpp | 4 +- tests/common/database_fixture.cpp | 16 +++--- tests/tests/authority_tests.cpp | 47 ++++++++++++----- tests/tests/block_tests.cpp | 52 +++++++++++-------- tests/tests/operation_tests.cpp | 7 +-- tests/tests/operation_tests2.cpp | 9 ++-- 8 files changed, 121 insertions(+), 61 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index e65cdf94..35d655fd 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -152,16 +152,47 @@ void database::init_genesis(const genesis_state_type& genesis_state) n.active = n.owner; n.statistics = create( [&](account_statistics_object& b){}).id; }); - create([this](account_object& a) { - a.name = "null-account"; + 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&){}).id; - a.owner = authority(1, key_id_type(), 1); - a.active = a.owner; - a.registrar = a.lifetime_referrer = a.referrer = account_id_type(1); + a.owner.weight_threshold = 1; + a.active.weight_threshold = 1; + a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_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_WITNESS_ACCOUNT); + FC_ASSERT(create([this](account_object& a) { + a.name = "relaxed-committee-account"; + a.statistics = create([](account_statistics_object&){}).id; + a.owner.weight_threshold = 1; + a.active.weight_threshold = 1; + a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_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_RELAXED_COMMITTEE_ACCOUNT); + FC_ASSERT(create([this](account_object& a) { + a.name = "null-account"; + a.statistics = create([](account_statistics_object&){}).id; + a.owner.weight_threshold = 0; + a.active.weight_threshold = 0; + a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_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_NULL_ACCOUNT); + FC_ASSERT(create([this](account_object& a) { + a.name = "temp-account"; + a.statistics = create([](account_statistics_object&){}).id; + a.owner.weight_threshold = 0; + a.active.weight_threshold = 0; + a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_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_TEMP_ACCOUNT); // Create core asset const asset_dynamic_data_object& dyn_asset = diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 066a18e6..4d1d3613 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -117,9 +117,9 @@ * Reserved Account IDs with special meaning */ ///@{ -#define GRAPHENE_GENESIS_ACCOUNT (graphene::chain::account_id_type(0)) +#define GRAPHENE_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(0)) #define GRAPHENE_WITNESS_ACCOUNT (graphene::chain::account_id_type(1)) -#define GRAPHENE_DELEGATE_ACCOUNT (graphene::chain::account_id_type(2)) +#define GRAPHENE_RELAXED_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(2)) #define GRAPHENE_NULL_ACCOUNT (graphene::chain::account_id_type(3)) #define GRAPHENE_TEMP_ACCOUNT (graphene::chain::account_id_type(4)) ///@} diff --git a/libraries/chain/transaction_evaluation_state.cpp b/libraries/chain/transaction_evaluation_state.cpp index e1271384..5228c471 100644 --- a/libraries/chain/transaction_evaluation_state.cpp +++ b/libraries/chain/transaction_evaluation_state.cpp @@ -25,7 +25,9 @@ namespace graphene { namespace chain { bool transaction_evaluation_state::check_authority( const account_object& account, authority::classification auth_class, int depth ) { - if( _skip_authority_check || approved_by.find(make_pair(account.id, auth_class)) != approved_by.end() ) + if( _skip_authority_check || + account.get_id() == GRAPHENE_TEMP_ACCOUNT || + approved_by.find(make_pair(account.id, auth_class)) != approved_by.end() ) return true; FC_ASSERT( account.id.instance() != 0 || _is_proposed_trx ); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index d92d123a..2d18c480 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -50,20 +50,20 @@ database_fixture::database_fixture() boost::program_options::variables_map options; // app.initialize(); - ahplugin->plugin_set_app( &app ); - ahplugin->plugin_initialize( options ); + ahplugin->plugin_set_app(&app); + ahplugin->plugin_initialize(options); secret_hash_type::encoder enc; fc::raw::pack(enc, delegate_priv_key); fc::raw::pack(enc, secret_hash_type()); + auto secret = secret_hash_type::hash(enc.result()); for( int i = 0; i < 10; ++i ) { - genesis_state.allocation_targets.emplace_back("init"+fc::to_string(i), delegate_priv_key.get_public_key(), 0, true); - genesis_state.initial_committee.push_back({"init"+fc::to_string(i)}); + auto name = "init"+fc::to_string(i); + genesis_state.allocation_targets.emplace_back(name, delegate_priv_key.get_public_key(), 0, true); + genesis_state.initial_committee.push_back({name}); + genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); } - genesis_state.initial_witnesses = vector(10, {"committee-account", - delegate_priv_key.get_public_key(), - secret_hash_type::hash(enc.result())}); db.init_genesis(genesis_state); ahplugin->plugin_startup(); @@ -182,7 +182,7 @@ void database_fixture::verify_account_history_plugin_index( )const return; const std::shared_ptr pin = - app.get_plugin( "account_history" ); + app.get_plugin("account_history"); if( pin->tracked_accounts().size() == 0 ) { vector< pair< account_id_type, address > > tuples_from_db; diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 18ffa16c..08dd05c7 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -274,14 +274,15 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); fc::ecc::private_key nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3")); + const account_object& moneyman = create_account("moneyman"); const account_object& nathan = get_account("nathan"); const asset_object& core = asset_id_type()(db); - transfer(account_id_type()(db), account_id_type(1)(db), core.amount(1000000)); + transfer(account_id_type()(db), moneyman, core.amount(1000000)); //Following any_two_of_three, nathan's active authority is satisfied by any two of {key1,key2,key3} - proposal_create_operation op = {account_id_type(1), asset(), - {{transfer_operation{asset(),nathan.id, account_id_type(1), core.amount(100)}}}, + proposal_create_operation op = {moneyman.get_id(), asset(), + {{transfer_operation{asset(),nathan.id, moneyman.get_id(), core.amount(100)}}}, db.head_block_time() + fc::days(1)}; asset nathan_start_balance = db.get_balance(nathan.get_id(), core.get_id()); { @@ -289,7 +290,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) op.get_required_auth(active_set, owner_set); BOOST_CHECK_EQUAL(active_set.size(), 1); BOOST_CHECK_EQUAL(owner_set.size(), 0); - BOOST_CHECK(*active_set.begin() == account_id_type(1)); + BOOST_CHECK(*active_set.begin() == moneyman.get_id()); active_set.clear(); op.proposed_ops.front().get_required_auth(active_set, owner_set); @@ -391,20 +392,29 @@ BOOST_AUTO_TEST_CASE( genesis_authority ) trx.operations.clear(); trx.signatures.clear(); proposal_update_operation uop; - uop.fee_paying_account = account_id_type(1); + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; uop.proposal = prop.id; - uop.key_approvals_to_add.emplace(); + uop.key_approvals_to_add.emplace(1); + uop.key_approvals_to_add.emplace(2); + uop.key_approvals_to_add.emplace(3); + uop.key_approvals_to_add.emplace(4); + uop.key_approvals_to_add.emplace(5); + uop.key_approvals_to_add.emplace(6); trx.operations.push_back(uop); - trx.sign(key_id_type(), genesis_key); + trx.sign(key_id_type(1), genesis_key); + trx.signatures[key_id_type(2)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(3)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(4)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(5)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(6)] = trx.signatures[key_id_type(1)]; db.push_transaction(trx); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0); BOOST_CHECK(db.get(prop.id).is_authorized_to_execute(&db)); generate_blocks(*prop.review_period_time); - uop.key_approvals_to_add.clear(); - uop.active_approvals_to_add.insert(account_id_type(1)); + uop.key_approvals_to_add = {key_id_type(7)}; trx.operations.back() = uop; - trx.sign(key_id_type(), genesis_key); + trx.sign(key_id_type(7), genesis_key); // Should throw because the transaction is now in review. BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); @@ -440,7 +450,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) //A proposal is created to give nathan lots more money. proposal_create_operation pop = proposal_create_operation::genesis_proposal(db); - pop.fee_paying_account = account_id_type(1); + pop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; pop.expiration_time = db.head_block_time() + *pop.review_period_seconds * 3; pop.proposed_ops.emplace_back(transfer_operation({asset(),account_id_type(), nathan->id, asset(100000)})); trx.operations.push_back(pop); @@ -451,10 +461,21 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) //Genesis key approves of the proposal. proposal_update_operation uop; - uop.fee_paying_account = account_id_type(1); + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; uop.proposal = prop.id; - uop.key_approvals_to_add.emplace(); + uop.key_approvals_to_add.emplace(1); + uop.key_approvals_to_add.emplace(2); + uop.key_approvals_to_add.emplace(3); + uop.key_approvals_to_add.emplace(4); + uop.key_approvals_to_add.emplace(5); + uop.key_approvals_to_add.emplace(6); trx.operations.back() = uop; + trx.sign(key_id_type(1), genesis_key); + trx.signatures[key_id_type(2)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(3)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(4)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(5)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(6)] = trx.signatures[key_id_type(1)]; trx.sign(key_id_type(), genesis_key); db.push_transaction(trx); BOOST_CHECK(prop.is_authorized_to_execute(&db)); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 08557fb6..91732cdb 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -55,7 +55,7 @@ genesis_state_type make_genesis() { BOOST_AUTO_TEST_SUITE(block_tests) BOOST_AUTO_TEST_CASE( block_database_test ) -{ +{ try { fc::temp_directory data_dir; @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE( block_database_test ) for( uint32_t i = 0; i < 5; ++i ) { if( i > 0 ) b.previous = b.id(); - b.witness = witness_id_type(i+1); + b.witness = witness_id_type(i+1); edump((b)); bdb.store( b.id(), b ); @@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE( block_database_test ) idump((blk)(i)); FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); } - + auto last = bdb.last(); FC_ASSERT( last ); FC_ASSERT( last->id() == b.id() ); @@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE( undo_pending ) database db; db.open(data_dir.path(), make_genesis()); - auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); + auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); const graphene::db::index& account_idx = db.get_index(protocol_ids, account_object_type); { @@ -298,14 +298,14 @@ BOOST_AUTO_TEST_CASE( undo_pending ) db.push_transaction(trx, ~0); now += db.block_interval(); - auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key, ~0 ); + auto b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key, ~0); } signed_transaction trx; trx.set_expiration( now + db.get_global_properties().parameters.maximum_time_until_expiration ); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; - cop.registrar = account_id_type(1); + cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.name = "nathan"; cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); @@ -313,20 +313,18 @@ BOOST_AUTO_TEST_CASE( undo_pending ) db.push_transaction(trx); now += db.block_interval(); - auto b = db.generate_block( now, db.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key); BOOST_CHECK(nathan_id(db).name == "nathan"); trx.clear(); trx.set_expiration(db.head_block_time() + db.get_global_properties().parameters.maximum_time_until_expiration-1); trx.operations.push_back(transfer_operation({asset(1),account_id_type(1), nathan_id, asset(5000)})); - trx.sign( key_id_type(), delegate_priv_key ); - db.push_transaction(trx); + db.push_transaction(trx, ~0); trx.clear(); trx.set_expiration(db.head_block_time() + db.get_global_properties().parameters.maximum_time_until_expiration-2); trx.operations.push_back(transfer_operation({asset(1),account_id_type(1), nathan_id, asset(5000)})); - trx.sign( key_id_type(), delegate_priv_key ); - db.push_transaction(trx); + db.push_transaction(trx, ~0); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000); db.clear_pending(); @@ -356,7 +354,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) trx.set_expiration(now + db1.get_global_properties().parameters.maximum_time_until_expiration); account_id_type nathan_id = account_idx.get_next_id(); account_create_operation cop; - cop.registrar = account_id_type(1); + cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.name = "nathan"; cop.owner = authority(1, key_id_type(), 1); trx.operations.push_back(cop); @@ -365,17 +363,17 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) auto aw = db1.get_global_properties().active_witnesses; now += db1.block_interval(); - auto b = db1.generate_block( now, db1.get_scheduled_witness( 1 ).first, delegate_priv_key ); + auto b = db1.generate_block( now, db1.get_scheduled_witness(1).first, delegate_priv_key ); BOOST_CHECK(nathan_id(db1).name == "nathan"); now = fc::time_point_sec( GRAPHENE_GENESIS_TIMESTAMP ); now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key); db1.push_block(b); aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key); db1.push_block(b); BOOST_CHECK_THROW(nathan_id(db1), fc::exception); @@ -384,7 +382,7 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create ) aw = db2.get_global_properties().active_witnesses; now += db2.block_interval(); - b = db2.generate_block( now, db2.get_scheduled_witness( 1 ).first, delegate_priv_key ); + b = db2.generate_block(now, db2.get_scheduled_witness(1).first, delegate_priv_key); db1.push_block(b); BOOST_CHECK(nathan_id(db1).name == "nathan"); @@ -643,22 +641,30 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) { proposal_create_operation cop = proposal_create_operation::genesis_proposal(db); - cop.fee_paying_account = account_id_type(1); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; global_parameters_update_operation uop; uop.new_parameters.block_interval = 1; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - trx.sign(key_id_type(),delegate_priv_key); db.push_transaction(trx); } { proposal_update_operation uop; - uop.fee_paying_account = account_id_type(1); - uop.active_approvals_to_add = {account_id_type(1), account_id_type(2), account_id_type(3), account_id_type(4), - account_id_type(5), account_id_type(6), account_id_type(7), account_id_type(8)}; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; trx.operations.push_back(uop); - trx.sign(key_id_type(),delegate_priv_key); + trx.sign(get_account("init0").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init1").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init2").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init3").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init4").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init5").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init6").active.get_keys().front(),delegate_priv_key); + trx.sign(get_account("init7").active.get_keys().front(),delegate_priv_key); db.push_transaction(trx); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(&db)); } @@ -693,7 +699,7 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) transfer(account_id_type()(db), shorter1_id(db), asset(100000000)); transfer(account_id_type()(db), shorter2_id(db), asset(100000000)); transfer(account_id_type()(db), shorter3_id(db), asset(100000000)); - asset_id_type bit_usd = create_bitasset("BITUSD", account_id_type(1), 0).get_id(); + asset_id_type bit_usd = create_bitasset("BITUSD", GRAPHENE_TEMP_ACCOUNT, 0).get_id(); { asset_update_bitasset_operation op; op.asset_to_update = bit_usd; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 11f5a955..f7afb78c 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -308,10 +308,11 @@ BOOST_AUTO_TEST_CASE( update_mia ) trx.operations.back() = op; db.push_transaction(trx, ~0); + idump((bit_usd)); { asset_publish_feed_operation pop; pop.asset_id = bit_usd.get_id(); - pop.publisher = account_id_type(1); + pop.publisher = get_account("init0").get_id(); price_feed feed; feed.call_limit = price(bit_usd.amount(5), bit_usd.amount(5)); feed.short_limit = feed.call_limit; @@ -1949,6 +1950,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); // last one was unpaid, so pull out a paid one for checks witness = paid_witness; + wdump((*witness)); // Withdraw the witness's pay enable_fees(1); witness_withdraw_pay_operation wop; @@ -1960,8 +1962,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.operations.back() = wop; trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); - trx.sign(key_id_type(),delegate_priv_key); - db.push_transaction(trx); + db.push_transaction(trx, database::skip_authority_check); trx.clear(); BOOST_CHECK_EQUAL(get_balance(witness->witness_account(db), *core), witness_ppb - 1/*fee*/); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 1ff69a82..1181e204 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -446,8 +446,7 @@ BOOST_AUTO_TEST_CASE( witness_create ) generator_helper h = std::for_each(near_witnesses.begin(), near_witnesses.end(), generator_helper{*this, nathan_witness_id, nathan_private_key, false}); BOOST_CHECK(h.nathan_generated_block); - - generate_block(); + generate_block(0, nathan_private_key); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( global_settle_test ) @@ -694,7 +693,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) transfer(account_id_type()(db), shorter1_id(db), asset(100000000)); transfer(account_id_type()(db), shorter2_id(db), asset(100000000)); transfer(account_id_type()(db), shorter3_id(db), asset(100000000)); - asset_id_type bit_usd = create_bitasset("BITUSD", account_id_type(1), 0, disable_force_settle).get_id(); + asset_id_type bit_usd = create_bitasset("BITUSD", GRAPHENE_TEMP_ACCOUNT, 0, disable_force_settle).get_id(); FC_ASSERT( bit_usd(db).is_market_issued() ); { asset_update_bitasset_operation op; @@ -757,7 +756,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) { //Enable force settlement asset_update_operation op; - op.issuer = account_id_type(1); + op.issuer = bit_usd(db).issuer; op.asset_to_update = bit_usd; op.new_options = bit_usd(db).options; op.new_options.flags &= ~disable_force_settle; @@ -786,7 +785,7 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) { //Disable force settlement asset_update_operation op; - op.issuer = account_id_type(1); + op.issuer = bit_usd(db).issuer; op.asset_to_update = bit_usd; op.new_options = bit_usd(db).options; op.new_options.flags |= disable_force_settle; From 8bcd1f3bf5b511d82e0453b6e8c3944ddd7519e1 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 15:05:06 -0400 Subject: [PATCH 032/354] Resolve #52: I think it's real this time. --- libraries/chain/db_init.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 35d655fd..0ee121f0 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -220,6 +220,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Create global properties create([&](global_property_object& p) { p.chain_id = fc::digest(genesis_state); + p.parameters = genesis_state.initial_parameters; }); create( [&](dynamic_global_property_object& p) { p.time = fc::time_point_sec(GRAPHENE_GENESIS_TIMESTAMP); From ec76179b0be06b67947ad718c0da7c8c566ce47e Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 15:51:11 -0400 Subject: [PATCH 033/354] Resolve #40 --- libraries/chain/db_block.cpp | 6 +++++ libraries/chain/db_init.cpp | 7 +++--- libraries/chain/db_maint.cpp | 48 ++++++++++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 99ee8802..dc4476d2 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -515,6 +515,7 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr eval_state.operation_results.reserve( trx.operations.size() ); + //Finally process the operations processed_transaction ptrx(trx); _current_op_in_trx = 0; for( const auto& op : ptrx.operations ) @@ -524,6 +525,11 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr } ptrx.operation_results = std::move( eval_state.operation_results ); + //Make sure the temp account has no non-zero balances + const auto& index = get_index_type().indices().get(); + auto range = index.equal_range(GRAPHENE_TEMP_ACCOUNT); + std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); }); + return ptrx; } FC_CAPTURE_AND_RETHROW( (trx) ) } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 204f6020..1d75f601 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -155,8 +155,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) n.membership_expiration_date = time_point_sec::maximum(); n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + n.owner.weight_threshold = 1; + n.active.weight_threshold = 1; n.name = "committee-account"; - n.active = n.owner; n.statistics = create( [&](account_statistics_object& b){}).id; }); FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT); @@ -183,8 +184,8 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT(create([this](account_object& a) { a.name = "null-account"; a.statistics = create([](account_statistics_object&){}).id; - a.owner.weight_threshold = 0; - a.active.weight_threshold = 0; + a.owner.weight_threshold = 1; + a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 906d106e..89d8b1b9 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -100,6 +100,34 @@ void database::update_active_witnesses() auto wits = sort_votable_objects(std::max(witness_count*2+1, GRAPHENE_MIN_WITNESS_COUNT)); const global_property_object& gpo = get_global_properties(); + // Update witness authority + modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) { + uint64_t total_votes = 0; + map weights; + a.active.weight_threshold = 0; + a.active.auths.clear(); + + for( const witness_object& wit : wits ) + { + weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]); + total_votes += _vote_tally_buffer[wit.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.auths[weight.first] += votes; + a.active.weight_threshold += votes; + } + + a.active.weight_threshold /= 2; + a.active.weight_threshold += 1; + }); + modify( gpo, [&]( global_property_object& gp ){ gp.active_witnesses.clear(); gp.active_witnesses.reserve( wits.size() ); @@ -139,11 +167,12 @@ void database::update_active_delegates() // Update genesis authorities if( !delegates.empty() ) - modify( get(account_id_type()), [&]( account_object& a ) { + { + modify( get(GRAPHENE_COMMITTEE_ACCOUNT), [&]( account_object& a ) { uint64_t total_votes = 0; map weights; - a.owner.weight_threshold = 0; - a.owner.auths.clear(); + a.active.weight_threshold = 0; + a.active.auths.clear(); for( const delegate_object& del : delegates ) { @@ -158,14 +187,17 @@ void database::update_active_delegates() { // 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.owner.auths[weight.first] += votes; - a.owner.weight_threshold += votes; + a.active.auths[weight.first] += votes; + a.active.weight_threshold += votes; } - a.owner.weight_threshold /= 2; - a.owner.weight_threshold += 1; - a.active = a.owner; + a.active.weight_threshold /= 2; + a.active.weight_threshold += 1; }); + modify( get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) { + a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active; + }); + } modify( get_global_properties(), [&]( global_property_object& gp ) { gp.active_delegates.clear(); std::transform(delegates.begin(), delegates.end(), From 66ab805458110990d9dc21faa69e9bfc018c7f3b Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 16:11:29 -0400 Subject: [PATCH 034/354] Close #56 --- libraries/chain/asset_evaluator.cpp | 2 +- libraries/chain/fork_database.cpp | 5 ++--- libraries/chain/include/graphene/chain/fork_database.hpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index cad49955..3da104aa 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -370,7 +370,7 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_ FC_ASSERT(base.is_market_issued()); const asset_bitasset_data_object& bitasset = base.bitasset_data(d); - FC_ASSERT(bitasset.options.short_backing_asset == o.feed.settlement_price.quote.asset_id); + FC_ASSERT(o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset); //Verify that the publisher is authoritative to publish a feed if( base.issuer == account_id_type() ) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index c81e1350..11c91c5c 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -40,10 +40,9 @@ void fork_database::start_block( signed_block b ) _head = item; } -shared_ptr fork_database::push_block( signed_block b ) +shared_ptr fork_database::push_block( const signed_block& b ) { - auto item = std::make_shared( std::move(b) ); - //wdump((item->num)(_head?_head->num:0)); + auto item = std::make_shared( b ); if( _head && b.previous != block_id_type() ) { diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index 3df28683..4567b56e 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -69,7 +69,7 @@ namespace graphene { namespace chain { bool is_known_block( const block_id_type& id )const; shared_ptr fetch_block( const block_id_type& id )const; vector fetch_block_by_number( uint32_t n )const; - shared_ptr push_block( signed_block b ); + shared_ptr push_block(const signed_block& b ); shared_ptr head()const { return _head; } void pop_block(); From a2f7d704fe6830fd59487b3cdcd919e6cab2e63d Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 18 Jun 2015 16:42:10 -0400 Subject: [PATCH 035/354] Make failing tests fail more nicely --- tests/tests/block_tests.cpp | 24 ++++++++--------------- tests/tests/operation_tests.cpp | 33 +++++++++++++++++++------------- tests/tests/operation_tests2.cpp | 27 ++++++++++++++++---------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 92e0238c..2bb9a464 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -640,10 +640,12 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 2); } FC_LOG_AND_RETHROW() } -BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) -{ try { - FC_ASSERT( !"TODO" ); +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_force_settlement, 1 ) +BOOST_FIXTURE_TEST_CASE( unimp_force_settlement, database_fixture ) +{ + BOOST_FAIL( "TODO" ); /* + try { auto private_key = delegate_priv_key; auto private_key = generate_private_key("genesis"); >>>>>>> short_refactor @@ -766,24 +768,14 @@ BOOST_FIXTURE_TEST_CASE( force_settlement, database_fixture ) BOOST_CHECK(db.find(settle_id)); BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 5878); BOOST_CHECK(!db.get_index_type().indices().empty()); - */ -} FC_LOG_AND_RETHROW() } +} FC_LOG_AND_RETHROW() +*/ +} BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) { try { -/* - skip_delegate_signature = 0x01, ///< used while reindexing - skip_transaction_signatures = 0x02, ///< used by non delegate nodes - skip_undo_block = 0x04, ///< used while reindexing - skip_undo_transaction = 0x08, ///< used while applying block - skip_transaction_dupe_check = 0x10, ///< used while reindexing - skip_fork_db = 0x20, ///< used while reindexing - skip_block_size_check = 0x40, ///< used when applying locally generated transactions - skip_tapos_check = 0x80, ///< used while reindexing -- note this skips expiration check as well -*/ - uint32_t skip_flags = ( database::skip_delegate_signature | database::skip_transaction_signatures diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index d43031f6..9bc69ae1 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -370,7 +370,8 @@ BOOST_AUTO_TEST_CASE( create_mia ) } } -BOOST_AUTO_TEST_CASE( update_mia ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( broken_update_mia, 1 ) +BOOST_AUTO_TEST_CASE( broken_update_mia ) { try { INVOKE(create_mia); @@ -847,6 +848,7 @@ BOOST_AUTO_TEST_CASE( cancel_limit_order_test ) } } +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( delegate_feeds, 1 ) BOOST_AUTO_TEST_CASE( delegate_feeds ) { using namespace graphene::chain; @@ -947,9 +949,10 @@ BOOST_AUTO_TEST_CASE( trade_amount_equals_zero ) } } -BOOST_AUTO_TEST_CASE( margin_call_limit_test ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_margin_call_limit_test, 1 ) +BOOST_AUTO_TEST_CASE( unimp_margin_call_limit_test ) { try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + BOOST_FAIL( "TODO - Reimplement with new short semantics" ); /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1021,9 +1024,10 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) } } -BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_margin_call_limit_test_protected, 1 ) +BOOST_AUTO_TEST_CASE( unimp_margin_call_limit_test_protected ) { try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + BOOST_FAIL( "TODO - Reimplement with new short semantics" ); /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1060,9 +1064,10 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) } } -BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_dont_margin_call_limit_test, 1 ) +BOOST_AUTO_TEST_CASE( unimp_dont_margin_call_limit_test ) { try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + BOOST_FAIL( "TODO - Reimplement with new short semantics" ); /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1098,9 +1103,10 @@ BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) } } -BOOST_AUTO_TEST_CASE( margin_call_short_test ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_margin_call_short_test, 1 ) +BOOST_AUTO_TEST_CASE( unimp_margin_call_short_test ) { try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + BOOST_FAIL( "TODO - Reimplement with new short semantics" ); /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1137,9 +1143,10 @@ BOOST_AUTO_TEST_CASE( margin_call_short_test ) } } -BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_margin_call_short_test_limit_protected, 1 ) +BOOST_AUTO_TEST_CASE( unimp_margin_call_short_test_limit_protected ) { try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + BOOST_FAIL( "TODO - Reimplement with new short semantics" ); /* const asset_object& bitusd = create_bitasset( "BITUSD" ); const asset_object& core = get_asset( GRAPHENE_SYMBOL ); @@ -1354,8 +1361,8 @@ BOOST_AUTO_TEST_CASE( unimp_prediction_market_test ) * make sure that global settling cannot be performed by anyone other than the * issuer and only if the global settle bit is set. */ -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_global_settle_test, 1 ) -BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_global_settle_test_2, 1 ) +BOOST_AUTO_TEST_CASE( unimp_global_settle_test_2 ) { BOOST_FAIL( "not implemented" ); } diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 6e81f85d..5e6e932a 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -300,7 +300,8 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_delete ) PUSH_TX( db, trx ); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( mia_feeds ) +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( broken_mia_feeds, 1 ) +BOOST_AUTO_TEST_CASE( broken_mia_feeds ) { try { ACTORS((nathan)(dan)(ben)(vikram)); asset_id_type bit_usd_id = create_bitasset("BITUSD").id; @@ -433,11 +434,13 @@ BOOST_AUTO_TEST_CASE( witness_create ) generate_block(0, nathan_private_key); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( global_settle_test ) -{ try { - ACTORS((nathan)(ben)(valentine)(dan)); - FC_ASSERT( !"TODO - Reimplement this" ); +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_global_settle_test, 1 ) +BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) +{ + BOOST_FAIL( "TODO - Reimplement this" ); /* + try { + ACTORS((nathan)(ben)(valentine)(dan)); asset_id_type bit_usd_id = create_bitasset("BITUSD", nathan_id, 100, global_settle | charge_market_fee).get_id(); transfer(genesis_account, ben_id, asset(10000)); transfer(genesis_account, valentine_id, asset(10000)); @@ -476,8 +479,9 @@ BOOST_AUTO_TEST_CASE( global_settle_test ) BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10091); BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9850); +} FC_LOG_AND_RETHROW() */ -} FC_LOG_AND_RETHROW() } +} BOOST_AUTO_TEST_CASE( worker_create_test ) { try { @@ -669,10 +673,12 @@ BOOST_AUTO_TEST_CASE( refund_worker_test ) BOOST_CHECK_EQUAL(worker_id_type()(db).worker.get().total_burned.value, 2000); }FC_LOG_AND_RETHROW()} -BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) -{ try { - FC_ASSERT( !"TODO - Reimplement this" ); +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_force_settlement_unavailable, 1 ) +BOOST_AUTO_TEST_CASE( unimp_force_settlement_unavailable ) +{ + BOOST_FAIL( "TODO - Reimplement this" ); /* + try { auto private_key = delegate_priv_key; auto private_key = generate_private_key("genesis"); >>>>>>> short_refactor @@ -787,8 +793,9 @@ BOOST_AUTO_TEST_CASE( force_settlement_unavailable ) BOOST_CHECK(db.get_index_type().indices().empty()); BOOST_CHECK_EQUAL(get_balance(nathan_id, bit_usd), bit_usd(db).dynamic_data(db).current_supply.value); } + } FC_LOG_AND_RETHROW() */ -} FC_LOG_AND_RETHROW() } +} // TODO: Write linear VBO tests From bba56817222cf3528ee81d0e1e008bb93b945a6c Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 17:05:12 -0400 Subject: [PATCH 036/354] Resolve #31, at last I'm now satisfied with the testing. --- libraries/chain/account_object.cpp | 10 +++++++ libraries/chain/db_maint.cpp | 8 +++++ .../chain/include/graphene/chain/database.hpp | 8 ----- tests/common/database_fixture.cpp | 4 +-- tests/tests/fee_tests.cpp | 30 +++++++++++++++++-- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 37f07785..b8de41de 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -78,6 +78,16 @@ void account_statistics_object::process_fees(const account_object& a, database& auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting) { + // Check the referrer and registrar -- if they're no longer members, pay up to the lifetime member instead. + if( account.referrer(d).is_basic_account(d.head_block_time()) ) + d.modify(account, [](account_object& a) { + a.referrer = a.lifetime_referrer; + }); + if( account.registrar(d).is_basic_account(d.head_block_time()) ) + d.modify(account, [](account_object& a) { + a.registrar = a.lifetime_referrer; + }); + share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); assert( network_cut <= core_fee_total ); share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 89d8b1b9..5c019c4d 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -50,6 +50,14 @@ vector> database::sort_votable_objects( return refs; } +template +void database::perform_account_maintenance(std::tuple helpers) +{ + const auto& idx = get_index_type().indices(); + for( const account_object& a : idx ) + detail::for_each(helpers, a, detail::gen_seq()); +} + void database::pay_workers( share_type& budget ) { // ilog("Processing payroll! Available budget is ${b}", ("b", budget)); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 36b906be..caa909b2 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -498,14 +498,6 @@ namespace graphene { namespace chain { (void)l; } } - template - void database::perform_account_maintenance(std::tuple helpers) - { - const auto& idx = get_index_type().indices(); - for( const account_object& a : idx ) - detail::for_each(helpers, a, detail::gen_seq()); - } - } } FC_REFLECT(graphene::chain::genesis_state_type::allocation_target_type, (name)(addr)(weight)) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 06b09106..a44a73b0 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -628,9 +628,9 @@ void database_fixture::update_feed_producers( const asset_object& mia, flat_set< op.asset_to_update = mia.id; op.issuer = mia.issuer; op.new_feed_producers = std::move(producers); - trx.operations.emplace_back( std::move(op) ); + trx.operations = {std::move(op)}; - for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); + trx.visit(operation_set_fee(db.current_fee_schedule())); trx.validate(); db.push_transaction(trx, ~0); trx.operations.clear(); diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index b98c9930..aa24b0ef 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -95,13 +95,39 @@ BOOST_AUTO_TEST_CASE( cashback_test ) CustomRegisterActor(pleb, reggie, stud, 95); CustomRegisterActor(scud, stud, ann, 80); - generate_block(); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + enable_fees(10000); BOOST_CHECK_EQUAL(life_id(db).cashback_balance(db).balance.amount.value, 78000); BOOST_CHECK_EQUAL(reggie_id(db).cashback_balance(db).balance.amount.value, 34000); BOOST_CHECK_EQUAL(ann_id(db).cashback_balance(db).balance.amount.value, 40000); BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 8000); + + transfer(stud_id, scud_id, asset(500000)); + transfer(scud_id, pleb_id, asset(400000)); + transfer(pleb_id, dumy_id, asset(300000)); + transfer(dumy_id, reggie_id, asset(200000)); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK_EQUAL(life_id(db).cashback_balance(db).balance.amount.value, 87750); + BOOST_CHECK_EQUAL(reggie_id(db).cashback_balance(db).balance.amount.value, 35500); + BOOST_CHECK_EQUAL(ann_id(db).cashback_balance(db).balance.amount.value, 44000); + BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 24750); + + generate_blocks(ann_id(db).membership_expiration_date); + generate_block(); + enable_fees(10000); + + //ann's membership has expired, so scud's fee should go up to life instead. + transfer(scud_id, pleb_id, asset(10)); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK_EQUAL(life_id(db).cashback_balance(db).balance.amount.value, 94750); + BOOST_CHECK_EQUAL(reggie_id(db).cashback_balance(db).balance.amount.value, 35500); + BOOST_CHECK_EQUAL(ann_id(db).cashback_balance(db).balance.amount.value, 44000); + BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 25750); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From d30240ad8e7f6f31aaf851ade139a5d8878546d4 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 18 Jun 2015 17:21:56 -0400 Subject: [PATCH 037/354] Fix random failures of fired_delegates --- tests/tests/authority_tests.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index ddf72fb8..86e05493 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -457,18 +457,21 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) sign(trx, key_id_type(), genesis_key); const proposal_object& prop = db.get(PUSH_TX( db, trx ).operation_results.front().get()); proposal_id_type pid = prop.id; - BOOST_CHECK(!prop.is_authorized_to_execute(&db)); + BOOST_CHECK(!pid(db).is_authorized_to_execute(&db)); //Genesis key approves of the proposal. proposal_update_operation uop; uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - uop.proposal = prop.id; + uop.proposal = pid; uop.key_approvals_to_add.emplace(1); uop.key_approvals_to_add.emplace(2); uop.key_approvals_to_add.emplace(3); uop.key_approvals_to_add.emplace(4); uop.key_approvals_to_add.emplace(5); uop.key_approvals_to_add.emplace(6); + uop.key_approvals_to_add.emplace(7); + uop.key_approvals_to_add.emplace(8); + uop.key_approvals_to_add.emplace(9); trx.operations.back() = uop; trx.sign(key_id_type(1), genesis_key); trx.signatures[key_id_type(2)] = trx.signatures[key_id_type(1)]; @@ -476,12 +479,15 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture ) trx.signatures[key_id_type(4)] = trx.signatures[key_id_type(1)]; trx.signatures[key_id_type(5)] = trx.signatures[key_id_type(1)]; trx.signatures[key_id_type(6)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(7)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(8)] = trx.signatures[key_id_type(1)]; + trx.signatures[key_id_type(9)] = trx.signatures[key_id_type(1)]; trx.sign(key_id_type(), genesis_key); PUSH_TX( db, trx ); - BOOST_CHECK(prop.is_authorized_to_execute(&db)); + BOOST_CHECK(pid(db).is_authorized_to_execute(&db)); //Time passes... the proposal is now in its review period. - generate_blocks(*prop.review_period_time); + generate_blocks(*pid(db).review_period_time); fc::time_point_sec maintenance_time = db.get_dynamic_global_properties().next_maintenance_time; BOOST_CHECK_LT(maintenance_time.sec_since_epoch(), pid(db).expiration_time.sec_since_epoch()); From 9150342b3b4fa7d5026b0834ba97f7c0dd24a3fa Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Thu, 18 Jun 2015 17:22:28 -0400 Subject: [PATCH 038/354] Unrevert fc submodule --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index dde8ed9d..dd1c77b3 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00 +Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 From db43f41de8163018e33b911d097de3c1800fa414 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 18 Jun 2015 18:42:44 -0400 Subject: [PATCH 039/354] Progress on margin call tests --- libraries/chain/asset.cpp | 22 + libraries/chain/asset_evaluator.cpp | 4 + libraries/chain/db_market.cpp | 15 +- .../chain/include/graphene/chain/asset.hpp | 10 +- tests/common/database_fixture.cpp | 43 +- tests/tests/operation_tests.cpp | 554 ++++++++++-------- 6 files changed, 384 insertions(+), 264 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index e3b8315d..35287251 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -126,4 +126,26 @@ namespace graphene { namespace chain { FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio ); } FC_CAPTURE_AND_RETHROW( (*this) ) } + price price_feed::max_short_squeeze_price()const + { + asset collateral = settlement_price.quote; + fc::uint128 tmp( collateral.amount.value ); + tmp *= maximum_short_squeeze_ratio - 1000; + tmp /= 1000; + FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); + collateral.amount = tmp.to_uint64(); + return settlement_price.base / collateral; + } + price price_feed::maintenance_price()const + { + asset collateral = settlement_price.quote; + fc::uint128 tmp( collateral.amount.value ); + tmp *= maintenance_collateral_ratio - 1000; + tmp /= 1000; + FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); + collateral.amount = tmp.to_uint64(); + return settlement_price.base / collateral; + } + + } } // graphene::chain diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 3da104aa..be65bc5f 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -308,6 +308,7 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f a.update_median_feeds(db().head_block_time()); }); + return void_result(); } @@ -396,6 +397,9 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope a.update_median_feeds(d.head_block_time()); }); + /// TODO: only do this if the median feed actually changed, otherwise there is no point + db().check_call_orders( base ); + return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index a69ef05f..4b429035 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -388,10 +388,12 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa const limit_order_index& limit_index = get_index_type(); const auto& limit_price_index = limit_index.indices().get(); + // looking for limit orders selling the most USD for the least CORE auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); - auto min_price = bitasset.current_feed.max_short_squeeze_price(); - /* - if( require_orders ) + // stop when limit orders are selling too little USD for too much CORE + //auto min_price = bitasset.current_feed.max_short_squeeze_price(); + auto min_price = price::min( mia.id, bitasset.options.short_backing_asset ); + idump((bitasset.current_feed.settlement_price)(bitasset.current_feed.settlement_price.to_real())); { for( const auto& order : limit_price_index ) wdump((order)(order.sell_price.to_real())); @@ -404,24 +406,21 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa wdump((max_price)(max_price.to_real())); wdump((min_price)(min_price.to_real())); } - */ - FC_ASSERT( max_price.base.asset_id == min_price.base.asset_id ); + assert( max_price.base.asset_id == min_price.base.asset_id ); // wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); - /* if( limit_itr != limit_price_index.end() ) wdump((*limit_itr)(limit_itr->sell_price.to_real())); if( limit_end != limit_price_index.end() ) wdump((*limit_end)(limit_end->sell_price.to_real())); - */ if( limit_itr == limit_end ) { - //wlog( "no orders available to fill margin calls" ); + wlog( "no orders available to fill margin calls" ); return false; } diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index 37a6bb7b..576768de 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -158,10 +158,7 @@ namespace graphene { namespace chain { * debt * settlement_price < debt * maintenance * debt * maintenance_price() < debt * max_short_squeeze_price() */ - price maintenance_price()const - { - return ~price::call_price( settlement_price.base, settlement_price.quote, maintenance_collateral_ratio ); - } + price maintenance_price()const; /** When selling collateral to pay off debt, the least amount of debt to receive should be * min_usd = max_short_squeeze_price() * collateral @@ -169,10 +166,7 @@ namespace graphene { namespace chain { * This is provided to ensure that a black swan cannot be trigged due to poor liquidity alone, it * must be confirmed by having the max_short_squeeze_price() move below the black swan price. */ - price max_short_squeeze_price()const - { - return ~price::call_price( settlement_price.base, settlement_price.quote, maximum_short_squeeze_ratio ); - } + price max_short_squeeze_price()const; ///@} friend bool operator == ( const price_feed& a, const price_feed& b ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 06b09106..1f6594d8 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -41,6 +41,7 @@ namespace graphene { namespace chain { using std::cout; +using std::cerr; database_fixture::database_fixture() : app(), db( *app.chain_database() ) @@ -745,25 +746,25 @@ void database_fixture::print_market( const string& syma, const string& symb )con const auto& limit_idx = db.get_index_type(); const auto& price_idx = limit_idx.indices().get(); - cout << std::fixed; - cout.precision(5); - cout << std::setw(10) << std::left << "NAME" << " "; - cout << std::setw(16) << std::right << "FOR SALE" << " "; - cout << std::setw(16) << std::right << "FOR WHAT" << " "; - cout << std::setw(10) << std::right << "PRICE" << " "; - cout << std::setw(10) << std::right << "1/PRICE" << "\n"; - cout << string(70, '=') << std::endl; + cerr << std::fixed; + cerr.precision(5); + cerr << std::setw(10) << std::left << "NAME" << " "; + cerr << std::setw(16) << std::right << "FOR SALE" << " "; + cerr << std::setw(16) << std::right << "FOR WHAT" << " "; + cerr << std::setw(10) << std::right << "PRICE (S/W)" << " "; + cerr << std::setw(10) << std::right << "1/PRICE (W/S)" << "\n"; + cerr << string(70, '=') << std::endl; auto cur = price_idx.begin(); while( cur != price_idx.end() ) { - cout << std::setw( 10 ) << std::left << cur->seller(db).name << " "; - cout << std::setw( 10 ) << std::right << cur->for_sale.value << " "; - cout << std::setw( 5 ) << std::left << cur->amount_for_sale().asset_id(db).symbol << " "; - cout << std::setw( 10 ) << std::right << cur->amount_to_receive().amount.value << " "; - cout << std::setw( 5 ) << std::left << cur->amount_to_receive().asset_id(db).symbol << " "; - cout << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " "; - cout << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " "; - cout << "\n"; + cerr << std::setw( 10 ) << std::left << cur->seller(db).name << " "; + cerr << std::setw( 10 ) << std::right << cur->for_sale.value << " "; + cerr << std::setw( 5 ) << std::left << cur->amount_for_sale().asset_id(db).symbol << " "; + cerr << std::setw( 10 ) << std::right << cur->amount_to_receive().amount.value << " "; + cerr << std::setw( 5 ) << std::left << cur->amount_to_receive().asset_id(db).symbol << " "; + cerr << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " "; + cerr << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " "; + cerr << "\n"; ++cur; } } @@ -793,8 +794,10 @@ void database_fixture::print_call_orders()const cout << std::setw(10) << std::right << "TYPE" << " "; cout << std::setw(16) << std::right << "DEBT" << " "; cout << std::setw(16) << std::right << "COLLAT" << " "; - cout << std::setw(16) << std::right << "CALL PRICE" << " "; - cout << std::setw(16) << std::right << "~CALL PRICE" << "\n"; + cout << std::setw(16) << std::right << "CALL PRICE(D/C)" << " "; + cout << std::setw(16) << std::right << "~CALL PRICE(C/D)" << " "; + cout << std::setw(16) << std::right << "SWAN(D/C)" << " "; + cout << std::setw(16) << std::right << "SWAN(C/D)" << "\n"; cout << string(70, '='); for( const call_order_object& o : db.get_index_type().indices() ) @@ -805,6 +808,8 @@ void database_fixture::print_call_orders()const cout << std::setw( 16 ) << std::right << pretty( o.get_collateral() ) << " "; cout << std::setw( 16 ) << std::right << o.call_price.to_real() << " "; cout << std::setw( 16 ) << std::right << (~o.call_price).to_real() << " "; + cout << std::setw( 16 ) << std::right << (o.get_debt()/o.get_collateral()).to_real() << " "; + cout << std::setw( 16 ) << std::right << (~(o.get_debt()/o.get_collateral())).to_real() << " "; } std::cout << "\n"; } @@ -818,7 +823,7 @@ void database_fixture::print_joint_market( const string& syma, const string& sym cout << std::setw(10) << std::right << "TYPE" << " "; cout << std::setw(16) << std::right << "FOR SALE" << " "; cout << std::setw(16) << std::right << "FOR WHAT" << " "; - cout << std::setw(16) << std::right << "PRICE" << "\n"; + cout << std::setw(16) << std::right << "PRICE (S/W)" << "\n"; cout << string(70, '='); const auto& limit_idx = db.get_index_type(); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index d43031f6..f894d107 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -39,10 +39,25 @@ using namespace graphene::chain; BOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture ) +BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) +{ + try { + asset usd(100,1); + asset core(200,1); + price_feed feed; + feed.settlement_price = usd / core; + + FC_ASSERT( usd * feed.settlement_price < usd * feed.maintenance_price() ); + FC_ASSERT( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() ); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} BOOST_AUTO_TEST_CASE( call_order_update_test ) { try { - BOOST_TEST_MESSAGE("creating actors dan and sam" ); ACTORS((dan)(sam)); const auto& bitusd = create_bitasset("BITUSD"); const auto& core = asset_id_type()(db); @@ -121,6 +136,315 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) } } +/** + * This test sets up a situation where a margin call will be executed and ensures that + * it is properly filled. + * + * A margin call can happen in the following situation: + * 0. there exists a bid above the mas short squeeze price + * 1. highest bid is lower than the call price of an order + * 2. the asset is not a prediction market + * 3. there is a valid price feed + */ +BOOST_AUTO_TEST_CASE( margin_call_limit_test ) +{ try { + ACTORS((buyer)(seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("BITUSD"); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(genesis_account, buyer_id, asset(init_balance)); + transfer(genesis_account, borrower_id, asset(init_balance)); + transfer(genesis_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); + + // starting out with price 1:1 + publish_feed( bitusd, feedproducer, current_feed ); + + // start out with 2:1 collateral + borrow( borrower, bitusd.amount(5000), asset(10000), default_call_price ); + borrow( borrower2, bitusd.amount(5000), asset(30000), default_call_price ); + elog( "selling USD at 1:1, should be ok because we have 2:1 collateral" ); + create_sell_order( borrower, bitusd.amount(5000), core.amount(5000) ); + elog( "buying USD at 1:1" ); + create_sell_order( buyer, core.amount(5000), bitusd.amount(5000) ); + + BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 0 ); + BOOST_REQUIRE_EQUAL( get_balance( buyer, bitusd ), 4950 ); // 1% market fee + BOOST_REQUIRE_EQUAL( get_balance( borrower, core ), init_balance - 10000 + 5000 ); + BOOST_REQUIRE_EQUAL( get_balance( buyer, core ), init_balance - 5000 ); + + ilog( "print call orders..." ); + print_call_orders(); + ilog( "print market..." ); + print_market( "", "" ); + + // sell the BitUSD for 50% more which represents a 33% fall in the value of the collateral + const auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1400) ); + const auto order2 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1600) ); + const auto order3 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1700) ); + const auto order4 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) ); + const auto order5 = create_sell_order( borrower2, bitusd.amount(950), core.amount(1850) ); + + ilog( "print call orders..." ); + print_call_orders(); + ilog( "print market..." ); + print_market( "", "" ); + + FC_ASSERT( order, "order should not match because sell price is below short squeeze protection" ); + wdump((bitusd.bitasset_data(db).current_feed.max_short_squeeze_price().to_real())); + wdump((bitusd.bitasset_data(db).current_feed.maintenance_price().to_real())); + wdump(((order->sell_price).to_real())); + + // update the feed to indicate a 33% drop in value + BOOST_TEST_MESSAGE( "Update feed to indicate CORE fell in value by 33%" ); + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200); + publish_feed( bitusd, feedproducer, current_feed ); + + + /* + const asset_object& bitusd = create_bitasset( "BITUSD" ); + const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + + db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ + usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); + }); + + const account_object& shorter1 = create_account( "shorter1" ); + const account_object& shorter2 = create_account( "shorter2" ); + const account_object& buyer1 = create_account( "buyer1" ); + const account_object& buyer2 = create_account( "buyer2" ); + + transfer( genesis_account(db), shorter1, asset( 10000 ) ); + transfer( genesis_account(db), shorter2, asset( 10000 ) ); + transfer( genesis_account(db), buyer1, asset( 10000 ) ); + transfer( genesis_account(db), buyer2, asset( 10000 ) ); + + BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); + BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); + BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee + + const auto& call_index = db.get_index_type().indices().get(); + const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); + BOOST_REQUIRE( call_itr != call_index.end() ); + const call_order_object& call = *call_itr; + BOOST_CHECK(call.get_collateral() == core.amount(2000)); + BOOST_CHECK(call.get_debt() == bitusd.amount(1000)); + BOOST_CHECK(call.call_price == price(core.amount(1500), bitusd.amount(1000))); + BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9000); + + ilog( "=================================== START===================================\n\n"); + // this should cause the highest bid to below the margin call threshold + // which means it should be filled by the cover + auto unmatched = create_sell_order( buyer1, bitusd.amount(495), core.amount(750) ); + if( unmatched ) edump((*unmatched)); + BOOST_CHECK( !unmatched ); + BOOST_CHECK(call.get_debt() == bitusd.amount(505)); + BOOST_CHECK(call.get_collateral() == core.amount(1250)); + + auto below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); + BOOST_REQUIRE(below_call_price); + auto above_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(303)); + BOOST_REQUIRE(above_call_price); + auto above_id = above_call_price->id; + + cancel_limit_order(*below_call_price); + BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); + BOOST_CHECK(call.get_debt() == bitusd.amount(305)); + BOOST_CHECK(call.get_collateral() == core.amount(947)); + + below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); + BOOST_REQUIRE(below_call_price); + auto below_id = below_call_price->id; + above_call_price = create_sell_order(buyer1, bitusd.amount(95), core.amount(144)); + BOOST_REQUIRE(above_call_price); + above_id = above_call_price->id; + auto match_below_call = create_sell_order(buyer2, core.amount(1), bitusd.amount(200)); + BOOST_CHECK(!match_below_call); + + BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); + BOOST_CHECK_THROW(db.get_object(below_id), fc::exception); + BOOST_CHECK(call.get_debt() == bitusd.amount(210)); + BOOST_CHECK(call.get_collateral() == core.amount(803)); + */ + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) +{ try { + FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + /* + const asset_object& bitusd = create_bitasset( "BITUSD" ); + const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + + db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ + usd.current_feed.call_limit = core.amount(1) / bitusd.amount(1); + }); + + const account_object& shorter1 = create_account( "shorter1" ); + const account_object& shorter2 = create_account( "shorter2" ); + const account_object& buyer1 = create_account( "buyer1" ); + const account_object& buyer2 = create_account( "buyer2" ); + + transfer( genesis_account(db), shorter1, asset( 10000 ) ); + transfer( genesis_account(db), shorter2, asset( 10000 ) ); + transfer( genesis_account(db), buyer1, asset( 10000 ) ); + transfer( genesis_account(db), buyer2, asset( 10000 ) ); + + BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); + BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); + BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee + + ilog( "=================================== START===================================\n\n"); + // this should cause the highest bid to below the margin call threshold + // which means it should be filled by the cover + auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) ); + if( unmatched ) edump((*unmatched)); + BOOST_REQUIRE( unmatched ); + */ + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) +{ try { + FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + /* + const asset_object& bitusd = create_bitasset( "BITUSD" ); + const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + + db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ + usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); + }); + + const account_object& shorter1 = create_account( "shorter1" ); + const account_object& shorter2 = create_account( "shorter2" ); + const account_object& buyer1 = create_account( "buyer1" ); + const account_object& buyer2 = create_account( "buyer2" ); + + transfer( genesis_account(db), shorter1, asset( 10000 ) ); + transfer( genesis_account(db), shorter2, asset( 10000 ) ); + transfer( genesis_account(db), buyer1, asset( 10000 ) ); + transfer( genesis_account(db), buyer2, asset( 10000 ) ); + + BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); + BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); + BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee + + // this should cause the highest bid to below the margin call threshold + // which means it should be filled by the cover + auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) ); + if( unmatched ) edump((*unmatched)); + BOOST_REQUIRE( unmatched ); + */ + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( margin_call_short_test ) +{ try { + FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + /* + const asset_object& bitusd = create_bitasset( "BITUSD" ); + const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + + db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ + usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); + }); + + const account_object& shorter1 = create_account( "shorter1" ); + const account_object& shorter2 = create_account( "shorter2" ); + const account_object& buyer1 = create_account( "buyer1" ); + const account_object& buyer2 = create_account( "buyer2" ); + + transfer( genesis_account(db), shorter1, asset( 10000 ) ); + transfer( genesis_account(db), shorter2, asset( 10000 ) ); + transfer( genesis_account(db), buyer1, asset( 10000 ) ); + transfer( genesis_account(db), buyer2, asset( 10000 ) ); + + BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); + BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); + BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee + ilog( "=================================== START===================================\n\n"); + + // this should cause the highest bid to below the margin call threshold + // which means it should be filled by the cover + auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); + if( unmatched ) edump((*unmatched)); + BOOST_REQUIRE( !unmatched ); + */ + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) +{ try { + FC_ASSERT( !"TODO - Reimplement with new short semantics" ); + /* + const asset_object& bitusd = create_bitasset( "BITUSD" ); + const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + + db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ + usd.current_feed.call_limit = core.amount(3) / bitusd.amount(4); + }); + + const account_object& shorter1 = create_account( "shorter1" ); + const account_object& shorter2 = create_account( "shorter2" ); + const account_object& buyer1 = create_account( "buyer1" ); + const account_object& buyer2 = create_account( "buyer2" ); + + transfer( genesis_account(db), shorter1, asset( 10000 ) ); + transfer( genesis_account(db), shorter2, asset( 10000 ) ); + transfer( genesis_account(db), buyer1, asset( 10000 ) ); + transfer( genesis_account(db), buyer2, asset( 10000 ) ); + + BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); + BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); + BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee + ilog( "=================================== START===================================\n\n"); + + // this should cause the highest bid to below the margin call threshold + // which means it should be filled by the cover + auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); + if( unmatched ) edump((*unmatched)); + BOOST_REQUIRE( unmatched ); + */ + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + + + + + + + + + + + + BOOST_AUTO_TEST_CASE( create_account_test ) { try { @@ -947,234 +1271,6 @@ BOOST_AUTO_TEST_CASE( trade_amount_equals_zero ) } } -BOOST_AUTO_TEST_CASE( margin_call_limit_test ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - const auto& call_index = db.get_index_type().indices().get(); - const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); - BOOST_REQUIRE( call_itr != call_index.end() ); - const call_order_object& call = *call_itr; - BOOST_CHECK(call.get_collateral() == core.amount(2000)); - BOOST_CHECK(call.get_debt() == bitusd.amount(1000)); - BOOST_CHECK(call.call_price == price(core.amount(1500), bitusd.amount(1000))); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9000); - - ilog( "=================================== START===================================\n\n"); - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(495), core.amount(750) ); - if( unmatched ) edump((*unmatched)); - BOOST_CHECK( !unmatched ); - BOOST_CHECK(call.get_debt() == bitusd.amount(505)); - BOOST_CHECK(call.get_collateral() == core.amount(1250)); - - auto below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); - BOOST_REQUIRE(below_call_price); - auto above_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(303)); - BOOST_REQUIRE(above_call_price); - auto above_id = above_call_price->id; - - cancel_limit_order(*below_call_price); - BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); - BOOST_CHECK(call.get_debt() == bitusd.amount(305)); - BOOST_CHECK(call.get_collateral() == core.amount(947)); - - below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); - BOOST_REQUIRE(below_call_price); - auto below_id = below_call_price->id; - above_call_price = create_sell_order(buyer1, bitusd.amount(95), core.amount(144)); - BOOST_REQUIRE(above_call_price); - above_id = above_call_price->id; - auto match_below_call = create_sell_order(buyer2, core.amount(1), bitusd.amount(200)); - BOOST_CHECK(!match_below_call); - - BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); - BOOST_CHECK_THROW(db.get_object(below_id), fc::exception); - BOOST_CHECK(call.get_debt() == bitusd.amount(210)); - BOOST_CHECK(call.get_collateral() == core.amount(803)); - */ - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(1) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - ilog( "=================================== START===================================\n\n"); - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( margin_call_short_test ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - ilog( "=================================== START===================================\n\n"); - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( !unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(4); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - ilog( "=================================== START===================================\n\n"); - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} /** * Create an order that cannot be filled immediately and have the From d5fb32a8399c987d1aac5b24dd58dd56b1130d61 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 09:07:23 -0400 Subject: [PATCH 040/354] fix margin calls --- libraries/chain/asset.cpp | 4 +- libraries/chain/db_market.cpp | 33 ++++++--- libraries/fc | 2 +- tests/tests/operation_tests.cpp | 123 +++++++------------------------- 4 files changed, 50 insertions(+), 112 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index 35287251..d0367f49 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -130,7 +130,7 @@ namespace graphene { namespace chain { { asset collateral = settlement_price.quote; fc::uint128 tmp( collateral.amount.value ); - tmp *= maximum_short_squeeze_ratio - 1000; + tmp *= maximum_short_squeeze_ratio; tmp /= 1000; FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); collateral.amount = tmp.to_uint64(); @@ -140,7 +140,7 @@ namespace graphene { namespace chain { { asset collateral = settlement_price.quote; fc::uint128 tmp( collateral.amount.value ); - tmp *= maintenance_collateral_ratio - 1000; + tmp *= maintenance_collateral_ratio; tmp /= 1000; FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); collateral.amount = tmp.to_uint64(); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 4b429035..f46e3017 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -290,7 +290,7 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c bool database::fill_order( const call_order_object& order, const asset& pays, const asset& receives ) { try { - idump((pays)(receives)(order)); + //idump((pays)(receives)(order)); assert( order.get_debt().asset_id == receives.asset_id ); assert( order.get_collateral().asset_id == pays.asset_id ); assert( order.get_collateral() >= pays ); @@ -311,7 +311,7 @@ bool database::fill_order( const call_order_object& order, const asset& pays, co const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this); modify( mia_ddo, [&]( asset_dynamic_data_object& ao ){ - idump((receives)); + //idump((receives)); ao.current_supply -= receives.amount; }); @@ -391,8 +391,11 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa // looking for limit orders selling the most USD for the least CORE auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); // stop when limit orders are selling too little USD for too much CORE - //auto min_price = bitasset.current_feed.max_short_squeeze_price(); - auto min_price = price::min( mia.id, bitasset.options.short_backing_asset ); + auto min_price = bitasset.current_feed.max_short_squeeze_price(); + // edump((bitasset.current_feed)); + // edump((min_price.to_real())(min_price)); + //auto min_price = price::min( mia.id, bitasset.options.short_backing_asset ); +/* idump((bitasset.current_feed.settlement_price)(bitasset.current_feed.settlement_price.to_real())); { for( const auto& order : limit_price_index ) @@ -406,6 +409,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa wdump((max_price)(max_price.to_real())); wdump((min_price)(min_price.to_real())); } + */ assert( max_price.base.asset_id == min_price.base.asset_id ); // wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); @@ -413,21 +417,23 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); + /* if( limit_itr != limit_price_index.end() ) wdump((*limit_itr)(limit_itr->sell_price.to_real())); if( limit_end != limit_price_index.end() ) wdump((*limit_end)(limit_end->sell_price.to_real())); +*/ if( limit_itr == limit_end ) { - wlog( "no orders available to fill margin calls" ); +// wlog( "no orders available to fill margin calls" ); return false; } auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); - bool filled_short_or_limit = false; + bool filled_limit = false; while( call_itr != call_end ) { @@ -440,16 +446,20 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa match_price = limit_itr->sell_price; usd_for_sale = limit_itr->amount_for_sale(); } - else return filled_short_or_limit; + else return filled_limit; +// wdump((match_price)); +// edump((usd_for_sale)); match_price.validate(); +// wdump((match_price)(~call_itr->call_price) ); if( match_price > ~call_itr->call_price ) { - return filled_short_or_limit; + return filled_limit; } auto usd_to_buy = call_itr->get_debt(); +// edump((usd_to_buy)); if( usd_to_buy * match_price > call_itr->get_collateral() ) { @@ -462,12 +472,13 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa asset call_pays, call_receives, order_pays, order_receives; if( usd_to_buy >= usd_for_sale ) { // fill order + //ilog( "filling all of limit order" ); call_receives = usd_for_sale; order_receives = usd_for_sale * match_price; call_pays = order_receives; order_pays = usd_for_sale; - filled_short_or_limit = true; + filled_limit = true; filled_call = (usd_to_buy == usd_for_sale); } else // fill call @@ -484,11 +495,11 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa if( filled_call ) ++call_itr; fill_order( *old_call_itr, call_pays, call_receives ); - auto old_limit_itr = !filled_call ? limit_itr++ : limit_itr; + auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr; fill_order( *old_limit_itr, order_pays, order_receives ); } // whlie call_itr != call_end - return filled_short_or_limit; + return filled_limit; } FC_CAPTURE_AND_RETHROW() } void database::pay_order( const account_object& receiver, const asset& receives, const asset& pays ) diff --git a/libraries/fc b/libraries/fc index dd1c77b3..dde8ed9d 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 +Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00 diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index fbdc595f..d0741a35 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -145,6 +145,10 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) * 1. highest bid is lower than the call price of an order * 2. the asset is not a prediction market * 3. there is a valid price feed + * + * This test creates two scenarios: + * a) when the bids are above the short squeese limit (should execute) + * b) when the bids are below the short squeeze limit (should not execute) */ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) { try { @@ -168,111 +172,34 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) publish_feed( bitusd, feedproducer, current_feed ); // start out with 2:1 collateral - borrow( borrower, bitusd.amount(5000), asset(10000), default_call_price ); - borrow( borrower2, bitusd.amount(5000), asset(30000), default_call_price ); - elog( "selling USD at 1:1, should be ok because we have 2:1 collateral" ); - create_sell_order( borrower, bitusd.amount(5000), core.amount(5000) ); - elog( "buying USD at 1:1" ); - create_sell_order( buyer, core.amount(5000), bitusd.amount(5000) ); + borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); + borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); - BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 0 ); - BOOST_REQUIRE_EQUAL( get_balance( buyer, bitusd ), 4950 ); // 1% market fee - BOOST_REQUIRE_EQUAL( get_balance( borrower, core ), init_balance - 10000 + 5000 ); - BOOST_REQUIRE_EQUAL( get_balance( buyer, core ), init_balance - 5000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower , core ), init_balance - 2000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, core ), init_balance - 4000 ); - ilog( "print call orders..." ); - print_call_orders(); - ilog( "print market..." ); - print_market( "", "" ); + // this should trigger margin call that is below the call limit, but above the + // protection threshold. + BOOST_TEST_MESSAGE( "Creating a margin call that is NOT protected by the max short squeeze price" ); + auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1400) ); + BOOST_REQUIRE( order == nullptr ); - // sell the BitUSD for 50% more which represents a 33% fall in the value of the collateral - const auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1400) ); - const auto order2 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1600) ); - const auto order3 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1700) ); - const auto order4 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) ); - const auto order5 = create_sell_order( borrower2, bitusd.amount(950), core.amount(1850) ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, core ), init_balance - 4000 + 1400 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 0 ); - ilog( "print call orders..." ); - print_call_orders(); - ilog( "print market..." ); - print_market( "", "" ); - - FC_ASSERT( order, "order should not match because sell price is below short squeeze protection" ); - wdump((bitusd.bitasset_data(db).current_feed.max_short_squeeze_price().to_real())); - wdump((bitusd.bitasset_data(db).current_feed.maintenance_price().to_real())); - wdump(((order->sell_price).to_real())); - - // update the feed to indicate a 33% drop in value - BOOST_TEST_MESSAGE( "Update feed to indicate CORE fell in value by 33%" ); - current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200); - publish_feed( bitusd, feedproducer, current_feed ); + BOOST_REQUIRE_EQUAL( get_balance( borrower, core ), init_balance - 2000 + 600 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); + BOOST_TEST_MESSAGE( "Creating a margin call that is protected by the max short squeeze price" ); + borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); + borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - const auto& call_index = db.get_index_type().indices().get(); - const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id)); - BOOST_REQUIRE( call_itr != call_index.end() ); - const call_order_object& call = *call_itr; - BOOST_CHECK(call.get_collateral() == core.amount(2000)); - BOOST_CHECK(call.get_debt() == bitusd.amount(1000)); - BOOST_CHECK(call.call_price == price(core.amount(1500), bitusd.amount(1000))); - BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9000); - - ilog( "=================================== START===================================\n\n"); - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(495), core.amount(750) ); - if( unmatched ) edump((*unmatched)); - BOOST_CHECK( !unmatched ); - BOOST_CHECK(call.get_debt() == bitusd.amount(505)); - BOOST_CHECK(call.get_collateral() == core.amount(1250)); - - auto below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); - BOOST_REQUIRE(below_call_price); - auto above_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(303)); - BOOST_REQUIRE(above_call_price); - auto above_id = above_call_price->id; - - cancel_limit_order(*below_call_price); - BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); - BOOST_CHECK(call.get_debt() == bitusd.amount(305)); - BOOST_CHECK(call.get_collateral() == core.amount(947)); - - below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1)); - BOOST_REQUIRE(below_call_price); - auto below_id = below_call_price->id; - above_call_price = create_sell_order(buyer1, bitusd.amount(95), core.amount(144)); - BOOST_REQUIRE(above_call_price); - above_id = above_call_price->id; - auto match_below_call = create_sell_order(buyer2, core.amount(1), bitusd.amount(200)); - BOOST_CHECK(!match_below_call); - - BOOST_CHECK_THROW(db.get_object(above_id), fc::exception); - BOOST_CHECK_THROW(db.get_object(below_id), fc::exception); - BOOST_CHECK(call.get_debt() == bitusd.amount(210)); - BOOST_CHECK(call.get_collateral() == core.amount(803)); - */ + // this should trigger margin call without protection from the price feed. + order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) ); + BOOST_REQUIRE( order != nullptr ); } catch( const fc::exception& e) { edump((e.to_detail_string())); throw; From d441da2959732be6781afc241b7f1de99efd6fb2 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 19 Jun 2015 15:20:48 +0200 Subject: [PATCH 041/354] Describe coverage testing --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e15ad7fe..b2121b05 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,31 @@ A list of CLI wallet commands is available [here](https://bitshares.github.io/do Code coverage testing --------------------- -TODO: Write something here +Check how much code is covered by unit tests, using gcov/lcov (see http://ltp.sourceforge.net/coverage/lcov.php ). + + cmake -D ENABLE_COVERAGE_TESTING=true -D CMAKE_BUILD_TYPE=Debug . + make + lcov --capture --initial --directory . --output-file base.info --no-external + libraries/fc/bloom_test + libraries/fc/task_cancel_test + libraries/fc/api + libraries/fc/blind + libraries/fc/ecc_test test + libraries/fc/real128_test + libraries/fc/lzma_test README.md + libraries/fc/ntp_test + tests/intense_test + tests/app_test + tests/chain_bench + tests/chain_test + tests/performance_test + lcov --capture --directory . --output-file test.info --no-external + lcov --add-tracefile base.info --add-tracefile test.info --output-file total.info + lcov -o interesting.info -r total.info libraries/fc/vendor/\* libraries/fc/tests/\* tests/\* + mkdir -p lcov + genhtml interesting.info --output-directory lcov --prefix `pwd` + +Now open `lcov/index.html` in a browser. Unit testing ------------ From be5a8c6365da2532cf67e9108debe060886ad3f6 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 09:57:23 -0400 Subject: [PATCH 042/354] update black swan implementation --- libraries/chain/asset_evaluator.cpp | 35 ++++-- libraries/chain/call_order_evaluator.cpp | 4 + libraries/chain/db_market.cpp | 117 ++++-------------- .../graphene/chain/asset_evaluator.hpp | 2 +- .../include/graphene/chain/asset_object.hpp | 17 +++ 5 files changed, 77 insertions(+), 98 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index be65bc5f..a19f91a4 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -247,6 +247,7 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita FC_ASSERT(a.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset."); const asset_bitasset_data_object& b = a.bitasset_data(d); + FC_ASSERT( !b.has_settlement(), "Cannot update a bitasset after a settlement has executed" ); if( o.new_options.short_backing_asset != b.options.short_backing_asset ) { FC_ASSERT(a.dynamic_asset_data_id(d).current_supply == 0); @@ -345,21 +346,40 @@ object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator: const database& d = db(); asset_to_settle = &op.amount.asset_id(d); FC_ASSERT(asset_to_settle->is_market_issued()); - FC_ASSERT(asset_to_settle->can_force_settle()); + const auto& bitasset = asset_to_settle->bitasset_data(d); + FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() ); FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount); return d.get_index_type().get_next_id(); } -object_id_type asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op) +operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op) { database& d = db(); d.adjust_balance(op.account, -op.amount); - return d.create([&](force_settlement_object& s) { - s.owner = op.account; - s.balance = op.amount; - s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec; - }).id; + + const auto& bitasset = asset_to_settle->bitasset_data(d); + if( bitasset.has_settlement() ) + { + auto settled_amount = op.amount * bitasset.settlement_price; + FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund ); + + d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){ + obj.settlement_fund -= settled_amount.amount; + }); + + d.adjust_balance(op.account, settled_amount); + + return settled_amount; + } + else + { + return d.create([&](force_settlement_object& s) { + s.owner = op.account; + s.balance = op.amount; + s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec; + }).id; + } } void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_operation& o) @@ -371,6 +391,7 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_ FC_ASSERT(base.is_market_issued()); const asset_bitasset_data_object& bitasset = base.bitasset_data(d); + FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" ); FC_ASSERT(o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset); //Verify that the publisher is authoritative to publish a feed if( base.issuer == account_id_type() ) diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index 19549e71..8fda9f2b 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -35,6 +35,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope _bitasset_data = &_debt_asset->bitasset_data(d); + /// if there is a settlement for this asset, then no further margin positions may be taken and + /// all existing margin positions should have been closed va database::globally_settle_asset + FC_ASSERT( !_bitasset_data->has_settlement() ); + FC_ASSERT( o.delta_collateral.asset_id == _bitasset_data->options.short_backing_asset ); if( _bitasset_data->is_prediction_market ) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index f46e3017..9a7cc0fb 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -28,14 +28,12 @@ namespace graphene { namespace chain { /** - for each short order, fill it at settlement price and place funds received into a total - calculate the USD->CORE price and convert all USD balances to CORE at that price and subtract CORE from total - - any fees accumulated by the issuer in the bitasset are forfeit / not redeemed - - cancel all open orders with bitasset in it - - any bitassets that use this bitasset as collateral are immediately settled at their feed price - - convert all balances in bitasset to CORE and subtract from total - - any prediction markets with usd as the backing get converted to CORE as the backing - any CORE left over due to rounding errors is paid to accumulated fees + * All margin positions are force closed at the swan price + * Collateral received goes into a force-settlement fund + * No new margin positions can be created for this asset + * No more price feed updates + * Force settlement happens without delay at the swan price, deducting from force-settlement fund + * No more asset updates may be issued. */ void database::globally_settle_asset( const asset_object& mia, const price& settlement_price ) { try { @@ -45,6 +43,8 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett edump( (mia.symbol)(settlement_price) ); const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); + FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" ); + const asset_object& backing_asset = bitasset.options.short_backing_asset(*this); asset collateral_gathered = backing_asset.amount(0); @@ -54,93 +54,30 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett const call_order_index& call_index = get_index_type(); const auto& call_price_index = call_index.indices().get(); - auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); - auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); - while( call_itr != call_end ) - { - auto pays = call_itr->get_debt() * settlement_price; - wdump( (call_itr->get_debt() ) ); - collateral_gathered += pays; - const auto& order = *call_itr; - ++call_itr; - FC_ASSERT( fill_order( order, pays, order.get_debt() ) ); - } - - const limit_order_index& limit_index = get_index_type(); - const auto& limit_price_index = limit_index.indices().get(); - - auto max_short_squeeze = bitasset.current_feed.max_short_squeeze_price(); - // cancel all orders selling the market issued asset - auto limit_itr = limit_price_index.lower_bound(price::max(mia.id, bitasset.options.short_backing_asset)); - auto limit_end = limit_price_index.upper_bound(~max_short_squeeze); - while( limit_itr != limit_end ) - { - const auto& order = *limit_itr; - ilog( "CANCEL LIMIT ORDER" ); - idump((order)); - ++limit_itr; - cancel_order( order ); - } - - limit_itr = limit_price_index.begin(); - while( limit_itr != limit_end ) - { - if( limit_itr->amount_for_sale().asset_id == mia.id ) - { - const auto& order = *limit_itr; - ilog( "CANCEL_AGAIN" ); - edump((order)); - ++limit_itr; - cancel_order( order ); - } - } - - limit_itr = limit_price_index.begin(); - while( limit_itr != limit_end ) + // cancel all call orders and accumulate it into collateral_gathered + auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); + auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); + while( call_itr != call_end ) { - if( limit_itr->amount_for_sale().asset_id == mia.id ) - { - const auto& order = *limit_itr; - edump((order)); - ++limit_itr; - cancel_order( order ); - } + auto pays = call_itr->get_debt() * settlement_price; + wdump( (call_itr->get_debt() ) ); + collateral_gathered += pays; + const auto& order = *call_itr; + ++call_itr; + FC_ASSERT( fill_order( order, pays, order.get_debt() ) ); } - // settle all balances - asset total_mia_settled = mia.amount(0); + modify( bitasset, [&]( asset_bitasset_data_object& obj ){ + obj.settlement_price = settlement_price; + obj.settlement_fund = collateral_gathered.amount; + }); - const auto& index = get_index_type().indices().get(); - auto range = index.equal_range(mia.get_id()); - for( auto itr = range.first; itr != range.second; ++itr ) - { - auto mia_balance = itr->get_balance(); - if( mia_balance.amount > 0 ) - { - adjust_balance(itr->owner, -mia_balance); - auto settled_amount = mia_balance * settlement_price; - idump( (mia_balance)(settled_amount)(settlement_price) ); - adjust_balance(itr->owner, settled_amount); - total_mia_settled += mia_balance; - collateral_gathered -= settled_amount; - } - } + /// TODO: after all margin positions are closed, the current supply will be reported as 0, but + /// that is a lie, the supply didn't change. We need to capture the current supply before + /// filling all call orders and then restore it afterward. Then in the force settlement + /// evaluator reduce the supply - // TODO: convert payments held in escrow - - modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){ - total_mia_settled.amount += obj.accumulated_fees; - obj.accumulated_fees = 0; - }); - - wlog( "====================== AFTER SETTLE BLACK SWAN UNCLAIMED SETTLEMENT FUNDS ==============\n" ); - wdump((collateral_gathered)(total_mia_settled)(original_mia_supply)(mia_dyn.current_supply)); - modify( bitasset.options.short_backing_asset(*this).dynamic_asset_data_id(*this), [&]( asset_dynamic_data_object& obj ){ - obj.accumulated_fees += collateral_gathered.amount; - }); - - FC_ASSERT( total_mia_settled.amount == original_mia_supply, "", ("total_settled",total_mia_settled)("original",original_mia_supply) ); - } FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) } +} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) } void database::cancel_order(const force_settlement_object& order, bool create_virtual_op) { diff --git a/libraries/chain/include/graphene/chain/asset_evaluator.hpp b/libraries/chain/include/graphene/chain/asset_evaluator.hpp index 212bc9af..786a7e2a 100644 --- a/libraries/chain/include/graphene/chain/asset_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/asset_evaluator.hpp @@ -114,7 +114,7 @@ namespace graphene { namespace chain { typedef asset_settle_operation operation_type; object_id_type do_evaluate(const operation_type& op); - object_id_type do_apply(const operation_type& op); + operation_result do_apply(const operation_type& op); const asset_object* asset_to_settle = nullptr; }; diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 95d78d4d..cb2a61a9 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -250,6 +250,21 @@ namespace graphene { namespace chain { /// Calculate the maximum force settlement volume per maintenance interval, given the current share supply share_type max_force_settlement_volume(share_type current_supply)const; + /** return true if there has been a black swan, false otherwise */ + bool has_settlement()const { return !settlement_price.is_null(); } + + /** + * In the event of a black swan, the swan price is saved in the settlement price, + * and all margin positions are settled at the same price with the siezed collateral + * being moved into the settlement fund. From this point on no further updates to + * the asset are permitted (no feeds, etc) and forced settlement occurs using + * the settlement price and fund. + */ + ///@{ + price settlement_price; + share_type settlement_fund; + ///@} + time_point_sec feed_expiration_time()const { return current_feed_publication_time + options.feed_lifetime_sec; } bool feed_is_expired(time_point_sec current_time)const @@ -292,6 +307,8 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db:: (options) (force_settled_volume) (is_prediction_market) + (settlement_price) + (settlement_fund) ) FC_REFLECT( graphene::chain::asset_object::asset_options, From 6fc11bb5fd27150505283bd921836429973534c2 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 10:23:32 -0400 Subject: [PATCH 043/354] Resolve #59: Add witnesses to default genesis in witness_node --- libraries/app/application.cpp | 12 ++++++++++++ libraries/plugins/witness/witness.cpp | 21 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 201a3eaa..6deec129 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -150,6 +150,18 @@ namespace detail { auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); genesis_state_type initial_state; + secret_hash_type::encoder enc; + fc::raw::pack(enc, nathan_key); + fc::raw::pack(enc, secret_hash_type()); + for( int i = 0; i < 10; ++i ) + { + initial_state.allocation_targets.emplace_back("init"+fc::to_string(i), nathan_key.get_public_key(), 0, true); + initial_state.initial_committee.push_back({"init"+fc::to_string(i)}); + } + initial_state.initial_witnesses = vector(10, {"committee-account", + nathan_key.get_public_key(), + secret_hash_type::hash(enc.result())}); + initial_state.allocation_targets.emplace_back("nathan", address(public_key_type(nathan_key.get_public_key())), 1); if( _options->count("genesis-json") ) initial_state = fc::json::from_file(_options->at("genesis-json").as()).as(); diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 13ac821e..8ad64c1e 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ void witness_plugin::plugin_set_program_options( ("witness-id,w", bpo::value>()->composing()->multitoken(), "ID of witness controlled by this node (e.g. \"1.7.0\", quotes are required, may specify multiple times)") ("private-key", bpo::value>()->composing()->multitoken()-> - DEFAULT_VALUE_VECTOR(std::make_pair(chain::key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("null_key"))))), + DEFAULT_VALUE_VECTOR(std::make_pair(chain::key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))))), "Tuple of [key ID, private key] (may specify multiple times)") ; config_file_options.add(command_line_options); @@ -63,9 +64,23 @@ void witness_plugin::plugin_startup() graphene::time::now(); for( auto wit : _witnesses ) { - auto key = wit(database()).signing_key; - if( !_private_keys.count(key) ) + auto signing_key = wit(database()).signing_key; + if( !_private_keys.count(signing_key) ) { + // Check if it's a duplicate key of one I do have + bool found_duplicate = false; + for( const auto& private_key : _private_keys ) + if( chain::public_key_type(private_key.second.get_public_key()) == signing_key(database()).key_address() ) + { + ilog("Found duplicate key: ${k1} matches ${k2}; using this key to sign for ${w}", + ("k1", private_key.first)("k2", signing_key)("w", wit)); + _private_keys[signing_key] = private_key.second; + found_duplicate = true; + break; + } + if( found_duplicate ) + continue; + elog("Unable to find key for witness ${w}. Removing it from my witnesses.", ("w", wit)); bad_wits.insert(wit); } From fc11f40937fe1ecea6b534f56176f848d4b85eb7 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 10:55:07 -0400 Subject: [PATCH 044/354] Fix app_test Sometimes it fails to connect, but when it does, it works. --- tests/app/main.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 93480b06..de35c181 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -40,7 +40,6 @@ BOOST_AUTO_TEST_CASE( two_node_network ) fc::temp_directory app_dir; fc::temp_directory app2_dir; fc::temp_file genesis_json; - fc::json::save_to_file(genesis_state_type(), genesis_json.path()); fc::time_point_sec now( GRAPHENE_GENESIS_TIMESTAMP ); @@ -48,7 +47,6 @@ BOOST_AUTO_TEST_CASE( two_node_network ) app1.register_plugin(); bpo::variables_map cfg; cfg.emplace("p2p-endpoint", bpo::variable_value(string("127.0.0.1:3939"), false)); - cfg.emplace("genesis-json", bpo::variable_value(boost::filesystem::path(genesis_json.path()), false)); app1.initialize(app_dir.path(), cfg); graphene::app::application app2; @@ -63,19 +61,20 @@ BOOST_AUTO_TEST_CASE( two_node_network ) app2.startup(); fc::usleep(fc::milliseconds(500)); - BOOST_CHECK_EQUAL(app1.p2p_node()->get_connection_count(), 1); + BOOST_REQUIRE_EQUAL(app1.p2p_node()->get_connection_count(), 1); BOOST_CHECK_EQUAL(std::string(app1.p2p_node()->get_connected_peers().front().host.get_address()), "127.0.0.1"); ilog("Connected!"); fc::ecc::private_key nathan_key = fc::ecc::private_key::generate(); - fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); + fc::ecc::private_key genesis_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); graphene::chain::signed_transaction trx; trx.set_expiration(now + fc::seconds(30)); std::shared_ptr db2 = app2.chain_database(); - trx.operations.push_back(key_create_operation({asset(), account_id_type(1), public_key_type(nathan_key.get_public_key())})); + trx.operations.push_back(key_create_operation({asset(), + GRAPHENE_TEMP_ACCOUNT, + public_key_type(nathan_key.get_public_key())})); trx.validate(); - trx.sign(key_id_type(0), genesis_key); processed_transaction ptrx = app1.chain_database()->push_transaction(trx); app1.p2p_node()->broadcast(graphene::net::trx_message(trx)); key_id_type nathan_key_id = ptrx.operation_results.front().get(); @@ -85,8 +84,10 @@ BOOST_AUTO_TEST_CASE( two_node_network ) ilog("Pushed transaction"); now += GRAPHENE_DEFAULT_BLOCK_INTERVAL; - app2.p2p_node()->broadcast(graphene::net::block_message(db2->generate_block( - now, db2->get_scheduled_witness( 1 ).first, genesis_key, database::skip_nothing ))); + app2.p2p_node()->broadcast(graphene::net::block_message(db2->generate_block(now, + db2->get_scheduled_witness(1).first, + genesis_key, + database::skip_nothing))); fc::usleep(fc::milliseconds(500)); BOOST_CHECK_EQUAL(app1.p2p_node()->get_connection_count(), 1); From dac278511b0b87e03b55b1591536f28083a094c8 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 11:24:35 -0400 Subject: [PATCH 045/354] Cleaning up Dan's mess: fix broken mia_feeds test --- tests/tests/operation_tests2.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 5e6e932a..4acf3425 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -300,8 +300,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_delete ) PUSH_TX( db, trx ); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( broken_mia_feeds, 1 ) -BOOST_AUTO_TEST_CASE( broken_mia_feeds ) +BOOST_AUTO_TEST_CASE( mia_feeds ) { try { ACTORS((nathan)(dan)(ben)(vikram)); asset_id_type bit_usd_id = create_bitasset("BITUSD").id; @@ -337,31 +336,32 @@ BOOST_AUTO_TEST_CASE( broken_mia_feeds ) const asset_object& bit_usd = bit_usd_id(db); asset_publish_feed_operation op({asset(), vikram_id}); op.asset_id = bit_usd_id; - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); // We'll expire margins after a month // Accept defaults for required collateral trx.operations.emplace_back(op); PUSH_TX( db, trx, ~0 ); const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db); - BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); + BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = ben_id; - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); - BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); + BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = dan_id; - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); + op.feed.maximum_short_squeeze_ratio = 1000; op.feed.maintenance_collateral_ratio = 1000; trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); - BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); + BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = nathan_id; @@ -479,7 +479,7 @@ BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10091); BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9850); -} FC_LOG_AND_RETHROW() +} FC_LOG_AND_RETHROW() */ } From 51e6421ea0ebfa042bc27583ad30e7adb421e4d6 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 12:11:32 -0400 Subject: [PATCH 046/354] fixed config settings --- libraries/chain/asset.cpp | 2 +- libraries/chain/include/graphene/chain/config.hpp | 5 ++--- tests/tests/operation_tests.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index d0367f49..eea50d9d 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -123,7 +123,7 @@ namespace graphene { namespace chain { if( !settlement_price.is_null() ) settlement_price.validate(); FC_ASSERT( maximum_short_squeeze_ratio >= 1000 ); - FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio ); + FC_ASSERT( maintenance_collateral_ratio <= maximum_short_squeeze_ratio ); } FC_CAPTURE_AND_RETHROW( (*this) ) } price price_feed::max_short_squeeze_price()const diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index ddb81f18..bab8c531 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -63,9 +63,8 @@ #define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0 #define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage -#define GRAPHENE_DEFAULT_INITIAL_COLLATERAL_RATIO 2000 -#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 -#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 +#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1500 // We require collateral of 1.5x or more +#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1750 // If there is a squeeze you are protected up to 1.75x... but black swan could occur first #define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24) #define GRAPHENE_DEFAULT_NUM_WITNESSES (101) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index d0741a35..3492cb33 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) { try { asset usd(100,1); - asset core(200,1); + asset core(100,0); price_feed feed; feed.settlement_price = usd / core; From 19f1e0498680e6233d8a896840f95a554e79e82c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 12:27:09 -0400 Subject: [PATCH 047/354] remove redundant tests --- tests/tests/operation_tests.cpp | 165 -------------------------------- 1 file changed, 165 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 3492cb33..3cf8b037 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -206,171 +206,6 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) } } -BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(1) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - ilog( "=================================== START===================================\n\n"); - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( margin_call_short_test ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - ilog( "=================================== START===================================\n\n"); - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( !unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected ) -{ try { - FC_ASSERT( !"TODO - Reimplement with new short semantics" ); - /* - const asset_object& bitusd = create_bitasset( "BITUSD" ); - const asset_object& core = get_asset( GRAPHENE_SYMBOL ); - - db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){ - usd.current_feed.call_limit = core.amount(3) / bitusd.amount(4); - }); - - const account_object& shorter1 = create_account( "shorter1" ); - const account_object& shorter2 = create_account( "shorter2" ); - const account_object& buyer1 = create_account( "buyer1" ); - const account_object& buyer2 = create_account( "buyer2" ); - - transfer( genesis_account(db), shorter1, asset( 10000 ) ); - transfer( genesis_account(db), shorter2, asset( 10000 ) ); - transfer( genesis_account(db), buyer1, asset( 10000 ) ); - transfer( genesis_account(db), buyer2, asset( 10000 ) ); - - BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) ); - BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) ); - BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee - ilog( "=================================== START===================================\n\n"); - - // this should cause the highest bid to below the margin call threshold - // which means it should be filled by the cover - auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) ); - if( unmatched ) edump((*unmatched)); - BOOST_REQUIRE( unmatched ); - */ - - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - - - - - - - - - - - BOOST_AUTO_TEST_CASE( create_account_test ) { From 626b5cbb2323640979859922535e4e9f7a76e49f Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 12:28:02 -0400 Subject: [PATCH 048/354] Fix broken update_mia test --- libraries/chain/asset.cpp | 4 ++- .../chain/include/graphene/chain/config.hpp | 3 +- tests/tests/operation_tests.cpp | 31 +++++++++---------- tests/tests/operation_tests2.cpp | 4 +-- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index eea50d9d..ef73dd73 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -122,7 +122,9 @@ namespace graphene { namespace chain { { try { if( !settlement_price.is_null() ) settlement_price.validate(); - FC_ASSERT( maximum_short_squeeze_ratio >= 1000 ); + FC_ASSERT( maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); + FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); + FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); FC_ASSERT( maintenance_collateral_ratio <= maximum_short_squeeze_ratio ); } FC_CAPTURE_AND_RETHROW( (*this) ) } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index bab8c531..5ff44984 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -61,9 +61,10 @@ #define GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES 10 #define GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS 10 +// These are NOT percentages #define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0 #define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage -#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1500 // We require collateral of 1.5x or more +#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1500 // We require collateral of 1.5x or more #define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1750 // If there is a squeeze you are protected up to 1.75x... but black swan could occur first #define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 3cf8b037..8196c263 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) try { asset usd(100,1); asset core(100,0); - price_feed feed; + price_feed feed; feed.settlement_price = usd / core; FC_ASSERT( usd * feed.settlement_price < usd * feed.maintenance_price() ); @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) } } BOOST_AUTO_TEST_CASE( call_order_update_test ) -{ +{ try { ACTORS((dan)(sam)); const auto& bitusd = create_bitasset("BITUSD"); @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - auto default_call_price = ~price::call_price( bitusd.amount(5000), asset(5000), 1750); + auto default_call_price = ~price::call_price( bitusd.amount(5000), asset(5000), 1750); BOOST_TEST_MESSAGE( "attempting to borrow using 2x collateral at 1:1 price now that there is a valid order" ); borrow( dan, bitusd.amount(5000), asset(10000), default_call_price ); @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 ); - + // test just increasing collateral BOOST_TEST_MESSAGE( "increasing collateral" ); borrow( dan, bitusd.amount(0), asset(10000), default_call_price ); @@ -123,11 +123,11 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) cover( dan, bitusd.amount(0), asset(1000), default_call_price ); BOOST_TEST_MESSAGE( "attempting change call price without changing debt/collateral ratio" ); - default_call_price = ~price::call_price( bitusd.amount(100), asset(50), 1750); + default_call_price = ~price::call_price( bitusd.amount(100), asset(50), 1750); cover( dan, bitusd.amount(0), asset(0), default_call_price ); BOOST_TEST_MESSAGE( "attempting change call price to be below minimum for debt/collateral ratio" ); - default_call_price = ~price::call_price( bitusd.amount(100), asset(500), 1750); + default_call_price = ~price::call_price( bitusd.amount(100), asset(500), 1750); BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0), default_call_price ), fc::exception ); } catch (fc::exception& e) { @@ -143,10 +143,10 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) * A margin call can happen in the following situation: * 0. there exists a bid above the mas short squeeze price * 1. highest bid is lower than the call price of an order - * 2. the asset is not a prediction market + * 2. the asset is not a prediction market * 3. there is a valid price feed * - * This test creates two scenarios: + * This test creates two scenarios: * a) when the bids are above the short squeese limit (should execute) * b) when the bids are below the short squeeze limit (should not execute) */ @@ -164,14 +164,14 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) transfer(genesis_account, borrower2_id, asset(init_balance)); update_feed_producers( bitusd, {feedproducer.id} ); - price_feed current_feed; + price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); - auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); + auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); - // starting out with price 1:1 + // starting out with price 1:1 publish_feed( bitusd, feedproducer, current_feed ); - // start out with 2:1 collateral + // start out with 2:1 collateral borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); @@ -456,8 +456,7 @@ BOOST_AUTO_TEST_CASE( create_mia ) } } -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( broken_update_mia, 1 ) -BOOST_AUTO_TEST_CASE( broken_update_mia ) +BOOST_AUTO_TEST_CASE( update_mia ) { try { INVOKE(create_mia); @@ -477,7 +476,6 @@ BOOST_AUTO_TEST_CASE( broken_update_mia ) trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); - //idump((bit_usd)); { asset_publish_feed_operation pop; pop.asset_id = bit_usd.get_id(); @@ -485,8 +483,9 @@ BOOST_AUTO_TEST_CASE( broken_update_mia ) price_feed feed; feed.settlement_price = price(bit_usd.amount(5), bit_usd.amount(5)); REQUIRE_THROW_WITH_VALUE(pop, feed, feed); - feed.settlement_price = price(bit_usd.amount(5), asset(5)); + feed.settlement_price = ~price(bit_usd.amount(5), asset(5)); REQUIRE_THROW_WITH_VALUE(pop, feed, feed); + feed.settlement_price = price(bit_usd.amount(5), asset(5)); pop.feed = feed; REQUIRE_THROW_WITH_VALUE(pop, feed.maintenance_collateral_ratio, 0); trx.operations.back() = pop; diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 4acf3425..5e35977d 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -356,8 +356,8 @@ BOOST_AUTO_TEST_CASE( mia_feeds ) op.publisher = dan_id; op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); - op.feed.maximum_short_squeeze_ratio = 1000; - op.feed.maintenance_collateral_ratio = 1000; + op.feed.maximum_short_squeeze_ratio = 1001; + op.feed.maintenance_collateral_ratio = 1001; trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); From 6bc2d06833b26493b57bb646cf2c32cc046c4894 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 13:31:47 -0400 Subject: [PATCH 049/354] Cashback to blockchain accounts is put in reserve pool --- libraries/chain/db_balance.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 791d8488..7873cbfb 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -97,6 +97,17 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b if( amount == 0 ) return; + if( acct.get_id() == GRAPHENE_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_WITNESS_ACCOUNT || + acct.get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT || acct.get_id() == GRAPHENE_NULL_ACCOUNT || + 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) { + d.current_supply -= amount; + }); + return; + } + uint32_t global_vesting_seconds = get_global_properties().parameters.cashback_vesting_period_seconds; fc::time_point_sec now = head_block_time(); From a90e3f0155da7d8b5ad3802d181a95fd31c76fdb Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 13:43:39 -0400 Subject: [PATCH 050/354] Fix broken delegate_feeds test --- tests/common/database_fixture.cpp | 5 +++-- tests/tests/basic_tests.cpp | 14 ++++++++++++++ tests/tests/operation_tests.cpp | 28 +++++++--------------------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 44bb134d..c19d9f11 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -78,14 +78,15 @@ database_fixture::database_fixture() database_fixture::~database_fixture() { + // If we're unwinding due to an exception, don't do any more checks. + // This way, boost test's last checkpoint tells us approximately where the error was. if( !std::uncaught_exception() ) { verify_asset_supplies(); verify_account_history_plugin_index(); + BOOST_CHECK( db.get_node_properties().skip_flags == database::skip_nothing ); } - BOOST_CHECK( db.get_node_properties().skip_flags == database::skip_nothing ); - if( data_dir ) db.close(); return; diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index 65ce489a..efe1ae5b 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -62,6 +62,20 @@ BOOST_AUTO_TEST_CASE( price_test ) BOOST_CHECK( ~price::min(0,1) == price::max(1,0) ); BOOST_CHECK( ~price::max(0,1) < ~price::min(0,1) ); BOOST_CHECK( ~price::max(0,1) <= ~price::min(0,1) ); + price a(asset(1), asset(2,1)); + price b(asset(2), asset(2,1)); + price c(asset(1), asset(2,1)); + BOOST_CHECK(a < b); + BOOST_CHECK(b > a); + BOOST_CHECK(a == c); + BOOST_CHECK(!(b == c)); + + price_feed dummy; + dummy.maintenance_collateral_ratio = 1002; + dummy.maximum_short_squeeze_ratio = 1234; + dummy.settlement_price = price(asset(1000), asset(2000, 1)); + price_feed dummy2 = dummy; + BOOST_CHECK(dummy == dummy2); } BOOST_AUTO_TEST_CASE( serialization_tests ) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 8196c263..759124b0 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -933,7 +933,6 @@ BOOST_AUTO_TEST_CASE( cancel_limit_order_test ) } } -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( delegate_feeds, 1 ) BOOST_AUTO_TEST_CASE( delegate_feeds ) { using namespace graphene::chain; @@ -955,44 +954,31 @@ BOOST_AUTO_TEST_CASE( delegate_feeds ) asset_publish_feed_operation op({asset(), active_witnesses[0]}); op.asset_id = bit_usd.get_id(); - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30)); // Accept defaults for required collateral trx.operations.emplace_back(op); PUSH_TX( db, trx, ~0 ); - { - //Dumb sanity check of some operators. Only here to improve code coverage. :D - price_feed dummy = op.feed; - BOOST_CHECK(op.feed == dummy); - price a(asset(1), bit_usd.amount(2)); - price b(asset(2), bit_usd.amount(2)); - price c(asset(1), bit_usd.amount(2)); - BOOST_CHECK(a < b); - BOOST_CHECK(b > a); - BOOST_CHECK(a == c); - BOOST_CHECK(!(b == c)); - } - const asset_bitasset_data_object& bitasset = bit_usd.bitasset_data(db); - BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); + BOOST_CHECK(bitasset.current_feed.settlement_price.to_real() == 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = active_witnesses[1]; - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25)); trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); - BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 25.0); + BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); op.publisher = active_witnesses[2]; - op.feed.settlement_price = price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); + op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40)); // But this delegate is an idiot. - op.feed.maintenance_collateral_ratio = 1000; + op.feed.maintenance_collateral_ratio = 1001; trx.operations.back() = op; PUSH_TX( db, trx, ~0 ); - BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), GRAPHENE_BLOCKCHAIN_PRECISION / 30.0); + BOOST_CHECK_EQUAL(bitasset.current_feed.settlement_price.to_real(), 30.0 / GRAPHENE_BLOCKCHAIN_PRECISION); BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); } catch (const fc::exception& e) { edump((e.to_detail_string())); From 5e475e0c15c66cd980471318b75bc9c6c487b03b Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 13:46:27 -0400 Subject: [PATCH 051/354] Mark expected failures while I track them down --- tests/tests/operation_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 759124b0..6fe13076 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1056,6 +1056,7 @@ BOOST_AUTO_TEST_CASE( fill_order ) o.calculate_fee(db.current_fee_schedule()); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( witness_withdraw_pay_test, 10 ) BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) { try { // there is an immediate maintenance interval in the first block From 5e28a79e6fe23a733cbb8a80498e8df76433dc60 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 18 Jun 2015 16:06:42 -0400 Subject: [PATCH 052/354] db_maint.cpp: Roll over leftover witness pay, see 8f739ac7678ba0a06bedf6f7f9c989e521aff346 for description --- libraries/chain/db_init.cpp | 1 + libraries/chain/db_maint.cpp | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1d75f601..0f95fd1d 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -232,6 +232,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); create( [&](dynamic_global_property_object& p) { p.time = fc::time_point_sec(GRAPHENE_GENESIS_TIMESTAMP); + p.witness_budget = 0; }); create([&](block_summary_object& p) { }); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 5c019c4d..92a2e6a1 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -233,6 +233,9 @@ share_type database::get_max_budget( fc::time_point_sec now )const // are available for the budget at this point, but not included // in core.burned(). share_type reserve = core.burned(*this) + core_dd.accumulated_fees; + // Similarly, we consider leftover witness_budget to be burned + // at the BEGINNING of the maintenance interval. + reserve += dpo.witness_budget; fc::uint128_t budget_u128 = reserve.value; budget_u128 *= uint64_t(dt); @@ -303,16 +306,24 @@ void database::process_budget() pay_workers(leftover_worker_funds); available_funds += leftover_worker_funds; + share_type unused_prev_witness_budget = dpo.witness_budget; modify(core, [&]( asset_dynamic_data_object& _core ) { - _core.current_supply = (_core.current_supply + witness_budget + - worker_budget - leftover_worker_funds - - _core.accumulated_fees); + _core.current_supply = (_core.current_supply + + witness_budget + + worker_budget + - leftover_worker_funds + - _core.accumulated_fees + - unused_prev_witness_budget + ); _core.accumulated_fees = 0; }); modify(dpo, [&]( dynamic_global_property_object& _dpo ) { - _dpo.witness_budget += witness_budget; + // Since initial witness_budget was rolled into + // available_funds, we replace it with witness_budget + // instead of adding it. + _dpo.witness_budget = witness_budget; _dpo.last_budget_time = now; }); From 9742bf511d5f03ea86e055bced929b9b1bad3f71 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 14:18:43 -0400 Subject: [PATCH 053/354] Fix witness_withdraw_pay_test --- tests/tests/operation_tests.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 6fe13076..b89caca6 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1056,18 +1056,16 @@ BOOST_AUTO_TEST_CASE( fill_order ) o.calculate_fee(db.current_fee_schedule()); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( witness_withdraw_pay_test, 10 ) BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) { try { // there is an immediate maintenance interval in the first block // which will initialize last_budget_time generate_block(); - // budget should be 25 satoshis based on 30 blocks at 5-second interval - // with 17 / 2**32 rate per block - const int ref_budget = 125; - // set to a value which will exhaust ref_budget after three witnesses + // Based on the size of the reserve fund later in the test, the witness budget will be set to this value + const int ref_budget = 624; const int witness_ppb = 55; + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) { _gpo.parameters.witness_pay_per_block = witness_ppb; @@ -1123,43 +1121,36 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) // maintenance will be in block 31. time of block 31 - time of block 1 = 30 * 5 seconds. schedule_maint(); - // TODO: Replace this with another check - //BOOST_CHECK_EQUAL(account_id_type()(db).statistics(db).cashback_rewards.value, 1000000000-200000000); - // first witness paid from old budget (so no pay) - BOOST_CHECK_EQUAL( core->burned(db).value, 0 ); + // The 80% lifetime referral fee went to the committee account, which burned it. Check that it's here. + BOOST_CHECK_EQUAL( core->burned(db).value, 840000000 ); generate_block(); - BOOST_CHECK_EQUAL( core->burned(db).value, 210000000 - ref_budget ); + BOOST_CHECK_EQUAL( core->burned(db).value, 840000000 + 210000000 - ref_budget ); BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget ); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); + // first witness paid from old budget (so no pay) BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 ); // second witness finally gets paid! generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - witness_ppb ); - const witness_object* paid_witness = witness; - // full payment to next witness generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 2 * witness_ppb ); - // partial payment to last witness generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); - BOOST_CHECK_EQUAL( witness->accumulated_income.value, ref_budget - 2 * witness_ppb ); - BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 ); + BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); + BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 3 * witness_ppb ); generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); - BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 ); - BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 ); + BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); + BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 4 * witness_ppb ); trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); - // last one was unpaid, so pull out a paid one for checks - witness = paid_witness; - //wdump((*witness)); // Withdraw the witness's pay enable_fees(1); witness_withdraw_pay_operation wop; @@ -1175,7 +1166,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) trx.clear(); BOOST_CHECK_EQUAL(get_balance(witness->witness_account(db), *core), witness_ppb - 1/*fee*/); - BOOST_CHECK_EQUAL(core->burned(db).value, 210000000 - ref_budget ); + BOOST_CHECK_EQUAL(core->burned(db).value, 840000000 + 210000000 - ref_budget ); BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0); } FC_LOG_AND_RETHROW() } From c80c839675e31167832573d17d47062a33f2208f Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 14:47:42 -0400 Subject: [PATCH 054/354] black swan test with force settle after --- libraries/chain/asset_evaluator.cpp | 6 +++ libraries/chain/db_market.cpp | 9 +++- tests/common/database_fixture.cpp | 19 ++++++++ tests/common/database_fixture.hpp | 1 + tests/tests/operation_tests.cpp | 68 +++++++++++++++++++++++------ 5 files changed, 87 insertions(+), 16 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index a19f91a4..b41eed9c 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -370,6 +370,12 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator:: d.adjust_balance(op.account, settled_amount); + const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d); + + d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){ + obj.current_supply -= op.amount.amount; + }); + return settled_amount; } else diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 9a7cc0fb..9c551640 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -37,10 +37,11 @@ namespace graphene { namespace chain { */ void database::globally_settle_asset( const asset_object& mia, const price& settlement_price ) { try { + /* elog( "BLACK SWAN!" ); debug_dump(); - edump( (mia.symbol)(settlement_price) ); + */ const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" ); @@ -54,6 +55,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett const call_order_index& call_index = get_index_type(); const auto& call_price_index = call_index.indices().get(); + // cancel all call orders and accumulate it into collateral_gathered auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); @@ -72,10 +74,13 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett obj.settlement_fund = collateral_gathered.amount; }); - /// TODO: after all margin positions are closed, the current supply will be reported as 0, but + /// After all margin positions are closed, the current supply will be reported as 0, but /// that is a lie, the supply didn't change. We need to capture the current supply before /// filling all call orders and then restore it afterward. Then in the force settlement /// evaluator reduce the supply + modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){ + obj.current_supply = original_mia_supply; + }); } FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 44bb134d..f526be7f 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -148,6 +148,11 @@ void database_fixture::verify_asset_supplies( )const if( asset_obj.id != asset_id_type() ) BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value); total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool; + if( asset_obj.is_market_issued() ) + { + const auto& bad = asset_obj.bitasset_data(db); + total_balances[bad.options.short_backing_asset] += bad.settlement_fund; + } } for( const witness_object& witness_obj : db.get_index_type>() ) { @@ -655,6 +660,20 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obj trx.operations.clear(); } +void database_fixture::force_settle( const account_object& who, asset what ) +{ try { + trx.set_expiration(db.head_block_time() + fc::minutes(1)); + trx.operations.clear(); + asset_settle_operation sop; + sop.account = who.id; + sop.amount = what; + trx.operations.push_back(sop); + for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); +} FC_CAPTURE_AND_RETHROW( (who)(what) ) } + void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price ) { try { asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id ); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index bfa45805..5a53608a 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -147,6 +147,7 @@ struct database_fixture { key_id_type key = key_id_type() ); + void force_settle( const account_object& who, asset what ); void update_feed_producers( const asset_object& mia, flat_set producers ); void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f ); void borrow( const account_object& who, asset what, asset collateral, price call_price = price()); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 8196c263..590884fd 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -206,6 +206,60 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) } } +/** + * This test sets up the minimum condition for a black swan to occur but does + * not test the full range of cases that may be possible during a black swan. + */ +BOOST_AUTO_TEST_CASE( black_swan ) +{ try { + ACTORS((buyer)(seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("BITUSD"); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(genesis_account, buyer_id, asset(init_balance)); + transfer(genesis_account, borrower_id, asset(init_balance)); + transfer(genesis_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); + + // starting out with price 1:1 + publish_feed( bitusd, feedproducer, current_feed ); + + // start out with 2:1 collateral + borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); + borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); + + BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower , core ), init_balance - 2000 ); + BOOST_REQUIRE_EQUAL( get_balance( borrower2, core ), init_balance - 4000 ); + + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200); + publish_feed( bitusd, feedproducer, current_feed ); + + /// this sell order is designed to trigger a black swan + auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(3000) ); + + FC_ASSERT( bitusd.bitasset_data(db).has_settlement() ); + wdump(( bitusd.bitasset_data(db) )); + + force_settle( borrower, bitusd.amount(100) ); + + BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" ); + BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ), fc::exception ); + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + + BOOST_AUTO_TEST_CASE( create_account_test ) { @@ -1248,20 +1302,6 @@ BOOST_AUTO_TEST_CASE( unimp_bulk_discount_test ) //const account_object& shorter2 = create_account( "bob" ); BOOST_FAIL( "not implemented" ); } - -/** - * This test sets up the minimum condition for a black swan to occur but does - * not test the full range of cases that may be possible during a black swan. - */ -BOOST_AUTO_TEST_CASE( margin_call_black_swan ) -{ try { - FC_ASSERT( "TODO - Reimplement with new short semantics" ); - } catch( const fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - /** * Assume the referrer gets 99% of transaction fee */ From 764b8ce4ae508902fad593886a97e6fe04e81e50 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 19 Jun 2015 14:49:46 -0400 Subject: [PATCH 055/354] operation_tests.cpp: Make witness_withdraw_pay_test computations more explicit --- tests/tests/operation_tests.cpp | 41 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 041db4fc..17d1b2bc 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1116,15 +1116,6 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) // which will initialize last_budget_time generate_block(); - // Based on the size of the reserve fund later in the test, the witness budget will be set to this value - const int ref_budget = 624; - const int witness_ppb = 55; - - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - _gpo.parameters.witness_pay_per_block = witness_ppb; - } ); - // Make an account and upgrade it to prime, so that witnesses get some pay create_account("nathan"); transfer(account_id_type()(db), get_account("nathan"), asset(10000000000)); @@ -1134,6 +1125,27 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) const account_object* nathan = &get_account("nathan"); enable_fees(105000000); BOOST_CHECK_GT(db.current_fee_schedule().membership_lifetime_fee, 0); + // Based on the size of the reserve fund later in the test, the witness budget will be set to this value + const uint64_t ref_budget = + ((uint64_t( db.current_fee_schedule().membership_lifetime_fee ) + * GRAPHENE_CORE_ASSET_CYCLE_RATE * 30 + * db.get_global_properties().parameters.block_interval + ) + ((uint64_t(1) << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)-1) + ) >> GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS + ; + // change this if ref_budget changes + BOOST_CHECK_EQUAL( ref_budget, 624 ); + const uint64_t witness_ppb = ref_budget * 10 / 23 + 1; + // change this if ref_budget changes + BOOST_CHECK_EQUAL( witness_ppb, 272 ); + // following two inequalities need to hold for maximal code coverage + BOOST_CHECK_LT( witness_ppb * 2, ref_budget ); + BOOST_CHECK_GT( witness_ppb * 3, ref_budget ); + + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + _gpo.parameters.witness_pay_per_block = witness_ppb; + } ); BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0); account_upgrade_operation uop; @@ -1186,6 +1198,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) // second witness finally gets paid! generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); + const witness_object* paid_witness = witness; BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - witness_ppb ); @@ -1196,17 +1209,19 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); - BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); - BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 3 * witness_ppb ); + BOOST_CHECK_LT( witness->accumulated_income.value, witness_ppb ); + BOOST_CHECK_EQUAL( witness->accumulated_income.value, ref_budget - 2 * witness_ppb ); + BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 ); generate_block(); witness = &db.fetch_block_by_number(db.head_block_num())->witness(db); - BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb ); - BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 4 * witness_ppb ); + BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 ); + BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 ); trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); // Withdraw the witness's pay enable_fees(1); + witness = paid_witness; witness_withdraw_pay_operation wop; wop.from_witness = witness->id; wop.to_account = witness->witness_account; From 14b158364fb49ed5db15c026bc22cd4092e57b3b Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 15:57:08 -0400 Subject: [PATCH 056/354] implement and test prediction market features --- libraries/chain/asset_evaluator.cpp | 2 + libraries/chain/call_order_evaluator.cpp | 55 +++++++++++++----------- libraries/chain/db_market.cpp | 3 +- tests/common/database_fixture.cpp | 41 ++++++++++++++++++ tests/common/database_fixture.hpp | 5 +++ tests/tests/operation_tests.cpp | 42 ++++++++++++++++++ 6 files changed, 121 insertions(+), 27 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index b41eed9c..2fa3566d 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -348,6 +348,8 @@ object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator: FC_ASSERT(asset_to_settle->is_market_issued()); const auto& bitasset = asset_to_settle->bitasset_data(d); FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() ); + if( bitasset.is_prediction_market ) + FC_ASSERT( bitasset.has_settlement(), "global settlement must occur before force settling a prediction market" ); FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount); return d.get_index_type().get_next_id(); diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index 8fda9f2b..ec6e6638 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -65,7 +65,7 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope void_result call_order_update_evaluator::do_apply(const call_order_update_operation& o) -{ +{ try { database& d = db(); //wdump( (_bitasset_data->current_feed) ); @@ -97,6 +97,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat auto& call_idx = d.get_index_type().indices().get(); auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) ); const call_order_object* call_obj = nullptr; + if( itr == call_idx.end() ) { FC_ASSERT( o.delta_collateral.amount > 0 ); @@ -127,34 +128,38 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat d.remove( *call_obj ); return void_result(); } - auto collateral = call_obj->get_collateral(); - auto mp = _bitasset_data->current_feed.maintenance_price(); - -// edump((debt)(collateral)((debt/collateral).to_real())(mp.to_real()) ); - // edump((debt*mp)); - /// paying off the debt at the user specified call price should require - /// less collateral than paying off the debt at the maitenance price - auto col_at_call_price = debt * o.call_price; - auto col_at_min_callprice = debt * mp; - FC_ASSERT( col_at_call_price <= col_at_min_callprice, "", ("debt*o.callprice",debt*o.call_price)("debt*mp",debt*mp) ); - FC_ASSERT( col_at_call_price <= collateral ); - - //wdump( (o.call_price)(mp)(call_obj->call_price.to_real())(mp.to_real()) ); - //FC_ASSERT( call_obj->call_price <= mp ); - - auto call_order_id = call_obj->id; - - //ilog( "checking call orders" ); - - // check to see if the order needs to be margin called now, but don't allow black swans and require there to be - // limit orders available that could be used to fill the order. - if( d.check_call_orders( *_debt_asset, false ) ) + /** then we must check for margin calls and other issues */ + if( !_bitasset_data->is_prediction_market ) { - FC_ASSERT( !d.find_object( call_order_id ), "If updating the call order triggers a margin call, then it must completely cover the order" ); + auto collateral = call_obj->get_collateral(); + auto mp = _bitasset_data->current_feed.maintenance_price(); + + // edump((debt)(collateral)((debt/collateral).to_real())(mp.to_real()) ); + // edump((debt*mp)); + /// paying off the debt at the user specified call price should require + /// less collateral than paying off the debt at the maitenance price + auto col_at_call_price = debt * o.call_price; + auto col_at_min_callprice = debt * mp; + FC_ASSERT( col_at_call_price <= col_at_min_callprice, "", ("debt*o.callprice",debt*o.call_price)("debt*mp",debt*mp) ); + FC_ASSERT( col_at_call_price <= collateral ); + + //wdump( (o.call_price)(mp)(call_obj->call_price.to_real())(mp.to_real()) ); + //FC_ASSERT( call_obj->call_price <= mp ); + + auto call_order_id = call_obj->id; + + //ilog( "checking call orders" ); + + // check to see if the order needs to be margin called now, but don't allow black swans and require there to be + // limit orders available that could be used to fill the order. + if( d.check_call_orders( *_debt_asset, false ) ) + { + FC_ASSERT( !d.find_object( call_order_id ), "If updating the call order triggers a margin call, then it must completely cover the order" ); + } } return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 9c551640..c90c737d 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -62,7 +62,6 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett while( call_itr != call_end ) { auto pays = call_itr->get_debt() * settlement_price; - wdump( (call_itr->get_debt() ) ); collateral_gathered += pays; const auto& order = *call_itr; ++call_itr; @@ -321,8 +320,8 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa { try { if( !mia.is_market_issued() ) return false; const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); - if( bitasset.current_feed.settlement_price.is_null() ) return false; if( bitasset.is_prediction_market ) return false; + if( bitasset.current_feed.settlement_price.is_null() ) return false; const call_order_index& call_index = get_index_type(); const auto& call_price_index = call_index.indices().get(); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index a194b6f0..e4912fa0 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -398,6 +398,32 @@ const asset_object& database_fixture::create_bitasset( return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW( (name)(flags) ) } +const asset_object& database_fixture::create_prediction_market( + const string& name, + account_id_type issuer /* = 1 */, + uint16_t market_fee_percent /* = 100 */ /* 1% */, + uint16_t flags /* = charge_market_fee */ + ) +{ try { + asset_create_operation creator; + creator.issuer = issuer; + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.precision = 2; + creator.common_options.market_fee_percent = market_fee_percent; + creator.common_options.issuer_permissions = flags | global_settle; + creator.common_options.flags = flags & ~global_settle; + creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)}); + creator.bitasset_options = asset_object::bitasset_options(); + creator.is_prediction_market = true; + trx.operations.push_back(std::move(creator)); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} FC_CAPTURE_AND_RETHROW( (name)(flags) ) } + const asset_object& database_fixture::create_user_issued_asset( const string& name ) { asset_create_operation creator; @@ -661,6 +687,21 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obj trx.operations.clear(); } +void database_fixture::force_global_settle( const asset_object& what, const price& p ) +{ try { + trx.set_expiration(db.head_block_time() + fc::minutes(1)); + trx.operations.clear(); + asset_global_settle_operation sop; + sop.issuer = what.issuer; + sop.asset_to_settle = what.id; + sop.settle_price = p; + trx.operations.push_back(sop); + for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); +} FC_CAPTURE_AND_RETHROW( (what)(p) ) } + void database_fixture::force_settle( const account_object& who, asset what ) { try { trx.set_expiration(db.head_block_time() + fc::minutes(1)); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 5a53608a..9f308fec 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -147,6 +147,7 @@ struct database_fixture { key_id_type key = key_id_type() ); + void force_global_settle( const asset_object& what, const price& p ); void force_settle( const account_object& who, asset what ); void update_feed_producers( const asset_object& mia, flat_set producers ); void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f ); @@ -159,6 +160,10 @@ struct database_fixture { account_id_type issuer = account_id_type(1), uint16_t market_fee_percent = 100 /*1%*/, uint16_t flags = charge_market_fee); + const asset_object& create_prediction_market(const string& name, + account_id_type issuer = account_id_type(1), + uint16_t market_fee_percent = 100 /*1%*/, + uint16_t flags = charge_market_fee); const asset_object& create_user_issued_asset( const string& name ); void issue_uia( const account_object& recipient, asset amount ); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 041db4fc..0f04fb57 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -259,6 +259,48 @@ BOOST_AUTO_TEST_CASE( black_swan ) } } +BOOST_AUTO_TEST_CASE( prediction_market ) +{ try { + ACTORS((judge)(dan)(nathan)); + + const auto& pmark = create_prediction_market("PMARK", judge_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + transfer(genesis_account, judge_id, asset(init_balance)); + transfer(genesis_account, dan_id, asset(init_balance)); + transfer(genesis_account, nathan_id, asset(init_balance)); + + auto default_call_price = ~price::call_price( pmark.amount(100), asset(100), 1750); + + BOOST_TEST_MESSAGE( "Require throw for mismatch collateral amounts" ); + BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000), default_call_price ), fc::exception ); + + BOOST_TEST_MESSAGE( "Open position with equal collateral" ); + borrow( dan, pmark.amount(1000), asset(1000), default_call_price ); + + BOOST_TEST_MESSAGE( "Cover position with unequal asset should fail." ); + BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000), default_call_price ), fc::exception ); + + BOOST_TEST_MESSAGE( "Cover half of position with equal ammounts" ); + cover( dan, pmark.amount(500), asset(500), default_call_price ); + + BOOST_TEST_MESSAGE( "Verify that forced settlment fails before global settlement" ); + BOOST_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception ); + + BOOST_TEST_MESSAGE( "Shouldn't be allowed to force settle at more than 1 collateral per debt" ); + BOOST_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception ); + + force_global_settle( pmark, pmark.amount(100) / core.amount(95) ); + + BOOST_TEST_MESSAGE( "Verify that forced settlment succeedes after global settlement" ); + force_settle( dan, pmark.amount(100) ); + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} BOOST_AUTO_TEST_CASE( create_account_test ) From 3d29b29e508a2e0c756b1b55f5017167ff53ba73 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 15:58:50 -0400 Subject: [PATCH 057/354] remove debug logs --- libraries/chain/db_market.cpp | 2 +- tests/tests/operation_tests.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index c90c737d..b9b3bb7c 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -405,7 +405,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa if( usd_to_buy * match_price > call_itr->get_collateral() ) { FC_ASSERT( enable_black_swan ); - elog( "black swan, we do not have enough collateral to cover at this price" ); + //elog( "black swan, we do not have enough collateral to cover at this price" ); globally_settle_asset( mia, call_itr->get_debt() / call_itr->get_collateral() ); return true; } diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index f3c824b6..ed182860 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -247,7 +247,6 @@ BOOST_AUTO_TEST_CASE( black_swan ) auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(3000) ); FC_ASSERT( bitusd.bitasset_data(db).has_settlement() ); - wdump(( bitusd.bitasset_data(db) )); force_settle( borrower, bitusd.amount(100) ); From 639930a2d7adb5c8fac2711adb895a06f0d83bf9 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 15:58:51 -0400 Subject: [PATCH 058/354] Test bulk discount on fees --- libraries/chain/account_object.cpp | 7 ++--- libraries/chain/db_block.cpp | 2 -- tests/tests/fee_tests.cpp | 41 ++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index b8de41de..fed6f66e 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -78,15 +78,12 @@ void account_statistics_object::process_fees(const account_object& a, database& auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting) { - // Check the referrer and registrar -- if they're no longer members, pay up to the lifetime member instead. + // 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; }); - if( account.registrar(d).is_basic_account(d.head_block_time()) ) - d.modify(account, [](account_object& a) { - a.registrar = a.lifetime_referrer; - }); share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); assert( network_cut <= core_fee_total ); diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index dc4476d2..bd5a6bb6 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -306,8 +306,6 @@ signed_block database::_generate_block( if( !(skip & skip_delegate_signature) ) _pending_block.sign( block_signing_private_key ); FC_ASSERT( fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size ); - //This line used to std::move(_pending_block) but this is unsafe as _pending_block is later referenced without being - //reinitialized. Future optimization could be to move it, then reinitialize it with the values we need to preserve. signed_block tmp = _pending_block; tmp.transaction_merkle_root = tmp.calculate_merkle_root(); _pending_block.transactions.clear(); diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index aa24b0ef..4bb1556a 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -130,4 +130,45 @@ BOOST_AUTO_TEST_CASE( cashback_test ) BOOST_CHECK_EQUAL(stud_id(db).cashback_balance(db).balance.amount.value, 25750); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(bulk_discount) +{ try { + ACTOR(nathan); + // Give nathan ALLLLLL the money! + transfer(GRAPHENE_COMMITTEE_ACCOUNT, nathan_id, db.get_balance(GRAPHENE_COMMITTEE_ACCOUNT, asset_id_type())); + enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10); + upgrade_to_lifetime_member(nathan_id); + share_type new_fees; + while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN ) + { + transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1)); + new_fees += transfer_operation().calculate_fee(db.current_fee_schedule()); + } + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10); + auto old_cashback = nathan_id(db).cashback_balance(db).balance; + + transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1)); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10); + + BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value, + old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 8); + + new_fees = 0; + while( nathan_id(db).statistics(db).lifetime_fees_paid + new_fees < GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX ) + { + transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1)); + new_fees += transfer_operation().calculate_fee(db.current_fee_schedule()); + } + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + enable_fees(GRAPHENE_BLOCKCHAIN_PRECISION*10); + old_cashback = nathan_id(db).cashback_balance(db).balance; + + transfer(nathan_id, GRAPHENE_COMMITTEE_ACCOUNT, asset(1)); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK_EQUAL(nathan_id(db).cashback_balance(db).balance.amount.value, + old_cashback.amount.value + GRAPHENE_BLOCKCHAIN_PRECISION * 9); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From c1d1884d0b6696579559c77f1d83ae68e5e3606a Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 16:09:51 -0400 Subject: [PATCH 059/354] Test burning of whitelisted asset --- tests/tests/uia_tests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index da3bed74..265aeb91 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -133,6 +133,9 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) trx.operations.back() = op; //Fail because nathan is blacklisted BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); + trx.operations = {asset_burn_operation{asset(), nathan.id, advanced.amount(10)}}; + //Fail because nathan is blacklisted + BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); std::swap(op.from, op.to); trx.operations.back() = op; //Fail because nathan is blacklisted @@ -181,6 +184,10 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) //Fail because nathan is not whitelisted BOOST_CHECK(!nathan.is_authorized_asset(advanced)); BOOST_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); + + trx.operations = {asset_burn_operation{asset(), dan.id, advanced.amount(10)}}; + PUSH_TX(db, trx, ~0); + BOOST_CHECK_EQUAL(get_balance(dan, advanced), 40); } catch(fc::exception& e) { edump((e.to_detail_string())); throw; From 7dd2a6dff54ed4cd78144f99df96501d60c4d41c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 16:13:32 -0400 Subject: [PATCH 060/354] adding description field to asset options --- libraries/chain/include/graphene/chain/asset_object.hpp | 6 ++++++ libraries/chain/operations.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index cb2a61a9..c3cd7481 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -156,6 +156,11 @@ namespace graphene { namespace chain { /** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */ flat_set blacklist_markets; + /** data that describes the meaning/purpose of this asset, fee will be charged proportional to + * size of description. + */ + string description; + /// Perform internal consistency checks. /// @throws fc::exception if any check fails void validate()const; @@ -323,6 +328,7 @@ FC_REFLECT( graphene::chain::asset_object::asset_options, (blacklist_authorities) (whitelist_markets) (blacklist_markets) + (description) ) FC_REFLECT( graphene::chain::asset_object::bitasset_options, (feed_lifetime_sec) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 8fc1a608..157df651 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -162,6 +162,7 @@ share_type asset_create_operation::calculate_fee( const fee_schedule_type& sched uint32_t s = symbol.size(); while( s <= 6 ) { core_fee_required *= 30; ++s; } + core_fee_required += ((fc::raw::pack_size(*this)*schedule.data_fee)/1024); return core_fee_required; } @@ -288,7 +289,7 @@ void asset_update_operation::validate()const share_type asset_update_operation::calculate_fee( const fee_schedule_type& k )const { - return k.asset_update_fee; + return k.asset_update_fee + ((fc::raw::pack_size(*this)*k.data_fee)/1024); } void asset_burn_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const From fdec2a24e3834b313675f40ef2fabc8362eed18c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 16:18:56 -0400 Subject: [PATCH 061/354] Fix #15 - adding unit test --- tests/tests/basic_tests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index efe1ae5b..8832d396 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -41,6 +41,15 @@ using namespace graphene::db; BOOST_FIXTURE_TEST_SUITE( basic_tests, database_fixture ) +/** verify that names are + * https://github.com/cryptonomex/graphene/issues/15 + */ +BOOST_AUTO_TEST_CASE( valid_name_test ) +{ + BOOST_REQUIRE( is_valid_name( "aaa-bbb-1" ) ); + BOOST_REQUIRE( !is_valid_name( "1aaa-bbb" ) ); +} + BOOST_AUTO_TEST_CASE( price_test ) { BOOST_CHECK( price::max(0,1) > price::min(0,1) ); From f88a7682dd962b2592e84a69385ebd68d3ced3e2 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 16:21:47 -0400 Subject: [PATCH 062/354] cheap names must have a number, dot, dash or slash --- libraries/chain/operations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 157df651..c8f8dbb3 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -115,7 +115,7 @@ share_type account_create_operation::calculate_fee( const fee_schedule_type& sch FC_ASSERT( s >= 2 ); - if( s == 8 ) + if( s >= 8 && s < 63 ) core_fee_required = schedule.account_len8_fee; else if( s == 7 ) core_fee_required = schedule.account_len7_fee; From c3b30b3dd7c1273d2f4b017b166b310591925e24 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 16:36:37 -0400 Subject: [PATCH 063/354] Tweak to database_fixture::borrow --- tests/common/database_fixture.cpp | 1 - tests/common/database_fixture.hpp | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e4912fa0..96b50c9c 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -718,7 +718,6 @@ void database_fixture::force_settle( const account_object& who, asset what ) void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price ) { try { - asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id ); trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.clear(); trx.operations.push_back( call_order_update_operation({ asset(), who.id, collateral, what, call_price }));; diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 9f308fec..edbead03 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -147,12 +147,14 @@ struct database_fixture { key_id_type key = key_id_type() ); - void force_global_settle( const asset_object& what, const price& p ); - void force_settle( const account_object& who, asset what ); - void update_feed_producers( const asset_object& mia, flat_set producers ); - void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f ); - void borrow( const account_object& who, asset what, asset collateral, price call_price = price()); - void cover( const account_object& who, asset what, asset collateral_freed, price call_price = price()); + void force_global_settle(const asset_object& what, const price& p); + void force_settle(const account_object& who, asset what); + void update_feed_producers(const asset_object& mia, flat_set producers); + void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f); + void borrow(account_id_type who, asset what, asset collateral, price call_price = price()) + { borrow(who(db), what, collateral, call_price); } + void borrow(const account_object& who, asset what, asset collateral, price call_price); + void cover(const account_object& who, asset what, asset collateral_freed, price call_price = price()); const asset_object& get_asset( const string& symbol )const; const account_object& get_account( const string& name )const; From 127ca9f8debf3a0e05d901c836cdc8f48b59ce0a Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 18:14:23 -0400 Subject: [PATCH 064/354] WIP: Remove call price from call_order_update_operation --- libraries/chain/call_order_evaluator.cpp | 43 +++++------- .../chain/include/graphene/chain/config.hpp | 4 +- .../include/graphene/chain/operations.hpp | 3 +- libraries/chain/operations.cpp | 4 +- tests/common/database_fixture.cpp | 8 +-- tests/common/database_fixture.hpp | 8 +-- tests/tests/operation_tests.cpp | 69 +++++++++---------- 7 files changed, 60 insertions(+), 79 deletions(-) diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index ec6e6638..e6709695 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -67,7 +67,6 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope void_result call_order_update_evaluator::do_apply(const call_order_update_operation& o) { try { database& d = db(); - //wdump( (_bitasset_data->current_feed) ); if( o.delta_debt.amount != 0 ) { @@ -104,11 +103,12 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat FC_ASSERT( o.delta_debt.amount > 0 ); call_obj = &d.create( [&](call_order_object& call ){ - call.borrower = o.funding_account; - call.collateral = o.delta_collateral.amount; - call.debt = o.delta_debt.amount; - call.call_price = ~o.call_price; - }); + call.borrower = o.funding_account; + call.collateral = o.delta_collateral.amount; + call.debt = o.delta_debt.amount; + call.call_price = price::call_price(o.delta_debt, o.delta_collateral, + _bitasset_data->current_feed.maintenance_collateral_ratio); + }); } else { @@ -117,41 +117,32 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat d.modify( *call_obj, [&]( call_order_object& call ){ call.collateral += o.delta_collateral.amount; call.debt += o.delta_debt.amount; - call.call_price = ~o.call_price; + call.call_price = price::call_price(call.get_debt(), call.get_collateral(), + _bitasset_data->current_feed.maintenance_collateral_ratio); }); } auto debt = call_obj->get_debt(); - if( debt.amount == 0 ) + if( debt.amount == 0 ) { FC_ASSERT( call_obj->collateral == 0 ); d.remove( *call_obj ); return void_result(); } - /** then we must check for margin calls and other issues */ + FC_ASSERT(call_obj->collateral > 0 && call_obj->debt > 0); + + // then we must check for margin calls and other issues if( !_bitasset_data->is_prediction_market ) { - auto collateral = call_obj->get_collateral(); - auto mp = _bitasset_data->current_feed.maintenance_price(); - - // edump((debt)(collateral)((debt/collateral).to_real())(mp.to_real()) ); - // edump((debt*mp)); - /// paying off the debt at the user specified call price should require - /// less collateral than paying off the debt at the maitenance price - auto col_at_call_price = debt * o.call_price; - auto col_at_min_callprice = debt * mp; - FC_ASSERT( col_at_call_price <= col_at_min_callprice, "", ("debt*o.callprice",debt*o.call_price)("debt*mp",debt*mp) ); - FC_ASSERT( col_at_call_price <= collateral ); - - //wdump( (o.call_price)(mp)(call_obj->call_price.to_real())(mp.to_real()) ); - //FC_ASSERT( call_obj->call_price <= mp ); + // Check that the order's debt per collateral is less than the system's minimum debt per collateral. + FC_ASSERT( ~call_obj->call_price < _bitasset_data->current_feed.maintenance_price(), + "Insufficient collateral for debt.", + ("a", call_obj->call_price)("b", _bitasset_data->current_feed.maintenance_price())); auto call_order_id = call_obj->id; - //ilog( "checking call orders" ); - - // check to see if the order needs to be margin called now, but don't allow black swans and require there to be + // check to see if the order needs to be margin called now, but don't allow black swans and require there to be // limit orders available that could be used to fill the order. if( d.check_call_orders( *_debt_asset, false ) ) { diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 5ff44984..7cb2db1c 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -64,8 +64,8 @@ // These are NOT percentages #define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0 #define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage -#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1500 // We require collateral of 1.5x or more -#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1750 // If there is a squeeze you are protected up to 1.75x... but black swan could occur first +#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 // Call when collateral only pays off 175% the debt +#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 // Stop calling when collateral only pays off 150% of the debt #define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24) #define GRAPHENE_DEFAULT_NUM_WITNESSES (101) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 39f46a5c..6e9744ac 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -845,7 +845,6 @@ namespace graphene { namespace chain { account_id_type funding_account; ///< pays fee, collateral, and cover asset delta_collateral; ///< the amount of collateral to add to the margin position asset delta_debt; ///< the amount of the debt to be paid off, may be negative to issue new debt - price call_price; ///< the price at which the collateral will be sold to cover the debt account_id_type fee_payer()const { return funding_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; @@ -1504,7 +1503,7 @@ FC_REFLECT( graphene::chain::limit_order_create_operation, ) FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) ) FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order) ) -FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(call_price) ) +FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt) ) FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo) ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 8fc1a608..6f16061a 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -401,9 +401,7 @@ void call_order_update_operation::validate()const { try { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( delta_collateral.asset_id != delta_debt.asset_id ); - FC_ASSERT( delta_debt.asset_id == call_price.base.asset_id ); - FC_ASSERT( delta_collateral.asset_id == call_price.quote.asset_id ); - call_price.validate(); + FC_ASSERT( delta_collateral.amount != 0 || delta_debt.amount != 0 ); } FC_CAPTURE_AND_RETHROW((*this)) } share_type call_order_update_operation::calculate_fee(const fee_schedule_type& k) const diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 96b50c9c..e74a34b3 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -716,22 +716,22 @@ void database_fixture::force_settle( const account_object& who, asset what ) trx.operations.clear(); } FC_CAPTURE_AND_RETHROW( (who)(what) ) } -void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price ) +void database_fixture::borrow(const account_object& who, asset what, asset collateral) { try { trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.clear(); - trx.operations.push_back( call_order_update_operation({ asset(), who.id, collateral, what, call_price }));; + trx.operations.push_back(call_order_update_operation({asset(), who.id, collateral, what}));; for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); trx.validate(); db.push_transaction(trx, ~0); trx.operations.clear(); } FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) } -void database_fixture::cover( const account_object& who, asset what, asset collateral, price call_price ) +void database_fixture::cover(const account_object& who, asset what, asset collateral) { try { trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.clear(); - trx.operations.push_back( call_order_update_operation({ asset(), who.id, -collateral, -what, call_price })); + trx.operations.push_back( call_order_update_operation({asset(), who.id, -collateral, -what})); for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) ); trx.validate(); db.push_transaction(trx, ~0); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index edbead03..c76811ea 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -151,10 +151,10 @@ struct database_fixture { void force_settle(const account_object& who, asset what); void update_feed_producers(const asset_object& mia, flat_set producers); void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f); - void borrow(account_id_type who, asset what, asset collateral, price call_price = price()) - { borrow(who(db), what, collateral, call_price); } - void borrow(const account_object& who, asset what, asset collateral, price call_price); - void cover(const account_object& who, asset what, asset collateral_freed, price call_price = price()); + void borrow(account_id_type who, asset what, asset collateral) + { borrow(who(db), what, collateral); } + void borrow(const account_object& who, asset what, asset collateral); + void cover(const account_object& who, asset what, asset collateral_freed); const asset_object& get_asset( const string& symbol )const; const account_object& get_account( const string& name )const; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index ed182860..836726ec 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -47,8 +47,12 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) price_feed feed; feed.settlement_price = usd / core; - FC_ASSERT( usd * feed.settlement_price < usd * feed.maintenance_price() ); - FC_ASSERT( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() ); + wdump((feed.settlement_price.to_real())); + wdump((feed.maintenance_price().to_real())); + wdump((feed.max_short_squeeze_price().to_real())); + + BOOST_CHECK( usd * feed.settlement_price < usd * feed.maintenance_price() ); + BOOST_CHECK( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() ); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -70,65 +74,58 @@ BOOST_AUTO_TEST_CASE( call_order_update_test ) FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - auto default_call_price = ~price::call_price( bitusd.amount(5000), asset(5000), 1750); - BOOST_TEST_MESSAGE( "attempting to borrow using 2x collateral at 1:1 price now that there is a valid order" ); - borrow( dan, bitusd.amount(5000), asset(10000), default_call_price ); + borrow( dan, bitusd.amount(5000), asset(10000)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 ); BOOST_TEST_MESSAGE( "covering 2500 usd and freeing 5000 core..." ); - cover( dan, bitusd.amount(2500), asset(5000), default_call_price ); + cover( dan, bitusd.amount(2500), asset(5000)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 5000 ); BOOST_TEST_MESSAGE( "verifying that attempting to cover the full amount without claiming the collateral fails" ); - BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0) ), fc::exception ); - cover( dan, bitusd.amount(2500), core.amount(5000), default_call_price ); + cover( dan, bitusd.amount(2500), core.amount(5000)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 0 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 ); - borrow( dan, bitusd.amount(5000), asset(10000), default_call_price ); + borrow( dan, bitusd.amount(5000), asset(10000)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 ); // test just increasing collateral BOOST_TEST_MESSAGE( "increasing collateral" ); - borrow( dan, bitusd.amount(0), asset(10000), default_call_price ); + borrow( dan, bitusd.amount(0), asset(10000)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 ); // test just decreasing debt BOOST_TEST_MESSAGE( "decreasing debt" ); - cover( dan, bitusd.amount(1000), asset(0), default_call_price ); + cover( dan, bitusd.amount(1000), asset(0)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 4000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 ); BOOST_TEST_MESSAGE( "increasing debt without increasing collateral" ); - borrow( dan, bitusd.amount(1000), asset(0), default_call_price ); + borrow( dan, bitusd.amount(1000), asset(0)); BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 ); BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 ); BOOST_TEST_MESSAGE( "increasing debt without increasing collateral again" ); - BOOST_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception ); BOOST_TEST_MESSAGE( "attempting to claim all collateral without paying off debt" ); - BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception ); BOOST_TEST_MESSAGE( "attempting reduce collateral without paying off any debt" ); - cover( dan, bitusd.amount(0), asset(1000), default_call_price ); - - BOOST_TEST_MESSAGE( "attempting change call price without changing debt/collateral ratio" ); - default_call_price = ~price::call_price( bitusd.amount(100), asset(50), 1750); - cover( dan, bitusd.amount(0), asset(0), default_call_price ); + cover( dan, bitusd.amount(0), asset(1000)); BOOST_TEST_MESSAGE( "attempting change call price to be below minimum for debt/collateral ratio" ); - default_call_price = ~price::call_price( bitusd.amount(100), asset(500), 1750); - BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception ); } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -166,14 +163,13 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); - auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); // starting out with price 1:1 publish_feed( bitusd, feedproducer, current_feed ); // start out with 2:1 collateral - borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); - borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); + borrow( borrower, bitusd.amount(1000), asset(2000)); + borrow( borrower2, bitusd.amount(1000), asset(4000) ); BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 ); @@ -194,8 +190,8 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test ) BOOST_TEST_MESSAGE( "Creating a margin call that is protected by the max short squeeze price" ); - borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); - borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); + borrow( borrower, bitusd.amount(1000), asset(2000) ); + borrow( borrower2, bitusd.amount(1000), asset(4000) ); // this should trigger margin call without protection from the price feed. order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) ); @@ -226,14 +222,13 @@ BOOST_AUTO_TEST_CASE( black_swan ) price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); - auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750); // starting out with price 1:1 publish_feed( bitusd, feedproducer, current_feed ); // start out with 2:1 collateral - borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ); - borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price ); + borrow( borrower, bitusd.amount(1000), asset(2000) ); + borrow( borrower2, bitusd.amount(1000), asset(4000) ); BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 ); @@ -249,9 +244,9 @@ BOOST_AUTO_TEST_CASE( black_swan ) FC_ASSERT( bitusd.bitasset_data(db).has_settlement() ); force_settle( borrower, bitusd.amount(100) ); - + BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" ); - BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000) ), fc::exception ); } catch( const fc::exception& e) { edump((e.to_detail_string())); throw; @@ -270,23 +265,21 @@ BOOST_AUTO_TEST_CASE( prediction_market ) transfer(genesis_account, dan_id, asset(init_balance)); transfer(genesis_account, nathan_id, asset(init_balance)); - auto default_call_price = ~price::call_price( pmark.amount(100), asset(100), 1750); - BOOST_TEST_MESSAGE( "Require throw for mismatch collateral amounts" ); - BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000) ), fc::exception ); BOOST_TEST_MESSAGE( "Open position with equal collateral" ); - borrow( dan, pmark.amount(1000), asset(1000), default_call_price ); + borrow( dan, pmark.amount(1000), asset(1000) ); BOOST_TEST_MESSAGE( "Cover position with unequal asset should fail." ); - BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000), default_call_price ), fc::exception ); + BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000) ), fc::exception ); BOOST_TEST_MESSAGE( "Cover half of position with equal ammounts" ); - cover( dan, pmark.amount(500), asset(500), default_call_price ); + cover( dan, pmark.amount(500), asset(500) ); BOOST_TEST_MESSAGE( "Verify that forced settlment fails before global settlement" ); BOOST_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception ); - + BOOST_TEST_MESSAGE( "Shouldn't be allowed to force settle at more than 1 collateral per debt" ); BOOST_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception ); From 96d2432c03520e02cb5e188f3fa5308c4d8e4e7c Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 19 Jun 2015 16:36:37 -0400 Subject: [PATCH 065/354] Tweak to database_fixture::borrow --- tests/common/database_fixture.cpp | 1 - tests/common/database_fixture.hpp | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e4912fa0..96b50c9c 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -718,7 +718,6 @@ void database_fixture::force_settle( const account_object& who, asset what ) void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price ) { try { - asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id ); trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.clear(); trx.operations.push_back( call_order_update_operation({ asset(), who.id, collateral, what, call_price }));; diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 9f308fec..edbead03 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -147,12 +147,14 @@ struct database_fixture { key_id_type key = key_id_type() ); - void force_global_settle( const asset_object& what, const price& p ); - void force_settle( const account_object& who, asset what ); - void update_feed_producers( const asset_object& mia, flat_set producers ); - void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f ); - void borrow( const account_object& who, asset what, asset collateral, price call_price = price()); - void cover( const account_object& who, asset what, asset collateral_freed, price call_price = price()); + void force_global_settle(const asset_object& what, const price& p); + void force_settle(const account_object& who, asset what); + void update_feed_producers(const asset_object& mia, flat_set producers); + void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f); + void borrow(account_id_type who, asset what, asset collateral, price call_price = price()) + { borrow(who(db), what, collateral, call_price); } + void borrow(const account_object& who, asset what, asset collateral, price call_price); + void cover(const account_object& who, asset what, asset collateral_freed, price call_price = price()); const asset_object& get_asset( const string& symbol )const; const account_object& get_account( const string& name )const; From 08bf4ec3fea2a54fb9367c043e76b4c9df56ce96 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 19 Jun 2015 18:49:32 -0400 Subject: [PATCH 066/354] progress --- libraries/chain/asset.cpp | 19 ++++++++++++++++++- libraries/chain/call_order_evaluator.cpp | 8 ++++---- libraries/chain/db_market.cpp | 7 +++---- .../chain/include/graphene/chain/asset.hpp | 2 +- .../graphene/chain/call_order_object.hpp | 2 +- tests/tests/operation_tests.cpp | 2 ++ 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index ef73dd73..80874c72 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -100,6 +100,20 @@ namespace graphene { namespace chain { price price::max( asset_id_type base, asset_id_type quote ) { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); } price price::min( asset_id_type base, asset_id_type quote ) { return asset( 1, base ) / asset( GRAPHENE_MAX_SHARE_SUPPLY, quote); } + /** + * The call price is defined so that the collateral is able to purchase debt * collateral ratio. + * + * Give a margin order with @ref debt and @ref collateral we can infer the market price that + * would be necessary to maintain the invariant that the collateral can purchase debt * collateral ratio + * + * (debt / collateral_ratio) / collateral == fair market price + * + * Stated another way: + * + * C * R / D + * + * This method only works if attempting to calculate the call price from a margin position. + */ price price::call_price(const asset& debt, const asset& collateral, uint16_t collateral_ratio) { try { fc::uint128 tmp( collateral.amount.value ); @@ -125,7 +139,8 @@ namespace graphene { namespace chain { FC_ASSERT( maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); - FC_ASSERT( maintenance_collateral_ratio <= maximum_short_squeeze_ratio ); + FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); + //FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio ); } FC_CAPTURE_AND_RETHROW( (*this) ) } price price_feed::max_short_squeeze_price()const @@ -138,6 +153,7 @@ namespace graphene { namespace chain { collateral.amount = tmp.to_uint64(); return settlement_price.base / collateral; } + /* price price_feed::maintenance_price()const { asset collateral = settlement_price.quote; @@ -148,6 +164,7 @@ namespace graphene { namespace chain { collateral.amount = tmp.to_uint64(); return settlement_price.base / collateral; } + */ } } // graphene::chain diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index e6709695..517473d4 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -106,7 +106,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat call.borrower = o.funding_account; call.collateral = o.delta_collateral.amount; call.debt = o.delta_debt.amount; - call.call_price = price::call_price(o.delta_debt, o.delta_collateral, + call.call_price = ~price::call_price(o.delta_debt, o.delta_collateral, _bitasset_data->current_feed.maintenance_collateral_ratio); }); } @@ -117,7 +117,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat d.modify( *call_obj, [&]( call_order_object& call ){ call.collateral += o.delta_collateral.amount; call.debt += o.delta_debt.amount; - call.call_price = price::call_price(call.get_debt(), call.get_collateral(), + call.call_price = ~price::call_price(call.get_debt(), call.get_collateral(), _bitasset_data->current_feed.maintenance_collateral_ratio); }); } @@ -136,9 +136,9 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat if( !_bitasset_data->is_prediction_market ) { // Check that the order's debt per collateral is less than the system's minimum debt per collateral. - FC_ASSERT( ~call_obj->call_price < _bitasset_data->current_feed.maintenance_price(), + FC_ASSERT( ~call_obj->call_price <= _bitasset_data->current_feed.settlement_price, "Insufficient collateral for debt.", - ("a", call_obj->call_price)("b", _bitasset_data->current_feed.maintenance_price())); + ("a", ~call_obj->call_price)("b", _bitasset_data->current_feed.settlement_price)); auto call_order_id = call_obj->id; diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index b9b3bb7c..1cc21373 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -334,9 +334,9 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa // stop when limit orders are selling too little USD for too much CORE auto min_price = bitasset.current_feed.max_short_squeeze_price(); // edump((bitasset.current_feed)); - // edump((min_price.to_real())(min_price)); + edump((min_price.to_real())(min_price)); + edump((max_price.to_real())(max_price)); //auto min_price = price::min( mia.id, bitasset.options.short_backing_asset ); -/* idump((bitasset.current_feed.settlement_price)(bitasset.current_feed.settlement_price.to_real())); { for( const auto& order : limit_price_index ) @@ -350,10 +350,9 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa wdump((max_price)(max_price.to_real())); wdump((min_price)(min_price.to_real())); } - */ assert( max_price.base.asset_id == min_price.base.asset_id ); - // wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); + wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index 576768de..d5f12cba 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -157,8 +157,8 @@ namespace graphene { namespace chain { * debt * maintenance_price() < collateral * debt * settlement_price < debt * maintenance * debt * maintenance_price() < debt * max_short_squeeze_price() - */ price maintenance_price()const; + */ /** When selling collateral to pay off debt, the least amount of debt to receive should be * min_usd = max_short_squeeze_price() * collateral diff --git a/libraries/chain/include/graphene/chain/call_order_object.hpp b/libraries/chain/include/graphene/chain/call_order_object.hpp index 67fae188..ea00217e 100644 --- a/libraries/chain/include/graphene/chain/call_order_object.hpp +++ b/libraries/chain/include/graphene/chain/call_order_object.hpp @@ -48,7 +48,7 @@ namespace graphene { namespace chain { account_id_type borrower; share_type collateral; ///< call_price.base.asset_id, access via get_collateral share_type debt; ///< call_price.quote.asset_id, access via get_collateral - price call_price; + price call_price; ///< Debt / Collateral }; /** diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 836726ec..a1b57a8a 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -47,12 +47,14 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) price_feed feed; feed.settlement_price = usd / core; + /* wdump((feed.settlement_price.to_real())); wdump((feed.maintenance_price().to_real())); wdump((feed.max_short_squeeze_price().to_real())); BOOST_CHECK( usd * feed.settlement_price < usd * feed.maintenance_price() ); BOOST_CHECK( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() ); + */ } catch (fc::exception& e) { edump((e.to_detail_string())); From dac9057b1216cbc58d2c6b2a36520fc53c2aaabe Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Fri, 19 Jun 2015 19:26:29 -0400 Subject: [PATCH 067/354] Add missing commands for asset features to wallet --- libraries/fc | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 49 +++- libraries/wallet/wallet.cpp | 254 +++++++++++++++++- 3 files changed, 295 insertions(+), 10 deletions(-) diff --git a/libraries/fc b/libraries/fc index dde8ed9d..dd1c77b3 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00 +Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 5a674350..38a473ea 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -208,7 +208,7 @@ class wallet_api * given amount of collateral */ signed_transaction borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, - string amount_of_collateral, bool broadcast = false); + string amount_of_collateral, bool broadcast = false); signed_transaction create_asset(string issuer, string symbol, @@ -218,8 +218,45 @@ class wallet_api bool broadcast = false); signed_transaction issue_asset(string to_account, string amount, + string symbol, + string memo, + bool broadcast = false); + + signed_transaction update_asset(string symbol, + optional new_issuer, + asset_object::asset_options new_options, + bool broadcast = false); + + signed_transaction update_bitasset(string symbol, + asset_object::bitasset_options new_options, + bool broadcast = false); + + signed_transaction update_asset_feed_producers(string symbol, + flat_set new_feed_producers, + bool broadcast = false); + + signed_transaction publish_asset_feed(string publishing_account, + string symbol, + price_feed feed, + bool broadcast = false); + + signed_transaction fund_asset_fee_pool(string from, + string symbol, + string amount, + bool broadcast = false); + + signed_transaction burn_asset(string from, + string amount, + string symbol, + bool broadcast = false); + + signed_transaction global_settle_asset(string symbol, + price settle_price, + bool broadcast = false); + + signed_transaction settle_asset(string account_to_settle, + string amount_to_settle, string symbol, - string memo, bool broadcast = false); signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); @@ -278,9 +315,17 @@ FC_API( graphene::wallet::wallet_api, (borrow_asset) (transfer) (create_asset) + (update_asset) + (update_bitasset) + (update_asset_feed_producers) + (publish_asset_feed) (issue_asset) (get_asset) (get_bitasset_data) + (fund_asset_fee_pool) + (burn_asset) + (global_settle_asset) + (settle_asset) (get_account) (get_account_id) (get_block) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index b041ae1d..3060b725 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Cryptonomex, Inc. + * Copyright (c) 2015, Cryptonomex, Incn * All rights reserved. * * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and @@ -286,14 +286,12 @@ class wallet_api_impl #ifdef __unix__ _old_umask = umask( S_IRWXG | S_IRWXO ); #endif - return; } void disable_umask_protection() { #ifdef __unix__ umask( _old_umask ); #endif - return; } map _builder_transactions; @@ -311,7 +309,6 @@ public: { fc::async([this]{resync();}, "Resync after block"); }, {dynamic_global_property_id_type()} ); - return; } virtual ~wallet_api_impl() { @@ -594,7 +591,6 @@ public: disable_umask_protection(); throw; } - return; } transaction_handle_type begin_builder_transaction() @@ -900,6 +896,190 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) } + signed_transaction update_asset(string symbol, + optional new_issuer, + asset_object::asset_options new_options, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + optional new_issuer_account_id; + if (new_issuer) + { + account_object new_issuer_account = get_account(*new_issuer); + new_issuer_account_id = new_issuer_account.id; + } + + asset_update_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_issuer = new_issuer_account_id; + update_op.new_options = new_options; + + signed_transaction tx; + tx.operations.push_back( update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(new_options)(broadcast) ) } + + signed_transaction update_bitasset(string symbol, + asset_object::bitasset_options new_options, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_bitasset_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_options = new_options; + + signed_transaction tx; + tx.operations.push_back( update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (symbol)(new_options)(broadcast) ) } + + signed_transaction update_asset_feed_producers(string symbol, + flat_set new_feed_producers, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_feed_producers_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_feed_producers.reserve(new_feed_producers.size()); + std::transform(new_feed_producers.begin(), new_feed_producers.end(), + std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()), + [this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); }); + + signed_transaction tx; + tx.operations.push_back( update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (symbol)(new_feed_producers)(broadcast) ) } + + signed_transaction publish_asset_feed(string publishing_account, + string symbol, + price_feed feed, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_publish_feed_operation publish_op; + publish_op.publisher = get_account_id(publishing_account); + publish_op.asset_id = asset_to_update->id; + publish_op.feed = feed; + + signed_transaction tx; + tx.operations.push_back( publish_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) } + + signed_transaction fund_asset_fee_pool(string from, + string symbol, + string amount, + bool broadcast /* = false */) + { try { + account_object from_account = get_account(from); + optional asset_to_fund = find_asset(symbol); + if (!asset_to_fund) + FC_THROW("No asset with that symbol exists!"); + asset_object core_asset = get_asset(asset_id_type()); + + asset_fund_fee_pool_operation fund_op; + fund_op.from_account = from_account.id; + fund_op.asset_id = asset_to_fund->id; + fund_op.amount = core_asset.amount_from_string(amount).amount; + + signed_transaction tx; + tx.operations.push_back( fund_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) } + + signed_transaction burn_asset(string from, + string amount, + string symbol, + bool broadcast /* = false */) + { try { + account_object from_account = get_account(from); + optional asset_to_burn = find_asset(symbol); + if (!asset_to_burn) + FC_THROW("No asset with that symbol exists!"); + + asset_burn_operation burn_op; + burn_op.payer = from_account.id; + burn_op.amount_to_burn = asset_to_burn->amount_from_string(amount); + + signed_transaction tx; + tx.operations.push_back( burn_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) } + + signed_transaction global_settle_asset(string symbol, + price settle_price, + bool broadcast /* = false */) + { try { + optional asset_to_settle = find_asset(symbol); + if (!asset_to_settle) + FC_THROW("No asset with that symbol exists!"); + + asset_global_settle_operation settle_op; + settle_op.issuer = asset_to_settle->issuer; + settle_op.asset_to_settle = asset_to_settle->id; + settle_op.settle_price = settle_price; + + signed_transaction tx; + tx.operations.push_back( settle_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) } + + signed_transaction settle_asset(string account_to_settle, + string amount_to_settle, + string symbol, + bool broadcast /* = false */) + { try { + optional asset_to_settle = find_asset(symbol); + if (!asset_to_settle) + FC_THROW("No asset with that symbol exists!"); + + asset_settle_operation settle_op; + settle_op.account = get_account_id(account_to_settle); + settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle); + + signed_transaction tx; + tx.operations.push_back( settle_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) } + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set req_active_approvals; @@ -1153,7 +1333,7 @@ public: }); std::stringstream ss; - for( int i = 0; i < asset_recs.size(); ++i ) + for( unsigned i = 0; i < asset_recs.size(); ++i ) ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; return ss.str(); @@ -1533,10 +1713,70 @@ signed_transaction wallet_api::create_asset(string issuer, return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast); } +signed_transaction wallet_api::update_asset(string symbol, + optional new_issuer, + asset_object::asset_options new_options, + bool broadcast /* = false */) +{ + return my->update_asset(symbol, new_issuer, new_options, broadcast); +} + +signed_transaction wallet_api::update_bitasset(string symbol, + asset_object::bitasset_options new_options, + bool broadcast /* = false */) +{ + return my->update_bitasset(symbol, new_options, broadcast); +} + +signed_transaction wallet_api::update_asset_feed_producers(string symbol, + flat_set new_feed_producers, + bool broadcast /* = false */) +{ + return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast); +} + +signed_transaction wallet_api::publish_asset_feed(string publishing_account, + string symbol, + price_feed feed, + bool broadcast /* = false */) +{ + return my->publish_asset_feed(publishing_account, symbol, feed, broadcast); +} + +signed_transaction wallet_api::fund_asset_fee_pool(string from, + string symbol, + string amount, + bool broadcast /* = false */) +{ + return my->fund_asset_fee_pool(from, symbol, amount, broadcast); +} + +signed_transaction wallet_api::burn_asset(string from, + string amount, + string symbol, + bool broadcast /* = false */) +{ + return my->fund_asset_fee_pool(from, amount, symbol, broadcast); +} + +signed_transaction wallet_api::global_settle_asset(string symbol, + price settle_price, + bool broadcast /* = false */) +{ + return global_settle_asset(symbol, settle_price, broadcast); +} + +signed_transaction wallet_api::settle_asset(string account_to_settle, + string amount_to_settle, + string symbol, + bool broadcast /* = false */) +{ + return settle_asset(account_to_settle, amount_to_settle, symbol); +} + void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = wallet_filename; - return; } signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */) From 1acb93472a6ec5eca5114bfaf1b1c8864183d598 Mon Sep 17 00:00:00 2001 From: Chronos Date: Sat, 20 Jun 2015 14:03:59 -0700 Subject: [PATCH 068/354] added wallet files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d5de0097..e27fbeef 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ doxygen wallet.json witness_node_data_dir + +*.wallet From 1b9ca9ffe8c546fd274258b4f2a411268f3d4be5 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Sat, 20 Jun 2015 18:33:58 -0400 Subject: [PATCH 069/354] Add wallet command for whitelisting accounts. Re-enable shutdown in node code which was causing some (not all) of the errors in two_node_network test #55, and likely occasional crashes at shutdown for the witness node --- libraries/net/node.cpp | 4 +-- .../wallet/include/graphene/wallet/wallet.hpp | 5 ++++ libraries/wallet/wallet.cpp | 30 +++++++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 998c480d..a0570f1b 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -4930,9 +4930,7 @@ namespace graphene { namespace net { namespace detail { void node::close() { - wlog( ".... WARNING NOT DOING ANYTHING WHEN I SHOULD ......" ); - return; - my->close(); + INVOKE_IN_IMPL(close); } struct simulated_network::node_info diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 38a473ea..9507165c 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -259,6 +259,11 @@ class wallet_api string symbol, bool broadcast = false); + signed_transaction whitelist_account(string authorizing_account, + string account_to_list, + account_whitelist_operation::account_listing new_listing_status, + bool broadcast = false); + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); void dbg_make_uia(string creator, string symbol); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3060b725..0c4e129b 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1080,6 +1080,24 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) } + signed_transaction whitelist_account(string authorizing_account, + string account_to_list, + account_whitelist_operation::account_listing new_listing_status, + bool broadcast /* = false */) + { try { + account_whitelist_operation whitelist_op; + whitelist_op.authorizing_account = get_account_id(authorizing_account); + whitelist_op.account_to_list = get_account_id(account_to_list); + whitelist_op.new_listing = new_listing_status; + + signed_transaction tx; + tx.operations.push_back( whitelist_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set req_active_approvals; @@ -1763,7 +1781,7 @@ signed_transaction wallet_api::global_settle_asset(string symbol, price settle_price, bool broadcast /* = false */) { - return global_settle_asset(symbol, settle_price, broadcast); + return my->global_settle_asset(symbol, settle_price, broadcast); } signed_transaction wallet_api::settle_asset(string account_to_settle, @@ -1771,7 +1789,15 @@ signed_transaction wallet_api::settle_asset(string account_to_settle, string symbol, bool broadcast /* = false */) { - return settle_asset(account_to_settle, amount_to_settle, symbol); + return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast); +} + +signed_transaction wallet_api::whitelist_account(string authorizing_account, + string account_to_list, + account_whitelist_operation::account_listing new_listing_status, + bool broadcast /* = false */) +{ + return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } void wallet_api::set_wallet_filename(string wallet_filename) From d1247af534ae483cfa011cf1f924984ff8fe313d Mon Sep 17 00:00:00 2001 From: ChronosCrypto Date: Sun, 21 Jun 2015 08:33:47 -0700 Subject: [PATCH 070/354] Updated readme with new private key --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2121b05..e07e217a 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Witness node The role of the witness node is to broadcast transactions, download blocks, and optionally sign them. -./witness_node --rpc-endpoint "127.0.0.1:8090" --enable-stale-production -w \""1.7.0"\" \""1.7.1"\" \""1.7.2"\" \""1.7.3"\" \""1.7.4"\" --private-key "[\"1.2.0\",\"aeebad4a796fcc2e15dc4c6061b45ed9b373f26adfc798ca7d2d8cc58182718e\"]" +./witness_node --rpc-endpoint "127.0.0.1:8090" --enable-stale-production -w \""1.7.0"\" \""1.7.1"\" \""1.7.2"\" \""1.7.3"\" \""1.7.4"\" --private-key "[\"1.2.0\",\"d2653ff7cbb2d8ff129ac27ef5781ce68b2558c41a74af1f2ddca635cbeef07d\"]" Running specific tests ---------------------- From 177950b791068c4e625b032ef3c9c88c5b5b5562 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sun, 21 Jun 2015 19:37:14 -0400 Subject: [PATCH 071/354] Fix margin call price calculation --- libraries/chain/asset.cpp | 23 +++++++++-------------- libraries/chain/call_order_evaluator.cpp | 4 ++-- libraries/chain/db_market.cpp | 2 ++ libraries/fc | 2 +- tests/tests/operation_tests.cpp | 12 ++++++++++-- 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index 80874c72..22a2dd6a 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -99,26 +99,21 @@ namespace graphene { namespace chain { } price price::max( asset_id_type base, asset_id_type quote ) { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); } price price::min( asset_id_type base, asset_id_type quote ) { return asset( 1, base ) / asset( GRAPHENE_MAX_SHARE_SUPPLY, quote); } - + /** - * The call price is defined so that the collateral is able to purchase debt * collateral ratio. + * The black swan price is defined as debt/collateral, we want to perform a margin call + * before debt == collateral. Given a debt/collateral ratio of 1 USD / CORE and + * a maintenance collateral requirement of 2x we can define the call price to be + * 2 USD / CORE. * - * Give a margin order with @ref debt and @ref collateral we can infer the market price that - * would be necessary to maintain the invariant that the collateral can purchase debt * collateral ratio - * - * (debt / collateral_ratio) / collateral == fair market price - * - * Stated another way: - * - * C * R / D - * - * This method only works if attempting to calculate the call price from a margin position. + * This method divides the collateral by the maintenance collateral ratio to derive + * a call price for the given black swan ratio. */ price price::call_price(const asset& debt, const asset& collateral, uint16_t collateral_ratio) { try { fc::uint128 tmp( collateral.amount.value ); - tmp *= collateral_ratio - 1000; - tmp /= 1000; + tmp *= 1000; + tmp /= collateral_ratio; FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); return asset( tmp.to_uint64(), collateral.asset_id) / debt; } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) } diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index 517473d4..b508d49f 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -106,7 +106,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat call.borrower = o.funding_account; call.collateral = o.delta_collateral.amount; call.debt = o.delta_debt.amount; - call.call_price = ~price::call_price(o.delta_debt, o.delta_collateral, + call.call_price = price::call_price(o.delta_debt, o.delta_collateral, _bitasset_data->current_feed.maintenance_collateral_ratio); }); } @@ -117,7 +117,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat d.modify( *call_obj, [&]( call_order_object& call ){ call.collateral += o.delta_collateral.amount; call.debt += o.delta_debt.amount; - call.call_price = ~price::call_price(call.get_debt(), call.get_collateral(), + call.call_price = price::call_price(call.get_debt(), call.get_collateral(), _bitasset_data->current_feed.maintenance_collateral_ratio); }); } diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 1cc21373..da0013d7 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -333,6 +333,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); // stop when limit orders are selling too little USD for too much CORE auto min_price = bitasset.current_feed.max_short_squeeze_price(); + /* // edump((bitasset.current_feed)); edump((min_price.to_real())(min_price)); edump((max_price.to_real())(max_price)); @@ -350,6 +351,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa wdump((max_price)(max_price.to_real())); wdump((min_price)(min_price.to_real())); } + */ assert( max_price.base.asset_id == min_price.base.asset_id ); wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); diff --git a/libraries/fc b/libraries/fc index dde8ed9d..c09035db 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00 +Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index a1b57a8a..800fe906 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -42,11 +42,19 @@ BOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture ) BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) { try { - asset usd(100,1); - asset core(100,0); + asset usd(1000,1); + asset core(1000,0); price_feed feed; feed.settlement_price = usd / core; + // require 3x min collateral + auto swanp = usd / core; + auto callp = ~price::call_price( usd, core, 1750 ); + // 1:1 collateral + wdump((callp.to_real())(callp)); + wdump((swanp.to_real())(swanp)); + FC_ASSERT( callp.to_real() > swanp.to_real() ); + /* wdump((feed.settlement_price.to_real())); wdump((feed.maintenance_price().to_real())); From df60efc83ce8a2ed0638a065c3cef34217fce604 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 22 Jun 2015 13:15:11 -0400 Subject: [PATCH 072/354] Update fc submodule --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index dd1c77b3..a8b85f6d 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 +Subproject commit a8b85f6dcc4558b7a9913b59ef5dc19f3b5e62ca From e814c4dbeb3ac6a7780ac9aa994c30ac54a465c0 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Jun 2015 19:07:51 -0400 Subject: [PATCH 073/354] type_id.cpp: Define space_id and type_id for all object types --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/type_id.cpp | 84 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 libraries/chain/type_id.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index dea3be7e..2f913c8d 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp") ## SORT .cpp by most likely to change / break compile add_library( graphene_chain types.cpp + type_id.cpp address.cpp asset.cpp diff --git a/libraries/chain/type_id.cpp b/libraries/chain/type_id.cpp new file mode 100644 index 00000000..3b698688 --- /dev/null +++ b/libraries/chain/type_id.cpp @@ -0,0 +1,84 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + +// C++ requires that static class variables declared and initialized +// in headers must also have a definition in a single source file, +// else linker errors will occur [1]. +// +// The purpose of this source file is to collect such definitions in +// a single place. +// +// [1] http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char + +const uint8_t account_object::space_id; +const uint8_t account_object::type_id; + +const uint8_t asset_object::space_id; +const uint8_t asset_object::type_id; + +const uint8_t block_summary_object::space_id; +const uint8_t block_summary_object::type_id; + +const uint8_t call_order_object::space_id; +const uint8_t call_order_object::type_id; + +const uint8_t delegate_object::space_id; +const uint8_t delegate_object::type_id; + +const uint8_t force_settlement_object::space_id; +const uint8_t force_settlement_object::type_id; + +const uint8_t global_property_object::space_id; +const uint8_t global_property_object::type_id; + +const uint8_t key_object::space_id; +const uint8_t key_object::type_id; + +const uint8_t limit_order_object::space_id; +const uint8_t limit_order_object::type_id; + +const uint8_t operation_history_object::space_id; +const uint8_t operation_history_object::type_id; + +const uint8_t proposal_object::space_id; +const uint8_t proposal_object::type_id; + +const uint8_t short_order_object::space_id; +const uint8_t short_order_object::type_id; + +const uint8_t transaction_object::space_id; +const uint8_t transaction_object::type_id; + +const uint8_t vesting_balance_object::space_id; +const uint8_t vesting_balance_object::type_id; + +const uint8_t withdraw_permission_object::space_id; +const uint8_t withdraw_permission_object::type_id; + +const uint8_t witness_object::space_id; +const uint8_t witness_object::type_id; + +const uint8_t witness_schedule_object::space_id; +const uint8_t witness_schedule_object::type_id; + +const uint8_t worker_object::space_id; +const uint8_t worker_object::type_id; + +} } From e934b9cf44d4f63143c4a95c76757323a88ac0c1 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Jun 2015 19:31:38 -0400 Subject: [PATCH 074/354] key_object.hpp: Move address_or_key variant to header, reflect it --- libraries/chain/include/graphene/chain/key_object.hpp | 5 ++++- libraries/chain/key_object.cpp | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/key_object.hpp b/libraries/chain/include/graphene/chain/key_object.hpp index 3a9d07da..7daaa5ab 100644 --- a/libraries/chain/include/graphene/chain/key_object.hpp +++ b/libraries/chain/include/graphene/chain/key_object.hpp @@ -22,6 +22,8 @@ #include namespace graphene { namespace chain { + typedef static_variant address_or_key; + /** * @class key_object * @brief maps an ID to a public key or address @@ -38,8 +40,9 @@ namespace graphene { namespace chain { address key_address()const; const public_key_type& key()const { return key_data.get(); } - static_variant key_data; + address_or_key key_data; }; } } +FC_REFLECT_TYPENAME( graphene::chain::address_or_key ) FC_REFLECT_DERIVED( graphene::chain::key_object, (graphene::db::object), (key_data) ) diff --git a/libraries/chain/key_object.cpp b/libraries/chain/key_object.cpp index a0a79fe8..d19eabba 100644 --- a/libraries/chain/key_object.cpp +++ b/libraries/chain/key_object.cpp @@ -21,8 +21,6 @@ namespace graphene { namespace chain { address key_object::key_address()const { - typedef static_variant address_or_key; - switch( key_data.which() ) { case address_or_key::tag
::value: From 9e46ed80a016e143f42e4663d134541b996ff088 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Jun 2015 19:39:32 -0400 Subject: [PATCH 075/354] Reflect many more types --- libraries/chain/include/graphene/chain/operations.hpp | 1 + libraries/chain/include/graphene/chain/types.hpp | 2 ++ .../include/graphene/chain/vesting_balance_object.hpp | 2 ++ libraries/chain/include/graphene/chain/worker_object.hpp | 2 ++ libraries/db/include/graphene/db/object.hpp | 7 ++++--- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 6e9744ac..a6ea5f09 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1571,4 +1571,5 @@ FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)( FC_REFLECT( graphene::chain::void_result, ) FC_REFLECT_TYPENAME( graphene::chain::operation ) +FC_REFLECT_TYPENAME( graphene::chain::operation_result ) FC_REFLECT_TYPENAME( fc::flat_set ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index a4d2b089..828c344e 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -576,6 +576,8 @@ FC_REFLECT( graphene::chain::chain_parameters, (worker_budget_per_day) ) +FC_REFLECT_TYPENAME( graphene::chain::share_type ) + FC_REFLECT_TYPENAME( graphene::chain::key_id_type ) FC_REFLECT_TYPENAME( graphene::chain::account_id_type ) FC_REFLECT_TYPENAME( graphene::chain::asset_id_type ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 3d6ff6fb..22c10b67 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -149,6 +149,8 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy, (coin_seconds_earned_last_update) ) +FC_REFLECT_TYPENAME( graphene::chain::vesting_policy ) + FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object), (owner) (balance) diff --git a/libraries/chain/include/graphene/chain/worker_object.hpp b/libraries/chain/include/graphene/chain/worker_object.hpp index 5306d41d..fe9fe842 100644 --- a/libraries/chain/include/graphene/chain/worker_object.hpp +++ b/libraries/chain/include/graphene/chain/worker_object.hpp @@ -188,6 +188,8 @@ FC_REFLECT( graphene::chain::refund_worker_type, (total_burned) ) FC_REFLECT( graphene::chain::refund_worker_type::initializer, ) FC_REFLECT( graphene::chain::vesting_balance_worker_type, (balance) ) FC_REFLECT( graphene::chain::vesting_balance_worker_type::initializer, (pay_vesting_period_days) ) +FC_REFLECT_TYPENAME( graphene::chain::worker_type ) +FC_REFLECT_TYPENAME( graphene::chain::worker_initializer ) FC_REFLECT_DERIVED( graphene::chain::worker_object, (graphene::db::object), (worker_account) (work_begin_date) diff --git a/libraries/db/include/graphene/db/object.hpp b/libraries/db/include/graphene/db/object.hpp index 81458477..16ff3fba 100644 --- a/libraries/db/include/graphene/db/object.hpp +++ b/libraries/db/include/graphene/db/object.hpp @@ -93,6 +93,8 @@ namespace graphene { namespace db { virtual vector pack()const { return fc::raw::pack( static_cast(*this) ); } }; + typedef flat_map annotation_map; + /** * @class annotated_object * @brief An object that is easily extended by providing pointers to other objects, one for each space. @@ -117,12 +119,11 @@ namespace graphene { namespace db { * Annotations should be accessed via get_annotation and set_annotation so * that they can be maintained in sorted order. */ - flat_map annotations; + annotation_map annotations; }; } } // graphene::db +FC_REFLECT_TYPENAME( graphene::db::annotation_map ) FC_REFLECT( graphene::db::object, (id) ) FC_REFLECT_DERIVED_TEMPLATE( (typename Derived), graphene::db::annotated_object, (graphene::db::object), (annotations) ) - - From e9acc40a7ddb4580b7defed77f0aec071bbc1ade Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Jun 2015 19:46:11 -0400 Subject: [PATCH 076/354] operation_history_object.hpp: Include operations.hpp --- .../chain/include/graphene/chain/operation_history_object.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/include/graphene/chain/operation_history_object.hpp b/libraries/chain/include/graphene/chain/operation_history_object.hpp index 78c8f16a..9f5d6e9e 100644 --- a/libraries/chain/include/graphene/chain/operation_history_object.hpp +++ b/libraries/chain/include/graphene/chain/operation_history_object.hpp @@ -17,6 +17,7 @@ */ #pragma once #include +#include namespace graphene { namespace chain { From a2b9bc6d277e4e23f26981cb30dc82d2d88d4543 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Jun 2015 20:38:51 -0400 Subject: [PATCH 077/354] object_database.hpp: Implement template methods for downcasting objects --- .../db/include/graphene/db/object_database.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/db/include/graphene/db/object_database.hpp b/libraries/db/include/graphene/db/object_database.hpp index e070125e..ff4cc52d 100644 --- a/libraries/db/include/graphene/db/object_database.hpp +++ b/libraries/db/include/graphene/db/object_database.hpp @@ -87,6 +87,19 @@ namespace graphene { namespace db { ///@} + template + static const T& cast( const object& obj ) + { + assert( nullptr != dynamic_cast(&obj) ); + return static_cast(obj); + } + template + static T& cast( object& obj ) + { + assert( nullptr != dynamic_cast(&obj) ); + return static_cast(obj); + } + template const T& get( object_id_type id )const { From da77990b7243da15dda51a098aeb6889fe395847 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 19 Jun 2015 15:48:00 -0400 Subject: [PATCH 078/354] Implement field_reflector and assert_op --- libraries/chain/CMakeLists.txt | 25 ++ libraries/chain/assert_evaluator.cpp | 92 +++++++ libraries/chain/db_init.cpp | 2 + libraries/chain/db_reflect_cmp.tmpl | 43 ++++ libraries/chain/field_reflector.cpp | 233 ++++++++++++++++++ .../graphene/chain/assert_evaluator.hpp | 34 +++ .../chain/include/graphene/chain/config.hpp | 1 + .../chain/include/graphene/chain/database.hpp | 6 +- .../include/graphene/chain/db_reflect_cmp.hpp | 60 +++++ .../graphene/chain/db_reflect_cmp_impl.hpp | 115 +++++++++ .../include/graphene/chain/operations.hpp | 24 +- .../include/graphene/chain/predicate.hpp | 60 +++++ .../chain/include/graphene/chain/types.hpp | 4 + .../include/graphene/chain/wild_object.hpp | 61 +++++ libraries/chain/operations.cpp | 10 + libraries/chain/predicate.cpp | 34 +++ libraries/chain/type_id.cpp | 5 +- .../account_history_plugin.cpp | 3 + programs/field_reflector/CMakeLists.txt | 12 + 19 files changed, 817 insertions(+), 7 deletions(-) create mode 100644 libraries/chain/assert_evaluator.cpp create mode 100644 libraries/chain/db_reflect_cmp.tmpl create mode 100644 libraries/chain/field_reflector.cpp create mode 100644 libraries/chain/include/graphene/chain/assert_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/db_reflect_cmp.hpp create mode 100644 libraries/chain/include/graphene/chain/db_reflect_cmp_impl.hpp create mode 100644 libraries/chain/include/graphene/chain/predicate.hpp create mode 100644 libraries/chain/include/graphene/chain/wild_object.hpp create mode 100644 libraries/chain/predicate.cpp create mode 100644 programs/field_reflector/CMakeLists.txt diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 2f913c8d..2ec25bf7 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -1,17 +1,42 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp") +add_executable( field_reflector + field_reflector.cpp + types.cpp + type_id.cpp + address.cpp + key_object.cpp + ${HEADERS} ) + +target_include_directories( field_reflector + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +target_link_libraries( field_reflector fc graphene_db ) + +add_custom_command( OUTPUT db_reflect_cmp.cpp + COMMAND field_reflector db_reflect_cmp.tmpl db_reflect_cmp.cpp.new + COMMAND ${CMAKE_COMMAND} -E copy_if_different db_reflect_cmp.cpp.new db_reflect_cmp.cpp + COMMAND ${CMAKE_COMMAND} -E remove db_reflect_cmp.cpp.new + DEPENDS field_reflector db_reflect_cmp.tmpl + ) + ## SORT .cpp by most likely to change / break compile add_library( graphene_chain types.cpp type_id.cpp + + db_reflect_cmp.cpp + address.cpp asset.cpp + predicate.cpp operations.cpp evaluator.cpp global_parameters_evaluator.cpp account_evaluator.cpp + assert_evaluator.cpp witness_evaluator.cpp delegate_evaluator.cpp asset_evaluator.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp new file mode 100644 index 00000000..9979aa66 --- /dev/null +++ b/libraries/chain/assert_evaluator.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +namespace graphene { namespace chain { + +namespace detail { + +struct predicate_check_visitor +{ + predicate_check_visitor( const database& d ): _db(d){} + + typedef bool result_type; + template bool operator()( const Predicate& pred )const + { + return pred.check_predicate( _db ); + } + + const database& _db; +}; + +} // graphene::chain::detail + +void_result assert_evaluator::do_evaluate( const assert_operation& o ) +{ + const database& _db = db(); + uint32_t skip = _db.get_node_properties().skip_flags; + // TODO: Skip flags + if( skip & database::skip_assert_evaluation ) + return void_result(); + for( const vector& s_pred : o.predicates ) + { + std::istringstream is( string( s_pred.begin(), s_pred.end() ) ); + // de-serialize just the static_variant tag + unsigned_int t; + fc::raw::unpack( is, t ); + // everyone checks: delegates must have allocated an opcode for it + FC_ASSERT( t < _db.get_global_properties().parameters.max_predicate_opcode ); + if( t >= predicate::count() ) + { + // + // delegates allocated an opcode, but our client doesn't know + // the semantics (i.e. we are running an old client) + // + // skip_unknown_predicate indicates we're cool with assuming + // unknown predicates pass + // + if( skip & database::skip_unknown_predicate ) + continue; + // + // ok, unknown predicate must die + // + FC_ASSERT( false, "unknown predicate" ); + } + // rewind to beginning, unpack it, and check it + is.clear(); + is.seekg(0); + predicate pred; + fc::raw::unpack( is, pred ); + bool pred_passed = pred.visit( detail::predicate_check_visitor( _db ) ); + FC_ASSERT( pred_passed ); + } + return void_result(); +} + +void_result assert_evaluator::do_apply( const assert_operation& o ) +{ + // assert_operation is always a no-op + return void_result(); +} + +} } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 0f95fd1d..86e58690 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -73,6 +74,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_reflect_cmp.tmpl b/libraries/chain/db_reflect_cmp.tmpl new file mode 100644 index 00000000..c4522b10 --- /dev/null +++ b/libraries/chain/db_reflect_cmp.tmpl @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +${generated_file_banner} + +/* +object_descriptor = ${object_descriptor} +*/ + +#include +#include +#include + +#include + +namespace graphene { namespace chain { namespace impl { + +bool cmp_attr_impl( + const object& obj, + uint16_t field_num, + const vector& lit, + uint8_t opc + ) +{ +${cmp_attr_impl_body} +} + +} } } // graphene::chain::detail diff --git a/libraries/chain/field_reflector.cpp b/libraries/chain/field_reflector.cpp new file mode 100644 index 00000000..ee4b2799 --- /dev/null +++ b/libraries/chain/field_reflector.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace graphene::chain; + +fc::mutable_variant_object g_vo_object_types; +std::vector< fc::mutable_variant_object > g_vo_fields; + +struct serialize_object_type_member_visitor +{ + public: + template + void operator()( const char* name )const + { + fc::mutable_variant_object vo; + vo["name"] = name; + vo["type"] = fc::get_typename::name(); + vo["id"] = g_vo_fields.size(); + g_vo_fields.push_back( vo ); + } +}; + +struct serialize_object_type_visitor +{ + typedef void result_type; + + int t = 0; + serialize_object_type_visitor(int _t ):t(_t){} + + template + result_type operator()( const Type& op )const + { + fc::mutable_variant_object vo; + vo["space_id"] = Type::space_id; + vo["type_id"] = Type::type_id; + g_vo_fields.clear(); + // visit all members + fc::reflector::visit( serialize_object_type_member_visitor() ); + + vo["fields"] = g_vo_fields; + g_vo_object_types[ fc::get_typename::name() ] = vo; + } +}; + +struct getattr_switch_table_entry +{ + uint32_t _switch_val; // (space << 24) | (type << 16) | fieldnum + string _object_typename; + string _field_typename; + string _field_name; +}; + +vector< getattr_switch_table_entry > build_switch_table() +{ + vector< getattr_switch_table_entry > result; + for( const auto& item : g_vo_object_types ) + { + const variant_object& vo = item.value().get_object(); + uint32_t top = (vo["space_id"].as_uint64() << 24) | (vo["type_id"].as_uint64() << 16); + for( const auto& field : vo["fields"].get_array() ) + { + getattr_switch_table_entry e; + e._switch_val = top | field["id"].as_uint64(); + e._object_typename = item.key(); + e._field_typename = field["type"].get_string(); + e._field_name = field["name"].get_string(); + result.push_back( e ); + } + } + + std::sort( result.begin(), result.end(), + []( const getattr_switch_table_entry& a, + const getattr_switch_table_entry& b ) + { + return a._switch_val < b._switch_val; + } ); + + return result; +} + +std::string generate_cmp_attr_impl( const vector< getattr_switch_table_entry >& switch_table ) +{ + std::ostringstream out; + + // switch( space ) + // switch( type ) + // switch( fieldnum ) + // switch( opc ) + + std::map< uint8_t, + std::map< uint8_t, + std::map< uint16_t, + const getattr_switch_table_entry* > > > index; + + for( const getattr_switch_table_entry& e : switch_table ) + { + uint8_t sp = (e._switch_val >> 24) & 0xFF; + uint8_t ty = (e._switch_val >> 16) & 0xFF; + uint16_t fn = (e._switch_val ) & 0xFFFF; + auto& i0 = index; + if( i0.find( sp ) == i0.end() ) + i0[sp] = std::map< uint8_t, std::map< uint16_t, const getattr_switch_table_entry* > >(); + auto& i1 = i0[sp]; + if( i1.find( ty ) == i1.end() ) + i1[ty] = std::map< uint16_t, const getattr_switch_table_entry* >(); + auto& i2 = i1[ty]; + i2[fn] = &e; + } + out << " switch( obj.id.space() )\n" + " {\n"; + for( const auto& e0 : index ) + { + out << " case " << int( e0.first ) << ":\n" + " switch( obj.id.type() )\n" + " {\n"; + for( const auto& e1 : e0.second ) + { + out << " case " << int( e1.first ) << ":\n" + " switch( field_num )\n" + " {\n"; + for( const auto& e2 : e1.second ) + { + const std::string& ft = e2.second->_field_typename; + const std::string& ot = e2.second->_object_typename; + const std::string& fn = e2.second->_field_name; + out << " case " << int( e2.first ) << ":\n" + " {\n" + " // " << ft + << " " << ot + << "." << fn + << "\n" + " const " << ft << "& dbval = object_database::cast< " << ot << " >( obj )." << fn << ";\n" + " return _cmp< " << ft << " >( dbval, lit, opc );\n" + " }\n"; + } + out << " default:\n" + " FC_ASSERT( false, \"unrecognized field_num\" );\n" + " }\n"; + } + out << " default:\n" + " FC_ASSERT( false, \"unrecognized object type\" );\n" + " }\n"; + } + out << " default:\n" + " FC_ASSERT( false, \"unrecognized object space\" );\n" + " }\n"; + + return out.str(); +} + +static const char generated_file_banner[] = +"// _ _ __ _ _ //\n" +"// | | | | / _(_) | //\n" +"// __ _ ___ _ __ ___ _ __ __ _| |_ ___ __| | | |_ _| | ___ //\n" +"// / _` |/ _ \\ '_ \\ / _ \\ '__/ _` | __/ _ \\/ _` | | _| | |/ _ \\ //\n" +"// | (_| | __/ | | | __/ | | (_| | || __/ (_| | | | | | | __/ //\n" +"// \\__, |\\___|_| |_|\\___|_| \\__,_|\\__\\___|\\__,_| |_| |_|_|\\___| //\n" +"// __/ | //\n" +"// |___/ //\n" +"// //\n" +"// Generated by: programs/field_reflector/main.cpp //\n" +"// //\n" +"// Warning: This is a generated file, any changes made here will be //\n" +"// overwritten by the build process. If you need to change what //\n" +"// is generated here, you should either modify the reflected //\n" +"// types, or modify the code generator itself. //\n" +"// //\n" +; + +int main( int argc, char** argv ) +{ + try + { + if( argc != 3 ) + { + std::cout << "syntax: " << argv[0] << " \n"; + return 1; + } + + graphene::chain::impl::wild_object wo; + + for( int32_t i = 0; i < wo.count(); ++i ) + { + wo.set_which(i); + wo.visit( serialize_object_type_visitor(i) ); + } + + vector< getattr_switch_table_entry > switch_table = build_switch_table(); + + fc::mutable_variant_object tmpl_params; + + tmpl_params["generated_file_banner"] = generated_file_banner; + tmpl_params["object_descriptor"] = fc::json::to_string( g_vo_object_types ); + tmpl_params["cmp_attr_impl_body"] = generate_cmp_attr_impl( switch_table ); + + std::ifstream template_file( argv[1] ); + std::stringstream ss; + ss << template_file.rdbuf(); + std::string result = fc::format_string( ss.str(), tmpl_params ); + std::ofstream result_file( argv[2] ); + result_file << result; + } + catch ( const fc::exception& e ){ edump((e.to_detail_string())); } + return 0; +} diff --git a/libraries/chain/include/graphene/chain/assert_evaluator.hpp b/libraries/chain/include/graphene/chain/assert_evaluator.hpp new file mode 100644 index 00000000..37ff998b --- /dev/null +++ b/libraries/chain/include/graphene/chain/assert_evaluator.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + + class assert_evaluator : public evaluator + { + public: + typedef assert_operation operation_type; + + void_result do_evaluate( const assert_operation& o ); + void_result do_apply( const assert_operation& o ); + }; + +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 7cb2db1c..e594a7f5 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -83,6 +83,7 @@ #define GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD (GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(100)) #define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT) #define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000) +#define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE 1 #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL // counter initialization values used to derive near and far future seeds for shuffling witnesses diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index caa909b2..0c036c95 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -109,7 +109,7 @@ namespace graphene { namespace chain { { skip_nothing = 0x00, skip_delegate_signature = 0x01, ///< used while reindexing - skip_transaction_signatures = 0x02, ///< used by non delegate nodes + skip_transaction_signatures = 0x02, ///< used by non-witness nodes skip_undo_block = 0x04, ///< used while reindexing skip_undo_transaction = 0x08, ///< used while applying block skip_transaction_dupe_check = 0x10, ///< used while reindexing @@ -117,7 +117,9 @@ namespace graphene { namespace chain { skip_block_size_check = 0x40, ///< used when applying locally generated transactions skip_tapos_check = 0x80, ///< used while reindexing -- note this skips expiration check as well skip_authority_check = 0x100, ///< used while reindexing -- disables any checking of authority on transactions - skip_merkle_check = 0x200 ///< used while reindexing + skip_merkle_check = 0x200, ///< used while reindexing + skip_assert_evaluation = 0x400, ///< used while reindexing + skip_unknown_predicate = 0x800 ///< used by non-witness nodes to allow unknown predicates }; void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type()); diff --git a/libraries/chain/include/graphene/chain/db_reflect_cmp.hpp b/libraries/chain/include/graphene/chain/db_reflect_cmp.hpp new file mode 100644 index 00000000..b18f49c3 --- /dev/null +++ b/libraries/chain/include/graphene/chain/db_reflect_cmp.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include + +namespace graphene { namespace chain { + +enum comparison_opcode +{ + opc_equal_to, + opc_not_equal_to, + opc_greater, + opc_less, + opc_greater_equal, + opc_less_equal +}; + +namespace impl { + +/** + * Compare the given field of the given object to the literal string. + * The comparison opcode is specified by opc, which should be one + * of comparison_opcode. + * + * The default behavior is to only allow opc_equal_to or + * opc_not_equal_to, and implement these based on comparing the + * serialized field to the given bytes. + * + * To override the default behavior, provide a template override + * for _cmp() in db_reflect_cmp_impl.hpp. Beware adding such an + * override will create a witness-only hardfork! + */ +bool cmp_attr_impl( + const graphene::db::object& obj, + uint16_t field_num, + const std::vector& lit, + uint8_t opc + ); +} + +} } diff --git a/libraries/chain/include/graphene/chain/db_reflect_cmp_impl.hpp b/libraries/chain/include/graphene/chain/db_reflect_cmp_impl.hpp new file mode 100644 index 00000000..68450e25 --- /dev/null +++ b/libraries/chain/include/graphene/chain/db_reflect_cmp_impl.hpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include +#include + +// +// This file implements the comparison used by the assert op. +// The entry point for comparison is _cmp(), it can be specialized for +// different types. The default implementation defers to _ser_eq_cmp() +// which only allows equality comparisons, and asserts +// + +namespace graphene { namespace chain { namespace impl { + +// useful for types which have all comparison ops implemented +template< typename T > +static bool _full_cmp( const T& a, const T& b, uint8_t opc ) +{ + switch( opc ) + { + case opc_equal_to: + return (a == b); + case opc_not_equal_to: + return (a != b); + case opc_greater: + return (a > b); + case opc_less: + return (a < b); + case opc_greater_equal: + return (a >= b); + case opc_less_equal: + return (a <= b); + default: + FC_ASSERT( false, "unknown comparison operator" ); + } +} + +// useful for types which have operator== implemented +template< typename T > +static bool _eq_cmp( const T& a, const T& b, uint8_t opc ) +{ + switch( opc ) + { + case opc_equal_to: + return (a == b); + case opc_not_equal_to: + return !(a == b); + default: + FC_ASSERT( false, "unknown comparison operator" ); + } +} + +// works for every serializable type +template< typename T > +static bool _ser_eq_cmp( const T& a, const std::vector& b, uint8_t opc ) +{ + std::vector< char > _a = fc::raw::pack( a ); + return _eq_cmp( _a, b, opc ); +} + +/* +static bool _cmp( const fc::sha224& a, const fc::sha224& b, uint8_t opc ) +{ + assert( a.data_size() == b.data_size() ); + int result = memcmp( a.data(), b.data(), a.data_size() ); + switch( opc ) + { + case opc_equal_to: + return (result == 0); + case opc_not_equal_to: + return (result != 0); + case opc_greater: + return (result > 0); + case opc_less: + return (result < 0); + case opc_greater_equal: + return (result >= 0); + case opc_less_equal: + return (result <= 0); + default: + FC_ASSERT( false, "unknown comparison operator" ); + } +} +*/ + +// _cmp needs to be specialized for types which don't have overloads +// for all comparison operators + +template< typename T > +static bool _cmp( const T& a, const std::vector& b, uint8_t opc ) +{ + return _ser_eq_cmp( a, b, opc ); +} + +} } } // graphene::chain::detail diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index a6ea5f09..f34af396 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -102,6 +102,25 @@ namespace graphene { namespace chain { * @{ */ + /** + * @brief assert that some conditions are true. + * @ingroup operations + */ + struct assert_operation + { + asset fee; + account_id_type fee_paying_account; + vector< vector< char > > predicates; + flat_set required_auths; + + account_id_type fee_payer()const { return fee_paying_account; } + void get_required_auth(flat_set& active_auth_set, flat_set&)const; + share_type calculate_fee( const fee_schedule_type& k )const{ return k.assert_op_fee; } + void validate()const; + + void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + }; + /** * @brief reserves a new ID to refer to a particular key or address. * @ingroup operations @@ -1339,7 +1358,8 @@ namespace graphene { namespace chain { vesting_balance_create_operation, vesting_balance_withdraw_operation, worker_create_operation, - custom_operation + custom_operation, + assert_operation > operation; /// @} // operations group @@ -1568,6 +1588,8 @@ FC_REFLECT( graphene::chain::worker_create_operation, (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(initializer) ) FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) ) +FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths) ) + FC_REFLECT( graphene::chain::void_result, ) FC_REFLECT_TYPENAME( graphene::chain::operation ) diff --git a/libraries/chain/include/graphene/chain/predicate.hpp b/libraries/chain/include/graphene/chain/predicate.hpp new file mode 100644 index 00000000..5980d090 --- /dev/null +++ b/libraries/chain/include/graphene/chain/predicate.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include + +namespace graphene { namespace chain { + +class database; + +class pred_field_lit_cmp +{ +public: + pred_field_lit_cmp( + object_id_type obj_id, + uint16_t field_num, + const vector& lit, + uint8_t opc + ) : + _obj_id( obj_id ), + _field_num( field_num ), + _lit( lit ), + _opc( opc ) + {} + pred_field_lit_cmp() {} // necessary for instantiating static_variant + ~pred_field_lit_cmp() {} + + bool check_predicate( const database& db )const; + + object_id_type _obj_id; + uint16_t _field_num; + vector _lit; + uint8_t _opc; +}; + +typedef static_variant< + pred_field_lit_cmp + > predicate; + +} } + +FC_REFLECT( graphene::chain::pred_field_lit_cmp, (_obj_id)(_field_num)(_lit)(_opc) ); diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 828c344e..c99e8ca9 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -375,6 +375,7 @@ namespace graphene { namespace chain { uint32_t global_settle_fee; uint32_t worker_create_fee; ///< the cost to create a new worker uint32_t worker_delete_fee; ///< the cost to delete a worker + uint32_t assert_op_fee; ///< fee per assert operation }; @@ -429,6 +430,7 @@ namespace graphene { namespace chain { bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block) share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day) + uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number void validate()const { @@ -548,6 +550,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (global_settle_fee) (worker_create_fee) (worker_delete_fee) + (assert_op_fee) ) FC_REFLECT( graphene::chain::chain_parameters, @@ -574,6 +577,7 @@ FC_REFLECT( graphene::chain::chain_parameters, (allow_non_member_whitelists) (witness_pay_per_block) (worker_budget_per_day) + (max_predicate_opcode) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/libraries/chain/include/graphene/chain/wild_object.hpp b/libraries/chain/include/graphene/chain/wild_object.hpp new file mode 100644 index 00000000..5b302dac --- /dev/null +++ b/libraries/chain/include/graphene/chain/wild_object.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { namespace impl { + +/** + * A static_variant of all object types. + * + * Used by field_reflector, this ultimately determines the object + * types which may be inspected by pred_field_lit_cmp. + */ +typedef fc::static_variant< + //null_object, + //base_object, + key_object, + account_object, + asset_object, + force_settlement_object, + delegate_object, + witness_object, + limit_order_object, + call_order_object, + //custom_object, + proposal_object, + operation_history_object, + withdraw_permission_object, + vesting_balance_object, + worker_object + > wild_object; + +} } } diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 73540f23..6fb26b4a 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -813,4 +813,14 @@ share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) return k.membership_annual_fee; } +void assert_operation::validate()const +{ + FC_ASSERT( fee.amount >= 0 ); +} +void assert_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +{ + active_auth_set.insert(fee_paying_account); + active_auth_set.insert(required_auths.begin(), required_auths.end()); +} + } } // namespace graphene::chain diff --git a/libraries/chain/predicate.cpp b/libraries/chain/predicate.cpp new file mode 100644 index 00000000..cedd6fdf --- /dev/null +++ b/libraries/chain/predicate.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include + +namespace graphene { namespace chain { + +bool pred_field_lit_cmp::check_predicate( const database& db )const +{ + return graphene::chain::impl::cmp_attr_impl( db.get_object( _obj_id ), _field_num, _lit, _opc ); +} + +} } // graphene::chain diff --git a/libraries/chain/type_id.cpp b/libraries/chain/type_id.cpp index 3b698688..7e1ee245 100644 --- a/libraries/chain/type_id.cpp +++ b/libraries/chain/type_id.cpp @@ -2,13 +2,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -60,9 +60,6 @@ const uint8_t operation_history_object::type_id; const uint8_t proposal_object::space_id; const uint8_t proposal_object::type_id; -const uint8_t short_order_object::space_id; -const uint8_t short_order_object::type_id; - const uint8_t transaction_object::space_id; const uint8_t transaction_object::type_id; diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 9c3d2ad1..13f28ee6 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -243,6 +243,9 @@ struct operation_get_impacted_accounts void operator()( const worker_create_operation& )const {} + + void operator()( const assert_operation& )const + {} }; account_create_observer::~account_create_observer() diff --git a/programs/field_reflector/CMakeLists.txt b/programs/field_reflector/CMakeLists.txt new file mode 100644 index 00000000..129ae28d --- /dev/null +++ b/programs/field_reflector/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable( field_reflector main.cpp ) +if( UNIX AND NOT APPLE ) + set(rt_library rt ) +endif() +#find_package( Gperftools QUIET ) +#if( GPERFTOOLS_FOUND ) +# message( STATUS "Found gperftools; compiling client with TCMalloc") +# list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) +#endif() + +target_link_libraries( field_reflector + PRIVATE graphene_app graphene_net graphene_chain graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) From 816ad4b7a660b7cb8cec81e3033c175c9995c5c9 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 19 Jun 2015 16:16:31 -0400 Subject: [PATCH 079/354] operation_tests2.cpp: Implement assert_op_test --- tests/tests/operation_tests2.cpp | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 5e35977d..e61c052b 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -29,6 +29,9 @@ #include #include +#include +#include + #include #include "../common/database_fixture.hpp" @@ -797,6 +800,47 @@ BOOST_AUTO_TEST_CASE( unimp_force_settlement_unavailable ) */ } +BOOST_AUTO_TEST_CASE( assert_op_test ) +{ + try { + // create some objects + auto nathan_private_key = generate_private_key("nathan"); + auto dan_private_key = generate_private_key("dan"); + public_key_type nathan_public_key = nathan_private_key.get_public_key(); + public_key_type dan_public_key = dan_private_key.get_public_key(); + key_id_type nathan_key_id = register_key(nathan_public_key).id; + key_id_type dan_key_id = register_key(dan_public_key).id; + account_id_type nathan_id = create_account("nathan", nathan_key_id).id; + + assert_operation op; + decltype( key_object::key_data ) lit_key = nathan_public_key; + + // nathan checks that his public key is equal to the given value. + op.fee_paying_account = nathan_id; + op.predicates = vector< vector< char > >(); + op.predicates.push_back( + fc::raw::pack( + predicate( + pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_equal_to ) + ) + ) ); + trx.operations.push_back(op); + trx.sign( nathan_key_id, nathan_private_key ); + PUSH_TX( db, trx ); + + // nathan checks that his public key is not equal to the given value (fail) + op.predicates.back() = + fc::raw::pack( + predicate( + pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_not_equal_to ) + ) + ); + trx.operations.back() = op; + trx.sign( nathan_key_id, nathan_private_key ); + BOOST_CHECK_THROW( PUSH_TX( db, trx ), fc::exception ); + } FC_LOG_AND_RETHROW() +} + // TODO: Write linear VBO tests BOOST_AUTO_TEST_SUITE_END() From 52a72cdd9f4c72b6909b3ee9f55ef01fedf17250 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 22 Jun 2015 14:56:40 -0400 Subject: [PATCH 080/354] Fix win64 compile errors (ambiguous comparisons between unsigned_int and uint16) --- libraries/chain/assert_evaluator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index 9979aa66..fe4891b7 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -55,8 +55,8 @@ void_result assert_evaluator::do_evaluate( const assert_operation& o ) unsigned_int t; fc::raw::unpack( is, t ); // everyone checks: delegates must have allocated an opcode for it - FC_ASSERT( t < _db.get_global_properties().parameters.max_predicate_opcode ); - if( t >= predicate::count() ) + FC_ASSERT( t.value < _db.get_global_properties().parameters.max_predicate_opcode ); + if( t.value >= predicate::count() ) { // // delegates allocated an opcode, but our client doesn't know From db85d35b199caea5225cf85418932d1a1f40b5d9 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 22 Jun 2015 14:57:43 -0400 Subject: [PATCH 081/354] Fix out-of-source builds, make field_reflector report errors finding template file --- libraries/chain/CMakeLists.txt | 10 +++++----- libraries/chain/field_reflector.cpp | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 2ec25bf7..0dbaa8c5 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -13,10 +13,10 @@ target_include_directories( field_reflector target_link_libraries( field_reflector fc graphene_db ) -add_custom_command( OUTPUT db_reflect_cmp.cpp - COMMAND field_reflector db_reflect_cmp.tmpl db_reflect_cmp.cpp.new - COMMAND ${CMAKE_COMMAND} -E copy_if_different db_reflect_cmp.cpp.new db_reflect_cmp.cpp - COMMAND ${CMAKE_COMMAND} -E remove db_reflect_cmp.cpp.new +add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp + COMMAND field_reflector ${CMAKE_CURRENT_SOURCE_DIR}/db_reflect_cmp.tmpl ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp + COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new DEPENDS field_reflector db_reflect_cmp.tmpl ) @@ -25,7 +25,7 @@ add_library( graphene_chain types.cpp type_id.cpp - db_reflect_cmp.cpp + ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp address.cpp asset.cpp diff --git a/libraries/chain/field_reflector.cpp b/libraries/chain/field_reflector.cpp index ee4b2799..ead68f4e 100644 --- a/libraries/chain/field_reflector.cpp +++ b/libraries/chain/field_reflector.cpp @@ -222,12 +222,18 @@ int main( int argc, char** argv ) tmpl_params["cmp_attr_impl_body"] = generate_cmp_attr_impl( switch_table ); std::ifstream template_file( argv[1] ); + if (!template_file) + FC_THROW("Error opening template file ${template_file}", ("template_file", argv[1])); std::stringstream ss; ss << template_file.rdbuf(); std::string result = fc::format_string( ss.str(), tmpl_params ); std::ofstream result_file( argv[2] ); result_file << result; } - catch ( const fc::exception& e ){ edump((e.to_detail_string())); } + catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + return 1; + } return 0; } From 3eaec849eb6fce46c80cbae087a719cadff6efe2 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Mon, 22 Jun 2015 15:03:18 -0400 Subject: [PATCH 082/354] Progress #61: Getting witnesses/delegates by owner Support and API are now present for retrieving witnesses and delegates by their owners. --- libraries/app/api.cpp | 24 +++++++++++++--- libraries/app/application.cpp | 10 +++---- libraries/app/include/graphene/app/api.hpp | 18 ++++++++++++ libraries/chain/db_block.cpp | 28 ++++--------------- libraries/chain/db_init.cpp | 18 ++++++------ libraries/chain/db_maint.cpp | 21 +++++++------- .../chain/include/graphene/chain/database.hpp | 4 +-- .../graphene/chain/delegate_object.hpp | 16 ++++++++++- .../include/graphene/chain/witness_object.hpp | 15 +++++++++- tests/common/database_fixture.cpp | 2 +- tests/tests/block_tests.cpp | 12 ++++---- 11 files changed, 107 insertions(+), 61 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 241678b3..bb1e725f 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -17,10 +17,8 @@ */ #include #include -#include #include #include -#include #include @@ -167,7 +165,7 @@ namespace graphene { namespace app { else { result.reserve(assets.size()); - + std::transform(assets.begin(), assets.end(), std::back_inserter(result), [this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); }); } @@ -242,7 +240,7 @@ namespace graphene { namespace app { auto itr = assets_by_symbol.lower_bound(lower_bound_symbol); - if( lower_bound_symbol == "" ) + if( lower_bound_symbol == "" ) itr = assets_by_symbol.begin(); while(limit-- && itr != assets_by_symbol.end()) @@ -251,6 +249,24 @@ namespace graphene { namespace app { return result; } + fc::optional database_api::get_delegate_by_account(account_id_type account) const + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; + } + + fc::optional database_api::get_witness_by_account(account_id_type account) const + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; + } + login_api::login_api(application& a) :_app(a) { diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 6deec129..f9711780 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -153,14 +153,14 @@ namespace detail { secret_hash_type::encoder enc; fc::raw::pack(enc, nathan_key); fc::raw::pack(enc, secret_hash_type()); + auto secret = secret_hash_type::hash(enc.result()); for( int i = 0; i < 10; ++i ) { - initial_state.allocation_targets.emplace_back("init"+fc::to_string(i), nathan_key.get_public_key(), 0, true); - initial_state.initial_committee.push_back({"init"+fc::to_string(i)}); + auto name = "init"+fc::to_string(i); + initial_state.allocation_targets.emplace_back(name, nathan_key.get_public_key(), 0, true); + initial_state.initial_committee.push_back({name}); + initial_state.initial_witnesses.push_back({name, nathan_key.get_public_key(), secret}); } - initial_state.initial_witnesses = vector(10, {"committee-account", - nathan_key.get_public_key(), - secret_hash_type::hash(enc.result())}); initial_state.allocation_targets.emplace_back("nathan", address(public_key_type(nathan_key.get_public_key())), 1); if( _options->count("genesis-json") ) diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index e4729c3d..e1b2e3c9 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -24,7 +24,10 @@ #include #include #include +#include +#include #include + #include namespace graphene { namespace app { @@ -165,6 +168,19 @@ namespace graphene { namespace app { */ vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + /** + * @brief Get the delegate owned by a given account + * @param account The ID of the account whose delegate should be retrieved + * @return The delegate object, or null if the account does not have a delegate + */ + fc::optional get_delegate_by_account(account_id_type account)const; + /** + * @brief Get the witness owned by a given account + * @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; + /** * @group Push Notification Methods * These methods may be used to get push notifications whenever an object or market is changed @@ -338,6 +354,8 @@ FC_API(graphene::app::database_api, (get_call_orders) (get_settle_orders) (list_assets) + (get_delegate_by_account) + (get_witness_by_account) (subscribe_to_objects) (unsubscribe_from_objects) (subscribe_to_market) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index bd5a6bb6..f1a483ba 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -80,7 +80,7 @@ const signed_transaction& database::get_recent_transaction(const transaction_id_ * * @return true if we switched forks as a result of this push. */ -bool database::push_block( const signed_block& new_block, uint32_t skip ) +bool database::push_block(const signed_block& new_block, uint32_t skip) { bool result; with_skip_flags( skip, [&]() @@ -90,29 +90,20 @@ bool database::push_block( const signed_block& new_block, uint32_t skip ) return result; } -bool database::_push_block( const signed_block& new_block ) +bool database::_push_block(const signed_block& new_block) { try { uint32_t skip = get_node_properties().skip_flags; if( !(skip&skip_fork_db) ) { - auto new_head = _fork_db.push_block( new_block ); + auto 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() ) { - //edump((new_head->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() ) { - auto branches = _fork_db.fetch_branch_from( new_head->data.id(), _pending_block.previous ); - for( auto item : branches.first ) - { - // wdump( ("new")(item->id)(item->data.previous) ); - } - for( auto item : branches.second ) - { - // wdump( ("old")(item->id)(item->data.previous) ); - } + auto branches = _fork_db.fetch_branch_from(new_head->data.id(), _pending_block.previous); // pop blocks until we hit the forked block while( head_block_id() != branches.second.back()->data.previous ) @@ -131,9 +122,6 @@ bool database::_push_block( const signed_block& new_block ) catch ( const fc::exception& e ) { except = e; } if( except ) { - //wdump((except->to_detail_string())); - // elog( "Encountered error when switching to a longer fork at id ${id}. Going back.", - // ("id", (*ritr)->id) ); // remove the rest of branches.first from the fork_db, those blocks are invalid while( ritr != branches.first.rend() ) { @@ -170,8 +158,8 @@ bool database::_push_block( const signed_block& new_block ) try { auto session = _undo_db.start_undo_session(); - apply_block( new_block, skip ); - _block_id_to_block.store( new_block.id(), new_block ); + apply_block(new_block, skip); + _block_id_to_block.store(new_block.id(), new_block); session.commit(); } catch ( const fc::exception& e ) { elog("Failed to push new block:\n${e}", ("e", e.to_detail_string())); @@ -204,7 +192,6 @@ processed_transaction database::push_transaction( const signed_transaction& trx, processed_transaction database::_push_transaction( const signed_transaction& trx ) { uint32_t skip = get_node_properties().skip_flags; - //wdump((trx.digest())(trx.id())); // If this is the first transaction pushed after applying a block, start a new undo session. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. if( !_pending_block_session ) _pending_block_session = _undo_db.start_undo_session(); @@ -239,9 +226,6 @@ processed_transaction database::push_proposal(const proposal_object& proposal) return std::make_pair(id, authority::owner); }); - //ilog("Attempting to push proposal ${prop}", ("prop", proposal)); - //idump((eval_state.approved_by)); - eval_state.operation_results.reserve(proposal.proposed_transaction.operations.size()); processed_transaction ptrx(proposal.proposed_transaction); eval_state._trx = &ptrx; diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 86e58690..882234ea 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -105,8 +105,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 >(); + add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); @@ -118,12 +118,12 @@ 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> >(); - add_index< primary_index< simple_index< witness_schedule_object > > >(); + add_index< primary_index> >(); + add_index< primary_index> >(); + add_index< primary_index> >(); + add_index< primary_index> >(); + add_index< primary_index> >(); + add_index< primary_index> >(); } void database::init_genesis(const genesis_state_type& genesis_state) @@ -356,6 +356,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) assert( wso.id == witness_schedule_id_type() ); _undo_db.enable(); -} FC_LOG_AND_RETHROW() } +} FC_CAPTURE_AND_RETHROW((genesis_state)) } } } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 92a2e6a1..11bc6770 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -31,18 +31,19 @@ namespace graphene { namespace chain { -template -vector> database::sort_votable_objects(size_t count) const +template +vector> database::sort_votable_objects(size_t count) const { - const auto& all_objects = dynamic_cast&>(get_index()); + using ObjectType = typename Index::object_type; + const auto& all_objects = get_index_type().indices(); count = std::min(count, all_objects.size()); vector> refs; refs.reserve(all_objects.size()); std::transform(all_objects.begin(), all_objects.end(), std::back_inserter(refs), [](const ObjectType& o) { return std::cref(o); }); - std::partial_sort( refs.begin(), refs.begin() + count, refs.end(), - [this]( const ObjectType& a, const ObjectType& b )->bool { + std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), + [this](const ObjectType& a, const ObjectType& b)->bool { return _vote_tally_buffer[a.vote_id] > _vote_tally_buffer[b.vote_id]; }); @@ -105,7 +106,7 @@ void database::update_active_witnesses() && (stake_tally <= stake_target) ) stake_tally += _witness_count_histogram_buffer[++witness_count]; - auto wits = sort_votable_objects(std::max(witness_count*2+1, GRAPHENE_MIN_WITNESS_COUNT)); + auto wits = sort_votable_objects(std::max(witness_count*2+1, GRAPHENE_MIN_WITNESS_COUNT)); const global_property_object& gpo = get_global_properties(); // Update witness authority @@ -171,12 +172,12 @@ void database::update_active_delegates() && (stake_tally <= stake_target) ) stake_tally += _committee_count_histogram_buffer[++delegate_count]; - auto delegates = sort_votable_objects(std::max(delegate_count*2+1, GRAPHENE_MIN_DELEGATE_COUNT)); + auto delegates = sort_votable_objects(std::max(delegate_count*2+1, GRAPHENE_MIN_DELEGATE_COUNT)); // Update genesis authorities if( !delegates.empty() ) { - modify( get(GRAPHENE_COMMITTEE_ACCOUNT), [&]( account_object& a ) { + modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) { uint64_t total_votes = 0; map weights; a.active.weight_threshold = 0; @@ -202,11 +203,11 @@ void database::update_active_delegates() a.active.weight_threshold /= 2; a.active.weight_threshold += 1; }); - modify( get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) { + modify(get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) { a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active; }); } - modify( get_global_properties(), [&]( global_property_object& gp ) { + modify(get_global_properties(), [&](global_property_object& gp) { gp.active_delegates.clear(); std::transform(delegates.begin(), delegates.end(), std::inserter(gp.active_delegates, gp.active_delegates.begin()), diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 0c036c95..c26b44a6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -399,8 +399,8 @@ namespace graphene { namespace chain { optional _pending_block_session; vector< unique_ptr > _operation_evaluators; - template - vector> sort_votable_objects(size_t count)const; + template + vector> sort_votable_objects(size_t count)const; //////////////////// db_block.cpp //////////////////// diff --git a/libraries/chain/include/graphene/chain/delegate_object.hpp b/libraries/chain/include/graphene/chain/delegate_object.hpp index 29dcbfd5..9807beef 100644 --- a/libraries/chain/include/graphene/chain/delegate_object.hpp +++ b/libraries/chain/include/graphene/chain/delegate_object.hpp @@ -18,6 +18,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -33,7 +34,7 @@ namespace graphene { namespace chain { * active delegates has control. * * Delegates were separated into a separate object to make iterating over - * the set of delegate easy. + * the set of delegate easy. */ class delegate_object : public abstract_object { @@ -45,6 +46,19 @@ namespace graphene { namespace chain { vote_id_type vote_id; }; + struct by_account; + using delegate_multi_index_type = multi_index_container< + delegate_object, + indexed_by< + ordered_unique< tag, + member + >, + hashed_unique< tag, + member + > + > + >; + using delegate_index = generic_index; } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::delegate_object, (graphene::db::object), diff --git a/libraries/chain/include/graphene/chain/witness_object.hpp b/libraries/chain/include/graphene/chain/witness_object.hpp index 5d5ba882..82677d66 100644 --- a/libraries/chain/include/graphene/chain/witness_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_object.hpp @@ -18,6 +18,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -40,6 +41,19 @@ namespace graphene { namespace chain { witness_object() : vote_id(vote_id_type::witness) {} }; + struct by_account; + using witness_multi_index_type = multi_index_container< + witness_object, + indexed_by< + hashed_unique< tag, + member + >, + hashed_unique< tag, + member + > + > + >; + using witness_index = generic_index; } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object), @@ -49,4 +63,3 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object), (last_secret) (accumulated_income) (vote_id) ) - diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e74a34b3..02685c58 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -155,7 +155,7 @@ void database_fixture::verify_asset_supplies( )const total_balances[bad.options.short_backing_asset] += bad.settlement_fund; } } - for( const witness_object& witness_obj : db.get_index_type>() ) + for( const witness_object& witness_obj : db.get_index_type().indices() ) { total_balances[asset_id_type()] += witness_obj.accumulated_income; } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 2bb9a464..cab95933 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -41,14 +41,14 @@ genesis_state_type make_genesis() { secret_hash_type::encoder enc; fc::raw::pack(enc, delegate_priv_key); fc::raw::pack(enc, secret_hash_type()); + auto secret = secret_hash_type::hash(enc.result()); for( int i = 0; i < 10; ++i ) { - genesis_state.allocation_targets.emplace_back("init"+fc::to_string(i), delegate_priv_key.get_public_key(), 0, true); - genesis_state.initial_committee.push_back({"init"+fc::to_string(i)}); + auto name = "init"+fc::to_string(i); + genesis_state.allocation_targets.emplace_back(name, delegate_priv_key.get_public_key(), 0, true); + genesis_state.initial_committee.push_back({name}); + genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); } - genesis_state.initial_witnesses = vector(10, {"committee-account", - delegate_priv_key.get_public_key(), - secret_hash_type::hash(enc.result())}); return genesis_state; } @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE( block_database_test ) for( uint32_t i = 0; i < 5; ++i ) { if( i > 0 ) b.previous = b.id(); - b.witness = witness_id_type(i+1); + b.witness = witness_id_type(i+1); bdb.store( b.id(), b ); auto fetch = bdb.fetch_by_number( b.block_num() ); From 758d588aa111caa9d6b7a55558ed6a809f3273bf Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 22 Jun 2015 15:04:19 -0400 Subject: [PATCH 083/354] switch to rationals --- libraries/chain/asset.cpp | 29 ++++++++++++++++++++++-- libraries/chain/call_order_evaluator.cpp | 7 ++++-- libraries/chain/db_market.cpp | 2 +- libraries/fc | 2 +- tests/tests/operation_tests.cpp | 4 ++-- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index 22a2dd6a..81b761b5 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -17,6 +17,7 @@ */ #include #include +#include namespace graphene { namespace chain { bool operator < ( const asset& a, const asset& b ) @@ -108,14 +109,38 @@ namespace graphene { namespace chain { * * This method divides the collateral by the maintenance collateral ratio to derive * a call price for the given black swan ratio. + * + * There exists some cases where the debt and collateral values are so small that + * dividing by the collateral ratio will result in a 0 price or really poor + * rounding errors. No matter what the collateral part of the price ratio can + * never go to 0 and the debt can never go more than GRAPHENE_MAX_SHARE_SUPPLY + * + * CR * DEBT/COLLAT or DEBT/(COLLAT/CR) */ - price price::call_price(const asset& debt, const asset& collateral, uint16_t collateral_ratio) + price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio) { try { + //wdump((debt)(collateral)(collateral_ratio)); + boost::rational swan(debt.amount.value,collateral.amount.value); + boost::rational ratio( collateral_ratio, 1000 ); + auto cp = swan * ratio; + return ~(asset( cp.numerator(), debt.asset_id ) / asset( cp.denominator(), collateral.asset_id )); + + /* + while( collateral.amount < 100000 && debt.amount < GRAPHENE_MAX_SHARE_SUPPLY/100 ) + { + collateral.amount *= 1000; + debt.amount *= 1000; + } + fc::uint128 tmp( collateral.amount.value ); tmp *= 1000; tmp /= collateral_ratio; FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); - return asset( tmp.to_uint64(), collateral.asset_id) / debt; + asset col( tmp.to_uint64(), collateral.asset_id); + + if( col.amount == 0 ) col.amount = 1; + return col / debt; + */ } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) } bool price::is_null() const { return *this == price(); } diff --git a/libraries/chain/call_order_evaluator.cpp b/libraries/chain/call_order_evaluator.cpp index b508d49f..379e2287 100644 --- a/libraries/chain/call_order_evaluator.cpp +++ b/libraries/chain/call_order_evaluator.cpp @@ -117,8 +117,11 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat d.modify( *call_obj, [&]( call_order_object& call ){ call.collateral += o.delta_collateral.amount; call.debt += o.delta_debt.amount; - call.call_price = price::call_price(call.get_debt(), call.get_collateral(), - _bitasset_data->current_feed.maintenance_collateral_ratio); + if( call.debt > 0 ) + { + call.call_price = price::call_price(call.get_debt(), call.get_collateral(), + _bitasset_data->current_feed.maintenance_collateral_ratio); + } }); } diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index da0013d7..59b35430 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -354,7 +354,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa */ assert( max_price.base.asset_id == min_price.base.asset_id ); - wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); + // wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); diff --git a/libraries/fc b/libraries/fc index dd1c77b3..dde8ed9d 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit dd1c77b327c6eba807168856c3c12e90173468c4 +Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00 diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 800fe906..b96088d6 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -51,8 +51,8 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) auto swanp = usd / core; auto callp = ~price::call_price( usd, core, 1750 ); // 1:1 collateral - wdump((callp.to_real())(callp)); - wdump((swanp.to_real())(swanp)); +// wdump((callp.to_real())(callp)); +// wdump((swanp.to_real())(swanp)); FC_ASSERT( callp.to_real() > swanp.to_real() ); /* From a1601cbc0f11727612b2b05b65e4e9d3eeb2d812 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 22 Jun 2015 15:55:50 -0400 Subject: [PATCH 084/354] fix bugs --- libraries/chain/asset.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index 81b761b5..daff0964 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -165,13 +165,23 @@ namespace graphene { namespace chain { price price_feed::max_short_squeeze_price()const { + boost::rational sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value); + boost::rational ratio( 1000, maximum_short_squeeze_ratio ); + auto cp = sp * ratio; + return (asset( cp.numerator(), settlement_price.base.asset_id ) / asset( cp.denominator(), settlement_price.quote.asset_id )); + + /* asset collateral = settlement_price.quote; fc::uint128 tmp( collateral.amount.value ); tmp *= maximum_short_squeeze_ratio; tmp /= 1000; FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); collateral.amount = tmp.to_uint64(); - return settlement_price.base / collateral; + auto tmp2 = settlement_price.base / collateral; + wdump((rtn)(tmp2)); + return rtn; + */ + } /* price price_feed::maintenance_price()const From de99437be49a4f295366c14e55532be5c41506b7 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 22 Jun 2015 17:29:40 -0400 Subject: [PATCH 085/354] remove min_market_fee due to potential attack vector with many small partial matches --- libraries/chain/asset_object.cpp | 1 - libraries/chain/db_market.cpp | 4 +--- libraries/chain/include/graphene/chain/asset_object.hpp | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index f3e5bf07..f5bab206 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -85,7 +85,6 @@ void asset_object::asset_options::validate()const FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY ); FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT ); FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY ); - FC_ASSERT( min_market_fee >= 0 && min_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY ); // There must be no high bits in permissions whose meaning is not known. FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) ); // There must be no high bits in flags which are not also high in permissions. diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 59b35430..3822566f 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -471,7 +471,7 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( !trade_asset.charges_market_fees() ) return trade_asset.amount(0); if( trade_asset.options.market_fee_percent == 0 ) - return trade_asset.amount(trade_asset.options.min_market_fee); + return trade_asset.amount(0); fc::uint128 a(trade_amount.amount.value); a *= trade_asset.options.market_fee_percent; @@ -480,8 +480,6 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( percent_fee.amount > trade_asset.options.max_market_fee ) percent_fee.amount = trade_asset.options.max_market_fee; - else if( percent_fee.amount < trade_asset.options.min_market_fee ) - percent_fee.amount = trade_asset.options.min_market_fee; return percent_fee; } diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index c3cd7481..63ea1cdd 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -128,7 +128,6 @@ namespace graphene { namespace chain { /// in this field means a 1% fee is charged on market trades of this asset. uint16_t market_fee_percent = 0; share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY; - share_type min_market_fee; /// The flags which the issuer has permission to update. See @ref asset_issuer_permission_flags uint16_t issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; @@ -320,7 +319,6 @@ FC_REFLECT( graphene::chain::asset_object::asset_options, (max_supply) (market_fee_percent) (max_market_fee) - (min_market_fee) (issuer_permissions) (flags) (core_exchange_rate) From 962310b415b3f1abb050faa40be8737870613329 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 22 Jun 2015 17:50:50 -0400 Subject: [PATCH 086/354] adding API call to get proposed transactions for an account --- libraries/app/api.cpp | 18 ++++++++++++++++++ libraries/app/include/graphene/app/api.hpp | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index bb1e725f..47ac4625 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -446,4 +446,22 @@ namespace graphene { namespace app { return result; } + /** TODO: add secondary index that will accelerate this process */ + vector database_api::get_proposed_transactions( account_id_type id )const + { + const auto& idx = _db.get_index_type(); + vector result; + + idx.inspect_all_objects( [&](const object& obj){ + const proposal_object& p = static_cast(obj); + if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() ) + result.push_back(p); + else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() ) + result.push_back(p); + else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() ) + result.push_back(p); + }); + return result; + } + } } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index e1b2e3c9..b0513b45 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -226,6 +227,13 @@ namespace graphene { namespace app { /// @brief Get a hexdump of the serialized binary form of a transaction std::string get_transaction_hex(const signed_transaction& trx)const; + + /** + * @return the set of proposed transactions relevant to the specified account id. + */ + vector get_proposed_transactions( account_id_type id )const; + + private: /** called every time a block is applied to report the objects that were changed */ void on_objects_changed(const vector& ids); @@ -362,6 +370,7 @@ FC_API(graphene::app::database_api, (unsubscribe_from_market) (cancel_all_subscriptions) (get_transaction_hex) + (get_proposed_transactions) ) FC_API(graphene::app::history_api, (get_account_history)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) From d7f7d6435219f8d51cd15858bfcc4f1459e430f4 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 22 Jun 2015 18:11:54 -0400 Subject: [PATCH 087/354] notify observes of changed objects after every pushed transaction --- libraries/chain/db_block.cpp | 13 +++++++++---- libraries/chain/include/graphene/chain/database.hpp | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index f1a483ba..51086361 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -202,6 +202,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx FC_ASSERT( (skip & skip_block_size_check) || fc::raw::pack_size(_pending_block) <= get_global_properties().parameters.maximum_block_size ); + notify_changed_objects(); // The transaction applied successfully. Merge its changes into the pending block session. session.merge(); return processed_trx; @@ -397,14 +398,18 @@ void database::_apply_block( const signed_block& next_block ) applied_block( next_block ); //emit _applied_ops.clear(); + notify_changed_objects(); + + update_pending_block(next_block, current_block_interval); +} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } + +void database::notify_changed_objects() +{ const auto& head_undo = _undo_db.head(); vector changed_ids; changed_ids.reserve(head_undo.old_values.size()); for( const auto& item : head_undo.old_values ) changed_ids.push_back(item.first); changed_objects(changed_ids); - - - update_pending_block(next_block, current_block_interval); -} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } +} processed_transaction database::apply_transaction( const signed_transaction& trx, uint32_t skip ) { diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c26b44a6..41c4b3a6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -200,7 +200,7 @@ namespace graphene { namespace chain { fc::signal applied_block; /** - * After a block has been applied and committed. The callback + * Emitted After a block has been applied and committed. The callback * should not yield and should execute quickly. */ fc::signal&)> changed_objects; @@ -394,6 +394,7 @@ namespace graphene { namespace chain { 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_changed_objects(); private: optional _pending_block_session; From 19d063145f324f1b6d1c2951e8e88da511ce79cc Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 22 Jun 2015 18:02:51 -0400 Subject: [PATCH 088/354] Add CLI commands for voting witnesses/delegates --- .../wallet/include/graphene/wallet/wallet.hpp | 13 +++ libraries/wallet/wallet.cpp | 84 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 9507165c..425da348 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -264,6 +264,16 @@ class wallet_api account_whitelist_operation::account_listing new_listing_status, bool broadcast = false); + signed_transaction vote_for_delegate(string voting_account, + string witness, + bool approve, + bool broadcast = false); + + signed_transaction vote_for_witness(string voting_account, + string witness, + bool approve, + bool broadcast = false); + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); void dbg_make_uia(string creator, string symbol); @@ -331,6 +341,9 @@ FC_API( graphene::wallet::wallet_api, (burn_asset) (global_settle_asset) (settle_asset) + (whitelist_account) + (vote_for_delegate) + (vote_for_witness) (get_account) (get_account_id) (get_block) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0c4e129b..2ab244d4 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1097,7 +1097,75 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } + + signed_transaction vote_for_delegate(string voting_account, + string delegate, + bool approve, + bool broadcast /* = false */) + { try { + account_object voting_account_object = get_account(voting_account); + account_id_type delegate_owner_account_id = get_account_id(delegate); + fc::optional delegate_obj = _remote_db->get_delegate_by_account(delegate_owner_account_id); + if (!delegate_obj) + FC_THROW("Account ${delegate} is not registered as a delegate", ("delegate", delegate)); + if (approve) + { + auto insert_result = voting_account_object.options.votes.insert(delegate_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate)); + } + else + { + unsigned votes_removed = voting_account_object.options.votes.erase(delegate_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for delegate ${delegate}", ("account", voting_account)("delegate", delegate)); + } + account_update_operation account_update_op; + account_update_op.account = voting_account_object.id; + account_update_op.new_options = voting_account_object.options; + signed_transaction tx; + tx.operations.push_back( account_update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (voting_account)(delegate)(approve)(broadcast) ) } + + signed_transaction vote_for_witness(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) + { try { + 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); + if (!witness_obj) + FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); + 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)); + } + 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)); + } + account_update_operation account_update_op; + account_update_op.account = voting_account_object.id; + account_update_op.new_options = voting_account_object.options; + + signed_transaction tx; + tx.operations.push_back( account_update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) } + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set req_active_approvals; @@ -1800,6 +1868,22 @@ signed_transaction wallet_api::whitelist_account(string authorizing_account, return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } +signed_transaction wallet_api::vote_for_delegate(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_delegate(voting_account, witness, approve, broadcast); +} + +signed_transaction wallet_api::vote_for_witness(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_witness(voting_account, witness, approve, broadcast); +} + void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = wallet_filename; From 1b9621021259b39baa411f4425dc4cb3fcb4d103 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Mon, 22 Jun 2015 19:37:18 -0400 Subject: [PATCH 089/354] Add CLI for registering delegates and witnesses, and setting voting proxy --- .../wallet/include/graphene/wallet/wallet.hpp | 13 +++ libraries/wallet/wallet.cpp | 84 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 425da348..b2d4ed95 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -264,6 +264,12 @@ class wallet_api account_whitelist_operation::account_listing new_listing_status, bool broadcast = false); + signed_transaction create_delegate(string owner_account, + bool broadcast = false); + + signed_transaction create_witness(string owner_account, + bool broadcast = false); + signed_transaction vote_for_delegate(string voting_account, string witness, bool approve, @@ -274,6 +280,10 @@ class wallet_api bool approve, bool broadcast = false); + signed_transaction set_voting_proxy(string account_to_modify, + optional voting_account, + bool broadcast = false); + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false); void dbg_make_uia(string creator, string symbol); @@ -342,8 +352,11 @@ FC_API( graphene::wallet::wallet_api, (global_settle_asset) (settle_asset) (whitelist_account) + (create_delegate) + (create_witness) (vote_for_delegate) (vote_for_witness) + (set_voting_proxy) (get_account) (get_account_id) (get_block) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2ab244d4..36c7e9c6 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1098,6 +1098,40 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } + signed_transaction create_delegate(string owner_account, + bool broadcast /* = false */) + { try { + + delegate_create_operation delegate_create_op; + delegate_create_op.delegate_account = get_account_id(owner_account); + if (_remote_db->get_delegate_by_account(delegate_create_op.delegate_account)) + FC_THROW("Account ${owner_account} is already a delegate", ("owner_account", owner_account)); + + signed_transaction tx; + tx.operations.push_back( delegate_create_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + + signed_transaction create_witness(string owner_account, + bool broadcast /* = false */) + { try { + + witness_create_operation witness_create_op; + witness_create_op.witness_account = get_account_id(owner_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; + tx.operations.push_back( witness_create_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + signed_transaction vote_for_delegate(string voting_account, string delegate, bool approve, @@ -1166,6 +1200,37 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) } + signed_transaction set_voting_proxy(string account_to_modify, + optional voting_account, + bool broadcast /* = false */) + { try { + account_object account_object_to_modify = get_account(account_to_modify); + if (voting_account) + { + account_id_type new_voting_account_id = get_account_id(*voting_account); + if (account_object_to_modify.options.voting_account == new_voting_account_id) + FC_THROW("Voting proxy for ${account} is already set to ${voter}", ("account", account_to_modify)("voter", *voting_account)); + account_object_to_modify.options.voting_account = new_voting_account_id; + } + else + { + if (account_object_to_modify.options.voting_account == account_id_type()) + FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify)); + account_object_to_modify.options.voting_account = account_id_type(); + } + + account_update_operation account_update_op; + account_update_op.account = account_object_to_modify.id; + account_update_op.new_options = account_object_to_modify.options; + + signed_transaction tx; + tx.operations.push_back( account_update_op ); + tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (account_to_modify)(voting_account)(broadcast) ) } + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set req_active_approvals; @@ -1868,6 +1933,18 @@ signed_transaction wallet_api::whitelist_account(string authorizing_account, return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } +signed_transaction wallet_api::create_delegate(string owner_account, + bool broadcast /* = false */) +{ + return my->create_delegate(owner_account, broadcast); +} + +signed_transaction wallet_api::create_witness(string owner_account, + bool broadcast /* = false */) +{ + return my->create_witness(owner_account, broadcast); +} + signed_transaction wallet_api::vote_for_delegate(string voting_account, string witness, bool approve, @@ -1884,6 +1961,13 @@ signed_transaction wallet_api::vote_for_witness(string voting_account, return my->vote_for_witness(voting_account, witness, approve, broadcast); } +signed_transaction wallet_api::set_voting_proxy(string account_to_modify, + optional voting_account, + bool broadcast /* = false */) +{ + return my->set_voting_proxy(account_to_modify, voting_account, broadcast); +} + void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = wallet_filename; From 8ac4bc1d5839be175870c982740ea6b861b701f2 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 09:08:34 -0400 Subject: [PATCH 090/354] Fix #39 Refactor assert_operation Reasons: 1. The protocol should not depend upon implementation details such as how the database objects are structured or reflected 2. The protocol should deal in abstract concepts 3. Should use fc::datastream rather than istringstream for performance and memory allocation reasons 4. Fees should be charged proportional to the size of the operation 5. Validate on the assert operation should also perform sanity checks on types 6. Protocol definition objects should never depend upon the database because they may be used in situations where the database and evaluators are not present. 7. Reflected field names should never have '_' in them because they become part of the *PUBLIC* json definition. --- libraries/chain/CMakeLists.txt | 23 -- libraries/chain/assert_evaluator.cpp | 59 ++--- libraries/chain/field_reflector.cpp | 239 ------------------ .../include/graphene/chain/operations.hpp | 12 +- .../include/graphene/chain/predicate.hpp | 61 +++-- .../include/graphene/chain/wild_object.hpp | 61 ----- libraries/chain/operations.cpp | 34 +++ libraries/chain/predicate.cpp | 34 --- tests/tests/operation_tests2.cpp | 8 +- 9 files changed, 100 insertions(+), 431 deletions(-) delete mode 100644 libraries/chain/field_reflector.cpp delete mode 100644 libraries/chain/include/graphene/chain/wild_object.hpp delete mode 100644 libraries/chain/predicate.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 0dbaa8c5..8c95289d 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -1,35 +1,12 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp") -add_executable( field_reflector - field_reflector.cpp - types.cpp - type_id.cpp - address.cpp - key_object.cpp - ${HEADERS} ) - -target_include_directories( field_reflector - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) - -target_link_libraries( field_reflector fc graphene_db ) - -add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - COMMAND field_reflector ${CMAKE_CURRENT_SOURCE_DIR}/db_reflect_cmp.tmpl ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new - DEPENDS field_reflector db_reflect_cmp.tmpl - ) - ## SORT .cpp by most likely to change / break compile add_library( graphene_chain types.cpp type_id.cpp - ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - address.cpp asset.cpp - predicate.cpp operations.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index fe4891b7..aa6862c4 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -24,61 +24,44 @@ namespace graphene { namespace chain { -namespace detail { - -struct predicate_check_visitor +struct predicate_visitor { - predicate_check_visitor( const database& d ): _db(d){} - typedef bool result_type; - template bool operator()( const Predicate& pred )const + const database& db; + + predicate_visitor( const database& d ):db(d){} + + bool operator()( const verify_account_name& p )const { - return pred.check_predicate( _db ); + return p.account_id(db).name == p.account_name; + } + bool operator()( const verify_symbol& p )const + { + return p.asset_id(db).symbol == p.symbol; } - - const database& _db; }; -} // graphene::chain::detail - void_result assert_evaluator::do_evaluate( const assert_operation& o ) { const database& _db = db(); uint32_t skip = _db.get_node_properties().skip_flags; - // TODO: Skip flags + if( skip & database::skip_assert_evaluation ) return void_result(); - for( const vector& s_pred : o.predicates ) + + for( const vector& pdata : o.predicates ) { - std::istringstream is( string( s_pred.begin(), s_pred.end() ) ); - // de-serialize just the static_variant tag - unsigned_int t; - fc::raw::unpack( is, t ); - // everyone checks: delegates must have allocated an opcode for it - FC_ASSERT( t.value < _db.get_global_properties().parameters.max_predicate_opcode ); - if( t.value >= predicate::count() ) + fc::datastream ds( pdata.data(), pdata.size() ); + predicate p; + try { + fc::raw::unpack( ds, p ); + } catch ( const fc::exception& e ) { - // - // delegates allocated an opcode, but our client doesn't know - // the semantics (i.e. we are running an old client) - // - // skip_unknown_predicate indicates we're cool with assuming - // unknown predicates pass - // if( skip & database::skip_unknown_predicate ) continue; - // - // ok, unknown predicate must die - // - FC_ASSERT( false, "unknown predicate" ); + throw; } - // rewind to beginning, unpack it, and check it - is.clear(); - is.seekg(0); - predicate pred; - fc::raw::unpack( is, pred ); - bool pred_passed = pred.visit( detail::predicate_check_visitor( _db ) ); - FC_ASSERT( pred_passed ); + FC_ASSERT( p.visit( predicate_visitor( _db ) ) ); } return void_result(); } diff --git a/libraries/chain/field_reflector.cpp b/libraries/chain/field_reflector.cpp deleted file mode 100644 index ead68f4e..00000000 --- a/libraries/chain/field_reflector.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -using namespace graphene::chain; - -fc::mutable_variant_object g_vo_object_types; -std::vector< fc::mutable_variant_object > g_vo_fields; - -struct serialize_object_type_member_visitor -{ - public: - template - void operator()( const char* name )const - { - fc::mutable_variant_object vo; - vo["name"] = name; - vo["type"] = fc::get_typename::name(); - vo["id"] = g_vo_fields.size(); - g_vo_fields.push_back( vo ); - } -}; - -struct serialize_object_type_visitor -{ - typedef void result_type; - - int t = 0; - serialize_object_type_visitor(int _t ):t(_t){} - - template - result_type operator()( const Type& op )const - { - fc::mutable_variant_object vo; - vo["space_id"] = Type::space_id; - vo["type_id"] = Type::type_id; - g_vo_fields.clear(); - // visit all members - fc::reflector::visit( serialize_object_type_member_visitor() ); - - vo["fields"] = g_vo_fields; - g_vo_object_types[ fc::get_typename::name() ] = vo; - } -}; - -struct getattr_switch_table_entry -{ - uint32_t _switch_val; // (space << 24) | (type << 16) | fieldnum - string _object_typename; - string _field_typename; - string _field_name; -}; - -vector< getattr_switch_table_entry > build_switch_table() -{ - vector< getattr_switch_table_entry > result; - for( const auto& item : g_vo_object_types ) - { - const variant_object& vo = item.value().get_object(); - uint32_t top = (vo["space_id"].as_uint64() << 24) | (vo["type_id"].as_uint64() << 16); - for( const auto& field : vo["fields"].get_array() ) - { - getattr_switch_table_entry e; - e._switch_val = top | field["id"].as_uint64(); - e._object_typename = item.key(); - e._field_typename = field["type"].get_string(); - e._field_name = field["name"].get_string(); - result.push_back( e ); - } - } - - std::sort( result.begin(), result.end(), - []( const getattr_switch_table_entry& a, - const getattr_switch_table_entry& b ) - { - return a._switch_val < b._switch_val; - } ); - - return result; -} - -std::string generate_cmp_attr_impl( const vector< getattr_switch_table_entry >& switch_table ) -{ - std::ostringstream out; - - // switch( space ) - // switch( type ) - // switch( fieldnum ) - // switch( opc ) - - std::map< uint8_t, - std::map< uint8_t, - std::map< uint16_t, - const getattr_switch_table_entry* > > > index; - - for( const getattr_switch_table_entry& e : switch_table ) - { - uint8_t sp = (e._switch_val >> 24) & 0xFF; - uint8_t ty = (e._switch_val >> 16) & 0xFF; - uint16_t fn = (e._switch_val ) & 0xFFFF; - auto& i0 = index; - if( i0.find( sp ) == i0.end() ) - i0[sp] = std::map< uint8_t, std::map< uint16_t, const getattr_switch_table_entry* > >(); - auto& i1 = i0[sp]; - if( i1.find( ty ) == i1.end() ) - i1[ty] = std::map< uint16_t, const getattr_switch_table_entry* >(); - auto& i2 = i1[ty]; - i2[fn] = &e; - } - out << " switch( obj.id.space() )\n" - " {\n"; - for( const auto& e0 : index ) - { - out << " case " << int( e0.first ) << ":\n" - " switch( obj.id.type() )\n" - " {\n"; - for( const auto& e1 : e0.second ) - { - out << " case " << int( e1.first ) << ":\n" - " switch( field_num )\n" - " {\n"; - for( const auto& e2 : e1.second ) - { - const std::string& ft = e2.second->_field_typename; - const std::string& ot = e2.second->_object_typename; - const std::string& fn = e2.second->_field_name; - out << " case " << int( e2.first ) << ":\n" - " {\n" - " // " << ft - << " " << ot - << "." << fn - << "\n" - " const " << ft << "& dbval = object_database::cast< " << ot << " >( obj )." << fn << ";\n" - " return _cmp< " << ft << " >( dbval, lit, opc );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized field_num\" );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized object type\" );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized object space\" );\n" - " }\n"; - - return out.str(); -} - -static const char generated_file_banner[] = -"// _ _ __ _ _ //\n" -"// | | | | / _(_) | //\n" -"// __ _ ___ _ __ ___ _ __ __ _| |_ ___ __| | | |_ _| | ___ //\n" -"// / _` |/ _ \\ '_ \\ / _ \\ '__/ _` | __/ _ \\/ _` | | _| | |/ _ \\ //\n" -"// | (_| | __/ | | | __/ | | (_| | || __/ (_| | | | | | | __/ //\n" -"// \\__, |\\___|_| |_|\\___|_| \\__,_|\\__\\___|\\__,_| |_| |_|_|\\___| //\n" -"// __/ | //\n" -"// |___/ //\n" -"// //\n" -"// Generated by: programs/field_reflector/main.cpp //\n" -"// //\n" -"// Warning: This is a generated file, any changes made here will be //\n" -"// overwritten by the build process. If you need to change what //\n" -"// is generated here, you should either modify the reflected //\n" -"// types, or modify the code generator itself. //\n" -"// //\n" -; - -int main( int argc, char** argv ) -{ - try - { - if( argc != 3 ) - { - std::cout << "syntax: " << argv[0] << " \n"; - return 1; - } - - graphene::chain::impl::wild_object wo; - - for( int32_t i = 0; i < wo.count(); ++i ) - { - wo.set_which(i); - wo.visit( serialize_object_type_visitor(i) ); - } - - vector< getattr_switch_table_entry > switch_table = build_switch_table(); - - fc::mutable_variant_object tmpl_params; - - tmpl_params["generated_file_banner"] = generated_file_banner; - tmpl_params["object_descriptor"] = fc::json::to_string( g_vo_object_types ); - tmpl_params["cmp_attr_impl_body"] = generate_cmp_attr_impl( switch_table ); - - std::ifstream template_file( argv[1] ); - if (!template_file) - FC_THROW("Error opening template file ${template_file}", ("template_file", argv[1])); - std::stringstream ss; - ss << template_file.rdbuf(); - std::string result = fc::format_string( ss.str(), tmpl_params ); - std::ofstream result_file( argv[2] ); - result_file << result; - } - catch ( const fc::exception& e ) - { - edump((e.to_detail_string())); - return 1; - } - return 0; -} diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index f34af396..8a322a50 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -105,17 +105,21 @@ namespace graphene { namespace chain { /** * @brief assert that some conditions are true. * @ingroup operations + * + * This operation performs no changes to the database state, but can but used to verify + * pre or post conditions for other operations. + * */ struct assert_operation { - asset fee; - account_id_type fee_paying_account; - vector< vector< char > > predicates; + asset fee; + account_id_type fee_paying_account; + vector< vector< char > > predicates; flat_set required_auths; account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; - share_type calculate_fee( const fee_schedule_type& k )const{ return k.assert_op_fee; } + share_type calculate_fee( const fee_schedule_type& k )const; void validate()const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } diff --git a/libraries/chain/include/graphene/chain/predicate.hpp b/libraries/chain/include/graphene/chain/predicate.hpp index 5980d090..a05d51ab 100644 --- a/libraries/chain/include/graphene/chain/predicate.hpp +++ b/libraries/chain/include/graphene/chain/predicate.hpp @@ -15,46 +15,55 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #pragma once #include -#include - namespace graphene { namespace chain { class database; -class pred_field_lit_cmp +/** + * Used to verify that account_id->name is account_name + */ +struct verify_account_name { -public: - pred_field_lit_cmp( - object_id_type obj_id, - uint16_t field_num, - const vector& lit, - uint8_t opc - ) : - _obj_id( obj_id ), - _field_num( field_num ), - _lit( lit ), - _opc( opc ) - {} - pred_field_lit_cmp() {} // necessary for instantiating static_variant - ~pred_field_lit_cmp() {} + account_id_type account_id; + string account_name; - bool check_predicate( const database& db )const; - - object_id_type _obj_id; - uint16_t _field_num; - vector _lit; - uint8_t _opc; + /** + * Perform state independent checks, such as verifying that + * account_name is a valid name for an account. + */ + bool validate()const { return is_valid_name( account_name ); } }; +/** + * Used to verify that account_id->name is account_name + */ +struct verify_symbol +{ + asset_id_type asset_id; + string symbol; + + /** + * Perform state independent checks, such as verifying that + * account_name is a valid name for an account. + */ + bool validate()const { return is_valid_symbol( symbol ); } +}; + +/** + * When defining predicates do not make the protocol dependent upon + * implementation details. + */ typedef static_variant< - pred_field_lit_cmp + verify_account_name, + verify_symbol > predicate; } } -FC_REFLECT( graphene::chain::pred_field_lit_cmp, (_obj_id)(_field_num)(_lit)(_opc) ); +FC_REFLECT( graphene::chain::verify_account_name, (account_id)(account_name) ) +FC_REFLECT( graphene::chain::verify_symbol, (asset_id)(symbol) ) + diff --git a/libraries/chain/include/graphene/chain/wild_object.hpp b/libraries/chain/include/graphene/chain/wild_object.hpp deleted file mode 100644 index 5b302dac..00000000 --- a/libraries/chain/include/graphene/chain/wild_object.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace graphene { namespace chain { namespace impl { - -/** - * A static_variant of all object types. - * - * Used by field_reflector, this ultimately determines the object - * types which may be inspected by pred_field_lit_cmp. - */ -typedef fc::static_variant< - //null_object, - //base_object, - key_object, - account_object, - asset_object, - force_settlement_object, - delegate_object, - witness_object, - limit_order_object, - call_order_object, - //custom_object, - proposal_object, - operation_history_object, - withdraw_permission_object, - vesting_balance_object, - worker_object - > wild_object; - -} } } diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 6fb26b4a..30cf15d3 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -17,6 +17,7 @@ */ #include #include +#include #include namespace graphene { namespace chain { @@ -813,9 +814,34 @@ share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) return k.membership_annual_fee; } +struct predicate_validator +{ + typedef void result_type; + + template + void operator()( const T& p )const + { + p.validate(); + } +}; + void assert_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); + for( const auto& item : predicates ) + { + FC_ASSERT( item.size() > 0 ); + fc::datastream ds( item.data(), item.size() ); + predicate p; + try { + fc::raw::unpack( ds, p ); + } + catch ( const fc::exception& e ) + { + continue; + } + p.visit( predicate_validator() ); + } } void assert_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { @@ -823,4 +849,12 @@ void assert_operation::get_required_auth(flat_set& active_auth_ active_auth_set.insert(required_auths.begin(), required_auths.end()); } +/** + * The fee for assert operations is proportional to their size + */ +share_type assert_operation::calculate_fee( const fee_schedule_type& k )const +{ + return (k.assert_op_fee * fc::raw::pack_size(*this)) / 1024; +} + } } // namespace graphene::chain diff --git a/libraries/chain/predicate.cpp b/libraries/chain/predicate.cpp deleted file mode 100644 index cedd6fdf..00000000 --- a/libraries/chain/predicate.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include - -#include -#include - -namespace graphene { namespace chain { - -bool pred_field_lit_cmp::check_predicate( const database& db )const -{ - return graphene::chain::impl::cmp_attr_impl( db.get_object( _obj_id ), _field_num, _lit, _opc ); -} - -} } // graphene::chain diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index e61c052b..9627c1cb 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -820,9 +820,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) op.predicates = vector< vector< char > >(); op.predicates.push_back( fc::raw::pack( - predicate( - pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_equal_to ) - ) + predicate( verify_account_name{ nathan_id, "nathan" } ) ) ); trx.operations.push_back(op); trx.sign( nathan_key_id, nathan_private_key ); @@ -831,9 +829,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) // nathan checks that his public key is not equal to the given value (fail) op.predicates.back() = fc::raw::pack( - predicate( - pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_not_equal_to ) - ) + predicate( verify_account_name{ nathan_id, "dan" } ) ); trx.operations.back() = op; trx.sign( nathan_key_id, nathan_private_key ); From 06a9488f8b2df6572e196821b9a4bfc669834ae6 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 10:12:53 -0400 Subject: [PATCH 091/354] Issue #53 - flexible vesting balance creation Use static variant to allow the types of vesting balances to be easily extended and the creation operation allows for many different types of initialization parameters. Added a check that requires a minimum claim date which allows creating of vesting balance objects with a cliff. --- .../include/graphene/chain/operations.hpp | 39 +++++++++++++--- .../include/graphene/chain/predicate.hpp | 4 ++ .../graphene/chain/vesting_balance_object.hpp | 43 +++++++++++++---- libraries/chain/operations.cpp | 1 - libraries/chain/vesting_balance_evaluator.cpp | 46 ++++++++++++++----- libraries/chain/vesting_balance_object.cpp | 4 ++ tests/tests/operation_tests.cpp | 9 ++-- 7 files changed, 112 insertions(+), 34 deletions(-) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 8a322a50..05187a10 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -25,6 +25,10 @@ #include #include #include +#include + +/// TODO: why does this file depend upon database objects? +/// we should remove these headers #include #include #include @@ -1184,6 +1188,25 @@ namespace graphene { namespace chain { } }; + struct linear_vesting_policy_initializer + { + /** while vesting begins on begin_date, none may be claimed before the start_claim time */ + fc::time_point_sec start_claim; + fc::time_point_sec begin_date; + uint32_t vesting_seconds = 0; + }; + + struct ccd_vesting_policy_initializer + { + /** while coindays may accrue over time, none may be claimed before the start_claim time */ + fc::time_point_sec start_claim; + uint32_t vesting_seconds = 0; + ccd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} + }; + + typedef fc::static_variant< linear_vesting_policy_initializer, ccd_vesting_policy_initializer > vesting_policy_initializer; + + /** * @brief Create a vesting balance. * @ingroup operations @@ -1203,11 +1226,11 @@ namespace graphene { namespace chain { */ struct vesting_balance_create_operation { - asset fee; - account_id_type creator; ///< Who provides funds initially - account_id_type owner; ///< Who is able to withdraw the balance - asset amount; - uint32_t vesting_seconds; + asset fee; + account_id_type creator; ///< Who provides funds initially + account_id_type owner; ///< Who is able to withdraw the balance + asset amount; + vesting_policy_initializer policy; account_id_type fee_payer()const { return creator; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; @@ -1220,6 +1243,7 @@ namespace graphene { namespace chain { } }; + /** * @brief Withdraw from a vesting balance. * @ingroup operations @@ -1585,7 +1609,7 @@ FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) -FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(vesting_seconds) ) +FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy) ) FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) FC_REFLECT( graphene::chain::worker_create_operation, @@ -1599,3 +1623,6 @@ FC_REFLECT( graphene::chain::void_result, ) FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT_TYPENAME( graphene::chain::operation_result ) FC_REFLECT_TYPENAME( fc::flat_set ) +FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (start_claim)(begin_date)(vesting_seconds) ) +FC_REFLECT(graphene::chain::ccd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) +FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) diff --git a/libraries/chain/include/graphene/chain/predicate.hpp b/libraries/chain/include/graphene/chain/predicate.hpp index a05d51ab..120caf4b 100644 --- a/libraries/chain/include/graphene/chain/predicate.hpp +++ b/libraries/chain/include/graphene/chain/predicate.hpp @@ -21,6 +21,9 @@ namespace graphene { namespace chain { +bool is_valid_symbol( const string& symbol ); +bool is_valid_name( const string& s ); + class database; /** @@ -66,4 +69,5 @@ typedef static_variant< FC_REFLECT( graphene::chain::verify_account_name, (account_id)(account_name) ) FC_REFLECT( graphene::chain::verify_symbol, (asset_id)(symbol) ) +FC_REFLECT_TYPENAME( graphene::chain::predicate ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 22c10b67..d8019d7d 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -38,20 +38,30 @@ namespace graphene { namespace chain { asset _amount) : balance(_balance), now(_now), amount(_amount) {} - asset balance; + asset balance; fc::time_point_sec now; - asset amount; + asset amount; }; /** - * Linear vesting balance. + * @brief Linear vesting balance with cliff + * + * This vesting balance type is used to mimic traditional stock vesting contracts where + * each day a certain amount vests until it is fully matured. + * + * @note New funds may not be added to a linear vesting balance. */ struct linear_vesting_policy { - uint32_t vesting_seconds; // must be greater than zero + uint32_t vesting_seconds = 0; ///< must be greater than zero + /** while coindays may accrue over time, none may be claimed before first_claim date */ + fc::time_point_sec start_claim; + /** linear vesting may begin prior to allowing the user to actually claim the funds, this + * can be used to create a cliff. + */ fc::time_point_sec begin_date; - share_type begin_balance; // same asset as balance - share_type total_withdrawn; // same asset as balance + share_type begin_balance; ///< same asset as balance + share_type total_withdrawn; ///< same asset as balance asset get_allowed_withdraw(const vesting_policy_context& ctx)const; bool is_deposit_allowed(const vesting_policy_context& ctx)const; @@ -63,10 +73,20 @@ namespace graphene { namespace chain { void on_withdraw(const vesting_policy_context& ctx); }; + + /** + * @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw + * + * The economic effect of this vesting policy is to require a certain amount of "interest" to accrue + * before the full balance may be withdrawn. Interest accrues as coindays (balance * length held). If + * some of the balance is withdrawn, the remaining balance must be held longer. + */ struct cdd_vesting_policy { - uint32_t vesting_seconds; + uint32_t vesting_seconds = 0; fc::uint128_t coin_seconds_earned; + /** while coindays may accrue over time, none may be claimed before first_claim date */ + fc::time_point_sec start_claim; fc::time_point_sec coin_seconds_earned_last_update; /** @@ -97,6 +117,7 @@ namespace graphene { namespace chain { cdd_vesting_policy > vesting_policy; + /** * Vesting balance object is a balance that is locked by the blockchain for a period of time. */ @@ -106,9 +127,9 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = vesting_balance_object_type; - account_id_type owner; - asset balance; - vesting_policy policy; + account_id_type owner; + asset balance; + vesting_policy policy; vesting_balance_object() {} @@ -138,6 +159,7 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::linear_vesting_policy, (vesting_seconds) + (start_claim) (begin_date) (begin_balance) (total_withdrawn) @@ -145,6 +167,7 @@ FC_REFLECT(graphene::chain::linear_vesting_policy, FC_REFLECT(graphene::chain::cdd_vesting_policy, (vesting_seconds) + (start_claim) (coin_seconds_earned) (coin_seconds_earned_last_update) ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 30cf15d3..6167f17d 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -697,7 +697,6 @@ void vesting_balance_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); - FC_ASSERT( vesting_seconds > 0 ); } void vesting_balance_withdraw_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index af521f80..4d1d7ed2 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -36,17 +36,48 @@ object_id_type vesting_balance_create_evaluator::do_evaluate( const vesting_bala FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); - /** we cannot create vesting balances that are market issued due to black swans */ - FC_ASSERT( !op.amount.asset_id(d).is_market_issued() ); - return object_id_type(); } +struct init_policy_visitor +{ + typedef void result_type; + + init_policy_visitor( vesting_policy& po, + const share_type& begin_balance, + const fc::time_point_sec& n ):p(po),init_balance(begin_balance),now(n){} + + vesting_policy& p; + share_type init_balance; + fc::time_point_sec now; + + void operator()( const linear_vesting_policy_initializer& i )const + { + linear_vesting_policy policy; + policy.vesting_seconds = i.vesting_seconds; + policy.begin_date = i.begin_date; + policy.start_claim = i.start_claim; + policy.begin_balance = init_balance; + p = policy; + } + + void operator()( const ccd_vesting_policy_initializer& i )const + { + cdd_vesting_policy policy; + policy.vesting_seconds = i.vesting_seconds; + policy.start_claim = i.start_claim; + policy.coin_seconds_earned = 0; + policy.coin_seconds_earned_last_update = now; + p = policy; + } +}; + object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op ) { database& d = db(); const time_point_sec now = d.head_block_time(); + FC_ASSERT( d.get_balance( op.creator, op.amount.asset_id ) >= op.amount ); d.adjust_balance( op.creator, -op.amount ); const vesting_balance_object& vbo = d.create< vesting_balance_object >( [&]( vesting_balance_object& obj ) @@ -55,16 +86,9 @@ 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; - - cdd_vesting_policy policy; - policy.vesting_seconds = op.vesting_seconds; - policy.coin_seconds_earned = 0; - policy.coin_seconds_earned_last_update = now; - - obj.policy = policy; + op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); - FC_ASSERT( d.get_balance( op.creator, op.amount.asset_id ) >= op.amount ); return vbo.id; } diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index bfc91de5..f57920c2 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -31,6 +31,8 @@ inline bool sum_below_max_shares(const asset& a, const asset& b) asset linear_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx) const { + if(ctx.now <= start_claim) + return asset(0, ctx.balance.asset_id); if(ctx.now <= begin_date) return asset(0, ctx.balance.asset_id); if(vesting_seconds == 0) @@ -96,6 +98,8 @@ void cdd_vesting_policy::update_coin_seconds_earned(const vesting_policy_context asset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx)const { + if(ctx.now <= start_claim) + return asset(0, ctx.balance.asset_id); fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx); fc::uint128_t withdraw_available = cs_earned / vesting_seconds; assert(withdraw_available <= ctx.balance.amount.value); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index b96088d6..0ddde5e4 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1395,7 +1395,8 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) op.creator = account_id_type(); op.owner = account_id_type(); op.amount = test_asset.amount( 100 ); - op.vesting_seconds = 60*60*24; + //op.vesting_seconds = 60*60*24; + op.policy = ccd_vesting_policy_initializer{ 60*60*24 }; // Fee must be non-negative REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount( 1 ) ); @@ -1407,10 +1408,6 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount( 0 ) ); REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount( -1 ) ); - // Min vesting period must be at least 1 sec - REQUIRE_OP_VALIDATION_SUCCESS( op, vesting_seconds, 1 ); - REQUIRE_OP_VALIDATION_FAILURE( op, vesting_seconds, 0 ); - // Setup world state we will need to test actual evaluation const account_object& alice_account = create_account( "alice" ); const account_object& bob_account = create_account( "bob" ); @@ -1488,7 +1485,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) create_op.creator = creator; create_op.owner = owner; create_op.amount = amount; - create_op.vesting_seconds = vesting_seconds; + create_op.policy = ccd_vesting_policy_initializer( vesting_seconds ); tx.operations.push_back( create_op ); processed_transaction ptx = PUSH_TX( db, tx, ~0 ); From da4bf0adc296d837acdcdc65c7bb5f6566e5840a Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 10:33:11 -0400 Subject: [PATCH 092/354] Issue #67 - add name/url to worker create operation --- libraries/chain/include/graphene/chain/config.hpp | 3 +++ libraries/chain/include/graphene/chain/operations.hpp | 4 +++- libraries/chain/operations.cpp | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index e594a7f5..757582b0 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -86,6 +86,9 @@ #define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE 1 #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL +#define GRAPHENE_MAX_WORKER_NAME_LENGTH 63 +#define GRAPHENE_MAX_WORKER_URL_LENGTH 127 + // counter initialization values used to derive near and far future seeds for shuffling witnesses // we use the fractional bits of sqrt(2) in hex #define GRAPHENE_NEAR_SCHEDULE_CTR_IV ( (uint64_t( 0x6a09 ) << 0x30) \ diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 05187a10..f6bf79ab 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1305,6 +1305,8 @@ namespace graphene { namespace chain { time_point_sec work_begin_date; time_point_sec work_end_date; share_type daily_pay; + string name; + string url; /// This should be set to the initializer appropriate for the type of worker to be created. worker_initializer initializer; @@ -1613,7 +1615,7 @@ FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(ow FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) FC_REFLECT( graphene::chain::worker_create_operation, - (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(initializer) ) + (fee)(owner)(work_begin_date)(work_end_date)(daily_pay)(name)(url)(initializer) ) FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths) ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 6167f17d..063ce897 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -778,6 +778,8 @@ void worker_create_operation::validate() const FC_ASSERT(work_end_date > work_begin_date); FC_ASSERT(daily_pay > 0); FC_ASSERT(daily_pay < GRAPHENE_BLOCKCHAIN_MAX_SHARES); + FC_ASSERT(name.size() < GRAPHENE_MAX_WORKER_NAME_LENGTH ); + FC_ASSERT(url.size() < GRAPHENE_MAX_WORKER_URL_LENGTH ); } share_type worker_create_operation::calculate_fee(const fee_schedule_type& k) const From b5b9d6d053cf44c5444ec1e78f7b10b22181ecd0 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 11:08:12 -0400 Subject: [PATCH 093/354] Issue #67 - add url to delegate/witness create operation --- libraries/chain/include/graphene/chain/config.hpp | 3 ++- libraries/chain/include/graphene/chain/operations.hpp | 6 ++++-- libraries/chain/operations.cpp | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 757582b0..149118c4 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -87,7 +87,8 @@ #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL #define GRAPHENE_MAX_WORKER_NAME_LENGTH 63 -#define GRAPHENE_MAX_WORKER_URL_LENGTH 127 + +#define GRAPHENE_MAX_URL_LENGTH 127 // counter initialization values used to derive near and far future seeds for shuffling witnesses // we use the fractional bits of sqrt(2) in hex diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index f6bf79ab..bf7ef5c1 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -314,6 +314,7 @@ namespace graphene { namespace chain { asset fee; /// The account which owns the delegate. This account pays the fee for this operation. account_id_type delegate_account; + string url; account_id_type fee_payer()const { return delegate_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; @@ -334,6 +335,7 @@ namespace graphene { namespace chain { asset fee; /// The account which owns the delegate. This account pays the fee for this operation. account_id_type witness_account; + string url; key_id_type block_signing_key; secret_hash_type initial_secret; @@ -1543,9 +1545,9 @@ FC_REFLECT( graphene::chain::account_whitelist_operation, (fee)(authorizing_acco FC_REFLECT( graphene::chain::account_transfer_operation, (fee)(account_id)(new_owner) ) FC_REFLECT( graphene::chain::delegate_create_operation, - (fee)(delegate_account) ) + (fee)(delegate_account)(url) ) -FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(block_signing_key)(initial_secret) ) +FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) ) FC_REFLECT( graphene::chain::witness_withdraw_pay_operation, (fee)(from_witness)(to_account)(amount) ) FC_REFLECT( graphene::chain::limit_order_create_operation, diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 063ce897..56eb9bcc 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -341,6 +341,7 @@ void delegate_create_operation::get_required_auth(flat_set& act void delegate_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH ); } void asset_fund_fee_pool_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -539,6 +540,7 @@ void witness_create_operation::get_required_auth(flat_set= 0); + FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH ); } share_type witness_create_operation::calculate_fee(const fee_schedule_type& k) const @@ -779,7 +781,7 @@ void worker_create_operation::validate() const FC_ASSERT(daily_pay > 0); FC_ASSERT(daily_pay < GRAPHENE_BLOCKCHAIN_MAX_SHARES); FC_ASSERT(name.size() < GRAPHENE_MAX_WORKER_NAME_LENGTH ); - FC_ASSERT(url.size() < GRAPHENE_MAX_WORKER_URL_LENGTH ); + FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH ); } share_type worker_create_operation::calculate_fee(const fee_schedule_type& k) const From 8e9b2e8ebb1c0d1306fdf63636e772c532564057 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 13:33:13 -0400 Subject: [PATCH 094/354] Issue #68 - created simple secondary index for accounts --- libraries/chain/account_object.cpp | 83 +++++++++++++++++++ libraries/chain/db_init.cpp | 5 +- .../include/graphene/chain/account_object.hpp | 38 +++++++++ libraries/db/include/graphene/db/index.hpp | 39 ++++++++- .../include/graphene/db/object_database.hpp | 4 +- 5 files changed, 165 insertions(+), 4 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index fed6f66e..054fb9bb 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -153,4 +153,87 @@ void account_object::options_type::validate() const "May not specify fewer witnesses or committee members than the number voted for."); } + + + + +set account_member_index::get_members( const account_object& a )const +{ + set result; + for( auto auth : a.owner.auths ) + result.insert(auth.first); + for( auto auth : a.active.auths ) + result.insert(auth.first); + return result; +} + +void account_member_index::object_inserted( const object& obj ) +{ + assert( dynamic_cast(&obj) ); // for debug only + const account_object& a = static_cast(obj); + + set members = get_members(a); + for( auto item : members ) + account_to_memberships[item].insert( obj.id ); +} + +void account_member_index::object_removed( const object& obj ) +{ + assert( dynamic_cast(&obj) ); // for debug only + const account_object& a = static_cast(obj); + + set members = get_members(a); + for( auto item : members ) + account_to_memberships[item].erase( obj.id ); +} + +void account_member_index::about_to_modify( const object& before ) +{ + before_members.clear(); + assert( dynamic_cast(&before) ); // for debug only + const account_object& a = static_cast(before); + before_members = get_members(a); +} + +void account_member_index::object_modified( const object& after ) +{ + assert( dynamic_cast(&after) ); // for debug only + const account_object& a = static_cast(after); + set after_members = get_members(a); + + vector removed; removed.reserve(before_members.size()); + std::set_difference( before_members.begin(), before_members.end(), + after_members.begin(), after_members.end(), + std::inserter( removed, removed.end() ) ); + + for( auto itr = removed.begin(); itr != removed.end(); ++itr ) + account_to_memberships[*itr].erase( after.id ); + + vector added; added.reserve(after_members.size()); + auto add_end = std::set_difference( after_members.begin(), after_members.end(), + before_members.begin(), before_members.end(), + std::inserter( added, added.end()) ); + + for( auto itr = added.begin(); itr != added.end(); ++itr ) + account_to_memberships[*itr].insert( after.id ); +} + + +void account_referrer_index::object_inserted( const object& obj ) +{ +} +void account_referrer_index::object_removed( const object& obj ) +{ +} +void account_referrer_index::about_to_modify( const object& before ) +{ +} +void account_referrer_index::object_modified( const object& after ) +{ +} + + + + + } } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 882234ea..0396f1b3 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -103,7 +103,10 @@ void database::initialize_indexes() //Protocol object indexes add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + auto acnt_index = add_index< primary_index >(); + acnt_index->add_secondary_index(); + acnt_index->add_secondary_index(); + add_index< primary_index> >(); add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 0ec2376a..8fcadbe8 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -256,6 +256,44 @@ class database; delegate_id_type delegate_id; // optional }; + /** + * @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account + * is an potential signing authority. + */ + class account_member_index : public secondary_index + { + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + + /** given an account or key, map it to the set of accounts that reference it in an active or owner authority */ + map< object_id_type, set > account_to_memberships; + + + protected: + set get_members( const account_object& a )const; + set before_members; + }; + + /** + * @brief This secondary index will allow a reverse lookup of all accounts that have been referred by + * a particular account. + */ + class account_referrer_index : public secondary_index + { + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + /** maps the referrer to the set of accounts that they have referred */ + map< account_id_type, set > referred_by; + }; + struct by_asset; struct by_account; struct by_balance; diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index 84c4103a..ae0e61b5 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -123,6 +123,16 @@ namespace graphene { namespace db { }; + class secondary_index + { + public: + virtual ~secondary_index(){}; + virtual void object_inserted( const object& obj ){}; + virtual void object_removed( const object& obj ){}; + virtual void about_to_modify( const object& before ){}; + virtual void object_modified( const object& after ){}; + }; + /** * Defines the common implementation */ @@ -143,13 +153,32 @@ namespace graphene { namespace db { /** called just after obj is modified */ void on_modify( const object& obj ); + template + void add_secondary_index() + { + _sindex.emplace_back( new T() ); + } + + template + const T& get_secondary_index()const + { + for( const auto& item : _sindex ) + { + const T* result = dynamic_cast(item.get()); + if( result != nullptr ) return *result; + } + assert( !"invalid index type" ); + } + protected: - vector< shared_ptr > _observers; + vector< shared_ptr > _observers; + vector< unique_ptr > _sindex; private: object_database& _db; }; + /** * @class primary_index * @brief Wraps a derived index to intercept calls to create, modify, and remove so that @@ -215,12 +244,16 @@ namespace graphene { namespace db { virtual const object& create(const std::function& constructor )override { const auto& result = DerivedIndex::create( constructor ); + for( const auto& item : _sindex ) + item->object_inserted( result ); on_add( result ); return result; } virtual void remove( const object& obj ) override { + for( const auto& item : _sindex ) + item->object_removed( obj ); on_remove(obj); DerivedIndex::remove(obj); } @@ -228,7 +261,11 @@ namespace graphene { namespace db { virtual void modify( const object& obj, const std::function& m )override { save_undo( obj ); + for( const auto& item : _sindex ) + item->about_to_modify( obj ); DerivedIndex::modify( obj, m ); + for( const auto& item : _sindex ) + item->object_modified( obj ); on_modify( obj ); } diff --git a/libraries/db/include/graphene/db/object_database.hpp b/libraries/db/include/graphene/db/object_database.hpp index ff4cc52d..627c8505 100644 --- a/libraries/db/include/graphene/db/object_database.hpp +++ b/libraries/db/include/graphene/db/object_database.hpp @@ -122,7 +122,7 @@ namespace graphene { namespace db { const T& get( object_id id )const { return get(id); } template - const IndexType* add_index() + IndexType* add_index() { typedef typename IndexType::object_type ObjectType; if( _index[ObjectType::space_id].size() <= ObjectType::type_id ) @@ -130,7 +130,7 @@ namespace graphene { namespace db { assert(!_index[ObjectType::space_id][ObjectType::type_id]); unique_ptr indexptr( new IndexType(*this) ); _index[ObjectType::space_id][ObjectType::type_id] = std::move(indexptr); - return static_cast(_index[ObjectType::space_id][ObjectType::type_id].get()); + return static_cast(_index[ObjectType::space_id][ObjectType::type_id].get()); } void pop_undo(); From a0d2b18959c08c96cc707b4a99d827957e79e69e Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 14:05:07 -0400 Subject: [PATCH 095/354] Issue #70 - update docs --- .../chain/include/graphene/chain/asset.hpp | 6 +++--- .../chain/include/graphene/chain/config.hpp | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index d5f12cba..b306833f 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -128,7 +128,7 @@ namespace graphene { namespace chain { /** * Required maintenance collateral is defined * as a fixed point number with a maximum value of 10.000 - * and a minimum value of 1.000. + * and a minimum value of 1.000. (denominated in 1000) * * A black swan event occurs when value_of_collateral equals * value_of_debt, to avoid a black swan a margin call is @@ -145,10 +145,10 @@ namespace graphene { namespace chain { */ price settlement_price; - /** Fixed point between 1.000 and 10.000 */ + /** Fixed point between 1.000 and 10.000, implied fixed point denominator is 1000 */ uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO; - /** Fixed point between 1.000 and 10.000 */ + /** Fixed point between 1.000 and 10.000, implied fixed point denominator is 1000 */ uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO; /** diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 149118c4..b7ea145d 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -48,24 +48,31 @@ #define GRAPHENE_INITIAL_SUPPLY GRAPHENE_BLOCKCHAIN_MAX_SHARES #define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION) #define GRAPHENE_MAX_INSTANCE_ID (uint64_t(-1)>>16) +/** percentage fields are fixed point with a denominator of 10,000 */ #define GRAPHENE_100_PERCENT 10000 #define GRAPHENE_1_PERCENT (GRAPHENE_100_PERCENT/100) /** NOTE: making this a power of 2 (say 2^15) would greatly accelerate fee calcs */ #define GRAPHENE_MAX_MARKET_FEE_PERCENT GRAPHENE_100_PERCENT #define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY (60*60*24) ///< 1 day #define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_OFFSET 0 ///< 1% -#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME 2000 ///< 20% +#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_MAX_VOLUME (20* GRAPHENE_1_PERCENT) ///< 20% #define GRAPHENE_DEFAULT_PRICE_FEED_LIFETIME (60*60*24) ///< 1 day #define GRAPHENE_MAX_FEED_PRODUCERS 200 #define GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP 10 #define GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES 10 #define GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS 10 -// These are NOT percentages -#define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0 -#define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage -#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 // Call when collateral only pays off 175% the debt -#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 // Stop calling when collateral only pays off 150% of the debt +/** + * These ratios are fixed point numbers with a denominator of 1000, the + * minimum maitenance collateral is therefore 1.001x and the default + * maintenance ratio is 1.75x + */ +///@{ +#define GRAPHENE_MIN_COLLATERAL_RATIO 1001 ///< lower than this could result in divide by 0 +#define GRAPHENE_MAX_COLLATERAL_RATIO 32000 ///< higher than this is unnecessary and may exceed int16 storage +#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 ///< Call when collateral only pays off 175% the debt +#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 ///< Stop calling when collateral only pays off 150% of the debt +///@} #define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24) #define GRAPHENE_DEFAULT_NUM_WITNESSES (101) From 08cba191c7f0ce6d89232b3a9a922e5767f1f802 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Tue, 23 Jun 2015 14:19:34 -0400 Subject: [PATCH 096/354] Progress #66: Audit calculate_fee implementations I've audited all the calculate_fee impls and they all should be reasonable at this point. TODO: Set default fee values. --- libraries/chain/db_init.cpp | 6 - .../include/graphene/chain/asset_object.hpp | 3 +- .../include/graphene/chain/operations.hpp | 240 +++++++++--------- .../chain/include/graphene/chain/types.hpp | 136 ++++++---- libraries/chain/operations.cpp | 138 ++++++---- libraries/chain/vesting_balance_evaluator.cpp | 2 +- tests/tests/basic_tests.cpp | 13 + tests/tests/operation_tests.cpp | 32 +-- 8 files changed, 331 insertions(+), 239 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 0396f1b3..a0f07ed0 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -249,8 +249,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) for( const auto& handout : genesis_state.allocation_targets ) total_allocation += handout.weight; - fc::time_point start_time = fc::time_point::now(); - for( const auto& handout : genesis_state.allocation_targets ) { asset amount(handout.weight); @@ -298,10 +296,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); } } - - // fc::microseconds duration = fc::time_point::now() - start_time; - // ilog("Finished allocating to ${n} accounts in ${t} milliseconds.", - // ("n", genesis_state.allocation_targets.size())("t", duration.count() / 1000)); } flat_set init_delegates; diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 63ea1cdd..7f81e274 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -155,7 +155,8 @@ namespace graphene { namespace chain { /** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */ flat_set blacklist_markets; - /** data that describes the meaning/purpose of this asset, fee will be charged proportional to + /** + * data that describes the meaning/purpose of this asset, fee will be charged proportional to * size of description. */ string description; diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index bf7ef5c1..61d22fef 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -28,7 +28,7 @@ #include /// TODO: why does this file depend upon database objects? -/// we should remove these headers +/// we should remove these headers #include #include #include @@ -110,23 +110,23 @@ namespace graphene { namespace chain { * @brief assert that some conditions are true. * @ingroup operations * - * This operation performs no changes to the database state, but can but used to verify - * pre or post conditions for other operations. + * This operation performs no changes to the database state, but can but used to verify + * pre or post conditions for other operations. * */ struct assert_operation { - asset fee; - account_id_type fee_paying_account; - vector< vector< char > > predicates; - flat_set required_auths; + asset fee; + account_id_type fee_paying_account; + vector> predicates; + flat_set required_auths; account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; void validate()const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -141,10 +141,10 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set , flat_set&)const; - share_type calculate_fee( const fee_schedule_type& k )const{ return k.key_create_fee; } + share_type calculate_fee(const fee_schedule_type& k)const{ return k.key_create_fee; } void validate()const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -224,6 +224,10 @@ namespace graphene { namespace chain { /** * @ingroup operations + * @brief Update an existing account + * + * This operation is used to update an existing account. It can be used to update the authorities, or adjust the options on the account. + * See @ref account_object::options_type for the options which may be updated. */ struct account_update_operation { @@ -342,7 +346,7 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return witness_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } }; @@ -362,7 +366,7 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return to_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { @@ -391,7 +395,7 @@ namespace graphene { namespace chain { void get_required_auth(flat_set& active_auth_set, flat_set&)const { active_auth_set.insert(account_id_type()); } void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } }; @@ -469,23 +473,21 @@ namespace graphene { namespace chain { */ struct transfer_operation { - /** paid by the from account, may be of any asset for which there is a funded fee pool - **/ - asset fee; + asset fee; + /// Account to transfer asset from account_id_type from; + /// Account to transfer asset to account_id_type to; - /** the amount and asset type that will be withdrawn from account "from" and added to account "to" - * - **/ - asset amount; + /// The amount of asset to transfer from @ref from to @ref to + asset amount; - /** user provided data encrypted to the memo key of the "to" account */ - optional memo; + /// User provided data encrypted to the memo key of the "to" account + optional memo; account_id_type fee_payer()const { return from; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); @@ -523,7 +525,7 @@ namespace graphene { namespace chain { void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -546,8 +548,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return issuer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -574,8 +576,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); acc.adjust( account, -amount ); @@ -595,11 +597,11 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return from_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( fee_payer(), -amount ); + acc.adjust(fee_payer(), -fee); + acc.adjust(fee_payer(), -amount); } }; @@ -635,8 +637,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return issuer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -663,8 +665,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return issuer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -695,10 +697,9 @@ namespace graphene { namespace chain { void get_required_auth(flat_set& active_auth_set, flat_set&)const { active_auth_set.insert(fee_payer()); } void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const - { return k.asset_update_fee; } - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const - { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const + { acc.adjust(fee_payer(), -fee); } }; /** @@ -725,10 +726,10 @@ namespace graphene { namespace chain { price_feed feed; account_id_type fee_payer()const { return publisher; } - void get_required_auth( flat_set& active_auth_set, flat_set& )const; + void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset() )const { acc.adjust( fee_payer(), -fee ); } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -748,9 +749,9 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return issuer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const - { acc.adjust( fee_payer(), -fee ); acc.adjust(issue_to_account, asset_to_issue); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const + { acc.adjust(fee_payer(), -fee); acc.adjust(issue_to_account, asset_to_issue); } }; /** @@ -768,11 +769,11 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return payer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( fee_payer(), -amount_to_burn ); + acc.adjust(fee_payer(), -fee); + acc.adjust(fee_payer(), -amount_to_burn); } }; @@ -800,31 +801,29 @@ namespace graphene { namespace chain { account_id_type seller; asset amount_to_sell; asset min_to_receive; - /** - * This order should expire if not filled by expiration - */ - time_point_sec expiration = time_point_sec::maximum(); - /** if this flag is set the entire order must be filled or - * the operation is rejected. - */ - bool fill_or_kill = false; + /// The order will be removed from the books if not filled by expiration + /// Upon expiration, all unsold asset will be returned to seller + time_point_sec expiration = time_point_sec::maximum(); + + /// If this flag is set the entire order must be filled or the operation is rejected + bool fill_or_kill = false; pair get_market()const { return amount_to_sell.asset_id < min_to_receive.asset_id ? - std::make_pair( amount_to_sell.asset_id, min_to_receive.asset_id ) : - std::make_pair( min_to_receive.asset_id, amount_to_sell.asset_id ); + std::make_pair(amount_to_sell.asset_id, min_to_receive.asset_id) : + std::make_pair(min_to_receive.asset_id, amount_to_sell.asset_id); } account_id_type fee_payer()const { return seller; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; + share_type calculate_fee(const fee_schedule_type& k)const; price get_price()const { return amount_to_sell / min_to_receive; } - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( seller, -amount_to_sell ); + acc.adjust(fee_payer(), -fee); + acc.adjust(seller, -amount_to_sell); } }; @@ -846,12 +845,11 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( fee_payer(), result.get() ); + acc.adjust(fee_payer(), -fee); + acc.adjust(fee_payer(), result.get()); } }; @@ -878,12 +876,12 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return funding_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( funding_account, -delta_collateral ); - acc.adjust( funding_account, delta_debt ); + acc.adjust(fee_payer(), -fee); + acc.adjust(funding_account, -delta_collateral); + acc.adjust(funding_account, delta_debt); } }; @@ -943,9 +941,9 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth( flat_set& active_auth_set, flat_set& )const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const { return 0; } - - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const + { acc.adjust(fee_payer(), -fee); } }; /** @@ -981,8 +979,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set& owner_auth_set)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const { return 0; } - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -1006,8 +1004,9 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set& owner_auth_set)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const { return 0; } - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const + { return k.proposal_delete_fee; } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; ///@} @@ -1037,10 +1036,12 @@ namespace graphene { namespace chain { void get_required_auth(flat_set& active_auth_set, flat_set&)const { active_auth_set.insert(fee_payer()); } void validate()const { FC_ASSERT( !"virtual operation" ); } - share_type calculate_fee( const fee_schedule_type& k )const { return share_type(); } - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { - // acc.adjust( fee_payer(), -fee ); fee never actually entered the account, this is a virtual operation - acc.adjust( account_id, receives ); + share_type calculate_fee(const fee_schedule_type& k)const + // This is a virtual operation; there is no fee + { return 0; } + + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { + acc.adjust(account_id, receives); } }; @@ -1082,8 +1083,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return withdraw_from_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -1118,8 +1119,8 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return withdraw_from_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** @@ -1153,12 +1154,12 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return withdraw_to_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( withdraw_to_account, amount_to_withdraw ); - acc.adjust( withdraw_from_account, -amount_to_withdraw ); + acc.adjust(fee_payer(), -fee); + acc.adjust(withdraw_to_account, amount_to_withdraw); + acc.adjust(withdraw_from_account, -amount_to_withdraw); } }; @@ -1183,14 +1184,14 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return withdraw_from_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); + acc.adjust(fee_payer(), -fee); } }; - struct linear_vesting_policy_initializer + struct linear_vesting_policy_initializer { /** while vesting begins on begin_date, none may be claimed before the start_claim time */ fc::time_point_sec start_claim; @@ -1198,16 +1199,15 @@ namespace graphene { namespace chain { uint32_t vesting_seconds = 0; }; - struct ccd_vesting_policy_initializer + struct cdd_vesting_policy_initializer { /** while coindays may accrue over time, none may be claimed before the start_claim time */ fc::time_point_sec start_claim; uint32_t vesting_seconds = 0; - ccd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} + cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} }; - typedef fc::static_variant< linear_vesting_policy_initializer, ccd_vesting_policy_initializer > vesting_policy_initializer; - + typedef fc::static_variant vesting_policy_initializer; /** * @brief Create a vesting balance. @@ -1229,19 +1229,19 @@ namespace graphene { namespace chain { struct vesting_balance_create_operation { asset fee; - account_id_type creator; ///< Who provides funds initially - account_id_type owner; ///< Who is able to withdraw the balance + account_id_type creator; ///< Who provides funds initially + account_id_type owner; ///< Who is able to withdraw the balance asset amount; vesting_policy_initializer policy; account_id_type fee_payer()const { return creator; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( creator, -amount ); + acc.adjust(fee_payer(), -fee); + acc.adjust(creator, -amount); } }; @@ -1259,17 +1259,17 @@ namespace graphene { namespace chain { { asset fee; vesting_balance_id_type vesting_balance; - account_id_type owner; ///< Must be vesting_balance.owner + account_id_type owner; ///< Must be vesting_balance.owner asset amount; account_id_type fee_payer()const { return owner; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); - acc.adjust( owner, amount ); + acc.adjust(fee_payer(), -fee); + acc.adjust(owner, amount); } }; @@ -1315,10 +1315,10 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return owner; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); + acc.adjust(fee_payer(), -fee); } }; @@ -1342,10 +1342,10 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return payer; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; void validate()const; - share_type calculate_fee( const fee_schedule_type& k )const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { - acc.adjust( fee_payer(), -fee ); + acc.adjust(fee_payer(), -fee); } }; @@ -1628,5 +1628,5 @@ FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT_TYPENAME( graphene::chain::operation_result ) FC_REFLECT_TYPENAME( fc::flat_set ) FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (start_claim)(begin_date)(vesting_seconds) ) -FC_REFLECT(graphene::chain::ccd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) +FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index c99e8ca9..db8c9334 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -18,7 +18,6 @@ #pragma once #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -337,45 +337,77 @@ namespace graphene { namespace chain { fee_schedule_type() { - memset( (char*)this, 0, sizeof(*this) ); + memset((char*)this, 0, sizeof(*this)); + } + + /// The number of bytes to charge a data fee for + const static int BYTES_PER_DATA_FEE = 1024; + + template + uint32_t total_data_fee(Ts... ts)const { + return data_size(ts...) / BYTES_PER_DATA_FEE * data_fee; } uint32_t key_create_fee; ///< the cost to register a public key with the blockchain uint32_t account_create_fee; ///< the cost to register the cheapest non-free account - uint32_t account_len8_fee; + uint32_t account_update_fee; ///< the cost to update an existing account + uint32_t account_transfer_fee; ///< the cost to transfer an account to a new owner + uint32_t account_len8up_fee; uint32_t account_len7_fee; uint32_t account_len6_fee; uint32_t account_len5_fee; uint32_t account_len4_fee; uint32_t account_len3_fee; uint32_t account_len2_fee; - uint32_t account_premium_fee; ///< accounts with premium names; i.e. @ref is_cheap_name returns false + uint32_t asset_len3_fee; + uint32_t asset_len4_fee; + uint32_t asset_len5_fee; + uint32_t asset_len6_fee; + uint32_t asset_len7up_fee; uint32_t account_whitelist_fee; ///< the fee to whitelist an account - uint32_t delegate_create_fee; ///< fixed fee for registering as a delegate; used to discourage frivioulous delegates + uint32_t delegate_create_fee; ///< fixed fee for registering as a delegate; used to discourage frivolous delegates uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay uint32_t transfer_fee; ///< fee for transferring some asset - uint32_t limit_order_fee; ///< fee for placing a limit order in the markets + uint32_t limit_order_create_fee; ///< fee for placing a limit order in the markets + uint32_t limit_order_cancel_fee; ///< fee for canceling a limit order uint32_t call_order_fee; ///< fee for placing a call order in the markets uint32_t publish_feed_fee; ///< fee for publishing a price feed uint32_t asset_create_fee; ///< the cost to register the cheapest asset uint32_t asset_update_fee; ///< the cost to modify a registered asset uint32_t asset_issue_fee; ///< the cost to modify a registered asset + uint32_t asset_burn_fee; ///< the cost to burn an asset uint32_t asset_fund_fee_pool_fee; ///< the cost to add funds to an asset's fee pool uint32_t asset_settle_fee; ///< the cost to trigger a forced settlement of a market-issued asset uint32_t market_fee; ///< a percentage charged on market orders uint32_t transaction_fee; ///< a base price for every transaction - uint32_t data_fee; ///< a price per 1024 bytes of user data + uint32_t data_fee; ///< a price per BYTES_PER_DATA_FEE bytes of user data uint32_t signature_fee; ///< a surcharge on transactions with more than 2 signatures. uint32_t global_parameters_update_fee; ///< the cost to update the global parameters uint32_t membership_annual_fee; ///< the annual cost of a membership subscription uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member - uint32_t withdraw_permission_update_fee; ///< the cost to create/update a withdraw permission + uint32_t withdraw_permission_create_fee; ///< the cost to create a withdraw permission + uint32_t withdraw_permission_update_fee; ///< the cost to update a withdraw permission + uint32_t withdraw_permission_claim_fee; ///< the cost to withdraw from a withdraw permission + uint32_t withdraw_permission_delete_fee; ///< the cost to delete a withdraw permission uint32_t vesting_balance_create_fee; uint32_t vesting_balance_withdraw_fee; uint32_t global_settle_fee; uint32_t worker_create_fee; ///< the cost to create a new worker uint32_t worker_delete_fee; ///< the cost to delete a worker uint32_t assert_op_fee; ///< fee per assert operation + uint32_t proposal_create_fee; ///< fee for creating a proposed transaction + uint32_t proposal_update_fee; ///< fee for adding or removing approval of a proposed transaction + uint32_t proposal_delete_fee; ///< fee for deleting a proposed transaction + uint32_t custom_operation_fee; ///< fee for a custom operation + + protected: + size_t data_size()const { + return 0; + } + template + size_t data_size(T t, Ts... ts)const { + return fc::raw::pack_size(t) + data_size(ts...); + } }; @@ -515,43 +547,57 @@ FC_REFLECT_ENUM( graphene::chain::meta_info_object_type, (meta_account_object_ty FC_REFLECT( graphene::chain::fee_schedule_type, - (key_create_fee) - (account_create_fee) - (account_len8_fee) - (account_len7_fee) - (account_len6_fee) - (account_len5_fee) - (account_len4_fee) - (account_len3_fee) - (account_len2_fee) - (account_premium_fee) - (account_whitelist_fee) - (delegate_create_fee) - (witness_withdraw_pay_fee) - (transfer_fee) - (limit_order_fee) - (call_order_fee) - (publish_feed_fee) - (asset_create_fee) - (asset_update_fee) - (asset_issue_fee) - (asset_fund_fee_pool_fee) - (asset_settle_fee) - (market_fee) - (transaction_fee) - (data_fee) - (signature_fee) - (global_parameters_update_fee) - (membership_annual_fee) - (membership_lifetime_fee) - (withdraw_permission_update_fee) - (vesting_balance_create_fee) - (vesting_balance_withdraw_fee) - (global_settle_fee) - (worker_create_fee) - (worker_delete_fee) - (assert_op_fee) - ) + (key_create_fee) + (account_create_fee) + (account_update_fee) + (account_transfer_fee) + (account_len8up_fee) + (account_len7_fee) + (account_len6_fee) + (account_len5_fee) + (account_len4_fee) + (account_len3_fee) + (account_len2_fee) + (asset_len3_fee) + (asset_len4_fee) + (asset_len5_fee) + (asset_len6_fee) + (asset_len7up_fee) + (account_whitelist_fee) + (delegate_create_fee) + (witness_withdraw_pay_fee) + (transfer_fee) + (limit_order_create_fee) + (limit_order_cancel_fee) + (call_order_fee) + (publish_feed_fee) + (asset_create_fee) + (asset_update_fee) + (asset_issue_fee) + (asset_burn_fee) + (asset_fund_fee_pool_fee) + (asset_settle_fee) + (market_fee) + (transaction_fee) + (data_fee) + (signature_fee) + (global_parameters_update_fee) + (membership_annual_fee) + (membership_lifetime_fee) + (withdraw_permission_create_fee) + (withdraw_permission_update_fee) + (withdraw_permission_claim_fee) + (withdraw_permission_delete_fee) + (vesting_balance_create_fee) + (vesting_balance_withdraw_fee) + (global_settle_fee) + (worker_create_fee) + (worker_delete_fee) + (assert_op_fee) + (proposal_create_fee) + (proposal_update_fee) + (proposal_delete_fee) + ) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 56eb9bcc..1fa19be1 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -112,12 +112,13 @@ share_type account_create_operation::calculate_fee( const fee_schedule_type& sch auto core_fee_required = schedule.account_create_fee; uint32_t s = name.size(); - if( is_cheap_name( name ) ) s = 63; + if( is_cheap_name(name) ) + s = 63; FC_ASSERT( s >= 2 ); if( s >= 8 && s < 63 ) - core_fee_required = schedule.account_len8_fee; + core_fee_required = schedule.account_len8up_fee; else if( s == 7 ) core_fee_required = schedule.account_len7_fee; else if( s == 6 ) @@ -131,19 +132,25 @@ share_type account_create_operation::calculate_fee( const fee_schedule_type& sch else if( s == 2 ) core_fee_required = schedule.account_len2_fee; + // Authorities and vote lists can be arbitrarily large, so charge a data fee for big ones + core_fee_required += schedule.total_data_fee(active, owner, options.votes); + return core_fee_required; } share_type account_update_operation::calculate_fee( const fee_schedule_type& schedule )const { - return schedule.account_create_fee; + auto core_fee_required = schedule.account_update_fee + schedule.total_data_fee(owner, active); + if( new_options ) + core_fee_required += schedule.total_data_fee(new_options->votes); + return core_fee_required; } void account_update_operation::get_required_auth(flat_set& active_auth_set, flat_set& owner_auth_set) const { if( owner || active ) - owner_auth_set.insert( account ); + owner_auth_set.insert(account); else - active_auth_set.insert( account ); + active_auth_set.insert(account); } void account_update_operation::validate()const @@ -156,14 +163,25 @@ void account_update_operation::validate()const new_options->validate(); } -share_type asset_create_operation::calculate_fee( const fee_schedule_type& schedule )const +share_type asset_create_operation::calculate_fee(const fee_schedule_type& schedule)const { auto core_fee_required = schedule.asset_create_fee; - uint32_t s = symbol.size(); - while( s <= 6 ) { core_fee_required *= 30; ++s; } + switch(symbol.size()) { + case 3: core_fee_required += schedule.asset_len3_fee; + break; + case 4: core_fee_required += schedule.asset_len4_fee; + break; + case 5: core_fee_required += schedule.asset_len5_fee; + break; + case 6: core_fee_required += schedule.asset_len6_fee; + break; + default: core_fee_required += schedule.asset_len7up_fee; + } + + // common_options contains several lists and a string. Charge fees for its size + core_fee_required += schedule.total_data_fee(common_options); - core_fee_required += ((fc::raw::pack_size(*this)*schedule.data_fee)/1024); return core_fee_required; } @@ -171,9 +189,7 @@ share_type transfer_operation::calculate_fee( const fee_schedule_type& schedule { share_type core_fee_required = schedule.transfer_fee; if( memo ) - { - core_fee_required += share_type((memo->message.size() * schedule.data_fee)/1024); - } + core_fee_required += schedule.total_data_fee(memo->message); return core_fee_required; } @@ -249,7 +265,7 @@ void asset_create_operation::get_required_auth(flat_set& active void asset_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( is_valid_symbol( symbol ) ); + FC_ASSERT( is_valid_symbol(symbol) ); common_options.validate(); if( common_options.issuer_permissions & (disable_force_settle|global_settle) ) FC_ASSERT( bitasset_options.valid() ); @@ -288,9 +304,9 @@ void asset_update_operation::validate()const FC_ASSERT(dummy.asset_id == asset_id_type()); } -share_type asset_update_operation::calculate_fee( const fee_schedule_type& k )const +share_type asset_update_operation::calculate_fee(const fee_schedule_type& k)const { - return k.asset_update_fee + ((fc::raw::pack_size(*this)*k.data_fee)/1024); + return k.asset_update_fee + k.total_data_fee(new_options); } void asset_burn_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -305,9 +321,9 @@ void asset_burn_operation::validate()const FC_ASSERT( amount_to_burn.amount.value > 0 ); } -share_type asset_burn_operation::calculate_fee( const fee_schedule_type& k )const +share_type asset_burn_operation::calculate_fee(const fee_schedule_type& k)const { - return k.asset_issue_fee; + return k.asset_burn_fee; } void asset_issue_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -330,7 +346,7 @@ share_type asset_issue_operation::calculate_fee( const fee_schedule_type& k )con share_type delegate_create_operation::calculate_fee( const fee_schedule_type& k )const { - return k.delegate_create_fee ; + return k.delegate_create_fee + k.total_data_fee(url); } void delegate_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -376,7 +392,7 @@ void limit_order_create_operation::validate()const share_type limit_order_create_operation::calculate_fee(const fee_schedule_type& k) const { - return k.limit_order_fee; + return k.limit_order_create_fee; } void limit_order_cancel_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -391,10 +407,9 @@ void limit_order_cancel_operation::validate()const share_type limit_order_cancel_operation::calculate_fee(const fee_schedule_type& k) const { - return k.limit_order_fee; + return k.limit_order_create_fee; } - void call_order_update_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { active_auth_set.insert(funding_account); @@ -433,6 +448,11 @@ void proposal_create_operation::validate() const for( const auto& op : proposed_ops ) op.validate(); } +share_type proposal_create_operation::calculate_fee(const fee_schedule_type& k) const +{ + return k.proposal_create_fee + k.total_data_fee(proposed_ops); +} + void proposal_update_operation::get_required_auth(flat_set& active_auth_set, flat_set& owner_auth_set) const { @@ -470,13 +490,23 @@ void proposal_update_operation::validate() const } } +share_type proposal_update_operation::calculate_fee(const fee_schedule_type& k) const +{ + return k.proposal_create_fee + k.total_data_fee(active_approvals_to_add, + active_approvals_to_remove, + owner_approvals_to_add, + owner_approvals_to_remove, + key_approvals_to_add, + key_approvals_to_remove); +} + void proposal_delete_operation::get_required_auth(flat_set& active_auth_set, flat_set& owner_auth_set) const { - if( using_owner_authority ) - owner_auth_set.insert(fee_paying_account); - else - active_auth_set.insert(fee_paying_account); + if( using_owner_authority ) + owner_auth_set.insert(fee_paying_account); + else + active_auth_set.insert(fee_paying_account); } void account_transfer_operation::validate()const @@ -491,7 +521,7 @@ void account_transfer_operation::get_required_auth(flat_set& ac share_type account_transfer_operation::calculate_fee( const fee_schedule_type& k )const { - return k.transfer_fee; + return k.account_transfer_fee; } @@ -545,7 +575,7 @@ void witness_create_operation::validate() const share_type witness_create_operation::calculate_fee(const fee_schedule_type& k) const { - return k.delegate_create_fee; + return k.delegate_create_fee + k.total_data_fee(url); } void withdraw_permission_update_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const @@ -579,11 +609,11 @@ void withdraw_permission_claim_operation::validate()const FC_ASSERT( fee.amount >= 0 ); } -share_type withdraw_permission_claim_operation::calculate_fee( const fee_schedule_type& schedule )const +share_type withdraw_permission_claim_operation::calculate_fee(const fee_schedule_type& k)const { - share_type core_fee_required = schedule.transfer_fee; + share_type core_fee_required = k.withdraw_permission_claim_fee; if( memo ) - core_fee_required += share_type((memo->message.size() * schedule.data_fee)/1024); + core_fee_required += k.total_data_fee(memo->message); return core_fee_required; } @@ -600,7 +630,7 @@ void withdraw_permission_delete_operation::validate() const share_type withdraw_permission_delete_operation::calculate_fee(const fee_schedule_type& k) const { - return k.withdraw_permission_update_fee; + return k.withdraw_permission_delete_fee; } void withdraw_permission_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -620,7 +650,7 @@ void withdraw_permission_create_operation::validate() const share_type withdraw_permission_create_operation::calculate_fee(const fee_schedule_type& k) const { - return k.withdraw_permission_update_fee; + return k.withdraw_permission_create_fee; } @@ -636,7 +666,7 @@ void asset_global_settle_operation::validate()const FC_ASSERT( asset_to_settle == settle_price.base.asset_id ); } -share_type asset_global_settle_operation::calculate_fee( const fee_schedule_type& k )const +share_type asset_global_settle_operation::calculate_fee(const fee_schedule_type& k)const { return k.global_settle_fee; } @@ -684,15 +714,21 @@ void asset_update_feed_producers_operation::validate() const FC_ASSERT( fee.amount >= 0 ); } -void vesting_balance_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +share_type asset_update_feed_producers_operation::calculate_fee(const fee_schedule_type &k) const { - // owner's authorization isn't needed since this is effectively a transfer of value TO the owner - active_auth_set.insert( creator ); + return k.asset_update_fee + k.total_data_fee(new_feed_producers); } -share_type vesting_balance_create_operation::calculate_fee( const fee_schedule_type& k )const +void vesting_balance_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { - return k.vesting_balance_create_fee; + // owner's authorization isn't needed since this is effectively a transfer of value TO the owner + active_auth_set.insert(creator); +} + +share_type vesting_balance_create_operation::calculate_fee(const fee_schedule_type& k)const +{ + // We don't want to have custom inspection for each policy type; instead, charge a data fee for big ones + return k.vesting_balance_create_fee + k.total_data_fee(policy); } void vesting_balance_create_operation::validate()const @@ -703,7 +739,7 @@ void vesting_balance_create_operation::validate()const void vesting_balance_withdraw_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { - active_auth_set.insert( owner ); + active_auth_set.insert(owner); } void vesting_balance_withdraw_operation::validate()const @@ -712,7 +748,7 @@ void vesting_balance_withdraw_operation::validate()const FC_ASSERT( amount.amount > 0 ); } -share_type vesting_balance_withdraw_operation::calculate_fee( const fee_schedule_type& k )const +share_type vesting_balance_withdraw_operation::calculate_fee(const fee_schedule_type& k)const { return k.vesting_balance_withdraw_fee; } @@ -764,9 +800,9 @@ void custom_operation::validate()const { FC_ASSERT( fee.amount > 0 ); } -share_type custom_operation::calculate_fee( const fee_schedule_type& k )const +share_type custom_operation::calculate_fee(const fee_schedule_type& k)const { - return (data.size() * k.data_fee)/1024; + return k.custom_operation_fee + k.total_data_fee(required_auths, data); } void worker_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const @@ -786,7 +822,8 @@ void worker_create_operation::validate() const share_type worker_create_operation::calculate_fee(const fee_schedule_type& k) const { - return k.worker_create_fee; + // Charge data fees for excessively long name, URL, or large initializers + return k.worker_create_fee + k.total_data_fee(name, url, initializer); } string memo_message::serialize() const @@ -817,10 +854,10 @@ share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) return k.membership_annual_fee; } -struct predicate_validator +struct predicate_validator { typedef void result_type; - + template void operator()( const T& p )const { @@ -838,7 +875,7 @@ void assert_operation::validate()const predicate p; try { fc::raw::unpack( ds, p ); - } + } catch ( const fc::exception& e ) { continue; @@ -853,11 +890,12 @@ void assert_operation::get_required_auth(flat_set& active_auth_ } /** - * The fee for assert operations is proportional to their size + * The fee for assert operations is proportional to their size, + * but cheaper than a data fee because they require no storage */ -share_type assert_operation::calculate_fee( const fee_schedule_type& k )const -{ - return (k.assert_op_fee * fc::raw::pack_size(*this)) / 1024; +share_type assert_operation::calculate_fee(const fee_schedule_type& k)const +{ + return std::max(size_t(1), fc::raw::pack_size(*this) / 1024) * k.assert_op_fee; } } } // namespace graphene::chain diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 4d1d7ed2..7589f0a9 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -61,7 +61,7 @@ struct init_policy_visitor p = policy; } - void operator()( const ccd_vesting_policy_initializer& i )const + void operator()( const cdd_vesting_policy_initializer& i )const { cdd_vesting_policy policy; policy.vesting_seconds = i.vesting_seconds; diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index 8832d396..c19d0b88 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -238,4 +238,17 @@ BOOST_AUTO_TEST_CASE( witness_rng_test_bits ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( data_fees ) +{ + fee_schedule_type fs; + fs.data_fee = 10; + int x = 1489520937; + BOOST_CHECK_EQUAL(fs.total_data_fee(), 0); + BOOST_CHECK_EQUAL(fs.total_data_fee(fs), 0); + BOOST_CHECK_EQUAL(fs.total_data_fee(fs.key_create_fee), 0); + BOOST_CHECK_EQUAL(fs.total_data_fee(x, fs, authority()), 0); + auto keys = vector(100, private_key_type::generate()); + BOOST_CHECK_EQUAL(fs.total_data_fee(keys, x), (fc::raw::pack_size(keys) + fc::raw::pack_size(x)) / 1024 * 10); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 0ddde5e4..50a0573e 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) // require 3x min collateral auto swanp = usd / core; auto callp = ~price::call_price( usd, core, 1750 ); - // 1:1 collateral + // 1:1 collateral // wdump((callp.to_real())(callp)); // wdump((swanp.to_real())(swanp)); FC_ASSERT( callp.to_real() > swanp.to_real() ); @@ -1388,7 +1388,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) INVOKE( create_uia ); const asset_object& core = asset_id_type()(db); - const asset_object& test_asset = get_asset( "TEST" ); + const asset_object& test_asset = get_asset("TEST"); vesting_balance_create_operation op; op.fee = core.amount( 0 ); @@ -1396,36 +1396,36 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) op.owner = account_id_type(); op.amount = test_asset.amount( 100 ); //op.vesting_seconds = 60*60*24; - op.policy = ccd_vesting_policy_initializer{ 60*60*24 }; + op.policy = cdd_vesting_policy_initializer{ 60*60*24 }; // Fee must be non-negative - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount( 1 ) ); - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount( 0 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount( -1 ) ); + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) ); + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(0) ); + REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount(-1) ); // Amount must be positive - REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount( 1 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount( 0 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount( -1 ) ); + REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount(1) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(0) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(-1) ); // Setup world state we will need to test actual evaluation - const account_object& alice_account = create_account( "alice" ); - const account_object& bob_account = create_account( "bob" ); + const account_object& alice_account = create_account("alice"); + const account_object& bob_account = create_account("bob"); - transfer( genesis_account(db), alice_account, core.amount( 100000 ) ); + transfer(genesis_account(db), alice_account, core.amount(100000)); op.creator = alice_account.get_id(); op.owner = alice_account.get_id(); - account_id_type nobody = account_id_type( 1234 ); + account_id_type nobody = account_id_type(1234); - trx.operations.push_back( op ); + trx.operations.push_back(op); // Invalid account_id's REQUIRE_THROW_WITH_VALUE( op, creator, nobody ); REQUIRE_THROW_WITH_VALUE( op, owner, nobody ); // Insufficient funds - REQUIRE_THROW_WITH_VALUE( op, amount, core.amount( 999999999 ) ); + REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(999999999) ); // Alice can fund a bond to herself or to Bob op.amount = core.amount( 1000 ); REQUIRE_OP_EVALUATION_SUCCESS( op, owner, alice_account.get_id() ); @@ -1485,7 +1485,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) create_op.creator = creator; create_op.owner = owner; create_op.amount = amount; - create_op.policy = ccd_vesting_policy_initializer( vesting_seconds ); + create_op.policy = cdd_vesting_policy_initializer(vesting_seconds); tx.operations.push_back( create_op ); processed_transaction ptx = PUSH_TX( db, tx, ~0 ); From 548c760e46c568a615cf557a02e3909d78eeaa4d Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 22 Jun 2015 14:16:34 -0400 Subject: [PATCH 097/354] wip burn worker --- .../chain/include/graphene/chain/config.hpp | 5 +++ .../include/graphene/chain/worker_object.hpp | 31 ++++++++++++++++--- libraries/chain/worker_object.cpp | 6 ++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index b7ea145d..2aa1b1ab 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -130,9 +130,14 @@ * Reserved Account IDs with special meaning */ ///@{ +/// Represents the current committee members, two-week review period (GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC) #define GRAPHENE_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(0)) +/// Represents the current witnesses #define GRAPHENE_WITNESS_ACCOUNT (graphene::chain::account_id_type(1)) +/// Represents the current committee members #define GRAPHENE_RELAXED_COMMITTEE_ACCOUNT (graphene::chain::account_id_type(2)) +/// Represents the canonical account with NO authority (nobody can access funds in null account) #define GRAPHENE_NULL_ACCOUNT (graphene::chain::account_id_type(3)) +/// Represents the canonical account with WILDCARD authority (anybody can access funds in temp account) #define GRAPHENE_TEMP_ACCOUNT (graphene::chain::account_id_type(4)) ///@} diff --git a/libraries/chain/include/graphene/chain/worker_object.hpp b/libraries/chain/include/graphene/chain/worker_object.hpp index fe9fe842..3204c8b3 100644 --- a/libraries/chain/include/graphene/chain/worker_object.hpp +++ b/libraries/chain/include/graphene/chain/worker_object.hpp @@ -52,9 +52,9 @@ namespace graphene { namespace chain { * @{ */ /** - * @brief A worker who burns all of his pay + * @brief A worker who returns all of his pay to the reserve * - * This worker type burns all pay he receives, paying it back to the network's reserve funds pool. + * This worker type pays everything he receives back to the network's reserve funds pool. */ struct refund_worker_type { @@ -92,16 +92,37 @@ namespace graphene { namespace chain { uint16_t pay_vesting_period_days; }; }; + + /** + * @brief A worker who permanently destroys all of his pay + * + * This worker sends all pay he receives to the null account. + */ + struct burn_worker_type + { + /// Record of how much this worker has burned in his lifetime + share_type total_burned; + + void pay_worker(share_type pay, database&); + + struct initializer + { + void init(database&, const worker_object&, burn_worker_type& worker)const + {} + }; + }; ///@} // The ordering of types in these two static variants MUST be the same. typedef static_variant< refund_worker_type, - vesting_balance_worker_type + vesting_balance_worker_type, + burn_worker_type > worker_type; typedef static_variant< refund_worker_type::initializer, - vesting_balance_worker_type::initializer + vesting_balance_worker_type::initializer, + burn_worker_type::initializer > worker_initializer; /// @brief A visitor for @ref worker_type which initializes the worker within @@ -188,6 +209,8 @@ FC_REFLECT( graphene::chain::refund_worker_type, (total_burned) ) FC_REFLECT( graphene::chain::refund_worker_type::initializer, ) FC_REFLECT( graphene::chain::vesting_balance_worker_type, (balance) ) FC_REFLECT( graphene::chain::vesting_balance_worker_type::initializer, (pay_vesting_period_days) ) +FC_REFLECT( graphene::chain::burn_worker_type, (total_burned) ) +FC_REFLECT( graphene::chain::burn_worker_type::initializer, ) FC_REFLECT_TYPENAME( graphene::chain::worker_type ) FC_REFLECT_TYPENAME( graphene::chain::worker_initializer ) FC_REFLECT_DERIVED( graphene::chain::worker_object, (graphene::db::object), diff --git a/libraries/chain/worker_object.cpp b/libraries/chain/worker_object.cpp index dcb8554b..4f7a6801 100644 --- a/libraries/chain/worker_object.cpp +++ b/libraries/chain/worker_object.cpp @@ -50,4 +50,10 @@ void vesting_balance_worker_type::initializer::init(database& db, const worker_o }).id; } +void burn_worker_type::pay_worker(share_type pay, database& db) +{ + total_burned += pay; + db.adjust_balance( GRAPHENE_NULL_ACCOUNT, pay ); +} + } } // graphene::chain From c43c71372d2371c0d984e670bd516142229e0c94 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 13:11:38 -0400 Subject: [PATCH 098/354] assert_evaluator.cpp: Remove skip_unknown_predicate flag, add max_predicate_opcode check --- libraries/chain/assert_evaluator.cpp | 22 ++++++++++--------- .../chain/include/graphene/chain/database.hpp | 3 +-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index aa6862c4..f7c44dd9 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -24,13 +24,13 @@ namespace graphene { namespace chain { -struct predicate_visitor +struct predicate_visitor { typedef bool result_type; const database& db; predicate_visitor( const database& d ):db(d){} - + bool operator()( const verify_account_name& p )const { return p.account_id(db).name == p.account_name; @@ -45,6 +45,7 @@ void_result assert_evaluator::do_evaluate( const assert_operation& o ) { const database& _db = db(); uint32_t skip = _db.get_node_properties().skip_flags; + auto max_predicate_opcode = _db.get_global_properties().parameters.max_predicate_opcode; if( skip & database::skip_assert_evaluation ) return void_result(); @@ -53,14 +54,15 @@ void_result assert_evaluator::do_evaluate( const assert_operation& o ) { fc::datastream ds( pdata.data(), pdata.size() ); predicate p; - try { - fc::raw::unpack( ds, p ); - } catch ( const fc::exception& e ) - { - if( skip & database::skip_unknown_predicate ) - continue; - throw; - } + // + // FC_ASSERT deep in the bowels of static_variant implementation + // will trip if we get a predicate which isn't in our code's + // static_variant. This is desired behavior. + // + fc::raw::unpack( ds, p ); + + FC_ASSERT( p.which() >= 0 ); + FC_ASSERT( unsigned(p.which()) < max_predicate_opcode ); FC_ASSERT( p.visit( predicate_visitor( _db ) ) ); } return void_result(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 41c4b3a6..2d340c00 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -118,8 +118,7 @@ namespace graphene { namespace chain { skip_tapos_check = 0x80, ///< used while reindexing -- note this skips expiration check as well skip_authority_check = 0x100, ///< used while reindexing -- disables any checking of authority on transactions skip_merkle_check = 0x200, ///< used while reindexing - skip_assert_evaluation = 0x400, ///< used while reindexing - skip_unknown_predicate = 0x800 ///< used by non-witness nodes to allow unknown predicates + skip_assert_evaluation = 0x400 ///< used while reindexing }; void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type()); From 0d02361af0c0af8f0306154b58fc9bc5a368f999 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 14:32:18 -0400 Subject: [PATCH 099/354] Clean up names of assert ops and evaluation dispatch --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/assert_evaluator.cpp | 15 +++--- .../include/graphene/chain/predicate.hpp | 52 +++++++++++-------- libraries/chain/predicate.cpp | 30 +++++++++++ tests/tests/operation_tests2.cpp | 4 +- 5 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 libraries/chain/predicate.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 8c95289d..9c048a13 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -7,6 +7,7 @@ add_library( graphene_chain address.cpp asset.cpp + predicate.cpp operations.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index f7c44dd9..a71ee19d 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -24,20 +24,17 @@ namespace graphene { namespace chain { -struct predicate_visitor +struct predicate_evaluator { typedef bool result_type; const database& db; - predicate_visitor( const database& d ):db(d){} + predicate_evaluator( const database& d ):db(d){} - bool operator()( const verify_account_name& p )const + template< typename T > + bool operator()( const T& p )const { - return p.account_id(db).name == p.account_name; - } - bool operator()( const verify_symbol& p )const - { - return p.asset_id(db).symbol == p.symbol; + return p.evaluate( db ); } }; @@ -63,7 +60,7 @@ void_result assert_evaluator::do_evaluate( const assert_operation& o ) FC_ASSERT( p.which() >= 0 ); FC_ASSERT( unsigned(p.which()) < max_predicate_opcode ); - FC_ASSERT( p.visit( predicate_visitor( _db ) ) ); + FC_ASSERT( p.visit( predicate_evaluator( _db ) ) ); } return void_result(); } diff --git a/libraries/chain/include/graphene/chain/predicate.hpp b/libraries/chain/include/graphene/chain/predicate.hpp index 120caf4b..32822ff0 100644 --- a/libraries/chain/include/graphene/chain/predicate.hpp +++ b/libraries/chain/include/graphene/chain/predicate.hpp @@ -21,53 +21,63 @@ namespace graphene { namespace chain { -bool is_valid_symbol( const string& symbol ); -bool is_valid_name( const string& s ); - class database; +namespace pred { + /** - * Used to verify that account_id->name is account_name + * Used to verify that account_id->name is equal to the given string literal. */ -struct verify_account_name +struct account_name_eq_lit { account_id_type account_id; - string account_name; + string name; /** - * Perform state independent checks, such as verifying that - * account_name is a valid name for an account. + * Perform state-independent checks. Verify + * account_name is a valid account name. */ - bool validate()const { return is_valid_name( account_name ); } + bool validate()const; + + /** + * Evaluate the predicate. + */ + bool evaluate( const database& db )const; }; /** - * Used to verify that account_id->name is account_name + * Used to verify that asset_id->symbol is equal to the given string literal. */ -struct verify_symbol +struct asset_symbol_eq_lit { asset_id_type asset_id; string symbol; /** - * Perform state independent checks, such as verifying that - * account_name is a valid name for an account. + * Perform state independent checks. Verify symbol is a + * valid asset symbol. */ - bool validate()const { return is_valid_symbol( symbol ); } + bool validate()const; + + /** + * Evaluate the predicate. + */ + bool evaluate( const database& db )const; }; +} + /** - * When defining predicates do not make the protocol dependent upon - * implementation details. + * When defining predicates do not make the protocol dependent upon + * implementation details. */ typedef static_variant< - verify_account_name, - verify_symbol + pred::account_name_eq_lit, + pred::asset_symbol_eq_lit > predicate; } } -FC_REFLECT( graphene::chain::verify_account_name, (account_id)(account_name) ) -FC_REFLECT( graphene::chain::verify_symbol, (asset_id)(symbol) ) +FC_REFLECT( graphene::chain::pred::account_name_eq_lit, (account_id)(name) ) +FC_REFLECT( graphene::chain::pred::asset_symbol_eq_lit, (asset_id)(symbol) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) - diff --git a/libraries/chain/predicate.cpp b/libraries/chain/predicate.cpp new file mode 100644 index 00000000..f8f820d2 --- /dev/null +++ b/libraries/chain/predicate.cpp @@ -0,0 +1,30 @@ + +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { namespace pred { + +bool account_name_eq_lit::validate()const +{ + return is_valid_name( name ); +} + +bool account_name_eq_lit::evaluate( const database& db )const +{ + return account_id(db).name == name; +} + +bool asset_symbol_eq_lit::validate()const +{ + return is_valid_symbol( symbol ); +} + +bool asset_symbol_eq_lit::evaluate( const database& db )const +{ + return asset_id(db).symbol == symbol; +} + +} } } diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 9627c1cb..2e80e9b1 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -820,7 +820,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) op.predicates = vector< vector< char > >(); op.predicates.push_back( fc::raw::pack( - predicate( verify_account_name{ nathan_id, "nathan" } ) + predicate( pred::account_name_eq_lit{ nathan_id, "nathan" } ) ) ); trx.operations.push_back(op); trx.sign( nathan_key_id, nathan_private_key ); @@ -829,7 +829,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) // nathan checks that his public key is not equal to the given value (fail) op.predicates.back() = fc::raw::pack( - predicate( verify_account_name{ nathan_id, "dan" } ) + predicate( pred::account_name_eq_lit{ nathan_id, "dan" } ) ); trx.operations.back() = op; trx.sign( nathan_key_id, nathan_private_key ); From cfcafcb5c2fab41d998c807699107b94e62f81b6 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Tue, 23 Jun 2015 17:05:35 -0400 Subject: [PATCH 100/354] Progress #66: Set initial fees There is now a default value for all fees --- libraries/chain/account_object.cpp | 51 ++++---- .../include/graphene/chain/operations.hpp | 2 +- .../chain/include/graphene/chain/types.hpp | 111 ++++++++---------- libraries/chain/operations.cpp | 4 +- tests/common/database_fixture.cpp | 1 + tests/tests/block_tests.cpp | 13 +- tests/tests/operation_tests.cpp | 24 ++-- tests/tests/operation_tests2.cpp | 20 ++-- 8 files changed, 95 insertions(+), 131 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 054fb9bb..7be8599f 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -153,11 +153,7 @@ void account_object::options_type::validate() const "May not specify fewer witnesses or committee members than the number voted for."); } - - - - -set account_member_index::get_members( const account_object& a )const +set account_member_index::get_members(const account_object& a)const { set result; for( auto auth : a.owner.auths ) @@ -167,17 +163,17 @@ set account_member_index::get_members( const account_object& a return result; } -void account_member_index::object_inserted( const object& obj ) +void account_member_index::object_inserted(const object& obj) { assert( dynamic_cast(&obj) ); // for debug only const account_object& a = static_cast(obj); - set members = get_members(a); + set members = get_members(a); for( auto item : members ) - account_to_memberships[item].insert( obj.id ); + account_to_memberships[item].insert(obj.id); } -void account_member_index::object_removed( const object& obj ) +void account_member_index::object_removed(const object& obj) { assert( dynamic_cast(&obj) ); // for debug only const account_object& a = static_cast(obj); @@ -187,7 +183,7 @@ void account_member_index::object_removed( const object& obj ) account_to_memberships[item].erase( obj.id ); } -void account_member_index::about_to_modify( const object& before ) +void account_member_index::about_to_modify(const object& before) { before_members.clear(); assert( dynamic_cast(&before) ); // for debug only @@ -195,29 +191,28 @@ void account_member_index::about_to_modify( const object& before ) before_members = get_members(a); } -void account_member_index::object_modified( const object& after ) +void account_member_index::object_modified(const object& after) { assert( dynamic_cast(&after) ); // for debug only const account_object& a = static_cast(after); - set after_members = get_members(a); - + set after_members = get_members(a); + vector removed; removed.reserve(before_members.size()); - std::set_difference( before_members.begin(), before_members.end(), - after_members.begin(), after_members.end(), - std::inserter( removed, removed.end() ) ); + std::set_difference(before_members.begin(), before_members.end(), + after_members.begin(), after_members.end(), + std::inserter(removed, removed.end())); for( auto itr = removed.begin(); itr != removed.end(); ++itr ) - account_to_memberships[*itr].erase( after.id ); - - vector added; added.reserve(after_members.size()); - auto add_end = std::set_difference( after_members.begin(), after_members.end(), - before_members.begin(), before_members.end(), - std::inserter( added, added.end()) ); - - for( auto itr = added.begin(); itr != added.end(); ++itr ) - account_to_memberships[*itr].insert( after.id ); -} + account_to_memberships[*itr].erase(after.id); + vector added; added.reserve(after_members.size()); + std::set_difference(after_members.begin(), after_members.end(), + before_members.begin(), before_members.end(), + std::inserter(added, added.end())); + + for( auto itr = added.begin(); itr != added.end(); ++itr ) + account_to_memberships[*itr].insert(after.id); +} void account_referrer_index::object_inserted( const object& obj ) { @@ -232,8 +227,4 @@ void account_referrer_index::object_modified( const object& after ) { } - - - - } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 61d22fef..7c556422 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -348,7 +348,7 @@ namespace graphene { namespace chain { void validate()const; share_type calculate_fee(const fee_schedule_type& k)const; - void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } + void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust(fee_payer(), -fee); } }; /** diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index db8c9334..ef3b12ad 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -335,11 +335,6 @@ namespace graphene { namespace chain { } }; - fee_schedule_type() - { - memset((char*)this, 0, sizeof(*this)); - } - /// The number of bytes to charge a data fee for const static int BYTES_PER_DATA_FEE = 1024; @@ -348,57 +343,54 @@ namespace graphene { namespace chain { return data_size(ts...) / BYTES_PER_DATA_FEE * data_fee; } - uint32_t key_create_fee; ///< the cost to register a public key with the blockchain - uint32_t account_create_fee; ///< the cost to register the cheapest non-free account - uint32_t account_update_fee; ///< the cost to update an existing account - uint32_t account_transfer_fee; ///< the cost to transfer an account to a new owner - uint32_t account_len8up_fee; - uint32_t account_len7_fee; - uint32_t account_len6_fee; - uint32_t account_len5_fee; - uint32_t account_len4_fee; - uint32_t account_len3_fee; - uint32_t account_len2_fee; - uint32_t asset_len3_fee; - uint32_t asset_len4_fee; - uint32_t asset_len5_fee; - uint32_t asset_len6_fee; - uint32_t asset_len7up_fee; - uint32_t account_whitelist_fee; ///< the fee to whitelist an account - uint32_t delegate_create_fee; ///< fixed fee for registering as a delegate; used to discourage frivolous delegates - uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay - uint32_t transfer_fee; ///< fee for transferring some asset - uint32_t limit_order_create_fee; ///< fee for placing a limit order in the markets - uint32_t limit_order_cancel_fee; ///< fee for canceling a limit order - uint32_t call_order_fee; ///< fee for placing a call order in the markets - uint32_t publish_feed_fee; ///< fee for publishing a price feed - uint32_t asset_create_fee; ///< the cost to register the cheapest asset - uint32_t asset_update_fee; ///< the cost to modify a registered asset - uint32_t asset_issue_fee; ///< the cost to modify a registered asset - uint32_t asset_burn_fee; ///< the cost to burn an asset - uint32_t asset_fund_fee_pool_fee; ///< the cost to add funds to an asset's fee pool - uint32_t asset_settle_fee; ///< the cost to trigger a forced settlement of a market-issued asset - uint32_t market_fee; ///< a percentage charged on market orders - uint32_t transaction_fee; ///< a base price for every transaction - uint32_t data_fee; ///< a price per BYTES_PER_DATA_FEE bytes of user data - uint32_t signature_fee; ///< a surcharge on transactions with more than 2 signatures. - uint32_t global_parameters_update_fee; ///< the cost to update the global parameters - uint32_t membership_annual_fee; ///< the annual cost of a membership subscription - uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member - uint32_t withdraw_permission_create_fee; ///< the cost to create a withdraw permission - uint32_t withdraw_permission_update_fee; ///< the cost to update a withdraw permission - uint32_t withdraw_permission_claim_fee; ///< the cost to withdraw from a withdraw permission - uint32_t withdraw_permission_delete_fee; ///< the cost to delete a withdraw permission - uint32_t vesting_balance_create_fee; - uint32_t vesting_balance_withdraw_fee; - uint32_t global_settle_fee; - uint32_t worker_create_fee; ///< the cost to create a new worker - uint32_t worker_delete_fee; ///< the cost to delete a worker - uint32_t assert_op_fee; ///< fee per assert operation - uint32_t proposal_create_fee; ///< fee for creating a proposed transaction - uint32_t proposal_update_fee; ///< fee for adding or removing approval of a proposed transaction - uint32_t proposal_delete_fee; ///< fee for deleting a proposed transaction - uint32_t custom_operation_fee; ///< fee for a custom operation + uint32_t key_create_fee = 270300; ///< the cost to register a public key with the blockchain + uint32_t account_create_fee = 666666; ///< the cost to register the cheapest non-free account + uint32_t account_update_fee = 150000; ///< the cost to update an existing account + uint32_t account_transfer_fee = 300000; ///< the cost to transfer an account to a new owner + uint32_t account_whitelist_fee = 300000; ///< the fee to whitelist an account + uint32_t account_len8up_fee = 1500000; + uint32_t account_len7_fee = 7000000; + uint32_t account_len6_fee = 14000000; + uint32_t account_len5_fee = 70000000; + uint32_t account_len4_fee = 280000000; + uint32_t account_len3_fee = 680000000; + uint32_t account_len2_fee = 1000000000; + uint32_t asset_create_fee = 680000000; ///< the cost to register the cheapest asset + uint32_t asset_update_fee = 150000; ///< the cost to modify a registered asset + uint32_t asset_issue_fee = 700000; ///< the cost to print a UIA and send it to an account + uint32_t asset_burn_fee = 1500000; ///< the cost to burn an asset + uint32_t asset_fund_fee_pool_fee = 150000; ///< the cost to add funds to an asset's fee pool + uint32_t asset_settle_fee = 7000000; ///< the cost to trigger a forced settlement of a market-issued asset + uint32_t asset_global_settle_fee = 140000000; ///< the cost to trigger a global forced settlement of a market asset + uint32_t asset_len7up_fee = 140000000; + uint32_t asset_len6_fee = 675000000; + uint32_t asset_len5_fee = 1350000000; + uint32_t asset_len4_fee = 2700000000; + uint64_t asset_len3_fee = 7000000000; + uint32_t delegate_create_fee = 680000000; ///< fee for registering as a delegate; used to discourage frivolous delegates + uint32_t witness_create_fee = 680000000; /// < fee for registering as a witness + uint32_t witness_withdraw_pay_fee = 1500000; ///< fee for withdrawing witness pay + uint32_t transfer_fee = 2700000; ///< fee for transferring some asset + uint32_t limit_order_create_fee = 666666; ///< fee for placing a limit order in the markets + uint32_t limit_order_cancel_fee = 150000; ///< fee for canceling a limit order + uint32_t call_order_fee = 800000; ///< fee for placing a call order in the markets + uint32_t publish_feed_fee = 666666; ///< fee for publishing a price feed + uint32_t data_fee = 13500000; ///< a price per BYTES_PER_DATA_FEE bytes of user data + uint32_t global_parameters_update_fee = 1350000; ///< the cost to update the global parameters + uint32_t membership_annual_fee = 270000000; ///< the annual cost of a membership subscription + uint32_t membership_lifetime_fee = 1350000000; ///< the cost to upgrade to a lifetime member + uint32_t withdraw_permission_create_fee = 2700000; ///< the cost to create a withdraw permission + uint32_t withdraw_permission_update_fee = 150000; ///< the cost to update a withdraw permission + uint32_t withdraw_permission_claim_fee = 700000; ///< the cost to withdraw from a withdraw permission + uint32_t withdraw_permission_delete_fee = 150000; ///< the cost to delete a withdraw permission + uint32_t vesting_balance_create_fee = 7000000; + uint32_t vesting_balance_withdraw_fee = 2700000; + uint32_t worker_create_fee = 680000000; ///< the cost to create a new worker + uint32_t assert_op_fee = 150000; ///< fee per assert operation + uint32_t proposal_create_fee = 7000000; ///< fee for creating a proposed transaction + uint32_t proposal_update_fee = 1500000; ///< fee for adding or removing approval of a proposed transaction + uint32_t proposal_delete_fee = 150000; ///< fee for deleting a proposed transaction + uint32_t custom_operation_fee = 300000; ///< fee for a custom operation protected: size_t data_size()const { @@ -565,6 +557,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (asset_len7up_fee) (account_whitelist_fee) (delegate_create_fee) + (witness_create_fee) (witness_withdraw_pay_fee) (transfer_fee) (limit_order_create_fee) @@ -577,10 +570,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (asset_burn_fee) (asset_fund_fee_pool_fee) (asset_settle_fee) - (market_fee) - (transaction_fee) (data_fee) - (signature_fee) (global_parameters_update_fee) (membership_annual_fee) (membership_lifetime_fee) @@ -590,9 +580,8 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (withdraw_permission_delete_fee) (vesting_balance_create_fee) (vesting_balance_withdraw_fee) - (global_settle_fee) + (asset_global_settle_fee) (worker_create_fee) - (worker_delete_fee) (assert_op_fee) (proposal_create_fee) (proposal_update_fee) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 1fa19be1..65edbad3 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -575,7 +575,7 @@ void witness_create_operation::validate() const share_type witness_create_operation::calculate_fee(const fee_schedule_type& k) const { - return k.delegate_create_fee + k.total_data_fee(url); + return k.witness_create_fee + k.total_data_fee(url); } void withdraw_permission_update_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const @@ -668,7 +668,7 @@ void asset_global_settle_operation::validate()const share_type asset_global_settle_operation::calculate_fee(const fee_schedule_type& k)const { - return k.global_settle_fee; + return k.asset_global_settle_fee; } void asset_settle_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 02685c58..45803265 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -65,6 +65,7 @@ database_fixture::database_fixture() genesis_state.initial_committee.push_back({name}); genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); } + fc::reflector::visit(fee_schedule_type::fee_set_visitor{genesis_state.initial_parameters.current_fees, 0}); db.init_genesis(genesis_state); ahplugin->plugin_startup(); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index cab95933..441266fa 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -49,6 +49,7 @@ genesis_state_type make_genesis() { genesis_state.initial_committee.push_back({name}); genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); } + fc::reflector::visit(fee_schedule_type::fee_set_visitor{genesis_state.initial_parameters.current_fees, 0}); return genesis_state; } @@ -74,25 +75,20 @@ BOOST_AUTO_TEST_CASE( block_database_test ) bdb.store( b.id(), b ); auto fetch = bdb.fetch_by_number( b.block_num() ); - //idump((fetch)); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); fetch = bdb.fetch_by_number( i+1 ); - //idump((fetch)); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); fetch = bdb.fetch_optional( b.id() ); - //idump((fetch)); FC_ASSERT( fetch.valid() ); FC_ASSERT( fetch->witness == b.witness ); } - //ilog("-----------" ); for( uint32_t i = 1; i < 5; ++i ) { auto blk = bdb.fetch_by_number( i ); FC_ASSERT( blk.valid() ); - //idump((blk)(i)); FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); } @@ -110,7 +106,6 @@ BOOST_AUTO_TEST_CASE( block_database_test ) { auto blk = bdb.fetch_by_number( i+1 ); FC_ASSERT( blk.valid() ); - //idump((blk)(i)); FC_ASSERT( blk->witness == witness_id_type(blk->block_num()) ); } @@ -141,7 +136,6 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) witness_id_type prev_witness = b.witness; now += db.block_interval(); witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first; - // TODO: Uncomment this when witness scheduling implemented BOOST_CHECK( cur_witness != prev_witness ); b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing ); BOOST_CHECK( b.witness == cur_witness ); @@ -149,7 +143,6 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) db.close(); } { - //wlog( "------------------------------------------------" ); database db; db.open(data_dir.path() ); BOOST_CHECK_EQUAL( db.head_block_num(), 200 ); @@ -159,7 +152,6 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) witness_id_type prev_witness = b.witness; now += db.block_interval(); witness_id_type cur_witness = db.get_scheduled_witness( 1 ).first; - // TODO: Uncomment this when witness scheduling implemented BOOST_CHECK( cur_witness != prev_witness ); b = db.generate_block( now, cur_witness, delegate_priv_key, database::skip_nothing ); } @@ -189,15 +181,12 @@ BOOST_AUTO_TEST_CASE( undo_block ) BOOST_CHECK( db.head_block_num() == 5 ); db.pop_block(); now -= db.block_interval(); - //wdump( (witness_schedule_id_type()(db)) ); BOOST_CHECK( db.head_block_num() == 4 ); db.pop_block(); now -= db.block_interval(); - //wdump( (witness_schedule_id_type()(db)) ); BOOST_CHECK( db.head_block_num() == 3 ); db.pop_block(); now -= db.block_interval(); - //wdump( (witness_schedule_id_type()(db)) ); BOOST_CHECK( db.head_block_num() == 2 ); for( uint32_t i = 0; i < 5; ++i ) { diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 50a0573e..fd418fa6 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -228,35 +228,35 @@ BOOST_AUTO_TEST_CASE( black_swan ) transfer(genesis_account, buyer_id, asset(init_balance)); transfer(genesis_account, borrower_id, asset(init_balance)); transfer(genesis_account, borrower2_id, asset(init_balance)); - update_feed_producers( bitusd, {feedproducer.id} ); + update_feed_producers(bitusd, {feedproducer.id}); price_feed current_feed; - current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.settlement_price = bitusd.amount(100) / core.amount(100); // starting out with price 1:1 - publish_feed( bitusd, feedproducer, current_feed ); + publish_feed(bitusd, feedproducer, current_feed); // start out with 2:1 collateral - borrow( borrower, bitusd.amount(1000), asset(2000) ); - borrow( borrower2, bitusd.amount(1000), asset(4000) ); + borrow(borrower, bitusd.amount(1000), asset(2000)); + borrow(borrower2, bitusd.amount(1000), asset(4000)); - BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 ); - BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 ); - BOOST_REQUIRE_EQUAL( get_balance( borrower , core ), init_balance - 2000 ); - BOOST_REQUIRE_EQUAL( get_balance( borrower2, core ), init_balance - 4000 ); + BOOST_REQUIRE_EQUAL( get_balance(borrower, bitusd), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance(borrower2, bitusd), 1000 ); + BOOST_REQUIRE_EQUAL( get_balance(borrower , core), init_balance - 2000 ); + BOOST_REQUIRE_EQUAL( get_balance(borrower2, core), init_balance - 4000 ); current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200); publish_feed( bitusd, feedproducer, current_feed ); /// this sell order is designed to trigger a black swan - auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(3000) ); + create_sell_order(borrower2, bitusd.amount(1000), core.amount(3000)); FC_ASSERT( bitusd.bitasset_data(db).has_settlement() ); - force_settle( borrower, bitusd.amount(100) ); + force_settle(borrower, bitusd.amount(100)); BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" ); - BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000) ), fc::exception ); + BOOST_REQUIRE_THROW( borrow(borrower, bitusd.amount(1000), asset(2000)), fc::exception ); } catch( const fc::exception& e) { edump((e.to_detail_string())); throw; diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 2e80e9b1..a087fa76 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -805,34 +805,28 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) try { // create some objects auto nathan_private_key = generate_private_key("nathan"); - auto dan_private_key = generate_private_key("dan"); public_key_type nathan_public_key = nathan_private_key.get_public_key(); - public_key_type dan_public_key = dan_private_key.get_public_key(); key_id_type nathan_key_id = register_key(nathan_public_key).id; - key_id_type dan_key_id = register_key(dan_public_key).id; account_id_type nathan_id = create_account("nathan", nathan_key_id).id; assert_operation op; - decltype( key_object::key_data ) lit_key = nathan_public_key; + decltype(key_object::key_data) lit_key = nathan_public_key; // nathan checks that his public key is equal to the given value. op.fee_paying_account = nathan_id; - op.predicates = vector< vector< char > >(); + op.predicates = vector>(); op.predicates.push_back( fc::raw::pack( - predicate( pred::account_name_eq_lit{ nathan_id, "nathan" } ) - ) ); + predicate(pred::account_name_eq_lit{ nathan_id, "nathan" }) + )); trx.operations.push_back(op); - trx.sign( nathan_key_id, nathan_private_key ); + trx.sign(nathan_key_id, nathan_private_key); PUSH_TX( db, trx ); // nathan checks that his public key is not equal to the given value (fail) - op.predicates.back() = - fc::raw::pack( - predicate( pred::account_name_eq_lit{ nathan_id, "dan" } ) - ); + op.predicates.back() = fc::raw::pack(predicate(pred::account_name_eq_lit{ nathan_id, "dan" })); trx.operations.back() = op; - trx.sign( nathan_key_id, nathan_private_key ); + trx.sign(nathan_key_id, nathan_private_key); BOOST_CHECK_THROW( PUSH_TX( db, trx ), fc::exception ); } FC_LOG_AND_RETHROW() } From fdefc69baf3ec43519f3fc847afb39858d2f4ad4 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 17:53:10 -0400 Subject: [PATCH 101/354] Issue #47 - Basic, Untested, Market History Plugin --- libraries/app/include/graphene/app/plugin.hpp | 2 +- libraries/plugins/CMakeLists.txt | 1 + .../account_history_plugin.hpp | 3 +- .../plugins/market_history/CMakeLists.txt | 13 ++ .../market_history/market_history_plugin.hpp | 131 ++++++++++++ .../market_history/market_history_plugin.cpp | 194 ++++++++++++++++++ 6 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 libraries/plugins/market_history/CMakeLists.txt create mode 100644 libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp create mode 100644 libraries/plugins/market_history/market_history_plugin.cpp diff --git a/libraries/app/include/graphene/app/plugin.hpp b/libraries/app/include/graphene/app/plugin.hpp index 5065679d..103cd441 100644 --- a/libraries/app/include/graphene/app/plugin.hpp +++ b/libraries/app/include/graphene/app/plugin.hpp @@ -104,9 +104,9 @@ class plugin : public abstract_plugin bpo::options_description& config_file_options ) override; - protected: chain::database& database() { return *app().chain_database(); } application& app()const { assert(_app); return *_app; } + protected: net::node& p2p_node() { return *app().p2p_node(); } private: diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 5f55e32b..a8bf2c23 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory( witness ) add_subdirectory( account_history ) +add_subdirectory( market_history ) diff --git a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp index 43b5c1f2..f0bdc464 100644 --- a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp +++ b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp @@ -42,7 +42,8 @@ namespace bpo = boost::program_options; enum account_history_object_type { - key_account_object_type + key_account_object_type = 0, + bucket_object_type = 1 ///< used in market_history_plugin }; class key_account_object : public abstract_object diff --git a/libraries/plugins/market_history/CMakeLists.txt b/libraries/plugins/market_history/CMakeLists.txt new file mode 100644 index 00000000..6394ba66 --- /dev/null +++ b/libraries/plugins/market_history/CMakeLists.txt @@ -0,0 +1,13 @@ +file(GLOB HEADERS "include/graphene/market_history/*.hpp") + +add_library( graphene_market_history + market_history_plugin.cpp + ) + +target_link_libraries( graphene_market_history graphene_chain graphene_app ) +target_include_directories( graphene_market_history + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties( market_history_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp new file mode 100644 index 00000000..3c146444 --- /dev/null +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include +#include + +#include + +namespace graphene { namespace market_history { +using namespace chain; +namespace bpo = boost::program_options; + +// +// 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 ACCOUNT_HISTORY_SPACE_ID +#define ACCOUNT_HISTORY_SPACE_ID 5 +#endif + +struct bucket_key +{ + asset_id_type base; + asset_id_type quote; + uint32_t seconds = 0; + fc::time_point_sec open; + + friend bool operator < ( const bucket_key& a, const bucket_key& b ) + { + return std::tie( a.base, a.quote, b.seconds, a.open ) < std::tie( b.base, b.quote, b.seconds, b.open ); + } + friend bool operator == ( const bucket_key& a, const bucket_key& b ) + { + return std::tie( a.base, a.quote, b.seconds, a.open ) == std::tie( b.base, b.quote, b.seconds, b.open ); + } +}; + +struct bucket_object : public abstract_object +{ + static const uint8_t space_id = ACCOUNT_HISTORY_SPACE_ID; + static const uint8_t type_id = 1; // market_history_plugin type, referenced from account_history_plugin.hpp + + price high()const { return asset( high_base, key.base ) / asset( high_quote, key.quote ); } + price low()const { return asset( low_base, key.base ) / asset( low_quote, key.quote ); } + + bucket_key key; + share_type high_base; + share_type high_quote; + share_type low_base; + share_type low_quote; + share_type open_base; + share_type open_quote; + share_type close_base; + share_type close_quote; + share_type base_volume; + share_type quote_volume; +}; + +struct by_key; +typedef multi_index_container< + bucket_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< bucket_object, bucket_key, &bucket_object::key > > + > +> bucket_object_multi_index_type; + +typedef generic_index bucket_index; + + +namespace detail +{ + class market_history_plugin_impl; +} + +/** + * The market history plugin can be configured to track any number of intervals via its configuration. Once per block it + * will scan the virtual operations and look for fill_order_operations and then adjust the appropriate bucket objects for + * each fill order. + */ +class market_history_plugin : public graphene::app::plugin +{ + public: + market_history_plugin(); + virtual ~market_history_plugin(); + + std::string plugin_name()const override; + virtual void plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) override; + virtual void plugin_initialize(const bpo::variables_map& options) override; + virtual void plugin_startup() override; + + vector get_history( const bucket_key& start, const bucket_key& end )const; + const flat_set& tracked_buckets()const; + + private: + friend class detail::market_history_plugin_impl; + std::unique_ptr my; +}; + +} } //graphene::market_history + +FC_REFLECT( graphene::market_history::bucket_key, (base)(quote)(seconds)(open) ) +FC_REFLECT( graphene::market_history::bucket_object, (key) + (high_base)(high_quote) + (low_base)(low_quote) + (open_base)(open_quote) + (close_base)(close_quote) + (base_volume)(quote_volume) ) + diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp new file mode 100644 index 00000000..d6dbd168 --- /dev/null +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace graphene { namespace market_history { + +namespace detail +{ + +class market_history_plugin_impl +{ + public: + market_history_plugin_impl(market_history_plugin& _plugin) + :_self( _plugin ) {} + virtual ~market_history_plugin_impl(); + + /** this method is called as a callback after a block is applied + * and will process/index all operations that were applied in the block. + */ + void update_market_histories( const signed_block& b ); + + graphene::chain::database& database() + { + return _self.database(); + } + + market_history_plugin& _self; + flat_set _tracked_buckets; +}; + + +struct operation_process_fill_order +{ + market_history_plugin& _plugin; + fc::time_point_sec _now; + + operation_process_fill_order( market_history_plugin& mhp, fc::time_point_sec n ) + :_plugin(mhp),_now(n) {} + + typedef void result_type; + + /** do nothing for other operation types */ + template + void operator()( const T& )const{} + + void operator()( const fill_order_operation& o )const + { + const auto& buckets = _plugin.tracked_buckets(); + auto& db = _plugin.database(); + const auto& bucket_idx = db.get_index_type(); + + for( auto bucket : buckets ) + { + bucket_key key; + key.base = o.pays.asset_id; + key.quote = o.receives.asset_id; + price trade_price = o.pays / o.receives; + if( key.base > key.quote ) + { + std::swap( key.base,key.quote ); + trade_price = ~trade_price; + } + + key.seconds = bucket; + key.open = fc::time_point() + fc::seconds((_now.sec_since_epoch() / key.seconds) * key.seconds); + + const auto& by_key_idx = bucket_idx.indices().get(); + auto itr = by_key_idx.find( key ); + if( itr == by_key_idx.end() ) + { // create new bucket + db.create( [&]( bucket_object& b ){ + b.key = key; + b.quote_volume += trade_price.quote.amount; + b.open_base = trade_price.base.amount; + b.open_quote = trade_price.quote.amount; + b.close_base = trade_price.base.amount; + b.close_quote = trade_price.quote.amount; + b.high_base = b.close_base; + b.high_quote = b.close_quote; + b.low_base = b.close_base; + b.low_quote = b.close_quote; + }); + } + else + { // update existing bucket + db.modify( *itr, [&]( bucket_object& b ){ + b.base_volume += trade_price.base.amount; + b.quote_volume += trade_price.quote.amount; + b.close_base = trade_price.base.amount; + b.close_quote = trade_price.quote.amount; + if( b.high() < trade_price ) + { + b.high_base = b.close_base; + b.high_quote = b.close_quote; + } + if( b.low() > trade_price ) + { + b.low_base = b.close_base; + b.low_quote = b.close_quote; + } + }); + } + } + } +}; + +market_history_plugin_impl::~market_history_plugin_impl() +{} + +void market_history_plugin_impl::update_market_histories( const signed_block& b ) +{ + graphene::chain::database& db = database(); + const vector& hist = db.get_applied_operations(); + for( auto op : hist ) + op.op.visit( operation_process_fill_order( _self, b.timestamp ) ); +} + +} // end namespace detail + + + + + + +market_history_plugin::market_history_plugin() : + my( new detail::market_history_plugin_impl(*this) ) +{ +} + +market_history_plugin::~market_history_plugin() +{ +} + +std::string market_history_plugin::plugin_name()const +{ + return "market_history"; +} + +void market_history_plugin::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg + ) +{ + cli.add_options() + ("bucket-size", bpo::value>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)") + ; + cfg.add(cli); +} + +void market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + database().applied_block.connect( [&]( const signed_block& b){ my->update_market_histories(b); } ); + database().add_index< primary_index< bucket_index > >(); + + LOAD_VALUE_SET(options, "bucket-size", my->_tracked_buckets, uint32_t); +} + +void market_history_plugin::plugin_startup() +{ +} + +const flat_set& market_history_plugin::tracked_buckets() const +{ + return my->_tracked_buckets; +} + +} } From 0b854bc70261cccf285f5222212be0b907daa420 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Tue, 23 Jun 2015 15:18:25 -0400 Subject: [PATCH 102/354] Build with TCMalloc if available --- programs/cli_wallet/CMakeLists.txt | 13 +++++++------ programs/field_reflector/CMakeLists.txt | 5 ----- programs/js_operation_serializer/CMakeLists.txt | 5 ----- programs/witness_node/CMakeLists.txt | 11 ++++++----- tests/CMakeLists.txt | 16 +++++++++++----- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/programs/cli_wallet/CMakeLists.txt b/programs/cli_wallet/CMakeLists.txt index 0aaa8676..8eed412e 100644 --- a/programs/cli_wallet/CMakeLists.txt +++ b/programs/cli_wallet/CMakeLists.txt @@ -2,15 +2,16 @@ add_executable( cli_wallet main.cpp ) if( UNIX AND NOT APPLE ) set(rt_library rt ) endif() -#find_package( Gperftools QUIET ) -#if( GPERFTOOLS_FOUND ) -# message( STATUS "Found gperftools; compiling client with TCMalloc") -# list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -#endif() + +find_package( Gperftools QUIET ) +if( GPERFTOOLS_FOUND ) + message( STATUS "Found gperftools; compiling cli_wallet with TCMalloc") + list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) +endif() target_link_libraries( cli_wallet PRIVATE graphene_app graphene_net graphene_chain graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) -endif(MSVC) \ No newline at end of file +endif(MSVC) diff --git a/programs/field_reflector/CMakeLists.txt b/programs/field_reflector/CMakeLists.txt index 129ae28d..00d78620 100644 --- a/programs/field_reflector/CMakeLists.txt +++ b/programs/field_reflector/CMakeLists.txt @@ -2,11 +2,6 @@ add_executable( field_reflector main.cpp ) if( UNIX AND NOT APPLE ) set(rt_library rt ) endif() -#find_package( Gperftools QUIET ) -#if( GPERFTOOLS_FOUND ) -# message( STATUS "Found gperftools; compiling client with TCMalloc") -# list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -#endif() target_link_libraries( field_reflector PRIVATE graphene_app graphene_net graphene_chain graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/programs/js_operation_serializer/CMakeLists.txt b/programs/js_operation_serializer/CMakeLists.txt index c814b53f..4815cb1d 100644 --- a/programs/js_operation_serializer/CMakeLists.txt +++ b/programs/js_operation_serializer/CMakeLists.txt @@ -2,11 +2,6 @@ add_executable( js_operation_serializer main.cpp ) if( UNIX AND NOT APPLE ) set(rt_library rt ) endif() -#find_package( Gperftools QUIET ) -#if( GPERFTOOLS_FOUND ) -# message( STATUS "Found gperftools; compiling client with TCMalloc") -# list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -#endif() target_link_libraries( js_operation_serializer PRIVATE graphene_app graphene_net graphene_chain graphene_utilities graphene_wallet fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 0c75e94a..386b1657 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -2,11 +2,12 @@ add_executable( witness_node main.cpp ) if( UNIX AND NOT APPLE ) set(rt_library rt ) endif() -#find_package( Gperftools QUIET ) -#if( GPERFTOOLS_FOUND ) -# message( STATUS "Found gperftools; compiling client with TCMalloc") -# list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -#endif() + +find_package( Gperftools QUIET ) +if( GPERFTOOLS_FOUND ) + message( STATUS "Found gperftools; compiling witness_node with TCMalloc") + list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) +endif() target_link_libraries( witness_node PRIVATE graphene_app graphene_account_history graphene_witness graphene_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 468425e4..c922da15 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,21 +1,27 @@ file(GLOB COMMON_SOURCES "common/*.cpp") +find_package( Gperftools QUIET ) +if( GPERFTOOLS_FOUND ) + message( STATUS "Found gperftools; compiling tests with TCMalloc") + list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) +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 fc ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history fc ${PLATFORM_SPECIFIC_LIBS} ) 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 fc ) +target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history 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_account_history graphene_time fc ) +target_link_libraries( chain_bench graphene_chain graphene_account_history graphene_time fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) -target_link_libraries( app_test graphene_account_history graphene_app graphene_net graphene_chain graphene_time fc ) +target_link_libraries( app_test graphene_account_history graphene_app graphene_net graphene_chain graphene_time 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 fc ) +target_link_libraries( intense_test graphene_chain graphene_app graphene_account_history fc ${PLATFORM_SPECIFIC_LIBS} ) From e6621809811569534f1dec32537f6401014b7702 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Tue, 23 Jun 2015 16:20:34 -0400 Subject: [PATCH 103/354] Update submodules --- .gitmodules | 1 + docs | 2 +- libraries/fc | 2 +- programs/cli_wallet/main.cpp | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index ee296306..24ef291b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "docs"] path = docs url = https://github.com/cryptonomex/graphene.wiki.git + ignore = dirty [submodule "libraries/fc"] path = libraries/fc url = https://github.com/cryptonomex/fc.git diff --git a/docs b/docs index 71ed2984..94ec061b 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 71ed2984b71d57cab13cdf12074cff150edc1d3d +Subproject commit 94ec061b91edbe9d1ebae576e641f921998428c8 diff --git a/libraries/fc b/libraries/fc index a8b85f6d..701b9ac0 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit a8b85f6dcc4558b7a9913b59ef5dc19f3b5e62ca +Subproject commit 701b9ac00c46234ee7cc6385027f5942590debc9 diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index c3a89df7..4230e495 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -88,7 +88,6 @@ int main( int argc, char** argv ) ac.rotate = true; ac.rotation_interval = fc::hours( 1 ); ac.rotation_limit = fc::days( 1 ); - ac.rotation_compression = false; std::cout << "Logging RPC to file: " << (data_dir / ac.filename).preferred_string() << "\n"; From 1e62c749af10ea30c8fa416cd9cb841626f822df Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Tue, 23 Jun 2015 17:33:06 -0400 Subject: [PATCH 104/354] Minor evaluator cleanup --- libraries/chain/account_evaluator.cpp | 21 +++--- libraries/chain/assert_evaluator.cpp | 8 +-- libraries/chain/asset_evaluator.cpp | 67 +++++++++---------- libraries/chain/delegate_evaluator.cpp | 13 ++-- .../chain/global_parameters_evaluator.cpp | 8 +-- .../graphene/chain/asset_evaluator.hpp | 4 +- .../graphene/chain/delegate_evaluator.hpp | 2 +- .../graphene/chain/limit_order_evaluator.hpp | 4 +- .../graphene/chain/proposal_evaluator.hpp | 2 +- .../chain/vesting_balance_evaluator.hpp | 2 +- .../chain/withdraw_permission_evaluator.hpp | 2 +- .../graphene/chain/witness_evaluator.hpp | 2 +- .../graphene/chain/worker_evaluator.hpp | 2 +- libraries/chain/limit_order_evaluator.cpp | 35 +++++----- libraries/chain/proposal_evaluator.cpp | 30 ++++----- libraries/chain/transfer_evaluator.cpp | 3 +- libraries/chain/vesting_balance_evaluator.cpp | 22 +++--- .../chain/withdraw_permission_evaluator.cpp | 10 +-- libraries/chain/witness_evaluator.cpp | 21 +++--- libraries/chain/worker_evaluator.cpp | 8 +-- 20 files changed, 133 insertions(+), 133 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 9d059393..03ed332f 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -113,7 +113,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio void_result account_update_evaluator::do_evaluate( const account_update_operation& o ) -{ +{ try { database& d = db(); const auto& chain_params = db().get_global_properties().parameters; @@ -152,9 +152,10 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio } return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } + void_result account_update_evaluator::do_apply( const account_update_operation& o ) -{ +{ try { db().modify( *acnt, [&](account_object& a){ if( o.owner ) a.owner = resolve_relative_ids(*o.owner); if( o.active ) a.active = resolve_relative_ids(*o.active); @@ -165,7 +166,7 @@ void_result account_update_evaluator::do_apply( const account_update_operation& } }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o) { try { @@ -179,7 +180,7 @@ void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_ope } FC_CAPTURE_AND_RETHROW( (o) ) } void_result account_whitelist_evaluator::do_apply(const account_whitelist_operation& o) -{ +{ try { database& d = db(); d.modify(*listed_account, [&o](account_object& a) { @@ -194,20 +195,20 @@ void_result account_whitelist_evaluator::do_apply(const account_whitelist_operat }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluator::operation_type& o) -{ +{ try { database& d = db(); account = &d.get(o.account_to_upgrade); FC_ASSERT(!account->is_lifetime_member()); return {}; -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o) -{ +{ try { database& d = db(); d.modify(*account, [&](account_object& a) { @@ -232,6 +233,6 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: }); return {}; -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index a71ee19d..6b805d2d 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -39,7 +39,7 @@ struct predicate_evaluator }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) -{ +{ try { const database& _db = db(); uint32_t skip = _db.get_node_properties().skip_flags; auto max_predicate_opcode = _db.get_global_properties().parameters.max_predicate_opcode; @@ -63,12 +63,12 @@ void_result assert_evaluator::do_evaluate( const assert_operation& o ) FC_ASSERT( p.visit( predicate_evaluator( _db ) ) ); } return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result assert_evaluator::do_apply( const assert_operation& o ) -{ +{ try { // assert_operation is always a no-op return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 2fa3566d..5f3dd48a 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -24,7 +24,7 @@ #include namespace graphene { namespace chain { -object_id_type asset_create_evaluator::do_evaluate( const asset_create_operation& op ) +void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op ) { try { database& d = db(); @@ -59,11 +59,11 @@ object_id_type asset_create_evaluator::do_evaluate( const asset_create_operation op.bitasset_options->force_settlement_delay_sec > chain_parameters.block_interval ); } - return object_id_type(); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type asset_create_evaluator::do_apply( const asset_create_operation& op ) -{ +{ try { const asset_dynamic_data_object& dyn_asset = db().create( [&]( asset_dynamic_data_object& a ) { a.current_supply = 0; @@ -95,8 +95,8 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o }); assert( new_asset.id == next_asset_id ); - return next_asset_id; -} + return new_asset.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o ) { try { @@ -120,7 +120,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o ) } FC_CAPTURE_AND_RETHROW( (o) ) } 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 ){ @@ -128,7 +128,7 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o ) }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_burn_evaluator::do_evaluate( const asset_burn_operation& o ) { try { @@ -151,7 +151,7 @@ void_result asset_burn_evaluator::do_evaluate( const asset_burn_operation& o ) } FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_burn_evaluator::do_apply( const asset_burn_operation& o ) -{ +{ try { db().adjust_balance( o.payer, -o.amount_to_burn ); db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ){ @@ -159,7 +159,7 @@ void_result asset_burn_evaluator::do_apply( const asset_burn_operation& o ) }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_fund_fee_pool_evaluator::do_evaluate(const asset_fund_fee_pool_operation& o) { try { @@ -173,7 +173,7 @@ void_result asset_fund_fee_pool_evaluator::do_evaluate(const asset_fund_fee_pool } FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_operation& o) -{ +{ try { db().adjust_balance(o.from_account, -o.amount); db().modify( *asset_dyn_data, [&]( asset_dynamic_data_object& data ) { @@ -181,7 +181,7 @@ void_result asset_fund_fee_pool_evaluator::do_apply(const asset_fund_fee_pool_op }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) { try { @@ -214,7 +214,7 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) } FC_CAPTURE_AND_RETHROW((o)) } void_result asset_update_evaluator::do_apply(const asset_update_operation& o) -{ +{ try { database& d = db(); // If we are now disabling force settlements, cancel all open force settlement orders @@ -236,10 +236,10 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o) }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& o) -{ +{ try { database& d = db(); const asset_object& a = o.asset_to_update(d); @@ -258,19 +258,19 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) ); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasset_operation& o) -{ +{ try { db().modify(*bitasset_to_update, [&o](asset_bitasset_data_object& b) { b.options = o.new_options; }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_update_feed_producers_evaluator::do_evaluate(const asset_update_feed_producers_evaluator::operation_type& o) -{ +{ try { database& d = db(); FC_ASSERT( o.new_feed_producers.size() <= d.get_global_properties().parameters.maximum_asset_feed_publishers ); @@ -286,10 +286,10 @@ void_result asset_update_feed_producers_evaluator::do_evaluate(const asset_updat bitasset_to_update = &b; FC_ASSERT( a.issuer == o.issuer ); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_feed_producers_evaluator::operation_type& o) -{ +{ try { db().modify(*bitasset_to_update, [&](asset_bitasset_data_object& a) { //This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored. //I need to update the map such that the keys match the new publishers, but not munge the old price feeds from @@ -311,11 +311,10 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f return void_result(); -} - +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result asset_global_settle_evaluator::do_evaluate(const asset_global_settle_evaluator::operation_type& op) -{ +{ try { const database& d = db(); asset_to_settle = &op.asset_to_settle(d); FC_ASSERT(asset_to_settle->is_market_issued()); @@ -332,31 +331,31 @@ void_result asset_global_settle_evaluator::do_evaluate(const asset_global_settle "Cannot force settle at supplied price: least collateralized short lacks sufficient collateral to settle."); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (op) ) } void_result asset_global_settle_evaluator::do_apply(const asset_global_settle_evaluator::operation_type& op) -{ +{ try { database& d = db(); d.globally_settle_asset( op.asset_to_settle(db()), op.settle_price ); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (op) ) } -object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::operation_type& op) -{ +void_result asset_settle_evaluator::do_evaluate(const asset_settle_evaluator::operation_type& op) +{ try { const database& d = db(); asset_to_settle = &op.amount.asset_id(d); FC_ASSERT(asset_to_settle->is_market_issued()); const auto& bitasset = asset_to_settle->bitasset_data(d); FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() ); - if( bitasset.is_prediction_market ) + if( bitasset.is_prediction_market ) FC_ASSERT( bitasset.has_settlement(), "global settlement must occur before force settling a prediction market" ); FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount); - return d.get_index_type().get_next_id(); -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op) -{ +{ try { database& d = db(); d.adjust_balance(op.account, -op.amount); @@ -388,7 +387,7 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator:: s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec; }).id; } -} +} FC_CAPTURE_AND_RETHROW( (op) ) } void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_operation& o) { try { @@ -430,6 +429,6 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope db().check_call_orders( base ); return void_result(); - } FC_CAPTURE_AND_RETHROW((o)) } +} FC_CAPTURE_AND_RETHROW((o)) } } } // graphene::chain diff --git a/libraries/chain/delegate_evaluator.cpp b/libraries/chain/delegate_evaluator.cpp index b86409f8..4d2d841d 100644 --- a/libraries/chain/delegate_evaluator.cpp +++ b/libraries/chain/delegate_evaluator.cpp @@ -22,14 +22,15 @@ #include namespace graphene { namespace chain { -object_id_type delegate_create_evaluator::do_evaluate( const delegate_create_operation& op ) -{ + +void_result delegate_create_evaluator::do_evaluate( const delegate_create_operation& op ) +{ try { FC_ASSERT(db().get(op.delegate_account).is_lifetime_member()); - return object_id_type(); -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type delegate_create_evaluator::do_apply( const delegate_create_operation& op ) -{ +{ try { vote_id_type vote_id; db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { vote_id = p.get_next_vote_id(vote_id_type::committee); @@ -40,6 +41,6 @@ object_id_type delegate_create_evaluator::do_apply( const delegate_create_operat obj.vote_id = vote_id; }); return new_del_object.id; -} +} FC_CAPTURE_AND_RETHROW( (op) ) } } } // graphene::chain diff --git a/libraries/chain/global_parameters_evaluator.cpp b/libraries/chain/global_parameters_evaluator.cpp index 49b05769..b73cfa14 100644 --- a/libraries/chain/global_parameters_evaluator.cpp +++ b/libraries/chain/global_parameters_evaluator.cpp @@ -22,19 +22,19 @@ namespace graphene { namespace chain { void_result global_parameters_update_evaluator::do_evaluate(const global_parameters_update_operation& o) -{ +{ try { FC_ASSERT(trx_state->_is_proposed_trx); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result global_parameters_update_evaluator::do_apply(const global_parameters_update_operation& o) -{ +{ try { db().modify(db().get_global_properties(), [&o](global_property_object& p) { p.pending_parameters = o.new_parameters; }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/asset_evaluator.hpp b/libraries/chain/include/graphene/chain/asset_evaluator.hpp index 786a7e2a..0a9c3b1a 100644 --- a/libraries/chain/include/graphene/chain/asset_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/asset_evaluator.hpp @@ -27,7 +27,7 @@ namespace graphene { namespace chain { public: typedef asset_create_operation operation_type; - object_id_type do_evaluate( const asset_create_operation& o ); + void_result do_evaluate( const asset_create_operation& o ); object_id_type do_apply( const asset_create_operation& o ); }; @@ -113,7 +113,7 @@ namespace graphene { namespace chain { public: typedef asset_settle_operation operation_type; - object_id_type do_evaluate(const operation_type& op); + void_result do_evaluate(const operation_type& op); operation_result do_apply(const operation_type& op); const asset_object* asset_to_settle = nullptr; diff --git a/libraries/chain/include/graphene/chain/delegate_evaluator.hpp b/libraries/chain/include/graphene/chain/delegate_evaluator.hpp index 6fd04a19..9d3d478e 100644 --- a/libraries/chain/include/graphene/chain/delegate_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/delegate_evaluator.hpp @@ -26,7 +26,7 @@ namespace graphene { namespace chain { public: typedef delegate_create_operation operation_type; - object_id_type do_evaluate( const delegate_create_operation& o ); + void_result do_evaluate( const delegate_create_operation& o ); object_id_type do_apply( const delegate_create_operation& o ); }; diff --git a/libraries/chain/include/graphene/chain/limit_order_evaluator.hpp b/libraries/chain/include/graphene/chain/limit_order_evaluator.hpp index 2560b58c..82e31566 100644 --- a/libraries/chain/include/graphene/chain/limit_order_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/limit_order_evaluator.hpp @@ -27,7 +27,7 @@ namespace graphene { namespace chain { public: typedef limit_order_create_operation operation_type; - object_id_type do_evaluate( const limit_order_create_operation& o ); + void_result do_evaluate( const limit_order_create_operation& o ); object_id_type do_apply( const limit_order_create_operation& o ); asset calculate_market_fee( const asset_object* aobj, const asset& trade_amount ); @@ -43,7 +43,7 @@ namespace graphene { namespace chain { public: typedef limit_order_cancel_operation operation_type; - asset do_evaluate( const limit_order_cancel_operation& o ); + void_result do_evaluate( const limit_order_cancel_operation& o ); asset do_apply( const limit_order_cancel_operation& o ); const limit_order_object* _order; diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index 454be5ab..27db2a00 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -29,7 +29,7 @@ namespace graphene { namespace chain { public: typedef proposal_create_operation operation_type; - object_id_type do_evaluate( const proposal_create_operation& o ); + void_result do_evaluate( const proposal_create_operation& o ); object_id_type do_apply( const proposal_create_operation& o ); transaction _proposed_trx; diff --git a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp index 09c49607..7634a7d0 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_evaluator.hpp @@ -29,7 +29,7 @@ class vesting_balance_create_evaluator : public evaluator namespace graphene { namespace chain { -object_id_type limit_order_create_evaluator::do_evaluate( const limit_order_create_operation& op ) -{ +void_result limit_order_create_evaluator::do_evaluate( const limit_order_create_operation& op ) +{ try { database& d = db(); FC_ASSERT( op.expiration >= d.head_block_time() ); @@ -42,13 +42,11 @@ object_id_type limit_order_create_evaluator::do_evaluate( const limit_order_crea FC_ASSERT( d.get_balance( _seller, _sell_asset ) >= op.amount_to_sell, "insufficient balance", ("balance",d.get_balance(_seller,_sell_asset))("amount_to_sell",op.amount_to_sell) ); - return object_id_type(); -} -template -std::reverse_iterator reverse( const I& itr ) { return std::reverse_iterator(itr); } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type limit_order_create_evaluator::do_apply( const limit_order_create_operation& op ) -{ +{ try { const auto& seller_stats = _seller->statistics(db()); db().modify( seller_stats, [&]( account_statistics_object& bal ){ if( op.amount_to_sell.asset_id == asset_id_type() ) @@ -120,23 +118,21 @@ object_id_type limit_order_create_evaluator::do_apply( const limit_order_create_ FC_ASSERT( !op.fill_or_kill || db().find_object(result) == nullptr ); return result; -} // limit_order_evaluator::do_apply +} FC_CAPTURE_AND_RETHROW( (op) ) } -asset limit_order_cancel_evaluator::do_evaluate( const limit_order_cancel_operation& o ) -{ - database& d = db(); +void_result limit_order_cancel_evaluator::do_evaluate( const limit_order_cancel_operation& o ) +{ try { + database& d = db(); _order = &o.order(d); - FC_ASSERT( _order->seller == o.fee_paying_account ); - auto refunded = _order->amount_for_sale(); - //adjust_balance( fee_paying_account, &refunded.asset_id(d), refunded.amount ); + FC_ASSERT( _order->seller == o.fee_paying_account ); - return refunded; -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } asset limit_order_cancel_evaluator::do_apply( const limit_order_cancel_operation& o ) -{ - database& d = db(); +{ try { + database& d = db(); auto base_asset = _order->sell_price.base.asset_id; auto quote_asset = _order->sell_price.quote.asset_id; @@ -150,5 +146,6 @@ asset limit_order_cancel_evaluator::do_apply( const limit_order_cancel_operation db().check_call_orders(quote_asset(d)); return refunded; -} +} FC_CAPTURE_AND_RETHROW( (o) ) } + } } // graphene::chain diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index cbf1613b..f65155aa 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -22,8 +22,8 @@ namespace graphene { namespace chain { -object_id_type proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) -{ +void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) +{ try { const database& d = db(); const auto& global_parameters = d.get_global_properties().parameters; @@ -47,11 +47,11 @@ object_id_type proposal_create_evaluator::do_evaluate(const proposal_create_oper _proposed_trx.operations.push_back(op.op); _proposed_trx.validate(); - return object_id_type(); -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o) -{ +{ try { database& d = db(); const proposal_object& proposal = d.create([&](proposal_object& proposal) { @@ -71,10 +71,10 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati }); return proposal.id; -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o) -{ +{ try { database& d = db(); _proposal = &o.proposal(d); @@ -106,10 +106,10 @@ void_result proposal_update_evaluator::do_evaluate(const proposal_update_operati } return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_update_evaluator::do_apply(const proposal_update_operation& o) -{ +{ try { database& d = db(); // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal skip @@ -147,10 +147,10 @@ void_result proposal_update_evaluator::do_apply(const proposal_update_operation& } return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result proposal_delete_evaluator::do_evaluate(const proposal_delete_operation& o) -{ +{ try { database& d = db(); _proposal = &o.proposal(d); @@ -162,13 +162,13 @@ void_result proposal_delete_evaluator::do_evaluate(const proposal_delete_operati ("provided", o.fee_paying_account)("required", *required_approvals)); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } -void_result proposal_delete_evaluator::do_apply(const proposal_delete_operation&) -{ +void_result proposal_delete_evaluator::do_apply(const proposal_delete_operation& o) +{ try { db().remove(*_proposal); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/transfer_evaluator.cpp b/libraries/chain/transfer_evaluator.cpp index 1bd28153..223fe474 100644 --- a/libraries/chain/transfer_evaluator.cpp +++ b/libraries/chain/transfer_evaluator.cpp @@ -51,5 +51,6 @@ void_result transfer_evaluator::do_apply( const transfer_operation& o ) db().adjust_balance( o.from, -o.amount ); db().adjust_balance( o.to, o.amount ); return void_result(); -} FC_CAPTURE_AND_RETHROW( (o) )} +} FC_CAPTURE_AND_RETHROW( (o) ) } + } } // graphene::chain diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 7589f0a9..fc1d2a95 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -23,8 +23,8 @@ namespace graphene { namespace chain { -object_id_type vesting_balance_create_evaluator::do_evaluate( const vesting_balance_create_operation& op ) -{ +void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance_create_operation& op ) +{ try { const database& d = db(); const account_object& creator_account = op.creator( d ); @@ -36,14 +36,14 @@ object_id_type vesting_balance_create_evaluator::do_evaluate( const vesting_bala FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); - return object_id_type(); -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } struct init_policy_visitor { typedef void result_type; - init_policy_visitor( vesting_policy& po, + init_policy_visitor( vesting_policy& po, const share_type& begin_balance, const fc::time_point_sec& n ):p(po),init_balance(begin_balance),now(n){} @@ -73,7 +73,7 @@ struct init_policy_visitor }; object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op ) -{ +{ try { database& d = db(); const time_point_sec now = d.head_block_time(); @@ -91,10 +91,10 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance return vbo.id; -} +} FC_CAPTURE_AND_RETHROW( (op) ) } 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(); @@ -106,10 +106,10 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan /* const account_object& owner_account = */ op.owner( d ); // TODO: Check asset authorizations and withdrawals return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (op) ) } 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(); @@ -128,6 +128,6 @@ void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_ // TODO: Check asset authorizations and withdrawals return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (op) ) } } } // graphene::chain diff --git a/libraries/chain/withdraw_permission_evaluator.cpp b/libraries/chain/withdraw_permission_evaluator.cpp index ff30daaf..086ec368 100644 --- a/libraries/chain/withdraw_permission_evaluator.cpp +++ b/libraries/chain/withdraw_permission_evaluator.cpp @@ -22,7 +22,7 @@ namespace graphene { namespace chain { -object_id_type withdraw_permission_create_evaluator::do_evaluate(const operation_type& op) +void_result withdraw_permission_create_evaluator::do_evaluate(const operation_type& op) { try { database& d = db(); FC_ASSERT(d.find_object(op.withdraw_from_account)); @@ -32,7 +32,7 @@ object_id_type withdraw_permission_create_evaluator::do_evaluate(const operation FC_ASSERT(op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec > d.head_block_time()); FC_ASSERT(op.withdrawal_period_sec >= d.get_global_properties().parameters.block_interval); - return d.get_index_type().get_next_id(); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type withdraw_permission_create_evaluator::do_apply(const operation_type& op) @@ -42,7 +42,7 @@ object_id_type withdraw_permission_create_evaluator::do_apply(const operation_ty p.authorized_account = op.authorized_account; p.withdrawal_limit = op.withdrawal_limit; p.withdrawal_period_sec = op.withdrawal_period_sec; - p.expiration = op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec; + p.expiration = op.period_start_time + op.periods_until_expiration * op.withdrawal_period_sec; p.period_start_time = op.period_start_time; }).id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -133,9 +133,9 @@ void_result withdraw_permission_delete_evaluator::do_evaluate(const withdraw_per } FC_CAPTURE_AND_RETHROW( (op) ) } void_result withdraw_permission_delete_evaluator::do_apply(const withdraw_permission_delete_evaluator::operation_type& op) -{ +{ try { db().remove(db().get(op.withdrawal_permission)); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (op) ) } } } // graphene::chain diff --git a/libraries/chain/witness_evaluator.cpp b/libraries/chain/witness_evaluator.cpp index 89befa44..f6724c87 100644 --- a/libraries/chain/witness_evaluator.cpp +++ b/libraries/chain/witness_evaluator.cpp @@ -23,14 +23,15 @@ #include namespace graphene { namespace chain { -object_id_type witness_create_evaluator::do_evaluate( const witness_create_operation& op ) -{ + +void_result witness_create_evaluator::do_evaluate( const witness_create_operation& op ) +{ try { FC_ASSERT(db().get(op.witness_account).is_lifetime_member()); - return object_id_type(); -} + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type witness_create_evaluator::do_apply( const witness_create_operation& op ) -{ +{ try { vote_id_type vote_id; db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { vote_id = p.get_next_vote_id(vote_id_type::witness); @@ -43,10 +44,10 @@ object_id_type witness_create_evaluator::do_apply( const witness_create_operatio obj.next_secret = op.initial_secret; }); return new_witness_object.id; -} +} FC_CAPTURE_AND_RETHROW( (op) ) } void_result witness_withdraw_pay_evaluator::do_evaluate(const witness_withdraw_pay_evaluator::operation_type& o) -{ +{ try { database& d = db(); witness = &d.get(o.from_witness); @@ -56,10 +57,10 @@ void_result witness_withdraw_pay_evaluator::do_evaluate(const witness_withdraw_p to_account = &d.get(o.to_account); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } void_result witness_withdraw_pay_evaluator::do_apply(const witness_withdraw_pay_evaluator::operation_type& o) -{ +{ try { database& d = db(); d.adjust_balance(o.to_account, asset(o.amount)); @@ -69,6 +70,6 @@ void_result witness_withdraw_pay_evaluator::do_apply(const witness_withdraw_pay_ }); return void_result(); -} +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain diff --git a/libraries/chain/worker_evaluator.cpp b/libraries/chain/worker_evaluator.cpp index 9faa8f29..060ffca3 100644 --- a/libraries/chain/worker_evaluator.cpp +++ b/libraries/chain/worker_evaluator.cpp @@ -22,15 +22,15 @@ namespace graphene { namespace chain { -object_id_type worker_create_evaluator::do_evaluate(const worker_create_evaluator::operation_type& o) +void_result worker_create_evaluator::do_evaluate(const worker_create_evaluator::operation_type& o) { try { database& d = db(); FC_ASSERT(d.get(o.owner).is_lifetime_member()); FC_ASSERT(o.work_begin_date >= d.head_block_time()); - return object_id_type(); -} FC_CAPTURE_AND_RETHROW((o)) } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } object_id_type worker_create_evaluator::do_apply(const worker_create_evaluator::operation_type& o) { try { @@ -51,6 +51,6 @@ object_id_type worker_create_evaluator::do_apply(const worker_create_evaluator:: w.worker.set_which(o.initializer.which()); w.worker.visit(worker_initialize_visitor(w, o.initializer, d)); }).id; -} FC_CAPTURE_AND_RETHROW((o)) } +} FC_CAPTURE_AND_RETHROW( (o) ) } } } // graphene::chain From b63de0ad29892aef8f1850227b6118e0e2ea082b Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Tue, 23 Jun 2015 17:41:09 -0400 Subject: [PATCH 105/354] Fix some compiler warnings --- libraries/chain/account_object.cpp | 3 +++ libraries/chain/db_init.cpp | 2 ++ libraries/chain/include/graphene/chain/witness_scheduler.hpp | 3 ++- tests/tests/operation_tests.cpp | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 7be8599f..8a455591 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -87,9 +87,12 @@ void account_statistics_object::process_fees(const account_object& a, database& share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); assert( network_cut <= core_fee_total ); + +#ifndef NDEBUG share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); share_type accumulated = network_cut - burned; assert( accumulated + burned == network_cut ); +#endif share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage); share_type referral = core_fee_total - network_cut - lifetime_cut; diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index a0f07ed0..4d087481 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -334,7 +334,9 @@ 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) { memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size()); diff --git a/libraries/chain/include/graphene/chain/witness_scheduler.hpp b/libraries/chain/include/graphene/chain/witness_scheduler.hpp index e3f18c4d..77f67478 100644 --- a/libraries/chain/include/graphene/chain/witness_scheduler.hpp +++ b/libraries/chain/include/graphene/chain/witness_scheduler.hpp @@ -40,6 +40,7 @@ class generic_witness_scheduler public: void check_invariant() const { +#ifndef NDEBUG CountType tokens = _ineligible_no_turn.size() + _eligible.size(); CountType turns = _eligible.size(); for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token ) @@ -47,7 +48,7 @@ class generic_witness_scheduler assert( _tokens == tokens ); assert( _turns == turns ); - +#endif flat_set< WitnessID > witness_set; // make sure each witness_id occurs only once among the three states diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index fd418fa6..b5bae524 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE( black_swan ) publish_feed( bitusd, feedproducer, current_feed ); /// this sell order is designed to trigger a black swan - create_sell_order(borrower2, bitusd.amount(1000), core.amount(3000)); + create_sell_order( borrower2, bitusd.amount(1000), core.amount(3000) ); FC_ASSERT( bitusd.bitasset_data(db).has_settlement() ); From f99b02ba581978107f35901438b09af65cace535 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 16:31:30 -0400 Subject: [PATCH 106/354] config.hpp: Implement GRAPHENE_COLLATERAL_RATIO_DENOM #70 --- libraries/chain/asset.cpp | 47 +------------------ .../chain/include/graphene/chain/asset.hpp | 6 +-- .../chain/include/graphene/chain/config.hpp | 3 +- 3 files changed, 7 insertions(+), 49 deletions(-) diff --git a/libraries/chain/asset.cpp b/libraries/chain/asset.cpp index daff0964..36963571 100644 --- a/libraries/chain/asset.cpp +++ b/libraries/chain/asset.cpp @@ -121,26 +121,9 @@ namespace graphene { namespace chain { { try { //wdump((debt)(collateral)(collateral_ratio)); boost::rational swan(debt.amount.value,collateral.amount.value); - boost::rational ratio( collateral_ratio, 1000 ); + boost::rational ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM ); auto cp = swan * ratio; return ~(asset( cp.numerator(), debt.asset_id ) / asset( cp.denominator(), collateral.asset_id )); - - /* - while( collateral.amount < 100000 && debt.amount < GRAPHENE_MAX_SHARE_SUPPLY/100 ) - { - collateral.amount *= 1000; - debt.amount *= 1000; - } - - fc::uint128 tmp( collateral.amount.value ); - tmp *= 1000; - tmp /= collateral_ratio; - FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); - asset col( tmp.to_uint64(), collateral.asset_id); - - if( col.amount == 0 ) col.amount = 1; - return col / debt; - */ } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) } bool price::is_null() const { return *this == price(); } @@ -166,35 +149,9 @@ namespace graphene { namespace chain { price price_feed::max_short_squeeze_price()const { boost::rational sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value); - boost::rational ratio( 1000, maximum_short_squeeze_ratio ); + boost::rational ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio ); auto cp = sp * ratio; return (asset( cp.numerator(), settlement_price.base.asset_id ) / asset( cp.denominator(), settlement_price.quote.asset_id )); - - /* - asset collateral = settlement_price.quote; - fc::uint128 tmp( collateral.amount.value ); - tmp *= maximum_short_squeeze_ratio; - tmp /= 1000; - FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); - collateral.amount = tmp.to_uint64(); - auto tmp2 = settlement_price.base / collateral; - wdump((rtn)(tmp2)); - return rtn; - */ - } - /* - price price_feed::maintenance_price()const - { - asset collateral = settlement_price.quote; - fc::uint128 tmp( collateral.amount.value ); - tmp *= maintenance_collateral_ratio; - tmp /= 1000; - FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY ); - collateral.amount = tmp.to_uint64(); - return settlement_price.base / collateral; - } - */ - } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index b306833f..697099a8 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -128,7 +128,7 @@ namespace graphene { namespace chain { /** * Required maintenance collateral is defined * as a fixed point number with a maximum value of 10.000 - * and a minimum value of 1.000. (denominated in 1000) + * and a minimum value of 1.000. (denominated in GRAPHENE_COLLATERAL_RATIO_DENOM) * * A black swan event occurs when value_of_collateral equals * value_of_debt, to avoid a black swan a margin call is @@ -145,10 +145,10 @@ namespace graphene { namespace chain { */ price settlement_price; - /** Fixed point between 1.000 and 10.000, implied fixed point denominator is 1000 */ + /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */ uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO; - /** Fixed point between 1.000 and 10.000, implied fixed point denominator is 1000 */ + /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */ uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO; /** diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 2aa1b1ab..75da2610 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -63,11 +63,12 @@ #define GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS 10 /** - * These ratios are fixed point numbers with a denominator of 1000, the + * These ratios are fixed point numbers with a denominator of GRAPHENE_COLLATERAL_RATIO_DENOM, the * minimum maitenance collateral is therefore 1.001x and the default * maintenance ratio is 1.75x */ ///@{ +#define GRAPHENE_COLLATERAL_RATIO_DENOM 1000 #define GRAPHENE_MIN_COLLATERAL_RATIO 1001 ///< lower than this could result in divide by 0 #define GRAPHENE_MAX_COLLATERAL_RATIO 32000 ///< higher than this is unnecessary and may exceed int16 storage #define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 ///< Call when collateral only pays off 175% the debt From 08651fc04516e21d0ebeda1b0e509679d5f08e9f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 17:37:51 -0400 Subject: [PATCH 107/354] db_debug.cpp: Fix witness index type in debug_dump() --- libraries/chain/db_debug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp index 34f12b58..34b37265 100644 --- a/libraries/chain/db_debug.cpp +++ b/libraries/chain/db_debug.cpp @@ -73,7 +73,7 @@ void database::debug_dump() total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees; total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool; } - for( const witness_object& witness_obj : db.get_index_type>() ) + for( const witness_object& witness_obj : db.get_index_type().indices() ) { //idump((witness_obj)); total_balances[asset_id_type()] += witness_obj.accumulated_income; From 6944a3fe1fbb2df78889fcdace9b75791e8cdb64 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 16:13:51 -0400 Subject: [PATCH 108/354] operation_tests.cpp: Remove redundant unimplemented tests --- tests/tests/operation_tests.cpp | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index b5bae524..965379d7 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1283,33 +1283,6 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test ) BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0); } FC_LOG_AND_RETHROW() } -/** - * This test should simulate a prediction market which means the following: - * - * 1) Issue a BitAsset without Forced Settling & With Global Settling - * 2) Don't Publish any Price Feeds - * 3) Ensure that margin calls do not occur even if the highest bid would indicate it - * 4) Match some Orders - * 5) Trigger Global Settle on the Asset - * 6) The maintenance collateral must always be 1:1 - */ -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_prediction_market_test, 1 ) -BOOST_AUTO_TEST_CASE( unimp_prediction_market_test ) -{ - BOOST_FAIL( "not implemented" ); -} - -/** - * This test should verify that the asset_global_settle operation works as expected, - * make sure that global settling cannot be performed by anyone other than the - * issuer and only if the global settle bit is set. - */ -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_global_settle_test_2, 1 ) -BOOST_AUTO_TEST_CASE( unimp_global_settle_test_2 ) -{ - BOOST_FAIL( "not implemented" ); -} - /** * Asset Burn Test should make sure that all assets except bitassets * can be burned and all supplies add up. From 5e630171ca60f7a1c73fe9d7b021e645a05b95b9 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 16:14:18 -0400 Subject: [PATCH 109/354] operation_tests2.cpp: Fix global_settle_test --- tests/tests/operation_tests2.cpp | 57 ++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index a087fa76..da2f58ec 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -437,29 +437,62 @@ BOOST_AUTO_TEST_CASE( witness_create ) generate_block(0, nathan_private_key); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_global_settle_test, 1 ) -BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) +/** + * This test should verify that the asset_global_settle operation works as expected, + * make sure that global settling cannot be performed by anyone other than the + * issuer and only if the global settle bit is set. + */ +BOOST_AUTO_TEST_CASE( global_settle_test ) { - BOOST_FAIL( "TODO - Reimplement this" ); - /* try { ACTORS((nathan)(ben)(valentine)(dan)); asset_id_type bit_usd_id = create_bitasset("BITUSD", nathan_id, 100, global_settle | charge_market_fee).get_id(); + + update_feed_producers( bit_usd_id(db), { nathan_id } ); + + price_feed feed; + feed.settlement_price = price( asset( 1000, bit_usd_id ), asset( 500 ) ); + feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + publish_feed( bit_usd_id(db), nathan, feed ); + transfer(genesis_account, ben_id, asset(10000)); transfer(genesis_account, valentine_id, asset(10000)); transfer(genesis_account, dan_id, asset(10000)); - create_short(ben_id, asset(1000, bit_usd_id), asset(1000)); - create_sell_order(valentine_id, asset(1000), asset(1000, bit_usd_id)); - create_short(valentine_id, asset(500, bit_usd_id), asset(600)); - create_sell_order(dan_id, asset(600), asset(500, bit_usd_id)); + borrow(ben, asset(1000, bit_usd_id), asset(1000)); + BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 1000); + BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000); - BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990); - BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 8400); + create_sell_order(ben_id, asset(1000, bit_usd_id), asset(1000)); BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000); + + create_sell_order(valentine_id, asset(1000), asset(1000, bit_usd_id)); + BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0); + BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10000); + BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990); + BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 9000); + + borrow(valentine, asset(500, bit_usd_id), asset(600)); + BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 1490); + BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 8400); + + create_sell_order(valentine_id, asset(500, bit_usd_id), asset(600)); + BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990); + BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 8400); + + create_sell_order(dan_id, asset(600), asset(500, bit_usd_id)); + BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 990); + BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 9000); + BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0); + BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 10000); BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 495); BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9400); + // add some collateral + borrow(ben, asset(0, bit_usd_id), asset(1000)); + BOOST_CHECK_EQUAL(get_balance(ben_id, asset_id_type()), 9000); + { asset_global_settle_operation op; op.asset_to_settle = bit_usd_id; @@ -476,6 +509,9 @@ BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) PUSH_TX( db, trx ); } + force_settle(valentine_id(db), asset(990, bit_usd_id)); + force_settle(dan_id(db), asset(495, bit_usd_id)); + BOOST_CHECK_EQUAL(get_balance(valentine_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(valentine_id, asset_id_type()), 10046); BOOST_CHECK_EQUAL(get_balance(ben_id, bit_usd_id), 0); @@ -483,7 +519,6 @@ BOOST_AUTO_TEST_CASE( unimp_global_settle_test ) BOOST_CHECK_EQUAL(get_balance(dan_id, bit_usd_id), 0); BOOST_CHECK_EQUAL(get_balance(dan_id, asset_id_type()), 9850); } FC_LOG_AND_RETHROW() - */ } BOOST_AUTO_TEST_CASE( worker_create_test ) From 20a10a074b9d85892541b5b9aded8276c2ef17a5 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 18:23:41 -0400 Subject: [PATCH 110/354] Issue #47 market history added to api --- libraries/app/CMakeLists.txt | 2 +- libraries/app/api.cpp | 23 +++++++++++++++++++ libraries/app/include/graphene/app/api.hpp | 7 +++++- .../market_history/market_history_plugin.hpp | 4 ++++ programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/main.cpp | 2 ++ 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 308ee736..07d1d43b 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -6,7 +6,7 @@ add_library( graphene_app plugin.cpp ) -target_link_libraries( graphene_app graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities ) +target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 47ac4625..6a7a2d15 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -445,6 +445,28 @@ namespace graphene { namespace app { } return result; } + 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(); + vector result; + result.reserve(100); + + if( a > b ) std::swap(a,b); + + const auto& bidx = db.get_index_type(); + const auto& by_key_idx = bidx.indices().get(); + + auto itr = by_key_idx.lower_bound( bucket_key( a, b, bucket_seconds, start ) ); + while( itr != by_key_idx.end() && itr->key.open <= end && result.size() < 100 ) + { + if( !(itr->key.base == a && itr->key.quote == b && itr->key.seconds == bucket_seconds) ) + return result; + result.push_back(*itr); + ++itr; + } + return result; + } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } /** TODO: add secondary index that will accelerate this process */ vector database_api::get_proposed_transactions( account_id_type id )const @@ -464,4 +486,5 @@ namespace graphene { namespace app { return result; } + } } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index b0513b45..b6a7efbe 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -29,10 +29,14 @@ #include #include + +#include + #include namespace graphene { namespace app { using namespace graphene::chain; + using namespace graphene::market_history; class application; @@ -270,6 +274,7 @@ namespace graphene { namespace app { int limit = 100, operation_history_id_type start = operation_history_id_type())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; private: application& _app; }; @@ -372,7 +377,7 @@ FC_API(graphene::app::database_api, (get_transaction_hex) (get_proposed_transactions) ) -FC_API(graphene::app::history_api, (get_account_history)) +FC_API(graphene::app::history_api, (get_account_history)(get_market_history)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) FC_API(graphene::app::login_api, (login) diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 3c146444..6c0bdca1 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -42,6 +42,10 @@ namespace bpo = boost::program_options; struct bucket_key { + bucket_key( asset_id_type a, asset_id_type b, uint32_t s, fc::time_point_sec o ) + :base(a),quote(b),seconds(s),open(o){} + bucket_key(){} + asset_id_type base; asset_id_type quote; uint32_t seconds = 0; diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 0c75e94a..d4d276ae 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -9,4 +9,4 @@ endif() #endif() target_link_libraries( witness_node - PRIVATE graphene_app graphene_account_history graphene_witness graphene_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 541bb00c..bb7604ef 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -49,6 +50,7 @@ int main(int argc, char** argv) { auto witness_plug = node.register_plugin(); auto history_plug = node.register_plugin(); + auto market_history_plug = node.register_plugin(); { bpo::options_description cli, cfg; From ed381af304b6bbadb90c2ba0743c352a4625769e Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 23 Jun 2015 22:12:07 -0400 Subject: [PATCH 111/354] Spell out boost::program_options in headers instead of using alias Multiple defs of bpo alias confused gcc and broke cli_wallet build. --- libraries/app/application.cpp | 2 ++ .../app/include/graphene/app/application.hpp | 15 +++++++-------- libraries/app/include/graphene/app/plugin.hpp | 13 ++++++------- libraries/app/plugin.cpp | 6 +++--- .../account_history/account_history_plugin.cpp | 2 +- .../account_history/account_history_plugin.hpp | 7 ++++--- .../market_history/market_history_plugin.hpp | 8 +++++--- .../market_history/market_history_plugin.cpp | 2 +- .../witness/include/graphene/witness/witness.hpp | 5 ++--- libraries/plugins/witness/witness.cpp | 2 ++ tests/app/main.cpp | 8 ++++---- 11 files changed, 37 insertions(+), 33 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index f9711780..869dcc52 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -50,6 +50,8 @@ using chain::block_id_type; using std::vector; +namespace bpo = boost::program_options; + namespace detail { class application_impl : public net::node_delegate diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index 6f29405a..f7ee1450 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -24,7 +24,6 @@ namespace graphene { namespace app { namespace detail { class application_impl; } - namespace bpo = boost::program_options; using std::string; class abstract_plugin; @@ -35,10 +34,10 @@ namespace graphene { namespace app { application(); ~application(); - void set_program_options( bpo::options_description& command_line_options, - bpo::options_description& configuration_file_options )const; - void initialize(const fc::path& data_dir, const bpo::variables_map&options); - void initialize_plugins( const bpo::variables_map& options ); + void set_program_options( boost::program_options::options_description& command_line_options, + boost::program_options::options_description& configuration_file_options )const; + void initialize(const fc::path& data_dir, const boost::program_options::variables_map&options); + void initialize_plugins( const boost::program_options::variables_map& options ); void startup(); void shutdown(); void startup_plugins(); @@ -50,7 +49,7 @@ namespace graphene { namespace app { auto plug = std::make_shared(); plug->plugin_set_app(this); - bpo::options_description plugin_cli_options("Options for plugin " + plug->plugin_name()), 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); @@ -80,8 +79,8 @@ namespace graphene { namespace app { void add_plugin( const string& name, std::shared_ptr p ); std::shared_ptr my; - bpo::options_description _cli_options; - bpo::options_description _cfg_options; + boost::program_options::options_description _cli_options; + boost::program_options::options_description _cfg_options; }; } } diff --git a/libraries/app/include/graphene/app/plugin.hpp b/libraries/app/include/graphene/app/plugin.hpp index 103cd441..53fb70b3 100644 --- a/libraries/app/include/graphene/app/plugin.hpp +++ b/libraries/app/include/graphene/app/plugin.hpp @@ -23,7 +23,6 @@ #include namespace graphene { namespace app { -namespace bpo = boost::program_options; class abstract_plugin { @@ -43,7 +42,7 @@ class abstract_plugin * * @param options The options passed to the application, via configuration files or command line */ - virtual void plugin_initialize( const bpo::variables_map& options ) = 0; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) = 0; /** * @brief Begin normal runtime operations @@ -79,8 +78,8 @@ class abstract_plugin * may simply provide an empty implementation of this method. */ virtual void plugin_set_program_options( - bpo::options_description& command_line_options, - bpo::options_description& config_file_options + boost::program_options::options_description& command_line_options, + boost::program_options::options_description& config_file_options ) = 0; }; @@ -95,13 +94,13 @@ class plugin : public abstract_plugin virtual ~plugin() override; virtual std::string plugin_name()const override; - virtual void plugin_initialize( const bpo::variables_map& options ) override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; virtual void plugin_startup() override; virtual void plugin_shutdown() override; virtual void plugin_set_app( application* app ) override; virtual void plugin_set_program_options( - bpo::options_description& command_line_options, - bpo::options_description& config_file_options + boost::program_options::options_description& command_line_options, + boost::program_options::options_description& config_file_options ) override; chain::database& database() { return *app().chain_database(); } diff --git a/libraries/app/plugin.cpp b/libraries/app/plugin.cpp index cfff8382..b52a53d6 100644 --- a/libraries/app/plugin.cpp +++ b/libraries/app/plugin.cpp @@ -36,7 +36,7 @@ std::string plugin::plugin_name()const return ""; } -void plugin::plugin_initialize( const bpo::variables_map& options ) +void plugin::plugin_initialize( const boost::program_options::variables_map& options ) { return; } @@ -58,8 +58,8 @@ void plugin::plugin_set_app( application* app ) } void plugin::plugin_set_program_options( - bpo::options_description& command_line_options, - bpo::options_description& config_file_options + boost::program_options::options_description& command_line_options, + boost::program_options::options_description& config_file_options ) { return; diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 13f28ee6..f632d8ae 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -593,7 +593,7 @@ void account_history_plugin::plugin_set_program_options( ) { cli.add_options() - ("track-account", bpo::value>()->composing()->multitoken(), "Account ID to track history for (may specify multiple times)") + ("track-account", boost::program_options::value>()->composing()->multitoken(), "Account ID to track history for (may specify multiple times)") ; cfg.add(cli); } diff --git a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp index f0bdc464..fbaf44c7 100644 --- a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp +++ b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp @@ -24,7 +24,6 @@ namespace graphene { namespace account_history { using namespace chain; -namespace bpo = boost::program_options; // // Plugins should #define their SPACE_ID's so plugins with @@ -82,8 +81,10 @@ class account_history_plugin : public graphene::app::plugin virtual ~account_history_plugin(); std::string plugin_name()const override; - virtual void plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) override; - virtual void plugin_initialize(const bpo::variables_map& options) 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; flat_set tracked_accounts()const; diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 6c0bdca1..1453ca6c 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -24,7 +24,6 @@ namespace graphene { namespace market_history { using namespace chain; -namespace bpo = boost::program_options; // // Plugins should #define their SPACE_ID's so plugins with @@ -111,8 +110,11 @@ class market_history_plugin : public graphene::app::plugin virtual ~market_history_plugin(); std::string plugin_name()const override; - virtual void plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) override; - virtual void plugin_initialize(const bpo::variables_map& options) 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; vector get_history( const bucket_key& start, const bucket_key& end )const; diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index d6dbd168..c097befd 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -169,7 +169,7 @@ void market_history_plugin::plugin_set_program_options( ) { cli.add_options() - ("bucket-size", bpo::value>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)") + ("bucket-size", boost::program_options::value>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)") ; cfg.add(cli); } diff --git a/libraries/plugins/witness/include/graphene/witness/witness.hpp b/libraries/plugins/witness/include/graphene/witness/witness.hpp index d0c825e9..85f0cf26 100644 --- a/libraries/plugins/witness/include/graphene/witness/witness.hpp +++ b/libraries/plugins/witness/include/graphene/witness/witness.hpp @@ -23,7 +23,6 @@ #include namespace graphene { namespace witness_plugin { -namespace bpo = boost::program_options; class witness_plugin : public graphene::app::plugin { public: @@ -47,7 +46,7 @@ public: void set_block_production(bool allow) { _production_enabled = allow; } - virtual void plugin_initialize( const bpo::variables_map& options ) override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; virtual void plugin_startup() override; virtual void plugin_shutdown() override; @@ -55,7 +54,7 @@ private: void schedule_next_production(const graphene::chain::chain_parameters& global_parameters); void block_production_loop(); - bpo::variables_map _options; + boost::program_options::variables_map _options; bool _production_enabled = false; std::map _private_keys; std::set _witnesses; diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 8ad64c1e..2d5b1c91 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -28,6 +28,8 @@ using namespace graphene::witness_plugin; using std::string; using std::vector; +namespace bpo = boost::program_options; + void witness_plugin::plugin_set_program_options( boost::program_options::options_description& command_line_options, boost::program_options::options_description& config_file_options) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index de35c181..f0abfa78 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -45,16 +45,16 @@ BOOST_AUTO_TEST_CASE( two_node_network ) graphene::app::application app1; app1.register_plugin(); - bpo::variables_map cfg; - cfg.emplace("p2p-endpoint", bpo::variable_value(string("127.0.0.1:3939"), false)); + boost::program_options::variables_map cfg; + cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:3939"), false)); app1.initialize(app_dir.path(), cfg); graphene::app::application app2; app2.register_plugin(); auto cfg2 = cfg; cfg2.erase("p2p-endpoint"); - cfg2.emplace("p2p-endpoint", bpo::variable_value(string("127.0.0.1:4040"), false)); - cfg2.emplace("seed-node", bpo::variable_value(vector{"127.0.0.1:3939"}, false)); + cfg2.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:4040"), false)); + cfg2.emplace("seed-node", boost::program_options::variable_value(vector{"127.0.0.1:3939"}, false)); app2.initialize(app2_dir.path(), cfg2); app1.startup(); From 44313ac8a40f8ed790efe53767c3c4faf9dadd5c Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 24 Jun 2015 09:58:33 -0400 Subject: [PATCH 112/354] Address #79: Should be fixed, but I can't test because of #55 --- libraries/app/application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 869dcc52..769dbf2e 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -152,6 +152,8 @@ namespace detail { auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); genesis_state_type initial_state; + fc::reflector::visit( + fee_schedule_type::fee_set_visitor{initial_state.initial_parameters.current_fees, 0}); secret_hash_type::encoder enc; fc::raw::pack(enc, nathan_key); fc::raw::pack(enc, secret_hash_type()); From af812a7b51132e79f70ba3a58c6dc366efd76558 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 12:46:03 -0400 Subject: [PATCH 113/354] #47 Adding API to get the available history buckets based upon the configuration --- docs | 2 +- libraries/app/api.cpp | 12 +++++- libraries/app/include/graphene/app/api.hpp | 38 ++++++++++--------- libraries/fc | 2 +- .../market_history/market_history_plugin.hpp | 3 +- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/docs b/docs index 94ec061b..71ed2984 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 94ec061b91edbe9d1ebae576e641f921998428c8 +Subproject commit 71ed2984b71d57cab13cdf12074cff150edc1d3d diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 6a7a2d15..014b7385 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -445,7 +445,17 @@ namespace graphene { namespace app { } return result; } - 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 + + + flat_set history_api::get_market_history_buckets()const + { + auto hist = _app.get_plugin( "market_history" ); + FC_ASSERT( hist ); + return hist->tracked_buckets(); + } + + 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(); diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index b6a7efbe..21fbc2e7 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -258,25 +258,27 @@ namespace graphene { namespace app { */ class history_api { - public: - history_api(application& app):_app(app){} + public: + history_api(application& app):_app(app){} - /** - * @brief Get operations relevant to the specificed account - * @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(account_id_type account, - operation_history_id_type stop = operation_history_id_type(), - int limit = 100, - operation_history_id_type start = operation_history_id_type())const; + /** + * @brief Get operations relevant to the specificed account + * @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(account_id_type account, + operation_history_id_type stop = operation_history_id_type(), + int limit = 100, + operation_history_id_type start = operation_history_id_type())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; - private: - application& _app; + 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; + flat_set get_market_history_buckets()const; + private: + application& _app; }; /** @@ -377,7 +379,7 @@ FC_API(graphene::app::database_api, (get_transaction_hex) (get_proposed_transactions) ) -FC_API(graphene::app::history_api, (get_account_history)(get_market_history)) +FC_API(graphene::app::history_api, (get_account_history)(get_market_history)(get_market_history_buckets)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) FC_API(graphene::app::login_api, (login) diff --git a/libraries/fc b/libraries/fc index 701b9ac0..c09035db 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 701b9ac00c46234ee7cc6385027f5942590debc9 +Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 1453ca6c..79484194 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -117,8 +117,7 @@ class market_history_plugin : public graphene::app::plugin const boost::program_options::variables_map& options) override; virtual void plugin_startup() override; - vector get_history( const bucket_key& start, const bucket_key& end )const; - const flat_set& tracked_buckets()const; + const flat_set& tracked_buckets()const; private: friend class detail::market_history_plugin_impl; From aaf31d840f12fb4457b7aade6382fa908670e1d9 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 14:01:37 -0400 Subject: [PATCH 114/354] #47 Fix crash, added history lenght limits, added init testing --- .../market_history/market_history_plugin.hpp | 1 + .../market_history/market_history_plugin.cpp | 53 ++++++++++++++++--- tests/common/database_fixture.cpp | 11 ++++ 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 79484194..201cf3a1 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -117,6 +117,7 @@ class market_history_plugin : public graphene::app::plugin const boost::program_options::variables_map& options) override; virtual void plugin_startup() override; + uint32_t max_history()const; const flat_set& tracked_buckets()const; private: diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index c097befd..25c85b79 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -53,6 +53,7 @@ class market_history_plugin_impl market_history_plugin& _self; flat_set _tracked_buckets; + uint32_t _maximum_history_per_bucket_size = 1000; }; @@ -76,17 +77,23 @@ struct operation_process_fill_order auto& db = _plugin.database(); const auto& bucket_idx = db.get_index_type(); + auto max_history = _plugin.max_history(); for( auto bucket : buckets ) { + auto cutoff = (fc::time_point() + fc::seconds( bucket * max_history)); + bucket_key key; key.base = o.pays.asset_id; key.quote = o.receives.asset_id; - price trade_price = o.pays / o.receives; + + /** for every matched order there are two fill order operations created, one for + * each side. We can filter the duplicates by only considering the fill operations where + * the base > quote + */ if( key.base > key.quote ) - { - std::swap( key.base,key.quote ); - trade_price = ~trade_price; - } + continue; + + price trade_price = o.pays / o.receives; key.seconds = bucket; key.open = fc::time_point() + fc::seconds((_now.sec_since_epoch() / key.seconds) * key.seconds); @@ -127,6 +134,23 @@ struct operation_process_fill_order } }); } + + if( max_history != 0 ) + { + key.open = fc::time_point_sec(); + auto itr = by_key_idx.lower_bound( key ); + + while( itr != by_key_idx.end() && + itr->key.base == key.base && + itr->key.quote == key.quote && + itr->key.seconds == bucket && + itr->key.open < cutoff ) + { + auto old_itr = itr; + ++itr; + db.remove( *old_itr ); + } + } } } }; @@ -136,6 +160,9 @@ market_history_plugin_impl::~market_history_plugin_impl() void market_history_plugin_impl::update_market_histories( const signed_block& b ) { + if( _maximum_history_per_bucket_size == 0 ) return; + if( _tracked_buckets.size() == 0 ) return; + graphene::chain::database& db = database(); const vector& hist = db.get_applied_operations(); for( auto op : hist ) @@ -169,18 +196,23 @@ void market_history_plugin::plugin_set_program_options( ) { cli.add_options() - ("bucket-size", boost::program_options::value>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)") + ("bucket-size", boost::program_options::value>()->composing()->multitoken(), + "Track market history by grouping orders into buckets of equal size measured in seconds, may specify more than one bucket size") + ("history-per-size", boost::program_options::value()->default_value(1000), + "How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)") ; cfg.add(cli); } void market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ +{ try { database().applied_block.connect( [&]( const signed_block& b){ my->update_market_histories(b); } ); database().add_index< primary_index< bucket_index > >(); LOAD_VALUE_SET(options, "bucket-size", my->_tracked_buckets, uint32_t); -} + if( options.count( "history-per-size" ) ) + my->_maximum_history_per_bucket_size = options["history-per-size"].as(); +} FC_CAPTURE_AND_RETHROW() } void market_history_plugin::plugin_startup() { @@ -191,4 +223,9 @@ const flat_set& market_history_plugin::tracked_buckets() const return my->_tracked_buckets; } +uint32_t market_history_plugin::max_history()const +{ + return my->_maximum_history_per_bucket_size; +} + } } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 45803265..e34bae4e 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -46,13 +47,17 @@ using std::cerr; database_fixture::database_fixture() : app(), db( *app.chain_database() ) { + try { auto ahplugin = app.register_plugin(); + auto mhplugin = app.register_plugin(); boost::program_options::variables_map options; // app.initialize(); ahplugin->plugin_set_app(&app); ahplugin->plugin_initialize(options); + mhplugin->plugin_set_app(&app); + mhplugin->plugin_initialize(options); secret_hash_type::encoder enc; fc::raw::pack(enc, delegate_priv_key); @@ -68,11 +73,17 @@ database_fixture::database_fixture() fc::reflector::visit(fee_schedule_type::fee_set_visitor{genesis_state.initial_parameters.current_fees, 0}); db.init_genesis(genesis_state); ahplugin->plugin_startup(); + mhplugin->plugin_startup(); generate_block(); genesis_key(db); // attempt to deref trx.set_expiration(db.head_block_time() + fc::minutes(1)); + } catch ( const fc::exception& e ) + { + edump( (e.to_detail_string()) ); + throw; + } return; } From 045cfbd9146a8d408ca9364f7641c194ac940a38 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 14:44:50 -0400 Subject: [PATCH 115/354] #47 Fix crash when parsing config file --- libraries/plugins/market_history/market_history_plugin.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index 25c85b79..a4ffc482 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -209,7 +209,11 @@ void market_history_plugin::plugin_initialize(const boost::program_options::vari database().applied_block.connect( [&]( const signed_block& b){ my->update_market_histories(b); } ); database().add_index< primary_index< bucket_index > >(); - LOAD_VALUE_SET(options, "bucket-size", my->_tracked_buckets, uint32_t); + if( options.count( "bucket-size" ) ) + { + const std::vector& buckets = options["bucket-size"].as>(); + for( auto o : buckets ) my->_tracked_buckets.insert(o); + } if( options.count( "history-per-size" ) ) my->_maximum_history_per_bucket_size = options["history-per-size"].as(); } FC_CAPTURE_AND_RETHROW() } From ffd797c048de651c6c8a955fe6f95fae56c6493c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 15:38:28 -0400 Subject: [PATCH 116/354] adding API call to return all accounts that reference a particular key id or account --- libraries/app/api.cpp | 19 +++++++++++++++++++ libraries/app/include/graphene/app/api.hpp | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 014b7385..105b948b 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -478,6 +478,25 @@ namespace graphene { namespace app { return result; } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } + /** + * @return all accounts that referr to the key or account id in their owner or active authorities. + */ + vector database_api::get_account_references( object_id_type key_or_account_id )const + { + const auto& idx = _db.get_index_type(); + const auto& aidx = dynamic_cast&>(idx); + const auto& refs = aidx.get_secondary_index(); + auto itr = refs.account_to_memberships.find(key_or_account_id); + vector result; + + if( itr != refs.account_to_memberships.end() ) + { + result.reserve( itr->second.size() ); + for( auto item : itr->second ) result.push_back(item); + } + return result; + } + /** TODO: add secondary index that will accelerate this process */ vector database_api::get_proposed_transactions( account_id_type id )const { diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 21fbc2e7..c081ef17 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -237,6 +237,10 @@ namespace graphene { namespace app { */ vector get_proposed_transactions( account_id_type id )const; + /** + * @return all accounts that referr to the key or account id in their owner or active authorities. + */ + vector get_account_references( object_id_type key_or_account_id )const; private: /** called every time a block is applied to report the objects that were changed */ @@ -378,6 +382,7 @@ FC_API(graphene::app::database_api, (cancel_all_subscriptions) (get_transaction_hex) (get_proposed_transactions) + (get_account_references) ) FC_API(graphene::app::history_api, (get_account_history)(get_market_history)(get_market_history_buckets)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) From c4b2bb443ba623b4b4ad07020a00f60c75df3ce1 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 16:08:36 -0400 Subject: [PATCH 117/354] #85 - changing key_object from simple_index to multi_index --- libraries/chain/db_init.cpp | 9 ++++++++- .../chain/include/graphene/chain/address.hpp | 1 + .../include/graphene/chain/key_object.hpp | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 4d087481..2d0763ed 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -103,11 +103,18 @@ void database::initialize_indexes() //Protocol object indexes add_index< primary_index >(); add_index< primary_index >(); + auto acnt_index = add_index< primary_index >(); acnt_index->add_secondary_index(); acnt_index->add_secondary_index(); - add_index< primary_index> >(); + // this is the fast effecient version for validation only + // add_index< primary_index> >(); + + // this is the slower version designed to aid GUI use. We will + // default to the "slow" version until we need a faster version. + add_index< primary_index >(); + add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/address.hpp b/libraries/chain/include/graphene/chain/address.hpp index 33357828..39f80800 100644 --- a/libraries/chain/include/graphene/chain/address.hpp +++ b/libraries/chain/include/graphene/chain/address.hpp @@ -56,6 +56,7 @@ namespace graphene { namespace chain { explicit operator std::string()const; ///< converts to base58 + checksum + friend size_t hash_value( const address& v ) { return *((size_t*)&v.addr._hash[2]); } fc::ripemd160 addr; }; inline bool operator == ( const address& a, const address& b ) { return a.addr == b.addr; } diff --git a/libraries/chain/include/graphene/chain/key_object.hpp b/libraries/chain/include/graphene/chain/key_object.hpp index 7daaa5ab..cb928f2c 100644 --- a/libraries/chain/include/graphene/chain/key_object.hpp +++ b/libraries/chain/include/graphene/chain/key_object.hpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { typedef static_variant address_or_key; @@ -42,6 +44,23 @@ namespace graphene { namespace chain { address_or_key key_data; }; + + struct by_address; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + key_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + hashed_non_unique< tag, const_mem_fun > + > + > key_multi_index_type; + /** + * @ingroup object_index + */ + typedef generic_index key_index; } } FC_REFLECT_TYPENAME( graphene::chain::address_or_key ) From 23a633bea172f4a4741cc607747cdb70c273ef1d Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 16:19:33 -0400 Subject: [PATCH 118/354] #85 adding api call get_keys_for_address --- libraries/app/api.cpp | 18 ++++++++++++++++++ libraries/app/include/graphene/app/api.hpp | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 105b948b..2d2a5a02 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -515,5 +515,23 @@ namespace graphene { namespace app { return result; } + /** + * @return all key_ids that have been registered for a given address. + */ + vector database_api::get_keys_for_address( const address& a )const + { try { + vector result; + const auto& idx = _db.get_index_type(); + const auto& aidx = idx.indices().get(); + auto itr = aidx.find(a); + + while( itr != aidx.end() && itr->key_address() == a ) + { + result.push_back( itr->id ); + ++itr; + } + return result; + } FC_CAPTURE_AND_RETHROW( (a) ) } + } } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index c081ef17..024b0697 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -242,6 +242,11 @@ namespace graphene { namespace app { */ vector get_account_references( object_id_type key_or_account_id )const; + /** + * @return all key_ids that have been registered for a given address. + */ + vector get_keys_for_address( const address& a )const; + private: /** called every time a block is applied to report the objects that were changed */ void on_objects_changed(const vector& ids); @@ -383,6 +388,7 @@ FC_API(graphene::app::database_api, (get_transaction_hex) (get_proposed_transactions) (get_account_references) + (get_keys_for_address) ) FC_API(graphene::app::history_api, (get_account_history)(get_market_history)(get_market_history_buckets)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) From a0765e2cf2efcdfece249aa104c869790696078c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 16:38:56 -0400 Subject: [PATCH 119/354] Removing unnecessary indexing from account history plugin, it can now focus on just tracking operation history --- .../account_history_plugin.cpp | 324 +----------------- .../account_history_plugin.hpp | 28 -- tests/common/database_fixture.cpp | 3 + 3 files changed, 4 insertions(+), 351 deletions(-) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index f632d8ae..4f5e763b 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -34,69 +34,15 @@ namespace graphene { namespace account_history { namespace detail { -class account_create_observer : public graphene::chain::evaluation_observer -{ - public: - account_create_observer( account_history_plugin& plugin ) - : _plugin( plugin ) {} - virtual ~account_create_observer(); - - virtual void post_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result ) override; - - account_history_plugin& _plugin; -}; - -class account_update_observer : public graphene::chain::evaluation_observer -{ - public: - account_update_observer( account_history_plugin& plugin ) - : _plugin( plugin ) - { - _pre_account_keys.reserve( GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP * 2 + 2 ); - } - virtual ~account_update_observer(); - - virtual void pre_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge ) override; - - virtual void post_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result ) override; - - virtual void evaluation_failed( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result ) override; - - account_history_plugin& _plugin; - flat_set< key_id_type > _pre_account_keys; -}; class account_history_plugin_impl { public: account_history_plugin_impl(account_history_plugin& _plugin) - : _self( _plugin ), - _create_observer( _plugin ), - _update_observer( _plugin ) + : _self( _plugin ) { } virtual ~account_history_plugin_impl(); - void rebuild_key_account_index(); - flat_set get_keys_for_account( const account_id_type& account_id ); @@ -104,7 +50,6 @@ class account_history_plugin_impl * and will process/index all operations that were applied in the block. */ void update_account_histories( const signed_block& b ); - void index_account_keys( const account_id_type& account_id ); graphene::chain::database& database() { @@ -112,8 +57,6 @@ class account_history_plugin_impl } account_history_plugin& _self; - account_create_observer _create_observer; - account_update_observer _update_observer; flat_set _tracked_accounts; }; @@ -248,271 +191,13 @@ struct operation_get_impacted_accounts {} }; -account_create_observer::~account_create_observer() -{ - return; -} - -void account_create_observer::post_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result - ) -{ - assert( op.which() == operation::tag::value ); - - if( !apply ) - return; - - // if we only care about given accounts, then key -> account mapping - // is not maintained - if( _plugin.my->_tracked_accounts.size() > 0 ) - return; - - account_id_type account_id = result.get< object_id_type >(); - _plugin.my->index_account_keys( account_id ); - return; -} - -account_update_observer::~account_update_observer() -{ - return; -} - -void account_update_observer::pre_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge - ) -{ - assert( op.which() == operation::tag::value ); - - if( !apply ) - return; - - // if we only care about given accounts, then key -> account mapping - // is not maintained - if( _plugin.my->_tracked_accounts.size() > 0 ) - return; - - // is this a tx which affects a key? - // switch( op.which() ) - // { - // see note in configure() why account_create_operation handling is unnecessary - // case operation::tag::value: - // const account_create_operation& create_op = op.get< account_create_operation >(); - // break; - // case operation::tag::value: - // const account_update_operation& update_op = op.get< account_update_operation >(); - // _pre_account_keys.clear(); - // get_updatable_account_keys( update_op, _pre_account_keys ); - // break; - // default: - // FC_ASSERT( false, "account_update_observer got unexpected operation type" ); - //} - - const account_update_operation& update_op = op.get< account_update_operation >(); - _pre_account_keys = _plugin.my->get_keys_for_account( update_op.account ); - return; -} - -void account_update_observer::post_evaluate( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result - ) -{ - assert( op.which() == operation::tag::value ); - - if( !apply ) - return; - - graphene::chain::database& db = _plugin.my->database(); - - // if we only care about given accounts, then key -> account mapping - // is not maintained - if( _plugin.my->_tracked_accounts.size() > 0 ) - return; - - // wild back-of-envelope heuristic: most account update ops - // don't add more than two keys - flat_set post_account_keys; - post_account_keys.reserve( _pre_account_keys.size() + 2 ); - - vector removed_account_keys; - removed_account_keys.reserve( _pre_account_keys.size() ); - - const account_update_operation& update_op = op.get< account_update_operation >(); - post_account_keys = _plugin.my->get_keys_for_account( update_op.account ); - - std::set_difference( - _pre_account_keys.begin(), _pre_account_keys.end(), - post_account_keys.begin(), post_account_keys.end(), - std::back_inserter( removed_account_keys ) - ); - - // - // If a key_id is in _pre_account_keys but not in post_account_keys, - // then it is placed in removed_account_keys by set_difference(). - // - // Note, the *address* corresponding to this key may still exist - // in the account, because it may be aliased to multiple key_id's. - // - // We delete the key_account_object for all removed_account_keys. - // This correctly deletes addresses which were removed - // from the account by the update_op. - // - // Unfortunately, in the case of aliased keys, it deletes - // key_account_object if *any* of the aliases was removed from - // the account. We want it to delete only if *all* of the aliases - // were removed from the account. - // - // However, we need to run index_account_keys() afterwards anyway. - // It will re-add to the index any addresses which had been - // deleted -- but only if they still exist in the account under - // at least one alias. - // - // This is precisely the correct behavior. - // - - for( const key_id_type& key_id : removed_account_keys ) - { - auto& index = db.get_index_type().indices().get(); - auto it = index.find( key_id(db).key_address() ); - assert( it != index.end() ); - - db.modify( *it, [&]( key_account_object& ka ) - { - ka.account_ids.erase( update_op.account ); - }); - } - - _plugin.my->index_account_keys( update_op.account ); - - return; -} - -void account_update_observer::evaluation_failed( - const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result - ) -{ - if( !apply ) - return; - - // if we only care about given accounts, then key -> account mapping - // is not maintained - if( _plugin.my->_tracked_accounts.size() > 0 ) - return; - - // cleaning up here is not strictly necessary, but good "hygiene" - _pre_account_keys.clear(); - return; -} account_history_plugin_impl::~account_history_plugin_impl() { return; } -void account_history_plugin_impl::rebuild_key_account_index() -{ - // TODO: We should really delete the index before we re-create it. - // TODO: Building and sorting a vector of tuples is probably more efficient - const graphene::chain::database& db = database(); - vector< pair< account_id_type, address > > tuples_from_db; - const auto& primary_account_idx = db.get_index_type().indices().get(); - for( const account_object& acct : primary_account_idx ) - index_account_keys( acct.id ); - return; -} - -flat_set account_history_plugin_impl::get_keys_for_account( const account_id_type& account_id ) -{ - const graphene::chain::database& db = database(); - const account_object& acct = account_id(db); - - const flat_map& owner_auths = - acct.owner.auths; - const flat_map& active_auths = - acct.active.auths; - - flat_set key_id_set; - key_id_set.reserve(owner_auths.size() + active_auths.size() + 2); - - key_id_set.insert(acct.options.memo_key); - - // we don't use get_keys() here to avoid an intermediate copy operation - for( const pair& item : active_auths ) - { - if( item.first.type() == key_object_type ) - key_id_set.insert( item.first ); - } - - for( const pair& item : owner_auths ) - { - if( item.first.type() == key_object_type ) - key_id_set.insert( item.first ); - } - - return key_id_set; -} - -void account_history_plugin_impl::index_account_keys( const account_id_type& account_id ) -{ - // for each key in account authority... get address, modify(..) - graphene::chain::database& db = database(); - - flat_set key_id_set = get_keys_for_account( account_id ); - - flat_set
address_set; - - // - // we pass the addresses through another flat_set because the - // blockchain doesn't force de-duplication of addresses - // (multiple key_id's might refer to the same address) - // - address_set.reserve( key_id_set.size() ); - - for( const key_id_type& key_id : key_id_set ) - address_set.insert( key_id(db).key_address() ); - - // add mappings for the given account - for( const address& addr : address_set ) - { - auto& idx = db.get_index_type().indices().get(); - auto it = idx.find( addr ); - if( it == idx.end() ) - { - // if unknown, we need to create a new object - db.create( [&]( key_account_object& ka ) - { - ka.key = addr; - ka.account_ids.insert( account_id ); - }); - } - else - { - // if known, we need to add to existing object - db.modify( *it, - [&]( key_account_object& ka ) - { - ka.account_ids.insert( account_id ); - }); - } - } - - return; -} void account_history_plugin_impl::update_account_histories( const signed_block& b ) { @@ -555,8 +240,6 @@ void account_history_plugin_impl::update_account_histories( const signed_block& { if( impacted.find( account_id ) != impacted.end() ) { - index_account_keys( account_id ); - // add history const auto& stats_obj = account_id(db).statistics(db); const auto& ath = db.create( [&]( account_transaction_history_object& obj ){ @@ -603,17 +286,12 @@ void account_history_plugin::plugin_initialize(const boost::program_options::var database().applied_block.connect( [&]( const signed_block& b){ my->update_account_histories(b); } ); database().add_index< primary_index< simple_index< operation_history_object > > >(); database().add_index< primary_index< simple_index< account_transaction_history_object > > >(); - database().add_index< primary_index< key_account_index >>(); - - database().register_evaluation_observer( my->_create_observer ); - database().register_evaluation_observer< graphene::chain::account_update_evaluator >( my->_update_observer ); LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type); } void account_history_plugin::plugin_startup() { - my->rebuild_key_account_index(); } flat_set account_history_plugin::tracked_accounts() const diff --git a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp index fbaf44c7..3734a715 100644 --- a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp +++ b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp @@ -45,29 +45,6 @@ enum account_history_object_type bucket_object_type = 1 ///< used in market_history_plugin }; -class key_account_object : public abstract_object -{ - public: - static const uint8_t space_id = ACCOUNT_HISTORY_SPACE_ID; - static const uint8_t type_id = key_account_object_type; - - key_account_object() {} - key_account_object( const address& a ) : key(a) {} - - address key; - flat_set account_ids; -}; - -struct by_key{}; -typedef multi_index_container< - key_account_object, - indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, - ordered_unique< tag, member< key_account_object, address, &key_account_object::key > > - > -> key_account_object_multi_index_type; - -typedef generic_index key_account_index; namespace detail { @@ -95,8 +72,3 @@ class account_history_plugin : public graphene::app::plugin } } //graphene::account_history -FC_REFLECT_DERIVED( graphene::account_history::key_account_object, - (graphene::db::object), - (key) - (account_ids) - ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e34bae4e..43464b59 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -188,6 +188,7 @@ void database_fixture::verify_asset_supplies( )const void database_fixture::verify_account_history_plugin_index( )const { + return; if( skip_key_index_test ) return; @@ -219,6 +220,7 @@ void database_fixture::verify_account_history_plugin_index( )const tuples_from_db.emplace_back( account_id, addr ); } + /* vector< pair< account_id_type, address > > tuples_from_index; tuples_from_index.reserve( tuples_from_db.size() ); const auto& key_account_idx = @@ -263,6 +265,7 @@ void database_fixture::verify_account_history_plugin_index( )const bool account_history_plugin_index_ok = is_equal; BOOST_CHECK( account_history_plugin_index_ok ); + */ } return; } From 11b357494b14d7ca032f6b889fd7b35adeb2a1a1 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Tue, 16 Jun 2015 19:38:06 -0400 Subject: [PATCH 120/354] Remove leveldb code from peer database, replace with writing to json file. Still need to add code to periodically write the file out to disk. --- .../include/graphene/net/peer_database.hpp | 2 - libraries/net/node.cpp | 15 +- libraries/net/peer_database.cpp | 165 ++++++------------ 3 files changed, 64 insertions(+), 118 deletions(-) diff --git a/libraries/net/include/graphene/net/peer_database.hpp b/libraries/net/include/graphene/net/peer_database.hpp index 89765a20..523cce9f 100644 --- a/libraries/net/include/graphene/net/peer_database.hpp +++ b/libraries/net/include/graphene/net/peer_database.hpp @@ -102,8 +102,6 @@ namespace graphene { namespace net { potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup); fc::optional lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup); - std::vector get_all()const; - typedef detail::peer_database_iterator iterator; iterator begin() const; iterator end() const; diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index a0570f1b..45aa03c3 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -400,7 +400,7 @@ namespace graphene { namespace net { namespace detail { fc::sha256 _chain_id; #define NODE_CONFIGURATION_FILENAME "node_config.json" -#define POTENTIAL_PEER_DATABASE_FILENAME "peers.leveldb" +#define POTENTIAL_PEER_DATABASE_FILENAME "peers.json" fc::path _node_configuration_directory; node_configuration _node_configuration; @@ -3596,6 +3596,19 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); + try + { + _potential_peer_db.close(); + } + catch ( const fc::exception& e ) + { + wlog( "Exception thrown while closing P2P peer database, ignoring: ${e}", ("e",e) ); + } + catch (...) + { + wlog( "Exception thrown while closing P2P peer database, ignoring" ); + } + // First, stop accepting incoming network connections try { diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 4ba0273b..008bc05e 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -28,7 +28,6 @@ #include #include -//#include @@ -37,49 +36,25 @@ namespace graphene { namespace net { { using namespace boost::multi_index; - struct potential_peer_database_entry - { - uint32_t database_key; - potential_peer_record peer_record; - - potential_peer_database_entry(uint32_t database_key, const potential_peer_record& peer_record) : - database_key(database_key), - peer_record(peer_record) - {} - potential_peer_database_entry(const potential_peer_database_entry& other) : - database_key(other.database_key), - peer_record(other.peer_record) - {} - - const fc::time_point_sec& get_last_seen_time() const { return peer_record.last_seen_time; } - const fc::ip::endpoint& get_endpoint() const { return peer_record.endpoint; } - }; - class peer_database_impl { public: struct last_seen_time_index {}; struct endpoint_index {}; - typedef boost::multi_index_container< potential_peer_database_entry, - indexed_by< ordered_non_unique< tag, - const_mem_fun< potential_peer_database_entry, - const fc::time_point_sec&, - &potential_peer_database_entry::get_last_seen_time> - >, - hashed_unique< tag, - const_mem_fun< potential_peer_database_entry, - const fc::ip::endpoint&, - &potential_peer_database_entry::get_endpoint - >, - std::hash - > - > - > potential_peer_set; - //private: - //typedef graphene::db::level_pod_map potential_peer_leveldb; - //potential_peer_leveldb _leveldb; + typedef boost::multi_index_container, + member >, + hashed_unique, + member, + std::hash > > > potential_peer_set; + private: potential_peer_set _potential_peer_set; + fc::path _peer_database_filename; public: void open(const fc::path& databaseFilename); @@ -104,65 +79,54 @@ namespace graphene { namespace net { _iterator(iterator) {} }; - peer_database_iterator::peer_database_iterator( const peer_database_iterator& c ) - :boost::iterator_facade(c){} + peer_database_iterator::peer_database_iterator( const peer_database_iterator& c ) : + boost::iterator_facade(c){} - void peer_database_impl::open(const fc::path& databaseFilename) + void peer_database_impl::open(const fc::path& peer_database_filename) { - /* - try + _peer_database_filename = peer_database_filename; + if (fc::exists(_peer_database_filename)) { - _leveldb.open(databaseFilename); - } - catch (const graphene::db::level_pod_map_open_failure&) - { - fc::remove_all(databaseFilename); - _leveldb.open(databaseFilename); - } - - _potential_peer_set.clear(); - - for (auto iter = _leveldb.begin(); iter.valid(); ++iter) - _potential_peer_set.insert(potential_peer_database_entry(iter.key(), iter.value())); -#define MAXIMUM_PEERDB_SIZE 1000 - if (_potential_peer_set.size() > MAXIMUM_PEERDB_SIZE) - { - // prune database to a reasonable size - auto iter = _potential_peer_set.begin(); - std::advance(iter, MAXIMUM_PEERDB_SIZE); - while (iter != _potential_peer_set.end()) + try { - _leveldb.remove(iter->database_key); - iter = _potential_peer_set.erase(iter); + std::vector peer_records = fc::json::from_file(_peer_database_filename).as >(); + std::copy(peer_records.begin(), peer_records.end(), std::inserter(_potential_peer_set, _potential_peer_set.end())); +#define MAXIMUM_PEERDB_SIZE 1000 + if (_potential_peer_set.size() > MAXIMUM_PEERDB_SIZE) + { + // prune database to a reasonable size + auto iter = _potential_peer_set.begin(); + std::advance(iter, MAXIMUM_PEERDB_SIZE); + _potential_peer_set.erase(iter, _potential_peer_set.end()); + } + } + catch (const fc::exception& e) + { + elog("error opening peer database file ${peer_database_filename}, starting with a clean database", + ("peer_database_filename", _peer_database_filename)); } } - */ } void peer_database_impl::close() { - //_leveldb.close(); + std::vector peer_records; + peer_records.reserve(_potential_peer_set.size()); + std::copy(_potential_peer_set.begin(), _potential_peer_set.end(), std::back_inserter(peer_records)); + try + { + fc::json::save_to_file(peer_records, _peer_database_filename); + } + catch (const fc::exception& e) + { + elog("error saving peer database to file ${peer_database_filename}", + ("peer_database_filename", _peer_database_filename)); + } _potential_peer_set.clear(); } void peer_database_impl::clear() { - /* - auto iter = _leveldb.begin(); - while (iter.valid()) - { - uint32_t key_to_remove = iter.key(); - ++iter; - try - { - _leveldb.remove(key_to_remove); - } - catch (fc::exception&) - { - // shouldn't happen, and if it does there's not much we can do - } - } - */ _potential_peer_set.clear(); } @@ -170,36 +134,23 @@ namespace graphene { namespace net { { auto iter = _potential_peer_set.get().find(endpointToErase); if (iter != _potential_peer_set.get().end()) - { - //_leveldb.remove(iter->database_key); _potential_peer_set.get().erase(iter); - } } void peer_database_impl::update_entry(const potential_peer_record& updatedRecord) { auto iter = _potential_peer_set.get().find(updatedRecord.endpoint); if (iter != _potential_peer_set.get().end()) - { - _potential_peer_set.get().modify(iter, [&updatedRecord](potential_peer_database_entry& entry) { entry.peer_record = updatedRecord; }); - //_leveldb.store(iter->database_key, updatedRecord); - } + _potential_peer_set.get().modify(iter, [&updatedRecord](potential_peer_record& record) { record = updatedRecord; }); else - { - uint32_t last_database_key; - //_leveldb.last(last_database_key); - uint32_t new_database_key = last_database_key + 1; - potential_peer_database_entry new_database_entry(new_database_key, updatedRecord); - _potential_peer_set.get().insert(new_database_entry); - //_leveldb.store(new_database_key, updatedRecord); - } + _potential_peer_set.get().insert(updatedRecord); } potential_peer_record peer_database_impl::lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup) { auto iter = _potential_peer_set.get().find(endpointToLookup); if (iter != _potential_peer_set.get().end()) - return iter->peer_record; + return *iter; return potential_peer_record(endpointToLookup); } @@ -207,7 +158,7 @@ namespace graphene { namespace net { { auto iter = _potential_peer_set.get().find(endpointToLookup); if (iter != _potential_peer_set.get().end()) - return iter->peer_record; + return *iter; return fc::optional(); } @@ -251,13 +202,11 @@ namespace graphene { namespace net { const potential_peer_record& peer_database_iterator::dereference() const { - return my->_iterator->peer_record; + return *my->_iterator; } } // end namespace detail - - peer_database::peer_database() : my(new detail::peer_database_impl) { @@ -315,19 +264,5 @@ namespace graphene { namespace net { { return my->size(); } - std::vector peer_database::get_all()const - { - std::vector results; - /* - auto itr = my->_leveldb.begin(); - while( itr.valid() ) - { - results.push_back( itr.value() ); - ++itr; - } - */ - return results; - } - } } // end namespace graphene::net From 37cadb2be8a5909d838c12a42cf3387647a56856 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Wed, 17 Jun 2015 13:49:28 -0400 Subject: [PATCH 121/354] Create directory for storing p2p config if it doesn't already exist --- libraries/net/peer_database.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 008bc05e..a17cc7be 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -113,8 +113,12 @@ namespace graphene { namespace net { std::vector peer_records; peer_records.reserve(_potential_peer_set.size()); std::copy(_potential_peer_set.begin(), _potential_peer_set.end(), std::back_inserter(peer_records)); + try { + fc::path peer_database_filename_dir = _peer_database_filename.parent_path(); + if (!fc::exists(peer_database_filename_dir)) + fc::create_directories(peer_database_filename_dir); fc::json::save_to_file(peer_records, _peer_database_filename); } catch (const fc::exception& e) From e7ec05f54598194ea5bb7324b58ea471e10035ba Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 24 Jun 2015 21:09:21 -0400 Subject: [PATCH 122/354] #74 Adding API call to get all call orders for a given account type --- libraries/app/api.cpp | 15 +++++++++++++++ libraries/app/include/graphene/app/api.hpp | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 2d2a5a02..d0deecc7 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -533,5 +533,20 @@ namespace graphene { namespace app { return result; } FC_CAPTURE_AND_RETHROW( (a) ) } + vector database_api::get_margin_positions( const account_id_type& id )const + { try { + const auto& idx = _db.get_index_type(); + const auto& aidx = idx.indices().get(); + auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) ); + auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) ); + vector result; + while( start != end ) + { + result.push_back(*start); + ++start; + } + return result; + } FC_CAPTURE_AND_RETHROW( (id) ) } + } } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 024b0697..be3b1d5f 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -247,6 +247,11 @@ namespace graphene { namespace app { */ vector get_keys_for_address( const address& a )const; + /** + * @return all open margin positions for a given account id. + */ + vector get_margin_positions( const account_id_type& id )const; + private: /** called every time a block is applied to report the objects that were changed */ void on_objects_changed(const vector& ids); @@ -389,6 +394,7 @@ FC_API(graphene::app::database_api, (get_proposed_transactions) (get_account_references) (get_keys_for_address) + (get_margin_positions) ) FC_API(graphene::app::history_api, (get_account_history)(get_market_history)(get_market_history_buckets)) FC_API(graphene::app::network_api, (broadcast_transaction)(add_node)(get_connected_peers)) From cb23e27aeab0b7ff1190bcb8f5f51e65255ab5f1 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 09:36:31 -0400 Subject: [PATCH 123/354] Update fc submodule #86 --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index c09035db..707b9497 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 +Subproject commit 707b9497c6ffd00e8ea45ffca429ac60880d7acc From a518c37c9ee21d0c8881b777232c8048f7e0d4c8 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 09:38:31 -0400 Subject: [PATCH 124/354] adding wallet api to return the market history --- .../plugins/market_history/market_history_plugin.cpp | 10 +++++++++- libraries/wallet/include/graphene/wallet/wallet.hpp | 2 ++ libraries/wallet/wallet.cpp | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index a4ffc482..b4ad56a1 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -73,6 +73,7 @@ struct operation_process_fill_order void operator()( const fill_order_operation& o )const { + ilog( "processing ${o}", ("o",o) ); const auto& buckets = _plugin.tracked_buckets(); auto& db = _plugin.database(); const auto& bucket_idx = db.get_index_type(); @@ -91,7 +92,10 @@ struct operation_process_fill_order * the base > quote */ if( key.base > key.quote ) + { + ilog( " skipping because base > quote" ); continue; + } price trade_price = o.pays / o.receives; @@ -102,7 +106,7 @@ struct operation_process_fill_order auto itr = by_key_idx.find( key ); if( itr == by_key_idx.end() ) { // create new bucket - db.create( [&]( bucket_object& b ){ + const auto& obj = db.create( [&]( bucket_object& b ){ b.key = key; b.quote_volume += trade_price.quote.amount; b.open_base = trade_price.base.amount; @@ -114,9 +118,11 @@ struct operation_process_fill_order b.low_base = b.close_base; b.low_quote = b.close_quote; }); + wlog( " creating bucket ${b}", ("b",obj) ); } else { // update existing bucket + wlog( " before updating bucket ${b}", ("b",*itr) ); db.modify( *itr, [&]( bucket_object& b ){ b.base_volume += trade_price.base.amount; b.quote_volume += trade_price.quote.amount; @@ -133,6 +139,7 @@ struct operation_process_fill_order b.low_quote = b.close_quote; } }); + wlog( " after bucket bucket ${b}", ("b",*itr) ); } if( max_history != 0 ) @@ -146,6 +153,7 @@ struct operation_process_fill_order itr->key.seconds == bucket && itr->key.open < cutoff ) { + elog( " removing old bucket ${b}", ("b", *itr) ); auto old_itr = itr; ++itr; db.remove( *old_itr ); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index b2d4ed95..39a22437 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -113,6 +113,7 @@ class wallet_api vector list_account_balances(const string& id); vector list_assets(const string& lowerbound, uint32_t limit)const; vector get_account_history(string name, int limit)const; + vector get_market_history(string symbol, string symbol2, uint32_t bucket)const; vector get_limit_orders(string a, string b, uint32_t limit)const; vector get_call_orders(string a, uint32_t limit)const; vector get_settle_orders(string a, uint32_t limit)const; @@ -362,6 +363,7 @@ FC_API( graphene::wallet::wallet_api, (get_block) (get_account_count) (get_account_history) + (get_market_history) (get_global_properties) (get_dynamic_global_properties) (get_object) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 36c7e9c6..1b0a1699 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1693,6 +1693,11 @@ vector wallet_api::get_account_history(string name, in return my->_remote_hist->get_account_history(get_account(name).get_id(), operation_history_id_type(), limit, operation_history_id_type()); } +vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const +{ + return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() ); +} + 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); From 88aeaa5e6926eeaf7056e3524a5132962fe47d71 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 09:47:56 -0400 Subject: [PATCH 125/354] #47 Fix bug with not initializing base volume --- libraries/fc | 2 +- .../plugins/market_history/market_history_plugin.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/fc b/libraries/fc index 707b9497..c09035db 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 707b9497c6ffd00e8ea45ffca429ac60880d7acc +Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index b4ad56a1..b2c87f3c 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -109,6 +109,7 @@ struct operation_process_fill_order const auto& obj = db.create( [&]( bucket_object& b ){ b.key = key; b.quote_volume += trade_price.quote.amount; + b.base_volume += trade_price.base.amount; b.open_base = trade_price.base.amount; b.open_quote = trade_price.quote.amount; b.close_base = trade_price.base.amount; @@ -118,11 +119,11 @@ struct operation_process_fill_order b.low_base = b.close_base; b.low_quote = b.close_quote; }); - wlog( " creating bucket ${b}", ("b",obj) ); + //wlog( " creating bucket ${b}", ("b",obj) ); } else { // update existing bucket - wlog( " before updating bucket ${b}", ("b",*itr) ); + // wlog( " before updating bucket ${b}", ("b",*itr) ); db.modify( *itr, [&]( bucket_object& b ){ b.base_volume += trade_price.base.amount; b.quote_volume += trade_price.quote.amount; @@ -139,7 +140,7 @@ struct operation_process_fill_order b.low_quote = b.close_quote; } }); - wlog( " after bucket bucket ${b}", ("b",*itr) ); + // wlog( " after bucket bucket ${b}", ("b",*itr) ); } if( max_history != 0 ) @@ -153,7 +154,7 @@ struct operation_process_fill_order itr->key.seconds == bucket && itr->key.open < cutoff ) { - elog( " removing old bucket ${b}", ("b", *itr) ); + // elog( " removing old bucket ${b}", ("b", *itr) ); auto old_itr = itr; ++itr; db.remove( *old_itr ); From bf0ab60cebf9f973168a0f0472e8ae9889aa6736 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 10:03:18 -0400 Subject: [PATCH 126/354] Fix #88 If pushing generated block fails, then re-push all transactions to remove the failing transaction --- libraries/chain/db_block.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 51086361..92a71525 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -181,6 +181,8 @@ bool database::_push_block(const signed_block& new_block) */ processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) { + _pending_block.timestamp = head_block_time(); + processed_transaction result; with_skip_flags( skip, [&]() { @@ -294,7 +296,21 @@ signed_block database::_generate_block( signed_block tmp = _pending_block; tmp.transaction_merkle_root = tmp.calculate_merkle_root(); _pending_block.transactions.clear(); - push_block( tmp, skip ); + + bool failed = false; + try { push_block( tmp, skip ); } catch ( const fc::exception& e ) { failed = true; } + if( failed ) + { + for( const auto& trx : tmp.transactions ) + { + try { + push_transaction( trx, skip ); + } catch ( const fc::exception& e ) { + wlog( "Transaction is no longer valid: ${trx}", ("trx",trx) ); + } + } + return _generate_block( when, witness_id, block_signing_private_key ); + } return tmp; } FC_CAPTURE_AND_RETHROW( (witness_id) ) } From 49acfb3bd6267c3a3da990651f09beae8213e6a7 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 10:33:39 -0400 Subject: [PATCH 127/354] Fix up application_impl's has_item() which was throwing to indicate a missing item instead of returning false. #55 --- libraries/app/application.cpp | 31 +++++++++++++++------- libraries/chain/block_database.cpp | 16 +++++++----- libraries/net/node.cpp | 42 ++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 18 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 769dbf2e..1c1a2f41 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -195,16 +195,27 @@ namespace detail { * If delegate has the item, the network has no need to fetch it. */ virtual bool has_item( const net::item_id& id ) override - { try { - if( id.item_type == graphene::net::block_message_type ) - { - return _chain_db->is_known_block( id.item_hash ); - } - else - { - return _chain_db->is_known_transaction( id.item_hash ); - } - } FC_CAPTURE_AND_RETHROW( (id) ) } + { + try + { + if( id.item_type == graphene::net::block_message_type ) + { + // for some reason, the contains() function called by is_known_block + // throws when the block is not present (instead of returning false) + try + { + return _chain_db->is_known_block( id.item_hash ); + } + catch (const fc::key_not_found_exception&) + { + return false; + } + } + else + return _chain_db->is_known_transaction( id.item_hash ); // is_known_transaction behaves normally + } + FC_CAPTURE_AND_RETHROW( (id) ) + } /** * @brief allows the application to validate an item prior to broadcasting to peers. diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 9bc95e49..434c6682 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -88,7 +88,8 @@ void block_database::remove( const block_id_type& id ) index_entry e; auto index_pos = sizeof(e)*block_header::num_from_id(id); _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + if ( _block_num_to_pos.tellg() <= index_pos ) + FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id)); _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); @@ -110,8 +111,8 @@ bool block_database::contains( const block_id_type& id )const index_entry e; auto index_pos = sizeof(e)*block_header::num_from_id(id); _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); - + if ( _block_num_to_pos.tellg() <= index_pos ) + FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id)); _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); @@ -124,7 +125,8 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const index_entry e; auto index_pos = sizeof(e)*block_num; _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + if ( _block_num_to_pos.tellg() <= index_pos ) + FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num)); _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); @@ -140,7 +142,8 @@ optional block_database::fetch_optional( const block_id_type& id ) index_entry e; auto index_pos = sizeof(e)*block_header::num_from_id(id); _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + if ( _block_num_to_pos.tellg() <= index_pos ) + FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id)); _block_num_to_pos.seekg( index_pos ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); @@ -170,7 +173,8 @@ optional block_database::fetch_by_number( uint32_t block_num )cons index_entry e; auto index_pos = sizeof(e)*block_num; _block_num_to_pos.seekg( 0, _block_num_to_pos.end ); - FC_ASSERT( _block_num_to_pos.tellg() > index_pos ); + if ( _block_num_to_pos.tellg() <= index_pos ) + FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num)); _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); _block_num_to_pos.read( (char*)&e, sizeof(e) ); diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 45aa03c3..0e17e4c5 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -1525,7 +1525,7 @@ namespace graphene { namespace net { namespace detail { else dlog("delayed_peer_deletion_task is already scheduled (current size of _peers_to_delete is ${size})", ("size", number_of_peers_to_delete)); #else - dlog("scheduling peer for deletion: ${peer} (this will not block)"); + dlog("scheduling peer for deletion: ${peer} (this will not block)", ("peer", peer_to_delete->get_remote_endpoint())); _peers_to_delete.push_back(peer_to_delete); if (!_node_is_shutting_down && (!_delayed_peer_deletion_task_done.valid() || _delayed_peer_deletion_task_done.ready())) @@ -5040,7 +5040,44 @@ namespace graphene { namespace net { namespace detail { return statistics; } -#define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \ +// define VERBOSE_NODE_DELEGATE_LOGGING to log whenever the node delegate throws exceptions +//#define VERBOSE_NODE_DELEGATE_LOGGING +#ifdef VERBOSE_NODE_DELEGATE_LOGGING +# define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \ + try \ + { \ + call_statistics_collector statistics_collector(#method_name, \ + &_ ## method_name ## _execution_accumulator, \ + &_ ## method_name ## _delay_before_accumulator, \ + &_ ## method_name ## _delay_after_accumulator); \ + if (_thread->is_current()) \ + { \ + call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \ + return _node_delegate->method_name(__VA_ARGS__); \ + } \ + else \ + return _thread->async([&](){ \ + call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \ + return _node_delegate->method_name(__VA_ARGS__); \ + }, "invoke " BOOST_STRINGIZE(method_name)).wait(); \ + } \ + catch (const fc::exception& e) \ + { \ + dlog("node_delegate threw fc::exception: ${e}", ("e", e)); \ + throw; \ + } \ + catch (const std::exception& e) \ + { \ + dlog("node_delegate threw std::exception: ${e}", ("e", e.what())); \ + throw; \ + } \ + catch (...) \ + { \ + dlog("node_delegate threw unrecognized exception"); \ + throw; \ + } +#else +# define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \ call_statistics_collector statistics_collector(#method_name, \ &_ ## method_name ## _execution_accumulator, \ &_ ## method_name ## _delay_before_accumulator, \ @@ -5055,6 +5092,7 @@ namespace graphene { namespace net { namespace detail { call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \ return _node_delegate->method_name(__VA_ARGS__); \ }, "invoke " BOOST_STRINGIZE(method_name)).wait() +#endif bool statistics_gathering_node_delegate_wrapper::has_item( const net::item_id& id ) { From 4e206d3c54488303ec4b61c8a4fe666fa67dde9a Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 11:57:50 -0400 Subject: [PATCH 128/354] #66 tweak default fees --- .../chain/include/graphene/chain/types.hpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index ef3b12ad..efadcf15 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -348,33 +348,33 @@ namespace graphene { namespace chain { uint32_t account_update_fee = 150000; ///< the cost to update an existing account uint32_t account_transfer_fee = 300000; ///< the cost to transfer an account to a new owner uint32_t account_whitelist_fee = 300000; ///< the fee to whitelist an account - uint32_t account_len8up_fee = 1500000; - uint32_t account_len7_fee = 7000000; - uint32_t account_len6_fee = 14000000; - uint32_t account_len5_fee = 70000000; - uint32_t account_len4_fee = 280000000; - uint32_t account_len3_fee = 680000000; - uint32_t account_len2_fee = 1000000000; - uint32_t asset_create_fee = 680000000; ///< the cost to register the cheapest asset + uint32_t account_len8up_fee = 5*10000000; ///< about $1 + uint32_t account_len7_fee = 5*100000000; ///< about $10 + uint64_t account_len6_fee = 5*500000000; ///< about $50 + uint64_t account_len5_fee = 5*1000000000; ///< about $100 + uint64_t account_len4_fee = 5*2000000000; ///< about $200 + uint64_t account_len3_fee = 5*3000000000; ///< about $300 + uint64_t account_len2_fee = 5*4000000000; ///< about $400 + uint32_t asset_create_fee = 5ll*500000000; ///< about $35 for LTM, the cost to register the cheapest asset uint32_t asset_update_fee = 150000; ///< the cost to modify a registered asset uint32_t asset_issue_fee = 700000; ///< the cost to print a UIA and send it to an account uint32_t asset_burn_fee = 1500000; ///< the cost to burn an asset uint32_t asset_fund_fee_pool_fee = 150000; ///< the cost to add funds to an asset's fee pool uint32_t asset_settle_fee = 7000000; ///< the cost to trigger a forced settlement of a market-issued asset uint32_t asset_global_settle_fee = 140000000; ///< the cost to trigger a global forced settlement of a market asset - uint32_t asset_len7up_fee = 140000000; - uint32_t asset_len6_fee = 675000000; - uint32_t asset_len5_fee = 1350000000; - uint32_t asset_len4_fee = 2700000000; - uint64_t asset_len3_fee = 7000000000; + uint64_t asset_len7up_fee = 5*500000000; ///< about $35 for LTM + uint64_t asset_len6_fee = 5*5000000000; ///< about $350 for LTM + uint64_t asset_len5_fee = 5*10000000000; ///< about $700 for LTM + uint64_t asset_len4_fee = 5*50000000000; ///< about $3500 for LTM + uint64_t asset_len3_fee = 5*70000000000; ///< about $5000 for LTM uint32_t delegate_create_fee = 680000000; ///< fee for registering as a delegate; used to discourage frivolous delegates uint32_t witness_create_fee = 680000000; /// < fee for registering as a witness uint32_t witness_withdraw_pay_fee = 1500000; ///< fee for withdrawing witness pay uint32_t transfer_fee = 2700000; ///< fee for transferring some asset uint32_t limit_order_create_fee = 666666; ///< fee for placing a limit order in the markets - uint32_t limit_order_cancel_fee = 150000; ///< fee for canceling a limit order + uint32_t limit_order_cancel_fee = 0; ///< fee for canceling a limit order uint32_t call_order_fee = 800000; ///< fee for placing a call order in the markets - uint32_t publish_feed_fee = 666666; ///< fee for publishing a price feed + uint32_t publish_feed_fee = 10000; ///< fee for publishing a price feed uint32_t data_fee = 13500000; ///< a price per BYTES_PER_DATA_FEE bytes of user data uint32_t global_parameters_update_fee = 1350000; ///< the cost to update the global parameters uint32_t membership_annual_fee = 270000000; ///< the annual cost of a membership subscription @@ -382,14 +382,14 @@ namespace graphene { namespace chain { uint32_t withdraw_permission_create_fee = 2700000; ///< the cost to create a withdraw permission uint32_t withdraw_permission_update_fee = 150000; ///< the cost to update a withdraw permission uint32_t withdraw_permission_claim_fee = 700000; ///< the cost to withdraw from a withdraw permission - uint32_t withdraw_permission_delete_fee = 150000; ///< the cost to delete a withdraw permission + uint32_t withdraw_permission_delete_fee = 0; ///< the cost to delete a withdraw permission uint32_t vesting_balance_create_fee = 7000000; uint32_t vesting_balance_withdraw_fee = 2700000; uint32_t worker_create_fee = 680000000; ///< the cost to create a new worker uint32_t assert_op_fee = 150000; ///< fee per assert operation uint32_t proposal_create_fee = 7000000; ///< fee for creating a proposed transaction uint32_t proposal_update_fee = 1500000; ///< fee for adding or removing approval of a proposed transaction - uint32_t proposal_delete_fee = 150000; ///< fee for deleting a proposed transaction + uint32_t proposal_delete_fee = 0; ///< fee for deleting a proposed transaction uint32_t custom_operation_fee = 300000; ///< fee for a custom operation protected: From 342abe6ca19c29e34ba6fc224fbaaa8905df230a Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 12:07:39 -0400 Subject: [PATCH 129/354] Fix tests --- libraries/chain/db_block.cpp | 9 +++++---- tests/common/database_fixture.cpp | 4 ++-- tests/tests/block_tests.cpp | 2 ++ tests/tests/operation_tests2.cpp | 11 +++++------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 92a71525..39588ef7 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -180,7 +180,7 @@ bool database::_push_block(const signed_block& new_block) * queues. */ processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) -{ +{ try { _pending_block.timestamp = head_block_time(); processed_transaction result; @@ -189,7 +189,7 @@ processed_transaction database::push_transaction( const signed_transaction& trx, result = _push_transaction( trx ); } ); return result; -} +} FC_CAPTURE_AND_RETHROW( (trx) ) } processed_transaction database::_push_transaction( const signed_transaction& trx ) { @@ -498,8 +498,9 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] ); trx_expiration = tapos_block_summary.timestamp + chain_parameters.block_interval*trx.relative_expiration; } else if( trx.relative_expiration == 0 ) { - trx_expiration = fc::time_point_sec(trx.ref_block_prefix); - FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration ); + trx_expiration = fc::time_point_sec() + fc::seconds(trx.ref_block_prefix); + FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "", + ("trx_expiration",trx_expiration)("_pending_block.timestamp",_pending_block.timestamp)("max_til_exp",chain_parameters.maximum_time_until_expiration)); } FC_ASSERT( _pending_block.timestamp <= trx_expiration ); } else if( !(skip & skip_transaction_signatures) ) { diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 43464b59..84c45313 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -932,9 +932,9 @@ bool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = } processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ ) -{ +{ try { return db.push_transaction( tx, skip_flags ); -} +} FC_CAPTURE_AND_RETHROW((tx)) } } // graphene::chain::test diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 441266fa..88315bc0 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -462,6 +462,8 @@ BOOST_AUTO_TEST_CASE( tapos ) db1.push_transaction(trx); now += db1.block_interval(); b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); + now += db1.block_interval(); + b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); trx.clear(); trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)})); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index da2f58ec..a8307147 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create ) account_id_type dan_id = create_account("dan", dan_key_id).id; transfer(account_id_type(), nathan_id, asset(1000)); generate_block(); - trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); + trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION / 2); { withdraw_permission_create_operation op; @@ -71,9 +71,8 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create ) REQUIRE_THROW_WITH_VALUE(op, withdrawal_period_sec, 1); trx.operations.back() = op; } - trx.sign(nathan_key_id, nathan_private_key); - PUSH_TX( db, trx ); + db.push_transaction( trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -87,7 +86,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_test ) account_id_type dan_id = get_account("dan").id; key_id_type dan_key_id = dan_id(db).active.auths.begin()->first; withdraw_permission_id_type permit; - trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); + trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION/2); fc::time_point_sec first_start_time; { @@ -213,7 +212,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_nominal_case ) account_id_type dan_id = get_account("dan").id; key_id_type dan_key_id = dan_id(db).active.auths.begin()->first; withdraw_permission_id_type permit; - trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); + trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION/2); while(true) { @@ -255,7 +254,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_update ) account_id_type dan_id = get_account("dan").id; key_id_type nathan_key_id = nathan_id(db).active.auths.begin()->first; withdraw_permission_id_type permit; - trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION); + trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION/2); { withdraw_permission_update_operation op; From 9e5297d6def66e02aba31f75005c01f7e76401f8 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 25 Jun 2015 12:58:45 -0400 Subject: [PATCH 130/354] Disable fees during genesis initialization This should be a more reliable fix to #81 --- libraries/chain/db_init.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2d0763ed..fda048c7 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -241,13 +241,15 @@ void database::init_genesis(const genesis_state_type& genesis_state) create([&](global_property_object& p) { p.chain_id = fc::digest(genesis_state); 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 + fc::reflector::visit(fee_schedule_type::fee_set_visitor{p.parameters.current_fees, 0}); }); create( [&](dynamic_global_property_object& p) { p.time = fc::time_point_sec(GRAPHENE_GENESIS_TIMESTAMP); p.witness_budget = 0; }); - create([&](block_summary_object& p) { - }); + create([&](block_summary_object&) {}); // Create user accounts, apply initial stake allocation if( !genesis_state.allocation_targets.empty() ) @@ -260,7 +262,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) { asset amount(handout.weight); - key_id_type key_id = apply_operation(genesis_eval_state, key_create_operation({asset(), committee_account.id, handout.addr})).get(); + key_id_type key_id = apply_operation(genesis_eval_state, + key_create_operation({asset(), + committee_account.id, + handout.addr})).get(); account_create_operation cop; cop.name = handout.name; cop.registrar = account_id_type(1); @@ -361,6 +366,11 @@ void database::init_genesis(const genesis_state_type& genesis_state) }) ; assert( wso.id == witness_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; + }); + _undo_db.enable(); } FC_CAPTURE_AND_RETHROW((genesis_state)) } From c5666879744413435ed813c61c8352f656af6577 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 14:14:30 -0400 Subject: [PATCH 131/354] fix tests properly --- libraries/chain/db_block.cpp | 4 +--- tests/tests/block_tests.cpp | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 39588ef7..387c388c 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -181,8 +181,6 @@ bool database::_push_block(const signed_block& new_block) */ processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) { try { - _pending_block.timestamp = head_block_time(); - processed_transaction result; with_skip_flags( skip, [&]() { @@ -502,7 +500,7 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "", ("trx_expiration",trx_expiration)("_pending_block.timestamp",_pending_block.timestamp)("max_til_exp",chain_parameters.maximum_time_until_expiration)); } - FC_ASSERT( _pending_block.timestamp <= trx_expiration ); + FC_ASSERT( _pending_block.timestamp <= trx_expiration, "", ("pending.timestamp",_pending_block.timestamp)("trx_exp",trx_expiration) ); } else if( !(skip & skip_transaction_signatures) ) { FC_ASSERT(trx.relative_expiration == 0, "May not use transactions with a reference block in block 1!"); } diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 88315bc0..eb4adefd 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -462,8 +462,10 @@ BOOST_AUTO_TEST_CASE( tapos ) db1.push_transaction(trx); now += db1.block_interval(); b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); + /* now += db1.block_interval(); b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); + */ trx.clear(); trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)})); From 543959fa6405b3b6abe41342e4061f3db71e41d7 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 14:33:46 -0400 Subject: [PATCH 132/354] Fix #92 --- libraries/chain/asset_evaluator.cpp | 4 ++-- libraries/chain/db_update.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 5f3dd48a..01a4f5a2 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -308,7 +308,7 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f a.feeds[*itr]; a.update_median_feeds(db().head_block_time()); }); - + db().check_call_orders( o.asset_to_update(db()) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } @@ -425,7 +425,7 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope a.update_median_feeds(d.head_block_time()); }); - /// TODO: only do this if the median feed actually changed, otherwise there is no point + /// TODO: optimization: only do this if the median feed actually changed, otherwise there is no point db().check_call_orders( base ); return void_result(); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 637915a8..8890313d 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -222,9 +222,13 @@ void database::update_expired_feeds() auto& asset_idx = get_index_type(); for( const asset_bitasset_data_object* b : asset_idx ) if( b->feed_is_expired(head_block_time()) ) + { modify(*b, [this](asset_bitasset_data_object& a) { a.update_median_feeds(head_block_time()); }); + + check_call_orders( b->current_feed.settlement_price.base.asset_id(*this) ); + } } void database::update_withdraw_permissions() From bf9f81a21dd1aa87b4c8adbe90f58e76b5cbc1f1 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 14:46:53 -0400 Subject: [PATCH 133/354] Fix #90 - Prediction Market Asset precision must match backing asset precision --- libraries/chain/asset_evaluator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 01a4f5a2..a06f0e6d 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -58,6 +58,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.bitasset_options->feed_lifetime_sec > chain_parameters.block_interval && op.bitasset_options->force_settlement_delay_sec > chain_parameters.block_interval ); } + if( op.is_prediction_market ) + { + FC_ASSERT( op.bitasset_options ); + FC_ASSERT( op.precision == op.bitasset_options->short_backing_asset(d).precision ); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } From 16e90c53acce430f59a229d282f75f67b5ba8bbd Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 15:06:10 -0400 Subject: [PATCH 134/354] #47 Fixing bucket_object serialization --- .../market_history/market_history_plugin.hpp | 13 +++++++------ .../market_history/market_history_plugin.cpp | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 201cf3a1..43639b25 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -128,10 +128,11 @@ class market_history_plugin : public graphene::app::plugin } } //graphene::market_history FC_REFLECT( graphene::market_history::bucket_key, (base)(quote)(seconds)(open) ) -FC_REFLECT( graphene::market_history::bucket_object, (key) - (high_base)(high_quote) - (low_base)(low_quote) - (open_base)(open_quote) - (close_base)(close_quote) - (base_volume)(quote_volume) ) +FC_REFLECT_DERIVED( graphene::market_history::bucket_object, (graphene::db::object), + (key) + (high_base)(high_quote) + (low_base)(low_quote) + (open_base)(open_quote) + (close_base)(close_quote) + (base_volume)(quote_volume) ) diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index b2c87f3c..f5662021 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -106,7 +106,7 @@ struct operation_process_fill_order auto itr = by_key_idx.find( key ); if( itr == by_key_idx.end() ) { // create new bucket - const auto& obj = db.create( [&]( bucket_object& b ){ + db.create( [&]( bucket_object& b ){ b.key = key; b.quote_volume += trade_price.quote.amount; b.base_volume += trade_price.base.amount; From e41d08d843726379fc7d3227fcc77bed8ba6dcfe Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 25 Jun 2015 15:07:13 -0400 Subject: [PATCH 135/354] added callback with 'deleted' objects --- libraries/app/api.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index d0deecc7..2210b008 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -336,6 +336,10 @@ namespace graphene { namespace app { { _subscriptions[id](obj->to_variant()); } + else + { + _subscriptions[id](fc::variant(id)); + } } }); } From 714161c56cc49a4474d3d1aa1434282a4506680c Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 15:07:20 -0400 Subject: [PATCH 136/354] Fix integer overflow warnings (errors?) --- libraries/chain/include/graphene/chain/types.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index efadcf15..e537a9ba 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -350,9 +351,9 @@ namespace graphene { namespace chain { uint32_t account_whitelist_fee = 300000; ///< the fee to whitelist an account uint32_t account_len8up_fee = 5*10000000; ///< about $1 uint32_t account_len7_fee = 5*100000000; ///< about $10 - uint64_t account_len6_fee = 5*500000000; ///< about $50 - uint64_t account_len5_fee = 5*1000000000; ///< about $100 - uint64_t account_len4_fee = 5*2000000000; ///< about $200 + uint64_t account_len6_fee = 5*UINT64_C(500000000); ///< about $50 + uint64_t account_len5_fee = 5*UINT64_C(1000000000); ///< about $100 + uint64_t account_len4_fee = 5*UINT64_C(2000000000); ///< about $200 uint64_t account_len3_fee = 5*3000000000; ///< about $300 uint64_t account_len2_fee = 5*4000000000; ///< about $400 uint32_t asset_create_fee = 5ll*500000000; ///< about $35 for LTM, the cost to register the cheapest asset @@ -362,7 +363,7 @@ namespace graphene { namespace chain { uint32_t asset_fund_fee_pool_fee = 150000; ///< the cost to add funds to an asset's fee pool uint32_t asset_settle_fee = 7000000; ///< the cost to trigger a forced settlement of a market-issued asset uint32_t asset_global_settle_fee = 140000000; ///< the cost to trigger a global forced settlement of a market asset - uint64_t asset_len7up_fee = 5*500000000; ///< about $35 for LTM + uint64_t asset_len7up_fee = 5*UINT64_C(500000000); ///< about $35 for LTM uint64_t asset_len6_fee = 5*5000000000; ///< about $350 for LTM uint64_t asset_len5_fee = 5*10000000000; ///< about $700 for LTM uint64_t asset_len4_fee = 5*50000000000; ///< about $3500 for LTM From d193e7701dc2e70213e6196e97ddfc901be15fca Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 15:42:22 -0400 Subject: [PATCH 137/354] Attempted fix for #96 --- programs/cli_wallet/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 4230e495..4f79b7d4 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -145,9 +145,9 @@ int main( int argc, char** argv ) for( auto& name_formatter : wapiptr->get_result_formatters() ) wallet_cli->format_result( name_formatter.first, name_formatter.second ); - boost::signals2::scoped_connection closed_connection = con->closed.connect([]{ + boost::signals2::scoped_connection closed_connection(con->closed.connect([]{ cerr << "Server has disconnected us.\n"; - }); + })); (void)(closed_connection); if( wapiptr->is_new() ) @@ -157,9 +157,9 @@ int main( int argc, char** argv ) } else wallet_cli->set_prompt( "locked >>> " ); - boost::signals2::scoped_connection locked_connection = wapiptr->lock_changed.connect([&](bool locked) { + boost::signals2::scoped_connection locked_connection(wapiptr->lock_changed.connect([&](bool locked) { wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " ); - }); + })); auto _websocket_server = std::make_shared(); if( options.count("rpc-endpoint") ) From c80e0c09ba53905b848c5910acd0df8129389382 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 16:30:41 -0400 Subject: [PATCH 138/354] Make CLI wallet return 0 when exiting normally --- programs/cli_wallet/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 4f79b7d4..679b6329 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -219,6 +219,7 @@ int main( int argc, char** argv ) catch ( const fc::exception& e ) { std::cout << e.to_detail_string() << "\n"; + return -1; } - return -1; + return 0; } From 0936f9b5f210a7886af89ebd92a1d24ca2da7957 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Thu, 25 Jun 2015 17:34:01 -0400 Subject: [PATCH 139/354] Fix #95 CLI wallet crash on exit --- libraries/wallet/wallet.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 1b0a1699..0054850c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -312,7 +312,18 @@ public: } virtual ~wallet_api_impl() { - _remote_db->cancel_all_subscriptions(); + try + { + _remote_db->cancel_all_subscriptions(); + } + catch (const fc::exception& e) + { + // Right now the wallet_api has no way of knowing if the connection to the + // witness has already disconnected (via the witness node exiting first). + // If it has exited, cancel_all_subscriptsions() will throw and there's + // nothing we can do about it. + // dlog("Caught exception ${e} while canceling database subscriptions", ("e", e)); + } } void encrypt_keys() From 5b55ab71eac48f1a4fb52b883f72a168d7a5a387 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Jun 2015 10:42:40 -0400 Subject: [PATCH 140/354] Resolve #94 Core exchange rate is now redundantly stored in price feed for bitassets, and updated when the median feed changes. This allows feed producers to update the core exchange rate. Redundant storage is necessary, because the core exchange rate is needed for user-issued assets as well as market issued assets. --- libraries/chain/asset_evaluator.cpp | 10 ++++++++-- libraries/chain/db_update.cpp | 20 +++++++++++++------ .../chain/include/graphene/chain/asset.hpp | 16 +++++++++------ tests/common/database_fixture.cpp | 2 +- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index a06f0e6d..536450cd 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -58,7 +58,7 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.bitasset_options->feed_lifetime_sec > chain_parameters.block_interval && op.bitasset_options->force_settlement_delay_sec > chain_parameters.block_interval ); } - if( op.is_prediction_market ) + if( op.is_prediction_market ) { FC_ASSERT( op.bitasset_options ); FC_ASSERT( op.precision == op.bitasset_options->short_backing_asset(d).precision ); @@ -313,6 +313,9 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f a.feeds[*itr]; a.update_median_feeds(db().head_block_time()); }); + db().modify(o.asset_to_update(db()), [this](asset_object& a) { + a.options.core_exchange_rate = bitasset_to_update->current_feed.core_exchange_rate; + }); db().check_call_orders( o.asset_to_update(db()) ); return void_result(); @@ -429,9 +432,12 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope a.feeds[o.publisher] = make_pair(d.head_block_time(), o.feed); a.update_median_feeds(d.head_block_time()); }); + d.modify(base, [&d](asset_object& a) { + a.options.core_exchange_rate = a.bitasset_data(d).current_feed.core_exchange_rate; + }); /// TODO: optimization: only do this if the median feed actually changed, otherwise there is no point - db().check_call_orders( base ); + db().check_call_orders(base); return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 8890313d..51353ed8 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -219,16 +219,24 @@ void database::clear_expired_orders() void database::update_expired_feeds() { - auto& asset_idx = get_index_type(); - for( const asset_bitasset_data_object* b : asset_idx ) - if( b->feed_is_expired(head_block_time()) ) + auto& asset_idx = get_index_type().indices(); + for( const asset_object& a : asset_idx ) + { + if( !a.is_market_issued() ) + continue; + + const asset_bitasset_data_object& b = a.bitasset_data(*this); + if( b.feed_is_expired(head_block_time()) ) { - modify(*b, [this](asset_bitasset_data_object& a) { + modify(b, [this](asset_bitasset_data_object& a) { a.update_median_feeds(head_block_time()); }); - - check_call_orders( b->current_feed.settlement_price.base.asset_id(*this) ); + modify(a, [&b](asset_object& a) { + a.options.core_exchange_rate = b.current_feed.core_exchange_rate; + }); + check_call_orders(b.current_feed.settlement_price.base.asset_id(*this)); } + } } void database::update_withdraw_permissions() diff --git a/libraries/chain/include/graphene/chain/asset.hpp b/libraries/chain/include/graphene/chain/asset.hpp index 697099a8..af9d1719 100644 --- a/libraries/chain/include/graphene/chain/asset.hpp +++ b/libraries/chain/include/graphene/chain/asset.hpp @@ -141,10 +141,13 @@ namespace graphene { namespace chain { */ ///@{ /** - * Forced settlements will evaluate using this price, defined as BITASSET / COLLATERAL + * Forced settlements will evaluate using this price, defined as BITASSET / COLLATERAL */ price settlement_price; + /// Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fees) + price core_exchange_rate; + /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */ uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO; @@ -154,14 +157,14 @@ namespace graphene { namespace chain { /** * When updating a call order the following condition must be maintained: * - * debt * maintenance_price() < collateral + * debt * maintenance_price() < collateral * debt * settlement_price < debt * maintenance - * debt * maintenance_price() < debt * max_short_squeeze_price() + * debt * maintenance_price() < debt * max_short_squeeze_price() price maintenance_price()const; */ - /** When selling collateral to pay off debt, the least amount of debt to receive should be - * min_usd = max_short_squeeze_price() * collateral + /** When selling collateral to pay off debt, the least amount of debt to receive should be + * min_usd = max_short_squeeze_price() * collateral * * This is provided to ensure that a black swan cannot be trigged due to poor liquidity alone, it * must be confirmed by having the max_short_squeeze_price() move below the black swan price. @@ -183,7 +186,8 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::asset, (amount)(asset_id) ) FC_REFLECT( graphene::chain::price, (base)(quote) ) -#define GRAPHENE_PRICE_FEED_FIELDS (settlement_price)(maintenance_collateral_ratio)(maximum_short_squeeze_ratio) +#define GRAPHENE_PRICE_FEED_FIELDS (settlement_price)(maintenance_collateral_ratio)(maximum_short_squeeze_ratio) \ + (core_exchange_rate) FC_REFLECT( graphene::chain::price_feed, GRAPHENE_PRICE_FEED_FIELDS ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 84c45313..458a242a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -400,7 +400,7 @@ const asset_object& database_fixture::create_bitasset( creator.fee = asset(); creator.symbol = name; creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; - creator.precision = 2; + creator.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; creator.common_options.market_fee_percent = market_fee_percent; creator.common_options.issuer_permissions = flags; creator.common_options.flags = flags & ~global_settle; From a626b0685f2032e3c388fea8b0495ce0bca905f2 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Jun 2015 10:57:38 -0400 Subject: [PATCH 141/354] Fix failing prediction market test --- tests/common/database_fixture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 458a242a..ee59c4a1 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -400,7 +400,7 @@ const asset_object& database_fixture::create_bitasset( creator.fee = asset(); creator.symbol = name; creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; - creator.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; + creator.precision = 2; creator.common_options.market_fee_percent = market_fee_percent; creator.common_options.issuer_permissions = flags; creator.common_options.flags = flags & ~global_settle; @@ -425,7 +425,7 @@ const asset_object& database_fixture::create_prediction_market( creator.fee = asset(); creator.symbol = name; creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; - creator.precision = 2; + creator.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; creator.common_options.market_fee_percent = market_fee_percent; creator.common_options.issuer_permissions = flags | global_settle; creator.common_options.flags = flags & ~global_settle; From 8d458969004c9ae107adeb8cdd1945a649f0da09 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Thu, 25 Jun 2015 12:33:21 -0400 Subject: [PATCH 142/354] Update submodules --- docs | 2 +- libraries/fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs b/docs index 71ed2984..529b6968 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 71ed2984b71d57cab13cdf12074cff150edc1d3d +Subproject commit 529b696844c496491498f836100bf6be7ca31bb1 diff --git a/libraries/fc b/libraries/fc index c09035db..fe7eed6b 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit c09035dba0cdab7fcb2c11bf81aaeaffaa981f66 +Subproject commit fe7eed6bebac28313e4e7350eb77f8da27f6a427 From 8b010b1f99622aae6ca7df19b14ca8f94038fa1b Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Fri, 26 Jun 2015 11:40:49 -0400 Subject: [PATCH 143/354] Fix compiler warning --- libraries/db/include/graphene/db/index.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index ae0e61b5..1dc6218d 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -167,7 +167,7 @@ namespace graphene { namespace db { const T* result = dynamic_cast(item.get()); if( result != nullptr ) return *result; } - assert( !"invalid index type" ); + FC_ASSERT( !"invalid index type" ); } protected: From dad1ca3beeed6186585ab509d15c86cd630157ec Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Jun 2015 15:11:41 -0400 Subject: [PATCH 144/354] Refactor: Move limit order execution to database This logic was previously located in limit_order_create_evaluator, but other code may need it in the future, so it should be made available at the database level. --- libraries/chain/asset_evaluator.cpp | 38 ++++++- libraries/chain/asset_object.cpp | 2 + libraries/chain/db_market.cpp | 107 +++++++++--------- .../include/graphene/chain/asset_object.hpp | 1 + .../chain/include/graphene/chain/config.hpp | 5 +- .../chain/include/graphene/chain/database.hpp | 11 +- .../chain/include/graphene/chain/types.hpp | 4 +- libraries/chain/limit_order_evaluator.cpp | 75 +++--------- 8 files changed, 118 insertions(+), 125 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 536450cd..4e78b808 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -54,7 +54,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o const asset_object& backing_backing = backing_bitasset_data.options.short_backing_asset(d); FC_ASSERT( !backing_backing.is_market_issued(), "May not create a bitasset backed by a bitasset backed by a bitasset." ); - } + FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing_backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } else + FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); FC_ASSERT( op.bitasset_options->feed_lifetime_sec > chain_parameters.block_interval && op.bitasset_options->force_settlement_delay_sec > chain_parameters.block_interval ); } @@ -192,13 +196,28 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) { try { database& d = db(); - if( o.new_issuer ) FC_ASSERT(d.find_object(*o.new_issuer)); - const asset_object& a = o.asset_to_update(d); auto a_copy = a; a_copy.options = o.new_options; a_copy.validate(); + if( o.new_issuer ) + { + FC_ASSERT(d.find_object(*o.new_issuer)); + if( a.is_market_issued() && *o.new_issuer == GRAPHENE_COMMITTEE_ACCOUNT ) + { + const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d); + if( backing.is_market_issued() ) + { + const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d); + FC_ASSERT( backing_backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } else + FC_ASSERT( backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } + } + //There must be no bits set in o.permissions which are unset in a.issuer_permissions. FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions), "Cannot reinstate previously revoked issuer permissions on an asset."); @@ -257,6 +276,19 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita { FC_ASSERT(a.dynamic_asset_data_id(d).current_supply == 0); FC_ASSERT(d.find_object(o.new_options.short_backing_asset)); + + if( a.issuer == GRAPHENE_COMMITTEE_ACCOUNT ) + { + const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d); + if( backing.is_market_issued() ) + { + const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d); + FC_ASSERT( backing_backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } else + FC_ASSERT( backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } } bitasset_to_update = &b; diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index f5bab206..a1694a68 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -16,6 +16,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include +#include #include diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 3822566f..28e7fa1c 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -109,7 +109,49 @@ void database::cancel_order( const limit_order_object& order, bool create_virtua // TODO: create a virtual cancel operation } - remove( order ); + remove(order); +} + +bool database::apply_order(const limit_order_object& new_order_object, bool allow_black_swan) +{ + auto order_id = new_order_object.id; + const asset_object& sell_asset = get(new_order_object.amount_for_sale().asset_id); + const asset_object& receive_asset = get(new_order_object.amount_to_receive().asset_id); + + // Possible optimization: We only need to check calls if both are true: + // - The new order is at the front of the book + // - The new order is below the call limit price + bool called_some = check_call_orders(sell_asset, allow_black_swan); + called_some |= check_call_orders(receive_asset, allow_black_swan); + if( called_some && !find_object(order_id) ) // then we were filled by call order + return true; + + const auto& limit_price_idx = get_index_type().indices().get(); + + // TODO: it should be possible to simply check the NEXT/PREV iterator after new_order_object to + // determine whether or not this order has "changed the book" in a way that requires us to + // check orders. For now I just lookup the lower bound and check for equality... this is log(n) vs + // constant time check. Potential optimization. + + auto max_price = ~new_order_object.sell_price; + auto limit_itr = limit_price_idx.lower_bound(max_price.max()); + auto limit_end = limit_price_idx.upper_bound(max_price); + + bool finished = false; + while( !finished && limit_itr != limit_end ) + { + auto old_limit_itr = limit_itr; + ++limit_itr; + // match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop. + finished = (match(new_order_object, *old_limit_itr, old_limit_itr->sell_price) != 2); + } + + //Possible optimization: only check calls if the new order completely filled some old order + //Do I need to check both assets? + check_call_orders(sell_asset, allow_black_swan); + check_call_orders(receive_asset, allow_black_swan); + + return find_object(order_id) == nullptr; } /** @@ -306,7 +348,7 @@ bool database::fill_order(const force_settlement_object& settle, const asset& pa /** * Starting with the least collateralized orders, fill them if their - * call price is above the max(lowest bid,call_limit). + * call price is above the max(lowest bid,call_limit). * * This method will return true if it filled a short or limit * @@ -316,7 +358,7 @@ 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) { try { if( !mia.is_market_issued() ) return false; const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this); @@ -333,42 +375,13 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); // stop when limit orders are selling too little USD for too much CORE auto min_price = bitasset.current_feed.max_short_squeeze_price(); - /* - // edump((bitasset.current_feed)); - edump((min_price.to_real())(min_price)); - edump((max_price.to_real())(max_price)); - //auto min_price = price::min( mia.id, bitasset.options.short_backing_asset ); - idump((bitasset.current_feed.settlement_price)(bitasset.current_feed.settlement_price.to_real())); - { - for( const auto& order : limit_price_index ) - wdump((order)(order.sell_price.to_real())); - - for( const auto& call : call_price_index ) - idump((call)(call.call_price.to_real())); - - // limit pirce index is sorted from highest price to lowest price. - //auto limit_itr = limit_price_index.lower_bound( price::max( mia.id, bitasset.options.short_backing_asset ) ); - wdump((max_price)(max_price.to_real())); - wdump((min_price)(min_price.to_real())); - } - */ assert( max_price.base.asset_id == min_price.base.asset_id ); - // wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); - auto limit_end = limit_price_index.upper_bound( min_price ); + auto limit_end = limit_price_index.upper_bound( min_price ); - /* - if( limit_itr != limit_price_index.end() ) - wdump((*limit_itr)(limit_itr->sell_price.to_real())); - if( limit_end != limit_price_index.end() ) - wdump((*limit_end)(limit_end->sell_price.to_real())); -*/ - - if( limit_itr == limit_end ) - { -// wlog( "no orders available to fill margin calls" ); + if( limit_itr == limit_end ) { return false; } @@ -389,32 +402,26 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa usd_for_sale = limit_itr->amount_for_sale(); } else return filled_limit; -// wdump((match_price)); -// edump((usd_for_sale)); match_price.validate(); -// wdump((match_price)(~call_itr->call_price) ); if( match_price > ~call_itr->call_price ) { return filled_limit; } auto usd_to_buy = call_itr->get_debt(); -// edump((usd_to_buy)); if( usd_to_buy * match_price > call_itr->get_collateral() ) { FC_ASSERT( enable_black_swan ); - //elog( "black swan, we do not have enough collateral to cover at this price" ); - globally_settle_asset( mia, call_itr->get_debt() / call_itr->get_collateral() ); + globally_settle_asset(mia, call_itr->get_debt() / call_itr->get_collateral()); return true; } asset call_pays, call_receives, order_pays, order_receives; if( usd_to_buy >= usd_for_sale ) { // fill order - //ilog( "filling all of limit order" ); call_receives = usd_for_sale; order_receives = usd_for_sale * match_price; call_pays = order_receives; @@ -422,9 +429,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa filled_limit = true; filled_call = (usd_to_buy == usd_for_sale); - } - else // fill call - { + } else { // fill call call_receives = usd_to_buy; order_receives = usd_to_buy * match_price; call_pays = order_receives; @@ -435,10 +440,10 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa auto old_call_itr = call_itr; if( filled_call ) ++call_itr; - fill_order( *old_call_itr, call_pays, call_receives ); + fill_order(*old_call_itr, call_pays, call_receives); auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr; - fill_order( *old_limit_itr, order_pays, order_receives ); + fill_order(*old_limit_itr, order_pays, order_receives); } // whlie call_itr != call_end return filled_limit; @@ -454,16 +459,6 @@ void database::pay_order( const account_object& receiver, const asset& receives, adjust_balance(receiver.get_id(), receives); } -/** - * For Market Issued assets Managed by Delegates, any fees collected in the MIA need - * to be sold and converted into CORE by accepting the best offer on the table. - */ -bool database::convert_fees( const asset_object& mia ) -{ - if( mia.issuer != account_id_type() ) return false; - return false; -} - asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount ) { assert( trade_asset.id == trade_amount.asset_id ); diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 7f81e274..ce15b0ba 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -33,6 +33,7 @@ namespace graphene { namespace chain { class account_object; + class database; using namespace graphene::db; /** diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 75da2610..dc875d66 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -64,8 +64,8 @@ /** * These ratios are fixed point numbers with a denominator of GRAPHENE_COLLATERAL_RATIO_DENOM, the - * minimum maitenance collateral is therefore 1.001x and the default - * maintenance ratio is 1.75x + * minimum maitenance collateral is therefore 1.001x and the default + * maintenance ratio is 1.75x */ ///@{ #define GRAPHENE_COLLATERAL_RATIO_DENOM 1000 @@ -92,6 +92,7 @@ #define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT) #define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000) #define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE 1 +#define GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD GRAPHENE_BLOCKCHAIN_PRECISION * 100; #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL #define GRAPHENE_MAX_WORKER_NAME_LENGTH 63 diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 2d340c00..9e003fe6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -348,6 +348,16 @@ namespace graphene { namespace chain { void cancel_order(const force_settlement_object& order, bool create_virtual_op = true); void cancel_order(const limit_order_object& order, bool create_virtual_op = true); + /** + * @brief Process a new limit order through the markets + * @param order The new order to process + * @return true if order was completely filled; false otherwise + * + * This function takes a new limit order, and runs the markets attempting to match it with existing orders + * already on the books. + */ + bool apply_order(const limit_order_object& new_order_object, bool allow_black_swan = true); + /** * Matches the two orders, * @@ -381,7 +391,6 @@ namespace graphene { namespace chain { // helpers to fill_order void pay_order( const account_object& receiver, const asset& receives, const asset& pays ); - bool convert_fees( const asset_object& mia ); asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); asset pay_market_fees( const asset_object& recv_asset, const asset& receives ); diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index e537a9ba..13669f77 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -355,7 +355,7 @@ namespace graphene { namespace chain { uint64_t account_len5_fee = 5*UINT64_C(1000000000); ///< about $100 uint64_t account_len4_fee = 5*UINT64_C(2000000000); ///< about $200 uint64_t account_len3_fee = 5*3000000000; ///< about $300 - uint64_t account_len2_fee = 5*4000000000; ///< about $400 + uint64_t account_len2_fee = 5*4000000000; ///< about $400 uint32_t asset_create_fee = 5ll*500000000; ///< about $35 for LTM, the cost to register the cheapest asset uint32_t asset_update_fee = 150000; ///< the cost to modify a registered asset uint32_t asset_issue_fee = 700000; ///< the cost to print a UIA and send it to an account @@ -456,6 +456,7 @@ namespace graphene { namespace chain { share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block) share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day) uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number + share_type fee_liquidation_threshold = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated void validate()const { @@ -614,6 +615,7 @@ FC_REFLECT( graphene::chain::chain_parameters, (witness_pay_per_block) (worker_budget_per_day) (max_predicate_opcode) + (fee_liquidation_threshold) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/libraries/chain/limit_order_evaluator.cpp b/libraries/chain/limit_order_evaluator.cpp index 7560357c..49f7713e 100644 --- a/libraries/chain/limit_order_evaluator.cpp +++ b/libraries/chain/limit_order_evaluator.cpp @@ -21,7 +21,7 @@ #include namespace graphene { namespace chain { -void_result limit_order_create_evaluator::do_evaluate( const limit_order_create_operation& op ) +void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op) { try { database& d = db(); @@ -32,9 +32,9 @@ void_result limit_order_create_evaluator::do_evaluate( const limit_order_create_ _receive_asset = &op.min_to_receive.asset_id(d); if( _sell_asset->options.whitelist_markets.size() ) - FC_ASSERT( _sell_asset->options.whitelist_markets.find( _receive_asset->id ) != _sell_asset->options.whitelist_markets.end() ); + FC_ASSERT( _sell_asset->options.whitelist_markets.find(_receive_asset->id) != _sell_asset->options.whitelist_markets.end() ); if( _sell_asset->options.blacklist_markets.size() ) - FC_ASSERT( _sell_asset->options.blacklist_markets.find( _receive_asset->id ) == _sell_asset->options.blacklist_markets.end() ); + FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() ); if( _sell_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset ) ); if( _receive_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset ) ); @@ -45,10 +45,10 @@ void_result limit_order_create_evaluator::do_evaluate( const limit_order_create_ return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } -object_id_type limit_order_create_evaluator::do_apply( const limit_order_create_operation& op ) +object_id_type limit_order_create_evaluator::do_apply(const limit_order_create_operation& op) { try { const auto& seller_stats = _seller->statistics(db()); - db().modify( seller_stats, [&]( account_statistics_object& bal ){ + db().modify(seller_stats, [&](account_statistics_object& bal) { if( op.amount_to_sell.asset_id == asset_id_type() ) { bal.total_core_in_orders += op.amount_to_sell.amount; @@ -57,70 +57,21 @@ object_id_type limit_order_create_evaluator::do_apply( const limit_order_create_ db().adjust_balance(op.seller, -op.amount_to_sell); - const auto& new_order_object = db().create( [&]( limit_order_object& obj ){ + const auto& new_order_object = db().create([&](limit_order_object& obj){ obj.seller = _seller->id; obj.for_sale = op.amount_to_sell.amount; obj.sell_price = op.get_price(); obj.expiration = op.expiration; }); - limit_order_id_type result = new_order_object.id; // save this because we may remove the object by filling it + limit_order_id_type order_id = new_order_object.id; // save this because we may remove the object by filling it + bool filled = db().apply_order(new_order_object); - // Possible optimization: We only need to check calls if both are true: - // - The new order is at the front of the book - // - The new order is below the call limit price - bool called_some = db().check_call_orders(*_sell_asset); - called_some |= db().check_call_orders(*_receive_asset); - if( called_some && !db().find(result) ) // then we were filled by call order - return result; + FC_ASSERT( !op.fill_or_kill || filled ); - const auto& limit_order_idx = db().get_index_type(); - const auto& limit_price_idx = limit_order_idx.indices().get(); - - // TODO: it should be possible to simply check the NEXT/PREV iterator after new_order_object to - // determine whether or not this order has "changed the book" in a way that requires us to - // check orders. For now I just lookup the lower bound and check for equality... this is log(n) vs - // constant time check. Potential optimization. - - auto max_price = ~op.get_price(); //op.min_to_receive / op.amount_to_sell; - auto limit_itr = limit_price_idx.lower_bound( max_price.max() ); - auto limit_end = limit_price_idx.upper_bound( max_price ); - - for( auto tmp = limit_itr; tmp != limit_end; ++tmp ) - { - assert( tmp != limit_price_idx.end() ); - } - - bool filled = false; - //if( new_order_object.amount_to_receive().asset_id(db()).is_market_issued() ) - if( _receive_asset->is_market_issued() ) - { // then we may also match against shorts - if( _receive_asset->bitasset_data(db()).options.short_backing_asset == asset_id_type() ) - { - bool converted_some = db().convert_fees( *_receive_asset ); - // just incase the new order was completely filled from fees - if( converted_some && !db().find(result) ) // then we were filled by call order - return result; - } - } - - while( !filled && limit_itr != limit_end ) - { - auto old_limit_itr = limit_itr; - ++limit_itr; - filled = (db().match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 ); - } - - //Possible optimization: only check calls if the new order completely filled some old order - //Do I need to check both assets? - db().check_call_orders(*_sell_asset); - db().check_call_orders(*_receive_asset); - - FC_ASSERT( !op.fill_or_kill || db().find_object(result) == nullptr ); - - return result; + return order_id; } FC_CAPTURE_AND_RETHROW( (op) ) } -void_result limit_order_cancel_evaluator::do_evaluate( const limit_order_cancel_operation& o ) +void_result limit_order_cancel_evaluator::do_evaluate(const limit_order_cancel_operation& o) { try { database& d = db(); @@ -130,7 +81,7 @@ void_result limit_order_cancel_evaluator::do_evaluate( const limit_order_cancel_ return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } -asset limit_order_cancel_evaluator::do_apply( const limit_order_cancel_operation& o ) +asset limit_order_cancel_evaluator::do_apply(const limit_order_cancel_operation& o) { try { database& d = db(); @@ -138,7 +89,7 @@ asset limit_order_cancel_evaluator::do_apply( const limit_order_cancel_operation auto quote_asset = _order->sell_price.quote.asset_id; auto refunded = _order->amount_for_sale(); - db().cancel_order( *_order, false /* don't create a virtual op*/ ); + db().cancel_order(*_order, false /* don't create a virtual op*/); // Possible optimization: order can be called by canceling a limit order iff the canceled order was at the top of the book. // Do I need to check calls in both assets? From dba009da4dc79f12da6c8c91cc4cfea1f652643f Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Jun 2015 15:46:16 -0400 Subject: [PATCH 145/354] Resolve #83: core_exchange_rate in feed updates on maintenance --- libraries/chain/asset_evaluator.cpp | 6 ------ libraries/chain/asset_object.cpp | 1 - libraries/chain/db_update.cpp | 6 ++++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 4e78b808..f6f62629 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -345,9 +345,6 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f a.feeds[*itr]; a.update_median_feeds(db().head_block_time()); }); - db().modify(o.asset_to_update(db()), [this](asset_object& a) { - a.options.core_exchange_rate = bitasset_to_update->current_feed.core_exchange_rate; - }); db().check_call_orders( o.asset_to_update(db()) ); return void_result(); @@ -464,9 +461,6 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope a.feeds[o.publisher] = make_pair(d.head_block_time(), o.feed); a.update_median_feeds(d.head_block_time()); }); - d.modify(base, [&d](asset_object& a) { - a.options.core_exchange_rate = a.bitasset_data(d).current_feed.core_exchange_rate; - }); /// TODO: optimization: only do this if the median feed actually changed, otherwise there is no point db().check_call_orders(base); diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index a1694a68..d72da15b 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -115,7 +115,6 @@ void asset_object::bitasset_options::validate() const FC_ASSERT(maximum_force_settlement_volume <= GRAPHENE_100_PERCENT); } - asset asset_object::amount_from_string(string amount_string) const { try { bool negative_found = false; diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 51353ed8..a51c3a99 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -231,11 +231,13 @@ void database::update_expired_feeds() modify(b, [this](asset_bitasset_data_object& a) { a.update_median_feeds(head_block_time()); }); + check_call_orders(b.current_feed.settlement_price.base.asset_id(*this)); + } + 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; }); - check_call_orders(b.current_feed.settlement_price.base.asset_id(*this)); - } } } From baea85ea3a64e74e53f7657bada919a807c93d45 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Fri, 26 Jun 2015 16:43:39 -0400 Subject: [PATCH 146/354] Resolve #97: Add exponentially growing fees on account creation It scales up aggressively right now; this can be adjusted later. I just wanted to demonstrate that it works. --- libraries/chain/account_evaluator.cpp | 11 +++++++++ libraries/chain/db_maint.cpp | 16 +++++++++---- .../chain/include/graphene/chain/config.hpp | 2 ++ .../graphene/chain/global_property_object.hpp | 3 ++- .../chain/include/graphene/chain/types.hpp | 6 ++++- tests/tests/block_tests.cpp | 23 +++++++++++++++++++ 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 03ed332f..a0d88024 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -108,6 +108,17 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.options.memo_key = get_relative_id(obj.options.memo_key); }); + const auto& global_properties = db().get_global_properties(); + 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 ) + db().modify(global_properties, [&dynamic_properties](global_property_object& p) { + p.parameters.current_fees.account_create_fee <<= p.parameters.account_fee_scale_bitshifts; + }); + return new_acnt_object.id; } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 11bc6770..3840b705 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -429,11 +429,18 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g update_active_witnesses(); update_active_delegates(); - if( gpo.pending_parameters ) - modify(gpo, [](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.account_create_fee >>= p.parameters.account_fee_scale_bitshifts * + (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale); + + if( p.pending_parameters ) + { p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); - }); + } + }); auto new_block_interval = global_props.parameters.block_interval; @@ -445,7 +452,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if( !r ) { _pending_block.timestamp -= r; - assert( (_pending_block.timestamp.sec_since_epoch() % new_block_interval) == 0 ); + assert( (_pending_block.timestamp.sec_since_epoch() % new_block_interval) == 0 ); } auto next_maintenance_time = get(dynamic_global_property_id_type()).next_maintenance_time; @@ -465,6 +472,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) { d.next_maintenance_time = next_maintenance_time; + d.accounts_registered_this_interval = 0; }); // Reset all BitAsset force settlement volumes to zero diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index dc875d66..b1e181b2 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -93,6 +93,8 @@ #define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000) #define GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE 1 #define GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD GRAPHENE_BLOCKCHAIN_PRECISION * 100; +#define GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE 1000 +#define GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS 4 #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL #define GRAPHENE_MAX_WORKER_NAME_LENGTH 63 diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index c69a9169..ee7e53c5 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -76,10 +76,10 @@ namespace graphene { namespace chain { time_point_sec next_maintenance_time; time_point_sec last_budget_time; share_type witness_budget; + uint32_t accounts_registered_this_interval; }; }} - FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object), (random) (head_block_number) @@ -88,6 +88,7 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene:: (current_witness) (next_maintenance_time) (witness_budget) + (accounts_registered_this_interval) ) FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::object), diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 13669f77..ab99b20b 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -345,7 +345,7 @@ namespace graphene { namespace chain { } uint32_t key_create_fee = 270300; ///< the cost to register a public key with the blockchain - uint32_t account_create_fee = 666666; ///< the cost to register the cheapest non-free account + uint64_t account_create_fee = 666666; ///< the cost to register the cheapest non-free account uint32_t account_update_fee = 150000; ///< the cost to update an existing account uint32_t account_transfer_fee = 300000; ///< the cost to transfer an account to a new owner uint32_t account_whitelist_fee = 300000; ///< the fee to whitelist an account @@ -457,6 +457,8 @@ namespace graphene { namespace chain { share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day) uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number share_type fee_liquidation_threshold = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated + uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings + uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling void validate()const { @@ -616,6 +618,8 @@ FC_REFLECT( graphene::chain::chain_parameters, (worker_budget_per_day) (max_predicate_opcode) (fee_liquidation_threshold) + (accounts_per_fee_scale) + (account_fee_scale_bitshifts) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index eb4adefd..f838dc34 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -839,4 +839,27 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture ) }); } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE( account_create_fee_scaling, database_fixture ) +{ try { + auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale; + enable_fees(1); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 1); + for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i ) + create_account("shill" + fc::to_string(i)); + generate_block(); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 16); + for( int i = 0; i < accounts_per_scale; ++i ) + create_account("moreshills" + fc::to_string(i)); + generate_block(); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 256); + for( int i = 0; i < accounts_per_scale; ++i ) + create_account("moarshills" + fc::to_string(i)); + generate_block(); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 4096); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees.account_create_fee, 1); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From 06b836f34410201fbfde7f292adc94b2f2983997 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Fri, 26 Jun 2015 17:47:03 -0400 Subject: [PATCH 147/354] Add doxygen docs for most cli wallet functions --- .gitignore | 4 + libraries/wallet/CMakeLists.txt | 27 +- libraries/wallet/Doxyfile.in | 2362 +++++++++++++++++ .../wallet/api_documentation_standin.cpp | 68 + .../wallet/generate_api_documentation.pl | 178 ++ .../graphene/wallet/api_documentation.hpp | 71 + .../wallet/include/graphene/wallet/wallet.hpp | 636 ++++- libraries/wallet/wallet.cpp | 283 +- 8 files changed, 3458 insertions(+), 171 deletions(-) create mode 100644 libraries/wallet/Doxyfile.in create mode 100644 libraries/wallet/api_documentation_standin.cpp create mode 100755 libraries/wallet/generate_api_documentation.pl create mode 100644 libraries/wallet/include/graphene/wallet/api_documentation.hpp diff --git a/.gitignore b/.gitignore index e27fbeef..c57368a7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ compile_commands.json libraries/utilities/git_revision.cpp +libraries/wallet/Doxyfile +libraries/wallet/api_documentation.cpp +libraries/wallet/doxygen + programs/cli_wallet/cli_wallet programs/js_operation_serializer/js_operation_serializer programs/witness_node/witness_node diff --git a/libraries/wallet/CMakeLists.txt b/libraries/wallet/CMakeLists.txt index 4e1793f2..45f8581b 100644 --- a/libraries/wallet/CMakeLists.txt +++ b/libraries/wallet/CMakeLists.txt @@ -1,5 +1,30 @@ file(GLOB HEADERS "include/graphene/wallet/*.hpp") -add_library( graphene_wallet cache.cpp wallet.cpp ${HEADERS} ) + +find_package( Perl ) +find_package( Doxygen ) + +if( PERL_FOUND AND DOXYGEN_FOUND ) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/doxygen/perlmod/DoxyDocs.pm + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${DOXYGEN_EXECUTABLE} + DEPENDS Doxyfile include/graphene/wallet/wallet.hpp ) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new + + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp + COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp.new + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate_api_documentation.pl ${CMAKE_CURRENT_BINARY_DIR}/doxygen/perlmod/DoxyDocs.pm ) + +else( PERL_FOUND AND DOXYGEN_FOUND ) + # no perl and doxygen, generate the best docs we can at runtime from reflection + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/api_documentation_standin.cpp ${CMAKE_CURRENT_BINARY_DIR}/api_documentation.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/api_documentation_standin.cpp ) +endif( PERL_FOUND AND DOXYGEN_FOUND ) + + +add_library( graphene_wallet cache.cpp 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_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/wallet/Doxyfile.in b/libraries/wallet/Doxyfile.in new file mode 100644 index 00000000..d8357322 --- /dev/null +++ b/libraries/wallet/Doxyfile.in @@ -0,0 +1,2362 @@ +# Doxyfile 1.8.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Graphene Wallet API" + +# 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 +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ${CMAKE_CURRENT_BINARY_DIR}/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wallet.hpp + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = NO + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /