/** * 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, "" ) }
/** * 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) ) }
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) ) }
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) ) }
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) ) }
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) ); } }
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, "" ) }
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, "" ) }
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) ) }
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; } } }
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) ) }
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) ) }
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) ) }
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) ) }
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) ) }