Merge branch 'feature/rbac' into feature/nft_market_rbac

This commit is contained in:
Srdjan Obucina 2020-07-15 14:49:31 +02:00
commit f7c43fa405
32 changed files with 2993 additions and 18 deletions

View file

@ -184,6 +184,14 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
// gpos
gpos_info get_gpos_info(const account_id_type account) const;
// rbac
vector<custom_permission_object> get_custom_permissions(const account_id_type account) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(const account_id_type account, const string& permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(const account_id_type account) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const;
// NFT
uint64_t nft_get_balance(const account_id_type owner) const;
optional<account_id_type> nft_owner_of(const nft_id_type token_id) const;
@ -1880,6 +1888,9 @@ set<public_key_type> database_api_impl::get_required_signatures( const signed_tr
available_keys,
[&]( account_id_type id ){ return &id(_db).active; },
[&]( account_id_type id ){ return &id(_db).owner; },
[&]( account_id_type id, const operation& op ) {
return _db.get_account_custom_authorities(id, op);
},
_db.get_global_properties().parameters.max_authority_depth );
wdump((result));
return result;
@ -1915,6 +1926,17 @@ set<public_key_type> database_api_impl::get_potential_signatures( const signed_t
result.insert(k);
return &auth;
},
[&]( account_id_type id, const operation& op ) {
vector<authority> custom_auths = _db.get_account_custom_authorities(id, op);
for (const auto& cauth: custom_auths)
{
for (const auto& k : cauth.get_keys())
{
result.insert(k);
}
}
return custom_auths;
},
_db.get_global_properties().parameters.max_authority_depth
);
@ -1942,6 +1964,9 @@ set<address> database_api_impl::get_potential_address_signatures( const signed_t
result.insert(k);
return &auth;
},
[&]( account_id_type id, const operation& op ) {
return _db.get_account_custom_authorities(id, op);
},
_db.get_global_properties().parameters.max_authority_depth
);
return result;
@ -1957,6 +1982,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const
trx.verify_authority( _db.get_chain_id(),
[this]( account_id_type id ){ return &id(_db).active; },
[this]( account_id_type id ){ return &id(_db).owner; },
[this]( account_id_type id, const operation& op ) {
return _db.get_account_custom_authorities(id, op); },
_db.get_global_properties().parameters.max_authority_depth );
return true;
}
@ -2327,6 +2354,45 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type
return result;
}
//////////////////////////////////////////////////////////////////////
// //
// RBAC methods //
// //
//////////////////////////////////////////////////////////////////////
vector<custom_permission_object> database_api::get_custom_permissions(const account_id_type account) const
{
return my->get_custom_permissions(account);
}
vector<custom_permission_object> database_api_impl::get_custom_permissions(const account_id_type account) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto prange = pindex.equal_range(boost::make_tuple(account));
vector<custom_permission_object> custom_permissions;
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
custom_permissions.push_back(pobj);
}
return custom_permissions;
}
fc::optional<custom_permission_object> database_api::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
{
return my->get_custom_permission_by_name(account, permission_name);
}
fc::optional<custom_permission_object> database_api_impl::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto prange = pindex.equal_range(boost::make_tuple(account, permission_name));
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
return pobj;
}
return {};
}
//////////////////////////////////////////////////////////////////////
// //
// NFT methods //
@ -2492,6 +2558,79 @@ nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_
return {};
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities(const account_id_type account) const
{
return my->get_custom_account_authorities(account);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities(const account_id_type account) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<custom_account_authority_object> custom_account_auths;
auto prange = pindex.equal_range(boost::make_tuple(account));
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
auto crange = cindex.equal_range(boost::make_tuple(pobj.id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
}
return custom_account_auths;
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
{
return my->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
{
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<custom_account_authority_object> custom_account_auths;
auto crange = cindex.equal_range(boost::make_tuple(permission_id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
return custom_account_auths;
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
{
return my->get_custom_account_authorities_by_permission_name(account, permission_name);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
{
vector<custom_account_authority_object> custom_account_auths;
fc::optional<custom_permission_object> pobj = get_custom_permission_by_name(account, permission_name);
if(!pobj)
{
return custom_account_auths;
}
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
auto crange = cindex.equal_range(boost::make_tuple(pobj->id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
return custom_account_auths;
}
vector<authority> database_api::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
{
return my->get_active_custom_account_authorities_by_operation(account, operation_type);
}
vector<authority> database_api_impl::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
{
operation op;
op.set_which(operation_type);
return _db.get_account_custom_authorities(account, op);
}
// Marketplace
vector<offer_object> database_api::list_offers(const offer_id_type lower_id, uint32_t limit) const
{

View file

@ -48,6 +48,8 @@
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/offer_object.hpp>
@ -712,6 +714,19 @@ class database_api
*/
gpos_info get_gpos_info(const account_id_type account) const;
//////////
// RBAC //
//////////
/**
* @return account and custom permissions/account-authorities info
*/
vector<custom_permission_object> get_custom_permissions(const account_id_type account) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(const account_id_type account, const string& permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(const account_id_type account) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const;
/////////
// NFT //
/////////
@ -939,6 +954,14 @@ FC_API(graphene::app::database_api,
// gpos
(get_gpos_info)
//rbac
(get_custom_permissions)
(get_custom_permission_by_name)
(get_custom_account_authorities)
(get_custom_account_authorities_by_permission_id)
(get_custom_account_authorities_by_permission_name)
(get_active_custom_account_authorities_by_operation)
// NFT
(nft_get_balance)
(nft_owner_of)

View file

@ -61,6 +61,8 @@ add_library( graphene_chain
protocol/vote.cpp
protocol/tournament.cpp
protocol/small_ops.cpp
protocol/custom_permission.cpp
protocol/custom_account_authority.cpp
protocol/offer.cpp
genesis_state.cpp
@ -113,6 +115,8 @@ add_library( graphene_chain
betting_market_evaluator.cpp
betting_market_object.cpp
betting_market_group_object.cpp
custom_permission_evaluator.cpp
custom_account_authority_evaluator.cpp
affiliate_payout.cpp

View file

@ -0,0 +1,147 @@
#include <graphene/chain/custom_account_authority_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
struct rbac_operation_hardfork_visitor
{
typedef void result_type;
const fc::time_point_sec block_time;
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
void operator()(int op_type) const
{
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
switch (op_type)
{
case operation::tag<custom_permission_create_operation>::value:
case operation::tag<custom_permission_update_operation>::value:
case operation::tag<custom_permission_delete_operation>::value:
case operation::tag<custom_account_authority_create_operation>::value:
case operation::tag<custom_account_authority_update_operation>::value:
case operation::tag<custom_account_authority_delete_operation>::value:
FC_ASSERT(block_time >= HARDFORK_RBAC_TIME, "Custom permission not allowed on this operation yet!");
break;
default:
FC_ASSERT(op_type < first_allowed_op, "Custom permission not allowed on this operation!");
}
}
};
void_result create_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_create_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
const custom_permission_object &pobj = op.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can update account authority object");
FC_ASSERT(op.valid_to > now, "valid_to expiry should be in future");
rbac_operation_hardfork_visitor rvtor(now);
rvtor(op.operation_type);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type create_custom_account_authority_evaluator::do_apply(const custom_account_authority_create_operation &op)
{
try
{
database &d = db();
return d.create<custom_account_authority_object>([&op](custom_account_authority_object &obj) mutable {
obj.permission_id = op.permission_id;
obj.operation_type = op.operation_type;
obj.valid_from = op.valid_from;
obj.valid_to = op.valid_to;
})
.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result update_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_update_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
const custom_account_authority_object &aobj = op.auth_id(d);
const custom_permission_object &pobj = aobj.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can update account authority object");
auto valid_from = aobj.valid_from;
auto valid_to = aobj.valid_to;
if (op.new_valid_from)
{
valid_from = *op.new_valid_from;
}
if (op.new_valid_to)
{
FC_ASSERT(*op.new_valid_to > now, "New valid_to expiry should be in the future");
valid_to = *op.new_valid_to;
}
FC_ASSERT(valid_from < valid_to, "valid_from should be before valid_to");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type update_custom_account_authority_evaluator::do_apply(const custom_account_authority_update_operation &op)
{
try
{
database &d = db();
const custom_account_authority_object &aobj = op.auth_id(d);
d.modify(aobj, [&op](custom_account_authority_object &obj) {
if (op.new_valid_from)
obj.valid_from = *op.new_valid_from;
if (op.new_valid_to)
obj.valid_to = *op.new_valid_to;
});
return op.auth_id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_delete_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
const custom_account_authority_object &aobj = op.auth_id(d);
const custom_permission_object &pobj = aobj.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can delete account authority object");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_account_authority_evaluator::do_apply(const custom_account_authority_delete_operation &op)
{
try
{
database &d = db();
const custom_account_authority_object &aobj = op.auth_id(d);
d.remove(aobj);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,131 @@
#include <graphene/chain/custom_permission_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
void_result create_custom_permission_evaluator::do_evaluate(const custom_permission_create_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
for (const auto &account_weight_pair : op.auth.account_auths)
{
account_weight_pair.first(d);
}
const auto &pindex = d.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto pitr = pindex.find(boost::make_tuple(op.owner_account, op.permission_name));
FC_ASSERT(pitr == pindex.end(), "Permission name already exists for the given account");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type create_custom_permission_evaluator::do_apply(const custom_permission_create_operation &op)
{
try
{
database &d = db();
return d.create<custom_permission_object>([&op](custom_permission_object &obj) mutable {
obj.account = op.owner_account;
obj.permission_name = op.permission_name;
obj.auth = op.auth;
})
.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result update_custom_permission_evaluator::do_evaluate(const custom_permission_update_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
const custom_permission_object &pobj = op.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can update permission object");
if (op.new_auth)
{
FC_ASSERT(!(*op.new_auth == pobj.auth), "New authority provided is not different from old authority");
for (const auto &account_weight_pair : op.new_auth->account_auths)
{
account_weight_pair.first(d);
}
}
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type update_custom_permission_evaluator::do_apply(const custom_permission_update_operation &op)
{
try
{
database &d = db();
const custom_permission_object &pobj = op.permission_id(d);
d.modify(pobj, [&op](custom_permission_object &obj) {
if (op.new_auth)
obj.auth = *op.new_auth;
});
return op.permission_id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_permission_evaluator::do_evaluate(const custom_permission_delete_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_RBAC_TIME, "Not allowed until RBAC HF");
op.owner_account(d);
const custom_permission_object &pobj = op.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can delete permission object");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_permission_evaluator::do_apply(const custom_permission_delete_operation &op)
{
try
{
database &d = db();
const custom_permission_object &pobj = op.permission_id(d);
// Remove the account authority objects linked to this permission
const auto& cindex = d.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<std::reference_wrapper<const custom_account_authority_object>> custom_auths;
auto crange = cindex.equal_range(boost::make_tuple(pobj.id));
// Store the references to the account authorities
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_auths.push_back(cobj);
}
// Now remove the account authorities
for(const auto& cauth : custom_auths)
{
d.remove(cauth);
}
// Now finally remove the permission
d.remove(pobj);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -791,7 +791,10 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
{
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
auto get_custom = [&]( account_id_type id, const operation& op ) {
return get_account_custom_authorities(id, op);
};
trx.verify_authority( chain_id, get_active, get_owner, get_custom, get_global_properties().parameters.max_authority_depth );
}
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is

View file

@ -27,6 +27,8 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <fc/smart_ref_impl.hpp>
@ -160,6 +162,27 @@ const witness_schedule_object& database::get_witness_schedule_object()const
return *_p_witness_schedule_obj;
}
vector<authority> database::get_account_custom_authorities(account_id_type account, const operation& op)const
{
const auto& pindex = get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
const auto& cindex = get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
auto prange = pindex.equal_range(boost::make_tuple(account));
time_point_sec now = head_block_time();
vector<authority> custom_auths;
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
auto crange = cindex.equal_range(boost::make_tuple(pobj.id, op.which()));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
if(now >= cobj.valid_from && now < cobj.valid_to)
{
custom_auths.push_back(pobj.auth);
}
}
}
return custom_auths;
}
bool database::item_locked(const nft_id_type &item) const
{
const auto &offer_idx = get_index_type<offer_index>();

View file

@ -49,6 +49,8 @@
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>
@ -79,6 +81,8 @@
#include <graphene/chain/event_evaluator.hpp>
#include <graphene/chain/betting_market_evaluator.hpp>
#include <graphene/chain/tournament_evaluator.hpp>
#include <graphene/chain/custom_permission_evaluator.hpp>
#include <graphene/chain/custom_account_authority_evaluator.hpp>
#include <graphene/chain/offer_evaluator.hpp>
#include <graphene/chain/nft_evaluator.hpp>
@ -255,6 +259,12 @@ void database::initialize_evaluators()
register_evaluator<lottery_reward_evaluator>();
register_evaluator<lottery_end_evaluator>();
register_evaluator<sweeps_vesting_claim_evaluator>();
register_evaluator<create_custom_permission_evaluator>();
register_evaluator<update_custom_permission_evaluator>();
register_evaluator<delete_custom_permission_evaluator>();
register_evaluator<create_custom_account_authority_evaluator>();
register_evaluator<update_custom_account_authority_evaluator>();
register_evaluator<delete_custom_account_authority_evaluator>();
register_evaluator<offer_evaluator>();
register_evaluator<bid_evaluator>();
register_evaluator<finalize_offer_evaluator>();
@ -305,6 +315,8 @@ void database::initialize_indexes()
tournament_details_idx->add_secondary_index<tournament_players_index>();
add_index< primary_index<match_index> >();
add_index< primary_index<game_index> >();
add_index< primary_index<custom_permission_index> >();
add_index< primary_index<custom_account_authority_index> >();
auto offer_idx = add_index< primary_index<offer_index> >();
offer_idx->add_secondary_index<offer_item_index>();

View file

@ -47,6 +47,7 @@
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed
@ -933,6 +934,15 @@ void rolling_period_start(database& db)
}
}
void clear_expired_custom_account_authorities(database& db)
{
const auto& cindex = db.get_index_type<custom_account_authority_index>().indices().get<by_expiration>();
while(!cindex.empty() && cindex.begin()->valid_to < db.head_block_time())
{
db.remove(*cindex.begin());
}
}
// Schedules payouts from a dividend distribution account to the current holders of the
// dividend-paying asset. This takes any deposits made to the dividend distribution account
// since the last time it was called, and distributes them to the current owners of the
@ -1707,7 +1717,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
// Ideally we have to do this after every block but that leads to longer block applicaiton/replay times.
// So keep it here as it is not critical. valid_to check ensures
// these custom account auths are not usable.
clear_expired_custom_account_authorities(*this);
// process_budget needs to run at the bottom because
// it needs to know the next_maintenance_time
process_budget();

View file

@ -293,6 +293,24 @@ struct get_impacted_account_visitor
void operator()( const sweeps_vesting_claim_operation& op ) {
_impacted.insert( op.account );
}
void operator()( const custom_permission_create_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_permission_update_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_permission_delete_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_create_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_update_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_delete_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const nft_metadata_create_operation& op ) {
_impacted.insert( op.owner );
}

View file

@ -0,0 +1,4 @@
// RBAC HARDFORK Wednesday, 20-May-20 00:00:00 UTC
#ifndef HARDFORK_RBAC_TIME
#define HARDFORK_RBAC_TIME (fc::time_point_sec( 1589932800 ))
#endif

View file

@ -234,3 +234,6 @@
#define GPOS_PERIOD (60*60*24*30*6) // 6 months
#define GPOS_SUBPERIOD (60*60*24*30) // 1 month
#define GPOS_VESTING_LOCKIN_PERIOD (60*60*24*30) // 1 month
#define RBAC_MIN_PERMISSION_NAME_LENGTH 3
#define RBAC_MAX_PERMISSION_NAME_LENGTH 10

View file

@ -0,0 +1,38 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/protocol/custom_account_authority.hpp>
namespace graphene
{
namespace chain
{
class create_custom_account_authority_evaluator : public evaluator<create_custom_account_authority_evaluator>
{
public:
typedef custom_account_authority_create_operation operation_type;
void_result do_evaluate(const custom_account_authority_create_operation &o);
object_id_type do_apply(const custom_account_authority_create_operation &o);
};
class update_custom_account_authority_evaluator : public evaluator<update_custom_account_authority_evaluator>
{
public:
typedef custom_account_authority_update_operation operation_type;
void_result do_evaluate(const custom_account_authority_update_operation &o);
object_id_type do_apply(const custom_account_authority_update_operation &o);
};
class delete_custom_account_authority_evaluator : public evaluator<delete_custom_account_authority_evaluator>
{
public:
typedef custom_account_authority_delete_operation operation_type;
void_result do_evaluate(const custom_account_authority_delete_operation &o);
void_result do_apply(const custom_account_authority_delete_operation &o);
};
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,55 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
/**
* @class custom_account_authority_object
* @brief Tracks the mappings between permission and operation types.
* @ingroup object
*/
class custom_account_authority_object : public abstract_object<custom_account_authority_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = custom_account_authority_object_type;
custom_permission_id_type permission_id;
int operation_type;
time_point_sec valid_from;
time_point_sec valid_to;
};
struct by_id;
struct by_permission_and_op;
struct by_expiration;
using custom_account_authority_multi_index_type = multi_index_container<
custom_account_authority_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_unique< tag<by_permission_and_op>,
composite_key<custom_account_authority_object,
member<custom_account_authority_object, custom_permission_id_type, &custom_account_authority_object::permission_id>,
member<custom_account_authority_object, int, &custom_account_authority_object::operation_type>,
member<object, object_id_type, &object::id>
>
>,
ordered_unique<tag<by_expiration>,
composite_key<custom_account_authority_object,
member<custom_account_authority_object, time_point_sec, &custom_account_authority_object::valid_to>,
member<object, object_id_type, &object::id>
>
>
>
>;
using custom_account_authority_index = generic_index<custom_account_authority_object, custom_account_authority_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::custom_account_authority_object, (graphene::db::object),
(permission_id)(operation_type)(valid_from)(valid_to) )

View file

@ -0,0 +1,38 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/protocol/custom_permission.hpp>
namespace graphene
{
namespace chain
{
class create_custom_permission_evaluator : public evaluator<create_custom_permission_evaluator>
{
public:
typedef custom_permission_create_operation operation_type;
void_result do_evaluate(const custom_permission_create_operation &o);
object_id_type do_apply(const custom_permission_create_operation &o);
};
class update_custom_permission_evaluator : public evaluator<update_custom_permission_evaluator>
{
public:
typedef custom_permission_update_operation operation_type;
void_result do_evaluate(const custom_permission_update_operation &o);
object_id_type do_apply(const custom_permission_update_operation &o);
};
class delete_custom_permission_evaluator : public evaluator<delete_custom_permission_evaluator>
{
public:
typedef custom_permission_delete_operation operation_type;
void_result do_evaluate(const custom_permission_delete_operation &o);
void_result do_apply(const custom_permission_delete_operation &o);
};
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,49 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
using namespace graphene::db;
/**
* @class custom_permission_object
* @brief Tracks all the custom permission of an account.
* @ingroup object
*/
class custom_permission_object : public abstract_object<custom_permission_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = custom_permission_object_type;
// Account for which this permission is being created
account_id_type account;
// Permission name
string permission_name;
// Authority required for this permission
authority auth;
};
struct by_id;
struct by_account_and_permission;
using custom_permission_multi_index_type = multi_index_container<
custom_permission_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_unique< tag<by_account_and_permission>,
composite_key<custom_permission_object,
member<custom_permission_object, account_id_type, &custom_permission_object::account>,
member<custom_permission_object, string, &custom_permission_object::permission_name>
>
>
>
>;
using custom_permission_index = generic_index<custom_permission_object, custom_permission_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::custom_permission_object, (graphene::db::object),
(account)(permission_name)(auth) )

View file

@ -295,6 +295,7 @@ namespace graphene { namespace chain {
uint32_t last_non_undoable_block_num() const;
vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const;
//////////////////// db_init.cpp ////////////////////
void initialize_evaluators();

View file

@ -0,0 +1,72 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene
{
namespace chain
{
struct custom_account_authority_create_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
custom_permission_id_type permission_id;
int operation_type;
time_point_sec valid_from;
time_point_sec valid_to;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
struct custom_account_authority_update_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
custom_account_authority_id_type auth_id;
optional<time_point_sec> new_valid_from;
optional<time_point_sec> new_valid_to;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
struct custom_account_authority_delete_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
custom_account_authority_id_type auth_id;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
} // namespace chain
} // namespace graphene
FC_REFLECT(graphene::chain::custom_account_authority_create_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account))

View file

@ -0,0 +1,69 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene
{
namespace chain
{
struct custom_permission_create_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
account_id_type owner_account;
string permission_name;
authority auth;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
struct custom_permission_update_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
custom_permission_id_type permission_id;
optional<authority> new_auth;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
struct custom_permission_delete_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
custom_permission_id_type permission_id;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return 0; }
};
} // namespace chain
} // namespace graphene
FC_REFLECT(graphene::chain::custom_permission_create_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth))
FC_REFLECT(graphene::chain::custom_permission_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account))
FC_REFLECT(graphene::chain::custom_permission_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account))

View file

@ -45,6 +45,8 @@
#include <graphene/chain/protocol/event.hpp>
#include <graphene/chain/protocol/betting_market.hpp>
#include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/protocol/custom_permission.hpp>
#include <graphene/chain/protocol/custom_account_authority.hpp>
#include <graphene/chain/protocol/offer.hpp>
#include <graphene/chain/protocol/nft_ops.hpp>
@ -138,6 +140,12 @@ namespace graphene { namespace chain {
lottery_reward_operation,
lottery_end_operation,
sweeps_vesting_claim_operation,
custom_permission_create_operation,
custom_permission_update_operation,
custom_permission_delete_operation,
custom_account_authority_create_operation,
custom_account_authority_update_operation,
custom_account_authority_delete_operation,
offer_operation,
bid_operation,
finalize_offer_operation,

View file

@ -141,6 +141,7 @@ namespace graphene { namespace chain {
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const;
@ -148,6 +149,7 @@ namespace graphene { namespace chain {
const chain_id_type& chain_id,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/**
@ -162,6 +164,7 @@ namespace graphene { namespace chain {
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const;
@ -194,6 +197,7 @@ namespace graphene { namespace chain {
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),

View file

@ -171,6 +171,8 @@ namespace graphene { namespace chain {
betting_market_group_object_type,
betting_market_object_type,
bet_object_type,
custom_permission_object_type,
custom_account_authority_object_type,
offer_object_type,
nft_metadata_type,
nft_object_type,
@ -234,6 +236,8 @@ namespace graphene { namespace chain {
class betting_market_group_object;
class betting_market_object;
class bet_object;
class custom_permission_object;
class custom_account_authority_object;
class offer_object;
class nft_metadata_object;
class nft_object;
@ -263,6 +267,8 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
typedef object_id< protocol_ids, custom_permission_object_type, custom_permission_object> custom_permission_id_type;
typedef object_id< protocol_ids, custom_account_authority_object_type, custom_account_authority_object> custom_account_authority_id_type;
typedef object_id< protocol_ids, offer_object_type, offer_object> offer_id_type;
typedef object_id< protocol_ids, nft_metadata_type, nft_metadata_object> nft_metadata_id_type;
typedef object_id< protocol_ids, nft_object_type, nft_object> nft_id_type;
@ -448,6 +454,8 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(betting_market_group_object_type)
(betting_market_object_type)
(bet_object_type)
(custom_permission_object_type)
(custom_account_authority_object_type)
(offer_object_type)
(nft_metadata_type)
(nft_object_type)
@ -522,6 +530,8 @@ FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_permission_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type )
FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )

View file

@ -132,6 +132,30 @@ struct proposal_operation_hardfork_visitor
FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
}
void operator()(const custom_permission_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_create_operation not allowed yet!" );
}
void operator()(const custom_permission_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_update_operation not allowed yet!" );
}
void operator()(const custom_permission_delete_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_delete_operation not allowed yet!" );
}
void operator()(const custom_account_authority_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_create_operation not allowed yet!" );
}
void operator()(const custom_account_authority_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_update_operation not allowed yet!" );
}
void operator()(const custom_account_authority_delete_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_delete_operation not allowed yet!" );
}
// loop and self visit in proposals
void operator()(const proposal_create_operation &v) const {
for (const op_wrapper &op : v.proposed_ops)

View file

@ -36,6 +36,8 @@ bool proposal_object::is_authorized_to_execute(database& db) const
available_key_approvals,
[&]( account_id_type id ){ return &id(db).active; },
[&]( account_id_type id ){ return &id(db).owner; },
[&]( account_id_type id, const operation& op ){
return db.get_account_custom_authorities(id, op); },
db.get_global_properties().parameters.max_authority_depth,
true, /* allow committee */
available_active_approvals,

View file

@ -0,0 +1,38 @@
#include <graphene/chain/protocol/custom_account_authority.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene
{
namespace chain
{
void custom_account_authority_create_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
FC_ASSERT(valid_from < valid_to, "valid_from should be earlier than valid_to");
FC_ASSERT(operation_type >= 0 && operation_type < operation::count(), "operation_type is not valid");
}
void custom_account_authority_update_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
FC_ASSERT(new_valid_from.valid() || new_valid_to.valid(), "Something must be updated");
if (new_valid_from && new_valid_to)
{
FC_ASSERT(*new_valid_from < *new_valid_to, "valid_from should be earlier than valid_to");
}
}
void custom_account_authority_delete_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,80 @@
#include <graphene/chain/protocol/custom_permission.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene
{
namespace chain
{
bool is_valid_permission_name(const string &name)
{
try
{
const size_t len = name.size();
// RBAC_MIN_PERMISSION_NAME_LENGTH <= len minimum length check
if (len < RBAC_MIN_PERMISSION_NAME_LENGTH)
{
return false;
}
// len <= RBAC_MAX_PERMISSION_NAME_LENGTH max length check
if (len > RBAC_MAX_PERMISSION_NAME_LENGTH)
{
return false;
}
// First character should be a letter between a-z
if (!(name[0] >= 'a' && name[0] <= 'z'))
{
return false;
}
// Any character of a permission name should either be a small case letter a-z or a digit 0-9
for (const auto &ch : name)
{
if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')))
{
return false;
}
}
// Don't accept active and owner permissions as we already have them by default
// This is for removing ambiguity for users, accepting them doesn't create any problems
if (name == "active" || name == "owner")
{
return false;
}
return true;
}
FC_CAPTURE_AND_RETHROW((name))
}
void custom_permission_create_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(is_valid_permission_name(permission_name), "Invalid permission name provided");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
FC_ASSERT(!auth.is_impossible(), "Impossible authority threshold auth provided");
FC_ASSERT(auth.address_auths.size() == 0, "Only account and key auths supported");
}
void custom_permission_update_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
FC_ASSERT(new_auth.valid(), "Something must be updated");
if (new_auth)
{
FC_ASSERT(!new_auth->is_impossible(), "Impossible authority threshold auth provided");
FC_ASSERT(new_auth->address_auths.size() == 0, "Only account and key auths supported");
}
}
void custom_permission_delete_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(owner_account != GRAPHENE_TEMP_ACCOUNT && owner_account != GRAPHENE_COMMITTEE_ACCOUNT && owner_account != GRAPHENE_WITNESS_ACCOUNT && owner_account != GRAPHENE_RELAXED_COMMITTEE_ACCOUNT,
"Custom permissions and account auths cannot be created for special accounts");
}
} // namespace chain
} // namespace graphene

View file

@ -248,6 +248,7 @@ struct sign_state
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion_depth,
bool allow_committe,
const flat_set<account_id_type>& active_aprovals,
@ -257,13 +258,6 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
flat_set<account_id_type> required_owner;
vector<authority> other;
for( const auto& op : ops )
operation_get_required_authorities( op, required_active, required_owner, other );
if( !allow_committe )
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" );
sign_state s(sigs,get_active);
s.max_recursion = max_recursion_depth;
for( auto& id : active_aprovals )
@ -271,6 +265,35 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
for( auto& id : owner_approvals )
s.approved_by.insert( id );
auto approved_by_custom_authority = [&s, &get_custom](
account_id_type account,
operation op ) mutable {
auto custom_auths = get_custom( account, op );
for( const auto& auth : custom_auths )
if( s.check_authority( &auth ) ) return true;
return false;
};
for( const auto& op : ops ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other );
auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) {
if ( approved_by_custom_authority( *itr, op ) )
itr = operation_required_active.erase( itr );
else
++itr;
}
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
}
if( !allow_committe )
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" );
for( const auto& auth : other )
{
GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
@ -325,17 +348,41 @@ set<public_key_type> signed_transaction::get_required_signatures(
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion_depth )const
{
flat_set<account_id_type> required_active;
flat_set<account_id_type> required_owner;
vector<authority> other;
get_required_authorities( required_active, required_owner, other );
const flat_set<public_key_type>& signature_keys = get_signature_keys( chain_id );
sign_state s( signature_keys, get_active, available_keys );
s.max_recursion = max_recursion_depth;
auto approved_by_custom_authority = [&s, &get_custom](
account_id_type account,
operation op ) mutable {
auto custom_auths = get_custom( account, op );
for( const auto& auth : custom_auths )
if( s.check_authority( &auth ) ) return true;
return false;
};
for( const auto& op : operations ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other );
auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) {
if ( approved_by_custom_authority( *itr, op ) )
itr = operation_required_active.erase( itr );
else
++itr;
}
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
}
for( const auto& auth : other )
s.check_authority(&auth);
for( auto& owner : required_owner )
@ -359,10 +406,11 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion
) const
{
set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, max_recursion );
set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom, max_recursion );
flat_set< public_key_type > result( s.begin(), s.end() );
for( const public_key_type& k : s )
@ -370,7 +418,7 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
result.erase( k );
try
{
graphene::chain::verify_authority( operations, result, get_active, get_owner, max_recursion );
graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, max_recursion );
continue; // element stays erased if verify_authority is ok
}
catch( const tx_missing_owner_auth& e ) {}
@ -385,9 +433,10 @@ void signed_transaction::verify_authority(
const chain_id_type& chain_id,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
uint32_t max_recursion )const
{ try {
graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, max_recursion );
graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion );
} FC_CAPTURE_AND_RETHROW( (*this) ) }
} } // graphene::chain

View file

@ -1895,6 +1895,37 @@ class wallet_api
bool is_gpos,
bool broadcast);
signed_transaction create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast = true);
signed_transaction update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast = true);
signed_transaction delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast = true);
signed_transaction create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast = true);
signed_transaction update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast = true);
signed_transaction delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast = true);
vector<custom_permission_object> get_custom_permissions(string owner) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(string owner, string permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(string owner) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const;
/////////
// NFT //
/////////
@ -2347,4 +2378,16 @@ FC_API( graphene::wallet::wallet_api,
(get_all_matched_bets_for_bettor)
(buy_ticket)
(quit)
(create_custom_permission)
(update_custom_permission)
(delete_custom_permission)
(create_custom_account_authority)
(update_custom_account_authority)
(delete_custom_account_authority)
(get_custom_permissions)
(get_custom_permission_by_name)
(get_custom_account_authorities)
(get_custom_account_authorities_by_permission_id)
(get_custom_account_authorities_by_permission_name)
(get_active_custom_account_authorities_by_operation)
)

View file

@ -3242,6 +3242,140 @@ public:
return sign_transaction(tx, broadcast);
}
signed_transaction create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast)
{
custom_permission_create_operation create_op;
create_op.owner_account = get_account(owner).id;
create_op.permission_name = permission_name;
create_op.auth = auth;
signed_transaction tx;
tx.operations.push_back(create_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast)
{
custom_permission_update_operation update_op;
update_op.owner_account = get_account(owner).id;
update_op.permission_id = permission_id;
update_op.new_auth = new_auth;
signed_transaction tx;
tx.operations.push_back(update_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast)
{
custom_permission_delete_operation delete_op;
delete_op.owner_account = get_account(owner).id;
delete_op.permission_id = permission_id;
signed_transaction tx;
tx.operations.push_back(delete_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast)
{
custom_account_authority_create_operation create_op;
create_op.owner_account = get_account(owner).id;
create_op.permission_id = permission_id;
create_op.operation_type = operation_type;
create_op.valid_from = valid_from;
create_op.valid_to = valid_to;
signed_transaction tx;
tx.operations.push_back(create_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast)
{
custom_account_authority_update_operation update_op;
update_op.owner_account = get_account(owner).id;
update_op.auth_id = auth_id;
update_op.new_valid_from = new_valid_from;
update_op.new_valid_to = new_valid_to;
signed_transaction tx;
tx.operations.push_back(update_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast)
{
custom_account_authority_delete_operation delete_op;
delete_op.owner_account = get_account(owner).id;
delete_op.auth_id = auth_id;
signed_transaction tx;
tx.operations.push_back(delete_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
vector<custom_permission_object> get_custom_permissions(string owner) const
{
return _remote_db->get_custom_permissions(get_account(owner).id);
}
fc::optional<custom_permission_object> get_custom_permission_by_name(string owner, string permission_name) const
{
return _remote_db->get_custom_permission_by_name(get_account(owner).id, permission_name);
}
vector<custom_account_authority_object> get_custom_account_authorities(string owner) const
{
return _remote_db->get_custom_account_authorities(get_account(owner).id);
}
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const
{
return _remote_db->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const
{
return _remote_db->get_custom_account_authorities_by_permission_name(get_account(owner).id, permission_name);
}
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const
{
return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type);
}
void dbg_make_uia(string creator, string symbol)
{
asset_options opts;
@ -4541,8 +4675,84 @@ signed_transaction wallet_api::approve_proposal(
return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast );
}
signed_transaction wallet_api::create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast)
{
return my->create_custom_permission(owner, permission_name, auth, broadcast);
}
signed_transaction wallet_api::update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast)
{
return my->update_custom_permission(owner, permission_id, new_auth, broadcast);
}
signed_transaction wallet_api::delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast)
{
return my->delete_custom_permission(owner, permission_id, broadcast);
}
signed_transaction wallet_api::create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast)
{
return my->create_custom_account_authority(owner, permission_id, operation_type, valid_from, valid_to, broadcast);
}
signed_transaction wallet_api::update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast)
{
return my->update_custom_account_authority(owner, auth_id, new_valid_from, new_valid_to, broadcast);
}
signed_transaction wallet_api::delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast)
{
return my->delete_custom_account_authority(owner, auth_id, broadcast);
}
vector<custom_permission_object> wallet_api::get_custom_permissions(string owner) const
{
return my->get_custom_permissions(owner);
}
fc::optional<custom_permission_object> wallet_api::get_custom_permission_by_name(string owner, string permission_name) const
{
return my->get_custom_permission_by_name(owner, permission_name);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities(string owner) const
{
return my->get_custom_account_authorities(owner);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const
{
return my->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities_by_permission_name(string owner, string permission_name) const
{
return my->get_custom_account_authorities_by_permission_name(owner, permission_name);
}
vector<authority> wallet_api::get_active_custom_account_authorities_by_operation(string owner, int operation_type) const
{
return my->get_active_custom_account_authorities_by_operation(owner, operation_type);
}
global_property_object wallet_api::get_global_properties() const
{

View file

@ -43,6 +43,8 @@
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>

View file

@ -1189,6 +1189,14 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
return &(aid(db).owner);
} ;
auto get_custom = [&](
account_id_type id,
const operation& op
) -> vector<authority>
{
return db.get_account_custom_authorities(id, op);
} ;
auto chk = [&](
const signed_transaction& tx,
flat_set<public_key_type> available_keys,
@ -1196,7 +1204,7 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
@ -1303,6 +1311,14 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
return &(aid(db).owner);
} ;
auto get_custom = [&](
account_id_type id,
const operation& op
) -> vector<authority>
{
return db.get_account_custom_authorities(id, op);
} ;
auto chk = [&](
const signed_transaction& tx,
flat_set<public_key_type> available_keys,
@ -1310,7 +1326,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
@ -1322,7 +1338,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
@ -1341,9 +1357,9 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) );
BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), fc::exception );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ), fc::exception );
sign( tx, alice_private_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom );
}
catch(fc::exception& e)
{

File diff suppressed because it is too large Load diff