Skip to content

Commit fc2fe17

Browse files
author
ABW
committed
block oversized authority in account recovery request (HF29)
1 parent 34945bc commit fc2fe17

4 files changed

Lines changed: 142 additions & 6 deletions

File tree

libraries/chain/hive_evaluator_account.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,17 +497,21 @@ void request_account_recovery_evaluator::do_apply( const request_account_recover
497497
const auto& recovery_request_idx = _db.get_index< account_recovery_request_index, by_account >();
498498
auto request = recovery_request_idx.find( o.account_to_recover );
499499

500-
FC_TODO( "validate_auth_size should always be called" );
501-
//ABW: now there is a bug - editing existing request can introduce authority wider than max,
502-
//such authority can also be used when cancelling (although it is kind of "workaroundable")
503-
//HF29 is needed to fix this, after that the check should be moved to validate
500+
bool checked_size = false;
501+
// TODO: after activation of HF29 check if the check can be moved to operation validate
502+
if( _db.is_in_control() || _db.has_hardfork( HIVE_HARDFORK_1_29_FIX_ENFORCEMENT_OF_AUTHORITY_SIZE_ON_RECOVERY_REQUEST ) )
503+
{
504+
validate_auth_size( o.new_owner_authority );
505+
checked_size = true;
506+
}
504507

505508
if( request == recovery_request_idx.end() ) // New Request
506509
{
507510
HIVE_CHAIN_STATE_ASSERT( !o.new_owner_authority.is_impossible() && "Cannot recover using an impossible authority.", o.account_to_recover, "Recovery authority for '${subject}' is impossible." );
508511
HIVE_CHAIN_STATE_ASSERT( o.new_owner_authority.weight_threshold, o.account_to_recover, "Cannot recover using an open authority." );
509512

510-
validate_auth_size( o.new_owner_authority );
513+
if( !checked_size )
514+
validate_auth_size( o.new_owner_authority );
511515

512516
// Check accounts in the new authority exist
513517
verify_authority_accounts_exist( _db, o.new_owner_authority, o.account_to_recover, authority::owner );

libraries/protocol/hardfork.d/1_29.hf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@
2020

2121
// allow modification of existing recurrent transfer with executions=1 (issue #786)
2222
#define HIVE_HARDFORK_1_29_ALLOW_MODIFY_LAST_RECURRENT_TRANSFER (HIVE_HARDFORK_1_29)
23+
// authority size limit can no longer be circumvented with use of account recovery
24+
#define HIVE_HARDFORK_1_29_FIX_ENFORCEMENT_OF_AUTHORITY_SIZE_ON_RECOVERY_REQUEST (HIVE_HARDFORK_1_29)
2325
#endif

tests/unit/db_fixture/database_fixture.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ void database_fixture::generate_block(uint32_t skip, const fc::ecc::private_key&
143143

144144
void database_fixture::generate_block_from_pending()
145145
{
146-
db_plugin->debug_generate_blocks( fc::optional< fc::ecc::private_key >(), 1, default_skip, 0, true, true );
146+
db_plugin->debug_generate_blocks( committee, 1, default_skip, 0, true, true );
147147
}
148148

149149
uint32_t database_fixture::generate_blocks(const fc::ecc::private_key& debug_key, uint32_t count, uint32_t skip)

tests/unit/tests/hf29_tests.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#ifdef IS_TEST_NET
2+
3+
#include <boost/test/unit_test.hpp>
4+
5+
#include <hive/chain/database_exceptions.hpp>
6+
#include <hive/chain/detail/state/account_object.hpp>
7+
#include <hive/chain/detail/state/account_object_multiindex.hpp>
8+
9+
#include <hive/plugins/debug_node/debug_node_plugin.hpp>
10+
11+
#include "../db_fixture/clean_database_fixture.hpp"
12+
13+
using namespace hive::chain;
14+
using namespace hive::protocol;
15+
16+
BOOST_FIXTURE_TEST_SUITE( hf29_tests, cluster_database_fixture )
17+
18+
BOOST_AUTO_TEST_CASE( recovery_request_oversized_authority )
19+
{
20+
try
21+
{
22+
bool is_hf29 = false;
23+
24+
auto _content = [ &is_hf29 ]( ptr_hardfork_database_fixture& executor )
25+
{
26+
BOOST_TEST_MESSAGE( "Testing: oversized authority in recovery request edit/cancel" );
27+
BOOST_REQUIRE_EQUAL( (bool)executor, true );
28+
29+
ACTORS_EXT( (*executor), (alice)(bob) )
30+
executor->fund( "alice", HIVE_asset( 1'000'000 ) );
31+
executor->db_plugin->debug_update( []( database& db )
32+
{
33+
const auto& alice = db.get_account( "alice" );
34+
db.modify( db.get_account( "bob" ), [&]( account_object& a )
35+
{
36+
a.set_recovery_account( alice );
37+
} );
38+
} );
39+
executor->generate_block();
40+
41+
request_account_recovery_operation initial_request;
42+
initial_request.recovery_account = "alice";
43+
initial_request.account_to_recover = "bob";
44+
initial_request.new_owner_authority = authority(
45+
1, executor->generate_private_key( "k0" ).get_public_key(), 1 );
46+
executor->push_transaction( initial_request, alice_private_key );
47+
executor->generate_block();
48+
49+
const auto& idx = executor->db->get_index< account_recovery_request_index, by_account >();
50+
BOOST_REQUIRE( idx.find( "bob" ) != idx.end() );
51+
BOOST_REQUIRE_EQUAL( idx.find( "bob" )->get_new_owner_authority().key_auths.size(), 1u );
52+
53+
authority oversized;
54+
oversized.weight_threshold = 1;
55+
for( int i = 0; i <= HIVE_MAX_AUTHORITY_MEMBERSHIP; ++i )
56+
oversized.add_authority( executor->generate_private_key( "extra_" + std::to_string( i ) ).get_public_key(), 1 );
57+
58+
request_account_recovery_operation oversized_edit;
59+
oversized_edit.recovery_account = "alice";
60+
oversized_edit.account_to_recover = "bob";
61+
oversized_edit.new_owner_authority = oversized;
62+
63+
// regular push_transaction is stopped on check due to "is_in_control"
64+
HIVE_REQUIRE_ASSERT( executor->push_transaction( oversized_edit, alice_private_key ),
65+
"size <= HIVE_MAX_AUTHORITY_MEMBERSHIP" );
66+
67+
// work around "is_in_control" by not actually executing transaction
68+
executor->db_plugin->debug_push_pending_transaction(
69+
executor->build_transaction( oversized_edit, alice_private_key ) );
70+
71+
if( is_hf29 )
72+
{
73+
// "p2p" block is stopped on faulty transaction after HF29
74+
HIVE_REQUIRE_ASSERT( executor->generate_block_from_pending(),
75+
"size <= HIVE_MAX_AUTHORITY_MEMBERSHIP" );
76+
}
77+
else
78+
{
79+
// "p2p" block is ok before HF28, even with faulty transaction
80+
executor->generate_block_from_pending();
81+
// oversized authority is included in edited request
82+
BOOST_REQUIRE_EQUAL( idx.find( "bob" )->get_new_owner_authority().key_auths.size(),
83+
size_t( HIVE_MAX_AUTHORITY_MEMBERSHIP + 1 ) );
84+
}
85+
86+
// weight_threshold == 0 marks cancellation
87+
oversized.weight_threshold = 0;
88+
89+
request_account_recovery_operation oversized_cancel;
90+
oversized_cancel.recovery_account = "alice";
91+
oversized_cancel.account_to_recover = "bob";
92+
oversized_cancel.new_owner_authority = oversized;
93+
94+
HIVE_REQUIRE_ASSERT( executor->push_transaction( oversized_cancel, alice_private_key ),
95+
"size <= HIVE_MAX_AUTHORITY_MEMBERSHIP" );
96+
97+
executor->db->clear_pending();
98+
executor->db_plugin->debug_push_pending_transaction(
99+
executor->build_transaction( oversized_cancel, alice_private_key ) );
100+
101+
if( is_hf29 )
102+
{
103+
HIVE_REQUIRE_ASSERT( executor->generate_block_from_pending(),
104+
"size <= HIVE_MAX_AUTHORITY_MEMBERSHIP" );
105+
// request not cancelled - still in the index with the original small authority
106+
BOOST_REQUIRE( idx.find( "bob" ) != idx.end() );
107+
BOOST_REQUIRE_EQUAL( idx.find( "bob" )->get_new_owner_authority().key_auths.size(), 1u );
108+
}
109+
else
110+
{
111+
executor->generate_block_from_pending();
112+
// oversized cancellation went through - request removed
113+
BOOST_REQUIRE( idx.find( "bob" ) == idx.end() );
114+
}
115+
};
116+
117+
BOOST_TEST_MESSAGE( "*****HF-28*****" );
118+
execute_hardfork<28>( _content );
119+
120+
is_hf29 = true;
121+
122+
BOOST_TEST_MESSAGE( "*****HF-29*****" );
123+
execute_hardfork<29>( _content );
124+
}
125+
FC_LOG_AND_RETHROW()
126+
}
127+
128+
BOOST_AUTO_TEST_SUITE_END()
129+
130+
#endif

0 commit comments

Comments
 (0)