/*
 * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <fc/uint128.hpp>

#include <graphene/chain/hardfork.hpp>

#include <graphene/chain/fba_accumulator_id.hpp>

#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/exceptions.hpp>

#include <boost/test/unit_test.hpp>

#include "../common/database_fixture.hpp"

using namespace graphene::chain;
using namespace graphene::chain::test;

BOOST_FIXTURE_TEST_SUITE( fee_tests, database_fixture )

BOOST_AUTO_TEST_CASE( nonzero_fee_test )
{
   try
   {
      ACTORS((alice)(bob));

      const share_type prec = asset::scaled_precision( asset_id_type()(db).precision );

      // Return number of core shares (times precision)
      auto _core = [&]( int64_t x ) -> asset
      {  return asset( x*prec );    };

      transfer( committee_account, alice_id, _core(1000000) );

      // make sure the database requires our fee to be nonzero
      enable_fees();

      signed_transaction tx;
      transfer_operation xfer_op;
      xfer_op.from = alice_id;
      xfer_op.to = bob_id;
      xfer_op.amount = _core(1000);
      xfer_op.fee = _core(0);
      tx.operations.push_back( xfer_op );
      set_expiration( db, tx );
      sign( tx, alice_private_key );
      GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), insufficient_fee );
   }
   catch( const fc::exception& e )
   {
      edump((e.to_detail_string()));
      throw;
   }
}

// assertion if "No asset in the trade is CORE." in market_evaluator.cpp
#if 0
BOOST_AUTO_TEST_CASE(asset_claim_fees_test)
{
   try
   {
      ACTORS((alice)(bob)(izzy)(jill));
      // Izzy issues asset to Alice
      // Jill issues asset to Bob
      // Alice and Bob trade in the market and pay fees
      // Verify that Izzy and Jill can claim the fees

//       const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision );

//       // Return number of core shares (times precision)
//       auto _core = [&]( int64_t x ) -> asset
//       {  return asset( x*core_prec );    };

//       transfer( committee_account, alice_id, _core(1000000) );
//       transfer( committee_account,   bob_id, _core(1000000) );
//       transfer( committee_account,  izzy_id, _core(1000000) );
//       transfer( committee_account,  jill_id, _core(1000000) );

//       asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id,   GRAPHENE_1_PERCENT, charge_market_fee ).id;
//       asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, 2*GRAPHENE_1_PERCENT, charge_market_fee ).id;

//       const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision );
//       const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision );

//       auto _izzy = [&]( int64_t x ) -> asset
//       {   return asset( x*izzy_prec, izzycoin_id );   };
//       auto _jill = [&]( int64_t x ) -> asset
//       {   return asset( x*jill_prec, jillcoin_id );   };

//       update_feed_producers( izzycoin_id(db), { izzy_id } );
//       update_feed_producers( jillcoin_id(db), { jill_id } );

//       const asset izzy_satoshi = asset(1, izzycoin_id);
//       const asset jill_satoshi = asset(1, jillcoin_id);

//       // Izzycoin is worth 100 BTS
//       price_feed feed;
//       feed.settlement_price = price( _izzy(1), _core(100) );
//       feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
//       feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
//       publish_feed( izzycoin_id(db), izzy, feed );

//       // Jillcoin is worth 30 BTS
//       feed.settlement_price = price( _jill(1), _core(30) );
//       feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
//       feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
//       publish_feed( jillcoin_id(db), jill, feed );

//       enable_fees();

//       // Alice and Bob create some coins
//       borrow( alice_id, _izzy( 200), _core( 60000) );
//       borrow(   bob_id, _jill(2000), _core(180000) );

//       // Alice and Bob place orders which match
//       create_sell_order( alice_id, _izzy(100), _jill(300) );   // Alice is willing to sell her Izzy's for 3 Jill
//       create_sell_order(   bob_id, _jill(700), _izzy(200) );   // Bob is buying up to 200 Izzy's for up to 3.5 Jill

//       // 100 Izzys and 300 Jills are matched, so the fees should be
//       //   1 Izzy (1%) and 6 Jill (2%).

//       auto claim_fees = [&]( account_id_type issuer, asset amount_to_claim )
//       {
//          asset_claim_fees_operation claim_op;
//          claim_op.issuer = issuer;
//          claim_op.amount_to_claim = amount_to_claim;
//          signed_transaction tx;
//          tx.operations.push_back( claim_op );
//          db.current_fee_schedule().set_fee( tx.operations.back() );
//          set_expiration( db, tx );
//          fc::ecc::private_key   my_pk = (issuer == izzy_id) ? izzy_private_key : jill_private_key;
//          fc::ecc::private_key your_pk = (issuer == izzy_id) ? jill_private_key : izzy_private_key;
//          sign( tx, your_pk );
//          GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );
//          tx.signatures.clear();
//          sign( tx, my_pk );
//          PUSH_TX( db, tx );
//       };

//       {
//          const asset_object& izzycoin = izzycoin_id(db);
//          const asset_object& jillcoin = jillcoin_id(db);

//          //wdump( (izzycoin)(izzycoin.dynamic_asset_data_id(db))((*izzycoin.bitasset_data_id)(db)) );
//          //wdump( (jillcoin)(jillcoin.dynamic_asset_data_id(db))((*jillcoin.bitasset_data_id)(db)) );

//          // check the correct amount of fees has been awarded
//          BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(1).amount );
//          BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(6).amount );

//       }

//       if( db.head_block_time() <= HARDFORK_413_TIME )
//       {
//          // can't claim before hardfork
//          GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) ), fc::exception );
//          generate_blocks( HARDFORK_413_TIME );
//          while( db.head_block_time() <= HARDFORK_413_TIME )
//          {
//             generate_block();
//          }
//       }

//       {
//          const asset_object& izzycoin = izzycoin_id(db);
//          const asset_object& jillcoin = jillcoin_id(db);

//          // can't claim more than balance
//          GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) + izzy_satoshi ), fc::exception );
//          GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(6) + jill_satoshi ), fc::exception );

//          // can't claim asset that doesn't belong to you
//          GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, izzy_satoshi ), fc::exception );
//          GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, jill_satoshi ), fc::exception );

//          // can claim asset in one go
//          claim_fees( izzy_id, _izzy(1) );
//          GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, izzy_satoshi ), fc::exception );
//          BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(0).amount );

         // can claim in multiple goes
         claim_fees( jill_id, _jill(4) );
         BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(2).amount );
         GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(2) + jill_satoshi ), fc::exception );
         claim_fees( jill_id, _jill(2) );
         BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(0).amount );
      }
   }
   FC_LOG_AND_RETHROW()
}
#endif

///////////////////////////////////////////////////////////////
// cashback_test infrastructure                              //
///////////////////////////////////////////////////////////////

#define CHECK_BALANCE( actor_name, amount ) \
   BOOST_CHECK_EQUAL( get_balance( actor_name ## _id, asset_id_type() ), amount )

#define CHECK_VESTED_CASHBACK( actor_name, amount ) \
   BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_vested_fees.value, amount )

#define CHECK_UNVESTED_CASHBACK( actor_name, amount ) \
   BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_fees.value, amount )

#define GET_CASHBACK_BALANCE( account ) \
   ( (account.cashback_vb.valid()) \
   ? account.cashback_balance(db).balance.amount.value \
   : 0 )

#define CHECK_CASHBACK_VBO( actor_name, _amount ) \
   BOOST_CHECK_EQUAL( GET_CASHBACK_BALANCE( actor_name ## _id(db) ), _amount )

#define P100 GRAPHENE_100_PERCENT
#define P1 GRAPHENE_1_PERCENT

uint64_t pct( uint64_t percentage, uint64_t val )
{
   fc::uint128_t x = percentage;
   x *= val;
   x /= GRAPHENE_100_PERCENT;
   return x.to_uint64();
}

uint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t val )
{
   return pct( percentage1, pct( percentage0, val ) );
}

uint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t percentage2, uint64_t val )
{
   return pct( percentage2, pct( percentage1, pct( percentage0, val ) ) );
}

struct actor_audit
{
   int64_t b0 = 0;      // starting balance parameter
   int64_t bal = 0;     // balance should be this
   int64_t ubal = 0;    // unvested balance (in VBO) should be this
   int64_t ucb = 0;     // unvested cashback in account_statistics should be this
   int64_t vcb = 0;     // vested cashback in account_statistics should be this
   int64_t ref_pct = 0; // referrer percentage should be this
};

BOOST_AUTO_TEST_CASE( cashback_test )
{ try {
   /*                        Account Structure used in this test                         *
    *                                                                                    *
    *               /-----------------\       /-------------------\                      *
    *               | life (Lifetime) |       |  rog (Lifetime)   |                      *
    *               \-----------------/       \-------------------/                      *
    *                  | Ref&Reg    | Refers     | Registers  | Registers                *
    *                  |            | 75         | 25         |                          *
    *                  v            v            v            |                          *
    *  /----------------\         /----------------\          |                          *
    *  |  ann (Annual)  |         |  dumy (basic)  |          |                          *
    *  \----------------/         \----------------/          |-------------.            *
    * 80 | Refers      L--------------------------------.     |             |            *
    *    v                     Refers                80 v     v 20          |            *
    *  /----------------\                         /----------------\        |            *
    *  |  scud (basic)  |<------------------------|  stud (basic)  |        |            *
    *  \----------------/ 20   Registers          | (Upgrades to   |        | 5          *
    *                                             |   Lifetime)    |        v            *
    *                                             \----------------/   /--------------\  *
    *                                                         L------->| pleb (Basic) |  *
    *                                                       95 Refers  \--------------/  *
    *                                                                                    *
    * Fee distribution chains (80-20 referral/net split, 50-30 referrer/LTM split)       *
    * life : 80% -> life, 20% -> net                                                     *
    * rog: 80% -> rog, 20% -> net                                                        *
    * ann (before upg): 80% -> life, 20% -> net                                          *
    * ann (after upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% -> net                   *
    * stud (before upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> rog,          *
    *                    20% -> net                                                      *
    * stud (after upg): 80% -> stud, 20% -> net                                          *
    * dumy : 75% * 80% -> life, 25% * 80% -> rog, 20% -> net                             *
    * scud : 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> stud, 20% -> net          *
    * pleb : 95% * 80% -> stud, 5% * 80% -> rog, 20% -> net                              *
    */

   BOOST_TEST_MESSAGE("Creating actors");

   ACTOR(life);
   ACTOR(rog);
   PREP_ACTOR(ann);
   PREP_ACTOR(scud);
   PREP_ACTOR(dumy);
   PREP_ACTOR(stud);
   PREP_ACTOR(pleb);

   account_id_type ann_id, scud_id, dumy_id, stud_id, pleb_id;
   actor_audit alife, arog, aann, ascud, adumy, astud, apleb;

   alife.b0 = 100000000;
   arog.b0 = 100000000;
   aann.b0 = 1000000;
   astud.b0 = 1000000;
   astud.ref_pct = 80 * GRAPHENE_1_PERCENT;
   ascud.ref_pct = 80 * GRAPHENE_1_PERCENT;
   adumy.ref_pct = 75 * GRAPHENE_1_PERCENT;
   apleb.ref_pct = 95 * GRAPHENE_1_PERCENT;

   transfer(account_id_type(), life_id, asset(alife.b0));
   alife.bal += alife.b0;
   transfer(account_id_type(), rog_id, asset(arog.b0));
   arog.bal += arog.b0;
   upgrade_to_lifetime_member(life_id);
   upgrade_to_lifetime_member(rog_id);

   BOOST_TEST_MESSAGE("Enable fees");
   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.options.memo_key = actor_name ## _private_key.get_public_key(); \
      op.active = authority(1, public_key_type(actor_name ## _private_key.get_public_key()), 1); \
      op.owner = op.active; \
      op.fee = fees.calculate_fee(op); \
      trx.operations = {op}; \
      sign( trx,  registrar_name ## _private_key ); \
      actor_name ## _id = PUSH_TX( db, trx ).operation_results.front().get<object_id_type>(); \
      trx.clear(); \
   }
#define CustomAuditActor(actor_name)                                \
   if( actor_name ## _id != account_id_type() )                     \
   {                                                                \
      CHECK_BALANCE( actor_name, a ## actor_name.bal );             \
      CHECK_VESTED_CASHBACK( actor_name, a ## actor_name.vcb );     \
      CHECK_UNVESTED_CASHBACK( actor_name, a ## actor_name.ucb );   \
      CHECK_CASHBACK_VBO( actor_name, a ## actor_name.ubal );       \
   }

#define CustomAudit()                                \
   {                                                 \
      CustomAuditActor( life );                      \
      CustomAuditActor( rog );                       \
      CustomAuditActor( ann );                       \
      CustomAuditActor( stud );                      \
      CustomAuditActor( dumy );                      \
      CustomAuditActor( scud );                      \
      CustomAuditActor( pleb );                      \
   }

   int64_t reg_fee    = fees.get< account_create_operation >().premium_fee;
   int64_t xfer_fee   = fees.get< transfer_operation >().fee;
   int64_t upg_an_fee = fees.get< account_upgrade_operation >().membership_annual_fee;
   int64_t upg_lt_fee = fees.get< account_upgrade_operation >().membership_lifetime_fee;
   // all percentages here are cut from whole pie!
   uint64_t network_pct = 20 * P1;
   uint64_t lt_pct = 375 * P100 / 1000;

   BOOST_TEST_MESSAGE("Register and upgrade Ann");
   {
      CustomRegisterActor(ann, life, life, 75);
      alife.vcb += reg_fee; alife.bal += -reg_fee;
      CustomAudit();

      transfer(life_id, ann_id, asset(aann.b0));
      alife.vcb += xfer_fee; alife.bal += -xfer_fee -aann.b0; aann.bal += aann.b0;
      CustomAudit();

      upgrade_to_annual_member(ann_id);
      aann.ucb += upg_an_fee; aann.bal += -upg_an_fee;

      // audit distribution of fees from Ann
      alife.ubal += pct( P100-network_pct, aann.ucb );
      alife.bal  += pct( P100-network_pct, aann.vcb );
      aann.ucb = 0; aann.vcb = 0;
      CustomAudit();
   }

   BOOST_TEST_MESSAGE("Register dumy and stud");
   CustomRegisterActor(dumy, rog, life, 75);
   arog.vcb += reg_fee; arog.bal += -reg_fee;
   CustomAudit();

   CustomRegisterActor(stud, rog, ann, 80);
   arog.vcb += reg_fee; arog.bal += -reg_fee;
   CustomAudit();

   BOOST_TEST_MESSAGE("Upgrade stud to lifetime member");

   transfer(life_id, stud_id, asset(astud.b0));
   alife.vcb += xfer_fee; alife.bal += -astud.b0 -xfer_fee; astud.bal += astud.b0;
   CustomAudit();

   upgrade_to_lifetime_member(stud_id);
   astud.ucb += upg_lt_fee; astud.bal -= upg_lt_fee;

/*
network_cut:   20000
referrer_cut:  40000 -> ann
registrar_cut: 10000 -> rog
lifetime_cut:  30000 -> life

NET : net
LTM : net' ltm
REF : net' ltm' ref
REG : net' ltm' ref'
*/

   // audit distribution of fees from stud
   alife.ubal += pct( P100-network_pct,      lt_pct,                     astud.ucb );
   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      astud.ref_pct, astud.ucb );
   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-astud.ref_pct, astud.ucb );
   astud.ucb  = 0;
   CustomAudit();

   BOOST_TEST_MESSAGE("Register pleb and scud");

   CustomRegisterActor(pleb, rog, stud, 95);
   arog.vcb += reg_fee; arog.bal += -reg_fee;
   CustomAudit();

   CustomRegisterActor(scud, stud, ann, 80);
   astud.vcb += reg_fee; astud.bal += -reg_fee;
   CustomAudit();

   generate_block();

   BOOST_TEST_MESSAGE("Wait for maintenance interval");

   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
   // audit distribution of fees from life
   alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb );
   alife.ucb = 0; alife.vcb = 0;

   // audit distribution of fees from rog
   arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb );
   arog.ucb = 0; arog.vcb = 0;

   // audit distribution of fees from ann
   alife.ubal += pct( P100-network_pct,      lt_pct,                    aann.ucb+aann.vcb );
   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      aann.ref_pct, aann.ucb+aann.vcb );
   alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb );
   aann.ucb = 0; aann.vcb = 0;

   // audit distribution of fees from stud
   astud.ubal += pct( P100-network_pct,                                  astud.ucb+astud.vcb );
   astud.ucb = 0; astud.vcb = 0;

   // audit distribution of fees from dumy
   alife.ubal += pct( P100-network_pct,      lt_pct,                     adumy.ucb+adumy.vcb );
   alife.ubal += pct( P100-network_pct, P100-lt_pct,      adumy.ref_pct, adumy.ucb+adumy.vcb );
   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb );
   adumy.ucb = 0; adumy.vcb = 0;

   // audit distribution of fees from scud
   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );
   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );
   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );
   ascud.ucb = 0; ascud.vcb = 0;

   // audit distribution of fees from pleb
   astud.ubal += pct( P100-network_pct,      lt_pct,                     apleb.ucb+apleb.vcb );
   astud.ubal += pct( P100-network_pct, P100-lt_pct,      apleb.ref_pct, apleb.ucb+apleb.vcb );
   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb );
   apleb.ucb = 0; apleb.vcb = 0;

   CustomAudit();

   BOOST_TEST_MESSAGE("Doing some transfers");

   transfer(stud_id, scud_id, asset(500000));
   astud.bal += -500000-xfer_fee; astud.vcb += xfer_fee; ascud.bal += 500000;
   CustomAudit();

   transfer(scud_id, pleb_id, asset(400000));
   ascud.bal += -400000-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 400000;
   CustomAudit();

   transfer(pleb_id, dumy_id, asset(300000));
   apleb.bal += -300000-xfer_fee; apleb.vcb += xfer_fee; adumy.bal += 300000;
   CustomAudit();

   transfer(dumy_id, rog_id, asset(200000));
   adumy.bal += -200000-xfer_fee; adumy.vcb += xfer_fee; arog.bal += 200000;
   CustomAudit();

   BOOST_TEST_MESSAGE("Waiting for maintenance time");

   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);

   // audit distribution of fees from life
   alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb );
   alife.ucb = 0; alife.vcb = 0;

   // audit distribution of fees from rog
   arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb );
   arog.ucb = 0; arog.vcb = 0;

   // audit distribution of fees from ann
   alife.ubal += pct( P100-network_pct,      lt_pct,                    aann.ucb+aann.vcb );
   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      aann.ref_pct, aann.ucb+aann.vcb );
   alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb );
   aann.ucb = 0; aann.vcb = 0;

   // audit distribution of fees from stud
   astud.ubal += pct( P100-network_pct,                                  astud.ucb+astud.vcb );
   astud.ucb = 0; astud.vcb = 0;

   // audit distribution of fees from dumy
   alife.ubal += pct( P100-network_pct,      lt_pct,                     adumy.ucb+adumy.vcb );
   alife.ubal += pct( P100-network_pct, P100-lt_pct,      adumy.ref_pct, adumy.ucb+adumy.vcb );
   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb );
   adumy.ucb = 0; adumy.vcb = 0;

   // audit distribution of fees from scud
   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );
   aann.ubal  += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );
   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );
   ascud.ucb = 0; ascud.vcb = 0;

   // audit distribution of fees from pleb
   astud.ubal += pct( P100-network_pct,      lt_pct,                     apleb.ucb+apleb.vcb );
   astud.ubal += pct( P100-network_pct, P100-lt_pct,      apleb.ref_pct, apleb.ucb+apleb.vcb );
   arog.ubal  += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb );
   apleb.ucb = 0; apleb.vcb = 0;

   CustomAudit();

   BOOST_TEST_MESSAGE("Waiting for annual membership to expire");
   BOOST_TEST_MESSAGE("Count of block to generate " +  std::to_string(ann_id(db).membership_expiration_date.sec_since_epoch()));

   generate_blocks(ann_id(db).membership_expiration_date);
   generate_block();

   BOOST_TEST_MESSAGE("Transferring from scud to pleb");

   //ann's membership has expired, so scud's fee should go up to life instead.
   transfer(scud_id, pleb_id, asset(10));
   ascud.bal += -10-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 10;
   CustomAudit();

   BOOST_TEST_MESSAGE("Waiting for maint interval");

   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);

   // audit distribution of fees from scud
   alife.ubal += pct( P100-network_pct,      lt_pct,                     ascud.ucb+ascud.vcb );
   alife.ubal += pct( P100-network_pct, P100-lt_pct,      ascud.ref_pct, ascud.ucb+ascud.vcb );
   astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb );
   ascud.ucb = 0; ascud.vcb = 0;

   CustomAudit();

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_CASE( account_create_fee_scaling )
{ try {
   auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale;
   db.modify(global_property_id_type()(db), [](global_property_object& gpo)
   {
      gpo.parameters.current_fees = std::make_shared<fee_schedule>(fee_schedule::get_default());
      gpo.parameters.current_fees->get<account_create_operation>().basic_fee = 1;
   });

   for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i )
   {
      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get<account_create_operation>().basic_fee, 1);
      create_account("shill" + fc::to_string(i));
   }
   for( int i = 0; i < accounts_per_scale; ++i )
   {
      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get<account_create_operation>().basic_fee, 16);
      create_account("moreshills" + fc::to_string(i));
   }
   for( int i = 0; i < accounts_per_scale; ++i )
   {
      BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get<account_create_operation>().basic_fee, 256);
      create_account("moarshills" + fc::to_string(i));
   }
   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get<account_create_operation>().basic_fee, 4096);

   generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
   BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get<account_create_operation>().basic_fee, 1);
} FC_LOG_AND_RETHROW() }

//This test is failing, since it is not related to Peerplays related changes, commeting for time being
// BOOST_AUTO_TEST_CASE( fee_refund_test )
// {
//    try
//    {
//       ACTORS((alice)(bob)(izzy));
//
//       int64_t alice_b0 = 1000000, bob_b0 = 1000000;
//
//       transfer( account_id_type(), alice_id, asset(alice_b0) );
//       transfer( account_id_type(), bob_id, asset(bob_b0) );
//
//       asset_id_type core_id = asset_id_type();
//       asset_id_type usd_id = create_user_issued_asset( "IZZYUSD", izzy_id(db), charge_market_fee ).id;
//       issue_uia( alice_id, asset( alice_b0, usd_id ) );
//       issue_uia( bob_id, asset( bob_b0, usd_id ) );
//
//       int64_t order_create_fee = 537;
//       int64_t order_cancel_fee = 129;
//
//       uint32_t skip = database::skip_witness_signature
//                     | database::skip_transaction_signatures
//                     | database::skip_transaction_dupe_check
//                     | database::skip_block_size_check
//                     | database::skip_tapos_check
//                     | database::skip_authority_check
//                     | database::skip_merkle_check
//                     ;
//
//       generate_block( skip );
//
//       for( int i=0; i<2; i++ )
//       {
//          if( i == 1 )
//          {
//             generate_blocks( HARDFORK_445_TIME, true, skip );
//             generate_block( skip );
//          }
//
//          // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation
//          // so we have to do it every time we stop generating/popping blocks and start doing tx's
//          enable_fees();
//          /*
//          change_fees({
//                        limit_order_create_operation::fee_parameters_type { order_create_fee },
//                        limit_order_cancel_operation::fee_parameters_type { order_cancel_fee }
//                      });
//          */
//          // C++ -- The above commented out statement doesn't work, I don't know why
//          // so we will use the following rather lengthy initialization instead
//          {
//             flat_set< fee_parameters > new_fees;
//             {
//                limit_order_create_operation::fee_parameters_type create_fee_params;
//                create_fee_params.fee = order_create_fee;
//                new_fees.insert( create_fee_params );
//             }
//             {
//                limit_order_cancel_operation::fee_parameters_type cancel_fee_params;
//                cancel_fee_params.fee = order_cancel_fee;
//                new_fees.insert( cancel_fee_params );
//             }
//             change_fees( new_fees );
//          }
//
//          // Alice creates order
//          // Bob creates order which doesn't match
//
//          // AAAAGGHH create_sell_order reads trx.expiration #469
//          set_expiration( db, trx );
//
//          // Check non-overlapping
//
//          limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(1000, usd_id) )->id;
//          limit_order_id_type bo1_id = create_sell_order(   bob_id, asset(500, usd_id), asset(1000) )->id;
//
//          BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee );
//          BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - order_create_fee );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 - 500 );
//
//          // Bob cancels order
//          cancel_limit_order( bo1_id(db) );
//
//          int64_t cancel_net_fee;
//          if( db.head_block_time() >= HARDFORK_445_TIME )
//             cancel_net_fee = order_cancel_fee;
//          else
//             cancel_net_fee = order_create_fee + order_cancel_fee;
//
//          BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee );
//          BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - cancel_net_fee );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 );
//
//          // Alice cancels order
//          cancel_limit_order( ao1_id(db) );
//
//          BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee );
//          BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ), bob_b0 - cancel_net_fee );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ), bob_b0 );
//
//          // Check partial fill
//          const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id) );
//          const limit_order_object* bo2 = create_sell_order(   bob_id, asset(100, usd_id), asset(500) );
//
//          BOOST_CHECK( ao2 != nullptr );
//          BOOST_CHECK( bo2 == nullptr );
//
//          BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 1000 );
//          BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 + 100 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ),   bob_b0 - cancel_net_fee - order_create_fee + 500 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ),   bob_b0 - 100 );
//
//          // cancel Alice order, show that entire deferred_fee was consumed by partial match
//          cancel_limit_order( *ao2 );
//
//          BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 500 - order_cancel_fee );
//          BOOST_CHECK_EQUAL( get_balance( alice_id,  usd_id ), alice_b0 + 100 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id, core_id ),   bob_b0 - cancel_net_fee - order_create_fee + 500 );
//          BOOST_CHECK_EQUAL( get_balance(   bob_id,  usd_id ),   bob_b0 - 100 );
//
//          // TODO: Check multiple fill
//          // there really should be a test case involving Alice creating multiple orders matched by single Bob order
//          // but we'll save that for future cleanup
//
//          // undo above tx's and reset
//          generate_block( skip );
//          db.pop_block();
//       }
//    }
//    FC_LOG_AND_RETHROW()
// }

BOOST_AUTO_TEST_CASE( stealth_fba_test )
{
   try
   {
      ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) );
      upgrade_to_lifetime_member(philbin_id);

      generate_blocks( HARDFORK_538_TIME );
      generate_blocks( HARDFORK_555_TIME );
      generate_blocks( HARDFORK_563_TIME );
      generate_blocks( HARDFORK_572_TIME );
      generate_blocks( HARDFORK_599_TIME );

      // Philbin (registrar who registers Rex)

      // Izzy (initial issuer of stealth asset, will later transfer to Tom)
      // Alice, Bob, Chloe, Dan (ABCD)
      // Rex (recycler -- buyback account for stealth asset)
      // Tom (owner of stealth asset who will be set as top_n authority)

      // Izzy creates STEALTH
      asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db),
         disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id;

      /*
      // this is disabled because it doesn't work, our modify() is probably being overwritten by undo

      //
      // Init blockchain with stealth ID's
      // On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
      // causing the designated_asset fields of these objects to be set at genesis, but for
      // this test we modify the db directly.
      //
      auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id )
      {
         db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba )
         {
            fba.designated_asset = asset_id;
         } );
      };

      set_fba_asset( fba_accumulator_id_transfer_to_blind  , stealth_id );
      set_fba_asset( fba_accumulator_id_blind_transfer     , stealth_id );
      set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id );
      */

      // Izzy kills some permission bits (this somehow happened to the real STEALTH in production)
      {
         asset_update_operation update_op;
         update_op.issuer = izzy_id;
         update_op.asset_to_update = stealth_id;
         asset_options new_options;
         new_options = stealth_id(db).options;
         new_options.issuer_permissions = charge_market_fee;
         new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee;
         // after fixing #579 you should be able to delete the following line
         new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) );
         update_op.new_options = new_options;
         signed_transaction tx;
         tx.operations.push_back( update_op );
         set_expiration( db, tx );
         sign( tx, izzy_private_key );
         PUSH_TX( db, tx );
      }

      // Izzy transfers issuer duty to Tom
      {
         asset_update_operation update_op;
         update_op.issuer = izzy_id;
         update_op.asset_to_update = stealth_id;
         update_op.new_issuer = tom_id;
         // new_options should be optional, but isn't...the following line should be unnecessary #580
         update_op.new_options = stealth_id(db).options;
         signed_transaction tx;
         tx.operations.push_back( update_op );
         set_expiration( db, tx );
         sign( tx, izzy_private_key );
         PUSH_TX( db, tx );
      }

      // Tom re-enables the permission bits to clear the flags, then clears them again
      // Allowed by #572 when current_supply == 0
      {
         asset_update_operation update_op;
         update_op.issuer = tom_id;
         update_op.asset_to_update = stealth_id;
         asset_options new_options;
         new_options = stealth_id(db).options;
         new_options.issuer_permissions = new_options.flags | charge_market_fee;
         update_op.new_options = new_options;
         signed_transaction tx;
         // enable perms is one op
         tx.operations.push_back( update_op );

         new_options.issuer_permissions = charge_market_fee;
         new_options.flags = charge_market_fee;
         update_op.new_options = new_options;
         // reset wrongly set flags and reset permissions can be done in a single op
         tx.operations.push_back( update_op );

         set_expiration( db, tx );
         sign( tx, tom_private_key );
         PUSH_TX( db, tx );
      }

      // Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom)
      account_id_type rex_id;
      {
         buyback_account_options bbo;
         bbo.asset_to_buy = stealth_id;
         bbo.asset_to_buy_issuer = tom_id;
         bbo.markets.emplace( asset_id_type() );
         account_create_operation create_op = make_account( "rex" );
         create_op.registrar = philbin_id;
         create_op.extensions.value.buyback_options = bbo;
         create_op.owner = authority::null_authority();
         create_op.active = authority::null_authority();

         signed_transaction tx;
         tx.operations.push_back( create_op );
         set_expiration( db, tx );
         sign( tx, philbin_private_key );
         sign( tx, tom_private_key );

         processed_transaction ptx = PUSH_TX( db, tx );
         rex_id = ptx.operation_results.back().get< object_id_type >();
      }

      // Tom issues some asset to Alice and Bob
      set_expiration( db, trx );  // #11
      issue_uia( alice_id, asset( 1000, stealth_id ) );
      issue_uia(   bob_id, asset( 1000, stealth_id ) );

      // Tom sets his authority to the top_n of the asset
      {
         top_holders_special_authority top2;
         top2.num_top_holders = 2;
         top2.asset = stealth_id;

         account_update_operation op;
         op.account = tom_id;
         op.extensions.value.active_special_authority = top2;
         op.extensions.value.owner_special_authority = top2;

         signed_transaction tx;
         tx.operations.push_back( op );

         set_expiration( db, tx );
         sign( tx, tom_private_key );

         PUSH_TX( db, tx );
      }

      // Wait until the next maintenance interval for top_n to take effect
      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);

      // Do a blind op to add some fees to the pool.
      fund( chloe_id(db), asset( 100000, asset_id_type() ) );

      auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation
      {
         fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) );
         public_key_type blind_pub = blind_key.get_public_key();

         fc::sha256 secret = fc::sha256::hash( key+"-secret" );
         fc::sha256 nonce = fc::sha256::hash( key+"-nonce" );

         transfer_to_blind_operation op;
         blind_output blind_out;
         blind_out.owner = authority( 1, blind_pub, 1 );
         blind_out.commitment = fc::ecc::blind( secret, amount.amount.value );
         blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value );

         op.amount = amount;
         op.from = account;
         op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 );
         op.outputs = {blind_out};

         return op;
      };

      {
         transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" );
         op.fee = asset( 1000, asset_id_type() );

         signed_transaction tx;
         tx.operations.push_back( op );
         set_expiration( db, tx );
         sign( tx, chloe_private_key );

         PUSH_TX( db, tx );
      }

      // wait until next maint interval
      generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);

      idump( ( get_operation_history( chloe_id ) ) );
      idump( ( get_operation_history( rex_id ) ) );
      idump( ( get_operation_history( tom_id ) ) );
   }
   catch( const fc::exception& e )
   {
      elog( "caught exception ${e}", ("e", e.to_detail_string()) );
      throw;
   }
}
// added test from bitshares for issues:
//    https://github.com/bitshares/bitshares-core/issues/429
//    https://github.com/bitshares/bitshares-core/issues/433
BOOST_AUTO_TEST_CASE( defaults_test )
{ try {
    fee_schedule schedule;
    const limit_order_create_operation::fee_parameters_type default_order_fee;

    // no fees set yet -> default
    asset fee = schedule.calculate_fee( limit_order_create_operation() );
    BOOST_CHECK_EQUAL( default_order_fee.fee, fee.amount.value );

    limit_order_create_operation::fee_parameters_type new_order_fee; new_order_fee.fee = 123;
    // set fee + check
    schedule.parameters.insert( new_order_fee );
    fee = schedule.calculate_fee( limit_order_create_operation() );
    BOOST_CHECK_EQUAL( new_order_fee.fee, fee.amount.value );

    // NO bid_collateral_operation in this version

    // bid_collateral fee defaults to call_order_update fee
    // call_order_update fee is unset -> default
    // const call_order_update_operation::fee_parameters_type default_short_fee;
    // call_order_update_operation::fee_parameters_type new_short_fee; new_short_fee.fee = 123;
    // fee = schedule.calculate_fee( bid_collateral_operation() );
    // BOOST_CHECK_EQUAL( default_short_fee.fee, fee.amount.value );

    // set call_order_update fee + check bid_collateral fee
    // schedule.parameters.insert( new_short_fee );
    // fee = schedule.calculate_fee( bid_collateral_operation() );
    // BOOST_CHECK_EQUAL( new_short_fee.fee, fee.amount.value );

    // set bid_collateral fee + check
    // bid_collateral_operation::fee_parameters_type new_bid_fee; new_bid_fee.fee = 124;
    // schedule.parameters.insert( new_bid_fee );
    // fee = schedule.calculate_fee( bid_collateral_operation() );
    // BOOST_CHECK_EQUAL( new_bid_fee.fee, fee.amount.value );
  }
  catch( const fc::exception& e )
  {
     elog( "caught exception ${e}", ("e", e.to_detail_string()) );
     throw;
  }
}

BOOST_AUTO_TEST_CASE( issue_429_test )
{
   try
   {
      ACTORS((alice));

      transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( asset_id_type()(db).precision ) ) );

      // make sure the database requires our fee to be nonzero
      enable_fees();

      const auto& fees = *db.get_global_properties().parameters.current_fees;
      auto fees_to_pay = fees.get<asset_create_operation>();

      {
         signed_transaction tx;
         asset_create_operation op;
         op.issuer = alice_id;
         op.symbol = "ALICE";
         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );
         op.fee = asset( (fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) & (~1) );
         tx.operations.push_back( op );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         PUSH_TX( db, tx );
      }

      verify_asset_supplies( db );

      {
         signed_transaction tx;
         asset_create_operation op;
         op.issuer = alice_id;
         op.symbol = "ALICE.ODD";
         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );
         op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1);
         tx.operations.push_back( op );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         PUSH_TX( db, tx );
      }

      verify_asset_supplies( db );

      {
         signed_transaction tx;
         asset_create_operation op;
         op.issuer = alice_id;
         op.symbol = "ALICE.ODDER";
         op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );
         op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1);
         tx.operations.push_back( op );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         PUSH_TX( db, tx );
      }

      verify_asset_supplies( db );
   }
   catch( const fc::exception& e )
   {
      edump((e.to_detail_string()));
      throw;
   }
}

BOOST_AUTO_TEST_CASE( issue_433_test )
{
   try
   {
      ACTORS((alice));

      auto& core = asset_id_type()(db);

      transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( core.precision ) ) );

      const auto& myusd = create_user_issued_asset( "MYUSD", alice, 0 );
      issue_uia( alice, myusd.amount( 2000000000 ) );

      // make sure the database requires our fee to be nonzero
      enable_fees();

      const auto& fees = *db.get_global_properties().parameters.current_fees;
      const auto asset_create_fees = fees.get<asset_create_operation>();

      fund_fee_pool( alice, myusd, 5*asset_create_fees.long_symbol );

      asset_create_operation op;
      op.issuer = alice_id;
      op.symbol = "ALICE";
      op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) );
      op.fee = myusd.amount( ((asset_create_fees.long_symbol + asset_create_fees.price_per_kbyte) & (~1)) );
      {
         signed_transaction tx;
         tx.operations.push_back( op );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         PUSH_TX( db, tx );
      }

      verify_asset_supplies( db );

      const auto proposal_create_fees = fees.get<proposal_create_operation>();
      proposal_create_operation prop;
      op.symbol = "ALICE.PROP";
      prop.fee_paying_account = alice_id;
      prop.proposed_ops.emplace_back( op );
      prop.expiration_time =  db.head_block_time() + fc::days(1);
      prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte );
      object_id_type proposal_id;
      {
         signed_transaction tx;
         tx.operations.push_back( prop );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         proposal_id = PUSH_TX( db, tx ).operation_results.front().get<object_id_type>();
      }
      const proposal_object& proposal = db.get<proposal_object>( proposal_id );

      const auto proposal_update_fees = fees.get<proposal_update_operation>();
      proposal_update_operation pup;
      pup.proposal = proposal.id;
      pup.fee_paying_account = alice_id;
      pup.active_approvals_to_add.insert(alice_id);
      pup.fee = asset( proposal_update_fees.fee + proposal_update_fees.price_per_kbyte );
      {
         signed_transaction tx;
         tx.operations.push_back( pup );
         set_expiration( db, tx );
         sign( tx, alice_private_key );
         PUSH_TX( db, tx );
      }

      verify_asset_supplies( db );
   }
   catch( const fc::exception& e )
   {
      edump((e.to_detail_string()));
      throw;
   }
}

BOOST_AUTO_TEST_SUITE_END()
