Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collateral asset fee accumulator for BSIPs 74 and 87 #2159

Merged
Merged
60 changes: 51 additions & 9 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@ namespace detail {
"Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME");
}
}
}

void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line too long.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix in BSIP87 PR.

{
// HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:
FC_ASSERT( !op.extensions.claim_from_asset_id.valid() ||
block_time >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME,
"Collateral-denominated fees are not yet active and therefore cannot be claimed." );
}

} // graphene::chain::detail

void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op )
{ try {
Expand Down Expand Up @@ -455,6 +464,9 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita
FC_ASSERT( asset_obj.dynamic_asset_data_id(d).current_supply == 0,
"Cannot update a bitasset if there is already a current supply." );

FC_ASSERT( asset_obj.dynamic_asset_data_id(d).accumulated_collateral_fees == 0,
"Must claim collateral-denominated fees before changing backing asset." );

const asset_object& new_backing_asset = op.new_options.short_backing_asset(d); // check if the asset exists

if( after_hf_core_922_931 )
Expand Down Expand Up @@ -920,25 +932,55 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
} FC_CAPTURE_AND_RETHROW((o)) }



/***
* @brief evaluator for asset_claim_fees operation
*
* Checks that we are able to claim fees denominated in asset Y (the amount_to_claim asset),
* from some container asset X which is presumed to have accumulated the fees we wish to claim.
* The container asset is either explicitly named in the extensions, or else assumed as the same
* asset as the amount_to_claim asset. Evaluation fails if either (a) operation issuer is not
* the same as the container_asset issuer, or (b) container_asset has no fee bucket for
* amount_to_claim asset.
*/
void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o )
{ try {
FC_ASSERT( o.amount_to_claim.asset_id(db()).issuer == o.issuer, "Asset fees may only be claimed by the issuer" );
database& d = db();
abitmore marked this conversation as resolved.
Show resolved Hide resolved

detail::check_asset_claim_fees_hardfork_87_74_collatfee(d.head_block_time(), o); // HF_REMOVABLE

const asset_object & container_asset = o.extensions.claim_from_asset_id.valid() ?
(*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d);
FC_ASSERT( container_asset.issuer == o.issuer, "Asset fees may only be claimed by the issuer" );
FC_ASSERT( container_asset.can_accumulate_fee(d,o.amount_to_claim),
"Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.",
("a",container_asset.symbol)("id",container_asset.id)("fid",o.amount_to_claim.asset_id) );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }


/***
* @brief apply asset_claim_fees operation
*/
void_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operation& o )
{ try {
database& d = db();

const asset_object& a = o.amount_to_claim.asset_id(d);
const asset_dynamic_data_object& addo = a.dynamic_asset_data_id(d);
FC_ASSERT( o.amount_to_claim.amount <= addo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("addo",addo) );
const asset_object & c = o.extensions.claim_from_asset_id.valid() ?
(*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d);
abitmore marked this conversation as resolved.
Show resolved Hide resolved
const asset_dynamic_data_object& ddo = c.dynamic_asset_data_id(d);
const asset_object & a = o.amount_to_claim.asset_id(d);
abitmore marked this conversation as resolved.
Show resolved Hide resolved

d.modify( addo, [&]( asset_dynamic_data_object& _addo ) {
_addo.accumulated_fees -= o.amount_to_claim.amount;
});
if ( c.get_id() == a.get_id() ) {
FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) );
abitmore marked this conversation as resolved.
Show resolved Hide resolved
d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) {
abitmore marked this conversation as resolved.
Show resolved Hide resolved
_addo.accumulated_fees -= o.amount_to_claim.amount;
});
} else {
FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_collateral_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) );
d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) {
_addo.accumulated_collateral_fees -= o.amount_to_claim.amount;
});
}

d.adjust_balance( o.issuer, o.amount_to_claim );

Expand Down
7 changes: 7 additions & 0 deletions libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This hardfork enables the extension to asset_claim_fees_operation to claim collateral-denominated fees.
// These fees are collected by BSIPs 87 and 74. This should be set to match the earlier of either
// HARDFORK_CORE_BSIP87_TIME or HARDFORK_CORE_BSIP74_TIME.
// This hardfork check should be removable after the hardfork date passes.
#ifndef HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME
#define HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME (fc::time_point_sec( 1679955066 ) ) // Temporary date until actual hardfork date is set
#endif
44 changes: 44 additions & 0 deletions libraries/chain/include/graphene/chain/asset_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ namespace graphene { namespace chain {
share_type current_supply;
share_type confidential_supply; ///< total asset held in confidential balances
share_type accumulated_fees; ///< fees accumulate to be paid out over time
share_type accumulated_collateral_fees; ///< accumulated collateral-denominated fees (for bitassets)
abitmore marked this conversation as resolved.
Show resolved Hide resolved
share_type fee_pool; ///< in core asset
};

Expand Down Expand Up @@ -164,6 +165,44 @@ namespace graphene { namespace chain {
template<class DB>
share_type reserved( const DB& db )const
{ return options.max_supply - dynamic_data(db).current_supply; }

/// @return true if asset can accumulate fees in the given denomination
template<class DB>
bool can_accumulate_fee(const DB& db, const asset& fee) const {
return (( fee.asset_id == get_id() ) ||
( is_market_issued() && fee.asset_id == bitasset_data(db).options.short_backing_asset ));
}

/***
* @brief receive a fee asset to accrue in dynamic_data object
*
* Asset owners define various fees (market fees, force-settle fees, etc.) to be
* collected for the asset owners. These fees are typically denominated in the asset
* itself, but for bitassets some of the fees are denominated in the collateral
* asset. This will place the fee in the right container.
*/
template<class DB>
void accumulate_fee(DB& db, const asset& fee) const
{
abitmore marked this conversation as resolved.
Show resolved Hide resolved
const auto& dyn_data = dynamic_asset_data_id(db);
if (fee.asset_id == get_id()) { // fee same as asset
db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){
obj.accumulated_fees += fee.amount;
});
} else { // fee different asset; perhaps collateral-denominated fee
FC_ASSERT( is_market_issued(),
"Asset ${a} (${id}) cannot accept fee of asset (${fid}).",
("a",this->symbol)("id",this->id)("fid",fee.asset_id) );
const auto & bad = bitasset_data(db);
FC_ASSERT( fee.asset_id == bad.options.short_backing_asset,
"Asset ${a} (${id}) cannot accept fee of asset (${fid}).",
("a",this->symbol)("id",this->id)("fid",fee.asset_id) );
db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){
obj.accumulated_collateral_fees += fee.amount;
});
}
}

};

/**
Expand All @@ -184,6 +223,11 @@ namespace graphene { namespace chain {
/// The tunable options for BitAssets are stored in this field.
bitasset_options options;

/// Check collateral-denominated fees:
template<class DB>
bool collateral_fees_are_zero(const DB& db) const
{ return asset_id(db).dynamic_asset_data_id(db).accumulated_collateral_fees > 0; }
abitmore marked this conversation as resolved.
Show resolved Hide resolved

/// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing
/// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map
/// should be treated as an implementation detail. The timestamp on each feed is the time it was published.
Expand Down
5 changes: 5 additions & 0 deletions libraries/chain/proposal_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace graphene { namespace chain {
namespace detail {
void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options);
void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options);
void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line too long.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix in BSIP87 PR.

}

struct proposal_operation_hardfork_visitor
Expand Down Expand Up @@ -61,6 +62,10 @@ struct proposal_operation_hardfork_visitor
detail::check_asset_options_hf_bsip81(block_time, v.new_options);
}

void operator()(const graphene::chain::asset_claim_fees_operation &v) const {
detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE
}

void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const {
if (block_time < HARDFORK_CORE_1468_TIME) {
FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468");
Expand Down
1 change: 1 addition & 0 deletions libraries/protocol/asset_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_oper
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type )
Expand Down
19 changes: 16 additions & 3 deletions libraries/protocol/include/graphene/protocol/asset_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,20 @@ namespace graphene { namespace protocol {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
};

struct additional_options_type
{
/// Which asset to claim fees from. This is needed, e.g., to claim collateral-
/// denominated fees from a collateral-backed smart asset. If unset, assumed to be same
/// asset as amount_to_claim is denominated in, such as would be the case when claiming
/// market fees.
fc::optional<asset_id_type> claim_from_asset_id;
abitmore marked this conversation as resolved.
Show resolved Hide resolved
};

asset fee;
account_id_type issuer;
asset amount_to_claim; /// amount_to_claim.asset_id->issuer must == issuer
extensions_type extensions;
account_id_type issuer; /// must match issuer of asset from which we claim fees
abitmore marked this conversation as resolved.
Show resolved Hide resolved
asset amount_to_claim;

additional_options_type extensions;
abitmore marked this conversation as resolved.
Show resolved Hide resolved

account_id_type fee_payer()const { return issuer; }
void validate()const;
Expand Down Expand Up @@ -521,6 +531,8 @@ namespace graphene { namespace protocol {

FC_REFLECT( graphene::protocol::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) )
FC_REFLECT( graphene::protocol::asset_claim_fees_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::protocol::asset_claim_fees_operation::additional_options_type, (claim_from_asset_id) )

FC_REFLECT( graphene::protocol::asset_claim_pool_operation, (fee)(issuer)(asset_id)(amount_to_claim)(extensions) )
FC_REFLECT( graphene::protocol::asset_claim_pool_operation::fee_parameters_type, (fee) )

Expand Down Expand Up @@ -619,6 +631,7 @@ GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operat
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type )
Expand Down
1 change: 1 addition & 0 deletions tests/common/database_fixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ void database_fixture::verify_asset_supplies( const database& db )
{
const auto& bad = asset_obj.bitasset_data(db);
total_balances[bad.options.short_backing_asset] += bad.settlement_fund;
total_balances[bad.options.short_backing_asset] += dasset_obj.accumulated_collateral_fees;
}
total_balances[asset_obj.id] += dasset_obj.confidential_supply.value;
}
Expand Down