Merge branch 'develop' of gitlab.com:PBSA/peerplays into develop
This commit is contained in:
commit
847a87472d
31 changed files with 1138 additions and 47 deletions
|
|
@ -217,6 +217,9 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
||||
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
|
||||
|
||||
// Account Role
|
||||
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
||||
|
||||
//private:
|
||||
const account_object* get_account_from_string( const std::string& name_or_id,
|
||||
bool throw_if_not_found = true ) const;
|
||||
|
|
@ -2888,6 +2891,23 @@ vector<offer_history_object> database_api_impl::get_offer_history_by_bidder(cons
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<account_role_object> database_api::get_account_roles_by_owner(account_id_type owner) const
|
||||
{
|
||||
return my->get_account_roles_by_owner(owner);
|
||||
}
|
||||
|
||||
vector<account_role_object> database_api_impl::get_account_roles_by_owner(account_id_type owner) const
|
||||
{
|
||||
const auto &idx_aro = _db.get_index_type<account_role_index>().indices().get<by_owner>();
|
||||
auto idx_aro_range = idx_aro.equal_range(owner);
|
||||
vector<account_role_object> result;
|
||||
for (auto itr = idx_aro_range.first; itr != idx_aro_range.second; ++itr)
|
||||
{
|
||||
result.push_back(*itr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Private methods //
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
|
||||
|
|
@ -829,6 +830,11 @@ class database_api
|
|||
vector<offer_history_object> get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
|
||||
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
||||
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
|
||||
|
||||
//////////////////
|
||||
// ACCOUNT ROLE //
|
||||
//////////////////
|
||||
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
||||
private:
|
||||
std::shared_ptr< database_api_impl > my;
|
||||
};
|
||||
|
|
@ -999,4 +1005,7 @@ FC_API(graphene::app::database_api,
|
|||
(get_offer_history_by_issuer)
|
||||
(get_offer_history_by_item)
|
||||
(get_offer_history_by_bidder)
|
||||
|
||||
// Account Roles
|
||||
(get_account_roles_by_owner)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ add_library( graphene_chain
|
|||
offer_evaluator.cpp
|
||||
nft_evaluator.cpp
|
||||
protocol/nft.cpp
|
||||
protocol/account_role.cpp
|
||||
account_role_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
|
|||
162
libraries/chain/account_role_evaluator.cpp
Normal file
162
libraries/chain/account_role_evaluator.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include <graphene/chain/account_role_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/rbac_hardfork_visitor.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
void_result account_role_create_evaluator::do_evaluate(const account_role_create_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const database &d = db();
|
||||
auto now = d.head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||
op.owner(d);
|
||||
|
||||
rbac_operation_hardfork_visitor arvtor(now);
|
||||
for (const auto &op_type : op.allowed_operations)
|
||||
{
|
||||
arvtor(op_type);
|
||||
}
|
||||
|
||||
for (const auto &acc : op.whitelisted_accounts)
|
||||
{
|
||||
acc(d);
|
||||
}
|
||||
|
||||
FC_ASSERT(op.valid_to > now, "valid_to expiry should be in future");
|
||||
FC_ASSERT((op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry");
|
||||
|
||||
const auto &ar_idx = d.get_index_type<account_role_index>().indices().get<by_owner>();
|
||||
auto aro_range = ar_idx.equal_range(op.owner);
|
||||
FC_ASSERT(std::distance(aro_range.first, aro_range.second) < d.get_global_properties().parameters.account_roles_max_per_account(), "Max account roles that can be created by one owner is reached");
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
object_id_type account_role_create_evaluator::do_apply(const account_role_create_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
database &d = db();
|
||||
return d.create<account_role_object>([&op](account_role_object &obj) mutable {
|
||||
obj.owner = op.owner;
|
||||
obj.name = op.name;
|
||||
obj.metadata = op.metadata;
|
||||
obj.allowed_operations = op.allowed_operations;
|
||||
obj.whitelisted_accounts = op.whitelisted_accounts;
|
||||
obj.valid_to = op.valid_to;
|
||||
})
|
||||
.id;
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
void_result account_role_update_evaluator::do_evaluate(const account_role_update_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const database &d = db();
|
||||
auto now = d.head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||
op.owner(d);
|
||||
const account_role_object &aobj = op.account_role_id(d);
|
||||
FC_ASSERT(aobj.owner == op.owner, "Only owner account can update account role object");
|
||||
|
||||
for (const auto &op_type : op.allowed_operations_to_remove)
|
||||
{
|
||||
FC_ASSERT(aobj.allowed_operations.find(op_type) != aobj.allowed_operations.end(),
|
||||
"Cannot remove non existent operation");
|
||||
}
|
||||
|
||||
for (const auto &acc : op.accounts_to_remove)
|
||||
{
|
||||
FC_ASSERT(aobj.whitelisted_accounts.find(acc) != aobj.whitelisted_accounts.end(),
|
||||
"Cannot remove non existent account");
|
||||
}
|
||||
|
||||
rbac_operation_hardfork_visitor arvtor(now);
|
||||
for (const auto &op_type : op.allowed_operations_to_add)
|
||||
{
|
||||
arvtor(op_type);
|
||||
}
|
||||
FC_ASSERT((aobj.allowed_operations.size() + op.allowed_operations_to_add.size() - op.allowed_operations_to_remove.size()) > 0, "Allowed operations should be positive");
|
||||
|
||||
for (const auto &acc : op.accounts_to_add)
|
||||
{
|
||||
acc(d);
|
||||
}
|
||||
FC_ASSERT((aobj.whitelisted_accounts.size() + op.accounts_to_add.size() - op.accounts_to_remove.size()) > 0, "Accounts should be positive");
|
||||
|
||||
if (op.valid_to)
|
||||
{
|
||||
FC_ASSERT(*op.valid_to > now, "valid_to expiry should be in future");
|
||||
FC_ASSERT((*op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
void_result account_role_update_evaluator::do_apply(const account_role_update_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
database &d = db();
|
||||
const account_role_object &aobj = op.account_role_id(d);
|
||||
d.modify(aobj, [&op](account_role_object &obj) {
|
||||
if (op.name)
|
||||
obj.name = *op.name;
|
||||
if (op.metadata)
|
||||
obj.metadata = *op.metadata;
|
||||
obj.allowed_operations.insert(op.allowed_operations_to_add.begin(), op.allowed_operations_to_add.end());
|
||||
obj.whitelisted_accounts.insert(op.accounts_to_add.begin(), op.accounts_to_add.end());
|
||||
for (const auto &op_type : op.allowed_operations_to_remove)
|
||||
obj.allowed_operations.erase(op_type);
|
||||
for (const auto &acc : op.accounts_to_remove)
|
||||
obj.whitelisted_accounts.erase(acc);
|
||||
if (op.valid_to)
|
||||
obj.valid_to = *op.valid_to;
|
||||
});
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
void_result account_role_delete_evaluator::do_evaluate(const account_role_delete_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const database &d = db();
|
||||
auto now = d.head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
|
||||
op.owner(d);
|
||||
const account_role_object &aobj = op.account_role_id(d);
|
||||
FC_ASSERT(aobj.owner == op.owner, "Only owner account can delete account role object");
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
void_result account_role_delete_evaluator::do_apply(const account_role_delete_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
database &d = db();
|
||||
const account_role_object &aobj = op.account_role_id(d);
|
||||
d.remove(aobj);
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
|
@ -4,37 +4,13 @@
|
|||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/custom_permission_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/rbac_hardfork_visitor.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
struct rbac_operation_hardfork_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
const fc::time_point_sec block_time;
|
||||
|
||||
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
|
||||
void operator()(int op_type) const
|
||||
{
|
||||
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
|
||||
switch (op_type)
|
||||
{
|
||||
case operation::tag<custom_permission_create_operation>::value:
|
||||
case operation::tag<custom_permission_update_operation>::value:
|
||||
case operation::tag<custom_permission_delete_operation>::value:
|
||||
case operation::tag<custom_account_authority_create_operation>::value:
|
||||
case operation::tag<custom_account_authority_update_operation>::value:
|
||||
case operation::tag<custom_account_authority_delete_operation>::value:
|
||||
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permission not allowed on this operation yet!");
|
||||
break;
|
||||
default:
|
||||
FC_ASSERT(op_type < first_allowed_op, "Custom permission not allowed on this operation!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void_result create_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_create_operation &op)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <graphene/chain/custom_permission_object.hpp>
|
||||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
|
|
@ -192,4 +193,11 @@ bool database::item_locked(const nft_id_type &item) const
|
|||
auto items_itr = market_items._locked_items.find(item);
|
||||
return (items_itr != market_items._locked_items.end());
|
||||
}
|
||||
|
||||
bool database::account_role_valid(const account_role_object &aro, account_id_type account, optional<int> op_type) const
|
||||
{
|
||||
return (aro.valid_to > head_block_time()) &&
|
||||
(aro.whitelisted_accounts.find(account) != aro.whitelisted_accounts.end()) &&
|
||||
(!op_type || (aro.allowed_operations.find(*op_type) != aro.allowed_operations.end()));
|
||||
}
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include <graphene/chain/custom_permission_object.hpp>
|
||||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
|
||||
|
|
@ -85,6 +86,7 @@
|
|||
#include <graphene/chain/custom_account_authority_evaluator.hpp>
|
||||
#include <graphene/chain/offer_evaluator.hpp>
|
||||
#include <graphene/chain/nft_evaluator.hpp>
|
||||
#include <graphene/chain/account_role_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -186,6 +188,9 @@ const uint8_t offer_object::type_id;
|
|||
const uint8_t offer_history_object::space_id;
|
||||
const uint8_t offer_history_object::type_id;
|
||||
|
||||
const uint8_t account_role_object::space_id;
|
||||
const uint8_t account_role_object::type_id;
|
||||
|
||||
void database::initialize_evaluators()
|
||||
{
|
||||
_operation_evaluators.resize(255);
|
||||
|
|
@ -275,6 +280,9 @@ void database::initialize_evaluators()
|
|||
register_evaluator<nft_safe_transfer_from_evaluator>();
|
||||
register_evaluator<nft_approve_evaluator>();
|
||||
register_evaluator<nft_set_approval_for_all_evaluator>();
|
||||
register_evaluator<account_role_create_evaluator>();
|
||||
register_evaluator<account_role_update_evaluator>();
|
||||
register_evaluator<account_role_delete_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -323,6 +331,7 @@ void database::initialize_indexes()
|
|||
|
||||
add_index< primary_index<nft_metadata_index > >();
|
||||
add_index< primary_index<nft_index > >();
|
||||
add_index< primary_index<account_role_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
|
|
|
|||
|
|
@ -941,6 +941,15 @@ void clear_expired_custom_account_authorities(database& db)
|
|||
}
|
||||
}
|
||||
|
||||
void clear_expired_account_roles(database& db)
|
||||
{
|
||||
const auto& arindex = db.get_index_type<account_role_index>().indices().get<by_expiration>();
|
||||
while(!arindex.empty() && arindex.begin()->valid_to < db.head_block_time())
|
||||
{
|
||||
db.remove(*arindex.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
|
||||
|
|
@ -1668,6 +1677,16 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod;
|
||||
if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() )
|
||||
p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period;
|
||||
if( !p.pending_parameters->extensions.value.rbac_max_permissions_per_account.valid() )
|
||||
p.pending_parameters->extensions.value.rbac_max_permissions_per_account = p.parameters.extensions.value.rbac_max_permissions_per_account;
|
||||
if( !p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime.valid() )
|
||||
p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime = p.parameters.extensions.value.rbac_max_account_authority_lifetime;
|
||||
if( !p.pending_parameters->extensions.value.rbac_max_authorities_per_permission.valid() )
|
||||
p.pending_parameters->extensions.value.rbac_max_authorities_per_permission = p.parameters.extensions.value.rbac_max_authorities_per_permission;
|
||||
if( !p.pending_parameters->extensions.value.account_roles_max_per_account.valid() )
|
||||
p.pending_parameters->extensions.value.account_roles_max_per_account = p.parameters.extensions.value.account_roles_max_per_account;
|
||||
if( !p.pending_parameters->extensions.value.account_roles_max_lifetime.valid() )
|
||||
p.pending_parameters->extensions.value.account_roles_max_lifetime = p.parameters.extensions.value.account_roles_max_lifetime;
|
||||
p.parameters = std::move(*p.pending_parameters);
|
||||
p.pending_parameters.reset();
|
||||
}
|
||||
|
|
@ -1717,8 +1736,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
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.
|
||||
// these custom account auths and account roles are not usable.
|
||||
clear_expired_custom_account_authorities(*this);
|
||||
clear_expired_account_roles(*this);
|
||||
// process_budget needs to run at the bottom because
|
||||
// it needs to know the next_maintenance_time
|
||||
process_budget();
|
||||
|
|
|
|||
|
|
@ -350,6 +350,15 @@ struct get_impacted_account_visitor
|
|||
void operator()( const finalize_offer_operation& op ) {
|
||||
_impacted.insert( op.fee_paying_account );
|
||||
}
|
||||
void operator()( const account_role_create_operation& op ){
|
||||
_impacted.insert( op.owner );
|
||||
}
|
||||
void operator()( const account_role_update_operation& op ){
|
||||
_impacted.insert( op.owner );
|
||||
}
|
||||
void operator()( const account_role_delete_operation& op ){
|
||||
_impacted.insert( op.owner );
|
||||
}
|
||||
};
|
||||
|
||||
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
|
||||
|
|
@ -437,6 +446,12 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
} case account_role_type:{
|
||||
const auto& aobj = dynamic_cast<const account_role_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
accounts.insert( aobj->whitelisted_accounts.begin(), aobj->whitelisted_accounts.end() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class account_role_create_evaluator : public evaluator<account_role_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef account_role_create_operation operation_type;
|
||||
void_result do_evaluate( const account_role_create_operation& o );
|
||||
object_id_type do_apply( const account_role_create_operation& o );
|
||||
};
|
||||
|
||||
class account_role_update_evaluator : public evaluator<account_role_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef account_role_update_operation operation_type;
|
||||
void_result do_evaluate( const account_role_update_operation& o );
|
||||
void_result do_apply( const account_role_update_operation& o );
|
||||
};
|
||||
|
||||
class account_role_delete_evaluator : public evaluator<account_role_delete_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef account_role_delete_operation operation_type;
|
||||
void_result do_evaluate( const account_role_delete_operation& o );
|
||||
void_result do_apply( const account_role_delete_operation& o );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
using namespace graphene::db;
|
||||
|
||||
class account_role_object : public abstract_object<account_role_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = account_role_type;
|
||||
|
||||
account_id_type owner;
|
||||
std::string name;
|
||||
std::string metadata;
|
||||
flat_set<int> allowed_operations;
|
||||
flat_set<account_id_type> whitelisted_accounts;
|
||||
time_point_sec valid_to;
|
||||
};
|
||||
|
||||
struct by_owner;
|
||||
struct by_expiration;
|
||||
using account_role_multi_index_type = multi_index_container<
|
||||
account_role_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_owner>,
|
||||
member<account_role_object, account_id_type, &account_role_object::owner>
|
||||
>,
|
||||
ordered_unique< tag<by_expiration>,
|
||||
composite_key<account_role_object,
|
||||
member<account_role_object, time_point_sec, &account_role_object::valid_to>,
|
||||
member<object, object_id_type, &object::id>>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using account_role_index = generic_index<account_role_object, account_role_multi_index_type>;
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::chain::account_role_object, (graphene::db::object),
|
||||
(owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to))
|
||||
|
|
@ -243,4 +243,7 @@
|
|||
|
||||
#define NFT_TOKEN_MIN_LENGTH 3
|
||||
#define NFT_TOKEN_MAX_LENGTH 15
|
||||
#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH
|
||||
#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH
|
||||
|
||||
#define ACCOUNT_ROLES_MAX_PER_ACCOUNT 20 // Max 20 roles can be created by a resource owner
|
||||
#define ACCOUNT_ROLES_MAX_LIFETIME 365*24*60*60 // 1 Year
|
||||
|
|
@ -283,6 +283,7 @@ namespace graphene { namespace chain {
|
|||
uint64_t get_random_bits( uint64_t bound );
|
||||
const witness_schedule_object& get_witness_schedule_object()const;
|
||||
bool item_locked(const nft_id_type& item)const;
|
||||
bool account_role_valid(const account_role_object& aro, account_id_type account, optional<int> op_type = optional<int>()) const;
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
uint32_t head_block_num()const;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace graphene { namespace chain {
|
|||
optional<uint16_t> revenue_split;
|
||||
bool is_transferable = false;
|
||||
bool is_sellable = true;
|
||||
optional<account_role_id_type> account_role;
|
||||
};
|
||||
|
||||
class nft_object : public abstract_object<nft_object>
|
||||
|
|
@ -95,7 +96,8 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object)
|
|||
(revenue_partner)
|
||||
(revenue_split)
|
||||
(is_transferable)
|
||||
(is_sellable) )
|
||||
(is_sellable)
|
||||
(account_role) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
|
||||
(nft_metadata_id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
struct account_role_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
account_id_type owner;
|
||||
std::string name;
|
||||
std::string metadata;
|
||||
flat_set<int> allowed_operations;
|
||||
flat_set<account_id_type> whitelisted_accounts;
|
||||
time_point_sec valid_to;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner; }
|
||||
void validate() const;
|
||||
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||
};
|
||||
|
||||
struct account_role_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
account_id_type owner;
|
||||
account_role_id_type account_role_id;
|
||||
optional<std::string> name;
|
||||
optional<std::string> metadata;
|
||||
flat_set<int> allowed_operations_to_add;
|
||||
flat_set<int> allowed_operations_to_remove;
|
||||
flat_set<account_id_type> accounts_to_add;
|
||||
flat_set<account_id_type> accounts_to_remove;
|
||||
optional<time_point_sec> valid_to;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner; }
|
||||
void validate() const;
|
||||
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||
};
|
||||
|
||||
struct account_role_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type owner;
|
||||
account_role_id_type account_role_id;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner; }
|
||||
void validate() const;
|
||||
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; }
|
||||
};
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
||||
FC_REFLECT(graphene::chain::account_role_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
|
||||
FC_REFLECT(graphene::chain::account_role_update_operation::fee_parameters_type, (fee)(price_per_kbyte))
|
||||
FC_REFLECT(graphene::chain::account_role_delete_operation::fee_parameters_type, (fee))
|
||||
|
||||
FC_REFLECT(graphene::chain::account_role_create_operation, (fee)(owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to)(extensions))
|
||||
FC_REFLECT(graphene::chain::account_role_update_operation, (fee)(owner)(account_role_id)(name)(metadata)(allowed_operations_to_add)(allowed_operations_to_remove)(accounts_to_add)(accounts_to_remove)(valid_to)(extensions))
|
||||
FC_REFLECT(graphene::chain::account_role_delete_operation, (fee)(owner)(account_role_id)(owner)(extensions))
|
||||
|
|
@ -52,6 +52,9 @@ namespace graphene { namespace chain {
|
|||
optional < uint16_t > rbac_max_permissions_per_account = RBAC_MAX_PERMISSIONS_PER_ACCOUNT;
|
||||
optional < uint32_t > rbac_max_account_authority_lifetime = RBAC_MAX_ACCOUNT_AUTHORITY_LIFETIME;
|
||||
optional < uint16_t > rbac_max_authorities_per_permission = RBAC_MAX_AUTHS_PER_PERMISSION;
|
||||
/* Account Roles - Permissions Parameters */
|
||||
optional < uint16_t > account_roles_max_per_account = ACCOUNT_ROLES_MAX_PER_ACCOUNT;
|
||||
optional < uint32_t > account_roles_max_lifetime = ACCOUNT_ROLES_MAX_LIFETIME;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -152,6 +155,12 @@ namespace graphene { namespace chain {
|
|||
inline uint16_t rbac_max_authorities_per_permission()const {
|
||||
return extensions.value.rbac_max_authorities_per_permission.valid() ? *extensions.value.rbac_max_authorities_per_permission : RBAC_MAX_AUTHS_PER_PERMISSION;
|
||||
}
|
||||
inline uint16_t account_roles_max_per_account()const {
|
||||
return extensions.value.account_roles_max_per_account.valid() ? *extensions.value.account_roles_max_per_account : ACCOUNT_ROLES_MAX_PER_ACCOUNT;
|
||||
}
|
||||
inline uint32_t account_roles_max_lifetime()const {
|
||||
return extensions.value.account_roles_max_lifetime.valid() ? *extensions.value.account_roles_max_lifetime : ACCOUNT_ROLES_MAX_LIFETIME;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -172,6 +181,8 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(rbac_max_permissions_per_account)
|
||||
(rbac_max_account_authority_lifetime)
|
||||
(rbac_max_authorities_per_permission)
|
||||
(account_roles_max_per_account)
|
||||
(account_roles_max_lifetime)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ struct custom_account_authority_create_operation : public base_operation
|
|||
time_point_sec valid_from;
|
||||
time_point_sec valid_to;
|
||||
account_id_type owner_account;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -38,6 +39,7 @@ struct custom_account_authority_update_operation : public base_operation
|
|||
optional<time_point_sec> new_valid_from;
|
||||
optional<time_point_sec> new_valid_to;
|
||||
account_id_type owner_account;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -54,6 +56,7 @@ struct custom_account_authority_delete_operation : public base_operation
|
|||
asset fee;
|
||||
custom_account_authority_id_type auth_id;
|
||||
account_id_type owner_account;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -64,10 +67,10 @@ struct custom_account_authority_delete_operation : public base_operation
|
|||
} // namespace graphene
|
||||
|
||||
FC_REFLECT(graphene::chain::custom_account_authority_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
|
||||
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_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account)(extensions))
|
||||
|
||||
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_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account)(extensions))
|
||||
|
||||
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))
|
||||
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account)(extensions))
|
||||
|
|
@ -18,6 +18,7 @@ struct custom_permission_create_operation : public base_operation
|
|||
account_id_type owner_account;
|
||||
string permission_name;
|
||||
authority auth;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -35,6 +36,7 @@ struct custom_permission_update_operation : public base_operation
|
|||
custom_permission_id_type permission_id;
|
||||
optional<authority> new_auth;
|
||||
account_id_type owner_account;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -51,6 +53,7 @@ struct custom_permission_delete_operation : public base_operation
|
|||
asset fee;
|
||||
custom_permission_id_type permission_id;
|
||||
account_id_type owner_account;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer() const { return owner_account; }
|
||||
void validate() const;
|
||||
|
|
@ -61,10 +64,10 @@ struct custom_permission_delete_operation : public base_operation
|
|||
} // namespace graphene
|
||||
|
||||
FC_REFLECT(graphene::chain::custom_permission_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
|
||||
FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth))
|
||||
FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth)(extensions))
|
||||
|
||||
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_update_operation, (fee)(permission_id)(new_auth)(owner_account)(extensions))
|
||||
|
||||
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))
|
||||
FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account)(extensions))
|
||||
|
|
@ -21,6 +21,9 @@ namespace graphene { namespace chain {
|
|||
optional<uint16_t> revenue_split;
|
||||
bool is_transferable = false;
|
||||
bool is_sellable = true;
|
||||
// Accounts Role
|
||||
optional<account_role_id_type> account_role;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return owner; }
|
||||
void validate() const;
|
||||
|
|
@ -29,7 +32,11 @@ namespace graphene { namespace chain {
|
|||
|
||||
struct nft_metadata_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
account_id_type owner;
|
||||
|
|
@ -41,6 +48,9 @@ namespace graphene { namespace chain {
|
|||
optional<uint16_t> revenue_split;
|
||||
optional<bool> is_transferable;
|
||||
optional<bool> is_sellable;
|
||||
// Accounts Role
|
||||
optional<account_role_id_type> account_role;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return owner; }
|
||||
void validate() const;
|
||||
|
|
@ -63,6 +73,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type approved;
|
||||
vector<account_id_type> approved_operators;
|
||||
std::string token_uri;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
void validate() const;
|
||||
|
|
@ -84,6 +95,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type to;
|
||||
nft_id_type token_id;
|
||||
string data;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return operator_; }
|
||||
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||
|
|
@ -98,6 +110,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
account_id_type approved;
|
||||
nft_id_type token_id;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return operator_; }
|
||||
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||
|
|
@ -112,6 +125,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
account_id_type operator_;
|
||||
bool approved;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return owner; }
|
||||
share_type calculate_fee(const fee_parameters_type &k) const;
|
||||
|
|
@ -126,10 +140,10 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty
|
|||
FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) )
|
||||
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) )
|
||||
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) )
|
||||
FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) )
|
||||
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
|
||||
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
|
||||
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) )
|
||||
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) )
|
||||
FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) (extensions) )
|
||||
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) (extensions) )
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include <graphene/chain/protocol/custom_account_authority.hpp>
|
||||
#include <graphene/chain/protocol/offer.hpp>
|
||||
#include <graphene/chain/protocol/nft_ops.hpp>
|
||||
#include <graphene/chain/protocol/account_role.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -155,7 +156,10 @@ namespace graphene { namespace chain {
|
|||
nft_mint_operation,
|
||||
nft_safe_transfer_from_operation,
|
||||
nft_approve_operation,
|
||||
nft_set_approval_for_all_operation
|
||||
nft_set_approval_for_all_operation,
|
||||
account_role_create_operation,
|
||||
account_role_update_operation,
|
||||
account_role_delete_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ namespace graphene { namespace chain {
|
|||
offer_object_type,
|
||||
nft_metadata_type,
|
||||
nft_object_type,
|
||||
account_role_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -241,6 +242,7 @@ namespace graphene { namespace chain {
|
|||
class offer_object;
|
||||
class nft_metadata_object;
|
||||
class nft_object;
|
||||
class account_role_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;
|
||||
|
|
@ -272,6 +274,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, offer_object_type, offer_object> offer_id_type;
|
||||
typedef object_id< protocol_ids, nft_metadata_type, nft_metadata_object> nft_metadata_id_type;
|
||||
typedef object_id< protocol_ids, nft_object_type, nft_object> nft_id_type;
|
||||
typedef object_id< protocol_ids, account_role_type, account_role_object> account_role_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -459,6 +462,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(offer_object_type)
|
||||
(nft_metadata_type)
|
||||
(nft_object_type)
|
||||
(account_role_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -535,6 +539,7 @@ FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type )
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
struct rbac_operation_hardfork_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
const fc::time_point_sec block_time;
|
||||
|
||||
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
|
||||
void operator()(int op_type) const
|
||||
{
|
||||
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
|
||||
switch (op_type)
|
||||
{
|
||||
case operation::tag<custom_permission_create_operation>::value:
|
||||
case operation::tag<custom_permission_update_operation>::value:
|
||||
case operation::tag<custom_permission_delete_operation>::value:
|
||||
case operation::tag<custom_account_authority_create_operation>::value:
|
||||
case operation::tag<custom_account_authority_update_operation>::value:
|
||||
case operation::tag<custom_account_authority_delete_operation>::value:
|
||||
case operation::tag<offer_operation>::value:
|
||||
case operation::tag<bid_operation>::value:
|
||||
case operation::tag<cancel_offer_operation>::value:
|
||||
case operation::tag<finalize_offer_operation>::value:
|
||||
case operation::tag<nft_metadata_create_operation>::value:
|
||||
case operation::tag<nft_metadata_update_operation>::value:
|
||||
case operation::tag<nft_mint_operation>::value:
|
||||
case operation::tag<nft_safe_transfer_from_operation>::value:
|
||||
case operation::tag<nft_approve_operation>::value:
|
||||
case operation::tag<nft_set_approval_for_all_operation>::value:
|
||||
case operation::tag<account_role_create_operation>::value:
|
||||
case operation::tag<account_role_update_operation>::value:
|
||||
case operation::tag<account_role_delete_operation>::value:
|
||||
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!");
|
||||
break;
|
||||
default:
|
||||
FC_ASSERT(op_type >= operation::tag<transfer_operation>::value && op_type < first_allowed_op, "Custom permissions and roles not allowed on this operation!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#include <graphene/chain/nft_evaluator.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -18,6 +20,10 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat
|
|||
(*op.revenue_partner)(db());
|
||||
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
|
||||
}
|
||||
if(op.account_role) {
|
||||
const auto& ar_obj = (*op.account_role)(db());
|
||||
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -32,6 +38,7 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat
|
|||
obj.revenue_split = op.revenue_split;
|
||||
obj.is_transferable = op.is_transferable;
|
||||
obj.is_sellable = op.is_sellable;
|
||||
obj.account_role = op.account_role;
|
||||
});
|
||||
return new_nft_metadata_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -57,6 +64,10 @@ void_result nft_metadata_update_evaluator::do_evaluate( const nft_metadata_updat
|
|||
(*op.revenue_partner)(db());
|
||||
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
|
||||
}
|
||||
if(op.account_role) {
|
||||
const auto& ar_obj = (*op.account_role)(db());
|
||||
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -77,6 +88,8 @@ void_result nft_metadata_update_evaluator::do_apply( const nft_metadata_update_o
|
|||
obj.is_transferable = *op.is_transferable;
|
||||
if( op.is_sellable.valid() )
|
||||
obj.is_sellable = *op.is_sellable;
|
||||
if( op.account_role.valid() )
|
||||
obj.account_role = op.account_role;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -144,6 +157,17 @@ void_result nft_safe_transfer_from_evaluator::do_evaluate( const nft_safe_transf
|
|||
const auto& nft_meta_obj = itr_nft->nft_metadata_id(db());
|
||||
FC_ASSERT( nft_meta_obj.is_transferable == true, "NFT is not transferable");
|
||||
|
||||
if (nft_meta_obj.account_role)
|
||||
{
|
||||
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
|
||||
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
|
||||
if(ar_itr != ar_idx.end())
|
||||
{
|
||||
FC_ASSERT(db().account_role_valid(*ar_itr, op.operator_, get_type()), "Account role not valid");
|
||||
FC_ASSERT(db().account_role_valid(*ar_itr, op.to), "Account role not valid");
|
||||
}
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
|
@ -38,6 +40,15 @@ namespace graphene
|
|||
}
|
||||
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
|
||||
FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable");
|
||||
if (nft_meta_obj.account_role)
|
||||
{
|
||||
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
|
||||
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
|
||||
if(ar_itr != ar_idx.end())
|
||||
{
|
||||
FC_ASSERT(db().account_role_valid(*ar_itr, op.issuer, get_type()), "Account role not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
FC_ASSERT(op.offer_expiration_date > d.head_block_time(), "Expiration should be in future");
|
||||
FC_ASSERT(op.fee.amount >= 0, "Invalid fee");
|
||||
|
|
@ -100,6 +111,18 @@ namespace graphene
|
|||
FC_ASSERT(!is_approved, "Bidder cannot already be an approved account of the item");
|
||||
FC_ASSERT(!is_approved_operator, "Bidder cannot already be an approved operator of the item");
|
||||
}
|
||||
|
||||
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
|
||||
FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable");
|
||||
if (nft_meta_obj.account_role)
|
||||
{
|
||||
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
|
||||
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
|
||||
if(ar_itr != ar_idx.end())
|
||||
{
|
||||
FC_ASSERT(db().account_role_valid(*ar_itr, op.bidder, get_type()), "Account role not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FC_ASSERT(op.bid_price.asset_id == offer.minimum_price.asset_id, "Asset type mismatch");
|
||||
|
|
|
|||
|
|
@ -196,6 +196,18 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_set_approval_for_all_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const account_role_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const account_role_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const account_role_delete_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
|
|
|
|||
61
libraries/chain/protocol/account_role.cpp
Normal file
61
libraries/chain/protocol/account_role.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#include <graphene/chain/protocol/account_role.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
void account_role_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||
FC_ASSERT(allowed_operations.size() > 0, "Allowed operations should be positive");
|
||||
FC_ASSERT(whitelisted_accounts.size() > 0, "Whitelisted accounts should be positive");
|
||||
}
|
||||
|
||||
void account_role_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||
for (auto aop : allowed_operations_to_add)
|
||||
{
|
||||
FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid");
|
||||
FC_ASSERT(allowed_operations_to_remove.find(aop) == allowed_operations_to_remove.end(),
|
||||
"Cannot add and remove allowed operation at the same time.");
|
||||
}
|
||||
for (auto aop : allowed_operations_to_remove)
|
||||
{
|
||||
FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid");
|
||||
FC_ASSERT(allowed_operations_to_add.find(aop) == allowed_operations_to_add.end(),
|
||||
"Cannot add and remove allowed operation at the same time.");
|
||||
}
|
||||
|
||||
for (auto acc : accounts_to_add)
|
||||
{
|
||||
FC_ASSERT(accounts_to_remove.find(acc) == accounts_to_remove.end(),
|
||||
"Cannot add and remove accounts at the same time.");
|
||||
}
|
||||
|
||||
for (auto acc : accounts_to_remove)
|
||||
{
|
||||
FC_ASSERT(accounts_to_add.find(acc) == accounts_to_add.end(),
|
||||
"Cannot add and remove accounts at the same time.");
|
||||
}
|
||||
}
|
||||
|
||||
void account_role_delete_operation::validate() const
|
||||
{
|
||||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||
}
|
||||
|
||||
share_type account_role_create_operation::calculate_fee(const fee_parameters_type &k) const
|
||||
{
|
||||
return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte);
|
||||
}
|
||||
|
||||
share_type account_role_update_operation::calculate_fee(const fee_parameters_type &k) const
|
||||
{
|
||||
return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte);
|
||||
}
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
|
@ -45,7 +45,6 @@ void nft_metadata_create_operation::validate() const
|
|||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||
FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided");
|
||||
FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided");
|
||||
FC_ASSERT(base_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI");
|
||||
}
|
||||
|
||||
void nft_metadata_update_operation::validate() const
|
||||
|
|
@ -55,14 +54,11 @@ void nft_metadata_update_operation::validate() const
|
|||
FC_ASSERT(is_valid_nft_token_name(*name), "Invalid NFT name provided");
|
||||
if(symbol)
|
||||
FC_ASSERT(is_valid_nft_token_name(*symbol), "Invalid NFT symbol provided");
|
||||
if(base_uri)
|
||||
FC_ASSERT((*base_uri).length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI");
|
||||
}
|
||||
|
||||
void nft_mint_operation::validate() const
|
||||
{
|
||||
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
|
||||
FC_ASSERT(token_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Token URI");
|
||||
}
|
||||
|
||||
share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_type &k) const
|
||||
|
|
@ -72,7 +68,7 @@ share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_typ
|
|||
|
||||
share_type nft_metadata_update_operation::calculate_fee(const fee_parameters_type &k) const
|
||||
{
|
||||
return k.fee;
|
||||
return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
|
||||
}
|
||||
|
||||
share_type nft_mint_operation::calculate_fee(const fee_parameters_type &k) const
|
||||
|
|
|
|||
|
|
@ -1950,6 +1950,7 @@ class wallet_api
|
|||
optional<uint16_t> revenue_split,
|
||||
bool is_transferable,
|
||||
bool is_sellable,
|
||||
optional<account_role_id_type> role_id,
|
||||
bool broadcast);
|
||||
|
||||
/**
|
||||
|
|
@ -1975,6 +1976,7 @@ class wallet_api
|
|||
optional<uint16_t> revenue_split,
|
||||
optional<bool> is_transferable,
|
||||
optional<bool> is_sellable,
|
||||
optional<account_role_id_type> role_id,
|
||||
bool broadcast);
|
||||
|
||||
/**
|
||||
|
|
@ -2113,6 +2115,28 @@ class wallet_api
|
|||
vector<offer_history_object> get_offer_history_by_item(const nft_id_type item, uint32_t limit, optional<offer_history_id_type> lower_id) const;
|
||||
vector<offer_history_object> get_offer_history_by_bidder(string bidder_account_id_or_name, uint32_t limit, optional<offer_history_id_type> lower_id) const;
|
||||
|
||||
signed_transaction create_account_role(string owner_account_id_or_name,
|
||||
string name,
|
||||
string metadata,
|
||||
flat_set<int> allowed_operations,
|
||||
flat_set<account_id_type> whitelisted_accounts,
|
||||
time_point_sec valid_to,
|
||||
bool broadcast);
|
||||
signed_transaction update_account_role(string owner_account_id_or_name,
|
||||
account_role_id_type role_id,
|
||||
optional<string> name,
|
||||
optional<string> metadata,
|
||||
flat_set<int> operations_to_add,
|
||||
flat_set<int> operations_to_remove,
|
||||
flat_set<account_id_type> accounts_to_add,
|
||||
flat_set<account_id_type> accounts_to_remove,
|
||||
optional<time_point_sec> valid_to,
|
||||
bool broadcast);
|
||||
signed_transaction delete_account_role(string owner_account_id_or_name,
|
||||
account_role_id_type role_id,
|
||||
bool broadcast);
|
||||
vector<account_role_object> get_account_roles_by_owner(string owner_account_id_or_name) 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 );
|
||||
|
|
@ -2387,6 +2411,10 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_offer_history_by_issuer)
|
||||
(get_offer_history_by_item)
|
||||
(get_offer_history_by_bidder)
|
||||
(create_account_role)
|
||||
(update_account_role)
|
||||
(delete_account_role)
|
||||
(get_account_roles_by_owner)
|
||||
(get_upcoming_tournaments)
|
||||
(get_tournaments)
|
||||
(get_tournaments_by_state)
|
||||
|
|
|
|||
|
|
@ -6375,6 +6375,7 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
|||
optional<uint16_t> revenue_split,
|
||||
bool is_transferable,
|
||||
bool is_sellable,
|
||||
optional<account_role_id_type> role_id,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
|
@ -6397,6 +6398,7 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
|
|||
}
|
||||
op.is_transferable = is_transferable;
|
||||
op.is_sellable = is_sellable;
|
||||
op.account_role = role_id;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
|
|
@ -6415,6 +6417,7 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na
|
|||
optional<uint16_t> revenue_split,
|
||||
optional<bool> is_transferable,
|
||||
optional<bool> is_sellable,
|
||||
optional<account_role_id_type> role_id,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
|
@ -6438,6 +6441,7 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na
|
|||
}
|
||||
op.is_transferable = is_transferable;
|
||||
op.is_sellable = is_sellable;
|
||||
op.account_role = role_id;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
|
|
@ -6719,6 +6723,88 @@ vector<offer_history_object> wallet_api::get_offer_history_by_bidder(string bidd
|
|||
account_object bidder_account = my->get_account(bidder_account_id_or_name);
|
||||
return my->_remote_db->get_offer_history_by_bidder(lb_id, bidder_account.id, limit);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_account_role(string owner_account_id_or_name,
|
||||
string name,
|
||||
string metadata,
|
||||
flat_set<int> allowed_operations,
|
||||
flat_set<account_id_type> whitelisted_accounts,
|
||||
time_point_sec valid_to,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
||||
account_role_create_operation op;
|
||||
op.owner = owner_account.id;
|
||||
op.name = name;
|
||||
op.metadata = metadata;
|
||||
op.allowed_operations = allowed_operations;
|
||||
op.whitelisted_accounts = whitelisted_accounts;
|
||||
op.valid_to = valid_to;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
trx.validate();
|
||||
|
||||
return my->sign_transaction( trx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_account_role(string owner_account_id_or_name,
|
||||
account_role_id_type role_id,
|
||||
optional<string> name,
|
||||
optional<string> metadata,
|
||||
flat_set<int> operations_to_add,
|
||||
flat_set<int> operations_to_remove,
|
||||
flat_set<account_id_type> accounts_to_add,
|
||||
flat_set<account_id_type> accounts_to_remove,
|
||||
optional<time_point_sec> valid_to,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
||||
account_role_update_operation op;
|
||||
op.owner = owner_account.id;
|
||||
op.account_role_id = role_id;
|
||||
op.name = name;
|
||||
op.metadata = metadata;
|
||||
op.allowed_operations_to_add = operations_to_add;
|
||||
op.allowed_operations_to_remove = operations_to_remove;
|
||||
op.accounts_to_add = accounts_to_add;
|
||||
op.accounts_to_remove = accounts_to_remove;
|
||||
op.valid_to = valid_to;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
trx.validate();
|
||||
|
||||
return my->sign_transaction( trx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::delete_account_role(string owner_account_id_or_name,
|
||||
account_role_id_type role_id,
|
||||
bool broadcast)
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
|
||||
account_role_delete_operation op;
|
||||
op.owner = owner_account.id;
|
||||
op.account_role_id = role_id;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations.push_back(op);
|
||||
my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
trx.validate();
|
||||
|
||||
return my->sign_transaction( trx, broadcast );
|
||||
}
|
||||
|
||||
vector<account_role_object> wallet_api::get_account_roles_by_owner(string owner_account_id_or_name) const
|
||||
{
|
||||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
return my->_remote_db->get_account_roles_by_owner(owner_account.id);
|
||||
}
|
||||
// default ctor necessary for FC_REFLECT
|
||||
signed_block_with_info::signed_block_with_info()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
379
tests/tests/account_role_tests.cpp
Normal file
379
tests/tests/account_role_tests.cpp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <fc/reflect/variant.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/custom_permission_object.hpp>
|
||||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
|
||||
#include <graphene/db/simple_index.hpp>
|
||||
|
||||
#include <fc/crypto/digest.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(account_role_tests, database_fixture)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(account_role_create_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("account_role_create_test");
|
||||
generate_blocks(HARDFORK_NFT_TIME);
|
||||
generate_block();
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
ACTORS((resourceowner)(alice)(bob)(charlie));
|
||||
upgrade_to_lifetime_member(resourceowner);
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
upgrade_to_lifetime_member(charlie);
|
||||
transfer(committee_account, resourceowner_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
transfer(committee_account, alice_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
transfer(committee_account, bob_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
transfer(committee_account, charlie_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send account_role_create_operation");
|
||||
|
||||
account_role_create_operation op;
|
||||
op.owner = resourceowner_id;
|
||||
op.name = "Test Account Role";
|
||||
op.metadata = "{\"country\": \"earth\", \"race\": \"human\" }";
|
||||
|
||||
int ops[] = {operation::tag<nft_safe_transfer_from_operation>::value,
|
||||
operation::tag<nft_approve_operation>::value,
|
||||
operation::tag<nft_set_approval_for_all_operation>::value,
|
||||
operation::tag<offer_operation>::value,
|
||||
operation::tag<bid_operation>::value,
|
||||
operation::tag<cancel_offer_operation>::value};
|
||||
op.allowed_operations.insert(ops, ops + 6);
|
||||
op.whitelisted_accounts.emplace(alice_id);
|
||||
op.whitelisted_accounts.emplace(bob_id);
|
||||
op.valid_to = db.head_block_time() + 1000;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, resourceowner_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
const auto &idx = db.get_index_type<account_role_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
auto obj = idx.begin();
|
||||
BOOST_REQUIRE(obj != idx.end());
|
||||
BOOST_CHECK(obj->owner == resourceowner_id);
|
||||
BOOST_CHECK(obj->name == "Test Account Role");
|
||||
BOOST_CHECK(obj->metadata == "{\"country\": \"earth\", \"race\": \"human\" }");
|
||||
flat_set<int> expected_allowed_operations = {operation::tag<nft_safe_transfer_from_operation>::value,
|
||||
operation::tag<nft_approve_operation>::value,
|
||||
operation::tag<nft_set_approval_for_all_operation>::value,
|
||||
operation::tag<offer_operation>::value,
|
||||
operation::tag<bid_operation>::value,
|
||||
operation::tag<cancel_offer_operation>::value};
|
||||
BOOST_CHECK(obj->allowed_operations == expected_allowed_operations);
|
||||
flat_set<account_id_type> expected_whitelisted_accounts = {alice_id, bob_id};
|
||||
BOOST_CHECK(obj->whitelisted_accounts == expected_whitelisted_accounts);
|
||||
BOOST_CHECK(obj->valid_to == db.head_block_time() + 1000);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(account_role_update_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("account_role_create_test");
|
||||
INVOKE(account_role_create_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(charlie);
|
||||
GET_ACTOR(resourceowner);
|
||||
set_expiration(db, trx);
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send account_role_update_operation");
|
||||
|
||||
account_role_update_operation op;
|
||||
op.owner = resourceowner_id;
|
||||
op.account_role_id = account_role_id_type(0);
|
||||
op.name = "Test Account Role Update";
|
||||
op.metadata = "{\"country\": \"earth\", \"race\": \"human\", \"op\":\"update\" }";
|
||||
|
||||
int ops_add[] = {operation::tag<finalize_offer_operation>::value};
|
||||
int ops_delete[] = {operation::tag<nft_safe_transfer_from_operation>::value, operation::tag<nft_approve_operation>::value};
|
||||
op.allowed_operations_to_add.insert(ops_add, ops_add + 1);
|
||||
op.allowed_operations_to_remove.insert(ops_delete, ops_delete + 2);
|
||||
op.valid_to = db.head_block_time() + 10000;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, resourceowner_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
const auto &idx = db.get_index_type<account_role_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
auto obj = idx.begin();
|
||||
BOOST_REQUIRE(obj != idx.end());
|
||||
BOOST_CHECK(obj->owner == resourceowner_id);
|
||||
BOOST_CHECK(obj->name == "Test Account Role Update");
|
||||
BOOST_CHECK(obj->metadata == "{\"country\": \"earth\", \"race\": \"human\", \"op\":\"update\" }");
|
||||
flat_set<int> expected_allowed_operations = {operation::tag<finalize_offer_operation>::value,
|
||||
operation::tag<nft_set_approval_for_all_operation>::value,
|
||||
operation::tag<offer_operation>::value,
|
||||
operation::tag<bid_operation>::value,
|
||||
operation::tag<cancel_offer_operation>::value};
|
||||
BOOST_CHECK(obj->allowed_operations == expected_allowed_operations);
|
||||
flat_set<account_id_type> expected_whitelisted_accounts = {alice_id, bob_id};
|
||||
BOOST_CHECK(obj->whitelisted_accounts == expected_whitelisted_accounts);
|
||||
BOOST_CHECK(obj->valid_to == db.head_block_time() + 10000);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(account_role_delete_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("account_role_delete_test");
|
||||
INVOKE(account_role_create_test);
|
||||
GET_ACTOR(resourceowner);
|
||||
set_expiration(db, trx);
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send account_role_delete_operation");
|
||||
|
||||
account_role_delete_operation op;
|
||||
op.owner = resourceowner_id;
|
||||
op.account_role_id = account_role_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, resourceowner_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
const auto &idx = db.get_index_type<account_role_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(idx.size() == 0);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nft_metadata_create_mint_with_account_role_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("nft_metadata_create_mint_with_account_role_test");
|
||||
INVOKE(account_role_create_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(resourceowner);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
|
||||
|
||||
nft_metadata_create_operation op;
|
||||
op.owner = resourceowner_id;
|
||||
op.name = "NFT Test";
|
||||
op.symbol = "NFT";
|
||||
op.base_uri = "http://nft.example.com";
|
||||
op.revenue_partner = resourceowner_id;
|
||||
op.revenue_split = 1000;
|
||||
op.is_transferable = true;
|
||||
op.is_sellable = true;
|
||||
op.account_role = account_role_id_type(0);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, resourceowner_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
|
||||
|
||||
const auto &nftmd_idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(nftmd_idx.size() == 1);
|
||||
auto nftmd_obj = nftmd_idx.begin();
|
||||
BOOST_REQUIRE(nftmd_obj != nftmd_idx.end());
|
||||
BOOST_CHECK(nftmd_obj->owner == resourceowner_id);
|
||||
BOOST_CHECK(nftmd_obj->name == "NFT Test");
|
||||
BOOST_CHECK(nftmd_obj->symbol == "NFT");
|
||||
BOOST_CHECK(nftmd_obj->base_uri == "http://nft.example.com");
|
||||
BOOST_CHECK(nftmd_obj->account_role == account_role_id_type(0));
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_mint_operation");
|
||||
|
||||
nft_mint_operation op;
|
||||
op.payer = resourceowner_id;
|
||||
op.nft_metadata_id = nftmd_obj->id;
|
||||
op.owner = alice_id;
|
||||
op.approved = alice_id;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, resourceowner_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Check nft_mint_operation results");
|
||||
|
||||
const auto &nft_idx = db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(nft_idx.size() == 1);
|
||||
auto nft_obj = nft_idx.begin();
|
||||
BOOST_REQUIRE(nft_obj != nft_idx.end());
|
||||
BOOST_CHECK(nft_obj->owner == alice_id);
|
||||
BOOST_CHECK(nft_obj->approved == alice_id);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nft_safe_transfer_account_role_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("nft_safe_transfer_account_role_test");
|
||||
INVOKE(nft_metadata_create_mint_with_account_role_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(charlie);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_safe_transfer_from_operation");
|
||||
|
||||
nft_safe_transfer_from_operation op;
|
||||
op.operator_ = alice_id;
|
||||
op.from = alice_id;
|
||||
op.to = bob_id;
|
||||
op.token_id = nft_id_type(0);
|
||||
op.data = "data";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check nft_safe_transfer_from_operation results");
|
||||
|
||||
const auto &nft_idx = db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(nft_idx.size() == 1);
|
||||
auto nft_obj = nft_idx.begin();
|
||||
BOOST_REQUIRE(nft_obj != nft_idx.end());
|
||||
BOOST_CHECK(nft_obj->owner == bob_id);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_safe_transfer_from_operation");
|
||||
|
||||
nft_safe_transfer_from_operation op;
|
||||
op.operator_ = bob_id;
|
||||
op.from = bob_id;
|
||||
op.to = charlie_id;
|
||||
op.token_id = nft_id_type(0);
|
||||
op.data = "data";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
// Charlie is not whitelisted by resource creator
|
||||
BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nft_offer_bid_account_role_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("nft_offer_bid_account_role_test");
|
||||
INVOKE(nft_metadata_create_mint_with_account_role_test);
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
GET_ACTOR(charlie);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send create_offer");
|
||||
|
||||
offer_operation offer_op;
|
||||
offer_op.item_ids.emplace(nft_id_type(0));
|
||||
offer_op.issuer = alice_id;
|
||||
offer_op.buying_item = false;
|
||||
offer_op.maximum_price = asset(10000);
|
||||
offer_op.minimum_price = asset(10);
|
||||
offer_op.offer_expiration_date = db.head_block_time() + fc::seconds(15);
|
||||
|
||||
trx.operations.push_back(offer_op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
// Charlie tries to bid but fails.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send create_bid by charlie");
|
||||
bid_operation bid_op;
|
||||
bid_op.offer_id = offer_id_type(0);
|
||||
// Buy it now price
|
||||
bid_op.bid_price = asset(10000);
|
||||
bid_op.bidder = charlie_id;
|
||||
trx.operations.push_back(bid_op);
|
||||
sign(trx, charlie_private_key);
|
||||
// Charlie is not whitelisting to perform bid_operation by resource/metadata owner
|
||||
BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
// Bob succeeds in bidding.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send create_bid by bob");
|
||||
bid_operation bid_op;
|
||||
bid_op.offer_id = offer_id_type(0);
|
||||
// Buy it now price
|
||||
bid_op.bid_price = asset(10000);
|
||||
bid_op.bidder = bob_id;
|
||||
trx.operations.push_back(bid_op);
|
||||
sign(trx, bob_private_key);
|
||||
// Bob is whitelisted in account role created by resource/metadata owner
|
||||
PUSH_TX(db, trx);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check offer results");
|
||||
|
||||
const auto &nft_idx = db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(nft_idx.size() == 1);
|
||||
auto nft_obj = nft_idx.begin();
|
||||
BOOST_REQUIRE(nft_obj != nft_idx.end());
|
||||
BOOST_CHECK(nft_obj->owner == bob_id);
|
||||
// Charlie tries to bid (buy offer) but fails
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send create_offer");
|
||||
|
||||
offer_operation offer_op;
|
||||
offer_op.item_ids.emplace(nft_id_type(0));
|
||||
offer_op.issuer = charlie_id;
|
||||
offer_op.buying_item = true;
|
||||
offer_op.maximum_price = asset(10000);
|
||||
offer_op.minimum_price = asset(10);
|
||||
offer_op.offer_expiration_date = db.head_block_time() + fc::seconds(15);
|
||||
|
||||
trx.operations.push_back(offer_op);
|
||||
sign(trx, charlie_private_key);
|
||||
// Charlie is not whitelisting to perform offer_operation by resource/metadata owner
|
||||
BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception);
|
||||
trx.clear();
|
||||
}
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue