SON194-SON195 - Report SON Down, addition of SON Account for sidechain consensus (#244)
* SON194-SON195 - Addition of SON BTC Account and report son down changes
* SON194-SON195 - SON BTC Account errors rectification
* SON194-SON195 - Adding Tests
* User sidechain address mappings (#240)
* WIP: Sidechain objects
* Revert "WIP: Sidechain objects"
This reverts commit 8676940a28.
* WIP: User sidechain address mappings
* Fix reflection problem
* Reflect missing members of sidechain_address_update_operation
* Add sidechain address operation tests
* Enable RPC calls
* Fix build errors due to merge conflict
* Fix RPC, add CLI wallet commands for sidechain addresses
* Improved peerplays_sidechain_plugin_impl
* Remove short param for son-id
* Fix crashing errors on bitcoin event received
* Code review changes
* SON207 - Introduce scheduling for SONs similar to witnesses (#251)
* Extend SON objects to contain sidechain public keys (#254)
Co-authored-by: obucinac <obucinac@users.noreply.github.com>
This commit is contained in:
parent
31ec55514b
commit
01fb1db6a6
11 changed files with 254 additions and 2 deletions
|
|
@ -319,6 +319,9 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ void database::initialize_evaluators()
|
|||
register_evaluator<add_sidechain_address_evaluator>();
|
||||
register_evaluator<update_sidechain_address_evaluator>();
|
||||
register_evaluator<delete_sidechain_address_evaluator>();
|
||||
register_evaluator<son_report_down_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
|
|||
|
|
@ -461,6 +461,70 @@ void database::update_active_sons()
|
|||
flat_set<son_id_type> active_sons(gpo.active_sons.begin(), gpo.active_sons.end());
|
||||
_sso.scheduler.update(active_sons);
|
||||
});
|
||||
|
||||
if(gpo.active_sons.size() > 0 ) {
|
||||
if(gpo.parameters.get_son_btc_account_id() == GRAPHENE_NULL_ACCOUNT) {
|
||||
const auto& son_btc_account = create<account_object>( [&]( account_object& obj ) {
|
||||
uint64_t total_votes = 0;
|
||||
obj.name = "son_btc_account";
|
||||
obj.statistics = create<account_statistics_object>([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id;
|
||||
obj.membership_expiration_date = time_point_sec::maximum();
|
||||
obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
|
||||
for( const auto& son_id : gpo.active_sons )
|
||||
{
|
||||
const son_object& son = get(son_id);
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
|
||||
// then I want to keep the most significant 16 bits of what's left.
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
|
||||
for( const auto& son_id : gpo.active_sons )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
const son_object& son = get(son_id);
|
||||
uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) );
|
||||
obj.active.account_auths[son.son_account] += votes;
|
||||
obj.active.weight_threshold += votes;
|
||||
}
|
||||
obj.active.weight_threshold *= 2;
|
||||
obj.active.weight_threshold /= 3;
|
||||
obj.active.weight_threshold += 1;
|
||||
});
|
||||
|
||||
modify( gpo, [&]( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
});
|
||||
} else {
|
||||
modify( get(gpo.parameters.get_son_btc_account_id()), [&]( account_object& obj )
|
||||
{
|
||||
uint64_t total_votes = 0;
|
||||
for( const auto& son_id : gpo.active_sons )
|
||||
{
|
||||
const son_object& son = get(son_id);
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
|
||||
// then I want to keep the most significant 16 bits of what's left.
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
for( const auto& son_id : gpo.active_sons )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
const son_object& son = get(son_id);
|
||||
uint16_t votes = std::max((_vote_tally_buffer[son.vote_id] >> bits_to_drop), uint64_t(1) );
|
||||
obj.active.account_auths[son.son_account] += votes;
|
||||
obj.active.weight_threshold += votes;
|
||||
}
|
||||
obj.active.weight_threshold *= 2;
|
||||
obj.active.weight_threshold /= 3;
|
||||
obj.active.weight_threshold += 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
|
|
|
|||
|
|
@ -306,6 +306,9 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ namespace graphene { namespace chain {
|
|||
optional < uint32_t > son_vesting_amount;
|
||||
optional < uint32_t > son_vesting_period;
|
||||
optional < uint32_t > son_pay_daily_max;
|
||||
optional < account_id_type > son_btc_account;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -138,6 +139,9 @@ namespace graphene { namespace chain {
|
|||
inline uint16_t son_pay_daily_max()const {
|
||||
return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX;
|
||||
}
|
||||
inline account_id_type get_son_btc_account_id() const {
|
||||
return extensions.value.son_btc_account.valid() ? *extensions.value.son_btc_account : GRAPHENE_NULL_ACCOUNT;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -155,6 +159,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(son_vesting_amount)
|
||||
(son_vesting_period)
|
||||
(son_pay_daily_max)
|
||||
(son_btc_account)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -144,7 +144,8 @@ namespace graphene { namespace chain {
|
|||
son_heartbeat_operation,
|
||||
sidechain_address_add_operation,
|
||||
sidechain_address_update_operation,
|
||||
sidechain_address_delete_operation
|
||||
sidechain_address_delete_operation,
|
||||
son_report_down_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -63,6 +63,19 @@ namespace graphene { namespace chain {
|
|||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_report_down_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type payer;
|
||||
time_point_sec down_ts;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) )
|
||||
|
|
@ -77,4 +90,7 @@ FC_REFLECT(graphene::chain::son_delete_operation::fee_parameters_type, (fee) )
|
|||
FC_REFLECT(graphene::chain::son_delete_operation, (fee)(son_id)(payer)(owner_account) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) )
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) )
|
||||
|
|
@ -40,4 +40,13 @@ public:
|
|||
object_id_type do_apply(const son_heartbeat_operation& o);
|
||||
};
|
||||
|
||||
class son_report_down_evaluator : public evaluator<son_report_down_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_report_down_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_report_down_operation& o);
|
||||
object_id_type do_apply(const son_report_down_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_report_down_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
for (const op_wrapper &op : v.proposed_ops)
|
||||
|
|
|
|||
|
|
@ -143,4 +143,36 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation&
|
|||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account");
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
auto itr = idx.find(op.son_id);
|
||||
auto stats = itr->statistics( db() );
|
||||
FC_ASSERT(itr->status == son_status::active, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if (itr->status == son_status::active) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_down_timestamp = op.down_ts;
|
||||
});
|
||||
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
so.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
|
|
@ -746,4 +746,118 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts);
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
||||
|
||||
try
|
||||
{
|
||||
INVOKE(son_heartbeat_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
|
||||
generate_block();
|
||||
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( sidx.size() == 1 );
|
||||
auto son_stats_obj = sidx.find( obj->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
BOOST_CHECK( obj->status == son_status::active);
|
||||
|
||||
const auto& son_btc_account = db.create<account_object>( [&]( account_object& obj ) {
|
||||
obj.name = "son_btc_account";
|
||||
obj.statistics = db.create<account_statistics_object>([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id;
|
||||
obj.membership_expiration_date = time_point_sec::maximum();
|
||||
obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
|
||||
obj.owner.add_authority( bob_id, 1 );
|
||||
obj.active.add_authority( bob_id, 1 );
|
||||
obj.active.weight_threshold = 1;
|
||||
obj.owner.weight_threshold = 1;
|
||||
});
|
||||
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.extensions.value.son_pay_daily_max = 200;
|
||||
_gpo.parameters.witness_pay_per_block = 0;
|
||||
|
||||
_gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
if( _gpo.pending_parameters )
|
||||
_gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
});
|
||||
|
||||
{
|
||||
// Check that transaction fails if down_ts < last_active_timestamp
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1));
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction fails if payer is not son_btc_account.
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = alice_id;
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction succeeds after getting enough approvals on son_btc_account.
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
|
||||
BOOST_CHECK( obj->status == son_status::in_maintenance);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction fails if report down sent for an in_maintenance SON.
|
||||
generate_block();
|
||||
// Send Report Down Operation for an active status SON
|
||||
son_report_down_operation op;
|
||||
op.payer = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
op.son_id = son_id_type(0);
|
||||
op.down_ts = son_stats_obj->last_active_timestamp;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
// Expect an exception
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
} BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue