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

Fix locked accounts #1728

Open
wants to merge 14 commits into
base: hardfork
Choose a base branch
from
127 changes: 123 additions & 4 deletions libraries/chain/account_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,57 @@
#include <graphene/chain/worker_object.hpp>

#include <algorithm>
#include <tuple>

namespace graphene {
namespace protocol {
void verify_cycled_authority( const account_id_type& id,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
uint32_t max_recursion_depth );
}
namespace chain {

namespace detail {

template <class DB>
void check_account_authorities(const account_id_type account_id,
const DB& db,
const optional<authority>& active,
const optional<authority>& owner)
{
const auto empty_auth = authority{};
const auto no_account = account_id_type{};

namespace graphene { namespace chain {
auto get_active = [&account_id, &db, &active, &owner, &empty_auth, &no_account]( account_id_type id )
{
if ( (no_account == id) || (account_id == id) )
{
if (active)
{
return &(*active);
}
return &empty_auth;
}
return &id(db).active;
};

auto get_owner = [&account_id, &db, &active, &owner, &empty_auth, &no_account]( account_id_type id )
{
if ( (no_account == id) || (account_id == id) )
{
if (owner)
{
return &(*owner);
}
return &empty_auth;
}
return &id(db).owner;
};

verify_cycled_authority(account_id, get_active, get_owner, db.get_global_properties().parameters.max_authority_depth);
};
} //detail

void verify_authority_accounts( const database& db, const authority& a )
{
Expand Down Expand Up @@ -133,6 +182,11 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
{
verify_authority_accounts( d, op.owner );
verify_authority_accounts( d, op.active );

if( d.head_block_time() >= HARDFORK_CYCLED_ACCOUNTS_TIME )
{
detail::check_account_authorities({}, d, op.active, op.owner);
}
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found )
Expand Down Expand Up @@ -276,8 +330,30 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio

try
{
if( o.owner ) verify_authority_accounts( d, *o.owner );
if( o.active ) verify_authority_accounts( d, *o.active );
if( o.owner )
{
verify_authority_accounts( d, *o.owner );
}
if( o.active )
{
verify_authority_accounts( d, *o.active );
}

try
{
detail::check_account_authorities(o.account, d, o.active, o.owner);
}
catch (tx_missing_active_auth)
{
if( d.head_block_time() < HARDFORK_CYCLED_ACCOUNTS_TIME )
{
cycle_detected = true;
}
else
{
throw;
}
}
}
GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded )
GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found )
Expand Down Expand Up @@ -316,7 +392,12 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
}

// update account object
d.modify( *acnt, [&o](account_object& a){
d.modify( *acnt, [&o, this](account_object& a){
if( cycle_detected && !a.stable_owner.valid())
{
a.stable_owner = a.owner;
}

if( o.owner )
{
a.owner = *o.owner;
Expand Down Expand Up @@ -360,6 +441,44 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

void_result account_unlock_evaluator::do_evaluate( const account_unlock_operation& o )
{ try {
database& d = db();
FC_ASSERT(d.head_block_time() >= HARDFORK_CYCLED_ACCOUNTS_TIME,
"Unlocking account is available after HARDFORK_CYCLED_ACCOUNTS_TIME only!"
);

acnt = &o.account_to_unlock(d);
FC_ASSERT( acnt->stable_owner.valid(), "Account ${a} is not unlockable.", ("a", o.account_to_unlock) );

return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

void_result account_unlock_evaluator::do_apply( const account_unlock_operation& o )
{ try {
database& d = db();

// update account object
d.modify( *acnt, [&o, this](account_object& a){
a.owner = *a.stable_owner;
a.stable_owner.reset();
});

const auto& bal_idx = d.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();
for( const auto& entry : bal_idx.get_account_balances( acnt->get_id() ) )
{
const auto balance = entry.second->get_balance();
const auto unlock_cost = balance.amount / 10;
const auto penalty = asset(unlock_cost, balance.asset_id);

d.adjust_balance(acnt->get_id(), -penalty);
d.adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, penalty);
d.push_applied_operation(account_unlock_penalty_payment_operation( acnt->get_id(), penalty ));
}

return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }

void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_operation& o)
{ try {
database& d = db();
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/db_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ void database::initialize_evaluators()
register_evaluator<account_create_evaluator>();
register_evaluator<account_update_evaluator>();
register_evaluator<account_upgrade_evaluator>();
register_evaluator<account_unlock_evaluator>();
register_evaluator<account_whitelist_evaluator>();
register_evaluator<committee_member_create_evaluator>();
register_evaluator<committee_member_update_evaluator>();
Expand Down
9 changes: 9 additions & 0 deletions libraries/chain/db_notify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ struct get_impacted_account_visitor
{
_impacted.insert( op.fee_payer() ); // account_id
}
void operator()(const account_unlock_penalty_payment_operation& op)
{
_impacted.insert( op.fee_payer() );
_impacted.insert( GRAPHENE_COMMITTEE_ACCOUNT );
}
void operator()( const execute_bid_operation& op )
{
_impacted.insert( op.fee_payer() ); // bidder
Expand All @@ -80,6 +85,10 @@ struct get_impacted_account_visitor
if( op.active )
add_authority_accounts( _impacted, *(op.active) );
}
void operator()( const account_unlock_operation& op )
{
_impacted.insert( op.fee_payer() ); // account_to_unlock
}
void operator()( const account_whitelist_operation& op )
{
_impacted.insert( op.fee_payer() ); // authorizing_account
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/CYCLED_ACCOUNTS_HF.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// #HARDFORK_CYCLED_ACCOUNTS_TIME Prevent to create/update account authorities that locks account.
#ifndef HARDFORK_CYCLED_ACCOUNTS_TIME
#define HARDFORK_CYCLED_ACCOUNTS_TIME (fc::time_point_sec( 1600000000 )) // Sunday, September 13, 2020 12:26:40 PM
#endif
12 changes: 12 additions & 0 deletions libraries/chain/include/graphene/chain/account_evaluator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class account_update_evaluator : public evaluator<account_update_evaluator>
void_result do_apply( const account_update_operation& o );

const account_object* acnt;
bool cycle_detected = false;
};

class account_upgrade_evaluator : public evaluator<account_upgrade_evaluator>
Expand All @@ -58,6 +59,17 @@ class account_upgrade_evaluator : public evaluator<account_upgrade_evaluator>
const account_object* account;
};

class account_unlock_evaluator : public evaluator<account_unlock_evaluator>
{
public:
typedef account_unlock_operation operation_type;

void_result do_evaluate( const account_unlock_operation& o );
void_result do_apply( const account_unlock_operation& o );

const account_object* acnt;
};

class account_whitelist_evaluator : public evaluator<account_whitelist_evaluator>
{
public:
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/graphene/chain/account_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ namespace graphene { namespace chain {
* update the active authority.
*/
authority owner;
optional<authority> stable_owner;
/// The owner authority contains the hot keys of the account. This authority has control over nearly all
/// operations the account may perform.
authority active;
Expand Down
3 changes: 1 addition & 2 deletions libraries/chain/include/graphene/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@

#define GRAPHENE_MAX_NESTED_OBJECTS (200)

#define GRAPHENE_CURRENT_DB_VERSION "20190503"
#define GRAPHENE_CURRENT_DB_VERSION "20191231"

#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3

4 changes: 4 additions & 0 deletions libraries/chain/proposal_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ struct proposal_operation_hardfork_visitor
void operator()(const graphene::chain::htlc_extend_operation &op) const {
FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" );
}
// hf_cycled_accounts
void operator()(const graphene::chain::account_unlock_operation &op) const {
FC_ASSERT( block_time >= HARDFORK_CYCLED_ACCOUNTS_TIME, "Not allowed until hardfork CYCLED_ACCOUNTS" );
}
// loop and self visit in proposals
void operator()(const graphene::chain::proposal_create_operation &v) const {
bool already_contains_proposal_update = false;
Expand Down
14 changes: 14 additions & 0 deletions libraries/protocol/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,17 @@ void account_update_operation::validate()const
validate_special_authority( *extensions.value.active_special_authority );
}

share_type account_unlock_operation::calculate_fee( const fee_parameters_type& k ) const
{
auto core_fee_required = k.fee;
return core_fee_required;
}

void account_unlock_operation::validate() const
{
FC_ASSERT( fee.amount >= 0 );
}

share_type account_upgrade_operation::calculate_fee(const fee_parameters_type& k) const
{
if( upgrade_to_lifetime_member )
Expand All @@ -284,9 +295,12 @@ GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_op
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_penalty_payment_operation )
69 changes: 67 additions & 2 deletions libraries/protocol/include/graphene/protocol/account.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,63 @@ namespace graphene { namespace protocol {
{ if( !is_owner_update() ) a.insert( account ); }
};

/**
* @ingroup operations
* @brief Fix locked account and restore access
*
* This operation is used to unlock account being blocked.
*/
struct account_unlock_operation : public base_operation
{
struct fee_parameters_type
{
share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
};

asset fee;
account_id_type account_to_unlock;
authority previous_authority;
extensions_type extensions;

account_id_type fee_payer() const { return account_to_unlock; }
void validate() const;
share_type calculate_fee( const fee_parameters_type& k ) const;

void get_required_active_authorities( flat_set<account_id_type>& active) const
{
// a little bit tricky, see operations.cpp : operation_get_required_auth
// active.insert( v.fee_payer() ); - fee_payer added explicitly as active authority
// but we needn't it to authorize because it is locked
active.erase( account_to_unlock );
}

void get_required_authorities( vector<authority>& a ) const
{
a.push_back( previous_authority );
}
};

struct account_unlock_penalty_payment_operation : public base_operation
{
struct fee_parameters_type {};

account_unlock_penalty_payment_operation(){}
account_unlock_penalty_payment_operation(
account_id_type a, asset p
)
:account_id(a), penalty(p), fee() {}

account_id_type account_id;
asset penalty;
asset fee;

account_id_type fee_payer()const { return account_id; }
void validate()const { FC_ASSERT( !"virtual operation" ); }

/// This is a virtual operation; there is no fee
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};

/**
* @brief This operation is used to whitelist and blacklist accounts, primarily for transacting in whitelisted assets
* @ingroup operations
Expand Down Expand Up @@ -288,27 +345,35 @@ FC_REFLECT( graphene::protocol::account_update_operation,
(fee)(account)(owner)(active)(new_options)(extensions)
)

FC_REFLECT( graphene::protocol::account_unlock_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::protocol::account_unlock_operation, (fee)(account_to_unlock)(previous_authority)(extensions) )


FC_REFLECT( graphene::protocol::account_upgrade_operation,
(fee)(account_to_upgrade)(upgrade_to_lifetime_member)(extensions) )

FC_REFLECT( graphene::protocol::account_whitelist_operation, (fee)(authorizing_account)(account_to_list)(new_listing)(extensions))

FC_REFLECT( graphene::protocol::account_create_operation::fee_parameters_type, (basic_fee)(premium_fee)(price_per_kbyte) )
FC_REFLECT( graphene::protocol::account_whitelist_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::protocol::account_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::protocol::account_upgrade_operation::fee_parameters_type, (membership_annual_fee)(membership_lifetime_fee) )
FC_REFLECT( graphene::protocol::account_transfer_operation::fee_parameters_type, (fee) )

FC_REFLECT( graphene::protocol::account_transfer_operation, (fee)(account_id)(new_owner)(extensions) )
FC_REFLECT( graphene::protocol::account_unlock_penalty_payment_operation, (fee)(account_id)(penalty) )
FC_REFLECT( graphene::protocol::account_unlock_penalty_payment_operation::fee_parameters_type, ) // VIRTUAL

GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_options )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation::fee_parameters_type )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_create_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_whitelist_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_update_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_upgrade_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_transfer_operation )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::account_unlock_penalty_payment_operation )

4 changes: 3 additions & 1 deletion libraries/protocol/include/graphene/protocol/operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ namespace graphene { namespace protocol {
htlc_redeem_operation,
htlc_redeemed_operation, // VIRTUAL
htlc_extend_operation,
htlc_refund_operation // VIRTUAL
htlc_refund_operation, // VIRTUAL
account_unlock_operation,
account_unlock_penalty_payment_operation //VIRTUAL
> operation;

/// @} // operations group
Expand Down
Loading