/**
    *  Process all fees and update the asset records.
    */
   void transaction_evaluation_state::post_evaluate()
   { try {
      required_fees += asset(_current_state->calculate_data_fee(fc::raw::pack_size(trx)),0);
      for( auto fee : balance )
      {
         if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) );
         if( fee.second > 0 )
         {
            if( fee.first == 0 && fee.second < required_fees.amount )
               FC_CAPTURE_AND_THROW( insufficient_fee, (fee)(required_fees.amount) );

            auto asset_record = _current_state->get_asset_record( fee.first );
            if( !asset_record )
              FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) );

            asset_record->collected_fees += fee.second;
            asset_record->current_share_supply -= fee.second;
            _current_state->store_asset_record( *asset_record );
         }
      }
      for( auto required_deposit : required_deposits )
      {
         auto provided_itr = provided_deposits.find( required_deposit.first );
         
         if( provided_itr->second < required_deposit.second )
            FC_CAPTURE_AND_THROW( missing_deposit, (required_deposit) );
      }

   } FC_RETHROW_EXCEPTIONS( warn, "" ) }
Example #2
0
  /**
   *  A price will reorder the asset types such that the
   *  asset type with the lower enum value is always the
   *  denominator.  Therefore  bts/usd and  usd/bts will
   *  always result in a price measured in usd/bts because
   *  asset::bts <  asset::usd.
   */
  price operator / ( const asset& a, const asset& b )
  {
    try 
    {
        if( a.asset_id == b.asset_id )
           FC_CAPTURE_AND_THROW( asset_divide_by_self );

        price p;
        auto l = a; auto r = b;
        if( l.asset_id < r.asset_id ) { std::swap(l,r); }
        ilog( "${a} / ${b}", ("a",l)("b",r) );

        if( r.amount == 0 )
           FC_CAPTURE_AND_THROW( asset_divide_by_zero, (r) );

        p.base_asset_id = r.asset_id;
        p.quote_asset_id = l.asset_id;

        fc::bigint bl = l.amount;
        fc::bigint br = r.amount;
        fc::bigint result = (bl * fc::bigint(BTS_PRICE_PRECISION)) / br;

        p.ratio = result;
        return p;
    } FC_RETHROW_EXCEPTIONS( warn, "${a} / ${b}", ("a",a)("b",b) );
  }
   void fire_delegate_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
       auto delegate_record = eval_state._current_state->get_account_record( this->delegate_id );
       if( !delegate_record ) FC_CAPTURE_AND_THROW( unknown_account_id, (delegate_id) );
       if( !delegate_record->is_delegate() ) FC_CAPTURE_AND_THROW( not_a_delegate, (delegate_record) );

       switch( (fire_delegate_operation::reason_type)this->reason )
       {
          case fire_delegate_operation::multiple_blocks_signed:
          {
             auto proof = fc::raw::unpack<multiple_block_proof>( this->data );

             FC_ASSERT( proof.first.id() != proof.second.id() );
             FC_ASSERT( proof.first.timestamp == proof.second.timestamp )
             FC_ASSERT( proof.first.signee() == proof.second.signee() )
             FC_ASSERT( proof.first.validate_signee( delegate_record->active_key() ) );

             // then fire the delegate
             // this maintains the invariant of total votes == total shares
             delegate_record->adjust_votes_against( delegate_record->votes_for() );
             delegate_record->adjust_votes_for( -delegate_record->votes_for() );
             eval_state._current_state->store_account_record( *delegate_record );
             break;
          }
          case fire_delegate_operation::invalid_testimony:
          {
             auto testimony = fc::raw::unpack<signed_delegate_testimony>( this->data );

             bool is_delegates_key = false;
             auto signee = testimony.signee();
             for( auto key : delegate_record->active_key_history )
             {
                if( signee == key.second )
                {
                   is_delegates_key = true;
                   break;
                }
             }
             if( !is_delegates_key ) FC_CAPTURE_AND_THROW( not_a_delegate_signature, (signee)(delegate_record) );

             auto trx_loc = eval_state._current_state->get_transaction( testimony.transaction_id );
             // delegate said it was valid, but it is invalid
             if( !trx_loc && testimony.valid )
             {
                delegate_record->adjust_votes_against( delegate_record->votes_for() );
                delegate_record->adjust_votes_for( -delegate_record->votes_for() );
                eval_state._current_state->store_account_record( *delegate_record );
             }
             else
             {
                FC_CAPTURE_AND_THROW( invalid_fire_operation );
             }
             break;
          }
       }
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
Example #4
0
   double chain_interface::to_pretty_price_double( const price& price_to_pretty_print )const
   {
      auto obase_asset = get_asset_record( price_to_pretty_print.base_asset_id );
      if( !obase_asset ) FC_CAPTURE_AND_THROW( unknown_asset_id, (price_to_pretty_print.base_asset_id) );

      auto oquote_asset = get_asset_record( price_to_pretty_print.quote_asset_id );
      if( !oquote_asset ) FC_CAPTURE_AND_THROW( unknown_asset_id, (price_to_pretty_print.quote_asset_id) );

      return fc::variant(string(price_to_pretty_print.ratio * obase_asset->get_precision() / oquote_asset->get_precision())).as_double() / (BTS_BLOCKCHAIN_MAX_SHARES*1000);
   }
   /**
    *  Process all fees and update the asset records.
    */
   void transaction_evaluation_state::post_evaluate()
   { try {
      for( const auto& item : withdraws )
      {
         auto asset_rec = _current_state->get_asset_record( item.first );
         if( !asset_rec.valid() ) FC_CAPTURE_AND_THROW( unknown_asset_id, (item) );
         if( asset_rec->id > 0 && asset_rec->is_market_issued() && asset_rec->transaction_fee > 0 )
         {
            sub_balance( address(), asset(asset_rec->transaction_fee, asset_rec->id) );
         }
      }

      balance[0]; // make sure we have something for this.
      for( const auto& fee : balance )
      {
         if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) );
         // if the fee is already in XTS or the fee balance is zero, move along...
         if( fee.first == 0 || fee.second == 0 )
           continue;

         auto asset_record = _current_state->get_asset_record( fee.first );
         if( !asset_record.valid() ) FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) );

         if( !asset_record->is_market_issued() )
           continue;

         // lowest ask is someone with XTS offered at a price of USD / XTS, fee.first
         // is an amount of USD which can be converted to price*USD XTS provided we
         // send lowest_ask.index.owner the USD
         oprice median_price = _current_state->get_median_delegate_price( fee.first, asset_id_type( 0 ) );
         if( median_price )
         {
            // fees paid in something other than XTS are discounted 50%
            alt_fees_paid += asset( (fee.second*2)/3, fee.first ) * *median_price;
         }
      }

      for( const auto& fee : balance )
      {
         if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) );
         if( fee.second > 0 ) // if a fee was paid...
         {
            auto asset_record = _current_state->get_asset_record( fee.first );
            if( !asset_record )
              FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) );

            asset_record->collected_fees += fee.second;
            _current_state->store_asset_record( *asset_record );
         }
      }
   } FC_RETHROW_EXCEPTIONS( warn, "" ) }
   /**
    *  Process all fees and update the asset records.
    */
   void transaction_evaluation_state::post_evaluate()
   { try {
      // Should this be here? We may not have fees in XTS now...
      balance[0]; // make sure we have something for this.
      for( const auto& fee : balance )
      {
         if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) );
         // if the fee is already in XTS or the fee balance is zero, move along...
         if( fee.first == 0 || fee.second == 0 )
           continue;

         auto asset_record = _current_state->get_asset_record( fee.first );
         if( !asset_record.valid() ) FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) );

         if( !asset_record->is_market_issued() )
           continue;

         // lowest ask is someone with XTS offered at a price of USD / XTS, fee.first
         // is an amount of USD which can be converted to price*USD XTS provided we
         // send lowest_ask.index.owner the USD
         oprice median_price = _current_state->get_median_delegate_price( fee.first );
         if( median_price )
         {
            // fees paid in something other than XTS are discounted 50%
            alt_fees_paid += asset( (fee.second*2)/3, fee.first ) * *median_price;
         }
      }

      for( const auto& fee : balance )
      {
         if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) );
         if( fee.second > 0 ) // if a fee was paid...
         {
            auto asset_record = _current_state->get_asset_record( fee.first );
            if( !asset_record )
              FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) );

            asset_record->collected_fees += fee.second;
            _current_state->store_asset_record( *asset_record );
         }
      }

      for( const auto& required_deposit : required_deposits )
      {
         auto provided_itr = provided_deposits.find( required_deposit.first );

         if( provided_itr->second < required_deposit.second )
            FC_CAPTURE_AND_THROW( missing_deposit, (required_deposit) );
      }

   } FC_RETHROW_EXCEPTIONS( warn, "" ) }
   void transaction_evaluation_state::evaluate( const signed_transaction& trx_arg, bool skip_signature_check, bool enforce_canonical )
   { try {
      _skip_signature_check = skip_signature_check;
      try {
        if( _current_state->now() >= trx_arg.expiration )
        {
           if( _current_state->now() > trx_arg.expiration || _current_state->get_head_block_num() >= BTS_V0_4_21_FORK_BLOCK_NUM )
           {
               const auto expired_by_sec = (_current_state->now() - trx_arg.expiration).to_seconds();
               FC_CAPTURE_AND_THROW( expired_transaction, (trx_arg)(_current_state->now())(expired_by_sec) );
           }
        }
        if( (_current_state->now() + BTS_BLOCKCHAIN_MAX_TRANSACTION_EXPIRATION_SEC) < trx_arg.expiration )
           FC_CAPTURE_AND_THROW( invalid_transaction_expiration, (trx_arg)(_current_state->now()) );

        auto trx_id = trx_arg.id();

        if( _current_state->is_known_transaction( trx_arg.expiration, trx_arg.digest( _chain_id ) ) )
          if (_current_state->get_head_block_num() >= FORK_25)
            FC_CAPTURE_AND_THROW( duplicate_transaction, (trx_id) );

        trx = trx_arg;
        if( !_skip_signature_check )
        {
           auto digest = trx_arg.digest( _chain_id );
           for( const auto& sig : trx.signatures )
           {
              auto key = fc::ecc::public_key( sig, digest, enforce_canonical ).serialize();
              signed_keys.insert( address(key) );
              signed_keys.insert( address(pts_address(key,false,56) ) );
              signed_keys.insert( address(pts_address(key,true,56) )  );
              signed_keys.insert( address(pts_address(key,false,0) )  );
              signed_keys.insert( address(pts_address(key,true,0) )   );
           }
        }
        current_op_index = 0;
        for( const auto& op : trx.operations )
        {
           evaluate_operation( op );
           ++current_op_index;
        }
        post_evaluate();
        validate_required_fee();
        update_delegate_votes();
      }
      catch ( const fc::exception& e )
      {
         validation_error = e;
         throw;
      }
   } FC_RETHROW_EXCEPTIONS( warn, "", ("trx",trx_arg) ) }
   void transaction_evaluation_state::evaluate( const signed_transaction& trx_arg, bool skip_signature_check )
   { try {
      reset();
      _skip_signature_check = skip_signature_check;
      try {
        if( trx_arg.expiration < _current_state->now() )
        {
           auto expired_by_sec = (trx_arg.expiration - _current_state->now()).to_seconds();
           FC_CAPTURE_AND_THROW( expired_transaction, (trx_arg)(_current_state->now())(expired_by_sec) );
        }
        if( trx_arg.expiration > (_current_state->now() + BTS_BLOCKCHAIN_MAX_TRANSACTION_EXPIRATION_SEC) )
           FC_CAPTURE_AND_THROW( invalid_transaction_expiration, (trx_arg)(_current_state->now()) );

        auto trx_size = fc::raw::pack_size(trx_arg);
        if(  trx_size > BTS_BLOCKCHAIN_MAX_TRANSACTION_SIZE )
           FC_CAPTURE_AND_THROW( oversized_transaction, (trx_size ) );
       
        auto trx_id = trx_arg.id();

        if( _current_state->is_known_transaction( trx_id ) )
           FC_CAPTURE_AND_THROW( duplicate_transaction, (trx_id) );
       
        trx = trx_arg;
        if( !_skip_signature_check )
        {
           auto digest = trx_arg.digest( _chain_id );
           for( auto sig : trx.signatures )
           {
              auto key = fc::ecc::public_key( sig, digest ).serialize();
              signed_keys.insert( address(key) );
              signed_keys.insert( address(pts_address(key,false,56) ) );
              signed_keys.insert( address(pts_address(key,true,56) )  );
              signed_keys.insert( address(pts_address(key,false,0) )  );
              signed_keys.insert( address(pts_address(key,true,0) )   );
           }
        }
        for( auto op : trx.operations )
        {
           evaluate_operation( op );
        }
        post_evaluate();
        validate_required_fee();
        update_delegate_votes();
      } 
      catch ( const fc::exception& e )
      {
         validation_error = e;
         throw;
      }
   } FC_RETHROW_EXCEPTIONS( warn, "", ("trx",trx_arg) ) }
Example #9
0
   string chain_interface::to_pretty_price( const price& price_to_pretty_print )const
   { try {
      auto obase_asset = get_asset_record( price_to_pretty_print.base_asset_id );
      if( !obase_asset ) FC_CAPTURE_AND_THROW( unknown_asset_id, (price_to_pretty_print.base_asset_id) );

      auto oquote_asset = get_asset_record( price_to_pretty_print.quote_asset_id );
      if( !oquote_asset ) FC_CAPTURE_AND_THROW( unknown_asset_id, (price_to_pretty_print.quote_asset_id) );

      auto tmp = price_to_pretty_print;
      tmp.ratio *= obase_asset->get_precision();
      tmp.ratio /= oquote_asset->get_precision();

      return tmp.ratio_string() + " " + oquote_asset->symbol + " / " + obase_asset->symbol;

   } FC_CAPTURE_AND_RETHROW( (price_to_pretty_print) ) }
Example #10
0
 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) ) }
Example #11
0
   asset chain_interface::to_ugly_asset(const std::string& amount, const std::string& symbol) const
   { try {
      auto record = get_asset_record( symbol );
      if( !record ) FC_CAPTURE_AND_THROW( unknown_asset_symbol, (symbol) );

      auto decimal = amount.find(".");
      if( decimal == string::npos )
         return asset(atoll(amount.c_str()) * record->precision, record->id);

      share_type whole = atoll(amount.substr(0, decimal).c_str()) * record->precision;
      string fraction_string = amount.substr(decimal+1);
      share_type fraction = atoll(fraction_string.c_str());

      if( fraction_string.empty() || fraction <= 0 )
         return asset(whole, record->id);

      while( fraction < record->precision )
         fraction *= 10;
      while( fraction > record->precision )
         fraction /= 10;
      while( fraction_string.size() && fraction_string[0] == '0')
      {
         fraction /= 10;
         fraction_string.erase(0, 1);
      }
      return asset(whole > 0? whole + fraction : whole - fraction, record->id);
   } FC_CAPTURE_AND_RETHROW( (amount)(symbol) ) }
 void validate_market(asset_id_type quote, asset_id_type base)
 {
    if( quote < base )
    {
       // force user to submit an ask rather than a bid
       FC_CAPTURE_AND_THROW( invalid_market, (quote)(base) );
    }
 }
Example #13
0
 void ad_operation::evaluate( transaction_evaluation_state& eval_state )const
 { try {
     if( this->amount.amount <= 0 )
         FC_CAPTURE_AND_THROW( negative_deposit, (amount) );
     
     FC_ASSERT( !message.empty() );
     
     FC_ASSERT( amount.asset_id == 0 );
     
     const size_t message_kb = (message.size() / 1024) + 1;
     const share_type required_fee = message_kb * BTS_BLOCKCHAIN_MIN_AD_FEE;
     
     FC_ASSERT( amount.amount >= required_fee, "Message of size ${s} KiB requires at least ${a} satoshis to be pay!",
               ("s",message_kb)("a",required_fee) );
     // half of the note fees goto collected fees(delegate pay), other go to ad owner
     eval_state.min_fees[amount.asset_id] += required_fee;
     
     FC_ASSERT( owner_account_id != 0 );
     const oaccount_record owner_account_rec = eval_state.pending_state()->get_account_record( abs( this->owner_account_id ) );
     FC_ASSERT( owner_account_rec.valid() );
     
     auto owner_address = owner_account_rec->active_address();
     auto ad_income_balance = eval_state.pending_state()->get_balance_record(withdraw_condition( withdraw_with_signature(owner_address), 0 ).get_address());
     if( !ad_income_balance )
         ad_income_balance = balance_record( owner_address, asset(0, 0), 0 );
     
     auto ad_pay = amount.amount - required_fee;
     ad_income_balance->balance += ad_pay;
     ad_income_balance->last_update = eval_state.pending_state()->now();
     ad_income_balance->deposit_date = eval_state.pending_state()->now();
     
     eval_state.pending_state()->store_balance_record( *ad_income_balance );
     
     eval_state.sub_balance( asset(ad_pay, amount.asset_id) );
     
     // checking the signature of the publisher.
     FC_ASSERT( publisher_account_id != 0 );
     const oaccount_record publisher_account_rec = eval_state.pending_state()->get_account_record( abs( this->publisher_account_id ) );
     FC_ASSERT( publisher_account_rec.valid() );
     
     eval_state.check_signature( publisher_account_rec->active_key() );
     
     ad_record record;
     record.index.account_id = owner_account_id;
     record.index.transaction_id = eval_state.trx.id();
     record.publisher_id = publisher_account_id;
     record.amount = amount;
     record.message = message;
     record.signer = message_signature;
     
     // the message must be signed by the claimed publisher account
     FC_ASSERT( publisher_account_rec->active_key() == record.signer_key() );
     
     FC_ASSERT( !eval_state.pending_state()->get_ad_record( record.index ).valid() );
     
     eval_state.pending_state()->store_ad_record( std::move( record ) );
 } FC_CAPTURE_AND_RETHROW( (*this) ) }
 void transaction_evaluation_state::validate_required_fee()
 { try {
    share_type required_fee = _current_state->calculate_data_fee( trx.data_size() );
    auto fee_itr = balance.find( 0 );
    if( fee_itr == balance.end() ||
        fee_itr->second < required_fee ) 
    {
       FC_CAPTURE_AND_THROW( insufficient_fee, (required_fee) );
    }
 } FC_RETHROW_EXCEPTIONS( warn, "" ) }
Example #15
0
   void peer_connection::read_loop()
   {
      ilog( "read loop" );
      try 
      {
         auto one_time_key = fc::ecc::private_key::generate();
         fc::ecc::public_key pub = one_time_key.get_public_key();
         auto s = pub.serialize();

         _socket.write( (char*)&s, sizeof(s) );

         fc::ecc::public_key_data remote_one_time_key;
         _socket.read( (char*)&remote_one_time_key, sizeof(remote_one_time_key) );

         _shared_secret = one_time_key.get_shared_secret( remote_one_time_key );

         elog( "${ss}", ("ss",_shared_secret) );

         if( _send_queue.size() && !_send_queue_complete.valid() )
            _send_queue_complete = fc::async( [=](){ process_send_queue(); } );

         message next_message;
         next_message.data.resize( BTS_NETWORK_MAX_MESSAGE_SIZE );

         while( !_read_loop.canceled() )
         {
            // read a message
            _socket.read( (char*)&next_message.size, sizeof(next_message.size) );
            wlog( "                                                      read message of size ${s} ", ("s", next_message.size) );

            if( next_message.size > BTS_NETWORK_MAX_MESSAGE_SIZE )
            {
               send_message( goodbye_message( message_too_large() ) ); 
               _socket.close();
               FC_CAPTURE_AND_THROW( message_too_large, (next_message.size) );
            }

            _socket.read( (char*)&next_message.type, sizeof(next_message.type) );
            wlog( "                     read message of size ${s}   type ${t}", ("s", next_message.size)("t",int(next_message.type)) );
            _socket.read( next_message.data.data(), next_message.size );
            wlog( "read body of message" );

            received_message( shared_from_this(), next_message );
         }
      } 
      catch ( const fc::exception& e )
      {
         ilog( "closed: ${e}", ("e", e.to_detail_string()) );
         connection_closed( shared_from_this(), e );
         return;
      }
      ilog( "closed!" );
      connection_closed( shared_from_this(), optional<fc::exception>() );
   }
 void transaction_evaluation_state::adjust_vote( slate_id_type slate_id, share_type amount )
 {
    if( slate_id )
    {
       auto slate = _current_state->get_delegate_slate( slate_id );
       if( !slate ) FC_CAPTURE_AND_THROW( unknown_delegate_slate, (slate_id) );
       for( auto delegate_id : slate->supported_delegates )
       {
          net_delegate_votes[delegate_id].votes_for += amount;
       }
    }
 }
   void transaction_evaluation_state::validate_required_fee()
   { try {
      asset xts_fees;
      auto fee_itr = balance.find( 0 );
      if( fee_itr != balance.end() ) xts_fees += asset( fee_itr->second, 0);

      xts_fees += alt_fees_paid;

      if( required_fees > xts_fees )
      {
         FC_CAPTURE_AND_THROW( insufficient_fee, (required_fees)(alt_fees_paid)(xts_fees)  );
      }
   } FC_RETHROW_EXCEPTIONS( warn, "" ) }
Example #18
0
   void add_collateral_operation::evaluate( transaction_evaluation_state& eval_state )
   {
        FC_ASSERT(!"Not implemented for this DAC.\n");
      if( this->cover_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (cover_index.order_price) );

      if( this->amount == 0 ) 
         FC_CAPTURE_AND_THROW( zero_amount );

      if( this->amount < 0 ) 
         FC_CAPTURE_AND_THROW( negative_deposit );

      asset delta_amount  = this->get_amount();
      eval_state.sub_balance( address(), delta_amount );

      // update collateral and call price
      auto current_cover   = eval_state._current_state->get_collateral_record( this->cover_index );
      if( NOT current_cover )
         FC_CAPTURE_AND_THROW( unknown_market_order, (cover_index) );

      current_cover->collateral_balance += delta_amount.amount;

      // changing the payoff balance changes the call price... so we need to remove the old record
      // and insert a new one.
      eval_state._current_state->store_collateral_record( this->cover_index, collateral_record() ); 

      auto new_call_price = asset(current_cover->payoff_balance, delta_amount.asset_id) /
                            asset((current_cover->collateral_balance*3)/4, 0);

      eval_state._current_state->store_collateral_record( market_index_key( new_call_price, this->cover_index.owner),
                                                          *current_cover );

      auto market_stat = eval_state._current_state->get_market_status( cover_index.order_price.quote_asset_id, cover_index.order_price.base_asset_id );
      FC_ASSERT( market_stat, "this should be valid for there to even be a position to cover" );
      market_stat->ask_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );
   }
 void transaction_evaluation_state::evaluate( const signed_transaction& trx_arg )
 { try {
    reset();
    try {
      if( trx_arg.expiration && *trx_arg.expiration < _current_state->now() )
         FC_CAPTURE_AND_THROW( expired_transaction, (trx_arg)(_current_state->now()) );
     
      auto trx_id = trx_arg.id();
      ilog( "id: ${id}", ("id",trx_id) );
      otransaction_record known_transaction= _current_state->get_transaction( trx_id );
      if( known_transaction )
         FC_CAPTURE_AND_THROW( duplicate_transaction, (known_transaction) );
     
      trx = trx_arg;
      auto digest = trx_arg.digest( _chain_id );
      for( auto sig : trx.signatures )
      {
         auto key = fc::ecc::public_key( sig, digest ).serialize();
         signed_keys.insert( address(key) );
         signed_keys.insert( address(pts_address(key,false,56) ) );
         signed_keys.insert( address(pts_address(key,true,56) )  );
         signed_keys.insert( address(pts_address(key,false,0) )  );
         signed_keys.insert( address(pts_address(key,true,0) )   );
      }
      for( auto op : trx.operations )
      {
         evaluate_operation( op );
      }
      post_evaluate();
      validate_required_fee();
      update_delegate_votes();
    } 
    catch ( const fc::exception& e )
    {
       validation_error = e;
       throw;
    }
 } FC_RETHROW_EXCEPTIONS( warn, "", ("trx",trx_arg) ) }
Example #20
0
    void note_operation::evaluate( transaction_evaluation_state& eval_state )const
    { try {
#ifndef WIN32
#warning [HARDFORK] Remove this check after PLS_V0_1_0_FORK_BLOCK_NUM has passed
#endif
        FC_ASSERT( eval_state.pending_state()->get_head_block_num() >= PLS_V0_1_0_FORK_BLOCK_NUM );

        if( this->amount.amount <= 0 )
            FC_CAPTURE_AND_THROW( negative_deposit, (amount) );
        
        FC_ASSERT( !message->data.empty() );
        FC_ASSERT( amount.asset_id == 0 );
        
        const size_t message_kb = (message->data.size() / 1024) + 1;
        const share_type required_fee = message_kb * BTS_BLOCKCHAIN_MIN_NOTE_FEE;
        
        FC_ASSERT( amount.amount >= required_fee, "Message of size ${s} KiB requires at least ${a} satoshis to be burned!",
                  ("s",message_kb)("a",required_fee) );
        
        // 30% of the note fees goto collected fees(delegate pay), other go to the operation pool
        eval_state.min_fees[amount.asset_id] += amount.amount * 3 / 10;
        
        // TODO: instead of burn, the left will go to a fee pool attached to this operation.
        auto op_reward_record = eval_state.pending_state()->get_operation_reward_record(note_op_type);
        auto reward_fee = amount.amount - amount.amount * 3 / 10;
        op_reward_record->fees[amount.asset_id] += reward_fee;
        eval_state.sub_balance( asset(reward_fee, amount.asset_id) );
        eval_state.pending_state()->store_operation_reward_record( *op_reward_record );
        
        // the transaction check the signature of the owner
        const oaccount_record account_rec = eval_state.pending_state()->get_account_record( abs( this->owner_account_id ) );
        FC_ASSERT( account_rec.valid() );
        
        eval_state.check_signature( account_rec->active_key() );
        
        note_record record;
        record.index.account_id = owner_account_id;
        record.index.transaction_id = eval_state.trx.id();
        record.amount = amount;
        record.message = message;
        record.signer = message_signature;
        
        // verify the signature of the message, the message signer must be the account_id's active key
        FC_ASSERT( account_rec->active_key() == record.signer_key() );
        
        FC_ASSERT( !eval_state.pending_state()->get_note_record( record.index ).valid() );
        
        eval_state.pending_state()->store_note_record( std::move( record ) );
    } FC_CAPTURE_AND_RETHROW( (*this) ) }
 void transaction_evaluation_state::adjust_vote( slate_id_type slate_id, share_type amount )
 {
    if( slate_id )
    {
       auto slate = _current_state->get_delegate_slate( slate_id );
       if( !slate ) FC_CAPTURE_AND_THROW( unknown_delegate_slate, (slate_id) );
       for( const auto& delegate_id : slate->supported_delegates )
       {
          if( BTS_BLOCKCHAIN_ENABLE_NEGATIVE_VOTES && delegate_id < signed_int(0) )
             net_delegate_votes[abs(delegate_id)].votes_for -= amount;
          else
             net_delegate_votes[abs(delegate_id)].votes_for += amount;
       }
    }
 }
Example #22
0
   void burn_operation::evaluate( transaction_evaluation_state& eval_state )const
   { try {
      if( this->amount.amount <= 0 )
         FC_CAPTURE_AND_THROW( negative_deposit, (amount) );

      if( !message.empty() )
          FC_ASSERT( amount.asset_id == 0 );

      if( amount.asset_id == 0 )
      {
          const size_t message_kb = (message.size() / 1024) + 1;
          const share_type required_fee = message_kb * BTS_BLOCKCHAIN_MIN_BURN_FEE;

          FC_ASSERT( amount.amount >= required_fee, "Message of size ${s} KiB requires at least ${a} satoshis to be burned!",
                     ("s",message_kb)("a",required_fee) );
      }

      oasset_record asset_rec = eval_state.pending_state()->get_asset_record( amount.asset_id );
      FC_ASSERT( asset_rec.valid() );
      FC_ASSERT( !asset_rec->is_market_issued() );

      asset_rec->current_supply -= this->amount.amount;
      eval_state.sub_balance( this->amount );

      eval_state.pending_state()->store_asset_record( *asset_rec );

      if( account_id != 0 ) // you can offer burnt offerings to God if you like... otherwise it must be an account
      {
          const oaccount_record account_rec = eval_state.pending_state()->get_account_record( abs( this->account_id ) );
          FC_ASSERT( account_rec.valid() );
      }

      burn_record record;
      record.index.account_id = account_id;
      record.index.transaction_id = eval_state.trx.id();
      record.amount = amount;
      record.message = message;
      record.signer = message_signature;

      FC_ASSERT( !eval_state.pending_state()->get_burn_record( record.index ).valid() );

      eval_state.pending_state()->store_burn_record( std::move( record ) );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
Example #23
0
   void release_escrow_operation::evaluate( transaction_evaluation_state& eval_state )const
   { try {
      auto escrow_balance_record = eval_state.pending_state()->get_balance_record( this->escrow_id );
      FC_ASSERT( escrow_balance_record.valid() );

      if( this->amount_to_receiver < 0 )
         FC_CAPTURE_AND_THROW( negative_withdraw, (amount_to_receiver) );

      if( this->amount_to_sender < 0 )
         FC_CAPTURE_AND_THROW( negative_withdraw, (amount_to_sender) );

      if( !eval_state.check_signature( this->released_by ) )
         FC_ASSERT( false, "transaction not signed by releasor" );

      auto escrow_condition = escrow_balance_record->condition.as<withdraw_with_escrow>();
      auto total_released = uint64_t(amount_to_sender) + uint64_t(amount_to_receiver);

      FC_ASSERT( total_released <= escrow_balance_record->balance );
      FC_ASSERT( total_released >= amount_to_sender ); // check for addition overflow
      FC_ASSERT( total_released >= amount_to_receiver ); // check for addition overflow

      escrow_balance_record->balance -= total_released;
      auto asset_rec = eval_state.pending_state()->get_asset_record( escrow_balance_record->condition.asset_id );

      if( amount_to_sender > 0 )
          FC_ASSERT( asset_rec->address_is_whitelisted( escrow_condition.sender ) );
      if( amount_to_receiver > 0 )
          FC_ASSERT( asset_rec->address_is_whitelisted( escrow_condition.receiver ) );

      const bool authority_is_retracting = asset_rec->flag_is_active( asset_record::retractable_balances )
                                           && eval_state.verify_authority( asset_rec->authority );

      if( escrow_condition.sender == this->released_by )
      {
         FC_ASSERT( amount_to_sender == 0 );
         FC_ASSERT( amount_to_receiver <= escrow_balance_record->balance );

         if( !eval_state.check_signature( escrow_condition.sender ) && !authority_is_retracting)
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.sender) );

         balance_record new_balance_record( escrow_condition.receiver,
                                            asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                            escrow_balance_record->slate_id() );
         auto current_receiver_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

         if( current_receiver_balance )
            current_receiver_balance->balance += amount_to_receiver;
         else
            current_receiver_balance = new_balance_record;

          eval_state.pending_state()->store_balance_record( *current_receiver_balance );
      }
      else if( escrow_condition.receiver == this->released_by )
      {
         FC_ASSERT( amount_to_receiver == 0 );
         FC_ASSERT( amount_to_sender <= escrow_balance_record->balance );

         if( !eval_state.check_signature( escrow_condition.receiver ) && !authority_is_retracting)
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.receiver) );

         balance_record new_balance_record( escrow_condition.sender,
                                            asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                            escrow_balance_record->slate_id() );
         auto current_sender_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

         if( current_sender_balance )
            current_sender_balance->balance += amount_to_sender;
         else
            current_sender_balance = new_balance_record;

         eval_state.pending_state()->store_balance_record( *current_sender_balance );
      }
      else if( escrow_condition.escrow == this->released_by )
      {
         if( !eval_state.check_signature( escrow_condition.escrow ) && !authority_is_retracting )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.escrow) );
         // get a balance record for the receiver, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.receiver,
                                               asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_receiver_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

            if( current_receiver_balance )
               current_receiver_balance->balance += amount_to_receiver;
            else
               current_receiver_balance = new_balance_record;
            eval_state.pending_state()->store_balance_record( *current_receiver_balance );
         }
         //  get a balance record for the sender, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.sender,
                                               asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_sender_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

            if( current_sender_balance )
               current_sender_balance->balance += amount_to_sender;
            else
               current_sender_balance = new_balance_record;
            eval_state.pending_state()->store_balance_record( *current_sender_balance );
         }
      }
      else if( address() == this->released_by )
      {
         if( !eval_state.check_signature( escrow_condition.sender ) && !authority_is_retracting)
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.sender) );
         if( !eval_state.check_signature( escrow_condition.receiver ) && !authority_is_retracting)
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.receiver) );
         // get a balance record for the receiver, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.receiver,
                                               asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_receiver_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

            if( current_receiver_balance )
               current_receiver_balance->balance += amount_to_receiver;
            else
               current_receiver_balance = new_balance_record;
            eval_state.pending_state()->store_balance_record( *current_receiver_balance );
         }
         //  get a balance record for the sender, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.sender,
                                               asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_sender_balance = eval_state.pending_state()->get_balance_record( new_balance_record.id());

            if( current_sender_balance )
               current_sender_balance->balance += amount_to_sender;
            else
               current_sender_balance = new_balance_record;
            eval_state.pending_state()->store_balance_record( *current_sender_balance );
         }
      }
      else
      {
          FC_ASSERT( false, "not released by a party to the escrow transaction" );
      }

      eval_state.pending_state()->store_balance_record( *escrow_balance_record );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
                  void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp )
                  {
                     try {
                         _quote_id = quote_id;
                         _base_id = base_id;
                         auto quote_asset = _pending_state->get_asset_record( _quote_id );

                         // DISABLE MARKET ISSUED ASSETS
                         if( quote_asset->is_market_issued() )
                            return; // don't execute anything.

                         // the order book is soreted 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
                         auto 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( next_pair ) );
                         _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( _short_itr.valid() ) --_short_itr;
                         else _short_itr = _db_impl._short_db.last();

                         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 consumed_bid_depth(0,base_id);
                         asset consumed_ask_depth(0,base_id);


                         asset usd_fees_collected(0,quote_id);
                         asset trading_volume(0, base_id);

                         omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id );
                         if( !market_stat.valid() )
                         {
                            if( quote_asset->is_market_issued() ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) );
                            FC_ASSERT( market_stat.valid() );
                         }

                         while( get_next_bid() && get_next_ask() )
                         {
                            idump( (_current_bid)(_current_ask) );
                            price ask_price = _current_ask->get_price();
                            // this works for bids, asks, and shorts.... but in the case of a cover
                            // the current ask can go lower than the call price in order to match
                            // the bid....
                            if( _current_ask->type == cover_order )
                            {
                               ask_price = std::min( _current_bid->get_price(), _current_ask->get_highest_cover_price() );
                            }

                            if( _current_bid->get_price() < ask_price )
                               break;

                            if( quote_asset->is_market_issued() )
                            {
                               if( !market_stat ||
                                   market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ||
                                   market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2
                                 )
                                 FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) );
                            }

                            auto quantity = std::min( _current_bid->get_quantity(), _current_ask->get_quantity() );

                            auto usd_paid_by_bid     = quantity * _current_bid->get_price();
                            auto usd_received_by_ask = quantity * _current_ask->get_price();
                            auto xts_paid_by_ask     = quantity;
                            auto xts_received_by_bid = quantity;

                            consumed_bid_depth += quantity;
                            consumed_ask_depth += quantity;

                            if( _current_bid->type == short_order )
                            {
                               usd_paid_by_bid = usd_received_by_ask;
                            }

                            if( _current_ask->type == cover_order )
                            {
                                usd_received_by_ask = usd_paid_by_bid;
                            }

                            FC_ASSERT( usd_paid_by_bid.amount >= 0 );
                            FC_ASSERT( xts_paid_by_ask.amount >= 0 );
                            FC_ASSERT( usd_received_by_ask.amount >= 0 );
                            FC_ASSERT( xts_received_by_bid.amount >= 0 );
                            FC_ASSERT( usd_paid_by_bid >= usd_received_by_ask );
                            FC_ASSERT( xts_paid_by_ask >= xts_received_by_bid );

                            // sanity check to keep supply from growing without bound
                            FC_ASSERT( usd_paid_by_bid < asset(quote_asset->maximum_share_supply,quote_id), "", ("usd_paid_by_bid",usd_paid_by_bid)("asset",quote_asset) );

                            usd_fees_collected += usd_paid_by_bid - usd_received_by_ask;
                            idump( (usd_fees_collected)(xts_paid_by_ask)(xts_received_by_bid)(quantity) );

                            market_transaction mtrx;
                            mtrx.bid_owner       = _current_bid->get_owner();
                            mtrx.ask_owner       = _current_ask->get_owner();
                            mtrx.bid_price       = _current_bid->get_price();
                            mtrx.ask_price       = ask_price;
                            mtrx.bid_paid        = usd_paid_by_bid;
                            mtrx.bid_received    = xts_received_by_bid;
                            mtrx.ask_paid        = xts_paid_by_ask;
                            mtrx.ask_received    = usd_received_by_ask;
                            mtrx.bid_type        = _current_bid->type;
                            mtrx.fees_collected  = xts_paid_by_ask - xts_received_by_bid;

                            _market_transactions.push_back(mtrx);
                            trading_volume += mtrx.bid_received;

                            market_stat->ask_depth -= xts_paid_by_ask.amount;
                            if( _current_ask->type == ask_order )
                            {
                               /* rounding errors on price cause this not to go to 0 in some cases */
                               if( quantity == _current_ask->get_quantity() )
                                  _current_ask->state.balance = 0;
                               else
                                  _current_ask->state.balance -= xts_paid_by_ask.amount;

                               FC_ASSERT( _current_ask->state.balance >= 0 );

                               auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), quote_id ).get_address();
                               auto ask_payout = _pending_state->get_balance_record( ask_balance_address );
                               if( !ask_payout )
                                  ask_payout = balance_record( _current_ask->get_owner(), asset(0,quote_id), 0 );
                               ask_payout->balance += usd_received_by_ask.amount;
                               ask_payout->last_update = _pending_state->now();

                               _pending_state->store_balance_record( *ask_payout );
                               _pending_state->store_ask_record( _current_ask->market_index, _current_ask->state );
                            }
                            else if( _current_ask->type == cover_order )
                            {
                               elog( "MATCHING COVER ORDER recv_usd: ${usd}  paid_collat: ${c}",
                                     ("usd",usd_received_by_ask)("c",xts_paid_by_ask) );
                               wlog( "current ask: ${c}", ("c",_current_ask) );
                               // we are in the margin call range...
                               _current_ask->state.balance  -= usd_received_by_ask.amount;
                               *(_current_ask->collateral)  -= xts_paid_by_ask.amount;

                               FC_ASSERT( _current_ask->state.balance >= 0 );
                               FC_ASSERT( *_current_ask->collateral >= 0 );

                               if( _current_ask->state.balance == 0 ) // no more USD left
                               { // send collateral home to mommy & daddy
                                     wlog( "            collateral balance is now 0!" );
                                     auto ask_balance_address = withdraw_condition(
                                                                       withdraw_with_signature(_current_ask->get_owner()),
                                                                       base_id ).get_address();

                                     auto ask_payout = _pending_state->get_balance_record( ask_balance_address );
                                     if( !ask_payout )
                                        ask_payout = balance_record( _current_ask->get_owner(), asset(0,base_id), 0 );
                                     ask_payout->balance += (*_current_ask->collateral);
                                     ask_payout->last_update = _pending_state->now();

                                     _pending_state->store_balance_record( *ask_payout );
                                     _current_ask->collateral = 0;

                               }
                               wlog( "storing collateral ${c}", ("c",_current_ask) );
                               _pending_state->store_collateral_record( _current_ask->market_index,
                                                                        collateral_record( *_current_ask->collateral,
                                                                                           _current_ask->state.balance ) );
                            }

                            if( _current_bid->type == bid_order )
                            {
                               _current_bid->state.balance -= usd_paid_by_bid.amount;
                               FC_ASSERT( _current_bid->state.balance >= 0 );

                               auto bid_payout = _pending_state->get_balance_record(
                                                         withdraw_condition( withdraw_with_signature(_current_bid->get_owner()), base_id ).get_address() );
                               if( !bid_payout )
                                  bid_payout = balance_record( _current_bid->get_owner(), asset(0,base_id), 0 );
                               bid_payout->balance += xts_received_by_bid.amount;
                               bid_payout->last_update = _pending_state->now();
                               _pending_state->store_balance_record( *bid_payout );
                               _pending_state->store_bid_record( _current_bid->market_index, _current_bid->state );

                            }
                            else if( _current_bid->type == short_order )
                            {
                               market_stat->bid_depth -= xts_received_by_bid.amount;

                               // TODO: what if the amount paid is 0 for bid and ask due to rounding errors,
                               // make sure this doesn't put us in an infinite loop.
                               if( quantity == _current_bid->get_quantity() )
                                  _current_bid->state.balance = 0;
                               else
                                  _current_bid->state.balance -= xts_received_by_bid.amount;
                               FC_ASSERT( _current_bid->state.balance >= 0 );

                               auto collateral = (xts_paid_by_ask + xts_received_by_bid).amount;
                               auto cover_price = usd_received_by_ask / asset( (3*collateral)/4, base_id );

                               market_index_key cover_index( cover_price, _current_ask->get_owner() );
                               auto ocover_record = _pending_state->get_collateral_record( cover_index );

                               if( NOT ocover_record )
                                  ocover_record = collateral_record();

                               ocover_record->collateral_balance += collateral;
                               ocover_record->payoff_balance += usd_received_by_ask.amount;
                               FC_ASSERT( ocover_record->payoff_balance >= 0 );
                               FC_ASSERT( ocover_record->collateral_balance >= 0 );
                               _pending_state->store_collateral_record( cover_index, *ocover_record );

                               _pending_state->store_short_record( _current_bid->market_index, _current_bid->state );
                            }
                         } // while bid && ask

                         if( quote_asset->is_market_issued() )
                         {
                            if( !market_stat ||
                                market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ||
                                market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2
                              )
                              FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) );
                         }
                         _pending_state->store_market_status( *market_stat );

                         if( trading_volume.amount > 0 && get_next_bid() && get_next_ask() )
                         {
                           market_history_key key(quote_id, base_id, market_history_key::each_block, _db_impl._head_block_header.timestamp);
                           market_history_record new_record(_current_bid->get_price(), _current_ask->get_price(), trading_volume.amount);
                           //LevelDB iterators are dumb and don't support proper past-the-end semantics.
                           auto last_key_itr = _db_impl._market_history_db.lower_bound(key);
                           if( !last_key_itr.valid() )
                             last_key_itr = _db_impl._market_history_db.last();
                           else
                             --last_key_itr;

                           key.timestamp = timestamp;

                           //Unless the previous record for this market is the same as ours...
                           if( (!(last_key_itr.valid()
                               && last_key_itr.key().quote_id == quote_id
                               && last_key_itr.key().base_id == base_id
                               && last_key_itr.key().granularity == market_history_key::each_block
                               && last_key_itr.value() == new_record)) )
                           {
                             //...add a new entry to the history table.
                             _pending_state->market_history[key] = new_record;
                           }

                           fc::time_point_sec start_of_this_hour = timestamp - (timestamp.sec_since_epoch() % (60*60));
                           market_history_key old_key(quote_id, base_id, market_history_key::each_hour, start_of_this_hour);
                           if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) )
                           {
                             auto old_record = *opt;
                             old_record.volume += new_record.volume;
                             if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask )
                             {
                               old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid);
                               old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask);
                               _pending_state->market_history[old_key] = old_record;
                             }
                           }
                           else
                             _pending_state->market_history[old_key] = new_record;

                           fc::time_point_sec start_of_this_day = timestamp - (timestamp.sec_since_epoch() % (60*60*24));
                           old_key = market_history_key(quote_id, base_id, market_history_key::each_day, start_of_this_day);
                           if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) )
                           {
                             auto old_record = *opt;
                             old_record.volume += new_record.volume;
                             if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask )
                             {
                               old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid);
                               old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask);
                               _pending_state->market_history[old_key] = old_record;
                             }
                           }
                           else
                             _pending_state->market_history[old_key] = new_record;
                         }

                         auto market_state = _pending_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.reset();
                         _pending_state->store_market_status( *market_state );

                         wlog( "done matching orders" );
                         _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(...)
                  bool get_next_ask()
                  { try {
                     if( _current_ask && _current_ask->state.balance > 0 )
                     {
                        wlog( "current ask" );
                        return _current_ask.valid();
                     }
                     _current_ask.reset();

                     /**
                      *  Margin calls take priority over all other ask orders
                      */
                     while( _current_bid && _collateral_itr.valid() )
                     {
                        auto cover_ask = market_order( cover_order,
                                                 _collateral_itr.key(),
                                                 order_record(_collateral_itr.value().payoff_balance),
                                                 _collateral_itr.value().collateral_balance  );

                        if( cover_ask.get_price().quote_asset_id == _quote_id &&
                            cover_ask.get_price().base_asset_id == _base_id )
                        {
                            if( _current_bid->get_price() < cover_ask.get_highest_cover_price()  )
                            {
                               // cover position has been blown out, current bid is not able to
                               // cover the position, so it will sit until the price recovers
                               // enough to fill it.
                               //
                               // The idea here is that the longs have agreed to a maximum
                               // protection equal to the collateral.  If they would like to
                               // sell their USD for XTS this is the best price the short is
                               // obligated to offer.
                               FC_CAPTURE_AND_THROW( insufficient_collateral, (_current_bid)(cover_ask)(cover_ask.get_highest_cover_price()));
                               --_collateral_itr;
                               continue;
                            }
                            // max bid must be greater than call price
                            if( _current_bid->get_price() < cover_ask.get_price() )
                            {
                             //  if( _current_ask->get_price() > cover_ask.get_price() )
                               {
                                  _current_ask = cover_ask;
                                  _current_payoff_balance = _collateral_itr.value().payoff_balance;
                                  --_collateral_itr;
                                  return _current_ask.valid();
                               }
                            }
                        }
                        break;
                     }

                     if( _ask_itr.valid() )
                     {
                        auto ask = market_order( ask_order, _ask_itr.key(), _ask_itr.value() );
                        wlog( "ASK ITER VALID: ${o}", ("o",ask) );
                        if( ask.get_price().quote_asset_id == _quote_id &&
                            ask.get_price().base_asset_id == _base_id )
                        {
                            _current_ask = ask;
                        }
                        ++_ask_itr;
                        return true;
                     }
                     return _current_ask.valid();
                  } FC_CAPTURE_AND_RETHROW() }
 /**
  *  Throws if the asset is not known to the blockchain.
  */
 void transaction_evaluation_state::validate_asset( const asset& asset_to_validate )const
 {
    auto asset_rec = _current_state->get_asset_record( asset_to_validate.asset_id );
    if( NOT asset_rec ) 
       FC_CAPTURE_AND_THROW( unknown_asset_id, (asset_to_validate) );
 }
   void release_escrow_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
      FC_ASSERT( !"This operation is not enabled yet!" );

      auto escrow_balance_record = eval_state._current_state->get_balance_record( this->escrow_id );
      FC_ASSERT( escrow_balance_record.valid() );

      if( !eval_state.check_signature( this->released_by ) )
         FC_ASSERT( !"transaction not signed by releasor" );

      auto escrow_condition = escrow_balance_record->condition.as<withdraw_with_escrow>();
      auto total_released = amount_to_sender + amount_to_receiver;

      FC_ASSERT( total_released <= escrow_balance_record->balance );
      FC_ASSERT( total_released >= amount_to_sender ); // check for addition overflow

      escrow_balance_record->balance -= total_released;
      auto asset_rec = eval_state._current_state->get_asset_record( escrow_balance_record->condition.asset_id );
      if( asset_rec->is_restricted() )
      {
         FC_ASSERT( eval_state._current_state->get_authorization( escrow_balance_record->condition.asset_id, escrow_condition.receiver ) );
      }
      if( asset_rec->is_retractable() )
      {
         if( eval_state.verify_authority( asset_rec->authority ) )
         {
            //
         }
      }

      if( escrow_condition.sender == this->released_by )
      {
         FC_ASSERT( amount_to_sender == 0 );
         FC_ASSERT( amount_to_receiver <= escrow_balance_record->balance );

         if( !eval_state.check_signature( escrow_condition.sender ) )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.sender) );

         balance_record new_balance_record( escrow_condition.receiver,
                                            asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                            escrow_balance_record->slate_id() );
         auto current_receiver_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

         if( current_receiver_balance )
            current_receiver_balance->balance += amount_to_receiver;
         else
            current_receiver_balance = new_balance_record;

          eval_state._current_state->store_balance_record( *current_receiver_balance );
      }
      else if( escrow_condition.receiver == this->released_by )
      {
         FC_ASSERT( amount_to_receiver == 0 );
         FC_ASSERT( amount_to_sender <= escrow_balance_record->balance );

         if( !eval_state.check_signature( escrow_condition.receiver ) )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.receiver) );

         balance_record new_balance_record( escrow_condition.sender,
                                            asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                            escrow_balance_record->slate_id() );
         auto current_sender_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

         if( current_sender_balance )
            current_sender_balance->balance += amount_to_sender;
         else
            current_sender_balance = new_balance_record;

         eval_state._current_state->store_balance_record( *current_sender_balance );
      }
      else if( escrow_condition.escrow == this->released_by )
      {
         if( !eval_state.check_signature( escrow_condition.escrow ) )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.escrow) );
         // get a balance record for the receiver, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.receiver,
                                               asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_receiver_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

            if( current_receiver_balance )
               current_receiver_balance->balance += amount_to_receiver;
            else
               current_receiver_balance = new_balance_record;
            eval_state._current_state->store_balance_record( *current_receiver_balance );
         }
         //  get a balance record for the sender, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.sender,
                                               asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_sender_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

            if( current_sender_balance )
               current_sender_balance->balance += amount_to_sender;
            else
               current_sender_balance = new_balance_record;
            eval_state._current_state->store_balance_record( *current_sender_balance );
         }
      }
      else if( address() == this->released_by )
      {
         if( !eval_state.check_signature( escrow_condition.sender ) )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.sender) );
         if( !eval_state.check_signature( escrow_condition.receiver ) )
             FC_CAPTURE_AND_THROW( missing_signature, (escrow_condition.receiver) );
         // get a balance record for the receiver, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.receiver,
                                               asset( amount_to_receiver, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_receiver_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

            if( current_receiver_balance )
               current_receiver_balance->balance += amount_to_receiver;
            else
               current_receiver_balance = new_balance_record;
            eval_state._current_state->store_balance_record( *current_receiver_balance );
         }
         //  get a balance record for the sender, create it if necessary and deposit funds
         {
            balance_record new_balance_record( escrow_condition.sender,
                                               asset( amount_to_sender, escrow_balance_record->asset_id() ),
                                               escrow_balance_record->slate_id() );
            auto current_sender_balance = eval_state._current_state->get_balance_record( new_balance_record.id());

            if( current_sender_balance )
               current_sender_balance->balance += amount_to_sender;
            else
               current_sender_balance = new_balance_record;
            eval_state._current_state->store_balance_record( *current_sender_balance );
         }
      }
      else
      {
          FC_ASSERT( !"not released by a party to the escrow transaction" );
      }

      eval_state._current_state->store_balance_record( *escrow_balance_record );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
Example #28
0
   void deposit_operation::evaluate( transaction_evaluation_state& eval_state )const
   { try {
       if( this->amount <= 0 )
          FC_CAPTURE_AND_THROW( negative_deposit, (amount) );

       switch( withdraw_condition_types( this->condition.type ) )
       {
          case withdraw_signature_type:
          case withdraw_multisig_type:
          case withdraw_escrow_type:
             break;
          default:
             FC_CAPTURE_AND_THROW( invalid_withdraw_condition, (*this) );
       }

       const balance_id_type deposit_balance_id = this->balance_id();

       obalance_record cur_record = eval_state.pending_state()->get_balance_record( deposit_balance_id );
       if( !cur_record.valid() )
       {
          cur_record = balance_record( this->condition );
          if( this->condition.type == withdraw_escrow_type )
             cur_record->meta_data = variant_object("creating_transaction_id", eval_state.trx.id() );
       }

       if( cur_record->balance == 0 )
       {
          cur_record->deposit_date = eval_state.pending_state()->now();
       }
       else
       {
          fc::uint128 old_sec_since_epoch( cur_record->deposit_date.sec_since_epoch() );
          fc::uint128 new_sec_since_epoch( eval_state.pending_state()->now().sec_since_epoch() );

          fc::uint128 avg = (old_sec_since_epoch * cur_record->balance) + (new_sec_since_epoch * this->amount);
          avg /= (cur_record->balance + this->amount);

          cur_record->deposit_date = time_point_sec( avg.to_integer() );
       }

       cur_record->balance += this->amount;
       eval_state.sub_balance( asset( this->amount, cur_record->asset_id() ) );

       if( cur_record->condition.asset_id == 0 && cur_record->condition.slate_id )
          eval_state.adjust_vote( cur_record->condition.slate_id, this->amount );

       cur_record->last_update = eval_state.pending_state()->now();

       const oasset_record asset_rec = eval_state.pending_state()->get_asset_record( cur_record->condition.asset_id );
       FC_ASSERT( asset_rec.valid() );

       if( asset_rec->is_market_issued() )
       {
           FC_ASSERT( cur_record->condition.slate_id == 0 );
       }

       const auto& owners = cur_record->owners();
       for( const address& owner : owners )
       {
           FC_ASSERT( asset_rec->address_is_whitelisted( owner ) );
       }

       eval_state.pending_state()->store_balance_record( *cur_record );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
Example #29
0
   void withdraw_operation::evaluate( transaction_evaluation_state& eval_state )const
   { try {
       if( this->amount <= 0 )
          FC_CAPTURE_AND_THROW( negative_withdraw, (amount) );

      obalance_record current_balance_record = eval_state.pending_state()->get_balance_record( this->balance_id );
      if( !current_balance_record.valid() )
         FC_CAPTURE_AND_THROW( unknown_balance_record, (balance_id) );

      if( this->amount > current_balance_record->get_spendable_balance( eval_state.pending_state()->now() ).amount )
         FC_CAPTURE_AND_THROW( insufficient_funds, (current_balance_record)(amount) );

      auto asset_rec = eval_state.pending_state()->get_asset_record( current_balance_record->condition.asset_id );
      FC_ASSERT( asset_rec.valid() );

      const bool authority_is_retracting = asset_rec->flag_is_active( asset_record::retractable_balances )
                                           && eval_state.verify_authority( asset_rec->authority );

      if( !authority_is_retracting )
      {
         FC_ASSERT( !asset_rec->flag_is_active( asset_record::halted_withdrawals ) );

         switch( (withdraw_condition_types)current_balance_record->condition.type )
         {
            case withdraw_signature_type:
            {
                const withdraw_with_signature condition = current_balance_record->condition.as<withdraw_with_signature>();
                const address owner = condition.owner;
                if( !eval_state.check_signature( owner ) )
                    FC_CAPTURE_AND_THROW( missing_signature, (owner) );
                break;
            }

            case withdraw_vesting_type:
            {
                const withdraw_vesting condition = current_balance_record->condition.as<withdraw_vesting>();
                const address owner = condition.owner;
                if( !eval_state.check_signature( owner ) )
                    FC_CAPTURE_AND_THROW( missing_signature, (owner) );
                break;
            }

            case withdraw_multisig_type:
            {
               auto multisig = current_balance_record->condition.as<withdraw_with_multisig>();

               uint32_t valid_signatures = 0;
               for( const auto& sig : multisig.owners )
                    valid_signatures += eval_state.check_signature( sig );

               if( valid_signatures < multisig.required )
                   FC_CAPTURE_AND_THROW( missing_signature, (valid_signatures)(multisig) );
               break;
            }

            default:
               FC_CAPTURE_AND_THROW( invalid_withdraw_condition, (current_balance_record->condition) );
         }
      }

      // update delegate vote on withdrawn account..
      if( current_balance_record->condition.asset_id == 0 && current_balance_record->condition.slate_id )
         eval_state.adjust_vote( current_balance_record->condition.slate_id, -this->amount );

      if( asset_rec->is_market_issued() )
      {
         auto yield = current_balance_record->calculate_yield( eval_state.pending_state()->now(),
                                                               current_balance_record->balance,
                                                               asset_rec->collected_fees,
                                                               asset_rec->current_supply );
         if( yield.amount > 0 )
         {
            asset_rec->collected_fees       -= yield.amount;
            current_balance_record->balance += yield.amount;
            current_balance_record->deposit_date = eval_state.pending_state()->now();
            eval_state.yield_claimed[ current_balance_record->asset_id() ] += yield.amount;
            eval_state.pending_state()->store_asset_record( *asset_rec );
         }
      }

      current_balance_record->balance -= this->amount;
      current_balance_record->last_update = eval_state.pending_state()->now();
      eval_state.pending_state()->store_balance_record( *current_balance_record );

      if( asset_rec->withdrawal_fee != 0 && !eval_state.verify_authority( asset_rec->authority ) )
          eval_state.min_fees[ asset_rec->id ] = std::max( asset_rec->withdrawal_fee, eval_state.min_fees[ asset_rec->id ] );

      eval_state.add_balance( asset( this->amount, current_balance_record->condition.asset_id ) );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }
Example #30
0
   void update_balance_vote_operation::evaluate( transaction_evaluation_state& eval_state )const
   { try {
      auto current_balance_record = eval_state.pending_state()->get_balance_record( this->balance_id );
      FC_ASSERT( current_balance_record.valid(), "No such balance!" );
      FC_ASSERT( current_balance_record->condition.asset_id == 0, "Only BTS balances can have restricted owners." );
      FC_ASSERT( current_balance_record->condition.type == withdraw_signature_type, "Restricted owners not enabled for anything but basic balances" );

      auto last_update_secs = current_balance_record->last_update.sec_since_epoch();
      ilog("last_update_secs is: ${secs}", ("secs", last_update_secs) );

      auto balance = current_balance_record->balance;
      auto fee = BTS_BLOCKCHAIN_PRECISION / 2;
      FC_ASSERT( balance > fee );

      auto asset_rec = eval_state.pending_state()->get_asset_record( current_balance_record->condition.asset_id );
      if( asset_rec->is_market_issued() ) FC_ASSERT( current_balance_record->condition.slate_id == 0 );

      if( current_balance_record->condition.slate_id )
      {
          eval_state.adjust_vote( current_balance_record->condition.slate_id, -balance );
      }
      current_balance_record->balance -= balance;
      current_balance_record->last_update = eval_state.pending_state()->now();

      ilog("I'm storing a balance record whose last update is: ${secs}", ("secs", current_balance_record->last_update) );
      eval_state.pending_state()->store_balance_record( *current_balance_record );

      auto new_restricted_owner = current_balance_record->restricted_owner;
      auto new_slate = current_balance_record->condition.slate_id;

      if( this->new_restricted_owner.valid() && (this->new_restricted_owner != new_restricted_owner) )
      {
          ilog("@n new restricted owner specified and its not the existing one");
          for( const auto& owner : current_balance_record->owners() ) //eventually maybe multisig can delegate vote
          {
              if( !eval_state.check_signature( owner ) )
                  FC_CAPTURE_AND_THROW( missing_signature, (owner) );
          }
          new_restricted_owner = this->new_restricted_owner;
          new_slate = this->new_slate;
      }
      else // NOT this->new_restricted_owner.valid() || (this->new_restricted_owner == new_restricted_owner)
      {
          auto restricted_owner = current_balance_record->restricted_owner;
          /*
          FC_ASSERT( restricted_owner.valid(),
                     "Didn't specify a new restricted owner, but one currently exists." );
                     */
          ilog( "@n now: ${secs}", ("secs", eval_state.pending_state()->now().sec_since_epoch()) );
          ilog( "@n last update: ${secs}", ("secs", last_update_secs ) );
          FC_ASSERT( eval_state.pending_state()->now().sec_since_epoch() - last_update_secs
                     >= BTS_BLOCKCHAIN_VOTE_UPDATE_PERIOD_SEC,
                     "You cannot update your vote this frequently with only the voting key!" );

          if( NOT eval_state.check_signature( *restricted_owner ) )
          {
              const auto& owners = current_balance_record->owners();
              for( const auto& owner : owners ) //eventually maybe multisig can delegate vote
              {
                  if( NOT eval_state.check_signature( owner ) )
                      FC_CAPTURE_AND_THROW( missing_signature, (owner) );
              }
          }
          new_slate = this->new_slate;
      }

      const auto owner = current_balance_record->owner();
      FC_ASSERT( owner.valid() );
      withdraw_condition new_condition( withdraw_with_signature( *owner ), 0, new_slate );
      balance_record newer_balance_record( new_condition );
      auto new_balance_record = eval_state.pending_state()->get_balance_record( newer_balance_record.id() );
      if( !new_balance_record.valid() )
          new_balance_record = current_balance_record;
      new_balance_record->condition = new_condition;

      if( new_balance_record->balance == 0 )
      {
         new_balance_record->deposit_date = eval_state.pending_state()->now();
      }
      else
      {
         fc::uint128 old_sec_since_epoch( current_balance_record->deposit_date.sec_since_epoch() );
         fc::uint128 new_sec_since_epoch( eval_state.pending_state()->now().sec_since_epoch() );

         fc::uint128 avg = (old_sec_since_epoch * new_balance_record->balance) + (new_sec_since_epoch * balance);
         avg /= (new_balance_record->balance + balance);

         new_balance_record->deposit_date = time_point_sec( avg.to_integer() );
      }

      new_balance_record->last_update = eval_state.pending_state()->now();
      new_balance_record->balance += (balance - fee);
      new_balance_record->restricted_owner = new_restricted_owner;

      eval_state.add_balance( asset(fee, 0) );

      // update delegate vote on deposited account..
      if( new_balance_record->condition.slate_id )
         eval_state.adjust_vote( new_balance_record->condition.slate_id, (balance-fee) );

      ilog("I'm storing a balance record whose last update is: ${secs}", ("secs", new_balance_record->last_update) );
      eval_state.pending_state()->store_balance_record( *new_balance_record );

   } FC_CAPTURE_AND_RETHROW( (*this) ) }