diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 0a1e623a..bc27d34a 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -81,6 +81,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 365edf07..a3781143 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -90,6 +90,7 @@ namespace graphene { namespace chain { bool charges_market_fees()const { return options.flags & charge_market_fee; } /// @return true if this asset may only be transferred to/from the issuer or market orders bool is_transfer_restricted()const { return options.flags & transfer_restricted; } + bool can_override()const { return options.flags & override_authority; } /// Helper function to get an asset object with the given amount in this asset's type asset amount(share_type a)const { return asset(a, id); } diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 24e319f2..09efd715 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -501,13 +501,13 @@ namespace graphene { namespace chain { */ struct transfer_operation { - asset fee; + asset fee; /// Account to transfer asset from - account_id_type from; + account_id_type from; /// Account to transfer asset to - account_id_type to; + account_id_type to; /// The amount of asset to transfer from @ref from to @ref to - asset amount; + asset amount; /// User provided data encrypted to the memo key of the "to" account optional memo; @@ -524,6 +524,41 @@ namespace graphene { namespace chain { } }; + /** + * @class override_transfer_operation + * @brief Allows the issuer of an asset to transfer an asset from any account to any account if they have override_authority + * @ingroup operations + * + * @pre amount.asset_id->issuer == issuer + * @pre issuer != from because this is pointless, use a normal transfer operation + */ + struct override_transfer_operation + { + asset fee; + account_id_type issuer; + /// Account to transfer asset from + account_id_type from; + /// Account to transfer asset to + account_id_type to; + /// The amount of asset to transfer from @ref from to @ref to + asset amount; + + /// User provided data encrypted to the memo key of the "to" account + optional memo; + + account_id_type fee_payer()const { return issuer; } + void get_required_auth(flat_set& active_auth_set, flat_set&)const; + void validate()const; + share_type calculate_fee(const fee_schedule_type& k)const; + void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const + { + acc.adjust( fee_payer(), -fee ); + acc.adjust( from, -amount ); + acc.adjust( to, amount ); + } + }; + + /** * @ingroup operations */ @@ -1420,7 +1455,8 @@ namespace graphene { namespace chain { worker_create_operation, custom_operation, assert_operation, - balance_claim_operation + balance_claim_operation, + override_transfer_operation > operation; /// @} // operations group @@ -1588,6 +1624,8 @@ FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account) FC_REFLECT( graphene::chain::transfer_operation, (fee)(from)(to)(amount)(memo) ) +FC_REFLECT( graphene::chain::override_transfer_operation, + (fee)(issuer)(from)(to)(amount)(memo) ) FC_REFLECT( graphene::chain::asset_create_operation, (fee) diff --git a/libraries/chain/include/graphene/chain/transfer_evaluator.hpp b/libraries/chain/include/graphene/chain/transfer_evaluator.hpp index c7551cee..a4ab5cc7 100644 --- a/libraries/chain/include/graphene/chain/transfer_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/transfer_evaluator.hpp @@ -31,4 +31,13 @@ namespace graphene { namespace chain { void_result do_apply( const transfer_operation& o ); }; + class override_transfer_evaluator : public evaluator + { + public: + typedef override_transfer_operation operation_type; + + void_result do_evaluate( const override_transfer_operation& o ); + void_result do_apply( const override_transfer_operation& o ); + }; + } } // graphene::chain diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 41c405a4..d3df4487 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -227,6 +227,15 @@ share_type transfer_operation::calculate_fee( const fee_schedule_type& schedule return core_fee_required; } +share_type override_transfer_operation::calculate_fee( const fee_schedule_type& schedule )const +{ + share_type core_fee_required = schedule.transfer_fee; + if( memo ) + core_fee_required += schedule.total_data_fee(memo->message); + return core_fee_required; +} + + struct key_data_validate { typedef void result_type; @@ -291,6 +300,20 @@ void transfer_operation::validate()const FC_ASSERT( amount.amount > 0 ); } +void override_transfer_operation::get_required_auth(flat_set& active_auth_set, + flat_set&) const +{ + active_auth_set.insert( issuer ); +} + +void override_transfer_operation::validate()const +{ + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( from != to ); + FC_ASSERT( amount.amount > 0 ); + FC_ASSERT( issuer != from ); +} + void asset_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { active_auth_set.insert(issuer); diff --git a/libraries/chain/transfer_evaluator.cpp b/libraries/chain/transfer_evaluator.cpp index 223fe474..a3bec25e 100644 --- a/libraries/chain/transfer_evaluator.cpp +++ b/libraries/chain/transfer_evaluator.cpp @@ -53,4 +53,40 @@ void_result transfer_evaluator::do_apply( const transfer_operation& o ) return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } + + +void_result override_transfer_evaluator::do_evaluate( const override_transfer_operation& op ) +{ try { + database& d = db(); + + const asset_object& asset_type = op.amount.asset_id(d); + FC_ASSERT( asset_type.can_override() ); + FC_ASSERT( asset_type.issuer == op.issuer ); + + const account_object& from_account = op.from(d); + const account_object& to_account = op.to(d); + const asset_object& fee_asset_type = op.fee.asset_id(d); + + if( asset_type.options.flags & white_list ) + { + FC_ASSERT( to_account.is_authorized_asset( asset_type ) ); + FC_ASSERT( from_account.is_authorized_asset( asset_type ) ); + } + + if( fee_asset_type.options.flags & white_list ) + FC_ASSERT( from_account.is_authorized_asset( asset_type ) ); + + FC_ASSERT( d.get_balance( &from_account, &asset_type ).amount >= op.amount.amount, + "", ("total_transfer",op.amount)("balance",d.get_balance(&from_account, &asset_type).amount) ); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result override_transfer_evaluator::do_apply( const override_transfer_operation& o ) +{ try { + db().adjust_balance( o.from, -o.amount ); + db().adjust_balance( o.to, o.amount ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } + } } // graphene::chain diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 36fb63f9..349f9669 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -187,6 +187,7 @@ struct operation_get_impacted_accounts void operator()( const worker_create_operation& )const {} void operator()( const assert_operation& )const {} void operator()( const balance_claim_operation& )const {} + void operator()( const override_transfer_operation& )const {} };