Skip to content

Commit e658bdd

Browse files
BSIP 40: Changes from code review
1 parent 4d95742 commit e658bdd

File tree

8 files changed

+72
-26
lines changed

8 files changed

+72
-26
lines changed

libraries/chain/custom_authority_evaluator.cpp

+21-11
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,21 @@ void_result custom_authority_create_evaluator::do_evaluate(const custom_authorit
4848
bool operation_forked_in = hardfork_visitor(now).visit((operation::tag_type)op.operation_type.value);
4949
FC_ASSERT(operation_forked_in, "Cannot create custom authority for operation which is not valid yet");
5050

51+
auto restriction_count = std::for_each(op.restrictions.begin(), op.restrictions.end(), restriction::adder()).sum;
52+
FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,
53+
"Custom authority has more than the maximum number of restrictions");
54+
5155
for (const auto& account_weight_pair : op.auth.account_auths)
5256
account_weight_pair.first(d);
5357

5458
const auto& index = d.get_index_type<custom_authority_index>().indices().get<by_account_custom>();
5559
auto range = index.equal_range(op.account);
5660
FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account,
57-
"Cannot create custom authority for account: account already has maximum number");
61+
"Cannot create custom authority: account already has maximum number");
62+
range = index.equal_range(boost::make_tuple(op.account, op.operation_type));
63+
FC_ASSERT(std::distance(range.first, range.second) < config->max_custom_authorities_per_account_op,
64+
"Cannot create custom authority: account already has maximum number for this operation type");
5865

59-
get_restriction_predicate(op.restrictions, op.operation_type);
6066
return void_result();
6167
} FC_CAPTURE_AND_RETHROW((op)) }
6268

@@ -74,21 +80,16 @@ object_id_type custom_authority_create_evaluator::do_apply(const custom_authorit
7480
std::for_each(op.restrictions.begin(), op.restrictions.end(), [&obj](const restriction& r) mutable {
7581
obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));
7682
});
77-
78-
// Update the predicate cache
79-
obj.update_predicate_cache();
8083
}).id;
8184
} FC_CAPTURE_AND_RETHROW((op)) }
8285

8386
void_result custom_authority_update_evaluator::do_evaluate(const custom_authority_update_operation& op)
8487
{ try {
8588
const database& d = db();
8689
auto now = d.head_block_time();
87-
FC_ASSERT(HARDFORK_BSIP_40_PASSED(now), "Custom active authorities are not yet enabled");
8890
old_object = &op.authority_to_update(d);
8991
FC_ASSERT(old_object->account == op.account, "Cannot update a different account's custom authority");
9092

91-
op.account(d);
9293
if (op.new_enabled)
9394
FC_ASSERT(*op.new_enabled != old_object->enabled,
9495
"Custom authority update specifies an enabled flag, but flag is not changed");
@@ -131,6 +132,17 @@ void_result custom_authority_update_evaluator::do_evaluate(const custom_authorit
131132
"Unable to add restrictions: causes wraparound of restriction IDs");
132133
}
133134

135+
// Add up the restriction counts for all old restrictions not being removed, and all new ones
136+
size_t restriction_count = 0;
137+
for (const auto& restriction_pair : old_object->restrictions)
138+
if (op.restrictions_to_remove.count(restriction_pair.first) == 0)
139+
restriction_count += restriction_pair.second.restriction_count();
140+
restriction_count += std::for_each(op.restrictions_to_add.begin(), op.restrictions_to_add.end(),
141+
restriction::adder()).sum;
142+
// Check restriction count against limit
143+
FC_ASSERT(restriction_count <= config->max_custom_authority_restrictions,
144+
"Cannot update custom authority: updated authority would exceed the maximum number of restrictions");
145+
134146
get_restriction_predicate(op.restrictions_to_add, old_object->operation_type);
135147
return void_result();
136148
} FC_CAPTURE_AND_RETHROW((op)) }
@@ -152,8 +164,8 @@ void_result custom_authority_update_evaluator::do_apply(const custom_authority_u
152164
obj.restrictions.insert(std::make_pair(obj.restriction_counter++, r));
153165
});
154166

155-
// Update the predicate cache
156-
obj.update_predicate_cache();
167+
// Clear the predicate cache
168+
obj.clear_predicate_cache();
157169
});
158170

159171
return void_result();
@@ -162,9 +174,7 @@ void_result custom_authority_update_evaluator::do_apply(const custom_authority_u
162174
void_result custom_authority_delete_evaluator::do_evaluate(const custom_authority_delete_operation& op)
163175
{ try {
164176
const database& d = db();
165-
FC_ASSERT(HARDFORK_BSIP_40_PASSED(d.head_block_time()), "Custom active authorities are not yet enabled");
166177

167-
op.account(d);
168178
old_object = &op.authority_to_delete(d);
169179
FC_ASSERT(old_object->account == op.account, "Cannot delete a different account's custom authority");
170180

libraries/chain/include/graphene/chain/custom_authority_object.hpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace graphene { namespace chain {
3939
class custom_authority_object : public abstract_object<custom_authority_object> {
4040
/// Unreflected field to store a cache of the predicate function
4141
/// Note that this cache can be modified when the object is const!
42-
optional<restriction_predicate_function> predicate_cache;
42+
mutable optional<restriction_predicate_function> predicate_cache;
4343

4444
public:
4545
static const uint8_t space_id = protocol_ids;
@@ -66,16 +66,17 @@ namespace graphene { namespace chain {
6666
}
6767
/// Get predicate, from cache if possible, and update cache if not (modifies const object!)
6868
restriction_predicate_function get_predicate() const {
69-
if (predicate_cache.valid())
70-
return *predicate_cache;
69+
if (!predicate_cache.valid())
70+
update_predicate_cache();
7171

72-
const_cast<custom_authority_object*>(this)->update_predicate_cache();
7372
return *predicate_cache;
7473
}
7574
/// Regenerate predicate function and update predicate cache
76-
void update_predicate_cache() {
75+
void update_predicate_cache() const {
7776
predicate_cache = get_restriction_predicate(get_restrictions(), operation_type);
7877
}
78+
/// Clear the cache of the predicate function
79+
void clear_predicate_cache() { predicate_cache.reset(); }
7980
};
8081

8182
struct by_account_custom;

libraries/chain/proposal_object.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ bool proposal_object::is_authorized_to_execute( database& db ) const
4141
available_key_approvals,
4242
[&db]( account_id_type id ){ return &id( db ).active; },
4343
[&db]( account_id_type id ){ return &id( db ).owner; },
44-
[&]( account_id_type id, const operation& op, rejected_predicate_map* rejects ){
44+
[&db]( account_id_type id, const operation& op, rejected_predicate_map* rejects ){
4545
return db.get_viable_custom_authorities(id, op, rejects); },
4646
allow_non_immediate_owner,
4747
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ),

libraries/protocol/custom_authorities/restriction_predicate.hxx

+4-7
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,12 @@ template<typename FieldContainer, typename ArgumentElement>
283283
struct predicate_has_all<FieldContainer, flat_set<ArgumentElement>,
284284
std::enable_if_t<is_container<FieldContainer> && !is_flat_set<FieldContainer> &&
285285
comparable_types<typename FieldContainer::value_type, ArgumentElement>>> {
286-
predicate_has_all<flat_set<typename FieldContainer::value_type>, flat_set<ArgumentElement>> inner;
287286
// Field is other container; convert to flat_set
288287
constexpr static bool valid = true;
289288
bool operator()(const FieldContainer& f, const flat_set<ArgumentElement>& a) const {
290289
if (f.size() < a.size()) return false;
291-
flat_set<typename FieldContainer::value_type> fs(f.begin(), f.end());
292-
return inner(fs, a);
290+
std::set<typename FieldContainer::value_type> fs(f.begin(), f.end());
291+
return std::includes(fs.begin(), fs.end(), a.begin(), a.end());
293292
}
294293
};
295294
template<typename OptionalType, typename Argument>
@@ -319,12 +318,10 @@ template<typename FieldContainer, typename ArgumentElement>
319318
struct predicate_has_none<FieldContainer, flat_set<ArgumentElement>,
320319
std::enable_if_t<is_container<FieldContainer> && !is_flat_set<FieldContainer> &&
321320
comparable_types<typename FieldContainer::value_type, ArgumentElement>>> {
322-
predicate_has_none<flat_set<typename FieldContainer::value_type>, flat_set<ArgumentElement>> inner;
323-
// Field is other container; convert to flat_set
321+
// Field is other container
324322
constexpr static bool valid = true;
325323
bool operator()(const FieldContainer& f, const flat_set<ArgumentElement>& a) const {
326-
flat_set<typename FieldContainer::value_type> fs(f.begin(), f.end());
327-
return inner(fs, a);
324+
return !std::any_of(f.begin(), f.end(), [&a](const auto& fe) { return a.count(fe) > 0; });
328325
}
329326
};
330327
template<typename OptionalType, typename Argument>

libraries/protocol/include/graphene/protocol/chain_parameters.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ namespace graphene { namespace protocol {
3939
{
4040
uint32_t max_custom_authority_lifetime_seconds = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS;
4141
uint32_t max_custom_authorities_per_account = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT;
42+
uint32_t max_custom_authorities_per_account_op = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT_OP;
43+
uint32_t max_custom_authority_restrictions = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_RESTRICTIONS;
4244
};
4345

4446
struct chain_parameters
@@ -108,6 +110,8 @@ FC_REFLECT( graphene::protocol::htlc_options,
108110
FC_REFLECT( graphene::protocol::custom_authority_options_type,
109111
(max_custom_authority_lifetime_seconds)
110112
(max_custom_authorities_per_account)
113+
(max_custom_authorities_per_account_op)
114+
(max_custom_authority_restrictions)
111115
)
112116

113117
FC_REFLECT( graphene::protocol::chain_parameters::ext,

libraries/protocol/include/graphene/protocol/config.hpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@
139139

140140
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743))
141141

142-
/// Maximum duration before a custom authority can expire
143-
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS (60*60*24*365)
142+
/// Maximum duration before a custom authority can expire (1 month)
143+
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS (60*60*24*30)
144144
/// Maximum number of custom authorities a particular account can set
145145
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT 10
146+
/// Maximum number of custom authorities a particular account can set for a particular operation
147+
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT_OP 3
148+
/// Maximum number of restrictions a custom authority can contain
149+
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_RESTRICTIONS 10

libraries/protocol/include/graphene/protocol/restriction.hpp

+20
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,26 @@ struct restriction {
107107
restriction() = default;
108108
restriction(const unsigned_int& member_index, function_type type, const argument_type& argument)
109109
: member_index(member_index), restriction_type(type), argument(argument) {}
110+
111+
struct adder {
112+
size_t sum = 0;
113+
void operator()(const restriction& r) { sum += r.restriction_count(); }
114+
void operator()(const vector<restriction>& r) { sum += std::for_each(r.begin(), r.end(), adder()).sum; }
115+
};
116+
117+
size_t restriction_count() const {
118+
if (argument.is_type<vector<restriction>>()) {
119+
const vector<restriction>& rs = argument.get<vector<restriction>>();
120+
return 1 + std::for_each(rs.begin(), rs.end(), adder()).sum;
121+
} else if (argument.is_type<vector<vector<restriction>>>()) {
122+
const vector<vector<restriction>>& rs = argument.get<vector<vector<restriction>>>();
123+
return 1 + std::for_each(rs.begin(), rs.end(), adder()).sum;
124+
} else if (argument.is_type<variant_assert_argument_type>()) {
125+
const variant_assert_argument_type& arg = argument.get<variant_assert_argument_type>();
126+
return 1 + std::for_each(arg.second.begin(), arg.second.end(), adder()).sum;
127+
}
128+
return 1;
129+
}
110130
};
111131

112132
} } // graphene::protocol

tests/tests/custom_authority_tests.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ BOOST_FIXTURE_TEST_SUITE(custom_authority_tests, database_fixture)
5252

5353
#define FUNC(TYPE) BOOST_PP_CAT(restriction::func_, TYPE)
5454

55+
size_t restriction_count(const vector<restriction>& rs) {
56+
return std::for_each(rs.begin(), rs.end(), restriction::adder()).sum;
57+
}
5558
template<typename Object>
5659
unsigned_int member_index(string name) {
5760
unsigned_int index;
@@ -106,6 +109,7 @@ BOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {
106109
// "extensions": []
107110
// }
108111
//]
112+
BOOST_CHECK_EQUAL(restriction_count(restrictions), 1);
109113
BOOST_CHECK(get_restriction_predicate(restrictions, operation::tag<transfer_operation>::value)(transfer)
110114
.rejection_path.size() == 2);
111115
// Index 0 (the outer-most) rejection path refers to the first and only restriction
@@ -222,6 +226,7 @@ BOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {
222226
// }
223227
//]
224228

229+
BOOST_CHECK_EQUAL(restriction_count(restrictions), 2);
225230
//////
226231
// Check the transfer operation that pays the fee with Asset ID 0 against the restriction.
227232
// This should violate the restriction.
@@ -275,6 +280,7 @@ BOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {
275280
// "extensions": []
276281
// }
277282
//]
283+
BOOST_CHECK_EQUAL(restriction_count(restrictions), 3);
278284

279285
//////
280286
// Create a transfer operation that authorizes transfer to Account ID 12
@@ -332,6 +338,7 @@ BOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {
332338
// "extensions": []
333339
// }
334340
//]
341+
BOOST_CHECK_EQUAL(restriction_count(restrictions), 2);
335342
auto predicate = get_restriction_predicate(restrictions, operation::tag<account_update_operation>::value);
336343

337344
//////
@@ -446,6 +453,7 @@ BOOST_AUTO_TEST_CASE(restriction_predicate_tests) { try {
446453
// "extensions": []
447454
// }
448455
//]
456+
BOOST_CHECK_EQUAL(restriction_count(or_restrictions), 3);
449457
auto predicate = get_restriction_predicate(or_restrictions, operation::tag<transfer_operation>::value);
450458

451459
//////
@@ -562,6 +570,7 @@ BOOST_AUTO_TEST_CASE(custom_auths) { try {
562570
// "extensions": []
563571
// }
564572
//]
573+
BOOST_CHECK_EQUAL(restriction_count(op.restrictions), 3);
565574

566575

567576
//////
@@ -1634,6 +1643,7 @@ BOOST_AUTO_TEST_CASE(custom_auths) { try {
16341643
// "extensions": []
16351644
// }
16361645
//]
1646+
BOOST_CHECK_EQUAL(restriction_count(op.restrictions), 3);
16371647

16381648
// Publish the new custom authority
16391649
trx.clear();

0 commit comments

Comments
 (0)