void user_auction_claim_operation::evaluate( transaction_evaluation_state& eval_state ) { try { FC_ASSERT( !"This operation is not enabled yet!" ); auto chain = eval_state._current_state; auto obj = chain->get_object_record( this->auction_id ); FC_ASSERT( obj.valid(), "No such auction." ); auto auction = obj->as<user_auction_record>(); FC_ASSERT( auction.is_complete( *chain ), "Auction is not over yet." ); if( this->claim_balance ) { FC_ASSERT( NOT auction.balance_claimed, "Those auction winnings have already been claimed." ); eval_state.check_update_permission( auction.beneficiary ); eval_state.add_balance( auction.previous_bid ); auction.balance_claimed = true; } if( this->claim_object ) // set the item's owner to the winner { FC_ASSERT( NOT auction.object_claimed, "That object has already been claimed." ); eval_state.check_update_permission( auction.previous_bidder ); auto item = chain->get_object_record( auction.item ); FC_ASSERT( item->owner_object == auction._id, "An auction was being held for an item owned by someone else?"); item->owner_object = auction.previous_bidder; chain->store_object_record( *item ); auction.object_claimed = true; } chain->store_object_record( object_record( auction ) ); } FC_CAPTURE_AND_RETHROW( (eval_state)(*this) ) }
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_validator::validate_name_output( const trx_output& out, transaction_evaluation_state& state, const block_evaluation_state_ptr& block_state ) { auto claim = out.as<claim_name_output>(); block_state->add_name_output( claim ); if( !state.has_name_input( claim ) ) { auto name_rec = _db->lookup_name( claim.name ); FC_ASSERT( !name_rec ); } FC_ASSERT( out.amount.unit == 0 ); state.add_output_asset( out.amount ); }
void transaction_validator::validate_signature_input( const meta_trx_input& in, transaction_evaluation_state& state, const block_evaluation_state_ptr& block_state ) { auto claim = in.output.as<claim_by_signature_output>(); FC_ASSERT( state.has_signature( claim.owner ), "", ("owner",claim.owner)("sigs",state.sigs) ); state.add_input_asset( in.output.amount ); if( in.output.amount.unit == 0 ) { accumulate_votes( in.output.amount.get_rounded_amount(), in.source.block_num, state ); block_state->add_input_delegate_votes( in.delegate_id, in.output.amount ); block_state->add_output_delegate_votes( state.trx.vote, in.output.amount ); } }
void data_operation::evaluate( transaction_evaluation_state& eval_state )const { #ifndef WIN32 #warning [SOFTFORK] Remove this check after BTS_V0_9_0_FORK_BLOCK_NUM has passed #endif FC_ASSERT( eval_state.pending_state()->get_head_block_num() >= BTS_V0_9_0_FORK_BLOCK_NUM ); }
void burn_operation::evaluate( transaction_evaluation_state& eval_state ) { try { if( message.size() ) FC_ASSERT( amount.asset_id == 0 ); if( amount.asset_id == 0 ) { FC_ASSERT( amount.amount >= BTS_BLOCKCHAIN_MIN_BURN_FEE, "", ("amount",amount) ("BTS_BLOCKCHAIN_MIN_BURN_FEE",BTS_BLOCKCHAIN_MIN_BURN_FEE) ); } oasset_record asset_rec = eval_state._current_state->get_asset_record( amount.asset_id ); FC_ASSERT( asset_rec.valid() ); FC_ASSERT( !asset_rec->is_market_issued() ); asset_rec->current_share_supply -= this->amount.amount; eval_state.sub_balance( address(), this->amount ); eval_state._current_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 { // TODO: support burning to any OBJECT ID not just accounts const oaccount_record account_rec = eval_state._current_state->get_account_record( abs( this->account_id ) ); FC_ASSERT( account_rec.valid() ); } eval_state._current_state->store_burn_record( burn_record( burn_record_key( {account_id, eval_state.trx.id()} ), burn_record_value( {amount,message,message_signature} ) ) ); } FC_CAPTURE_AND_RETHROW( (*this) ) }
void domain_update_signin_operation::evaluate( transaction_evaluation_state& eval_state ) { auto odomain_rec = eval_state._current_state->get_domain_record( this->domain_name ); auto now = eval_state._current_state->now().sec_since_epoch(); FC_ASSERT( odomain_rec.valid(), "Trying to update domain which does not exist" ); FC_ASSERT( odomain_rec->get_true_state(now) == domain_record::owned, "Attempting to update a domain which is not in 'owned' state"); FC_ASSERT( eval_state.check_signature( odomain_rec->owner ), "Update not signed by owner" ); odomain_rec->signin_key = this->signin_key; odomain_rec->last_update = eval_state._current_state->now().sec_since_epoch(); eval_state._current_state->store_domain_record( *odomain_rec ); }
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 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 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) ) }
transaction_summary transaction_validator::on_evaluate( transaction_evaluation_state& state, const block_evaluation_state_ptr& block_state ) { transaction_summary sum; state.inputs = _db->fetch_inputs( state.trx.inputs ); auto trx_delegate = _db->lookup_delegate( state.trx.vote ); FC_ASSERT( !!trx_delegate, "unable to find delegate id ${id}", ("id",state.trx.vote) ); /** make sure inputs are unique */ std::unordered_set<output_reference> unique_inputs; for( auto in : state.trx.inputs ) { FC_ASSERT( unique_inputs.insert( in.output_ref ).second, "transaction references same output more than once.", ("trx",state.trx) ) } /** validate all inputs */ for( auto in : state.inputs ) { FC_ASSERT( !in.meta_output.is_spent(), "", ("trx",state.trx) ); validate_input( in, state, block_state ); } /** validate all inputs */ for( auto out : state.trx.outputs ) validate_output( out, state, block_state ); state.balance_assets(); sum.valid_votes = state.valid_votes; sum.invalid_votes = state.invalid_votes; sum.spent = state.spent; sum.fees = state.get_total_in(0) - state.get_total_out(0); if( state.get_required_fees() >= 0 ) { FC_ASSERT( sum.fees >= state.get_required_fees(0), "", ("fees",sum.fees)("required",state.get_required_fees())); } return sum; }
void withdraw_operation::evaluate_v3( transaction_evaluation_state& eval_state ) { try { if( eval_state._current_state->get_head_block_num() < BTS_V0_4_15_FORK_BLOCK_NUM ) return evaluate_v2( eval_state ); if( this->amount <= 0 ) FC_CAPTURE_AND_THROW( negative_deposit, (amount) ); obalance_record current_balance_record = eval_state._current_state->get_balance_record( this->balance_id ); if( !current_balance_record ) FC_CAPTURE_AND_THROW( unknown_balance_record, (balance_id) ); if( this->amount > current_balance_record->balance ) // Some withdraw conditions require extra checks (e.g. vesting condition) FC_CAPTURE_AND_THROW( insufficient_funds, (current_balance_record) (amount) (current_balance_record->balance - amount) ); switch( (withdraw_condition_types)current_balance_record->condition.type ) { case withdraw_signature_type: { auto owner = current_balance_record->condition.as<withdraw_with_signature>().owner; if( !eval_state.check_signature( owner ) ) FC_CAPTURE_AND_THROW( missing_signature, (owner) ); 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.delegate_slate_id ) eval_state.adjust_vote( current_balance_record->condition.delegate_slate_id, -this->amount ); auto asset_rec = eval_state._current_state->get_asset_record( current_balance_record->condition.asset_id ); FC_ASSERT( asset_rec.valid() ); if( asset_rec->is_market_issued() ) { auto yield = current_balance_record->calculate_yield_v1( eval_state._current_state->now(), current_balance_record->balance, asset_rec->collected_fees, asset_rec->current_share_supply ); if( yield.amount > 0 ) { asset_rec->collected_fees -= yield.amount; current_balance_record->balance += yield.amount; current_balance_record->deposit_date = eval_state._current_state->now(); eval_state.yield[current_balance_record->condition.asset_id] += yield.amount; eval_state._current_state->store_asset_record( *asset_rec ); } } current_balance_record->balance -= this->amount; current_balance_record->last_update = eval_state._current_state->now(); eval_state._current_state->store_balance_record( *current_balance_record ); 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) ) }
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 transaction_validator::validate_pts_signature_output( const trx_output& out, transaction_evaluation_state& state, const block_evaluation_state_ptr& block_state ) { state.add_output_asset( out.amount ); }
void dns_transaction_validator::validate_domain_output(const trx_output& out, transaction_evaluation_state& state) { auto dns_state = dynamic_cast<dns_tx_evaluation_state&>(state); FC_ASSERT( ! dns_state.seen_domain_output, "More than one domain claim output in one tx: ${tx}", ("tx", state.trx) ); dns_state.seen_domain_output = true; // "name" and "value" length limits auto dns_out = out.as<claim_domain_output>(); dns_db* db = dynamic_cast<dns_db*>(_db); FC_ASSERT( db != nullptr ); /* If we haven't seen a domain input then the only valid output is a new * domain auction. */ if (! dns_state.seen_domain_input) { // name doesn't already exist (not in DB OR older than 1 year) if ( db->has_dns_record(dns_out.name) ) { auto old_record = db->get_dns_record(dns_out.name); auto old_tx_id = old_record.last_update_ref.trx_hash; auto block_num = db->fetch_trx_num(old_tx_id).block_num; auto current_block = db->head_block_num(); if (current_block - block_num < BTS_BLOCKCHAIN_BLOCKS_PER_YEAR) { FC_ASSERT(!"Name already exists (and is younger than 1 block-year)"); } } FC_ASSERT(dns_out.flags == claim_domain_output::for_auction, "New auction started with for_auction flag not set"); return; } /* Otherwise, the transaction must have a domain input and it must exist * in the database, and it can't be expired */ //TODO do this just from the input without looking into the DB? FC_ASSERT( db->has_dns_record(dns_out.name), "Transaction references a name that doesn't exist"); auto old_record = db->get_dns_record(dns_out.name); auto old_tx_id = old_record.last_update_ref.trx_hash; auto block_num = db->fetch_trx_num(old_tx_id).block_num; auto current_block = db->head_block_num(); auto block_age = current_block - block_num; FC_ASSERT( block_age < BTS_BLOCKCHAIN_BLOCKS_PER_YEAR, "Domain transaction references an expired domain as an input"); FC_ASSERT(dns_out.name == dns_state.dns_claimed.name, "bid transaction refers to different input and output names"); // case on state of claimed output // * if auction is over (not_for_sale OR output is older than 3 days) if (dns_out.flags == claim_domain_output::not_for_sale || block_age >= 3 * BTS_BLOCKCHAIN_BLOCKS_PER_DAY) { // If you're the owner, do whatever you like! if (! state.has_signature(dns_out.owner) ) { FC_ASSERT(false, "Domain tx requiring signature doesn't have it: ${tx}", ("tx", state.trx)); } } else { // Currently in an auction FC_ASSERT(dns_out.flags == claim_domain_output::for_auction, "bid made without keeping for_auction flag"); //TODO use macros in dns_config.hpp instead of hard-coded constants //TODO restore FC_ASSERT(out.amount.get_rounded_amount() >= (11 * dns_state.claimed.amount.get_rounded_amount()) / 10, "Bid was too small: ${trx}", ("trx", state.trx) ); // half of difference goes to fee dns_state.add_required_fees((out.amount - dns_state.claimed.amount) / 2); // check for output to past owner bool found = false; for (auto other_out : state.trx.outputs) { bool right_claim = other_out.claim_func == claim_by_signature; bool enough = (other_out.amount == dns_state.claimed.amount + (out.amount - dns_state.claimed.amount / 2)); bool to_owner = right_claim && other_out.as<claim_by_signature_output>().owner == dns_state.dns_claimed.owner; if (right_claim && enough && to_owner) { found = true; break; } } if (!found) { FC_ASSERT(!"Bid did not pay enough to previous owner"); } } }
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 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 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) ) }