Skip to content

Commit dcf1734

Browse files
BSIP 40: Add evaluators, transaction eval code, testing
Implementation passes cursory tests.
1 parent 7c3bdb2 commit dcf1734

18 files changed

+302
-65
lines changed

libraries/app/database_api.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const
23412341
trx.verify_authority( _db.get_chain_id(),
23422342
[this]( account_id_type id ){ return &id(_db).active; },
23432343
[this]( account_id_type id ){ return &id(_db).owner; },
2344+
[this]( account_id_type id, const operation& op ) {
2345+
return _db.get_viable_custom_authorities(id, op); },
23442346
allow_non_immediate_owner,
23452347
_db.get_global_properties().parameters.max_authority_depth );
23462348
return true;
@@ -2365,6 +2367,8 @@ bool database_api_impl::verify_account_authority( const string& account_name_or_
23652367
graphene::chain::verify_authority(ops, keys,
23662368
[this]( account_id_type id ){ return &id(_db).active; },
23672369
[this]( account_id_type id ){ return &id(_db).owner; },
2370+
// Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op
2371+
[](auto, auto) { return vector<authority>(); },
23682372
true );
23692373
}
23702374
catch (fc::exception& ex)

libraries/chain/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ add_library( graphene_chain
5555
htlc_evaluator.cpp
5656
confidential_evaluator.cpp
5757
special_authority_evaluation.cpp
58+
custom_authority_evaluator.cpp
5859
buyback.cpp
5960

6061
account_object.cpp

libraries/chain/db_block.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,15 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
644644
if( !(skip & skip_transaction_signatures) )
645645
{
646646
bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_CORE_584_TIME );
647-
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
648-
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
647+
auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
648+
auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; };
649+
auto get_custom = [this]( account_id_type id, const operation& op ) {
650+
return get_viable_custom_authorities(id, op);
651+
};
649652
trx.verify_authority( chain_id,
650653
get_active,
651654
get_owner,
655+
get_custom,
652656
allow_non_immediate_owner,
653657
get_global_properties().parameters.max_authority_depth );
654658
}

libraries/chain/db_getter.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <graphene/chain/asset_object.hpp>
2828
#include <graphene/chain/chain_property_object.hpp>
2929
#include <graphene/chain/global_property_object.hpp>
30+
#include <graphene/chain/custom_authority_object.hpp>
3031

3132
namespace graphene { namespace chain {
3233

@@ -95,6 +96,21 @@ node_property_object& database::node_properties()
9596
return _node_property_object;
9697
}
9798

99+
vector<authority> database::get_viable_custom_authorities(account_id_type account, const operation &op) const
100+
{
101+
const auto& index = get_index_type<custom_authority_index>().indices().get<by_account_custom>();
102+
auto range = index.equal_range(boost::make_tuple(account, unsigned_int(op.which()), true));
103+
vector<authority> results;
104+
105+
std::for_each(range.first, range.second,
106+
[&results, &op, now=head_block_time()](const custom_authority_object& cust_auth) {
107+
if (cust_auth.is_valid(now) && cust_auth.get_predicate()(op))
108+
results.insert(results.end(), cust_auth.auth);
109+
});
110+
111+
return results;
112+
}
113+
98114
uint32_t database::last_non_undoable_block_num() const
99115
{
100116
//see https://github.com/bitshares/bitshares-core/issues/377

libraries/chain/db_init.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <graphene/chain/witness_evaluator.hpp>
6565
#include <graphene/chain/worker_evaluator.hpp>
6666
#include <graphene/chain/htlc_evaluator.hpp>
67+
#include <graphene/chain/custom_authority_evaluator.hpp>
6768

6869
#include <fc/uint128.hpp>
6970
#include <fc/crypto/digest.hpp>
@@ -182,6 +183,9 @@ void database::initialize_evaluators()
182183
register_evaluator<htlc_create_evaluator>();
183184
register_evaluator<htlc_redeem_evaluator>();
184185
register_evaluator<htlc_extend_evaluator>();
186+
register_evaluator<custom_authority_create_evaluator>();
187+
register_evaluator<custom_authority_update_evaluator>();
188+
register_evaluator<custom_authority_delete_evaluator>();
185189
}
186190

187191
void database::initialize_indexes()

libraries/chain/hardfork.d/BSIP_40.hf

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// BSIP 40 (Custom Active Authorities) hardfork check
2+
#ifndef HARDFORK_BSIP_40_TIME
3+
// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled
4+
#define HARDFORK_BSIP_40_TIME (fc::time_point_sec( 1893456000 ))
5+
#define HARDFORK_BSIP_40_PASSED(now) (now >= HARDFORK_BSIP_40_TIME)
6+
#endif

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,17 @@ namespace graphene { namespace chain {
4949
authority auth;
5050
vector<restriction> restrictions;
5151

52+
/// Check whether the custom authority is valid
53+
bool is_valid(time_point_sec now) const { return enabled && now >= valid_from && now < valid_to; }
54+
5255
/// Unreflected field to store a cache of the predicate function
53-
optional<restriction_predicate_function> predicate_cache;
56+
mutable optional<restriction_predicate_function> predicate_cache;
57+
58+
/// Get predicate, from cache if possible, and update cache if not (modifies const object!)
59+
restriction_predicate_function get_predicate() const {
60+
if (!predicate_cache.valid()) predicate_cache = get_restriction_predicate(restrictions, operation_type);
61+
return *predicate_cache;
62+
}
5463
};
5564

5665
struct by_account_custom;
@@ -66,6 +75,8 @@ namespace graphene { namespace chain {
6675
composite_key<
6776
custom_authority_object,
6877
member<custom_authority_object, account_id_type, &custom_authority_object::account>,
78+
member<custom_authority_object, unsigned_int, &custom_authority_object::operation_type>,
79+
member<custom_authority_object, bool, &custom_authority_object::enabled>,
6980
member<object, object_id_type, &object::id>
7081
>
7182
>

libraries/chain/include/graphene/chain/database.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ namespace graphene { namespace chain {
279279

280280
node_property_object& node_properties();
281281

282+
vector<authority> get_viable_custom_authorities( account_id_type account, const operation& op )const;
282283

283284
uint32_t last_non_undoable_block_num() const;
284285
//////////////////// db_init.cpp ////////////////////

libraries/chain/proposal_object.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ bool proposal_object::is_authorized_to_execute(database& db) const
3838
available_key_approvals,
3939
[&]( account_id_type id ){ return &id(db).active; },
4040
[&]( account_id_type id ){ return &id(db).owner; },
41+
[&]( account_id_type id, const operation& op ){
42+
return db.get_viable_custom_authorities(id, op); },
4143
allow_non_immediate_owner,
4244
db.get_global_properties().parameters.max_authority_depth,
4345
true, /* allow committee */

libraries/protocol/include/graphene/protocol/authority.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ namespace graphene { namespace protocol {
107107
(a.key_auths == b.key_auths) &&
108108
(a.address_auths == b.address_auths);
109109
}
110+
friend bool operator!= ( const authority& a, const authority& b ) { return !(a==b); }
110111
uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); }
111112
void clear() { account_auths.clear(); key_auths.clear(); address_auths.clear(); weight_threshold = 0; }
112113

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

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ namespace graphene { namespace protocol {
3535
uint32_t max_preimage_size;
3636
};
3737

38+
struct custom_authority_options
39+
{
40+
uint32_t max_custom_authority_lifetime_seconds = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS;
41+
uint32_t max_custom_authorities_per_account = GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT;
42+
int64_t max_operation_tag = GRAPHENE_DEFAULT_MAX_OPERATION_TAG;
43+
};
44+
3845
struct chain_parameters
3946
{
4047
/** using a shared_ptr breaks the circular dependency created between operations and the fee schedule */
@@ -74,6 +81,7 @@ namespace graphene { namespace protocol {
7481
struct ext
7582
{
7683
optional< htlc_options > updatable_htlc_options;
84+
optional< custom_authority_options > custom_authority_options;
7785
};
7886

7987
extension<ext> extensions;
@@ -98,6 +106,11 @@ FC_REFLECT( graphene::protocol::htlc_options,
98106
(max_preimage_size)
99107
)
100108

109+
FC_REFLECT( graphene::protocol::custom_authority_options,
110+
(max_custom_authority_lifetime_seconds)
111+
(max_operation_tag)
112+
)
113+
101114
FC_REFLECT( graphene::protocol::chain_parameters::ext,
102115
(updatable_htlc_options)
103116
)

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

+9
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,12 @@
138138
///@}
139139

140140
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743))
141+
142+
/// Maximum duration before a custom authority can expire
143+
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITY_LIFETIME_SECONDS (60*60*24*365)
144+
/// Maximum number of custom authorities a particular account can set
145+
#define GRAPHENE_DEFAULT_MAX_CUSTOM_AUTHORITIES_PER_ACCOUNT 10
146+
147+
/// Maximum operation tag which has been forked in so far. Defaults to 56 because that's what it was when we first
148+
/// began keeping track
149+
#define GRAPHENE_DEFAULT_MAX_OPERATION_TAG 56

libraries/protocol/include/graphene/protocol/transaction.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
namespace graphene { namespace protocol {
2828

29+
using custom_authority_lookup = std::function<vector<authority>(account_id_type, const operation&)>;
30+
2931
/**
3032
* @defgroup transactions Transactions
3133
*
@@ -160,6 +162,7 @@ namespace graphene { namespace protocol {
160162
* @param chain_id the ID of a block chain
161163
* @param get_active callback function to retrieve active authorities of a given account
162164
* @param get_owner callback function to retrieve owner authorities of a given account
165+
* @param get_custom callback function to retrieve viable custom authorities for a given account and operation
163166
* @param allow_non_immediate_owner whether to allow owner authority of non-immediately
164167
* required accounts to authorize operations in the transaction
165168
* @param max_recursion maximum level of recursion when verifying, since an account
@@ -169,6 +172,7 @@ namespace graphene { namespace protocol {
169172
const chain_id_type& chain_id,
170173
const std::function<const authority*(account_id_type)>& get_active,
171174
const std::function<const authority*(account_id_type)>& get_owner,
175+
custom_authority_lookup get_custom,
172176
bool allow_non_immediate_owner,
173177
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
174178

@@ -183,6 +187,7 @@ namespace graphene { namespace protocol {
183187
const flat_set<public_key_type>& available_keys,
184188
const std::function<const authority*(account_id_type)>& get_active,
185189
const std::function<const authority*(account_id_type)>& get_owner,
190+
custom_authority_lookup get_custom,
186191
bool allow_non_immediate_owner,
187192
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
188193
) const;
@@ -252,6 +257,7 @@ namespace graphene { namespace protocol {
252257
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
253258
const std::function<const authority*(account_id_type)>& get_active,
254259
const std::function<const authority*(account_id_type)>& get_owner,
260+
custom_authority_lookup get_custom,
255261
bool allow_non_immediate_owner,
256262
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
257263
bool allow_committe = false,

libraries/protocol/restriction_predicate.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ template<typename...> using make_void = void;
3939
template<typename T> constexpr static bool is_integral =
4040
std::conditional_t<std::is_same<T, bool>::value, std::false_type, std::is_integral<T>>::value;
4141

42+
// Metafunction to check if type is some instantiation of fc::safe
43+
template<typename> constexpr static bool is_safe = false;
44+
template<typename I> constexpr static bool is_safe<fc::safe<I>> = true;
45+
4246
// Metafunction to check if two types are comparable, which means not void_t, and either the same or both integral
4347
template<typename T, typename U>
4448
constexpr static bool comparable_types = !std::is_same<T, void_t>::value &&
45-
(std::is_same<T, U>::value || (is_integral<T> && is_integral<U>));
49+
(std::is_same<T, U>::value || ((is_integral<T> || is_safe<T>) &&
50+
(is_integral<U> || is_safe<U>)));
4651

4752
// Metafunction to check if type is a container
4853
template<typename, typename = size_t> constexpr static bool is_container = false;
@@ -233,6 +238,9 @@ template<typename Element> struct predicate_in<flat_set<Element>> {
233238
// Simple inclusion check
234239
template<typename Field, typename = std::enable_if_t<comparable_types<Field, Element>>>
235240
bool operator()(const Field& f) const { return a.count(f) != 0; }
241+
// Check for safe value
242+
template<typename Field, typename = std::enable_if_t<comparable_types<Field, Element>>>
243+
bool operator()(const fc::safe<Field>& f) const { return a.count(f.value) != 0; }
236244
// Check for optional value
237245
template<typename Field, typename = std::enable_if_t<comparable_types<Field, Element>>>
238246
bool operator()(const fc::optional<Field>& f) const {

libraries/protocol/transaction.cpp

+32-9
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ struct sign_state
267267
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
268268
const std::function<const authority*(account_id_type)>& get_active,
269269
const std::function<const authority*(account_id_type)>& get_owner,
270+
custom_authority_lookup get_custom,
270271
bool allow_non_immediate_owner,
271272
uint32_t max_recursion_depth,
272273
bool allow_committe,
@@ -277,19 +278,39 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
277278
flat_set<account_id_type> required_owner;
278279
vector<authority> other;
279280

280-
for( const auto& op : ops )
281-
operation_get_required_authorities( op, required_active, required_owner, other );
282-
283-
if( !allow_committe )
284-
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
285-
invalid_committee_approval, "Committee account may only propose transactions" );
286-
287281
sign_state s( sigs, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth );
288282
for( auto& id : active_aprovals )
289283
s.approved_by.insert( id );
290284
for( auto& id : owner_approvals )
291285
s.approved_by.insert( id );
292286

287+
auto approved_by_custom_authority = [&s, get_custom = std::move(get_custom)]( account_id_type account,
288+
operation op ) mutable {
289+
auto viable_custom_auths = get_custom(account, op);
290+
for (const auto& auth : viable_custom_auths)
291+
if (s.check_authority(&auth)) return true;
292+
return false;
293+
};
294+
295+
for( const auto& op : ops ) {
296+
flat_set<account_id_type> operation_required_active;
297+
operation_get_required_authorities( op, operation_required_active, required_owner, other );
298+
299+
auto itr = operation_required_active.begin();
300+
while ( itr != operation_required_active.end() ) {
301+
if ( approved_by_custom_authority( *itr, op ) )
302+
itr = operation_required_active.erase( itr );
303+
else
304+
++itr;
305+
}
306+
307+
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
308+
}
309+
310+
if( !allow_committe )
311+
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
312+
invalid_committee_approval, "Committee account may only propose transactions" );
313+
293314
for( const auto& auth : other )
294315
{
295316
GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
@@ -375,6 +396,7 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
375396
const flat_set<public_key_type>& available_keys,
376397
const std::function<const authority*(account_id_type)>& get_active,
377398
const std::function<const authority*(account_id_type)>& get_owner,
399+
custom_authority_lookup get_custom,
378400
bool allow_non_immediate_owner,
379401
uint32_t max_recursion
380402
) const
@@ -387,7 +409,7 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
387409
result.erase( k );
388410
try
389411
{
390-
graphene::protocol::verify_authority( operations, result, get_active, get_owner,
412+
graphene::protocol::verify_authority( operations, result, get_active, get_owner, get_custom,
391413
allow_non_immediate_owner, max_recursion );
392414
continue; // element stays erased if verify_authority is ok
393415
}
@@ -433,11 +455,12 @@ void signed_transaction::verify_authority(
433455
const chain_id_type& chain_id,
434456
const std::function<const authority*(account_id_type)>& get_active,
435457
const std::function<const authority*(account_id_type)>& get_owner,
458+
custom_authority_lookup get_custom,
436459
bool allow_non_immediate_owner,
437460
uint32_t max_recursion )const
438461
{ try {
439462
graphene::protocol::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner,
440-
allow_non_immediate_owner, max_recursion );
463+
get_custom, allow_non_immediate_owner, max_recursion );
441464
} FC_CAPTURE_AND_RETHROW( (*this) ) }
442465

443466
} } // graphene::protocol

tests/performance/market_fee_sharing_tests.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <chrono>
2727
#include <graphene/chain/hardfork.hpp>
28+
#include <fc/time.hpp>
2829
#include "../common/database_fixture.hpp"
2930

3031
using namespace graphene::chain;

0 commit comments

Comments
 (0)