Exemple #1
0
/**
 *  This method dumps the state of the blockchain in a semi-human readable form for the
 *  purpose of tracking down funds and mismatches in currency allocation
 */
void database::debug_dump()
{
   const auto& db = *this;
   const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);

   const auto& balance_index = db.get_index_type<account_balance_index>().indices();
   const simple_index<account_statistics_object>& statistics_index = db.get_index_type<simple_index<account_statistics_object>>();
   map<asset_id_type,share_type> total_balances;
   map<asset_id_type,share_type> total_debts;
   share_type core_in_orders;
   share_type reported_core_in_orders;

   for( const account_balance_object& a : balance_index )
   {
    //  idump(("balance")(a));
      total_balances[a.asset_type] += a.balance;
   }
   for( const account_statistics_object& s : statistics_index )
   {
    //  idump(("statistics")(s));
      reported_core_in_orders += s.total_core_in_orders;
   }
   for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
   {
 //     idump(("limit_order")(o));
      auto for_sale = o.amount_for_sale();
      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 call_order_object& o : db.get_index_type<call_order_index>().indices() )
   {
//      idump(("call_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;
      total_debts[o.get_debt().asset_id] += o.get_debt().amount;
   }
   for( const asset_object& asset_obj : db.get_index_type<asset_index>().indices() )
   {
      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;
//      edump((total_balances[asset_obj.id])(asset_obj.dynamic_asset_data_id(db).current_supply ) );
   }

   if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )
   {
      edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));
   }

   edump((core_in_orders)(reported_core_in_orders));

   /*
   const auto& vbidx = db.get_index_type<simple_index<vesting_balance_object>>();
   for( const auto& s : vbidx )
   {
//      idump(("vesting_balance")(s));
   }
   */
}
Exemple #2
0
      void cancel_current_short( market_transaction& mtrx, const asset_id_type& quote_asset_id )
      {
         FC_ASSERT( _current_bid->type == short_order );
         FC_ASSERT( mtrx.bid_type == short_order );

         elog( "Canceling current short" );
         edump( (mtrx) );

         // Create automatic market cancel transaction
         mtrx.ask_paid       = asset();
         mtrx.ask_received   = asset( 0, quote_asset_id );
         mtrx.bid_received   = _current_bid->get_balance();
         mtrx.bid_paid       = asset( 0, quote_asset_id );
         mtrx.bid_collateral.reset();

         // Fund refund balance record
         const balance_id_type id = withdraw_condition( withdraw_with_signature( mtrx.bid_owner ), 0 ).get_address();
         obalance_record bid_payout = _pending_state->get_balance_record( id );
         if( !bid_payout.valid() )
            bid_payout = balance_record( mtrx.bid_owner, asset( 0, 0 ), 0 );

         bid_payout->balance += mtrx.bid_received.amount;
         bid_payout->last_update = _pending_state->now();
         bid_payout->deposit_date = _pending_state->now();
         _pending_state->store_balance_record( *bid_payout );

         // Remove short order
         _current_bid->state.balance = 0;
         _pending_state->store_short_record( _current_bid->market_index, _current_bid->state );
      }
Exemple #3
0
 ~witness_plugin() {
    try {
       if( _block_production_task.valid() )
          _block_production_task.cancel_and_wait(__FUNCTION__);
    } catch(fc::canceled_exception&) {
       //Expected exception. Move along.
    } catch(fc::exception& e) {
       edump((e.to_detail_string()));
    }
 }
Exemple #4
0
int main( int argc, char** argv, char** envp )
{
   try
   {
      //steemit::chain::database db;
      steemit::chain::block_log log;

      fc::temp_directory temp_dir( "." );

      //db.open( temp_dir );
      log.open( temp_dir.path() / "log" );

      idump( (log.head() ) );

      steemit::protocol::signed_block b1;
      b1.witness = "alice";
      b1.previous = steemit::protocol::block_id_type();

      log.append( b1 );
      log.flush();
      idump( (b1) );
      idump( ( log.head() ) );
      idump( (fc::raw::pack_size(b1)) );

      steemit::protocol::signed_block b2;
      b2.witness = "bob";
      b2.previous = b1.id();

      log.append( b2 );
      log.flush();
      idump( (b2) );
      idump( (log.head() ) );
      idump( (fc::raw::pack_size(b2)) );

      auto r1 = log.read_block( 0 );
      idump( (r1) );
      idump( (fc::raw::pack_size(r1.first)) );

      auto r2 = log.read_block( r1.second );
      idump( (r2) );
      idump( (fc::raw::pack_size(r2.first)) );

      idump( (log.read_head()) );
      idump( (fc::raw::pack_size(log.read_head())));

      auto r3 = log.read_block( r2.second );
      idump( (r3) );
   }
   catch ( const std::exception& e )
   {
      edump( ( std::string( e.what() ) ) );
   }

   return 0;
}
Exemple #5
0
std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of_fork) const
{
  pair<fork_database::branch_type, fork_database::branch_type> branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork);
  if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) )
  {
     edump( (head_of_fork)
            (head_block_id())
            (branches.first.size())
            (branches.second.size()) );
     assert(branches.first.back()->previous_id() == branches.second.back()->previous_id());
  }
  std::vector<block_id_type> result;
  for (const item_ptr& fork_block : branches.second)
    result.emplace_back(fork_block->id);
  result.emplace_back(branches.first.back()->previous_id());
  return result;
}
Exemple #6
0
      read_only::get_actions_result read_only::get_actions( const read_only::get_actions_params& params )const {
         edump((params));
        auto& chain = history->chain_plug->chain();
        const auto& db = chain.db();

        const auto& idx = db.get_index<account_history_index, by_account_action_seq>();

        int32_t start = 0;
        int32_t pos = params.pos ? *params.pos : -1;
        int32_t end = 0;
        int32_t offset = params.offset ? *params.offset : -20;
        auto n = params.account_name;
        idump((pos));
        if( pos == -1 ) {
            auto itr = idx.lower_bound( boost::make_tuple( name(n.value+1), 0 ) );
            if( itr == idx.begin() ) {
               if( itr->account == n )
                  pos = itr->account_sequence_num+1;
            } else if( itr != idx.begin() ) --itr;

            if( itr->account == n ) 
               pos = itr->account_sequence_num + 1;
        }

        if( pos== -1 ) pos = 0xfffffff;

        if( offset > 0 ) {
           start = pos;
           end   = start + offset;
        } else {
           start = pos + offset;
           if( start > pos ) start = 0;
           end   = pos;
        }
        FC_ASSERT( end >= start );

        idump((start)(end));

        auto start_itr = idx.lower_bound( boost::make_tuple( n, start ) );
        auto end_itr = idx.lower_bound( boost::make_tuple( n, end+1) );

        auto start_time = fc::time_point::now();
        auto end_time = start_time;

        get_actions_result result;
        result.last_irreversible_block = chain.last_irreversible_block_num();
        while( start_itr != end_itr ) {
           const auto& a = db.get<action_history_object, by_action_sequence_num>( start_itr->action_sequence_num );
           fc::datastream<const char*> ds( a.packed_action_trace.data(), a.packed_action_trace.size() );
           action_trace t;
           fc::raw::unpack( ds, t );
           result.actions.emplace_back( ordered_action_result{
                                 start_itr->action_sequence_num,
                                 start_itr->account_sequence_num,
                                 a.block_num, a.block_time,
                                 chain.to_variant_with_abi(t)
                                 });

           end_time = fc::time_point::now();
           if( end_time - start_time > fc::microseconds(100000) ) {
              result.time_limit_exceeded_error = true;
              break;
           }
           ++start_itr;
        }
        return result;
      }
Exemple #7
0
      /**
       * Returns a synopsis of the blockchain used for syncing.  This consists of a list of
       * block hashes at intervals exponentially increasing towards the genesis block.
       * When syncing to a peer, the peer uses this data to determine if we're on the same
       * fork as they are, and if not, what blocks they need to send us to get us on their
       * fork.
       *
       * In the over-simplified case, this is a straighforward synopsis of our current
       * preferred blockchain; when we first connect up to a peer, this is what we will be sending.
       * It looks like this:
       *   If the blockchain is empty, it will return the empty list.
       *   If the blockchain has one block, it will return a list containing just that block.
       *   If it contains more than one block:
       *     the first element in the list will be the hash of the highest numbered block that
       *         we cannot undo
       *     the second element will be the hash of an item at the half way point in the undoable
       *         segment of the blockchain
       *     the third will be ~3/4 of the way through the undoable segment of the block chain
       *     the fourth will be at ~7/8...
       *       &c.
       *     the last item in the list will be the hash of the most recent block on our preferred chain
       * so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
       *    a n u x z
       * the idea being that by sending a small (<30) number of block ids, we can summarize a huge
       * blockchain.  The block ids are more dense near the end of the chain where because we are
       * more likely to be almost in sync when we first connect, and forks are likely to be short.
       * If the peer we're syncing with in our example is on a fork that started at block 'v',
       * then they will reply to our synopsis with a list of all blocks starting from block 'u',
       * the last block they know that we had in common.
       *
       * In the real code, there are several complications.
       *
       * First, as an optimization, we don't usually send a synopsis of the entire blockchain, we
       * send a synopsis of only the segment of the blockchain that we have undo data for.  If their
       * fork doesn't build off of something in our undo history, we would be unable to switch, so there's
       * no reason to fetch the blocks.
       *
       * Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
       * we are missing, they only send a chunk of a few thousand blocks at once.  After we get those
       * block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
       * the next 2000 ids" because they may have switched forks themselves and they don't track what
       * they've sent us).  For faster performance, we want to get a fairly long list of block ids first,
       * then start downloading the blocks.
       * The peer doesn't handle these follow-up block id requests any different from the initial request;
       * it treats the synopsis we send as our blockchain and bases its response entirely off that.  So to
       * get the response we want (the next chunk of block ids following the last one they sent us, or,
       * failing that, the shortest fork off of the last list of block ids they sent), we need to construct
       * a synopsis as if our blockchain was made up of:
       *    1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
       *    2. the blocks we've already pushed from their fork (if there's a fork)
       *    3. the block ids they've previously sent us
       * Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
       * number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
       * We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
       * fork database.  The reference_point parameter is the last block from that peer that has been
       * successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
       * the main chain.
       */
      virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
                                                               uint32_t number_of_blocks_after_reference_point) override
      { try {
          std::vector<item_hash_t> synopsis;
          synopsis.reserve(30);
          uint32_t high_block_num;
          uint32_t non_fork_high_block_num;
          uint32_t low_block_num = _chain_db->last_non_undoable_block_num();
          std::vector<block_id_type> fork_history;

          if (reference_point != item_hash_t())
          {
            // the node is asking for a summary of the block chain up to a specified
            // block, which may or may not be on a fork
            // for now, assume it's not on a fork
            if (is_included_block(reference_point))
            {
              // reference_point is a block we know about and is on the main chain
              uint32_t reference_point_block_num = block_header::num_from_id(reference_point);
              assert(reference_point_block_num > 0);
              high_block_num = reference_point_block_num;
              non_fork_high_block_num = high_block_num;

              if (reference_point_block_num < low_block_num)
              {
                // we're on the same fork (at least as far as reference_point) but we've passed
                // reference point and could no longer undo that far if we diverged after that
                // block.  This should probably only happen due to a race condition where
                // the network thread calls this function, and then immediately pushes a bunch of blocks,
                // then the main thread finally processes this function.
                // with the current framework, there's not much we can do to tell the network
                // thread what our current head block is, so we'll just pretend that
                // our head is actually the reference point.
                // this *may* enable us to fetch blocks that we're unable to push, but that should
                // be a rare case (and correctly handled)
                low_block_num = reference_point_block_num;
              }
            }
            else
            {
              // block is a block we know about, but it is on a fork
              try
              {
                fork_history = _chain_db->get_block_ids_on_fork(reference_point);
                // returns a vector where the last element is the common ancestor with the preferred chain,
                // and the first element is the reference point you passed in
                assert(fork_history.size() >= 2);

                if( fork_history.front() != reference_point )
                {
                   edump( (fork_history)(reference_point) );
                   assert(fork_history.front() == reference_point);
                }
                block_id_type last_non_fork_block = fork_history.back();
                fork_history.pop_back();  // remove the common ancestor
                boost::reverse(fork_history);

                if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?)
                  non_fork_high_block_num = 0;
                else
                  non_fork_high_block_num = block_header::num_from_id(last_non_fork_block);

                high_block_num = non_fork_high_block_num + fork_history.size();
                assert(high_block_num == block_header::num_from_id(fork_history.back()));
              }
              catch (const fc::exception& e)
              {
                // unable to get fork history for some reason.  maybe not linked?
                // we can't return a synopsis of its chain
                elog("Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}", ("hash", reference_point)("exception", e));
                throw;
              }
              if (non_fork_high_block_num < low_block_num)
              {
                wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
                     "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
                     ("low_block_num", low_block_num)
                     ("non_fork_high_block_num", non_fork_high_block_num));
                FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
              }
            }
          }
          else
          {
            // no reference point specified, summarize the whole block chain
            high_block_num = _chain_db->head_block_num();
            non_fork_high_block_num = high_block_num;
            if (high_block_num == 0)
              return synopsis; // we have no blocks
          }
          
          if( low_block_num == 0)
             low_block_num = 1;

          // at this point:
          // low_block_num is the block before the first block we can undo,
          // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
          // high_block_num is the block number of the reference block, or the end of the chain if no reference provided

          // true_high_block_num is the ending block number after the network code appends any item ids it
          // knows about that we don't
          uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
          do
          {
            // for each block in the synopsis, figure out where to pull the block id from.
            // if it's <= non_fork_high_block_num, we grab it from the main blockchain;
            // if it's not, we pull it from the fork history
            if (low_block_num <= non_fork_high_block_num)
              synopsis.push_back(_chain_db->get_block_id_for_num(low_block_num));
            else
              synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]);
            low_block_num += (true_high_block_num - low_block_num + 2) / 2;
          }
          while (low_block_num <= high_block_num);

          //idump((synopsis));
          return synopsis;
      } FC_CAPTURE_AND_RETHROW() }
/**
 *  Starting with the least collateralized orders, fill them if their
 *  call price is above the max(lowest bid,call_limit).
 *
 *  This method will return true if it filled a short or limit
 *
 *  @param mia - the market issued asset that should be called.
 *  @param enable_black_swan - when adjusting collateral, triggering a black swan is invalid and will throw
 *                             if enable_black_swan is not set to true.
 *
 *  @return true if a margin call was executed.
 */
bool database::check_call_orders(const asset_object& mia, bool enable_black_swan)
{ try {
    if( !mia.is_market_issued() ) return false;

    if( check_for_blackswan( mia, enable_black_swan ) ) 
       return false;

    const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
    if( bitasset.is_prediction_market ) return false;
    if( bitasset.current_feed.settlement_price.is_null() ) return false;

    const call_order_index& call_index = get_index_type<call_order_index>();
    const auto& call_price_index = call_index.indices().get<by_price>();

    const limit_order_index& limit_index = get_index_type<limit_order_index>();
    const auto& limit_price_index = limit_index.indices().get<by_price>();

    // 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();

    assert( max_price.base.asset_id == min_price.base.asset_id );
    // 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_end )
       return false;

    auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
    auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
    auto call_itr = call_price_index.lower_bound( call_min );
    auto call_end = call_price_index.upper_bound( call_max );

    bool filled_limit = false;
    bool margin_called = false;

    while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end )
    {
       bool  filled_call      = false;
       price match_price;
       asset usd_for_sale;
       if( limit_itr != limit_end )
       {
          assert( limit_itr != limit_price_index.end() );
          match_price      = limit_itr->sell_price;
          usd_for_sale     = limit_itr->amount_for_sale();
       }
       else return margin_called;

       match_price.validate();

       // would be margin called, but there is no matching order #436
       bool feed_protected = ( bitasset.current_feed.settlement_price > ~call_itr->call_price );
       if( feed_protected && (head_block_time() > HARDFORK_436_TIME) )
          return margin_called;

       // would be margin called, but there is no matching order
       if( match_price > ~call_itr->call_price )
          return margin_called;

       if( feed_protected )
       {
          ilog( "Feed protected margin call executing (HARDFORK_436_TIME not here yet)" );
          idump( (*call_itr) );
          idump( (*limit_itr) );
       }

     //  idump((*call_itr));
     //  idump((*limit_itr));

     //  ilog( "match_price <= ~call_itr->call_price  performing a margin call" );

       margin_called = true;

       auto usd_to_buy   = call_itr->get_debt();

       if( usd_to_buy * match_price > call_itr->get_collateral() )
       {
          elog( "black swan detected" ); 
          edump((enable_black_swan));
          FC_ASSERT( enable_black_swan );
          globally_settle_asset(mia, bitasset.current_feed.settlement_price );
          return true;
       }

       asset call_pays, call_receives, order_pays, order_receives;
       if( usd_to_buy >= usd_for_sale )
       {  // fill order
          call_receives   = usd_for_sale;
          order_receives  = usd_for_sale * match_price;
          call_pays       = order_receives;
          order_pays      = usd_for_sale;

          filled_limit = true;
          filled_call           = (usd_to_buy == usd_for_sale);
       } else { // fill call
          call_receives  = usd_to_buy;
          order_receives = usd_to_buy * match_price;
          call_pays      = order_receives;
          order_pays     = usd_to_buy;

          filled_call    = true;
       }

       FC_ASSERT( filled_call || filled_limit );

       auto old_call_itr = call_itr;
       if( filled_call ) ++call_itr;
       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, true);

    } // whlie call_itr != call_end

    return margin_called;
} FC_CAPTURE_AND_RETHROW() }
Exemple #9
0
      void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp )
      {
         try
         {
             const uint32_t pending_block_num = _pending_state->get_head_block_num();

             _quote_id = quote_id;
             _base_id = base_id;
             oasset_record quote_asset = _pending_state->get_asset_record( _quote_id );
             oasset_record base_asset = _pending_state->get_asset_record( _base_id );
             FC_ASSERT( quote_asset.valid() && base_asset.valid() );

             // The order book is sorted from low to high price. So to get the last item (highest bid),
             // we need to go to the first item in the next market class and then back up one
             const price next_pair = (base_id+1 == quote_id) ? price( 0, quote_id+1, 0 ) : price( 0, quote_id, base_id+1 );
             _bid_itr        = _db_impl._bid_db.lower_bound( market_index_key( next_pair ) );
             _ask_itr        = _db_impl._ask_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) );
             _short_itr      = _db_impl._short_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) );
             _collateral_itr = _db_impl._collateral_db.lower_bound( market_index_key( next_pair ) );

             if( !_ask_itr.valid() )
             {
                wlog( "ask iter invalid..." );
                _ask_itr = _db_impl._ask_db.begin();
             }

             if( _bid_itr.valid() )   --_bid_itr;
             else _bid_itr = _db_impl._bid_db.last();

             if( _collateral_itr.valid() )   --_collateral_itr;
             else _collateral_itr = _db_impl._collateral_db.last();

             asset trading_volume(0, base_id);

             // Set initial market status
             {
                 omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id );
                 if( !market_stat ) market_stat = market_status( quote_id, base_id, 0, 0 );
                 _market_stat = *market_stat;
             }

             price min_cover_ask;
             price opening_price;
             price closing_price;

             const oprice median_feed_price = _db_impl.self->get_median_delegate_price( quote_id, base_id );
             if( quote_asset->is_market_issued() )
             {
                 // If bootstrapping market for the very first time
                 if( _market_stat.center_price.ratio == fc::uint128_t() )
                 {
                     if( median_feed_price.valid() )
                         _market_stat.center_price = *median_feed_price;
                     else 
                         FC_CAPTURE_AND_THROW( insufficient_feeds, (quote_id) );
                 }

                 min_cover_ask = _market_stat.minimum_ask();
             }

             int last_orders_filled = -1;

             bool order_did_execute = false;

             // prime the pump, to make sure that margin calls (asks) have a bid to check against.
             get_next_bid(); get_next_ask();
             idump( (_current_bid)(_current_ask) );
             while( get_next_bid() && get_next_ask() )
             {
                idump( (_current_bid)(_current_ask) );

                // Make sure that at least one order was matched every time we enter the loop
                FC_ASSERT( _orders_filled != last_orders_filled, "We appear caught in an order matching loop" );
                last_orders_filled = _orders_filled;

                const asset bid_quantity_xts = _current_bid->get_quantity();
                const asset ask_quantity_xts = _current_ask->get_quantity();

                asset current_bid_balance = _current_bid->get_balance();

                // Initialize the market transaction
                market_transaction mtrx;
                mtrx.bid_owner = _current_bid->get_owner();
                mtrx.ask_owner = _current_ask->get_owner();
                // Always execute shorts at the center price
                mtrx.bid_price = (_current_bid->type != short_order) ? _current_bid->get_price() : _market_stat.center_price;
                mtrx.ask_price = _current_ask->get_price();
                mtrx.bid_type  = _current_bid->type;
                mtrx.ask_type  = _current_ask->type;

                if( _current_ask->type == cover_order && _current_bid->type == short_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued() ); 

                   /** don't allow new shorts to execute unless there is a feed, all other
                    * trades are still valid. (we shouldn't stop the market)
                    */
                   if( !median_feed_price.valid() )
                   {
                      _current_bid.reset();
                      continue;
                   }

                   if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached
                      break;

                   if( _current_bid->state.short_price_limit.valid() )
                   {
                      if( *_current_bid->state.short_price_limit < mtrx.ask_price )
                      {
                         _current_bid.reset();
                         continue; // skip shorts that are over the price limit.
                      }
                      mtrx.bid_price = *_current_bid->state.short_price_limit;
                   }

                   mtrx.ask_price = mtrx.bid_price;

                   // Bound collateral ratio
                   price collateral_rate = _current_bid->get_price();
                   if( collateral_rate > _market_stat.center_price )
                      collateral_rate = _market_stat.center_price;

                   const asset ask_quantity_usd = _current_ask->get_quote_quantity();
                   const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate;

                   const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd );

                   mtrx.ask_received   = trade_quantity_usd;
                   mtrx.bid_paid       = mtrx.ask_received;

                   mtrx.ask_paid       = mtrx.ask_received * mtrx.ask_price;
                   mtrx.bid_received   = mtrx.ask_paid;

                   mtrx.bid_collateral = mtrx.bid_paid / collateral_rate;

                   // Handle rounding errors
                   if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION )
                       mtrx.bid_collateral = _current_bid->get_balance();

                   // If too little collateral at this price
                   if( *mtrx.bid_collateral < mtrx.ask_paid )
                   {
                       edump( (mtrx) );
                       _current_bid.reset();
                       continue;
                   }

                   pay_current_short( mtrx, *quote_asset );
                   pay_current_cover( mtrx, *quote_asset );

                   _market_stat.bid_depth -= mtrx.bid_collateral->amount;
                   _market_stat.ask_depth += mtrx.bid_collateral->amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == cover_order && _current_bid->type == bid_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued()  );
                   if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached
                      break;

                   /**
                    *  Don't allow margin calls to be executed too far below
                    *  the minimum ask, this could lead to an attack where someone
                    *  walks the whole book to steal the collateral.
                    */
                   if( mtrx.bid_price < _market_stat.minimum_ask() )
                   {
                      _current_ask.reset();
                      continue;
                   }

                   mtrx.ask_price = mtrx.bid_price;

                   const asset max_usd_purchase = asset( *_current_ask->collateral, _base_id ) * mtrx.bid_price;
                   asset usd_exchanged = std::min( current_bid_balance, max_usd_purchase );

                   // Bound quote asset amount exchanged
                   const asset required_usd_purchase = _current_ask->get_balance();
                   if( required_usd_purchase < usd_exchanged )
                      usd_exchanged = required_usd_purchase;

                   mtrx.ask_received = usd_exchanged;
                   mtrx.bid_paid     = mtrx.ask_received;

                   // Handle rounding errors
                   if( usd_exchanged == max_usd_purchase )
                      mtrx.ask_paid = asset(*_current_ask->collateral,_base_id);
                   else
                      mtrx.ask_paid = usd_exchanged * mtrx.bid_price;

                   mtrx.bid_received = mtrx.ask_paid;

                   pay_current_bid( mtrx, *quote_asset );
                   pay_current_cover( mtrx, *quote_asset );

                   // TODO: Do we need to decrease bid depth as well?
                   _market_stat.ask_depth -= mtrx.ask_paid.amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == ask_order && _current_bid->type == short_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued() );

                   /** don't allow new shorts to execute unless there is a feed, all other
                    * trades are still valid.
                    */
                   if( !median_feed_price.valid() )
                   {
                      _current_bid.reset();
                      continue;
                   }

                   if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached
                      break;

                   if( _current_bid->state.short_price_limit.valid() )
                   {
                      if( *_current_bid->state.short_price_limit < mtrx.ask_price )
                      {
                         elog( "short price limit < bid price" );
                         _current_bid.reset();
                         continue; // skip shorts that are over the price limit.
                      }
                      mtrx.bid_price = *_current_bid->state.short_price_limit;
                   }

                   // Bound collateral ratio
                   price collateral_rate = _current_bid->get_price();
                   if( collateral_rate > _market_stat.center_price )
                      collateral_rate = _market_stat.center_price;

                   const asset ask_quantity_usd = _current_ask->get_quote_quantity();
                   const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate;

                   const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd );

                   mtrx.ask_received   = trade_quantity_usd;
                   mtrx.bid_paid       = mtrx.ask_received;

                   mtrx.ask_paid       = mtrx.ask_received * mtrx.ask_price;
                   mtrx.bid_received   = mtrx.ask_paid;

                   mtrx.bid_collateral = mtrx.bid_paid / collateral_rate;

                   // Handle rounding errors
                   if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION )
                       mtrx.bid_collateral = _current_bid->get_balance();

                   // If too little collateral at this price
                   if( *mtrx.bid_collateral < mtrx.ask_paid )
                   {
                       edump( (mtrx) );
                       _current_bid.reset();
                       continue;
                   }

                   pay_current_short( mtrx, *quote_asset );
                   pay_current_ask( mtrx, *quote_asset );

                   _market_stat.bid_depth -= mtrx.bid_collateral->amount;
                   _market_stat.ask_depth += mtrx.bid_collateral->amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == ask_order && _current_bid->type == bid_order )
                {
                   if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached
                      break;

                   const asset quantity_xts = std::min( bid_quantity_xts, ask_quantity_xts );

                   // Everyone gets the price they asked for
                   mtrx.ask_received   = quantity_xts * mtrx.ask_price;
                   mtrx.bid_paid       = quantity_xts * mtrx.bid_price;

                   mtrx.ask_paid       = quantity_xts;
                   mtrx.bid_received   = quantity_xts;

                   // Handle rounding errors
                   if( quantity_xts == bid_quantity_xts )
                      mtrx.bid_paid = current_bid_balance;

                   mtrx.fees_collected = mtrx.bid_paid - mtrx.ask_received;

                   pay_current_bid( mtrx, *quote_asset );
                   pay_current_ask( mtrx, *base_asset );

                   // TODO: Do we need to decrease bid depth as well?
                   _market_stat.ask_depth -= mtrx.ask_paid.amount;

                   order_did_execute = true;
                }

                push_market_transaction( mtrx );

                if( mtrx.ask_received.asset_id == 0 )
                  trading_volume += mtrx.ask_received;
                else if( mtrx.bid_received.asset_id == 0 )
                  trading_volume += mtrx.bid_received;
                if( opening_price == price() )
                  opening_price = mtrx.bid_price;
                closing_price = mtrx.bid_price;

                if( mtrx.fees_collected.asset_id == base_asset->id )
                    base_asset->collected_fees += mtrx.fees_collected.amount;
                else if( mtrx.fees_collected.asset_id == quote_asset->id )
                    quote_asset->collected_fees += mtrx.fees_collected.amount;
             } // while( next bid && next ask )

             // update any fees collected
             _pending_state->store_asset_record( *quote_asset );
             _pending_state->store_asset_record( *base_asset );

             _market_stat.last_error.reset();

             // Force at least one center price update every 6 blocks
             order_did_execute |= (pending_block_num % 6) == 0;

             if( _current_bid && _current_ask && order_did_execute )
             {
                if( median_feed_price.valid() )
                {
                   _market_stat.center_price = *median_feed_price;
                }
                else if( _current_bid->type != short_order ) // we cannot use short prices for this
                {
                   _market_stat.center_price.ratio *= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR-1);

                   const price max_bid = _market_stat.maximum_bid();

                   // limit the maximum movement rate of the price.
                   if( _current_bid->get_price() < min_cover_ask )
                      _market_stat.center_price.ratio += min_cover_ask.ratio;
                   else if( _current_bid->get_price() > max_bid )
                      _market_stat.center_price.ratio += max_bid.ratio;
                   else
                      _market_stat.center_price.ratio += _current_bid->get_price().ratio;

                   if( _current_ask->get_price() < min_cover_ask )
                      _market_stat.center_price.ratio += min_cover_ask.ratio;
                   else if( _current_ask->get_price() > max_bid )
                      _market_stat.center_price.ratio += max_bid.ratio;
                   else
                      _market_stat.center_price.ratio += _current_ask->get_price().ratio;

                   _market_stat.center_price.ratio /= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR+1);
                }
             }

             // Update market status and market history
             _pending_state->store_market_status( _market_stat );
             update_market_history( trading_volume, opening_price, closing_price, _market_stat, timestamp );

             wlog( "done matching orders" );
             idump( (_current_bid)(_current_ask) );

             _pending_state->apply_changes();
        }
        catch( const fc::exception& e )
        {
           wlog( "error executing market ${quote} / ${base}\n ${e}", ("quote",quote_id)("base",base_id)("e",e.to_detail_string()) );
           auto market_state = _prior_state->get_market_status( quote_id, base_id );
           if( !market_state )
              market_state = market_status( quote_id, base_id, 0, 0 );
           market_state->last_error = e;
           _prior_state->store_market_status( *market_state );
        }
      } // execute(...)
Exemple #10
0
int main( int argc, char** argv )
{
   try {
      auto InB1 = fc::sha256::hash("InB1");
      auto InB2 = fc::sha256::hash("InB2");
      auto OutB1 = fc::sha256::hash("OutB1");


      auto InC1 = fc::ecc::blind(InB1,25);
      auto InC2 = fc::ecc::blind(InB2,75);

      auto OutC1 = fc::ecc::blind(OutB1,40);

      auto OutB2 = fc::ecc::blind_sum( {InB1,InB2,OutB1}, 2 );
      auto OutC2 = fc::ecc::blind( OutB2, 60 );

      FC_ASSERT( fc::ecc::verify_sum( {InC1,InC2}, {OutC1,OutC2}, 0 ) );
      auto nonce = fc::sha256::hash("nonce");

      auto proof = fc::ecc::range_proof_sign( 0, OutC1, OutB1, nonce, 0, 0, 40 );
      wdump( (proof.size()));

      auto result = fc::ecc::range_get_info( proof );
      wdump((result));
      FC_ASSERT( result.max_value >= 60 );
      FC_ASSERT( result.min_value >= 0 );


      auto B1 = fc::sha256::hash("B1");
      auto B2 = fc::sha256::hash("B2");
      auto b3 = fc::sha256::hash("b3");
      auto B4 = fc::sha256::hash("B4");
      auto C1 = fc::ecc::blind( B1, 1  );
      auto C2 = fc::ecc::blind( B2, 2  );
      auto c3 = fc::ecc::blind( b3, 3  );
      auto C4 = fc::ecc::blind( B4, -1 );

      auto B3 = fc::ecc::blind_sum( {B1,B2}, 2 );
      auto C3 = fc::ecc::blind( B3, 3 );


      auto B2m1 = fc::ecc::blind_sum( {B2,B1}, 1 );
      auto C2m1 = fc::ecc::blind( B2m1, 1 );

      FC_ASSERT( fc::ecc::verify_sum( {C1,C2}, {C3}, 0 ) );
      FC_ASSERT( fc::ecc::verify_sum( {C3}, {C1,C2}, 0 ) );
      FC_ASSERT( fc::ecc::verify_sum( {C3}, {C1,C2}, 0 ) );


      {
         auto B1 = fc::sha256::hash("B1");
         auto B2 = fc::sha256::hash("B2");
         auto B3 = fc::sha256::hash("B3");

         //secp256k1_scalar_get_b32((unsigned char*)&B1, (const secp256k1_scalar_t*)&B2);
         //B1 = fc::variant("b2e5da56ef9f2a34d3e22fd12634bc99261e95c87b9960bf94ed3d27b30").as<fc::sha256>();

         auto C1 = fc::ecc::blind( B1, INT64_MAX );
         auto C2 = fc::ecc::blind( B1, 0 );
         auto C3 = fc::ecc::blind( B1, 1 );

         FC_ASSERT( fc::ecc::verify_sum( {C2}, {C3}, -1 ) );
         FC_ASSERT( fc::ecc::verify_sum( {C1}, {C1}, 0 ) );
         FC_ASSERT( fc::ecc::verify_sum( {C2}, {C2}, 0 ) );
         FC_ASSERT( fc::ecc::verify_sum( {C3}, {C2}, 1 ) );
         FC_ASSERT( fc::ecc::verify_sum( {C1}, {C2}, INT64_MAX ) );
         FC_ASSERT( fc::ecc::verify_sum( {C2}, {C1}, -INT64_MAX ) );


      }


   }
   catch ( const fc::exception& e )
   {
      edump((e.to_detail_string()));
   }
   return 0;
}