From 2a7c8749d342a767d1fec4c671eb3e26f7b3adc2 Mon Sep 17 00:00:00 2001 From: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Date: Tue, 26 May 2020 12:28:25 +0000 Subject: [PATCH 01/13] rbac1 - evaluators and op validators added --- libraries/chain/CMakeLists.txt | 4 + .../custom_account_authority_evaluator.cpp | 150 ++++++++++++++++++ .../chain/custom_permission_evaluator.cpp | 115 ++++++++++++++ libraries/chain/db_init.cpp | 12 ++ libraries/chain/db_notify.cpp | 19 +++ libraries/chain/hardfork.d/RBAC.hf | 4 + .../chain/include/graphene/chain/config.hpp | 3 + .../custom_account_authority_evaluator.hpp | 34 ++++ .../chain/custom_account_authority_object.hpp | 55 +++++++ .../chain/custom_permission_evaluator.hpp | 34 ++++ .../chain/custom_permission_object.hpp | 49 ++++++ .../protocol/custom_account_authority.hpp | 59 +++++++ .../chain/protocol/custom_permission.hpp | 56 +++++++ .../graphene/chain/protocol/operations.hpp | 10 +- .../include/graphene/chain/protocol/types.hpp | 10 ++ .../protocol/custom_account_authority.cpp | 39 +++++ .../chain/protocol/custom_permission.cpp | 77 +++++++++ programs/js_operation_serializer/main.cpp | 2 + 18 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 libraries/chain/custom_account_authority_evaluator.cpp create mode 100644 libraries/chain/custom_permission_evaluator.cpp create mode 100644 libraries/chain/hardfork.d/RBAC.hf create mode 100644 libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/custom_account_authority_object.hpp create mode 100644 libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/custom_permission_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/custom_permission.hpp create mode 100644 libraries/chain/protocol/custom_account_authority.cpp create mode 100644 libraries/chain/protocol/custom_permission.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 07f1ea0a..3ab155fe 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -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 genesis_state.cpp get_config.cpp @@ -112,6 +114,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 diff --git a/libraries/chain/custom_account_authority_evaluator.cpp b/libraries/chain/custom_account_authority_evaluator.cpp new file mode 100644 index 00000000..0d4cbf14 --- /dev/null +++ b/libraries/chain/custom_account_authority_evaluator.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include +#include +#include + +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) {} + + template + void operator()(const T &v) const {} + + void operator()(const custom_permission_create_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_create_operation not allowed yet!" ); + } + void operator()(const custom_permission_update_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_update_operation not allowed yet!" ); + } + void operator()(const custom_permission_delete_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_delete_operation not allowed yet!" ); + } + void operator()(const custom_account_authority_create_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_create_operation not allowed yet!" ); + } + void operator()(const custom_account_authority_update_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_update_operation not allowed yet!" ); + } + void operator()(const custom_account_authority_delete_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_delete_operation not allowed yet!" ); + } +}; + +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"); + 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([&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) { + FC_ASSERT(*op.new_valid_from != aobj.valid_from, + "New valid_from provided is not different from old valid_from"); + valid_from = *op.new_valid_from; + } + + if (op.new_valid_to) { + FC_ASSERT(*op.new_valid_to != aobj.valid_to, + "New valid_to provided is not different from old 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)) +} + +object_id_type 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); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +} // namespace chain +} // namespace graphene diff --git a/libraries/chain/custom_permission_evaluator.cpp b/libraries/chain/custom_permission_evaluator.cpp new file mode 100644 index 00000000..621f9e8b --- /dev/null +++ b/libraries/chain/custom_permission_evaluator.cpp @@ -0,0 +1,115 @@ +#include + +#include +#include +#include + +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().indices().get(); + 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([&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); + // TODO: Remove all the custom_account_authority_object linked to this permission object. + d.remove(pobj); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) +} + +} // namespace chain +} // namespace graphene \ No newline at end of file diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 4e30029b..49214931 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include @@ -77,6 +79,8 @@ #include #include #include +#include +#include #include @@ -243,6 +247,12 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -284,6 +294,8 @@ void database::initialize_indexes() tournament_details_idx->add_secondary_index(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index e91eaa6b..b25399e6 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -293,6 +293,25 @@ 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 graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/hardfork.d/RBAC.hf b/libraries/chain/hardfork.d/RBAC.hf new file mode 100644 index 00000000..e91e1b8b --- /dev/null +++ b/libraries/chain/hardfork.d/RBAC.hf @@ -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 diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 710db6c5..fe552610 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -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 \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp b/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp new file mode 100644 index 00000000..b168a3de --- /dev/null +++ b/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class create_custom_account_authority_evaluator : public 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 +{ +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 +{ +public: + typedef custom_account_authority_delete_operation operation_type; + + void_result do_evaluate(const custom_account_authority_delete_operation& o); + object_id_type do_apply(const custom_account_authority_delete_operation& o); +}; + +} } // namespace graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/custom_account_authority_object.hpp b/libraries/chain/include/graphene/chain/custom_account_authority_object.hpp new file mode 100644 index 00000000..acca8bcf --- /dev/null +++ b/libraries/chain/include/graphene/chain/custom_account_authority_object.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include + +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 + { + 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, + member + >, + ordered_unique< tag, + composite_key, + member, + member + > + >, + ordered_unique, + composite_key, + member + > + > + > + >; + using custom_account_authority_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::custom_account_authority_object, (graphene::db::object), + (permission_id)(operation_type)(valid_from)(valid_to) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp b/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp new file mode 100644 index 00000000..f436b6e5 --- /dev/null +++ b/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class create_custom_permission_evaluator : public 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 +{ +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 +{ +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 graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/custom_permission_object.hpp b/libraries/chain/include/graphene/chain/custom_permission_object.hpp new file mode 100644 index 00000000..72789ef4 --- /dev/null +++ b/libraries/chain/include/graphene/chain/custom_permission_object.hpp @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include + +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 + { + 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, + member + >, + ordered_unique< tag, + composite_key, + member + > + > + > + >; + using custom_permission_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::custom_permission_object, (graphene::db::object), + (account)(permission_name)(auth) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp new file mode 100644 index 00000000..be1d7868 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp @@ -0,0 +1,59 @@ +#pragma once +#include + +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 new_valid_from; + optional 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 graphene::chain + +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) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp new file mode 100644 index 00000000..7226dfe8 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp @@ -0,0 +1,56 @@ +#pragma once +#include + +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 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 graphene::chain + +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) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index cb9a83a1..c64249d9 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -45,6 +45,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { @@ -135,7 +137,13 @@ namespace graphene { namespace chain { ticket_purchase_operation, lottery_reward_operation, lottery_end_operation, - sweeps_vesting_claim_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 > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 8ea3a8af..c5ff066f 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -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, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -230,6 +232,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; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -256,6 +260,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; // implementation types class global_property_object; @@ -436,6 +442,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) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -505,6 +513,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( graphene::chain::void_t, ) diff --git a/libraries/chain/protocol/custom_account_authority.cpp b/libraries/chain/protocol/custom_account_authority.cpp new file mode 100644 index 00000000..4cbdb0e9 --- /dev/null +++ b/libraries/chain/protocol/custom_account_authority.cpp @@ -0,0 +1,39 @@ +#include +#include + +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"); +} + +} } // graphene::chain diff --git a/libraries/chain/protocol/custom_permission.cpp b/libraries/chain/protocol/custom_permission.cpp new file mode 100644 index 00000000..eb2c88cd --- /dev/null +++ b/libraries/chain/protocol/custom_permission.cpp @@ -0,0 +1,77 @@ +#include +#include + +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"); +} + +} } // graphene::chain diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 8994b36b..a2962bb1 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include From c74e83d088708dfdcd21eeb833c7c43d3d9c6a7b Mon Sep 17 00:00:00 2001 From: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Date: Tue, 26 May 2020 15:48:02 +0000 Subject: [PATCH 02/13] rbac2 - op_type hf checks --- .../custom_account_authority_evaluator.cpp | 75 ++++++------- .../chain/custom_permission_evaluator.cpp | 11 +- .../custom_account_authority_evaluator.hpp | 26 +++-- .../chain/custom_permission_evaluator.hpp | 26 +++-- .../protocol/custom_account_authority.hpp | 99 +++++++++-------- .../chain/protocol/custom_permission.hpp | 93 +++++++++------- .../protocol/custom_account_authority.cpp | 35 +++--- .../chain/protocol/custom_permission.cpp | 103 +++++++++--------- 8 files changed, 253 insertions(+), 215 deletions(-) diff --git a/libraries/chain/custom_account_authority_evaluator.cpp b/libraries/chain/custom_account_authority_evaluator.cpp index 0d4cbf14..651698c8 100644 --- a/libraries/chain/custom_account_authority_evaluator.cpp +++ b/libraries/chain/custom_account_authority_evaluator.cpp @@ -15,28 +15,23 @@ 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) {} - - template - void operator()(const T &v) const {} - - void operator()(const custom_permission_create_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_create_operation not allowed yet!" ); - } - void operator()(const custom_permission_update_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_update_operation not allowed yet!" ); - } - void operator()(const custom_permission_delete_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_delete_operation not allowed yet!" ); - } - void operator()(const custom_account_authority_create_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_create_operation not allowed yet!" ); - } - void operator()(const custom_account_authority_update_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_update_operation not allowed yet!" ); - } - void operator()(const custom_account_authority_delete_operation &op) const { - FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_delete_operation not allowed yet!" ); + 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::value; + switch (op_type) + { + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::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!"); + } } }; @@ -48,9 +43,11 @@ void_result create_custom_account_authority_evaluator::do_evaluate(const custom_ 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); + 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)) @@ -66,7 +63,8 @@ object_id_type create_custom_account_authority_evaluator::do_apply(const custom_ obj.operation_type = op.operation_type; obj.valid_from = op.valid_from; obj.valid_to = op.valid_to; - }).id; + }) + .id; } FC_CAPTURE_AND_RETHROW((op)) } @@ -79,20 +77,22 @@ void_result update_custom_account_authority_evaluator::do_evaluate(const custom_ 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); + 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) { + if (op.new_valid_from) + { FC_ASSERT(*op.new_valid_from != aobj.valid_from, - "New valid_from provided is not different from old valid_from"); + "New valid_from provided is not different from old valid_from"); valid_from = *op.new_valid_from; } - if (op.new_valid_to) { + if (op.new_valid_to) + { FC_ASSERT(*op.new_valid_to != aobj.valid_to, - "New valid_to provided is not different from old valid_to"); + "New valid_to provided is not different from old valid_to"); FC_ASSERT(*op.new_valid_to > now, "New valid_to expiry should be in the future"); valid_to = *op.new_valid_to; } @@ -106,8 +106,8 @@ object_id_type update_custom_account_authority_evaluator::do_apply(const custom_ { try { - database& d = db(); - const custom_account_authority_object& aobj = op.auth_id(d); + 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; @@ -127,24 +127,25 @@ void_result delete_custom_account_authority_evaluator::do_evaluate(const custom_ 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); + 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)) } -object_id_type delete_custom_account_authority_evaluator::do_apply(const custom_account_authority_delete_operation &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); + 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 +} // namespace graphene \ No newline at end of file diff --git a/libraries/chain/custom_permission_evaluator.cpp b/libraries/chain/custom_permission_evaluator.cpp index 621f9e8b..ae319420 100644 --- a/libraries/chain/custom_permission_evaluator.cpp +++ b/libraries/chain/custom_permission_evaluator.cpp @@ -39,7 +39,8 @@ object_id_type create_custom_permission_evaluator::do_apply(const custom_permiss obj.account = op.owner_account; obj.permission_name = op.permission_name; obj.auth = op.auth; - }).id; + }) + .id; } FC_CAPTURE_AND_RETHROW((op)) } @@ -52,7 +53,7 @@ void_result update_custom_permission_evaluator::do_evaluate(const custom_permiss 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); + 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) { @@ -72,7 +73,7 @@ object_id_type update_custom_permission_evaluator::do_apply(const custom_permiss try { database &d = db(); - const custom_permission_object& pobj = op.permission_id(d); + 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; @@ -91,7 +92,7 @@ void_result delete_custom_permission_evaluator::do_evaluate(const custom_permiss 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); + 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(); } @@ -103,7 +104,7 @@ void_result delete_custom_permission_evaluator::do_apply(const custom_permission try { database &d = db(); - const custom_permission_object& pobj = op.permission_id(d); + const custom_permission_object &pobj = op.permission_id(d); // TODO: Remove all the custom_account_authority_object linked to this permission object. d.remove(pobj); return void_result(); diff --git a/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp b/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp index b168a3de..3fe1f6f9 100644 --- a/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/custom_account_authority_evaluator.hpp @@ -2,33 +2,37 @@ #include #include -namespace graphene { namespace chain { +namespace graphene +{ +namespace chain +{ class create_custom_account_authority_evaluator : public evaluator { public: - typedef custom_account_authority_create_operation operation_type; + 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); + 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 { public: - typedef custom_account_authority_update_operation operation_type; + 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); + 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 { public: - typedef custom_account_authority_delete_operation operation_type; + typedef custom_account_authority_delete_operation operation_type; - void_result do_evaluate(const custom_account_authority_delete_operation& o); - object_id_type do_apply(const custom_account_authority_delete_operation& o); + void_result do_evaluate(const custom_account_authority_delete_operation &o); + void_result do_apply(const custom_account_authority_delete_operation &o); }; -} } // namespace graphene::chain \ No newline at end of file +} // namespace chain +} // namespace graphene \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp b/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp index f436b6e5..c9bc2801 100644 --- a/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/custom_permission_evaluator.hpp @@ -2,33 +2,37 @@ #include #include -namespace graphene { namespace chain { +namespace graphene +{ +namespace chain +{ class create_custom_permission_evaluator : public evaluator { public: - typedef custom_permission_create_operation operation_type; + 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); + 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 { public: - typedef custom_permission_update_operation operation_type; + 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); + 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 { public: - typedef custom_permission_delete_operation operation_type; + 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); + void_result do_evaluate(const custom_permission_delete_operation &o); + void_result do_apply(const custom_permission_delete_operation &o); }; -} } // namespace graphene::chain \ No newline at end of file +} // namespace chain +} // namespace graphene \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp index be1d7868..91da60ef 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp @@ -1,59 +1,72 @@ #pragma once #include -namespace graphene { namespace chain { +namespace graphene +{ +namespace chain +{ - struct custom_account_authority_create_operation : public base_operation +struct custom_account_authority_create_operation : public base_operation +{ + struct fee_parameters_type { - 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; } + uint64_t fee = 0; }; - struct custom_account_authority_update_operation : public base_operation + 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 { - struct fee_parameters_type { uint64_t fee = 0; }; - - asset fee; - custom_account_authority_id_type auth_id; - optional new_valid_from; - optional 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; } + uint64_t fee = 0; }; - struct custom_account_authority_delete_operation : public base_operation + asset fee; + custom_account_authority_id_type auth_id; + optional new_valid_from; + optional 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 { - 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; } + uint64_t fee = 0; }; -} } // namespace graphene::chain + asset fee; + custom_account_authority_id_type auth_id; + account_id_type owner_account; -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) ) + account_id_type fee_payer() const { return owner_account; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const { return 0; } +}; -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) ) +} // namespace chain +} // namespace graphene -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) ) \ No newline at end of file +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)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp index 7226dfe8..32faf0e2 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp @@ -1,56 +1,69 @@ #pragma once #include -namespace graphene { namespace chain { +namespace graphene +{ +namespace chain +{ - struct custom_permission_create_operation : public base_operation +struct custom_permission_create_operation : public base_operation +{ + struct fee_parameters_type { - 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; } + uint64_t fee = 0; }; - struct custom_permission_update_operation : public base_operation + 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 { - struct fee_parameters_type { uint64_t fee = 0; }; - - asset fee; - custom_permission_id_type permission_id; - optional 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; } + uint64_t fee = 0; }; - struct custom_permission_delete_operation : public base_operation + asset fee; + custom_permission_id_type permission_id; + optional 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 { - 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; } + uint64_t fee = 0; }; -} } // namespace graphene::chain + asset fee; + custom_permission_id_type permission_id; + account_id_type owner_account; -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) ) + account_id_type fee_payer() const { return owner_account; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const { return 0; } +}; -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) ) +} // namespace chain +} // namespace graphene -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) ) \ No newline at end of file +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)) \ No newline at end of file diff --git a/libraries/chain/protocol/custom_account_authority.cpp b/libraries/chain/protocol/custom_account_authority.cpp index 4cbdb0e9..bfbb51c7 100644 --- a/libraries/chain/protocol/custom_account_authority.cpp +++ b/libraries/chain/protocol/custom_account_authority.cpp @@ -1,39 +1,38 @@ #include #include -namespace graphene { namespace chain { +namespace graphene +{ +namespace chain +{ -void custom_account_authority_create_operation::validate()const { +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, + 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 { +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, + 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) { + 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 { +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, + 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"); } -} } // graphene::chain +} // namespace chain +} // namespace graphene diff --git a/libraries/chain/protocol/custom_permission.cpp b/libraries/chain/protocol/custom_permission.cpp index eb2c88cd..ad240ce7 100644 --- a/libraries/chain/protocol/custom_permission.cpp +++ b/libraries/chain/protocol/custom_permission.cpp @@ -1,77 +1,80 @@ #include #include -namespace graphene { namespace chain { +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 ) +bool is_valid_permission_name(const string &name) +{ + try { - 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')) ) + 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; } - } - // 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) ) } + return true; + } + FC_CAPTURE_AND_RETHROW((name)) +} -void custom_permission_create_operation::validate()const { +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, + 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 { +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, + 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.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 { +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, + 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"); } -} } // graphene::chain +} // namespace chain +} // namespace graphene From f376503ac6de53facff7465e67ac4b83f465a9fb Mon Sep 17 00:00:00 2001 From: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Date: Wed, 27 May 2020 11:36:57 +0000 Subject: [PATCH 03/13] rbac3 - tx auth verify changes --- libraries/app/database_api.cpp | 2 + libraries/chain/db_block.cpp | 5 ++- libraries/chain/db_getter.cpp | 22 +++++++++ .../chain/include/graphene/chain/database.hpp | 1 + .../graphene/chain/protocol/transaction.hpp | 3 ++ libraries/chain/proposal_evaluator.cpp | 24 ++++++++++ libraries/chain/proposal_object.cpp | 2 + libraries/chain/protocol/transaction.cpp | 45 +++++++++++++++---- tests/tests/authority_tests.cpp | 14 ++++-- 9 files changed, 105 insertions(+), 13 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index df6458f3..2aee274d 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1934,6 +1934,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; } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index b45d922b..1d555fa5 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -790,7 +790,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 diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index a6f7af19..294aaa5e 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include @@ -159,4 +161,24 @@ const witness_schedule_object& database::get_witness_schedule_object()const return *_p_witness_schedule_obj; } +vector database::get_account_custom_authorities(account_id_type account, const operation& op)const +{ + const auto& pindex = get_index_type().indices().get(); + const auto& cindex = get_index_type().indices().get(); + auto prange = pindex.equal_range(boost::make_tuple(account)); + time_point_sec now = head_block_time(); + vector 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; +} } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index e697b797..2b921b1d 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -294,6 +294,7 @@ namespace graphene { namespace chain { uint32_t last_non_undoable_block_num() const; + vector get_account_custom_authorities(account_id_type account, const operation& op)const; //////////////////// db_init.cpp //////////////////// void initialize_evaluators(); diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 2a9909a5..7408a29c 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -148,6 +148,7 @@ namespace graphene { namespace chain { const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; /** @@ -162,6 +163,7 @@ namespace graphene { namespace chain { const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH ) const; @@ -194,6 +196,7 @@ namespace graphene { namespace chain { void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committe = false, const flat_set& active_aprovals = flat_set(), diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 88d985ff..efa07a46 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -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) diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 1d5a8706..a2f6d1ae 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -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, diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 093e7833..7f931aba 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -248,6 +248,7 @@ struct sign_state void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion_depth, bool allow_committe, const flat_set& active_aprovals, @@ -257,13 +258,6 @@ void verify_authority( const vector& ops, const flat_set required_owner; vector 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,37 @@ void verify_authority( const vector& ops, const flat_set 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) ); @@ -359,6 +384,7 @@ set signed_transaction::minimize_required_signatures( const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion ) const { @@ -370,7 +396,7 @@ set 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 +411,10 @@ void signed_transaction::verify_authority( const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + const std::function(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 diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 2afd12a6..92c62ba6 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1303,6 +1303,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 + { + return db.get_account_custom_authorities(id, op); + } ; + auto chk = [&]( const signed_transaction& tx, flat_set available_keys, @@ -1322,7 +1330,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + set 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 +1349,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) { From b1124a166f3a6669c2d1f016727ae80b89ceccd5 Mon Sep 17 00:00:00 2001 From: Roshan Syed Date: Wed, 27 May 2020 12:18:22 +0000 Subject: [PATCH 04/13] Update .gitlab-ci.yml --- .gitlab-ci.yml | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 42ec77fc..94966ab6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,26 +30,4 @@ test: - ./tests/chain_test - ./tests/cli_test tags: - - builder - -code_quality: - stage: test - image: docker:stable - variables: - DOCKER_DRIVER: overlay2 - allow_failure: true - services: - - docker:stable-dind - script: - - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - - docker run - --env SOURCE_CODE="$PWD" - --volume "$PWD":/code - --volume /var/run/docker.sock:/var/run/docker.sock - "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code - artifacts: - paths: [gl-code-quality-report.json] - expire_in: 1 week - except: - variables: - - $CODE_QUALITY_DISABLED + - builder \ No newline at end of file From 4c2efdb871eae370271df2eff3dfb9f806919948 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Wed, 27 May 2020 19:57:27 +0000 Subject: [PATCH 05/13] rbac4 - basic op tests --- libraries/chain/protocol/transaction.cpp | 2 - tests/tests/custom_permission_tests.cpp | 454 +++++++++++++++++++++++ 2 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 tests/tests/custom_permission_tests.cpp diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 7f931aba..ff375bc5 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -274,8 +274,6 @@ void verify_authority( const vector& ops, const flat_set operation_required_active; operation_get_required_authorities( op, operation_required_active, required_owner, other ); diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp new file mode 100644 index 00000000..ff037995 --- /dev/null +++ b/tests/tests/custom_permission_tests.cpp @@ -0,0 +1,454 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE(custom_permission_tests, database_fixture) + +BOOST_AUTO_TEST_CASE(permission_create_fail_test) +{ + try + { + generate_blocks(HARDFORK_RBAC_TIME); + generate_block(); + set_expiration(db, trx); + ACTORS((alice)(bob)); + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + transfer(committee_account, alice_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, bob_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + const auto &pidx = db.get_index_type().indices().get(); + // alice fails to create custom permission + { + custom_permission_create_operation op; + op.owner_account = alice_id; + op.auth = authority(1, bob_id, 1); + op.permission_name = "123"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = ""; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "1ab"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = ".abc"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "abc."; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "ABC"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "active"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "owner"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "abcdefghijk"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "ab"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "***"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.permission_name = "a12"; + BOOST_CHECK_NO_THROW(op.validate()); + op.permission_name = "a1b"; + BOOST_CHECK_NO_THROW(op.validate()); + op.permission_name = "abc"; + BOOST_CHECK_NO_THROW(op.validate()); + op.permission_name = "abc123defg"; + BOOST_CHECK_NO_THROW(op.validate()); + BOOST_REQUIRE(pidx.size() == 0); + } + { + custom_permission_create_operation op; + op.permission_name = "abc"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + const fc::ecc::private_key tpvk = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("test"))); + const public_key_type tpbk(tpvk.get_public_key()); + op.auth = authority(1, address(tpbk), 1); + BOOST_CHECK_THROW(op.validate(), fc::exception); + + BOOST_REQUIRE(pidx.size() == 0); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(permission_create_success_test) +{ + try + { + generate_blocks(HARDFORK_RBAC_TIME); + generate_block(); + set_expiration(db, trx); + ACTORS((alice)(bob)); + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + transfer(committee_account, alice_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, bob_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + const auto &pidx = db.get_index_type().indices().get(); + // Alice creates a permission abc + { + custom_permission_create_operation op; + op.permission_name = "abc"; + op.owner_account = alice_id; + op.auth = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + } + // Alice tries to create a permission with same name but fails + { + custom_permission_create_operation op; + op.permission_name = "abc"; + op.owner_account = alice_id; + op.auth = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(permission_update_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + ACTORS((charlie)); + const auto &pidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + // Alice tries to update permission with same auth but fails + { + custom_permission_update_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + op.new_auth = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice tries to update permission with no auth but fails + { + custom_permission_update_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice tries to update permission with charlie onwer_account but fails + { + custom_permission_update_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = charlie_id; + op.new_auth = authority(1, charlie_id, 1); + trx.operations.push_back(op); + sign(trx, charlie_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice updates permission abc with wrong permission_id + { + custom_permission_update_operation op; + op.permission_id = custom_permission_id_type(1); + op.owner_account = alice_id; + op.new_auth = authority(1, charlie_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice updates permission abc with new auth + { + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + custom_permission_update_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + op.new_auth = authority(1, charlie_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, charlie_id, 1)); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(permission_delete_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + // Alice tries to delete permission abc with wrong owner_account + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = bob_id; + trx.operations.push_back(op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice tries to delete permission abc with wrong permission_id + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(1); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice deletes permission abc + { + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 0); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_authority_create_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + generate_block(); + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = db.head_block_time(); + op.valid_to = db.head_block_time() + fc::seconds(10 * db.block_interval()); + op.operation_type = 0; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // Alice creates the same account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = db.head_block_time(); + op.valid_to = db.head_block_time() + fc::seconds(11 * db.block_interval()); + op.operation_type = 0; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_authority_update_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + // Alice update the account auth linking with permission abc + { + custom_account_authority_update_operation op; + op.auth_id = custom_account_authority_id_type(0); + fc::time_point_sec expiry = db.head_block_time() + fc::seconds(50 * db.block_interval()); + op.new_valid_to = expiry; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + BOOST_REQUIRE(cidx.size() == 2); + BOOST_REQUIRE(custom_account_authority_id_type(0)(db).valid_to == expiry); + BOOST_REQUIRE(custom_account_authority_id_type(1)(db).valid_to < expiry); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_authority_delete_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + // Alice deletes account auth linking with permission abc + { + custom_account_authority_delete_operation op; + op.auth_id = custom_account_authority_id_type(0); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + BOOST_REQUIRE(cidx.size() == 1); + } + // Alice deletes the account auth linking with permission abc + { + custom_account_authority_delete_operation op; + op.auth_id = custom_account_authority_id_type(1); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + BOOST_REQUIRE(cidx.size() == 0); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = bob_id; + op.to = alice_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() From 88a8753a604597387a867e6e286aed60ef359b03 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Fri, 29 May 2020 10:42:24 +0000 Subject: [PATCH 06/13] rbac5 - clear expired and deleted permission linked auths --- .../chain/custom_permission_evaluator.cpp | 19 ++- libraries/chain/db_maint.cpp | 15 +- tests/tests/custom_permission_tests.cpp | 136 ++++++++++-------- 3 files changed, 110 insertions(+), 60 deletions(-) diff --git a/libraries/chain/custom_permission_evaluator.cpp b/libraries/chain/custom_permission_evaluator.cpp index ae319420..dff6d110 100644 --- a/libraries/chain/custom_permission_evaluator.cpp +++ b/libraries/chain/custom_permission_evaluator.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace graphene @@ -105,7 +106,21 @@ void_result delete_custom_permission_evaluator::do_apply(const custom_permission { database &d = db(); const custom_permission_object &pobj = op.permission_id(d); - // TODO: Remove all the custom_account_authority_object linked to this permission object. + // Remove the account authority objects linked to this permission + const auto& cindex = d.get_index_type().indices().get(); + vector> 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(); } @@ -113,4 +128,4 @@ void_result delete_custom_permission_evaluator::do_apply(const custom_permission } } // namespace chain -} // namespace graphene \ No newline at end of file +} // namespace graphene diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 11a2b426..d300a34b 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #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().indices().get(); + 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() ) for( const auto& d : get_index_type().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(); diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp index ff037995..e2015b58 100644 --- a/tests/tests/custom_permission_tests.cpp +++ b/tests/tests/custom_permission_tests.cpp @@ -93,11 +93,17 @@ BOOST_AUTO_TEST_CASE(permission_create_success_test) generate_blocks(HARDFORK_RBAC_TIME); generate_block(); set_expiration(db, trx); - ACTORS((alice)(bob)); + ACTORS((alice)(bob)(charlie)(dave)(erin)); upgrade_to_lifetime_member(alice); upgrade_to_lifetime_member(bob); + upgrade_to_lifetime_member(charlie); + upgrade_to_lifetime_member(dave); + upgrade_to_lifetime_member(erin); transfer(committee_account, alice_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); transfer(committee_account, bob_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, charlie_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, dave_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, erin_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); const auto &pidx = db.get_index_type().indices().get(); // Alice creates a permission abc { @@ -138,7 +144,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) INVOKE(permission_create_success_test); GET_ACTOR(alice); GET_ACTOR(bob); - ACTORS((charlie)); + GET_ACTOR(charlie); const auto &pidx = db.get_index_type().indices().get(); BOOST_REQUIRE(pidx.size() == 1); BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); @@ -210,56 +216,6 @@ BOOST_AUTO_TEST_CASE(permission_update_test) FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(permission_delete_test) -{ - try - { - INVOKE(permission_create_success_test); - GET_ACTOR(alice); - GET_ACTOR(bob); - const auto &pidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); - BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); - BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); - // Alice tries to delete permission abc with wrong owner_account - { - custom_permission_delete_operation op; - op.permission_id = custom_permission_id_type(0); - op.owner_account = bob_id; - trx.operations.push_back(op); - sign(trx, bob_private_key); - BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); - trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); - } - // Alice tries to delete permission abc with wrong permission_id - { - custom_permission_delete_operation op; - op.permission_id = custom_permission_id_type(1); - op.owner_account = alice_id; - trx.operations.push_back(op); - sign(trx, alice_private_key); - BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); - trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); - } - // Alice deletes permission abc - { - BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); - BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); - custom_permission_delete_operation op; - op.permission_id = custom_permission_id_type(0); - op.owner_account = alice_id; - trx.operations.push_back(op); - sign(trx, alice_private_key); - PUSH_TX(db, trx); - trx.clear(); - BOOST_REQUIRE(pidx.size() == 0); - } - } - FC_LOG_AND_RETHROW() -} - BOOST_AUTO_TEST_CASE(account_authority_create_test) { try @@ -279,7 +235,7 @@ BOOST_AUTO_TEST_CASE(account_authority_create_test) op.permission_id = custom_permission_id_type(0); op.valid_from = db.head_block_time(); op.valid_to = db.head_block_time() + fc::seconds(10 * db.block_interval()); - op.operation_type = 0; + op.operation_type = operation::tag::value; op.owner_account = alice_id; trx.operations.push_back(op); sign(trx, alice_private_key); @@ -294,7 +250,7 @@ BOOST_AUTO_TEST_CASE(account_authority_create_test) op.permission_id = custom_permission_id_type(0); op.valid_from = db.head_block_time(); op.valid_to = db.head_block_time() + fc::seconds(11 * db.block_interval()); - op.operation_type = 0; + op.operation_type = operation::tag::value; op.owner_account = alice_id; trx.operations.push_back(op); sign(trx, alice_private_key); @@ -379,6 +335,57 @@ BOOST_AUTO_TEST_CASE(account_authority_delete_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(permission_delete_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + BOOST_REQUIRE(cidx.size() == 2); + // Alice tries to delete permission abc with wrong owner_account + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = bob_id; + trx.operations.push_back(op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice tries to delete permission abc with wrong permission_id + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(1); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + } + // Alice deletes permission abc + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 0); + BOOST_REQUIRE(cidx.size() == 0); + } + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) { try @@ -390,6 +397,7 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) const auto &cidx = db.get_index_type().indices().get(); BOOST_REQUIRE(pidx.size() == 1); BOOST_REQUIRE(cidx.size() == 2); + // alice->bob transfer_operation op with active auth, success generate_block(); { transfer_operation op; @@ -404,7 +412,7 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) trx.clear(); generate_block(); } - + // alice->bob transfer_operation op with the created custom account auth, success { transfer_operation op; op.amount.asset_id = asset_id_type(0); @@ -418,7 +426,7 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) trx.clear(); generate_block(); } - + // alice->bob transfer_operation op with extra unnecessary sigs (both active and the custom auth), fails { transfer_operation op; op.amount.asset_id = asset_id_type(0); @@ -433,7 +441,7 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) trx.clear(); generate_block(); } - + // bob->alice transfer_operation op with alice active auth sig, fails { transfer_operation op; op.amount.asset_id = asset_id_type(0); @@ -447,6 +455,20 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) trx.clear(); generate_block(); } + // bob->alice transfer_operation op with bob active auth sig, success + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = bob_id; + op.to = alice_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } } FC_LOG_AND_RETHROW() } From 29e3802954d7924a005bd52a61c88899239ae57a Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Mon, 1 Jun 2020 12:20:47 +0000 Subject: [PATCH 07/13] rbac6 - more tests --- tests/tests/custom_permission_tests.cpp | 427 ++++++++++++++++++++++-- 1 file changed, 408 insertions(+), 19 deletions(-) diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp index e2015b58..695de208 100644 --- a/tests/tests/custom_permission_tests.cpp +++ b/tests/tests/custom_permission_tests.cpp @@ -133,6 +133,20 @@ BOOST_AUTO_TEST_CASE(permission_create_success_test) BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); } + // Alice creates a permission def + { + custom_permission_create_operation op; + op.permission_name = "def"; + op.owner_account = alice_id; + op.auth = authority(1, charlie_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 2); + BOOST_REQUIRE(custom_permission_id_type(1)(db).permission_name == "def"); + BOOST_REQUIRE(custom_permission_id_type(1)(db).auth == authority(1, charlie_id, 1)); + } } FC_LOG_AND_RETHROW() } @@ -146,9 +160,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) GET_ACTOR(bob); GET_ACTOR(charlie); const auto &pidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); - BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); - BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + BOOST_REQUIRE(pidx.size() == 2); // Alice tries to update permission with same auth but fails { custom_permission_update_operation op; @@ -159,7 +171,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) sign(trx, alice_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice tries to update permission with no auth but fails { @@ -170,7 +182,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) sign(trx, alice_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice tries to update permission with charlie onwer_account but fails { @@ -182,7 +194,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) sign(trx, charlie_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice updates permission abc with wrong permission_id { @@ -194,7 +206,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) sign(trx, alice_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice updates permission abc with new auth { @@ -208,7 +220,7 @@ BOOST_AUTO_TEST_CASE(permission_update_test) sign(trx, alice_private_key); PUSH_TX(db, trx); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, charlie_id, 1)); } @@ -225,9 +237,7 @@ BOOST_AUTO_TEST_CASE(account_authority_create_test) GET_ACTOR(bob); const auto &pidx = db.get_index_type().indices().get(); const auto &cidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); - BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); - BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); + BOOST_REQUIRE(pidx.size() == 2); generate_block(); // Alice creates a new account auth linking with permission abc { @@ -272,7 +282,7 @@ BOOST_AUTO_TEST_CASE(account_authority_update_test) GET_ACTOR(bob); const auto &pidx = db.get_index_type().indices().get(); const auto &cidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); BOOST_REQUIRE(cidx.size() == 2); generate_block(); // Alice update the account auth linking with permission abc @@ -304,7 +314,7 @@ BOOST_AUTO_TEST_CASE(account_authority_delete_test) GET_ACTOR(bob); const auto &pidx = db.get_index_type().indices().get(); const auto &cidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); BOOST_REQUIRE(cidx.size() == 2); generate_block(); // Alice deletes account auth linking with permission abc @@ -344,7 +354,7 @@ BOOST_AUTO_TEST_CASE(permission_delete_test) GET_ACTOR(bob); const auto &pidx = db.get_index_type().indices().get(); const auto &cidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, bob_id, 1)); BOOST_REQUIRE(cidx.size() == 2); @@ -357,18 +367,18 @@ BOOST_AUTO_TEST_CASE(permission_delete_test) sign(trx, bob_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice tries to delete permission abc with wrong permission_id { custom_permission_delete_operation op; - op.permission_id = custom_permission_id_type(1); + op.permission_id = custom_permission_id_type(2); op.owner_account = alice_id; trx.operations.push_back(op); sign(trx, alice_private_key); BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); trx.clear(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); } // Alice deletes permission abc { @@ -379,7 +389,7 @@ BOOST_AUTO_TEST_CASE(permission_delete_test) sign(trx, alice_private_key); PUSH_TX(db, trx); trx.clear(); - BOOST_REQUIRE(pidx.size() == 0); + BOOST_REQUIRE(pidx.size() == 1); BOOST_REQUIRE(cidx.size() == 0); } } @@ -395,7 +405,7 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) GET_ACTOR(bob); const auto &pidx = db.get_index_type().indices().get(); const auto &cidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(pidx.size() == 2); BOOST_REQUIRE(cidx.size() == 2); // alice->bob transfer_operation op with active auth, success generate_block(); @@ -469,6 +479,385 @@ BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) trx.clear(); generate_block(); } + // Alice deletes permission abc + { + custom_permission_delete_operation op; + op.permission_id = custom_permission_id_type(0); + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(cidx.size() == 0); + generate_block(); + } + // alice->bob transfer_operation op with active auth, success + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // alice->bob transfer_operation op with the deleted custom account auth, fail + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(transfer_op_auhtorized_auth_change_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 2); + BOOST_REQUIRE(cidx.size() == 2); + // alice->bob transfer_operation op with the created custom account auth, success + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // bob changes his auth by changing his auth key + fc::ecc::private_key test_private_key = generate_private_key("test"); + public_key_type test_public_key = public_key_type(test_private_key.get_public_key()); + { + account_update_operation op; + op.account = bob.get_id(); + op.active = authority(1, test_public_key, 1); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // alice->bob transfer_operation op with bob first private key, fails + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + // alice->bob transfer_operation op with bob first private key, fails + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + sign(trx, test_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(transfer_op_multi_ops_in_single_trx_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + // bob->alice xfer op + transfer_operation bob_to_alice_xfer_op; + bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); + bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + bob_to_alice_xfer_op.from = bob_id; + bob_to_alice_xfer_op.to = alice_id; + bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); + // Change bob's active auth to alice's auth + { + account_update_operation op; + op.account = bob_id; + op.active = authority(1, alice_id, 1); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Success -> alice active key + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Fail -> custom account auth is bob active auth which is alice active key + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + // Success -> bob's active key is alice's auth active key + trx.operations = {bob_to_alice_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> bob's owner key + trx.operations = {bob_to_alice_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> alice active key is auth for both alice and bob + trx.operations = {alice_to_bob_xfer_op, bob_to_alice_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Fail -> custom account auth is bob active auth which is alice active key + trx.operations = {alice_to_bob_xfer_op, bob_to_alice_xfer_op}; + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + // Fail -> alice active auth satisfies everything, bob owner key is not used + trx.operations = {alice_to_bob_xfer_op, bob_to_alice_xfer_op}; + sign(trx, bob_private_key); + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + // Fail -> extra unnecessary signature of charlie + trx.operations = {alice_to_bob_xfer_op, bob_to_alice_xfer_op}; + sign(trx, bob_private_key); + sign(trx, alice_private_key); + sign(trx, charlie_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(transfer_op_multi_sig_with_common_auth_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + GET_ACTOR(dave); + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + // Change alice's active auth to multisig 2-of-3 bob, charlie, dave + { + account_update_operation op; + op.account = alice_id; + op.active = authority(2, bob_id, 1, charlie_id, 1, dave_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Success -> alice owner key + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> alice custom auth is bob + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> 2-of-3 auth satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, charlie_private_key); + sign(trx, dave_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Fail -> Custom auth(bob private key) itself satisfies + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + sign(trx, charlie_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + // Fail -> Custom auth(bob private key) itself satisfies + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + sign(trx, dave_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(transfer_op_multi_sig_with_out_common_auth_test) +{ + try + { + generate_blocks(HARDFORK_RBAC_TIME); + generate_block(); + set_expiration(db, trx); + ACTORS((alice)(bob)(charlie)(dave)); + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + upgrade_to_lifetime_member(charlie); + upgrade_to_lifetime_member(dave); + transfer(committee_account, alice_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, bob_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, charlie_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, dave_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + fc::ecc::private_key test_private_key = generate_private_key("test"); + public_key_type test_public_key = public_key_type(test_private_key.get_public_key()); + { + custom_permission_create_operation op; + op.permission_name = "abc"; + op.owner_account = alice_id; + op.auth = authority(1, test_public_key, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 1); + BOOST_REQUIRE(custom_permission_id_type(0)(db).permission_name == "abc"); + BOOST_REQUIRE(custom_permission_id_type(0)(db).auth == authority(1, test_public_key, 1)); + generate_block(); + } + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = db.head_block_time(); + op.valid_to = db.head_block_time() + fc::seconds(10 * db.block_interval()); + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // Multisig with common account auth + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + // Change alice's active auth to multisig 2-of-3 bob, charlie, dave + { + account_update_operation op; + op.account = alice_id; + op.active = authority(2, bob_id, 1, charlie_id, 1, dave_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Success -> alice owner key + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Fail -> auth not satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + // Success -> custom key auth satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, test_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> 2-of-3 auth satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, charlie_private_key); + sign(trx, dave_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> 2-of-3 auth satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + sign(trx, charlie_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + // Success -> 2-of-3 auth satisfied + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + sign(trx, dave_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } } FC_LOG_AND_RETHROW() } From 8ee292053a4901561af722bfcf31c5dcdc52d148 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Mon, 1 Jun 2020 16:30:39 +0000 Subject: [PATCH 08/13] rbac7 - more tests --- tests/tests/custom_permission_tests.cpp | 329 +++++++++++++++++++++++- 1 file changed, 325 insertions(+), 4 deletions(-) diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp index 695de208..c590fa15 100644 --- a/tests/tests/custom_permission_tests.cpp +++ b/tests/tests/custom_permission_tests.cpp @@ -25,16 +25,28 @@ BOOST_AUTO_TEST_CASE(permission_create_fail_test) { try { - generate_blocks(HARDFORK_RBAC_TIME); - generate_block(); - set_expiration(db, trx); ACTORS((alice)(bob)); upgrade_to_lifetime_member(alice); upgrade_to_lifetime_member(bob); transfer(committee_account, alice_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); transfer(committee_account, bob_id, asset(1000 * GRAPHENE_BLOCKCHAIN_PRECISION)); const auto &pidx = db.get_index_type().indices().get(); + { + custom_permission_create_operation op; + op.permission_name = "abc"; + op.owner_account = alice_id; + op.auth = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + // Fail, not RBAC HF time yet + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + BOOST_REQUIRE(pidx.size() == 0); + } // alice fails to create custom permission + generate_blocks(HARDFORK_RBAC_TIME); + generate_block(); + set_expiration(db, trx); { custom_permission_create_operation op; op.owner_account = alice_id; @@ -74,12 +86,13 @@ BOOST_AUTO_TEST_CASE(permission_create_fail_test) { custom_permission_create_operation op; op.permission_name = "abc"; + // No valid auth BOOST_CHECK_THROW(op.validate(), fc::exception); const fc::ecc::private_key tpvk = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("test"))); const public_key_type tpbk(tpvk.get_public_key()); op.auth = authority(1, address(tpbk), 1); + // Address auth not supported BOOST_CHECK_THROW(op.validate(), fc::exception); - BOOST_REQUIRE(pidx.size() == 0); } } @@ -396,6 +409,132 @@ BOOST_AUTO_TEST_CASE(permission_delete_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(authority_validity_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(pidx.size() == 2); + generate_block(); + time_point_sec valid_from = db.head_block_time() + fc::seconds(20 * db.block_interval()); + time_point_sec valid_to = db.head_block_time() + fc::seconds(30 * db.block_interval()); + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // alice->bob transfer_operation op with active auth, success + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // alice->bob fail as block time < valid_from + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + generate_blocks(valid_from); + // alice->bob fail as block time < valid_from + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // time >= valid_from + // alice->bob transfer_operation op with bob active auth sig, success + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + generate_blocks(valid_to); + // alice->bob fail as block time >= valid_to + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + // alice->bob fail as block time > valid_to + { + transfer_operation op; + op.amount.asset_id = asset_id_type(0); + op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + op.from = alice_id; + op.to = bob_id; + op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE(transfer_op_custom_permission_test) { try @@ -862,4 +1001,186 @@ BOOST_AUTO_TEST_CASE(transfer_op_multi_sig_with_out_common_auth_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(proposal_op_test) +{ + try + { + INVOKE(account_authority_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + GET_ACTOR(dave); + generate_block(); + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + + // bob->alice xfer op + transfer_operation bob_to_alice_xfer_op; + bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); + bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + bob_to_alice_xfer_op.from = bob_id; + bob_to_alice_xfer_op.to = alice_id; + bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); + + proposal_create_operation prop; + prop.fee_paying_account = alice_id; + prop.proposed_ops = {op_wrapper(alice_to_bob_xfer_op), op_wrapper(bob_to_alice_xfer_op)}; + prop.expiration_time = db.head_block_time() + 21600; + trx.operations = {prop}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + proposal_update_operation approve_prop; + approve_prop.proposal = proposal_id_type(0); + approve_prop.fee_paying_account = bob_id; + approve_prop.active_approvals_to_add = {bob_id}; + trx.operations = {approve_prop}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + + // bob->alice xfer op + transfer_operation bob_to_alice_xfer_op; + bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); + bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + bob_to_alice_xfer_op.from = bob_id; + bob_to_alice_xfer_op.to = alice_id; + bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); + + custom_account_authority_create_operation authorize_xfer_op; + authorize_xfer_op.permission_id = custom_permission_id_type(1); + authorize_xfer_op.valid_from = db.head_block_time(); + authorize_xfer_op.valid_to = db.head_block_time() + fc::seconds(10 * db.block_interval()); + authorize_xfer_op.operation_type = operation::tag::value; + authorize_xfer_op.owner_account = alice_id; + + proposal_create_operation prop; + prop.fee_paying_account = alice_id; + prop.proposed_ops = {op_wrapper(authorize_xfer_op)}; + prop.expiration_time = db.head_block_time() + 21600; + trx.operations = {prop}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + proposal_update_operation approve_prop; + approve_prop.proposal = proposal_id_type(1); + approve_prop.fee_paying_account = alice_id; + approve_prop.active_approvals_to_add = {alice_id}; + trx.operations = {approve_prop}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, charlie_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + trx.operations = {bob_to_alice_xfer_op}; + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + + trx.operations = {bob_to_alice_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_authority_delete_after_expiry_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + time_point_sec valid_from = db.head_block_time() + fc::seconds(20 * db.block_interval()); + time_point_sec valid_to = db.head_block_time() + fc::seconds(30 * db.block_interval()); + BOOST_REQUIRE(pidx.size() == 2); + generate_block(); + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = db.get_dynamic_global_properties().next_maintenance_time; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + } + generate_blocks(valid_to); + generate_block(); + BOOST_REQUIRE(cidx.size() == 2); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + BOOST_REQUIRE(cidx.size() == 1); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + BOOST_REQUIRE(cidx.size() == 0); + } + FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() From 4c2ea61c48f8179a226d648fbae80d7894ca225d Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Tue, 2 Jun 2020 10:31:38 +0000 Subject: [PATCH 09/13] rbac8 - more tests --- tests/tests/custom_permission_tests.cpp | 269 +++++++++++++++++++++--- 1 file changed, 237 insertions(+), 32 deletions(-) diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp index c590fa15..9880f51a 100644 --- a/tests/tests/custom_permission_tests.cpp +++ b/tests/tests/custom_permission_tests.cpp @@ -1011,23 +1011,24 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) GET_ACTOR(charlie); GET_ACTOR(dave); generate_block(); + const auto &prop_idx = db.get_index_type().indices().get(); + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + + // bob->alice xfer op + transfer_operation bob_to_alice_xfer_op; + bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); + bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + bob_to_alice_xfer_op.from = bob_id; + bob_to_alice_xfer_op.to = alice_id; + bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); { - // alice->bob xfer op - transfer_operation alice_to_bob_xfer_op; - alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); - alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; - alice_to_bob_xfer_op.from = alice_id; - alice_to_bob_xfer_op.to = bob_id; - alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); - - // bob->alice xfer op - transfer_operation bob_to_alice_xfer_op; - bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); - bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; - bob_to_alice_xfer_op.from = bob_id; - bob_to_alice_xfer_op.to = alice_id; - bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); - + set_expiration(db, trx); proposal_create_operation prop; prop.fee_paying_account = alice_id; prop.proposed_ops = {op_wrapper(alice_to_bob_xfer_op), op_wrapper(bob_to_alice_xfer_op)}; @@ -1047,25 +1048,55 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) PUSH_TX(db, trx); trx.clear(); generate_block(); + BOOST_REQUIRE(prop_idx.find(proposal_id_type(0)) == prop_idx.end()); } - { - // alice->bob xfer op - transfer_operation alice_to_bob_xfer_op; - alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); - alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; - alice_to_bob_xfer_op.from = alice_id; - alice_to_bob_xfer_op.to = bob_id; - alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + set_expiration(db, trx); + custom_account_authority_create_operation authorize_xfer_op; + authorize_xfer_op.permission_id = custom_permission_id_type(0); + authorize_xfer_op.valid_from = db.head_block_time(); + authorize_xfer_op.valid_to = db.head_block_time() + fc::seconds(10 * db.block_interval()); + authorize_xfer_op.operation_type = operation::tag::value; + authorize_xfer_op.owner_account = alice_id; + trx.operations = {authorize_xfer_op}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); - // bob->alice xfer op - transfer_operation bob_to_alice_xfer_op; - bob_to_alice_xfer_op.amount.asset_id = asset_id_type(0); - bob_to_alice_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; - bob_to_alice_xfer_op.from = bob_id; - bob_to_alice_xfer_op.to = alice_id; - bob_to_alice_xfer_op.fee.asset_id = asset_id_type(0); + proposal_create_operation prop; + prop.fee_paying_account = alice_id; + prop.proposed_ops = {op_wrapper(alice_to_bob_xfer_op), op_wrapper(bob_to_alice_xfer_op)}; + prop.expiration_time = db.head_block_time() + 21600; + trx.operations = {prop}; + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + + proposal_update_operation approve_prop; + approve_prop.proposal = proposal_id_type(1); + approve_prop.fee_paying_account = bob_id; + approve_prop.active_approvals_to_add = {bob_id}; + trx.operations = {approve_prop}; + sign(trx, alice_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + + approve_prop.proposal = proposal_id_type(1); + approve_prop.fee_paying_account = bob_id; + approve_prop.active_approvals_to_add = {alice_id, bob_id}; + trx.operations = {approve_prop}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + BOOST_REQUIRE(prop_idx.find(proposal_id_type(1)) == prop_idx.end()); + } + { + set_expiration(db, trx); custom_account_authority_create_operation authorize_xfer_op; authorize_xfer_op.permission_id = custom_permission_id_type(1); authorize_xfer_op.valid_from = db.head_block_time(); @@ -1084,7 +1115,7 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) generate_block(); proposal_update_operation approve_prop; - approve_prop.proposal = proposal_id_type(1); + approve_prop.proposal = proposal_id_type(2); approve_prop.fee_paying_account = alice_id; approve_prop.active_approvals_to_add = {alice_id}; trx.operations = {approve_prop}; @@ -1092,6 +1123,7 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) PUSH_TX(db, trx); trx.clear(); generate_block(); + BOOST_REQUIRE(prop_idx.find(proposal_id_type(2)) == prop_idx.end()); trx.operations = {alice_to_bob_xfer_op}; sign(trx, bob_private_key); @@ -1183,4 +1215,177 @@ BOOST_AUTO_TEST_CASE(account_authority_delete_after_expiry_test) } FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(account_owner_authority_fail_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + time_point_sec valid_from = db.head_block_time(); + time_point_sec valid_to = db.head_block_time() + fc::seconds(30 * db.block_interval()); + BOOST_REQUIRE(pidx.size() == 2); + generate_block(); + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 2); + generate_block(); + } + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + trx.operations.push_back(alice_to_bob_xfer_op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + { + account_update_operation op; + op.account = alice_id; + op.owner = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + { + account_update_operation op; + op.account = alice_id; + op.active = authority(1, bob_id, 1); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(multisig_combined_op_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + GET_ACTOR(dave); + GET_ACTOR(erin); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + time_point_sec valid_from = db.head_block_time(); + time_point_sec valid_to = db.head_block_time() + fc::seconds(30 * db.block_interval()); + BOOST_REQUIRE(pidx.size() == 2); + generate_block(); + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + { + account_update_operation op; + op.account = alice_id; + op.active = authority(2, bob_id, 1, charlie_id, 1, dave_id, 1); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + // alice account update + account_update_operation auop; + auop.account = alice_id; + auop.active = authority(1, erin_id, 1); + trx.operations = {alice_to_bob_xfer_op, auop}; + sign(trx, bob_private_key); + sign(trx, charlie_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, erin_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + { + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() From c43ff2e53d4b4ffdfb3121a86a5fe264fc89439a Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Wed, 3 Jun 2020 08:58:18 +0000 Subject: [PATCH 10/13] rbac9 - wallet and db api changes --- libraries/app/database_api.cpp | 120 ++++++++++ .../app/include/graphene/app/database_api.hpp | 23 ++ .../custom_account_authority_evaluator.cpp | 4 - .../wallet/include/graphene/wallet/wallet.hpp | 44 ++++ libraries/wallet/wallet.cpp | 210 ++++++++++++++++++ 5 files changed, 397 insertions(+), 4 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 2aee274d..ecb407be 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -184,6 +184,14 @@ class database_api_impl : public std::enable_shared_from_this // gpos gpos_info get_gpos_info(const account_id_type account) const; + // rbac + vector get_custom_permissions(const account_id_type account) const; + fc::optional get_custom_permission_by_name(const account_id_type account, const string& permission_name) const; + vector get_custom_account_authorities(const account_id_type account) const; + vector get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const; + vector get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const; + vector get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const; + //private: const account_object* get_account_from_string( const std::string& name_or_id, bool throw_if_not_found = true ) const; @@ -2305,6 +2313,118 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type return result; } +////////////////////////////////////////////////////////////////////// +// // +// RBAC methods // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_custom_permissions(const account_id_type account) const +{ + return my->get_custom_permissions(account); +} + +vector database_api_impl::get_custom_permissions(const account_id_type account) const +{ + const auto& pindex = _db.get_index_type().indices().get(); + auto prange = pindex.equal_range(boost::make_tuple(account)); + vector 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 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 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().indices().get(); + 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 {}; +} + +vector database_api::get_custom_account_authorities(const account_id_type account) const +{ + return my->get_custom_account_authorities(account); +} + +vector database_api_impl::get_custom_account_authorities(const account_id_type account) const +{ + const auto& pindex = _db.get_index_type().indices().get(); + const auto& cindex = _db.get_index_type().indices().get(); + vector 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 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 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().indices().get(); + vector 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 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 database_api_impl::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const +{ + vector custom_account_auths; + fc::optional pobj = get_custom_permission_by_name(account, permission_name); + if(!pobj) + { + return custom_account_auths; + } + const auto& cindex = _db.get_index_type().indices().get(); + 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 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 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); +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index dc8aba52..2c3d9974 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -48,6 +48,9 @@ #include #include +#include +#include + #include #include @@ -709,6 +712,18 @@ class database_api */ gpos_info get_gpos_info(const account_id_type account) const; + ////////// + // RBAC // + ////////// + /** + * @return account and custom permissions/account-authorities info + */ + vector get_custom_permissions(const account_id_type account) const; + fc::optional get_custom_permission_by_name(const account_id_type account, const string& permission_name) const; + vector get_custom_account_authorities(const account_id_type account) const; + vector get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const; + vector get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const; + vector get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const; private: @@ -848,4 +863,12 @@ 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) ) diff --git a/libraries/chain/custom_account_authority_evaluator.cpp b/libraries/chain/custom_account_authority_evaluator.cpp index 651698c8..cce68775 100644 --- a/libraries/chain/custom_account_authority_evaluator.cpp +++ b/libraries/chain/custom_account_authority_evaluator.cpp @@ -84,15 +84,11 @@ void_result update_custom_account_authority_evaluator::do_evaluate(const custom_ auto valid_to = aobj.valid_to; if (op.new_valid_from) { - FC_ASSERT(*op.new_valid_from != aobj.valid_from, - "New valid_from provided is not different from old valid_from"); valid_from = *op.new_valid_from; } if (op.new_valid_to) { - FC_ASSERT(*op.new_valid_to != aobj.valid_to, - "New valid_to provided is not different from old valid_to"); FC_ASSERT(*op.new_valid_to > now, "New valid_to expiry should be in the future"); valid_to = *op.new_valid_to; } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7f4c7859..517749d6 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1895,6 +1895,38 @@ 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 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 new_valid_from, + fc::optional 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 get_custom_permissions(string owner) const; + fc::optional get_custom_permission_by_name(string owner, string permission_name) const; + vector get_custom_account_authorities(string owner) const; + vector get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const; + vector get_custom_account_authorities_by_permission_name(string owner, string permission_name) const; + vector get_active_custom_account_authorities_by_operation(string owner, int operation_type) const; + void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); void dbg_push_blocks( std::string src_filename, uint32_t count ); @@ -2157,4 +2189,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) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0bbf305a..65148b16 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -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 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 new_valid_from, + fc::optional 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 get_custom_permissions(string owner) const + { + return _remote_db->get_custom_permissions(get_account(owner).id); + } + + fc::optional 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 get_custom_account_authorities(string owner) const + { + return _remote_db->get_custom_account_authorities(get_account(owner).id); + } + + vector 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 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 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 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 new_valid_from, + fc::optional 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 wallet_api::get_custom_permissions(string owner) const +{ + return my->get_custom_permissions(owner); +} + +fc::optional wallet_api::get_custom_permission_by_name(string owner, string permission_name) const +{ + return my->get_custom_permission_by_name(owner, permission_name); +} + +vector wallet_api::get_custom_account_authorities(string owner) const +{ + return my->get_custom_account_authorities(owner); +} + +vector 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 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 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 { From f3cf062a42dc1ac7fe293c4d94dcbec270e999a3 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Thu, 4 Jun 2020 12:19:18 +0000 Subject: [PATCH 11/13] rbac10 - db api changes for required signature fetch --- libraries/app/database_api.cpp | 9 ++++++ .../graphene/chain/protocol/transaction.hpp | 1 + libraries/chain/protocol/transaction.cpp | 28 +++++++++++++++++-- tests/tests/authority_tests.cpp | 12 ++++++-- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index ecb407be..4b1bc232 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1865,6 +1865,9 @@ set 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; @@ -1900,6 +1903,9 @@ set database_api_impl::get_potential_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 ); @@ -1927,6 +1933,9 @@ set
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; diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 7408a29c..ec8f3f53 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -141,6 +141,7 @@ namespace graphene { namespace chain { const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index ff375bc5..62419948 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -348,17 +348,41 @@ set signed_transaction::get_required_signatures( const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion_depth )const { flat_set required_active; flat_set required_owner; vector other; - get_required_authorities( required_active, required_owner, other ); const flat_set& 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 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 ) @@ -386,7 +410,7 @@ set signed_transaction::minimize_required_signatures( 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 ) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 92c62ba6..a6169489 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -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 + { + return db.get_account_custom_authorities(id, op); + } ; + auto chk = [&]( const signed_transaction& tx, flat_set available_keys, @@ -1196,7 +1204,7 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + set 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; } ; @@ -1318,7 +1326,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + set 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; } ; From 3f9965b213074e5fa5fc5706c11073fcb31a8c3d Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Thu, 4 Jun 2020 16:12:02 +0000 Subject: [PATCH 12/13] rbac11 - add db_api tests --- tests/tests/custom_permission_tests.cpp | 260 +++++++++++++++++++++++- 1 file changed, 258 insertions(+), 2 deletions(-) diff --git a/tests/tests/custom_permission_tests.cpp b/tests/tests/custom_permission_tests.cpp index 9880f51a..242585b4 100644 --- a/tests/tests/custom_permission_tests.cpp +++ b/tests/tests/custom_permission_tests.cpp @@ -1064,7 +1064,6 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) trx.clear(); generate_block(); - proposal_create_operation prop; prop.fee_paying_account = alice_id; prop.proposed_ops = {op_wrapper(alice_to_bob_xfer_op), op_wrapper(bob_to_alice_xfer_op)}; @@ -1154,7 +1153,6 @@ BOOST_AUTO_TEST_CASE(proposal_op_test) PUSH_TX(db, trx); trx.clear(); generate_block(); - } } FC_LOG_AND_RETHROW() @@ -1388,4 +1386,262 @@ BOOST_AUTO_TEST_CASE(multisig_combined_op_test) } FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(db_api_test) +{ + try + { + INVOKE(permission_create_success_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + GET_ACTOR(dave); + GET_ACTOR(erin); + auto alice_public_key = alice_private_key.get_public_key(); + auto bob_public_key = bob_private_key.get_public_key(); + auto charlie_public_key = charlie_private_key.get_public_key(); + auto dave_public_key = dave_private_key.get_public_key(); + auto erin_public_key = erin_private_key.get_public_key(); + const auto &pidx = db.get_index_type().indices().get(); + const auto &cidx = db.get_index_type().indices().get(); + time_point_sec valid_from = db.head_block_time(); + time_point_sec valid_to = db.head_block_time() + fc::seconds(30 * db.block_interval()); + BOOST_REQUIRE(pidx.size() == 2); + generate_block(); + // alice->bob xfer op + transfer_operation alice_to_bob_xfer_op; + alice_to_bob_xfer_op.amount.asset_id = asset_id_type(0); + alice_to_bob_xfer_op.amount.amount = 100 * GRAPHENE_BLOCKCHAIN_PRECISION; + alice_to_bob_xfer_op.from = alice_id; + alice_to_bob_xfer_op.to = bob_id; + alice_to_bob_xfer_op.fee.asset_id = asset_id_type(0); + // alice account update + account_update_operation auop1; + auop1.account = alice_id; + auop1.active = authority(2, bob_id, 1, charlie_id, 1, dave_id, 1); + // alice account update + account_update_operation auop2; + auop2.account = alice_id; + auop2.active = authority(1, erin_id, 1); + // alice owner update + account_update_operation auop3; + auop3.account = alice_id; + auop3.owner = authority(1, bob_id, 1); + // get_required_signatures Auth Lambdas + set result; + auto get_active_rs = [&](account_id_type aid) -> const authority * { + return &(aid(db).active); + }; + + auto get_owner_rs = [&](account_id_type aid) -> const authority * { + return &(aid(db).owner); + }; + + auto get_custom = [&](account_id_type id, const operation &op) -> vector { + return db.get_account_custom_authorities(id, op); + }; + + // get_potential_signatures Auth lambdas + auto get_active_ps = [&](account_id_type id) -> const authority * { + const auto &auth = id(db).active; + for (const auto &k : auth.get_keys()) + result.insert(k); + return &auth; + }; + + auto get_owner_ps = [&](account_id_type id) -> const authority * { + const auto &auth = id(db).owner; + for (const auto &k : auth.get_keys()) + result.insert(k); + return &auth; + }; + // Transfer before custom account auth creation + { + result.clear(); + trx.operations = {alice_to_bob_xfer_op}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{alice_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{alice_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Alice creates a new account auth linking with permission abc + { + custom_account_authority_create_operation op; + op.permission_id = custom_permission_id_type(0); + op.valid_from = valid_from; + op.valid_to = valid_to; + op.operation_type = operation::tag::value; + op.owner_account = alice_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + BOOST_REQUIRE(cidx.size() == 1); + generate_block(); + } + // Transfer after custom account auth creation + { + result.clear(); + trx.operations = {alice_to_bob_xfer_op}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{alice_public_key, bob_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{bob_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Alice account update after custom account auth creation + { + result.clear(); + trx.operations = {auop1}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{alice_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{alice_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Alice account update and transfer after custom account auth creation + { + result.clear(); + trx.operations = {alice_to_bob_xfer_op, auop2}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{bob_public_key, charlie_public_key, dave_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{bob_public_key, charlie_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, bob_private_key); + sign(trx, charlie_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Transfer after alice account update again + { + result.clear(); + trx.operations = {alice_to_bob_xfer_op}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{erin_public_key, bob_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{bob_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, erin_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + // Alice owner auth update + { + result.clear(); + trx.operations = {auop3}; + trx.get_required_signatures( + db.get_chain_id(), + flat_set(), + get_active_ps, + get_owner_ps, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + set exp_result_ps{alice_public_key, erin_public_key}; + BOOST_REQUIRE(result == exp_result_ps); + set exp_result_rs{alice_public_key, erin_public_key}; + set result_rs = trx.get_required_signatures( + db.get_chain_id(), + flat_set(exp_result_ps.begin(), exp_result_ps.end()), + get_active_rs, + get_owner_rs, + get_custom, + db.get_global_properties().parameters.max_authority_depth); + BOOST_REQUIRE(result_rs == exp_result_rs); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + generate_block(); + } + // Transfer with custom account auth + { + trx.operations = {alice_to_bob_xfer_op}; + sign(trx, bob_private_key); + PUSH_TX(db, trx); + trx.clear(); + generate_block(); + } + } + FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() From e9f31f03d5f16d94a53e6b15c9863b8e58be34e7 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Tue, 23 Jun 2020 16:14:00 +0000 Subject: [PATCH 13/13] rbac12 - add missing code for key auths --- libraries/app/database_api.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 4b1bc232..10ec067e 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1904,7 +1904,15 @@ set database_api_impl::get_potential_signatures( const signed_t return &auth; }, [&]( account_id_type id, const operation& op ) { - return _db.get_account_custom_authorities(id, op); + vector 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 );