Skip to content
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