diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 80ae9e68..79729638 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -97,17 +97,11 @@ void account_statistics_object::process_fees(const account_object& a, database& assert( referrer_cut + registrar_cut + accumulated + reserveed + lifetime_cut == core_fee_total ); }; - share_type vesting_fee_subtotal(pending_fees); - share_type vested_fee_subtotal(pending_vested_fees); - share_type vesting_cashback, vested_cashback; + pay_out_fees(a, pending_fees, true); + pay_out_fees(a, pending_vested_fees, false); - pay_out_fees(a, vesting_fee_subtotal, true); - d.deposit_cashback(a, vesting_cashback, true); - pay_out_fees(a, vested_fee_subtotal, false); - d.deposit_cashback(a, vested_cashback, false); - - d.modify(*this, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) { - s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal; + d.modify(*this, [&](account_statistics_object& s) { + s.lifetime_fees_paid += pending_fees + pending_vested_fees; s.pending_fees = 0; s.pending_vested_fees = 0; }); diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 967f4410..10770624 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -463,24 +463,7 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_ const asset_bitasset_data_object& bitasset = base.bitasset_data(d); FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" ); - // - // many of these checks should be moved to price_feed.validate() - // or the operation validator when new network is started - // - if( !o.feed.core_exchange_rate.is_null() ) - { - o.feed.core_exchange_rate.validate(); - } - if( (!o.feed.settlement_price.is_null()) && (!o.feed.core_exchange_rate.is_null()) ) - { - FC_ASSERT( o.feed.settlement_price.base.asset_id == o.feed.core_exchange_rate.base.asset_id ); - FC_ASSERT( o.feed.settlement_price.quote.asset_id == o.feed.core_exchange_rate.quote.asset_id ); - } - - FC_ASSERT( !o.feed.settlement_price.is_null() ); - FC_ASSERT( !o.feed.core_exchange_rate.is_null() ); FC_ASSERT( o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset ); - FC_ASSERT( o.feed.is_for( o.asset_id ) ); //Verify that the publisher is authoritative to publish a feed if( base.options.flags & witness_fed_asset ) diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index 927dca9a..e6bcea4f 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -130,6 +130,21 @@ void asset_publish_feed_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); feed.validate(); + + // maybe some of these could be moved to feed.validate() + if( !feed.core_exchange_rate.is_null() ) + { + feed.core_exchange_rate.validate(); + } + if( (!feed.settlement_price.is_null()) && (!feed.core_exchange_rate.is_null()) ) + { + FC_ASSERT( feed.settlement_price.base.asset_id == feed.core_exchange_rate.base.asset_id ); + FC_ASSERT( feed.settlement_price.quote.asset_id == feed.core_exchange_rate.quote.asset_id ); + } + + FC_ASSERT( !feed.settlement_price.is_null() ); + FC_ASSERT( !feed.core_exchange_rate.is_null() ); + FC_ASSERT( feed.is_for( asset_id ) ); } void asset_reserve_operation::validate()const diff --git a/libraries/wallet/include/graphene/wallet/reflect_util.hpp b/libraries/wallet/include/graphene/wallet/reflect_util.hpp index 962b0347..5fefd81e 100644 --- a/libraries/wallet/include/graphene/wallet/reflect_util.hpp +++ b/libraries/wallet/include/graphene/wallet/reflect_util.hpp @@ -53,16 +53,34 @@ struct static_variant_map_visitor int which; }; +template< typename StaticVariant > +struct from_which_visitor +{ + typedef StaticVariant result_type; + + template< typename Member > // Member is member of static_variant + result_type operator()( const Member& dummy ) + { + Member result; + from_variant( v, result ); + return result; // converted from StaticVariant to Result automatically due to return type + } + + const variant& v; + + from_which_visitor( const variant& _v ) : v(_v) {} +}; + } // namespace impl template< typename T > T from_which_variant( int which, const variant& v ) { // Parse a variant for a known which() - T result; - result.set_which( which ); - from_variant( v, result ); - return result; + T dummy; + dummy.set_which( which ); + impl::from_which_visitor< T > vtor(v); + return dummy.visit( vtor ); } template diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7f86aeb5..3dc5a7e5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -216,6 +216,13 @@ struct approval_delta vector key_approvals_to_remove; }; +struct worker_vote_delta +{ + flat_set vote_for; + flat_set vote_against; + flat_set vote_abstain; +}; + struct signed_block_with_info : public signed_block { signed_block_with_info( const signed_block& block ); @@ -1124,6 +1131,43 @@ class wallet_api string block_signing_key, bool broadcast = false); + + /** + * Create a worker object. + * + * @param owner_account The account which owns the worker and will be paid + * @param work_begin_date When the work begins + * @param work_end_date When the work ends + * @param daily_pay Amount of pay per day (NOT per maint interval) + * @param name Any text + * @param url Any text + * @param worker_settings {"type" : "burn"|"refund"|"vesting", "pay_vesting_period_days" : x} + * @param broadcast true if you wish to broadcast the transaction. + */ + signed_transaction create_worker( + string owner_account, + time_point_sec work_begin_date, + time_point_sec work_end_date, + share_type daily_pay, + string name, + string url, + variant worker_settings, + bool broadcast = false + ); + + /** + * Update your votes for a worker + * + * @param account The account which will pay the fee and update votes. + * @param worker_vote_delta {"vote_for" : [...], "vote_against" : [...], "vote_abstain" : [...]} + * @param broadcast true if you wish to broadcast the transaction. + */ + signed_transaction update_worker_votes( + string account, + worker_vote_delta delta, + bool broadcast = false + ); + /** * Get information about a vesting balance object. * @@ -1380,6 +1424,12 @@ FC_REFLECT( graphene::wallet::approval_delta, (key_approvals_to_remove) ) +FC_REFLECT( graphene::wallet::worker_vote_delta, + (vote_for) + (vote_against) + (vote_abstain) +) + FC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain::signed_block), (block_id)(signing_key) ) @@ -1440,6 +1490,8 @@ FC_API( graphene::wallet::wallet_api, (list_committee_members) (create_witness) (update_witness) + (create_worker) + (update_worker_votes) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0342f9cf..f927eccf 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1407,6 +1407,122 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (witness_name)(url)(block_signing_key)(broadcast) ) } + template + static WorkerInit _create_worker_initializer( const variant& worker_settings ) + { + WorkerInit result; + from_variant( worker_settings, result ); + return result; + } + + signed_transaction create_worker( + string owner_account, + time_point_sec work_begin_date, + time_point_sec work_end_date, + share_type daily_pay, + string name, + string url, + variant worker_settings, + bool broadcast + ) + { + worker_initializer init; + std::string wtype = worker_settings["type"].get_string(); + + // TODO: Use introspection to do this dispatch + if( wtype == "burn" ) + init = _create_worker_initializer< burn_worker_initializer >( worker_settings ); + else if( wtype == "refund" ) + init = _create_worker_initializer< refund_worker_initializer >( worker_settings ); + else if( wtype == "vesting" ) + init = _create_worker_initializer< vesting_balance_worker_initializer >( worker_settings ); + else + { + FC_ASSERT( false, "unknown worker[\"type\"] value" ); + } + + worker_create_operation op; + op.owner = get_account( owner_account ).id; + op.work_begin_date = work_begin_date; + op.work_end_date = work_end_date; + op.daily_pay = daily_pay; + op.name = name; + op.url = url; + op.initializer = init; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } + + signed_transaction update_worker_votes( + string account, + worker_vote_delta delta, + bool broadcast + ) + { + account_object acct = get_account( account ); + account_update_operation op; + + // you could probably use a faster algorithm for this, but flat_set is fast enough :) + flat_set< worker_id_type > merged; + merged.reserve( delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); + for( const worker_id_type& wid : delta.vote_for ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + for( const worker_id_type& wid : delta.vote_against ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + for( const worker_id_type& wid : delta.vote_abstain ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + + // should be enforced by FC_ASSERT's above + assert( merged.size() == delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); + + vector< object_id_type > query_ids; + for( const worker_id_type& wid : merged ) + query_ids.push_back( wid ); + + flat_set new_votes( acct.options.votes ); + + fc::variants objects = _remote_db->get_objects( query_ids ); + for( const variant& obj : objects ) + { + worker_object wo; + from_variant( obj, wo ); + new_votes.erase( wo.vote_for ); + new_votes.erase( wo.vote_against ); + if( delta.vote_for.find( wo.id ) != delta.vote_for.end() ) + new_votes.insert( wo.vote_for ); + else if( delta.vote_against.find( wo.id ) != delta.vote_against.end() ) + new_votes.insert( wo.vote_against ); + else + assert( delta.vote_abstain.find( wo.id ) != delta.vote_abstain.end() ); + } + + account_update_operation update_op; + update_op.account = acct.id; + update_op.new_options = acct.options; + update_op.new_options->votes = new_votes; + + signed_transaction tx; + tx.operations.push_back( update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -2864,6 +2980,28 @@ signed_transaction wallet_api::create_witness(string owner_account, return my->create_witness(owner_account, url, broadcast); } +signed_transaction wallet_api::create_worker( + string owner_account, + time_point_sec work_begin_date, + time_point_sec work_end_date, + share_type daily_pay, + string name, + string url, + variant worker_settings, + bool broadcast /* = false */) +{ + return my->create_worker( owner_account, work_begin_date, work_end_date, + daily_pay, name, url, worker_settings, broadcast ); +} + +signed_transaction wallet_api::update_worker_votes( + string owner_account, + worker_vote_delta delta, + bool broadcast /* = false */) +{ + return my->update_worker_votes( owner_account, delta, broadcast ); +} + signed_transaction wallet_api::update_witness( string witness_name, string url,