Merge branch '563-fork-stealth-fee-routing' into develop

Includes dependency '572-fork-reenable-asset-perms'
This commit is contained in:
theoreticalbts 2016-02-15 15:37:22 -05:00
commit 92685af364
25 changed files with 720 additions and 11 deletions

View file

@ -348,6 +348,8 @@ namespace graphene { namespace app {
break;
case impl_buyback_object_type:
break;
case impl_fba_accumulator_object_type:
break;
}
}
return result;

View file

@ -198,6 +198,11 @@ struct get_impacted_account_visitor
_impacted.insert( op.account );
}
void operator()( const fba_distribute_operation& op )
{
_impacted.insert( op.account_id );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )

View file

@ -81,6 +81,7 @@ add_library( graphene_chain
account_object.cpp
asset_object.cpp
fba_object.cpp
proposal_object.cpp
vesting_balance_object.cpp

View file

@ -260,14 +260,28 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
database& d = db();
bool sa_before, sa_after;
d.modify( *acnt, [&](account_object& a){
if( o.owner ) a.owner = *o.owner;
if( o.active ) a.active = *o.active;
if( o.owner )
{
a.owner = *o.owner;
a.top_n_control_flags = 0;
}
if( o.active )
{
a.active = *o.active;
a.top_n_control_flags = 0;
}
if( o.new_options ) a.options = *o.new_options;
sa_before = a.has_special_authority();
if( o.extensions.value.owner_special_authority.valid() )
{
a.owner_special_authority = *(o.extensions.value.owner_special_authority);
a.top_n_control_flags = 0;
}
if( o.extensions.value.active_special_authority.valid() )
{
a.active_special_authority = *(o.extensions.value.active_special_authority);
a.top_n_control_flags = 0;
}
sa_after = a.has_special_authority();
});

View file

@ -252,9 +252,12 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
}
}
// new issuer_permissions must be subset of old issuer permissions
FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions),
"Cannot reinstate previously revoked issuer permissions on an asset.");
if( (d.head_block_time() < HARDFORK_572_TIME) || (a.dynamic_asset_data_id(d).current_supply != 0) )
{
// new issuer_permissions must be subset of old issuer permissions
FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions),
"Cannot reinstate previously revoked issuer permissions on an asset.");
}
// changed flags must be subset of old issuer permissions
FC_ASSERT(!((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions),

View file

@ -26,6 +26,8 @@
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
@ -67,6 +69,13 @@ void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_opera
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_to_blind_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_transfer_to_blind );
else
generic_evaluator::pay_fee();
}
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
{ try {
@ -104,9 +113,13 @@ void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_o
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_from_blind_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_transfer_from_blind );
else
generic_evaluator::pay_fee();
}
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
{ try {
@ -157,5 +170,12 @@ void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation&
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void blind_transfer_evaluator::pay_fee()
{
if( db().head_block_time() >= HARDFORK_563_TIME )
pay_fba_fee( fba_accumulator_id_blind_transfer );
else
generic_evaluator::pay_fee();
}
} } // graphene::chain

View file

@ -23,6 +23,7 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -33,6 +34,7 @@
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
@ -212,6 +214,8 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<budget_record_object > > >();
add_index< primary_index< special_authority_index > >();
add_index< primary_index< buyback_index > >();
add_index< primary_index< simple_index< fba_accumulator_object > > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)
@ -663,6 +667,36 @@ void database::init_genesis(const genesis_state_type& genesis_state)
wso.current_shuffled_witnesses.push_back( wid );
});
// Create FBA counters
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_to_blind ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_blind_transfer ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{
FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_from_blind ) );
acc.accumulated_fba_fees = 0;
#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET;
#endif
});
FC_ASSERT( get_index<fba_accumulator_object>().get_next_id() == fba_accumulator_id_type( fba_accumulator_id_count ) );
debug_dump();
_undo_db.enable();

View file

@ -28,6 +28,7 @@
#include <fc/uint128.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/account_object.hpp>
@ -36,6 +37,7 @@
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/special_authority_object.hpp>
@ -512,11 +514,100 @@ void update_top_n_authorities( database& db )
db.modify( acct, [&]( account_object& a )
{
vc.finish( is_owner ? a.owner : a.active );
if( !vc.is_empty() )
a.top_n_control_flags |= (is_owner ? account_object::top_n_control_owner : account_object::top_n_control_active);
} );
}
} );
}
void split_fba_balance(
database& db,
uint64_t fba_id,
uint16_t network_pct,
uint16_t designated_asset_buyback_pct,
uint16_t designated_asset_issuer_pct
)
{
FC_ASSERT( uint32_t(network_pct) + uint32_t(designated_asset_buyback_pct) + uint32_t(designated_asset_issuer_pct) == GRAPHENE_100_PERCENT );
const fba_accumulator_object& fba = fba_accumulator_id_type( fba_id )(db);
if( fba.accumulated_fba_fees == 0 )
return;
const asset_object& core = asset_id_type(0)(db);
const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(db);
if( !fba.is_configured(db) )
{
ilog( "${n} core given to network at block ${b} due to non-configured FBA", ("n", fba.accumulated_fba_fees)("b", db.head_block_time()) );
db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd )
{
_core_dd.current_supply -= fba.accumulated_fba_fees;
} );
db.modify( fba, [&]( fba_accumulator_object& _fba )
{
_fba.accumulated_fba_fees = 0;
} );
return;
}
fc::uint128_t buyback_amount_128 = fba.accumulated_fba_fees.value;
buyback_amount_128 *= designated_asset_buyback_pct;
buyback_amount_128 /= GRAPHENE_100_PERCENT;
share_type buyback_amount = buyback_amount_128.to_uint64();
fc::uint128_t issuer_amount_128 = fba.accumulated_fba_fees.value;
issuer_amount_128 *= designated_asset_issuer_pct;
issuer_amount_128 /= GRAPHENE_100_PERCENT;
share_type issuer_amount = issuer_amount_128.to_uint64();
// this assert should never fail
FC_ASSERT( buyback_amount + issuer_amount <= fba.accumulated_fba_fees );
share_type network_amount = fba.accumulated_fba_fees - (buyback_amount + issuer_amount);
const asset_object& designated_asset = (*fba.designated_asset)(db);
if( network_amount != 0 )
{
db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd )
{
_core_dd.current_supply -= network_amount;
} );
}
fba_distribute_operation vop;
vop.account_id = *designated_asset.buyback_account;
vop.fba_id = fba.id;
vop.amount = buyback_amount;
if( vop.amount != 0 )
{
db.adjust_balance( *designated_asset.buyback_account, asset(buyback_amount) );
db.push_applied_operation(vop);
}
vop.account_id = designated_asset.issuer;
vop.fba_id = fba.id;
vop.amount = issuer_amount;
if( vop.amount != 0 )
{
db.adjust_balance( designated_asset.issuer, asset(issuer_amount) );
db.push_applied_operation(vop);
}
db.modify( fba, [&]( fba_accumulator_object& _fba )
{
_fba.accumulated_fba_fees = 0;
} );
}
void distribute_fba_balances( database& db )
{
split_fba_balance( db, fba_accumulator_id_transfer_to_blind , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );
split_fba_balance( db, fba_accumulator_id_blind_transfer , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );
split_fba_balance( db, fba_accumulator_id_transfer_from_blind, 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT );
}
void create_buyback_orders( database& db )
{
const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get<by_id>();
@ -597,6 +688,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
{
const auto& gpo = get_global_properties();
distribute_fba_balances(*this);
create_buyback_orders(*this);
struct vote_tally_helper {

View file

@ -30,6 +30,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -103,4 +104,18 @@ database& generic_evaluator::db()const { return trx_state->db(); }
}
} FC_CAPTURE_AND_RETHROW() }
void generic_evaluator::pay_fba_fee( uint64_t fba_id )
{
database& d = db();
const fba_accumulator_object& fba = d.get< fba_accumulator_object >( fba_accumulator_id_type( fba_id ) );
if( !fba.is_configured(d) )
{
generic_evaluator::pay_fee();
return;
}
d.modify( fba, [&]( fba_accumulator_object& _fba )
{
_fba.accumulated_fba_fees += core_fee_paid;
} );
}
} }

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/fba_object.hpp>
namespace graphene { namespace chain {
bool fba_accumulator_object::is_configured( const database& db )const
{
if( !designated_asset.valid() )
{
ilog( "FBA fee in block ${b} not paid because designated asset was not configured", ("b", db.head_block_num()) );
return false;
}
const asset_object* dasset = db.find(*designated_asset);
if( dasset == nullptr )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not exist", ("b", db.head_block_num()) );
return false;
}
if( dasset->is_market_issued() )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: FBA is a BitAsset", ("b", db.head_block_num()) );
return false;
}
const uint16_t allowed_flags = charge_market_fee;
// check enabled issuer_permissions bits is subset of allowed_flags bits
if( (dasset->options.issuer_permissions & allowed_flags) != dasset->options.issuer_permissions )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed permissions enabled", ("b", db.head_block_num()) );
return false;
}
// check enabled issuer_permissions bits is subset of allowed_flags bits
if( (dasset->options.flags & allowed_flags) != dasset->options.flags )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed flags enabled", ("b", db.head_block_num()) );
return false;
}
if( !dasset->buyback_account.valid() )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not have a buyback account", ("b", db.head_block_num()) );
return false;
}
const account_object& issuer_acct = dasset->issuer(db);
if( issuer_acct.owner_special_authority.which() != special_authority::tag< top_holders_special_authority >::value )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set owner top_n control", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.active_special_authority.which() != special_authority::tag< top_holders_special_authority >::value )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set active top_n control", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.owner_special_authority.get< top_holders_special_authority >().asset != *designated_asset )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.active_special_authority.get< top_holders_special_authority >().asset != *designated_asset )
{
ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) );
return false;
}
if( issuer_acct.top_n_control_flags != (account_object::top_n_control_owner | account_object::top_n_control_active) )
{
ilog( "FBA fee in block ${b} not paid because designated asset's top_n control has not yet activated (wait until next maintenance interval)", ("b", db.head_block_num()) );
return false;
}
return true;
}
} }

View file

@ -0,0 +1,4 @@
// #563 Stealth fee routing
#ifndef HARDFORK_563_TIME
#define HARDFORK_563_TIME (fc::time_point_sec( 1455127200 ))
#endif

View file

@ -0,0 +1,4 @@
// #572 Allow asset to update permission flags when no supply exists
#ifndef HARDFORK_572_TIME
#define HARDFORK_572_TIME (fc::time_point_sec( 1450378800 ))
#endif

View file

@ -208,6 +208,14 @@ namespace graphene { namespace chain {
special_authority owner_special_authority = no_special_authority();
special_authority active_special_authority = no_special_authority();
/**
* This flag is set when the top_n logic sets both authorities,
* and gets reset when authority or special_authority is set.
*/
uint8_t top_n_control_flags = 0;
static const uint8_t top_n_control_owner = 1;
static const uint8_t top_n_control_active = 2;
/**
* This is a set of assets which the account is allowed to have.
* This is utilized to restrict buyback accounts to the assets that trade in their markets.
@ -365,6 +373,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object,
(whitelisted_accounts)(blacklisted_accounts)
(cashback_vb)
(owner_special_authority)(active_special_authority)
(top_n_control_flags)
(allowed_assets)
)

View file

@ -37,6 +37,8 @@ class transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator
void_result do_evaluate( const transfer_to_blind_operation& o );
void_result do_apply( const transfer_to_blind_operation& o ) ;
virtual void pay_fee() override;
};
class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>
@ -46,6 +48,8 @@ class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evalu
void_result do_evaluate( const transfer_from_blind_operation& o );
void_result do_apply( const transfer_from_blind_operation& o ) ;
virtual void pay_fee() override;
};
class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
@ -55,6 +59,8 @@ class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
void_result do_evaluate( const blind_transfer_operation& o );
void_result do_apply( const blind_transfer_operation& o ) ;
virtual void pay_fee() override;
};
} } // namespace graphene::chain

View file

@ -167,3 +167,6 @@
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@}
// hack for unit test
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(1))

View file

@ -138,6 +138,11 @@ namespace graphene { namespace chain {
object_id_type get_relative_id( object_id_type rel_id )const;
/**
* pay_fee() for FBA subclass should simply call this method
*/
void pay_fba_fee( uint64_t fba_id );
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
/**
* An object will be created at genesis for each of these FBA accumulators.
*/
enum graphene_fba_accumulator_id_enum
{
fba_accumulator_id_transfer_to_blind = 0,
fba_accumulator_id_blind_transfer,
fba_accumulator_id_transfer_from_blind,
fba_accumulator_id_count
};
} }

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
class database;
/**
* fba_accumulator_object accumulates fees to be paid out via buyback or other FBA mechanism.
*/
class fba_accumulator_object : public graphene::db::abstract_object< fba_accumulator_object >
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_fba_accumulator_object_type;
share_type accumulated_fba_fees;
optional< asset_id_type > designated_asset;
bool is_configured( const database& db )const;
};
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), (accumulated_fba_fees)(designated_asset) )

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct fba_distribute_operation : public base_operation
{
struct fee_parameters_type {};
asset fee; // always zero
account_id_type account_id;
fba_accumulator_id_type fba_id;
share_type amount;
account_id_type fee_payer()const { return account_id; }
void validate()const { FC_ASSERT( false ); }
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};
} }
FC_REFLECT( graphene::chain::fba_distribute_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::fba_distribute_operation, (fee)(account_id)(fba_id)(amount) )

View file

@ -29,6 +29,8 @@
#include <graphene/chain/protocol/balance.hpp>
#include <graphene/chain/protocol/custom.hpp>
#include <graphene/chain/protocol/committee_member.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/protocol/fba.hpp>
#include <graphene/chain/protocol/market.hpp>
#include <graphene/chain/protocol/proposal.hpp>
#include <graphene/chain/protocol/transfer.hpp>
@ -36,7 +38,6 @@
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
#include <graphene/chain/protocol/confidential.hpp>
namespace graphene { namespace chain {
@ -89,7 +90,8 @@ namespace graphene { namespace chain {
blind_transfer_operation,
transfer_from_blind_operation,
asset_settle_cancel_operation, // VIRTUAL
asset_claim_fees_operation
asset_claim_fees_operation,
fba_distribute_operation // VIRTUAL
> operation;
/// @} // operations group

View file

@ -154,7 +154,8 @@ namespace graphene { namespace chain {
impl_witness_schedule_object_type,
impl_budget_record_object_type,
impl_special_authority_object_type,
impl_buyback_object_type
impl_buyback_object_type,
impl_fba_accumulator_object_type
};
//typedef fc::unsigned_int object_id_type;
@ -205,6 +206,7 @@ namespace graphene { namespace chain {
class budget_record_object;
class special_authority_object;
class buyback_object;
class fba_accumulator_object;
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
@ -224,6 +226,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_blinded_balance_object_type, blinded_balance_object > blinded_balance_id_type;
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type;
typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
@ -355,6 +358,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_budget_record_object_type)
(impl_special_authority_object_type)
(impl_buyback_object_type)
(impl_fba_accumulator_object_type)
)
FC_REFLECT_TYPENAME( graphene::chain::share_type )
@ -384,6 +388,9 @@ FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type )
FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type )
FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type )
FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags,

View file

@ -63,6 +63,11 @@ struct vote_counter
out_auth = auth;
}
bool is_empty()const
{
return (total_votes == 0);
}
uint64_t last_votes = std::numeric_limits<uint64_t>::max();
uint64_t total_votes = 0;
int8_t bitshift = -1;

View file

@ -32,6 +32,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
@ -195,6 +196,8 @@ void database_fixture::verify_asset_supplies( const database& db )
}
for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() )
total_balances[ vbo.balance.asset_id ] += vbo.balance.amount;
for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() )
total_balances[ asset_id_type() ] += fba.accumulated_fba_fees;
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
@ -1033,6 +1036,24 @@ int64_t database_fixture::get_balance( const account_object& account, const asse
return db.get_balance(account.get_id(), a.get_id()).amount.value;
}
vector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const
{
vector< operation_history_object > result;
const auto& stats = account_id(db).statistics(db);
if(stats.most_recent_op == account_transaction_history_id_type())
return result;
const account_transaction_history_object* node = &stats.most_recent_op(db);
while( true )
{
result.push_back( node->operation_id(db) );
if(node->next == account_transaction_history_id_type())
break;
node = db.find(node->next);
}
return result;
}
namespace test {
void set_expiration( const database& db, transaction& tx )

View file

@ -28,6 +28,8 @@
#include <fc/io/json.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <iostream>
using namespace graphene::db;
@ -278,6 +280,7 @@ struct database_fixture {
void print_joint_market( const string& syma, const string& symb )const;
int64_t get_balance( account_id_type account, asset_id_type a )const;
int64_t get_balance( const account_object& account, const asset_object& a )const;
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
};
namespace test {

View file

@ -24,7 +24,12 @@
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
@ -721,4 +726,210 @@ BOOST_AUTO_TEST_CASE( fee_refund_test )
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( stealth_fba_test )
{
try
{
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) );
upgrade_to_lifetime_member(philbin_id);
generate_blocks( HARDFORK_538_TIME );
generate_blocks( HARDFORK_555_TIME );
generate_blocks( HARDFORK_563_TIME );
generate_blocks( HARDFORK_572_TIME );
// Philbin (registrar who registers Rex)
// Izzy (initial issuer of stealth asset, will later transfer to Tom)
// Alice, Bob, Chloe, Dan (ABCD)
// Rex (recycler -- buyback account for stealth asset)
// Tom (owner of stealth asset who will be set as top_n authority)
// Izzy creates STEALTH
asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db),
disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id;
/*
// this is disabled because it doesn't work, our modify() is probably being overwritten by undo
//
// Init blockchain with stealth ID's
// On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
// causing the designated_asset fields of these objects to be set at genesis, but for
// this test we modify the db directly.
//
auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id )
{
db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba )
{
fba.designated_asset = asset_id;
} );
};
set_fba_asset( fba_accumulator_id_transfer_to_blind , stealth_id );
set_fba_asset( fba_accumulator_id_blind_transfer , stealth_id );
set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id );
*/
// Izzy kills some permission bits (this somehow happened to the real STEALTH in production)
{
asset_update_operation update_op;
update_op.issuer = izzy_id;
update_op.asset_to_update = stealth_id;
asset_options new_options;
new_options = stealth_id(db).options;
new_options.issuer_permissions = charge_market_fee;
new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee;
// after fixing #579 you should be able to delete the following line
new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) );
update_op.new_options = new_options;
signed_transaction tx;
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, izzy_private_key );
PUSH_TX( db, tx );
}
// Izzy transfers issuer duty to Tom
{
asset_update_operation update_op;
update_op.issuer = izzy_id;
update_op.asset_to_update = stealth_id;
update_op.new_issuer = tom_id;
// new_options should be optional, but isn't...the following line should be unnecessary #580
update_op.new_options = stealth_id(db).options;
signed_transaction tx;
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, izzy_private_key );
PUSH_TX( db, tx );
}
// Tom re-enables the permission bits to clear the flags, then clears them again
// Allowed by #572 when current_supply == 0
{
asset_update_operation update_op;
update_op.issuer = tom_id;
update_op.asset_to_update = stealth_id;
asset_options new_options;
new_options = stealth_id(db).options;
new_options.issuer_permissions = new_options.flags | charge_market_fee;
update_op.new_options = new_options;
signed_transaction tx;
// enable perms is one op
tx.operations.push_back( update_op );
new_options.issuer_permissions = charge_market_fee;
new_options.flags = charge_market_fee;
update_op.new_options = new_options;
// reset wrongly set flags and reset permissions can be done in a single op
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, tom_private_key );
PUSH_TX( db, tx );
}
// Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom)
account_id_type rex_id;
{
buyback_account_options bbo;
bbo.asset_to_buy = stealth_id;
bbo.asset_to_buy_issuer = tom_id;
bbo.markets.emplace( asset_id_type() );
account_create_operation create_op = make_account( "rex" );
create_op.registrar = philbin_id;
create_op.extensions.value.buyback_options = bbo;
create_op.owner = authority::null_authority();
create_op.active = authority::null_authority();
signed_transaction tx;
tx.operations.push_back( create_op );
set_expiration( db, tx );
sign( tx, philbin_private_key );
sign( tx, tom_private_key );
processed_transaction ptx = PUSH_TX( db, tx );
rex_id = ptx.operation_results.back().get< object_id_type >();
}
// Tom issues some asset to Alice and Bob
set_expiration( db, trx ); // #11
issue_uia( alice_id, asset( 1000, stealth_id ) );
issue_uia( bob_id, asset( 1000, stealth_id ) );
// Tom sets his authority to the top_n of the asset
{
top_holders_special_authority top2;
top2.num_top_holders = 2;
top2.asset = stealth_id;
account_update_operation op;
op.account = tom_id;
op.extensions.value.active_special_authority = top2;
op.extensions.value.owner_special_authority = top2;
signed_transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
sign( tx, tom_private_key );
PUSH_TX( db, tx );
}
// Wait until the next maintenance interval for top_n to take effect
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// Do a blind op to add some fees to the pool.
fund( chloe_id(db), asset( 100000, asset_id_type() ) );
auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation
{
fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) );
public_key_type blind_pub = blind_key.get_public_key();
fc::sha256 secret = fc::sha256::hash( key+"-secret" );
fc::sha256 nonce = fc::sha256::hash( key+"-nonce" );
transfer_to_blind_operation op;
blind_output blind_out;
blind_out.owner = authority( 1, blind_pub, 1 );
blind_out.commitment = fc::ecc::blind( secret, amount.amount.value );
blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value );
op.amount = amount;
op.from = account;
op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 );
op.outputs = {blind_out};
return op;
};
{
transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" );
op.fee = asset( 1000, asset_id_type() );
signed_transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
sign( tx, chloe_private_key );
PUSH_TX( db, tx );
}
// wait until next maint interval
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
idump( ( get_operation_history( chloe_id ) ) );
idump( ( get_operation_history( rex_id ) ) );
idump( ( get_operation_history( tom_id ) ) );
}
catch( const fc::exception& e )
{
elog( "caught exception ${e}", ("e", e.to_detail_string()) );
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()