Merge branch 'develop' into feature/SONs-base

This commit is contained in:
Serki 2020-09-22 18:23:36 +02:00
commit c6c26a7d9f
73 changed files with 7781 additions and 139 deletions

View file

@ -34,4 +34,4 @@ test:
- ./build/tests/chain_test --log_level=message - ./build/tests/chain_test --log_level=message
- ./build/tests/cli_test --log_level=message - ./build/tests/cli_test --log_level=message
tags: tags:
- builder - builder

View file

@ -14,6 +14,7 @@ VERSION=`cat /etc/peerplays/version`
# * $PEERPLAYSD_P2P_ENDPOINT # * $PEERPLAYSD_P2P_ENDPOINT
# * $PEERPLAYSD_WITNESS_ID # * $PEERPLAYSD_WITNESS_ID
# * $PEERPLAYSD_PRIVATE_KEY # * $PEERPLAYSD_PRIVATE_KEY
# * $PEERPLAYSD_DEBUG_PRIVATE_KEY
# * $PEERPLAYSD_TRACK_ACCOUNTS # * $PEERPLAYSD_TRACK_ACCOUNTS
# * $PEERPLAYSD_PARTIAL_OPERATIONS # * $PEERPLAYSD_PARTIAL_OPERATIONS
# * $PEERPLAYSD_MAX_OPS_PER_ACCOUNT # * $PEERPLAYSD_MAX_OPS_PER_ACCOUNT
@ -51,6 +52,10 @@ if [[ ! -z "$PEERPLAYSD_PRIVATE_KEY" ]]; then
ARGS+=" --private-key=$PEERPLAYSD_PRIVATE_KEY" ARGS+=" --private-key=$PEERPLAYSD_PRIVATE_KEY"
fi fi
if [[ ! -z "$PEERPLAYSD_DEBUG_PRIVATE_KEY" ]]; then
ARGS+=" --debug-private-key=$PEERPLAYSD_DEBUG_PRIVATE_KEY"
fi
if [[ ! -z "$PEERPLAYSD_TRACK_ACCOUNTS" ]]; then if [[ ! -z "$PEERPLAYSD_TRACK_ACCOUNTS" ]]; then
for ACCOUNT in $PEERPLAYSD_TRACK_ACCOUNTS ; do for ACCOUNT in $PEERPLAYSD_TRACK_ACCOUNTS ; do
ARGS+=" --track-account=$ACCOUNT" ARGS+=" --track-account=$ACCOUNT"

View file

@ -934,6 +934,7 @@ void application::set_program_options(boost::program_options::options_descriptio
("enable-standby-votes-tracking", bpo::value<bool>()->implicit_value(true), ("enable-standby-votes-tracking", bpo::value<bool>()->implicit_value(true),
"Whether to enable tracking of votes of standby witnesses and committee members. " "Whether to enable tracking of votes of standby witnesses and committee members. "
"Set it to true to provide accurate data to API clients, set to false for slightly better performance.") "Set it to true to provide accurate data to API clients, set to false for slightly better performance.")
("plugins", bpo::value<string>(), "Space-separated list of plugins to activate")
; ;
command_line_options.add(configuration_file_options); command_line_options.add(configuration_file_options);
command_line_options.add_options() command_line_options.add_options()
@ -980,18 +981,21 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
} }
std::vector<string> wanted; std::set<string> wanted;
if( options.count("plugins") ) if( options.count("plugins") )
{ {
boost::split(wanted, options.at("plugins").as<std::string>(), [](char c){return c == ' ';}); boost::split(wanted, options.at("plugins").as<std::string>(), [](char c){return c == ' ';});
} }
else else
{ {
wanted.push_back("witness"); wanted.insert("account_history");
wanted.push_back("account_history"); wanted.insert("market_history");
wanted.push_back("market_history"); wanted.insert("accounts_list");
wanted.push_back("bookie"); wanted.insert("affiliate_stats");
} }
wanted.insert("witness");
wanted.insert("bookie");
int es_ah_conflict_counter = 0; int es_ah_conflict_counter = 0;
for (auto& it : wanted) for (auto& it : wanted)
{ {

View file

@ -202,6 +202,39 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
// gpos // gpos
gpos_info get_gpos_info(const account_id_type account) const; gpos_info get_gpos_info(const account_id_type account) const;
// rbac
vector<custom_permission_object> get_custom_permissions(const account_id_type account) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(const account_id_type account, const string& permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(const account_id_type account) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const;
// NFT
uint64_t nft_get_balance(const account_id_type owner) const;
optional<account_id_type> nft_owner_of(const nft_id_type token_id) const;
optional<account_id_type> nft_get_approved(const nft_id_type token_id) const;
bool nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const;
string nft_get_name(const nft_metadata_id_type nft_metadata_id) const;
string nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const;
string nft_get_token_uri(const nft_id_type token_id) const;
uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const;
nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const;
nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const;
vector<nft_object> nft_get_all_tokens() const;
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const;
// Marketplace
vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_object> list_sell_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_object> list_buy_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_history_object> list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const;
vector<offer_object> get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
vector<offer_object> get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const;
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;
//private: //private:
const account_object* get_account_from_string( const std::string& name_or_id, const account_object* get_account_from_string( const std::string& name_or_id,
bool throw_if_not_found = true ) const; bool throw_if_not_found = true ) const;
@ -2093,6 +2126,9 @@ set<public_key_type> database_api_impl::get_required_signatures( const signed_tr
available_keys, available_keys,
[&]( account_id_type id ){ return &id(_db).active; }, [&]( account_id_type id ){ return &id(_db).active; },
[&]( account_id_type id ){ return &id(_db).owner; }, [&]( 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 ); _db.get_global_properties().parameters.max_authority_depth );
wdump((result)); wdump((result));
return result; return result;
@ -2128,6 +2164,17 @@ set<public_key_type> database_api_impl::get_potential_signatures( const signed_t
result.insert(k); result.insert(k);
return &auth; return &auth;
}, },
[&]( account_id_type id, const operation& op ) {
vector<authority> custom_auths = _db.get_account_custom_authorities(id, op);
for (const auto& cauth: custom_auths)
{
for (const auto& k : cauth.get_keys())
{
result.insert(k);
}
}
return custom_auths;
},
_db.get_global_properties().parameters.max_authority_depth _db.get_global_properties().parameters.max_authority_depth
); );
@ -2155,6 +2202,9 @@ set<address> database_api_impl::get_potential_address_signatures( const signed_t
result.insert(k); result.insert(k);
return &auth; 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 _db.get_global_properties().parameters.max_authority_depth
); );
return result; return result;
@ -2170,6 +2220,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const
trx.verify_authority( _db.get_chain_id(), trx.verify_authority( _db.get_chain_id(),
[this]( account_id_type id ){ return &id(_db).active; }, [this]( account_id_type id ){ return &id(_db).active; },
[this]( account_id_type id ){ return &id(_db).owner; }, [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 ); _db.get_global_properties().parameters.max_authority_depth );
return true; return true;
} }
@ -2476,6 +2528,7 @@ graphene::app::gpos_info database_api::get_gpos_info(const account_id_type accou
return my->get_gpos_info(account); return my->get_gpos_info(account);
} }
graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const
{ {
FC_ASSERT( _db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time FC_ASSERT( _db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time
@ -2539,6 +2592,538 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type
return result; return result;
} }
//////////////////////////////////////////////////////////////////////
// //
// RBAC methods //
// //
//////////////////////////////////////////////////////////////////////
vector<custom_permission_object> database_api::get_custom_permissions(const account_id_type account) const
{
return my->get_custom_permissions(account);
}
vector<custom_permission_object> database_api_impl::get_custom_permissions(const account_id_type account) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto prange = pindex.equal_range(boost::make_tuple(account));
vector<custom_permission_object> custom_permissions;
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
custom_permissions.push_back(pobj);
}
return custom_permissions;
}
fc::optional<custom_permission_object> database_api::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
{
return my->get_custom_permission_by_name(account, permission_name);
}
fc::optional<custom_permission_object> database_api_impl::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto prange = pindex.equal_range(boost::make_tuple(account, permission_name));
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
return pobj;
}
return {};
}
//////////////////////////////////////////////////////////////////////
// //
// NFT methods //
// //
//////////////////////////////////////////////////////////////////////
uint64_t database_api::nft_get_balance(const account_id_type owner) const
{
return my->nft_get_balance(owner);
}
uint64_t database_api_impl::nft_get_balance(const account_id_type owner) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
const auto &idx_nft_range = idx_nft.equal_range(owner);
return std::distance(idx_nft_range.first, idx_nft_range.second);
}
optional<account_id_type> database_api::nft_owner_of(const nft_id_type token_id) const
{
return my->nft_owner_of(token_id);
}
optional<account_id_type> database_api_impl::nft_owner_of(const nft_id_type token_id) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
auto itr_nft = idx_nft.find(token_id);
if (itr_nft != idx_nft.end()) {
return itr_nft->owner;
}
return {};
}
optional<account_id_type> database_api::nft_get_approved(const nft_id_type token_id) const
{
return my->nft_get_approved(token_id);
}
optional<account_id_type> database_api_impl::nft_get_approved(const nft_id_type token_id) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
auto itr_nft = idx_nft.find(token_id);
if (itr_nft != idx_nft.end()) {
return itr_nft->approved;
}
return {};
}
bool database_api::nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const
{
return my->nft_is_approved_for_all(owner, operator_);
}
bool database_api_impl::nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
const auto &idx_nft_range = idx_nft.equal_range(owner);
if (std::distance(idx_nft_range.first, idx_nft_range.second) == 0) {
return false;
}
bool result = true;
std::for_each(idx_nft_range.first, idx_nft_range.second, [&](const nft_object &obj) {
result = result && (obj.approved == operator_);
});
return result;
}
string database_api::nft_get_name(const nft_metadata_id_type nft_metadata_id) const
{
return my->nft_get_name(nft_metadata_id);
}
string database_api_impl::nft_get_name(const nft_metadata_id_type nft_metadata_id) const
{
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
auto itr_nft_md = idx_nft_md.find(nft_metadata_id);
if (itr_nft_md != idx_nft_md.end()) {
return itr_nft_md->name;
}
return "";
}
string database_api::nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const
{
return my->nft_get_symbol(nft_metadata_id);
}
string database_api_impl::nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const
{
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
auto itr_nft_md = idx_nft_md.find(nft_metadata_id);
if (itr_nft_md != idx_nft_md.end()) {
return itr_nft_md->symbol;
}
return "";
}
string database_api::nft_get_token_uri(const nft_id_type token_id) const
{
return my->nft_get_token_uri(token_id);
}
string database_api_impl::nft_get_token_uri(const nft_id_type token_id) const
{
string result = "";
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
auto itr_nft = idx_nft.find(token_id);
if (itr_nft != idx_nft.end()) {
result = itr_nft->token_uri;
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
auto itr_nft_md = idx_nft_md.find(itr_nft->nft_metadata_id);
if (itr_nft_md != idx_nft_md.end()) {
result = itr_nft_md->base_uri + itr_nft->token_uri;
}
}
return result;
}
uint64_t database_api::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const
{
return my->nft_get_total_supply(nft_metadata_id);
}
uint64_t database_api_impl::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const
{
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
return idx_nft_md.size();
}
nft_object database_api::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const
{
return my->nft_token_by_index(nft_metadata_id, token_idx);
}
nft_object database_api_impl::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_metadata>();
auto idx_nft_range = idx_nft.equal_range(nft_metadata_id);
uint64_t tmp_idx = token_idx;
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
if (tmp_idx == 0) {
return *itr;
}
tmp_idx = tmp_idx - 1;
}
return {};
}
nft_object database_api::nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const
{
return my->nft_token_of_owner_by_index(nft_metadata_id, owner, token_idx);
}
nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_metadata_and_owner>();
auto idx_nft_range = idx_nft.equal_range(std::make_tuple(nft_metadata_id, owner));
uint64_t tmp_idx = token_idx;
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
if (tmp_idx == 0) {
return *itr;
}
tmp_idx = tmp_idx - 1;
}
return {};
}
vector<nft_object> database_api::nft_get_all_tokens() const
{
return my->nft_get_all_tokens();
}
vector<nft_object> database_api_impl::nft_get_all_tokens() const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
vector<nft_object> result;
for (auto itr = idx_nft.begin(); itr != idx_nft.end(); ++itr) {
result.push_back(*itr);
}
return result;
}
vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner) const
{
return my->nft_get_tokens_by_owner(owner);
}
vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner) const
{
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
auto idx_nft_range = idx_nft.equal_range(owner);
vector<nft_object> result;
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
result.push_back(*itr);
}
return result;
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities(const account_id_type account) const
{
return my->get_custom_account_authorities(account);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities(const account_id_type account) const
{
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<custom_account_authority_object> custom_account_auths;
auto prange = pindex.equal_range(boost::make_tuple(account));
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
auto crange = cindex.equal_range(boost::make_tuple(pobj.id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
}
return custom_account_auths;
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
{
return my->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
{
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<custom_account_authority_object> custom_account_auths;
auto crange = cindex.equal_range(boost::make_tuple(permission_id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
return custom_account_auths;
}
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
{
return my->get_custom_account_authorities_by_permission_name(account, permission_name);
}
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
{
vector<custom_account_authority_object> custom_account_auths;
fc::optional<custom_permission_object> pobj = get_custom_permission_by_name(account, permission_name);
if(!pobj)
{
return custom_account_auths;
}
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
auto crange = cindex.equal_range(boost::make_tuple(pobj->id));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_account_auths.push_back(cobj);
}
return custom_account_auths;
}
vector<authority> database_api::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
{
return my->get_active_custom_account_authorities_by_operation(account, operation_type);
}
vector<authority> database_api_impl::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
{
operation op;
op.set_which(operation_type);
return _db.get_account_custom_authorities(account, op);
}
// Marketplace
vector<offer_object> database_api::list_offers(const offer_id_type lower_id, uint32_t limit) const
{
return my->list_offers(lower_id, limit);
}
vector<offer_object> database_api_impl::list_offers(const offer_id_type lower_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
vector<offer_object> result;
result.reserve(limit);
auto itr = offers_idx.lower_bound(lower_id);
while(limit-- && itr != offers_idx.end())
result.emplace_back(*itr++);
return result;
}
vector<offer_object> database_api::list_sell_offers(const offer_id_type lower_id, uint32_t limit) const
{
return my->list_sell_offers(lower_id, limit);
}
vector<offer_object> database_api_impl::list_sell_offers(const offer_id_type lower_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
vector<offer_object> result;
result.reserve(limit);
auto itr = offers_idx.lower_bound(lower_id);
while(limit && itr != offers_idx.end())
{
if(itr->buying_item == false)
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_object> database_api::list_buy_offers(const offer_id_type lower_id, uint32_t limit) const
{
return my->list_buy_offers(lower_id, limit);
}
vector<offer_object> database_api_impl::list_buy_offers(const offer_id_type lower_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
vector<offer_object> result;
result.reserve(limit);
auto itr = offers_idx.lower_bound(lower_id);
while(limit && itr != offers_idx.end())
{
if(itr->buying_item == true)
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_history_object> database_api::list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const
{
return my->list_offer_history(lower_id, limit);
}
vector<offer_history_object> database_api_impl::list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
vector<offer_history_object> result;
result.reserve(limit);
auto itr = oh_idx.lower_bound(lower_id);
while(limit-- && itr != oh_idx.end())
result.emplace_back(*itr++);
return result;
}
vector<offer_object> database_api::get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
{
return my->get_offers_by_issuer(lower_id, issuer_account_id, limit);
}
vector<offer_object> database_api_impl::get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
vector<offer_object> result;
result.reserve(limit);
auto itr = offers_idx.lower_bound(lower_id);
while(limit && itr != offers_idx.end())
{
if(itr->issuer == issuer_account_id)
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_object> database_api::get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const
{
return my->get_offers_by_item(lower_id, item, limit);
}
vector<offer_object> database_api_impl::get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
vector<offer_object> result;
result.reserve(limit);
auto itr = offers_idx.lower_bound(lower_id);
while(limit && itr != offers_idx.end())
{
if(itr->item_ids.find(item) != itr->item_ids.end())
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_history_object> database_api::get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
{
return my->get_offer_history_by_issuer(lower_id, issuer_account_id, limit);
}
vector<offer_history_object> database_api::get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const
{
return my->get_offer_history_by_item(lower_id, item, limit);
}
vector<offer_history_object> database_api::get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const
{
return my->get_offer_history_by_bidder(lower_id, bidder_account_id, limit);
}
vector<offer_history_object> database_api_impl::get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
vector<offer_history_object> result;
result.reserve(limit);
auto itr = oh_idx.lower_bound(lower_id);
while(limit && itr != oh_idx.end())
{
if(itr->issuer == issuer_account_id)
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_history_object> database_api_impl::get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
vector<offer_history_object> result;
result.reserve(limit);
auto itr = oh_idx.lower_bound(lower_id);
while(limit && itr != oh_idx.end())
{
if(itr->item_ids.find(item) != itr->item_ids.end())
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
vector<offer_history_object> database_api_impl::get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const
{
FC_ASSERT( limit <= 100 );
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
vector<offer_history_object> result;
result.reserve(limit);
auto itr = oh_idx.lower_bound(lower_id);
while(limit && itr != oh_idx.end())
{
if(itr->bidder && *itr->bidder == bidder_account_id)
{
result.emplace_back(*itr);
limit--;
}
itr++;
}
return result;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// // // //
// Private methods // // Private methods //

View file

@ -51,6 +51,11 @@
#include <graphene/chain/witness_object.hpp> #include <graphene/chain/witness_object.hpp>
#include <graphene/chain/tournament_object.hpp> #include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/market_history/market_history_plugin.hpp> #include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp> #include <fc/api.hpp>
@ -808,8 +813,121 @@ class database_api
*/ */
gpos_info get_gpos_info(const account_id_type account) const; gpos_info get_gpos_info(const account_id_type account) const;
//////////
// RBAC //
//////////
/**
* @return account and custom permissions/account-authorities info
*/
vector<custom_permission_object> get_custom_permissions(const account_id_type account) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(const account_id_type account, const string& permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(const account_id_type account) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const;
/////////
// NFT //
/////////
/**
* @brief Returns the number of NFT owned by account
* @param owner Owner account ID
* @return Number of NFTs owned by account
*/
uint64_t nft_get_balance(const account_id_type owner) const;
/**
* @brief Returns the NFT owner
* @param token_id NFT ID
* @return NFT owner account ID
*/
optional<account_id_type> nft_owner_of(const nft_id_type token_id) const;
/**
* @brief Returns the NFT approved account ID
* @param token_id NFT ID
* @return NFT approved account ID
*/
optional<account_id_type> nft_get_approved(const nft_id_type token_id) const;
/**
* @brief Returns operator approved state for all NFT owned by owner
* @param owner NFT owner account ID
* @param token_id NFT ID
* @return True if operator is approved for all NFT owned by owner, else False
*/
bool nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const;
/**
* @brief Returns NFT name from NFT metadata
* @param nft_metadata_id NFT metadata ID
* @return NFT name
*/
string nft_get_name(const nft_metadata_id_type nft_metadata_id) const;
/**
* @brief Returns NFT symbol from NFT metadata
* @param nft_metadata_id NFT metadata ID
* @return NFT symbol
*/
string nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const;
/**
* @brief Returns NFT URI
* @param token_id NFT ID
* @return NFT URI
*/
string nft_get_token_uri(const nft_id_type token_id) const;
/**
* @brief Returns total number of NFTs assigned to NFT metadata
* @param nft_metadata_id NFT metadata ID
* @return Total number of NFTs assigned to NFT metadata
*/
uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const;
/**
* @brief Returns NFT by index from NFT metadata
* @param nft_metadata_id NFT metadata ID
* @param token_idx NFT index in the list of tokens
* @return NFT symbol
*/
nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const;
/**
* @brief Returns NFT by owner and index
* @param nft_metadata_id NFT metadata ID
* @param owner NFT owner
* @param token_idx NFT index in the list of tokens
* @return NFT object
*/
nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const;
/**
* @brief Returns list of all available NTF's
* @return List of all available NFT's
*/
vector<nft_object> nft_get_all_tokens() const;
/**
* @brief Returns NFT's owned by owner
* @param owner NFT owner
* @return List of NFT owned by owner
*/
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const;
//////////////////
// MARKET PLACE //
//////////////////
vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_object> list_sell_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_object> list_buy_offers(const offer_id_type lower_id, uint32_t limit) const;
vector<offer_history_object> list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const;
vector<offer_object> get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
vector<offer_object> get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const;
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;
private: private:
std::shared_ptr< database_api_impl > my; std::shared_ptr< database_api_impl > my;
}; };
@ -965,4 +1083,37 @@ FC_API(graphene::app::database_api,
// gpos // gpos
(get_gpos_info) (get_gpos_info)
//rbac
(get_custom_permissions)
(get_custom_permission_by_name)
(get_custom_account_authorities)
(get_custom_account_authorities_by_permission_id)
(get_custom_account_authorities_by_permission_name)
(get_active_custom_account_authorities_by_operation)
// NFT
(nft_get_balance)
(nft_owner_of)
(nft_get_approved)
(nft_is_approved_for_all)
(nft_get_name)
(nft_get_symbol)
(nft_get_token_uri)
(nft_get_total_supply)
(nft_token_by_index)
(nft_token_of_owner_by_index)
(nft_get_all_tokens)
(nft_get_tokens_by_owner)
// Marketplace
(list_offers)
(list_sell_offers)
(list_buy_offers)
(list_offer_history)
(get_offers_by_issuer)
(get_offers_by_item)
(get_offer_history_by_issuer)
(get_offer_history_by_item)
(get_offer_history_by_bidder)
) )

View file

@ -62,6 +62,9 @@ add_library( graphene_chain
protocol/vote.cpp protocol/vote.cpp
protocol/tournament.cpp protocol/tournament.cpp
protocol/small_ops.cpp protocol/small_ops.cpp
protocol/custom_permission.cpp
protocol/custom_account_authority.cpp
protocol/offer.cpp
genesis_state.cpp genesis_state.cpp
get_config.cpp get_config.cpp
@ -113,9 +116,15 @@ add_library( graphene_chain
betting_market_evaluator.cpp betting_market_evaluator.cpp
betting_market_object.cpp betting_market_object.cpp
betting_market_group_object.cpp betting_market_group_object.cpp
custom_permission_evaluator.cpp
custom_account_authority_evaluator.cpp
affiliate_payout.cpp affiliate_payout.cpp
offer_object.cpp
offer_evaluator.cpp
nft_evaluator.cpp
protocol/nft.cpp
son_evaluator.cpp son_evaluator.cpp
son_object.cpp son_object.cpp

View file

@ -23,6 +23,8 @@
*/ */
#include <graphene/chain/asset_object.hpp> #include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/io/raw.hpp> #include <fc/io/raw.hpp>
#include <fc/uint128.hpp> #include <fc/uint128.hpp>
@ -185,6 +187,41 @@ vector<account_id_type> asset_object::get_holders( database& db ) const
return holders; return holders;
} }
vector<uint64_t> asset_object::get_ticket_ids( database& db ) const
{
auto& asset_bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
vector<uint64_t> ids;
const auto range = asset_bal_idx.equal_range( boost::make_tuple( get_id() ) );
for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )
{
const auto& stats = bal.owner(db).statistics(db);
const account_transaction_history_object* ath = static_cast<const account_transaction_history_object*>(&stats.most_recent_op(db));
for( uint64_t balance = bal.balance.value; balance > 0;)
{
if(ath != nullptr)
{
const operation_history_object& oho = db.get<operation_history_object>( ath->operation_id );
if( oho.op.which() == operation::tag<ticket_purchase_operation>::value && get_id() == oho.op.get<ticket_purchase_operation>().lottery)
{
uint64_t tickets_count = oho.op.get<ticket_purchase_operation>().tickets_to_buy;
ids.insert(ids.end(), tickets_count, oho.id.instance());
balance -= tickets_count;
assert(balance >= 0);
}
if( ath->next == account_transaction_history_id_type() )
{
ath = nullptr;
break;
}
else ath = db.find(ath->next);
}
}
}
return ids;
}
void asset_object::distribute_benefactors_part( database& db ) void asset_object::distribute_benefactors_part( database& db )
{ {
transaction_evaluation_state eval( &db ); transaction_evaluation_state eval( &db );
@ -206,6 +243,7 @@ map< account_id_type, vector< uint16_t > > asset_object::distribute_winners_part
transaction_evaluation_state eval( &db ); transaction_evaluation_state eval( &db );
auto holders = get_holders( db ); auto holders = get_holders( db );
vector<uint64_t> ticket_ids = get_ticket_ids(db);
FC_ASSERT( dynamic_data( db ).current_supply == holders.size() ); FC_ASSERT( dynamic_data( db ).current_supply == holders.size() );
map<account_id_type, vector<uint16_t> > structurized_participants; map<account_id_type, vector<uint16_t> > structurized_participants;
for( account_id_type holder : holders ) for( account_id_type holder : holders )
@ -234,6 +272,11 @@ map< account_id_type, vector< uint16_t > > asset_object::distribute_winners_part
reward_op.lottery = get_id(); reward_op.lottery = get_id();
reward_op.is_benefactor_reward = false; reward_op.is_benefactor_reward = false;
reward_op.winner = holders[winner_num]; reward_op.winner = holders[winner_num];
if(db.head_block_time() > HARDFORK_5050_1_TIME && ticket_ids.size() > winner_num)
{
const static_variant<uint64_t, void_t> tkt_id = ticket_ids[winner_num];
reward_op.winner_ticket_id = tkt_id;
}
reward_op.win_percentage = tickets[c]; reward_op.win_percentage = tickets[c];
reward_op.amount = asset( jackpot * tickets[c] * ( 1. - sweeps_distribution_percentage / (double)GRAPHENE_100_PERCENT ) / GRAPHENE_100_PERCENT , db.get_balance(id).asset_id ); reward_op.amount = asset( jackpot * tickets[c] * ( 1. - sweeps_distribution_percentage / (double)GRAPHENE_100_PERCENT ) / GRAPHENE_100_PERCENT , db.get_balance(id).asset_id );
db.apply_operation(eval, reward_op); db.apply_operation(eval, reward_op);

View file

@ -0,0 +1,152 @@
#include <graphene/chain/custom_account_authority_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
struct rbac_operation_hardfork_visitor
{
typedef void result_type;
const fc::time_point_sec block_time;
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
void operator()(int op_type) const
{
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
switch (op_type)
{
case operation::tag<custom_permission_create_operation>::value:
case operation::tag<custom_permission_update_operation>::value:
case operation::tag<custom_permission_delete_operation>::value:
case operation::tag<custom_account_authority_create_operation>::value:
case operation::tag<custom_account_authority_update_operation>::value:
case operation::tag<custom_account_authority_delete_operation>::value:
FC_ASSERT(block_time >= HARDFORK_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
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT 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");
FC_ASSERT((op.valid_to - op.valid_from) <= fc::seconds(d.get_global_properties().parameters.rbac_max_account_authority_lifetime()), "Validity of the auth beyond max expiry");
rbac_operation_hardfork_visitor rvtor(now);
rvtor(op.operation_type);
const auto& cindex = d.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
auto count = cindex.count(boost::make_tuple(op.permission_id));
FC_ASSERT(count < d.get_global_properties().parameters.rbac_max_authorities_per_permission(), "Max operations that can be linked to a permission reached");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type create_custom_account_authority_evaluator::do_apply(const custom_account_authority_create_operation &op)
{
try
{
database &d = db();
return d.create<custom_account_authority_object>([&op](custom_account_authority_object &obj) mutable {
obj.permission_id = op.permission_id;
obj.operation_type = op.operation_type;
obj.valid_from = op.valid_from;
obj.valid_to = op.valid_to;
})
.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result update_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_update_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner_account(d);
const custom_account_authority_object &aobj = op.auth_id(d);
const custom_permission_object &pobj = aobj.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can update account authority object");
auto valid_from = aobj.valid_from;
auto valid_to = aobj.valid_to;
if (op.new_valid_from)
{
valid_from = *op.new_valid_from;
}
if (op.new_valid_to)
{
FC_ASSERT(*op.new_valid_to > now, "New valid_to expiry should be in the future");
valid_to = *op.new_valid_to;
}
FC_ASSERT(valid_from < valid_to, "valid_from should be before valid_to");
FC_ASSERT((valid_to - valid_from) <= fc::seconds(d.get_global_properties().parameters.rbac_max_account_authority_lifetime()), "Validity of the auth beyond max expiry");
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_NFT_TIME, "Not allowed until NFT HF");
op.owner_account(d);
const custom_account_authority_object &aobj = op.auth_id(d);
const custom_permission_object &pobj = aobj.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can delete account authority object");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_account_authority_evaluator::do_apply(const custom_account_authority_delete_operation &op)
{
try
{
database &d = db();
const custom_account_authority_object &aobj = op.auth_id(d);
d.remove(aobj);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,133 @@
#include <graphene/chain/custom_permission_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
void_result create_custom_permission_evaluator::do_evaluate(const custom_permission_create_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner_account(d);
for (const auto &account_weight_pair : op.auth.account_auths)
{
account_weight_pair.first(d);
}
const auto &pindex = d.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
auto pitr = pindex.find(boost::make_tuple(op.owner_account, op.permission_name));
FC_ASSERT(pitr == pindex.end(), "Permission name already exists for the given account");
auto count = pindex.count(boost::make_tuple(op.owner_account));
FC_ASSERT(count < d.get_global_properties().parameters.rbac_max_permissions_per_account(), "Max permissions per account reached");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type create_custom_permission_evaluator::do_apply(const custom_permission_create_operation &op)
{
try
{
database &d = db();
return d.create<custom_permission_object>([&op](custom_permission_object &obj) mutable {
obj.account = op.owner_account;
obj.permission_name = op.permission_name;
obj.auth = op.auth;
})
.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result update_custom_permission_evaluator::do_evaluate(const custom_permission_update_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT 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_NFT_TIME, "Not allowed until NFT HF");
op.owner_account(d);
const custom_permission_object &pobj = op.permission_id(d);
FC_ASSERT(pobj.account == op.owner_account, "Only owner account can delete permission object");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result delete_custom_permission_evaluator::do_apply(const custom_permission_delete_operation &op)
{
try
{
database &d = db();
const custom_permission_object &pobj = op.permission_id(d);
// Remove the account authority objects linked to this permission
const auto& cindex = d.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
vector<std::reference_wrapper<const custom_account_authority_object>> custom_auths;
auto crange = cindex.equal_range(boost::make_tuple(pobj.id));
// Store the references to the account authorities
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
custom_auths.push_back(cobj);
}
// Now remove the account authorities
for(const auto& cauth : custom_auths)
{
d.remove(cauth);
}
// Now finally remove the permission
d.remove(pobj);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -727,6 +727,7 @@ void database::_apply_block( const signed_block& next_block )
update_withdraw_permissions(); update_withdraw_permissions();
update_tournaments(); update_tournaments();
update_betting_markets(next_block.timestamp); update_betting_markets(next_block.timestamp);
finalize_expired_offers();
// n.b., update_maintenance_flag() happens this late // n.b., update_maintenance_flag() happens this late
// because get_slot_time() / get_slot_at_time() is needed above // because get_slot_time() / get_slot_at_time() is needed above
@ -799,9 +800,14 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) ) if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{ {
auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; auto get_owner = [this]( 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 = [this]( 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,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()),
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 //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is

View file

@ -27,6 +27,10 @@
#include <graphene/chain/asset_object.hpp> #include <graphene/chain/asset_object.hpp>
#include <graphene/chain/chain_property_object.hpp> #include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/global_property_object.hpp> #include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/son_object.hpp> #include <graphene/chain/son_object.hpp>
#include <graphene/chain/son_proposal_object.hpp> #include <graphene/chain/son_proposal_object.hpp>
#include <fc/smart_ref_impl.hpp> #include <fc/smart_ref_impl.hpp>
@ -147,6 +151,37 @@ const std::vector<uint32_t> database::get_winner_numbers( asset_id_type for_asse
return result; return result;
} }
vector<authority> database::get_account_custom_authorities(account_id_type account, const operation& op)const
{
const auto& pindex = get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
const auto& cindex = get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
auto prange = pindex.equal_range(boost::make_tuple(account));
time_point_sec now = head_block_time();
vector<authority> custom_auths;
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
{
auto crange = cindex.equal_range(boost::make_tuple(pobj.id, op.which()));
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
{
if(now >= cobj.valid_from && now < cobj.valid_to)
{
custom_auths.push_back(pobj.auth);
}
}
}
return custom_auths;
}
bool database::item_locked(const nft_id_type &item) const
{
const auto &offer_idx = get_index_type<offer_index>();
const auto &oidx = dynamic_cast<const base_primary_index &>(offer_idx);
const auto &market_items = oidx.get_secondary_index<graphene::chain::offer_item_index>();
auto items_itr = market_items._locked_items.find(item);
return (items_itr != market_items._locked_items.end());
}
std::set<son_id_type> database::get_sons_being_deregistered() std::set<son_id_type> database::get_sons_being_deregistered()
{ {
std::set<son_id_type> ret; std::set<son_id_type> ret;

View file

@ -49,6 +49,12 @@
#include <graphene/chain/tournament_object.hpp> #include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/match_object.hpp> #include <graphene/chain/match_object.hpp>
#include <graphene/chain/game_object.hpp> #include <graphene/chain/game_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/sport_object.hpp> #include <graphene/chain/sport_object.hpp>
#include <graphene/chain/event_group_object.hpp> #include <graphene/chain/event_group_object.hpp>
#include <graphene/chain/event_object.hpp> #include <graphene/chain/event_object.hpp>
@ -82,6 +88,10 @@
#include <graphene/chain/event_evaluator.hpp> #include <graphene/chain/event_evaluator.hpp>
#include <graphene/chain/betting_market_evaluator.hpp> #include <graphene/chain/betting_market_evaluator.hpp>
#include <graphene/chain/tournament_evaluator.hpp> #include <graphene/chain/tournament_evaluator.hpp>
#include <graphene/chain/custom_permission_evaluator.hpp>
#include <graphene/chain/custom_account_authority_evaluator.hpp>
#include <graphene/chain/offer_evaluator.hpp>
#include <graphene/chain/nft_evaluator.hpp>
#include <graphene/chain/son_evaluator.hpp> #include <graphene/chain/son_evaluator.hpp>
#include <graphene/chain/son_wallet_evaluator.hpp> #include <graphene/chain/son_wallet_evaluator.hpp>
#include <graphene/chain/son_wallet_deposit_evaluator.hpp> #include <graphene/chain/son_wallet_deposit_evaluator.hpp>
@ -174,12 +184,20 @@ const uint8_t betting_market_object::type_id;
const uint8_t bet_object::space_id; const uint8_t bet_object::space_id;
const uint8_t bet_object::type_id; const uint8_t bet_object::type_id;
const uint8_t nft_object::space_id;
const uint8_t nft_object::type_id;
const uint8_t betting_market_position_object::space_id; const uint8_t betting_market_position_object::space_id;
const uint8_t betting_market_position_object::type_id; const uint8_t betting_market_position_object::type_id;
const uint8_t global_betting_statistics_object::space_id; const uint8_t global_betting_statistics_object::space_id;
const uint8_t global_betting_statistics_object::type_id; const uint8_t global_betting_statistics_object::type_id;
const uint8_t offer_object::space_id;
const uint8_t offer_object::type_id;
const uint8_t offer_history_object::space_id;
const uint8_t offer_history_object::type_id;
void database::initialize_evaluators() void database::initialize_evaluators()
{ {
@ -254,6 +272,22 @@ void database::initialize_evaluators()
register_evaluator<lottery_reward_evaluator>(); register_evaluator<lottery_reward_evaluator>();
register_evaluator<lottery_end_evaluator>(); register_evaluator<lottery_end_evaluator>();
register_evaluator<sweeps_vesting_claim_evaluator>(); register_evaluator<sweeps_vesting_claim_evaluator>();
register_evaluator<create_custom_permission_evaluator>();
register_evaluator<update_custom_permission_evaluator>();
register_evaluator<delete_custom_permission_evaluator>();
register_evaluator<create_custom_account_authority_evaluator>();
register_evaluator<update_custom_account_authority_evaluator>();
register_evaluator<delete_custom_account_authority_evaluator>();
register_evaluator<offer_evaluator>();
register_evaluator<bid_evaluator>();
register_evaluator<cancel_offer_evaluator>();
register_evaluator<finalize_offer_evaluator>();
register_evaluator<nft_metadata_create_evaluator>();
register_evaluator<nft_metadata_update_evaluator>();
register_evaluator<nft_mint_evaluator>();
register_evaluator<nft_safe_transfer_from_evaluator>();
register_evaluator<nft_approve_evaluator>();
register_evaluator<nft_set_approval_for_all_evaluator>();
register_evaluator<create_son_evaluator>(); register_evaluator<create_son_evaluator>();
register_evaluator<update_son_evaluator>(); register_evaluator<update_son_evaluator>();
register_evaluator<deregister_son_evaluator>(); register_evaluator<deregister_son_evaluator>();
@ -315,6 +349,13 @@ void database::initialize_indexes()
tournament_details_idx->add_secondary_index<tournament_players_index>(); tournament_details_idx->add_secondary_index<tournament_players_index>();
add_index< primary_index<match_index> >(); add_index< primary_index<match_index> >();
add_index< primary_index<game_index> >(); add_index< primary_index<game_index> >();
add_index< primary_index<custom_permission_index> >();
add_index< primary_index<custom_account_authority_index> >();
auto offer_idx = add_index< primary_index<offer_index> >();
offer_idx->add_secondary_index<offer_item_index>();
add_index< primary_index<nft_metadata_index > >();
add_index< primary_index<nft_index > >();
add_index< primary_index<son_proposal_index> >(); add_index< primary_index<son_proposal_index> >();
add_index< primary_index<son_wallet_index> >(); add_index< primary_index<son_wallet_index> >();
@ -353,6 +394,7 @@ void database::initialize_indexes()
add_index< primary_index<lottery_balance_index > >(); add_index< primary_index<lottery_balance_index > >();
add_index< primary_index<sweeps_vesting_balance_index > >(); add_index< primary_index<sweeps_vesting_balance_index > >();
add_index< primary_index<offer_history_index > >();
add_index< primary_index<son_stats_index > >(); add_index< primary_index<son_stats_index > >();
} }

View file

@ -48,6 +48,7 @@
#include <graphene/chain/witness_object.hpp> #include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp> #include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_object.hpp> #include <graphene/chain/worker_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed #define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed
@ -577,7 +578,7 @@ void database::update_active_committee_members()
update_committee_member_total_votes( cm ); update_committee_member_total_votes( cm );
} }
} }
// Update committee authorities // Update committee authorities
if( !committee_members.empty() ) if( !committee_members.empty() )
{ {
@ -1195,8 +1196,6 @@ uint32_t database::get_gpos_current_subperiod()
const auto now = this->head_block_time(); const auto now = this->head_block_time();
auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch(); auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch();
FC_ASSERT(period_start <= now && now <= period_end);
// get in what sub period we are // get in what sub period we are
uint32_t current_subperiod = 0; uint32_t current_subperiod = 0;
std::list<uint32_t> period_list(number_of_subperiods); std::list<uint32_t> period_list(number_of_subperiods);
@ -1230,13 +1229,13 @@ double database::calculate_vesting_factor(const account_object& stake_account)
// variables needed // variables needed
const auto number_of_subperiods = vesting_period / vesting_subperiod; const auto number_of_subperiods = vesting_period / vesting_subperiod;
double vesting_factor; double vesting_factor;
// get in what sub period we are // get in what sub period we are
uint32_t current_subperiod = get_gpos_current_subperiod(); uint32_t current_subperiod = get_gpos_current_subperiod();
if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0; if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0;
// On starting new vesting period, all votes become zero until someone votes, To avoid a situation of zero votes, // On starting new vesting period, all votes become zero until someone votes, To avoid a situation of zero votes,
// changes were done to roll in GPOS rules, the vesting factor will be 1 for whoever votes in 6th sub-period of last vesting period // changes were done to roll in GPOS rules, the vesting factor will be 1 for whoever votes in 6th sub-period of last vesting period
// BLOCKBACK-174 fix // BLOCKBACK-174 fix
if(current_subperiod == 1 && this->head_block_time() >= HARDFORK_GPOS_TIME + vesting_period) //Applicable only from 2nd vesting period if(current_subperiod == 1 && this->head_block_time() >= HARDFORK_GPOS_TIME + vesting_period) //Applicable only from 2nd vesting period
@ -1318,13 +1317,22 @@ void rolling_period_start(database& db)
if(now.sec_since_epoch() >= (period_start + vesting_period)) if(now.sec_since_epoch() >= (period_start + vesting_period))
{ {
// roll // roll
db.modify(db.get_global_properties(), [now](global_property_object& p) { db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
p.parameters.extensions.value.gpos_period_start = now.sec_since_epoch(); p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
}); });
} }
} }
} }
void clear_expired_custom_account_authorities(database& db)
{
const auto& cindex = db.get_index_type<custom_account_authority_index>().indices().get<by_expiration>();
while(!cindex.empty() && cindex.begin()->valid_to < db.head_block_time())
{
db.remove(*cindex.begin());
}
}
// Schedules payouts from a dividend distribution account to the current holders of the // 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 // 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 // since the last time it was called, and distributes them to the current owners of the
@ -2093,7 +2101,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
} }
} }
} tally_helper(*this, gpo); } tally_helper(*this, gpo);
perform_account_maintenance( tally_helper ); perform_account_maintenance( tally_helper );
struct clear_canary { struct clear_canary {
clear_canary(vector<uint64_t>& target): target(target){} clear_canary(vector<uint64_t>& target): target(target){}
@ -2139,6 +2147,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() ) if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() )
p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod; p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod;
if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() ) 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;
p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period; p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period;
if( !p.pending_parameters->extensions.value.son_vesting_amount.valid() ) if( !p.pending_parameters->extensions.value.son_vesting_amount.valid() )
p.pending_parameters->extensions.value.son_vesting_amount = p.parameters.extensions.value.son_vesting_amount; p.pending_parameters->extensions.value.son_vesting_amount = p.parameters.extensions.value.son_vesting_amount;
@ -2203,7 +2212,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() ) //for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() ) for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; }); 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 // process_budget needs to run at the bottom because
// it needs to know the next_maintenance_time // it needs to know the next_maintenance_time
process_budget(); process_budget();

View file

@ -40,6 +40,7 @@
#include <graphene/chain/vesting_balance_object.hpp> #include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/transaction_object.hpp> #include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/impacted.hpp> #include <graphene/chain/impacted.hpp>
#include <graphene/chain/hardfork.hpp>
using namespace fc; using namespace fc;
@ -49,8 +50,13 @@ using namespace graphene::chain;
struct get_impacted_account_visitor struct get_impacted_account_visitor
{ {
flat_set<account_id_type>& _impacted; flat_set<account_id_type>& _impacted;
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {} bool _ignore_custom_op_reqd_auths;
typedef void result_type;
get_impacted_account_visitor( flat_set<account_id_type>& impact, bool ignore_custom_operation_required_auths )
: _impacted( impact ), _ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
using result_type = void;
void operator()( const transfer_operation& op ) void operator()( const transfer_operation& op )
{ {
@ -136,7 +142,7 @@ struct get_impacted_account_visitor
{ {
vector<authority> other; vector<authority> other;
for( const auto& proposed_op : op.proposed_ops ) for( const auto& proposed_op : op.proposed_ops )
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other ); operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other, _ignore_custom_op_reqd_auths );
for( auto& o : other ) for( auto& o : other )
add_authority_accounts( _impacted, o ); add_authority_accounts( _impacted, o );
} }
@ -293,6 +299,57 @@ struct get_impacted_account_visitor
void operator()( const sweeps_vesting_claim_operation& op ) { void operator()( const sweeps_vesting_claim_operation& op ) {
_impacted.insert( op.account ); _impacted.insert( op.account );
} }
void operator()( const custom_permission_create_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_permission_update_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_permission_delete_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_create_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_update_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const custom_account_authority_delete_operation& op ){
_impacted.insert( op.owner_account );
}
void operator()( const nft_metadata_create_operation& op ) {
_impacted.insert( op.owner );
}
void operator()( const nft_metadata_update_operation& op ) {
_impacted.insert( op.owner );
}
void operator()( const nft_mint_operation& op ) {
_impacted.insert( op.owner );
}
void operator()( const nft_safe_transfer_from_operation& op ) {
_impacted.insert( op.from );
_impacted.insert( op.to );
}
void operator()( const nft_approve_operation& op ) {
_impacted.insert( op.operator_ );
_impacted.insert( op.approved );
}
void operator()( const nft_set_approval_for_all_operation& op ) {
_impacted.insert( op.owner );
_impacted.insert( op.operator_ );
}
void operator()( const offer_operation& op ) {
_impacted.insert( op.issuer );
}
void operator()( const bid_operation& op ) {
_impacted.insert( op.bidder );
}
void operator()( const cancel_offer_operation& op ) {
_impacted.insert( op.issuer );
}
void operator()( const finalize_offer_operation& op ) {
_impacted.insert( op.fee_paying_account );
}
void operator()( const son_create_operation& op ) { void operator()( const son_create_operation& op ) {
_impacted.insert( op.owner_account ); _impacted.insert( op.owner_account );
} }
@ -352,20 +409,17 @@ struct get_impacted_account_visitor
} }
}; };
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result ) void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
{ get_impacted_account_visitor vtor = get_impacted_account_visitor( result, ignore_custom_operation_required_auths );
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
op.visit( vtor ); op.visit( vtor );
} }
void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result ) void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
{
for( const auto& op : tx.operations ) for( const auto& op : tx.operations )
operation_get_impacted_accounts( op, result ); operation_get_impacted_accounts( op, result, ignore_custom_operation_required_auths );
} }
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts ) void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts, bool ignore_custom_operation_required_auths ) {
{
if( obj->id.space() == protocol_ids ) if( obj->id.space() == protocol_ids )
{ {
switch( (object_type)obj->id.type() ) switch( (object_type)obj->id.type() )
@ -412,12 +466,14 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case proposal_object_type:{ } case proposal_object_type:{
const auto& aobj = dynamic_cast<const proposal_object*>(obj); const auto& aobj = dynamic_cast<const proposal_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts ); transaction_get_impacted_accounts( aobj->proposed_transaction, accounts,
ignore_custom_operation_required_auths);
break; break;
} case operation_history_object_type:{ } case operation_history_object_type:{
const auto& aobj = dynamic_cast<const operation_history_object*>(obj); const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
operation_get_impacted_accounts( aobj->op, accounts ); operation_get_impacted_accounts( aobj->op, accounts,
ignore_custom_operation_required_auths);
break; break;
} case withdraw_permission_object_type:{ } case withdraw_permission_object_type:{
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj); const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
@ -486,7 +542,8 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case impl_transaction_object_type:{ } case impl_transaction_object_type:{
const auto& aobj = dynamic_cast<const transaction_object*>(obj); const auto& aobj = dynamic_cast<const transaction_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->trx, accounts ); transaction_get_impacted_accounts( aobj->trx, accounts,
ignore_custom_operation_required_auths);
break; break;
} case impl_blinded_balance_object_type:{ } case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj); const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
@ -531,6 +588,7 @@ void database::notify_changed_objects()
if( _undo_db.enabled() ) if( _undo_db.enabled() )
{ {
const auto& head_undo = _undo_db.head(); const auto& head_undo = _undo_db.head();
auto chain_time = head_block_time();
// New // New
if( !new_objects.empty() ) if( !new_objects.empty() )
@ -542,7 +600,8 @@ void database::notify_changed_objects()
new_ids.push_back(item); new_ids.push_back(item);
auto obj = find_object(item); auto obj = find_object(item);
if(obj != nullptr) if(obj != nullptr)
get_relevant_accounts(obj, new_accounts_impacted); get_relevant_accounts(obj, new_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted)
@ -556,7 +615,8 @@ void database::notify_changed_objects()
for( const auto& item : head_undo.old_values ) for( const auto& item : head_undo.old_values )
{ {
changed_ids.push_back(item.first); changed_ids.push_back(item.first);
get_relevant_accounts(item.second.get(), changed_accounts_impacted); get_relevant_accounts(item.second.get(), changed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted)
@ -573,7 +633,8 @@ void database::notify_changed_objects()
removed_ids.emplace_back( item.first ); removed_ids.emplace_back( item.first );
auto obj = item.second.get(); auto obj = item.second.get();
removed.emplace_back( obj ); removed.emplace_back( obj );
get_relevant_accounts(obj, removed_accounts_impacted); get_relevant_accounts(obj, removed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted)

View file

@ -706,6 +706,31 @@ void database::update_betting_markets(fc::time_point_sec current_block_time)
remove_completed_events(); remove_completed_events();
} }
void database::finalize_expired_offers(){
try {
detail::with_skip_flags( *this,
get_node_properties().skip_flags | skip_authority_check, [&](){
transaction_evaluation_state cancel_context(this);
//Cancel expired limit orders
auto& limit_index = get_index_type<offer_index>().indices().get<by_expiration_date>();
auto itr = limit_index.begin();
while( itr != limit_index.end() && itr->offer_expiration_date <= head_block_time() )
{
const offer_object& offer = *itr;
++itr;
finalize_offer_operation finalize;
finalize.fee_paying_account = offer.issuer;
finalize.offer_id = offer.id;
finalize.fee = asset( 0, asset_id_type() );
finalize.result = offer.bidder ? result_type::Expired : result_type::ExpiredNoBid;
cancel_context.skip_fee_schedule_check = true;
apply_operation(cancel_context, finalize);
}
});
} FC_CAPTURE_AND_RETHROW()}
void database::remove_son_proposal( const proposal_object& proposal ) void database::remove_son_proposal( const proposal_object& proposal )
{ try { { try {
if( proposal.proposed_transaction.operations.size() == 1 && if( proposal.proposed_transaction.operations.size() == 1 &&

View file

@ -0,0 +1,4 @@
// 5050_1 HARDFORK Thursday, 16 April 2020 19:00:00 GMT
#ifndef HARDFORK_5050_1_TIME
#define HARDFORK_5050_1_TIME (fc::time_point_sec( 1587063600 ))
#endif

View file

@ -0,0 +1,6 @@
// #210 Check authorities on custom_operation
#ifndef HARDFORK_CORE_210_TIME
#define HARDFORK_CORE_210_TIME (fc::time_point_sec(1893456000)) // Jan 1 00:00:00 2030 (Not yet scheduled)
// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored.
#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME)
#endif

View file

@ -1,5 +1,4 @@
// GPOS HARDFORK Monday, 31 Dec 2019 00:00:00 GMT - 1577750400 // GPOS HARDFORK Monday, 6 January 2020 01:00:00 GMT
// GPOS HARDFORK Monday, March 30, 2020 2:00:00 PM - 1585569600
#ifndef HARDFORK_GPOS_TIME #ifndef HARDFORK_GPOS_TIME
#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1585569600 )) #define HARDFORK_GPOS_TIME (fc::time_point_sec( 1578272400 ))
#endif #endif

View file

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

View file

@ -134,6 +134,7 @@ namespace graphene { namespace chain {
optional<lottery_asset_options> lottery_options; optional<lottery_asset_options> lottery_options;
time_point_sec get_lottery_expiration() const; time_point_sec get_lottery_expiration() const;
vector<account_id_type> get_holders( database& db ) const; vector<account_id_type> get_holders( database& db ) const;
vector<uint64_t> get_ticket_ids( database& db ) const;
void distribute_benefactors_part( database& db ); void distribute_benefactors_part( database& db );
map< account_id_type, vector< uint16_t > > distribute_winners_part( database& db ); map< account_id_type, vector< uint16_t > > distribute_winners_part( database& db );
void distribute_sweeps_holders_part( database& db ); void distribute_sweeps_holders_part( database& db );

View file

@ -244,3 +244,13 @@
#define GPOS_PERIOD (60*60*24*30*6) // 6 months #define GPOS_PERIOD (60*60*24*30*6) // 6 months
#define GPOS_SUBPERIOD (60*60*24*30) // 1 month #define GPOS_SUBPERIOD (60*60*24*30) // 1 month
#define GPOS_VESTING_LOCKIN_PERIOD (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
#define RBAC_MAX_PERMISSIONS_PER_ACCOUNT 5 // 5 per account
#define RBAC_MAX_ACCOUNT_AUTHORITY_LIFETIME 180*24*60*60 // 6 months
#define RBAC_MAX_AUTHS_PER_PERMISSION 15 // 15 ops linked per permission
#define NFT_TOKEN_MIN_LENGTH 3
#define NFT_TOKEN_MAX_LENGTH 15
#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH

View file

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

View file

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

View file

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

View file

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

View file

@ -301,6 +301,7 @@ namespace graphene { namespace chain {
const std::vector<uint32_t> get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const; const std::vector<uint32_t> get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const;
std::vector<uint32_t> get_seeds( asset_id_type for_asset, uint8_t count_winners )const; std::vector<uint32_t> get_seeds( asset_id_type for_asset, uint8_t count_winners )const;
uint64_t get_random_bits( uint64_t bound ); uint64_t get_random_bits( uint64_t bound );
bool item_locked(const nft_id_type& item)const;
std::set<son_id_type> get_sons_being_deregistered(); std::set<son_id_type> get_sons_being_deregistered();
std::set<son_id_type> get_sons_being_reported_down(); std::set<son_id_type> get_sons_being_reported_down();
std::set<son_id_type> get_sons_to_be_deregistered(); std::set<son_id_type> get_sons_to_be_deregistered();
@ -321,6 +322,7 @@ namespace graphene { namespace chain {
uint32_t last_non_undoable_block_num() const; uint32_t last_non_undoable_block_num() const;
vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const;
//////////////////// db_init.cpp //////////////////// //////////////////// db_init.cpp ////////////////////
void initialize_evaluators(); void initialize_evaluators();
@ -546,6 +548,7 @@ namespace graphene { namespace chain {
void update_betting_markets(fc::time_point_sec current_block_time); void update_betting_markets(fc::time_point_sec current_block_time);
bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true,
const asset_bitasset_data_object* bitasset_ptr = nullptr ); const asset_bitasset_data_object* bitasset_ptr = nullptr );
void finalize_expired_offers();
///Steps performed only at maintenance intervals ///Steps performed only at maintenance intervals
///@{ ///@{

View file

@ -30,13 +30,12 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
void operation_get_impacted_accounts( void operation_get_impacted_accounts( const graphene::chain::operation& op,
const graphene::chain::operation& op, fc::flat_set<graphene::chain::account_id_type>& result,
fc::flat_set<graphene::chain::account_id_type>& result ); bool ignore_custom_operation_required_auths );
void transaction_get_impacted_accounts( void transaction_get_impacted_accounts( const graphene::chain::transaction& tx,
const graphene::chain::transaction& tx, fc::flat_set<graphene::chain::account_id_type>& result,
fc::flat_set<graphene::chain::account_id_type>& result bool ignore_custom_operation_required_auths );
);
} } // graphene::app } } // graphene::app

View file

@ -0,0 +1,59 @@
#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 nft_metadata_create_evaluator : public evaluator<nft_metadata_create_evaluator>
{
public:
typedef nft_metadata_create_operation operation_type;
void_result do_evaluate( const nft_metadata_create_operation& o );
object_id_type do_apply( const nft_metadata_create_operation& o );
};
class nft_metadata_update_evaluator : public evaluator<nft_metadata_update_evaluator>
{
public:
typedef nft_metadata_update_operation operation_type;
void_result do_evaluate( const nft_metadata_update_operation& o );
void_result do_apply( const nft_metadata_update_operation& o );
};
class nft_mint_evaluator : public evaluator<nft_mint_evaluator>
{
public:
typedef nft_mint_operation operation_type;
void_result do_evaluate( const nft_mint_operation& o );
object_id_type do_apply( const nft_mint_operation& o );
};
class nft_safe_transfer_from_evaluator : public evaluator<nft_safe_transfer_from_evaluator>
{
public:
typedef nft_safe_transfer_from_operation operation_type;
void_result do_evaluate( const nft_safe_transfer_from_operation& o );
object_id_type do_apply( const nft_safe_transfer_from_operation& o );
};
class nft_approve_evaluator : public evaluator<nft_approve_evaluator>
{
public:
typedef nft_approve_operation operation_type;
void_result do_evaluate( const nft_approve_operation& o );
object_id_type do_apply( const nft_approve_operation& o );
};
class nft_set_approval_for_all_evaluator : public evaluator<nft_set_approval_for_all_evaluator>
{
public:
typedef nft_set_approval_for_all_operation operation_type;
void_result do_evaluate( const nft_set_approval_for_all_operation& o );
void_result do_apply( const nft_set_approval_for_all_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,106 @@
#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 nft_metadata_object : public abstract_object<nft_metadata_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = nft_metadata_type;
account_id_type owner;
std::string name;
std::string symbol;
std::string base_uri;
optional<account_id_type> revenue_partner;
optional<uint16_t> revenue_split;
bool is_transferable = false;
bool is_sellable = true;
};
class nft_object : public abstract_object<nft_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = nft_object_type;
nft_metadata_id_type nft_metadata_id;
account_id_type owner;
account_id_type approved;
vector<account_id_type> approved_operators;
std::string token_uri;
};
struct by_name;
struct by_symbol;
using nft_metadata_multi_index_type = multi_index_container<
nft_metadata_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_unique< tag<by_name>,
member<nft_metadata_object, std::string, &nft_metadata_object::name>
>,
ordered_unique< tag<by_symbol>,
member<nft_metadata_object, std::string, &nft_metadata_object::symbol>
>
>
>;
using nft_metadata_index = generic_index<nft_metadata_object, nft_metadata_multi_index_type>;
struct by_metadata;
struct by_metadata_and_owner;
struct by_owner;
struct by_owner_and_id;
using nft_multi_index_type = multi_index_container<
nft_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_non_unique< tag<by_metadata>,
member<nft_object, nft_metadata_id_type, &nft_object::nft_metadata_id>
>,
ordered_non_unique< tag<by_metadata_and_owner>,
composite_key<nft_object,
member<nft_object, nft_metadata_id_type, &nft_object::nft_metadata_id>,
member<nft_object, account_id_type, &nft_object::owner>
>
>,
ordered_non_unique< tag<by_owner>,
member<nft_object, account_id_type, &nft_object::owner>
>,
ordered_unique< tag<by_owner_and_id>,
composite_key<nft_object,
member<nft_object, account_id_type, &nft_object::owner>,
member<object, object_id_type, &object::id>
>
>
>
>;
using nft_index = generic_index<nft_object, nft_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object),
(owner)
(name)
(symbol)
(base_uri)
(revenue_partner)
(revenue_split)
(is_transferable)
(is_sellable) )
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
(nft_metadata_id)
(owner)
(approved)
(approved_operators)
(token_uri) )

View file

@ -0,0 +1,47 @@
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene
{
namespace chain
{
class offer_evaluator : public evaluator<offer_evaluator>
{
public:
typedef offer_operation operation_type;
void_result do_evaluate(const offer_operation &o);
object_id_type do_apply(const offer_operation &o);
};
class bid_evaluator : public evaluator<bid_evaluator>
{
public:
typedef bid_operation operation_type;
void_result do_evaluate(const bid_operation &o);
void_result do_apply(const bid_operation &o);
};
class cancel_offer_evaluator : public evaluator<cancel_offer_evaluator>
{
public:
typedef cancel_offer_operation operation_type;
void_result do_evaluate(const cancel_offer_operation &o);
void_result do_apply(const cancel_offer_operation &o);
};
class finalize_offer_evaluator : public evaluator<finalize_offer_evaluator>
{
public:
typedef finalize_offer_operation operation_type;
void_result do_evaluate(const finalize_offer_operation &op);
void_result do_apply(const finalize_offer_operation &op);
};
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,109 @@
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene
{
namespace chain
{
class database;
struct by_expiration_date
{
};
class offer_object : public graphene::db::abstract_object<offer_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = offer_object_type;
account_id_type issuer;
set<nft_id_type> item_ids;
optional<account_id_type> bidder;
optional<asset> bid_price;
asset minimum_price;
asset maximum_price;
bool buying_item;
fc::time_point_sec offer_expiration_date;
offer_id_type get_id() const { return id; }
};
class offer_history_object
: public graphene::db::abstract_object<offer_history_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_offer_history_object_type;
account_id_type issuer;
set<nft_id_type> item_ids;
optional<account_id_type> bidder;
optional<asset> bid_price;
asset minimum_price;
asset maximum_price;
bool buying_item;
fc::time_point_sec offer_expiration_date;
result_type result;
offer_history_id_type get_id() const { return id; }
};
class offer_item_index : public secondary_index
{
public:
virtual void object_inserted(const object &obj) override;
virtual void object_removed(const object &obj) override;
virtual void about_to_modify(const object &before) override{};
virtual void object_modified(const object &after) override;
set<nft_id_type> _locked_items;
};
struct compare_by_expiration_date
{
bool operator()(const fc::time_point_sec &o1,
const fc::time_point_sec &o2) const
{
return o1 < o2;
}
};
using offer_multi_index_type = multi_index_container<
offer_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id>>,
ordered_non_unique<tag<by_expiration_date>,
member<offer_object, fc::time_point_sec,
&offer_object::offer_expiration_date>,
compare_by_expiration_date>>>;
using offer_history_multi_index_type = multi_index_container<
offer_history_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id>>,
ordered_non_unique<tag<by_expiration_date>,
member<offer_history_object, fc::time_point_sec,
&offer_history_object::offer_expiration_date>,
compare_by_expiration_date>>>;
using offer_index = generic_index<offer_object, offer_multi_index_type>;
using offer_history_index =
generic_index<offer_history_object, offer_history_multi_index_type>;
} // namespace chain
} // namespace graphene
FC_REFLECT_DERIVED(graphene::chain::offer_object, (graphene::db::object),
(issuer)(item_ids)(bidder)(bid_price)(minimum_price)(
maximum_price)(buying_item)(offer_expiration_date))
FC_REFLECT_DERIVED(graphene::chain::offer_history_object,
(graphene::db::object),
(issuer)(item_ids)(bidder)(bid_price)(minimum_price)(
maximum_price)(buying_item)(offer_expiration_date)(result))

View file

@ -49,6 +49,10 @@ namespace graphene { namespace chain {
optional < uint32_t > gpos_subperiod = GPOS_SUBPERIOD; optional < uint32_t > gpos_subperiod = GPOS_SUBPERIOD;
optional < uint32_t > gpos_period_start = HARDFORK_GPOS_TIME.sec_since_epoch(); optional < uint32_t > gpos_period_start = HARDFORK_GPOS_TIME.sec_since_epoch();
optional < uint32_t > gpos_vesting_lockin_period = GPOS_VESTING_LOCKIN_PERIOD; optional < uint32_t > gpos_vesting_lockin_period = GPOS_VESTING_LOCKIN_PERIOD;
/* rbac parameters */
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;
optional < uint32_t > son_vesting_amount = SON_VESTING_AMOUNT; optional < uint32_t > son_vesting_amount = SON_VESTING_AMOUNT;
optional < uint32_t > son_vesting_period = SON_VESTING_PERIOD; optional < uint32_t > son_vesting_period = SON_VESTING_PERIOD;
@ -177,6 +181,15 @@ namespace graphene { namespace chain {
inline uint32_t gpos_vesting_lockin_period()const { inline uint32_t gpos_vesting_lockin_period()const {
return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period return extensions.value.gpos_vesting_lockin_period.valid() ? *extensions.value.gpos_vesting_lockin_period : GPOS_VESTING_LOCKIN_PERIOD; /// GPOS vesting lockin period
} }
inline uint16_t rbac_max_permissions_per_account()const {
return extensions.value.rbac_max_permissions_per_account.valid() ? *extensions.value.rbac_max_permissions_per_account : RBAC_MAX_PERMISSIONS_PER_ACCOUNT;
}
inline uint32_t rbac_max_account_authority_lifetime()const {
return extensions.value.rbac_max_account_authority_lifetime.valid() ? *extensions.value.rbac_max_account_authority_lifetime : RBAC_MAX_ACCOUNT_AUTHORITY_LIFETIME;
}
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 account_id_type son_account() const { inline account_id_type son_account() const {
return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT; return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT;
} }
@ -200,6 +213,9 @@ FC_REFLECT( graphene::chain::parameter_extension,
(gpos_subperiod) (gpos_subperiod)
(gpos_period_start) (gpos_period_start)
(gpos_vesting_lockin_period) (gpos_vesting_lockin_period)
(rbac_max_permissions_per_account)
(rbac_max_account_authority_lifetime)
(rbac_max_authorities_per_permission)
(son_vesting_amount) (son_vesting_amount)
(son_vesting_period) (son_vesting_period)
(son_pay_max) (son_pay_max)

View file

@ -50,6 +50,9 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return payer; } account_id_type fee_payer()const { return payer; }
void validate()const; void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const; share_type calculate_fee(const fee_parameters_type& k)const;
void get_required_active_authorities( flat_set<account_id_type>& auths )const {
auths.insert( required_auths.begin(), required_auths.end() );
}
}; };
} } // namespace graphene::chain } } // namespace graphene::chain

View file

@ -0,0 +1,73 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene
{
namespace chain
{
struct custom_account_authority_create_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
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;
};
struct custom_account_authority_update_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
custom_account_authority_id_type auth_id;
optional<time_point_sec> new_valid_from;
optional<time_point_sec> new_valid_to;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; }
};
struct custom_account_authority_delete_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
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 k.fee; }
};
} // namespace chain
} // 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_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account))

View file

@ -0,0 +1,70 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene
{
namespace chain
{
struct custom_permission_create_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
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;
};
struct custom_permission_update_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
custom_permission_id_type permission_id;
optional<authority> new_auth;
account_id_type owner_account;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; }
};
struct custom_permission_delete_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
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 k.fee; }
};
} // namespace chain
} // 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_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account))
FC_REFLECT(graphene::chain::custom_permission_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account))

View file

@ -52,6 +52,8 @@ namespace graphene { namespace chain {
share_type calculate_fee( const fee_parameters_type& k )const; share_type calculate_fee( const fee_parameters_type& k )const;
}; };
typedef static_variant<uint64_t, void_t> ticket_num;
/** /**
* @ingroup operations * @ingroup operations
*/ */
@ -73,7 +75,7 @@ namespace graphene { namespace chain {
// true if recieved from benefators section of lottery; false otherwise // true if recieved from benefators section of lottery; false otherwise
bool is_benefactor_reward; bool is_benefactor_reward;
extensions_type extensions; ticket_num winner_ticket_id;
account_id_type fee_payer()const { return account_id_type(); } account_id_type fee_payer()const { return account_id_type(); }
void validate()const {}; void validate()const {};
@ -114,7 +116,7 @@ FC_REFLECT( graphene::chain::ticket_purchase_operation,
) )
FC_REFLECT( graphene::chain::ticket_purchase_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::ticket_purchase_operation::fee_parameters_type, (fee) )
FC_REFLECT_TYPENAME( graphene::chain::ticket_num )
FC_REFLECT( graphene::chain::lottery_reward_operation, FC_REFLECT( graphene::chain::lottery_reward_operation,
(fee) (fee)
(lottery) (lottery)
@ -122,7 +124,7 @@ FC_REFLECT( graphene::chain::lottery_reward_operation,
(amount) (amount)
(win_percentage) (win_percentage)
(is_benefactor_reward) (is_benefactor_reward)
(extensions) (winner_ticket_id)
) )
FC_REFLECT( graphene::chain::lottery_reward_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::lottery_reward_operation::fee_parameters_type, (fee) )

View file

@ -0,0 +1,135 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
struct nft_metadata_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 symbol;
std::string base_uri;
optional<account_id_type> revenue_partner;
optional<uint16_t> revenue_split;
bool is_transferable = false;
bool is_sellable = true;
account_id_type fee_payer()const { return owner; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_metadata_update_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type owner;
nft_metadata_id_type nft_metadata_id;
optional<std::string> name;
optional<std::string> symbol;
optional<std::string> base_uri;
optional<account_id_type> revenue_partner;
optional<uint16_t> revenue_split;
optional<bool> is_transferable;
optional<bool> is_sellable;
account_id_type fee_payer()const { return owner; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_mint_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 payer;
nft_metadata_id_type nft_metadata_id;
account_id_type owner;
account_id_type approved;
vector<account_id_type> approved_operators;
std::string token_uri;
account_id_type fee_payer()const { return payer; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_safe_transfer_from_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 operator_;
account_id_type from;
account_id_type to;
nft_id_type token_id;
string data;
account_id_type fee_payer()const { return operator_; }
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_approve_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type operator_;
account_id_type approved;
nft_id_type token_id;
account_id_type fee_payer()const { return operator_; }
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_set_approval_for_all_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type owner;
account_id_type operator_;
bool approved;
account_id_type fee_payer()const { return owner; }
share_type calculate_fee(const fee_parameters_type &k) const;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::nft_metadata_create_operation::fee_parameters_type, (fee) (price_per_kbyte) )
FC_REFLECT( graphene::chain::nft_metadata_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_mint_operation::fee_parameters_type, (fee) (price_per_kbyte) )
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_type, (fee) (price_per_kbyte) )
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) )

View file

@ -0,0 +1,143 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/memo.hpp>
namespace graphene
{
namespace chain
{
/*
* @class offer_operation
* @brief To place an offer to buy or sell an item, a user broadcasts a
* proposed transaction
* @ingroup operations
* operation
*/
struct offer_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; /// only required for large memos.
};
asset fee;
set<nft_id_type> item_ids;
// /**
// * minimum_price.asset_id == maximum_price.asset_id.
// * to set fixed price without auction minimum_price == maximum_price
// * If buying_item is true, and minimum_price != maximum_price, the user is
// proposing a “reverse auction”
// * where bidders can offer to sell the item for progressively lower prices.
// * In this case, minimum_price functions as the sell-it-now price for the
// reverse auction
// */
account_id_type issuer;
/// minimum_price is minimum bid price. 0 if no minimum_price required
asset minimum_price;
/// buy_it_now price. 0 if no maximum price
asset maximum_price;
/// true means user wants to buy item, false mean user is selling item
bool buying_item;
/// not transaction expiration date
fc::time_point_sec offer_expiration_date;
/// User provided data encrypted to the memo key of the "to" account
optional<memo_data> memo;
extensions_type extensions;
account_id_type fee_payer() const { return issuer; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct bid_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type bidder;
asset bid_price;
offer_id_type offer_id;
extensions_type extensions;
account_id_type fee_payer() const { return bidder; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct cancel_offer_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type issuer;
offer_id_type offer_id;
extensions_type extensions;
account_id_type fee_payer() const { return issuer; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
enum class result_type
{
Expired = 0,
ExpiredNoBid = 1,
Cancelled = 2
};
struct finalize_offer_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = 0;
};
asset fee;
account_id_type fee_paying_account;
offer_id_type offer_id;
result_type result;
extensions_type extensions;
account_id_type fee_payer() const { return fee_paying_account; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
} // namespace chain
} // namespace graphene
FC_REFLECT(graphene::chain::offer_operation::fee_parameters_type,
(fee)(price_per_kbyte));
FC_REFLECT(graphene::chain::offer_operation,
(fee)(item_ids)(issuer)(minimum_price)(maximum_price)(buying_item)(offer_expiration_date)(memo)(extensions));
FC_REFLECT(graphene::chain::bid_operation::fee_parameters_type,
(fee));
FC_REFLECT(graphene::chain::bid_operation,
(fee)(bidder)(bid_price)(offer_id)(extensions));
FC_REFLECT(graphene::chain::cancel_offer_operation::fee_parameters_type,
(fee));
FC_REFLECT(graphene::chain::cancel_offer_operation,
(fee)(issuer)(offer_id)(extensions));
FC_REFLECT_ENUM(graphene::chain::result_type, (Expired)(ExpiredNoBid)(Cancelled));
FC_REFLECT(graphene::chain::finalize_offer_operation::fee_parameters_type,
(fee));
FC_REFLECT(graphene::chain::finalize_offer_operation,
(fee)(fee_paying_account)(offer_id)(result)(extensions));

View file

@ -45,6 +45,10 @@
#include <graphene/chain/protocol/event.hpp> #include <graphene/chain/protocol/event.hpp>
#include <graphene/chain/protocol/betting_market.hpp> #include <graphene/chain/protocol/betting_market.hpp>
#include <graphene/chain/protocol/tournament.hpp> #include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/protocol/custom_permission.hpp>
#include <graphene/chain/protocol/custom_account_authority.hpp>
#include <graphene/chain/protocol/offer.hpp>
#include <graphene/chain/protocol/nft_ops.hpp>
#include <graphene/chain/protocol/son.hpp> #include <graphene/chain/protocol/son.hpp>
#include <graphene/chain/protocol/sidechain_address.hpp> #include <graphene/chain/protocol/sidechain_address.hpp>
#include <graphene/chain/protocol/son_wallet.hpp> #include <graphene/chain/protocol/son_wallet.hpp>
@ -142,6 +146,22 @@ namespace graphene { namespace chain {
lottery_reward_operation, lottery_reward_operation,
lottery_end_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,
offer_operation,
bid_operation,
cancel_offer_operation,
finalize_offer_operation,
nft_metadata_create_operation,
nft_metadata_update_operation,
nft_mint_operation,
nft_safe_transfer_from_operation,
nft_approve_operation,
nft_set_approval_for_all_operation,
son_create_operation, son_create_operation,
son_update_operation, son_update_operation,
son_deregister_operation, son_deregister_operation,
@ -171,10 +191,11 @@ namespace graphene { namespace chain {
* *
* @return a set of required authorities for @ref op * @return a set of required authorities for @ref op
*/ */
void operation_get_required_authorities( const operation& op, void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active, flat_set<account_id_type>& active,
flat_set<account_id_type>& owner, flat_set<account_id_type>& owner,
vector<authority>& other ); vector<authority>& other,
bool ignore_custom_operation_required_auths );
void operation_validate( const operation& op ); void operation_validate( const operation& op );

View file

@ -112,7 +112,10 @@ namespace graphene { namespace chain {
return results; return results;
} }
void get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const; void get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const;
}; };
/** /**
@ -141,6 +144,8 @@ namespace graphene { namespace chain {
const flat_set<public_key_type>& available_keys, const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const; )const;
@ -148,6 +153,8 @@ namespace graphene { namespace chain {
const chain_id_type& chain_id, const chain_id_type& chain_id,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/** /**
@ -156,12 +163,13 @@ namespace graphene { namespace chain {
* some cases where get_required_signatures() returns a * some cases where get_required_signatures() returns a
* non-minimal set. * non-minimal set.
*/ */
set<public_key_type> minimize_required_signatures( set<public_key_type> minimize_required_signatures(
const chain_id_type& chain_id, const chain_id_type& chain_id,
const flat_set<public_key_type>& available_keys, const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const; ) const;
@ -194,10 +202,12 @@ namespace graphene { namespace chain {
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs, void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false, bool allow_committee = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(), const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),
const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>()); const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>() );
/** /**
* @brief captures the result of evaluating the operations contained in the transaction * @brief captures the result of evaluating the operations contained in the transaction

View file

@ -171,6 +171,11 @@ namespace graphene { namespace chain {
betting_market_group_object_type, betting_market_group_object_type,
betting_market_object_type, betting_market_object_type,
bet_object_type, bet_object_type,
custom_permission_object_type,
custom_account_authority_object_type,
offer_object_type,
nft_metadata_type,
nft_object_type,
son_object_type, son_object_type,
son_proposal_object_type, son_proposal_object_type,
son_wallet_object_type, son_wallet_object_type,
@ -207,6 +212,7 @@ namespace graphene { namespace chain {
impl_global_betting_statistics_object_type, impl_global_betting_statistics_object_type,
impl_lottery_balance_object_type, impl_lottery_balance_object_type,
impl_sweeps_vesting_balance_object_type, impl_sweeps_vesting_balance_object_type,
impl_offer_history_object_type,
impl_son_statistics_object_type, impl_son_statistics_object_type,
impl_son_schedule_object_type impl_son_schedule_object_type
}; };
@ -239,6 +245,11 @@ namespace graphene { namespace chain {
class betting_market_group_object; class betting_market_group_object;
class betting_market_object; class betting_market_object;
class bet_object; class bet_object;
class custom_permission_object;
class custom_account_authority_object;
class offer_object;
class nft_metadata_object;
class nft_object;
class son_object; class son_object;
class son_proposal_object; class son_proposal_object;
class son_wallet_object; class son_wallet_object;
@ -272,6 +283,11 @@ 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_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, 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, bet_object_type, bet_object> bet_id_type;
typedef object_id< protocol_ids, custom_permission_object_type, custom_permission_object> custom_permission_id_type;
typedef object_id< protocol_ids, custom_account_authority_object_type, custom_account_authority_object> custom_account_authority_id_type;
typedef object_id< protocol_ids, offer_object_type, offer_object> offer_id_type;
typedef object_id< protocol_ids, nft_metadata_type, nft_metadata_object> nft_metadata_id_type;
typedef object_id< protocol_ids, nft_object_type, nft_object> nft_id_type;
typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; typedef object_id< protocol_ids, son_object_type, son_object> son_id_type;
typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type;
typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type; typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type;
@ -302,6 +318,7 @@ namespace graphene { namespace chain {
class global_betting_statistics_object; class global_betting_statistics_object;
class lottery_balance_object; class lottery_balance_object;
class sweeps_vesting_balance_object; class sweeps_vesting_balance_object;
class offer_history_object;
class son_statistics_object; class son_statistics_object;
class son_schedule_object; class son_schedule_object;
@ -332,6 +349,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_global_betting_statistics_object_type, global_betting_statistics_object > global_betting_statistics_id_type; typedef object_id< implementation_ids, impl_global_betting_statistics_object_type, global_betting_statistics_object > global_betting_statistics_id_type;
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type; typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type; typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type;
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type; typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type; typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
@ -463,6 +481,11 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(betting_market_group_object_type) (betting_market_group_object_type)
(betting_market_object_type) (betting_market_object_type)
(bet_object_type) (bet_object_type)
(custom_permission_object_type)
(custom_account_authority_object_type)
(offer_object_type)
(nft_metadata_type)
(nft_object_type)
(son_object_type) (son_object_type)
(son_proposal_object_type) (son_proposal_object_type)
(son_wallet_object_type) (son_wallet_object_type)
@ -497,6 +520,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_global_betting_statistics_object_type) (impl_global_betting_statistics_object_type)
(impl_lottery_balance_object_type) (impl_lottery_balance_object_type)
(impl_sweeps_vesting_balance_object_type) (impl_sweeps_vesting_balance_object_type)
(impl_offer_history_object_type)
(impl_son_statistics_object_type) (impl_son_statistics_object_type)
(impl_son_schedule_object_type) (impl_son_schedule_object_type)
) )
@ -525,6 +549,7 @@ FC_REFLECT_TYPENAME( graphene::chain::betting_market_group_id_type )
FC_REFLECT_TYPENAME( graphene::chain::betting_market_id_type ) FC_REFLECT_TYPENAME( graphene::chain::betting_market_id_type )
FC_REFLECT_TYPENAME( graphene::chain::bet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::bet_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type )
FC_REFLECT_TYPENAME( graphene::chain::offer_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type ) FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type ) FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type ) FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
@ -541,6 +566,11 @@ FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_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::global_betting_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_permission_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type )
FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )

View file

@ -0,0 +1,238 @@
#include <graphene/chain/nft_evaluator.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_create_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(db());
const auto& idx_nft_md_by_name = db().get_index_type<nft_metadata_index>().indices().get<by_name>();
FC_ASSERT( idx_nft_md_by_name.find(op.name) == idx_nft_md_by_name.end(), "NFT name already in use" );
const auto& idx_nft_md_by_symbol = db().get_index_type<nft_metadata_index>().indices().get<by_symbol>();
FC_ASSERT( idx_nft_md_by_symbol.find(op.symbol) == idx_nft_md_by_symbol.end(), "NFT symbol already in use" );
FC_ASSERT((op.revenue_partner && op.revenue_split) || (!op.revenue_partner && !op.revenue_split), "NFT revenue partner info invalid");
if (op.revenue_partner) {
(*op.revenue_partner)(db());
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_create_operation& op )
{ try {
const auto& new_nft_metadata_object = db().create<nft_metadata_object>( [&]( nft_metadata_object& obj ){
obj.owner = op.owner;
obj.name = op.name;
obj.symbol = op.symbol;
obj.base_uri = op.base_uri;
obj.revenue_partner = op.revenue_partner;
obj.revenue_split = op.revenue_split;
obj.is_transferable = op.is_transferable;
obj.is_sellable = op.is_sellable;
});
return new_nft_metadata_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_metadata_update_evaluator::do_evaluate( const nft_metadata_update_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(db());
const auto& idx_nft_md = db().get_index_type<nft_metadata_index>().indices().get<by_id>();
auto itr_nft_md = idx_nft_md.find(op.nft_metadata_id);
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
FC_ASSERT( itr_nft_md->owner == op.owner, "Only owner can modify NFT metadata" );
const auto& idx_nft_md_by_name = db().get_index_type<nft_metadata_index>().indices().get<by_name>();
const auto& idx_nft_md_by_symbol = db().get_index_type<nft_metadata_index>().indices().get<by_symbol>();
if (op.name.valid())
FC_ASSERT((itr_nft_md->name != *op.name) && (idx_nft_md_by_name.find(*op.name) == idx_nft_md_by_name.end()), "NFT name already in use");
if (op.symbol.valid())
FC_ASSERT((itr_nft_md->symbol != *op.symbol) && (idx_nft_md_by_symbol.find(*op.symbol) == idx_nft_md_by_symbol.end()), "NFT symbol already in use");
FC_ASSERT((op.revenue_partner && op.revenue_split) || (!op.revenue_partner && !op.revenue_split), "NFT revenue partner info invalid");
if (op.revenue_partner) {
(*op.revenue_partner)(db());
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_metadata_update_evaluator::do_apply( const nft_metadata_update_operation& op )
{ try {
db().modify(db().get(op.nft_metadata_id), [&] ( nft_metadata_object& obj ) {
if( op.name.valid() )
obj.name = *op.name;
if( op.symbol.valid() )
obj.symbol = *op.symbol;
if( op.base_uri.valid() )
obj.base_uri = *op.base_uri;
if( op.revenue_partner.valid() )
obj.revenue_partner = op.revenue_partner;
if( op.revenue_split.valid() )
obj.revenue_split = op.revenue_split;
if( op.is_transferable.valid() )
obj.is_transferable = *op.is_transferable;
if( op.is_sellable.valid() )
obj.is_sellable = *op.is_sellable;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_mint_evaluator::do_evaluate( const nft_mint_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.payer(db());
op.owner(db());
op.approved(db());
for(const auto& op_iter: op.approved_operators) {
op_iter(db());
}
const auto& idx_nft_md = db().get_index_type<nft_metadata_index>().indices().get<by_id>();
auto itr_nft_md = idx_nft_md.find(op.nft_metadata_id);
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
FC_ASSERT( itr_nft_md->owner == op.payer, "Only metadata owner can mint NFT" );
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type nft_mint_evaluator::do_apply( const nft_mint_operation& op )
{ try {
const auto& new_nft_object = db().create<nft_object>( [&]( nft_object& obj ){
obj.nft_metadata_id = op.nft_metadata_id;
obj.owner = op.owner;
obj.approved = op.approved;
obj.approved_operators = op.approved_operators;
obj.token_uri = op.token_uri;
});
return new_nft_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_safe_transfer_from_evaluator::do_evaluate( const nft_safe_transfer_from_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto& idx_nft = db().get_index_type<nft_index>().indices().get<by_id>();
const auto& idx_acc = db().get_index_type<account_index>().indices().get<by_id>();
auto itr_nft = idx_nft.find(op.token_id);
FC_ASSERT( itr_nft != idx_nft.end(), "NFT does not exists" );
FC_ASSERT(!db().item_locked(op.token_id), "Item(s) is already on sale on market, transfer is not allowed");
auto itr_operator = idx_acc.find(op.operator_);
FC_ASSERT( itr_operator != idx_acc.end(), "Operator account does not exists" );
auto itr_owner = idx_acc.find(itr_nft->owner);
FC_ASSERT( itr_owner != idx_acc.end(), "Owner account does not exists" );
auto itr_from = idx_acc.find(op.from);
FC_ASSERT( itr_from != idx_acc.end(), "Sender account does not exists" );
FC_ASSERT( itr_from->id == itr_owner->id, "Sender account is not owner of this NFT" );
auto itr_to = idx_acc.find(op.to);
FC_ASSERT( itr_to != idx_acc.end(), "Receiver account does not exists" );
auto itr_approved_op = std::find(itr_nft->approved_operators.begin(), itr_nft->approved_operators.end(), op.operator_);
FC_ASSERT( (itr_nft->owner == op.operator_) || (itr_nft->approved == itr_operator->id) || (itr_approved_op != itr_nft->approved_operators.end()), "Operator is not NFT owner or approved operator" );
const auto& nft_meta_obj = itr_nft->nft_metadata_id(db());
FC_ASSERT( nft_meta_obj.is_transferable == true, "NFT is not transferable");
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type nft_safe_transfer_from_evaluator::do_apply( const nft_safe_transfer_from_operation& op )
{ try {
const auto& idx = db().get_index_type<nft_index>().indices().get<by_id>();
auto itr = idx.find(op.token_id);
if (itr != idx.end())
{
db().modify(*itr, [&op](nft_object &obj) {
obj.owner = op.to;
obj.approved = {};
obj.approved_operators.clear();
});
}
return op.token_id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_approve_evaluator::do_evaluate( const nft_approve_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto& idx_nft = db().get_index_type<nft_index>().indices().get<by_id>();
const auto& idx_acc = db().get_index_type<account_index>().indices().get<by_id>();
auto itr_nft = idx_nft.find(op.token_id);
FC_ASSERT( itr_nft != idx_nft.end(), "NFT does not exists" );
auto itr_owner = idx_acc.find(op.operator_);
FC_ASSERT( itr_owner != idx_acc.end(), "Owner account does not exists" );
auto itr_approved = idx_acc.find(op.approved);
FC_ASSERT( itr_approved != idx_acc.end(), "Approved account does not exists" );
auto itr_approved_op = std::find(itr_nft->approved_operators.begin(), itr_nft->approved_operators.end(), op.operator_);
FC_ASSERT( (itr_nft->owner == itr_owner->id) || (itr_approved_op != itr_nft->approved_operators.end()), "Sender is not NFT owner or approved operator" );
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type nft_approve_evaluator::do_apply( const nft_approve_operation& op )
{ try {
const auto& idx = db().get_index_type<nft_index>().indices().get<by_id>();
auto itr = idx.find(op.token_id);
if (itr != idx.end())
{
db().modify(*itr, [&op](nft_object &obj) {
obj.approved = op.approved;
//auto itr = std::find(obj.approved_operators.begin(), obj.approved_operators.end(), op.approved);
//if (itr == obj.approved_operators.end()) {
// obj.approved_operators.push_back(op.approved);
//}
});
}
return op.token_id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_set_approval_for_all_evaluator::do_evaluate( const nft_set_approval_for_all_operation& op )
{ try {
auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(db());
const auto& idx_acc = db().get_index_type<account_index>().indices().get<by_id>();
auto itr_operator = idx_acc.find(op.operator_);
FC_ASSERT( itr_operator != idx_acc.end(), "Operator account does not exists" );
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result nft_set_approval_for_all_evaluator::do_apply( const nft_set_approval_for_all_operation& op )
{ try {
const auto &idx = db().get_index_type<nft_index>().indices().get<by_owner>();
const auto &idx_range = idx.equal_range(op.owner);
std::for_each(idx_range.first, idx_range.second, [&](const nft_object &obj) {
db().modify(obj, [&op](nft_object &obj) {
auto itr = std::find(obj.approved_operators.begin(), obj.approved_operators.end(), op.operator_);
if ((op.approved) && (itr == obj.approved_operators.end())) {
obj.approved_operators.push_back(op.operator_);
}
if ((!op.approved) && (itr != obj.approved_operators.end())) {
obj.approved_operators.erase(itr);
}
});
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
} } // graphene::chain

View file

@ -0,0 +1,338 @@
#include <graphene/chain/offer_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <iostream>
namespace graphene
{
namespace chain
{
void_result offer_evaluator::do_evaluate(const offer_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.issuer(d);
for (const auto &item : op.item_ids)
{
const auto &nft_obj = item(d);
FC_ASSERT(!d.item_locked(item), "Item(s) is already on sale");
bool is_owner = (nft_obj.owner == op.issuer);
bool is_approved = (nft_obj.approved == op.issuer);
bool is_approved_operator = (std::find(nft_obj.approved_operators.begin(), nft_obj.approved_operators.end(), op.issuer) != nft_obj.approved_operators.end());
if (op.buying_item)
{
FC_ASSERT(!is_owner, "Buyer cannot already be an onwer of the item");
FC_ASSERT(!is_approved, "Buyer cannot already be approved account of the item");
FC_ASSERT(!is_approved_operator, "Buyer cannot already be an approved operator of the item");
}
else
{
FC_ASSERT(is_owner, "Issuer has no authority to sell 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");
}
FC_ASSERT(op.offer_expiration_date > d.head_block_time(), "Expiration should be in future");
FC_ASSERT(op.fee.amount >= 0, "Invalid fee");
FC_ASSERT(op.minimum_price.amount >= 0 && op.maximum_price.amount > 0, "Invalid amount");
FC_ASSERT(op.minimum_price.asset_id == op.maximum_price.asset_id, "Asset ID mismatch");
FC_ASSERT(op.maximum_price >= op.minimum_price, "Invalid max min prices");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type offer_evaluator::do_apply(const offer_operation &op)
{
try
{
database &d = db();
if (op.buying_item)
{
d.adjust_balance(op.issuer, -op.maximum_price);
}
const auto &offer_obj = db().create<offer_object>([&](offer_object &obj) {
obj.issuer = op.issuer;
obj.item_ids = op.item_ids;
obj.minimum_price = op.minimum_price;
obj.maximum_price = op.maximum_price;
obj.buying_item = op.buying_item;
obj.offer_expiration_date = op.offer_expiration_date;
});
return offer_obj.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result bid_evaluator::do_evaluate(const bid_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto &offer = op.offer_id(d);
op.bidder(d);
for (const auto &item : offer.item_ids)
{
const auto &nft_obj = item(d);
bool is_owner = (nft_obj.owner == op.bidder);
bool is_approved = (nft_obj.approved == op.bidder);
bool is_approved_operator = (std::find(nft_obj.approved_operators.begin(), nft_obj.approved_operators.end(), op.bidder) != nft_obj.approved_operators.end());
if (offer.buying_item)
{
FC_ASSERT(is_owner, "Bidder has no authority to sell the item");
}
else
{
FC_ASSERT(!is_owner, "Bidder cannot already be an onwer of the item");
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");
}
}
FC_ASSERT(op.bid_price.asset_id == offer.minimum_price.asset_id, "Asset type mismatch");
FC_ASSERT(offer.minimum_price.amount == 0 || op.bid_price >= offer.minimum_price);
FC_ASSERT(offer.maximum_price.amount == 0 || op.bid_price <= offer.maximum_price);
if (offer.bidder)
{
FC_ASSERT((offer.buying_item && op.bid_price < *offer.bid_price) || (!offer.buying_item && op.bid_price > *offer.bid_price), "There is already a better bid than this");
}
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result bid_evaluator::do_apply(const bid_operation &op)
{
try
{
database &d = db();
const auto &offer = op.offer_id(d);
if (!offer.buying_item)
{
if (offer.bidder)
{
d.adjust_balance(*offer.bidder, *offer.bid_price);
}
d.adjust_balance(op.bidder, -op.bid_price);
}
d.modify(op.offer_id(d), [&](offer_object &o) {
if (op.bid_price == (offer.buying_item ? offer.minimum_price : offer.maximum_price))
{
o.offer_expiration_date = d.head_block_time();
}
o.bidder = op.bidder;
o.bid_price = op.bid_price;
});
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result cancel_offer_evaluator::do_evaluate(const cancel_offer_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto &offer = op.offer_id(d);
op.issuer(d);
FC_ASSERT(op.issuer == offer.issuer, "Only offer issuer can cancel the offer");
FC_ASSERT(offer.offer_expiration_date > d.head_block_time(), "Expiration should be in future when cancelling the offer");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result cancel_offer_evaluator::do_apply(const cancel_offer_operation &op)
{
try
{
database &d = db();
const auto &offer = op.offer_id(d);
if (offer.buying_item)
{
// Refund the max price to issuer
d.adjust_balance(offer.issuer, offer.maximum_price);
}
else
{
if (offer.bidder)
{
// Refund the bid price to the best bidder till now
d.adjust_balance(*offer.bidder, *offer.bid_price);
}
}
d.create<offer_history_object>([&](offer_history_object &obj) {
obj.issuer = offer.issuer;
obj.item_ids = offer.item_ids;
obj.bidder = offer.bidder;
obj.bid_price = offer.bid_price;
obj.minimum_price = offer.minimum_price;
obj.maximum_price = offer.maximum_price;
obj.buying_item = offer.buying_item;
obj.offer_expiration_date = offer.offer_expiration_date;
obj.result = result_type::Cancelled;
});
// This should unlock the item
d.remove(op.offer_id(d));
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result finalize_offer_evaluator::do_evaluate(const finalize_offer_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto &offer = op.offer_id(d);
if (op.result != result_type::ExpiredNoBid)
{
FC_ASSERT(offer.bidder, "No valid bidder");
FC_ASSERT((*offer.bid_price).amount >= 0, "Invalid bid price");
}
else
{
FC_ASSERT(!offer.bidder, "There should not be a valid bidder");
}
switch (op.result)
{
case result_type::Expired:
case result_type::ExpiredNoBid:
FC_ASSERT(offer.offer_expiration_date <= d.head_block_time(), "Offer finalized beyong expiration time");
break;
default:
FC_THROW_EXCEPTION(fc::assert_exception, "finalize_offer_operation: unknown result type.");
break;
}
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result finalize_offer_evaluator::do_apply(const finalize_offer_operation &op)
{
try
{
database &d = db();
offer_object offer = op.offer_id(d);
// Calculate the fees for all revenue partners of the items
auto calc_fee = [&](int64_t &tot_fees) {
map<account_id_type, asset> fee_map;
for (const auto &item : offer.item_ids)
{
const auto &nft_obj = item(d);
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
if (nft_meta_obj.revenue_partner && *nft_meta_obj.revenue_split > 0)
{
const auto &rev_partner = *nft_meta_obj.revenue_partner;
const auto &rev_split = *nft_meta_obj.revenue_split;
int64_t item_fee = static_cast<int64_t>((rev_split * (*offer.bid_price).amount.value / GRAPHENE_100_PERCENT) / offer.item_ids.size());
const auto &fee_asset = asset(item_fee, (*offer.bid_price).asset_id);
auto ret_val = fee_map.insert({rev_partner, fee_asset});
if (ret_val.second == false)
{
fee_map[rev_partner] += fee_asset;
}
tot_fees += item_fee;
}
}
return fee_map;
};
if (op.result != result_type::ExpiredNoBid)
{
int64_t tot_fees = 0;
auto &&fee_map = calc_fee(tot_fees);
// Transfer all the fee
for (const auto &fee_itr : fee_map)
{
auto &acc = fee_itr.first;
auto &acc_fee = fee_itr.second;
d.adjust_balance(acc, acc_fee);
}
// Calculate the remaining seller amount after the fee is deducted
auto &&seller_amount = *offer.bid_price - asset(tot_fees, (*offer.bid_price).asset_id);
// Buy Offer
if (offer.buying_item)
{
// Send the seller his amount
d.adjust_balance(*offer.bidder, seller_amount);
if (offer.bid_price < offer.maximum_price)
{
// Send the buyer the delta
d.adjust_balance(offer.issuer, offer.maximum_price - *offer.bid_price);
}
}
else
{
// Sell Offer, send the seller his amount
d.adjust_balance(offer.issuer, seller_amount);
}
// Tranfer the NFTs
for (auto item : offer.item_ids)
{
auto &nft_obj = item(d);
d.modify(nft_obj, [&offer](nft_object &obj) {
if (offer.buying_item)
{
obj.owner = offer.issuer;
}
else
{
obj.owner = *offer.bidder;
}
obj.approved = {};
obj.approved_operators.clear();
});
}
}
else
{
if (offer.buying_item)
{
d.adjust_balance(offer.issuer, offer.maximum_price);
}
}
d.create<offer_history_object>([&](offer_history_object &obj) {
obj.issuer = offer.issuer;
obj.item_ids = offer.item_ids;
obj.bidder = offer.bidder;
obj.bid_price = offer.bid_price;
obj.minimum_price = offer.minimum_price;
obj.maximum_price = offer.maximum_price;
obj.buying_item = offer.buying_item;
obj.offer_expiration_date = offer.offer_expiration_date;
obj.result = op.result;
});
// This should unlock the item
d.remove(op.offer_id(d));
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,50 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/offer_object.hpp>
namespace graphene
{
namespace chain
{
void offer_item_index::object_inserted(const object &obj)
{
assert(dynamic_cast<const offer_object *>(&obj));
const offer_object &oo = static_cast<const offer_object &>(obj);
if (!oo.buying_item)
{
for (const auto &id : oo.item_ids)
{
_locked_items.emplace(id);
}
}
}
void offer_item_index::object_modified(const object &after)
{
assert(dynamic_cast<const offer_object *>(&after));
const offer_object &oo = static_cast<const offer_object &>(after);
if (oo.buying_item && oo.bidder)
{
for (const auto &id : oo.item_ids)
{
_locked_items.emplace(id);
}
}
}
void offer_item_index::object_removed(const object &obj)
{
assert(dynamic_cast<const offer_object *>(&obj));
const offer_object &oo = static_cast<const offer_object &>(obj);
if (!oo.buying_item || oo.bidder)
{
for (const auto &id : oo.item_ids)
{
_locked_items.erase(id);
}
}
}
} // namespace chain
} // namespace graphene

View file

@ -128,6 +128,70 @@ struct proposal_operation_hardfork_visitor
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" ); FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" );
} }
void operator()(const custom_permission_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_permission_create_operation not allowed yet!" );
}
void operator()(const custom_permission_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_permission_update_operation not allowed yet!" );
}
void operator()(const custom_permission_delete_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_permission_delete_operation not allowed yet!" );
}
void operator()(const custom_account_authority_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_account_authority_create_operation not allowed yet!" );
}
void operator()(const custom_account_authority_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_account_authority_update_operation not allowed yet!" );
}
void operator()(const custom_account_authority_delete_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "custom_account_authority_delete_operation not allowed yet!" );
}
void operator()(const offer_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "offer_operation not allowed yet!" );
}
void operator()(const bid_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "bid_operation not allowed yet!" );
}
void operator()(const cancel_offer_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "cancel_offer_operation not allowed yet!" );
}
void operator()(const finalize_offer_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "finalize_offer_operation not allowed yet!" );
}
void operator()(const nft_metadata_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_metadata_create_operation not allowed yet!" );
}
void operator()(const nft_metadata_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_metadata_update_operation not allowed yet!" );
}
void operator()(const nft_mint_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_mint_operation not allowed yet!" );
}
void operator()(const nft_safe_transfer_from_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_safe_transfer_from_operation not allowed yet!" );
}
void operator()(const nft_approve_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_approve_operation not allowed yet!" );
}
void operator()(const nft_set_approval_for_all_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_set_approval_for_all_operation not allowed yet!" );
}
void operator()(const son_create_operation &v) const { void operator()(const son_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" ); FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
} }
@ -185,16 +249,17 @@ void son_hardfork_visitor::operator()( const son_report_down_operation &v )
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
{ try { { try {
const database& d = db(); const database& d = db();
auto block_time = d.head_block_time();
proposal_operation_hardfork_visitor vtor( d.head_block_time() ); proposal_operation_hardfork_visitor vtor( block_time );
vtor( o ); vtor( o );
const auto& global_parameters = d.get_global_properties().parameters; const auto& global_parameters = d.get_global_properties().parameters;
FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." ); FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." );
FC_ASSERT( o.expiration_time <= d.head_block_time() + global_parameters.maximum_proposal_lifetime, FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime,
"Proposal expiration time is too far in the future."); "Proposal expiration time is too far in the future.");
FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()), FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - block_time),
"Proposal review period must be less than its overall lifetime." ); "Proposal review period must be less than its overall lifetime." );
{ {
@ -203,7 +268,8 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
vector<authority> other; vector<authority> other;
for( auto& op : o.proposed_ops ) for( auto& op : o.proposed_ops )
{ {
operation_get_required_authorities(op.op, auths, auths, other); operation_get_required_authorities( op.op, auths, auths, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(block_time) );
} }
FC_ASSERT( other.size() == 0 ); // TODO: what about other??? FC_ASSERT( other.size() == 0 ); // TODO: what about other???
@ -226,18 +292,19 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
} }
} }
for( const op_wrapper& op : o.proposed_ops ) for (const op_wrapper& op : o.proposed_ops)
_proposed_trx.operations.push_back(op.op); _proposed_trx.operations.push_back(op.op);
_proposed_trx.validate(); _proposed_trx.validate();
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o) object_id_type proposal_create_evaluator::do_apply( const proposal_create_operation& o )
{ try { { try {
database& d = db(); database& d = db();
auto chain_time = d.head_block_time();
const proposal_object& proposal = d.create<proposal_object>([&](proposal_object& proposal) { const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {
_proposed_trx.expiration = o.expiration_time; _proposed_trx.expiration = o.expiration_time;
proposal.proposed_transaction = _proposed_trx; proposal.proposed_transaction = _proposed_trx;
proposal.proposer = o.fee_paying_account; proposal.proposer = o.fee_paying_account;
@ -251,7 +318,8 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
// TODO: consider caching values from evaluate? // TODO: consider caching values from evaluate?
for( auto& op : _proposed_trx.operations ) for( auto& op : _proposed_trx.operations )
operation_get_required_authorities(op, required_active, proposal.required_owner_approvals, other); operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) );
//All accounts which must provide both owner and active authority should be omitted from the active authority set; //All accounts which must provide both owner and active authority should be omitted from the active authority set;
//owner authority approval implies active authority approval. //owner authority approval implies active authority approval.
@ -269,7 +337,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
return proposal.id; return proposal.id;
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o) void_result proposal_update_evaluator::do_evaluate( const proposal_update_operation& o )
{ try { { try {
database& d = db(); database& d = db();

View file

@ -24,18 +24,22 @@
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp> #include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
bool proposal_object::is_authorized_to_execute(database& db) const bool proposal_object::is_authorized_to_execute( database& db ) const
{ {
transaction_evaluation_state dry_run_eval(&db); transaction_evaluation_state dry_run_eval( &db );
try { try {
verify_authority( proposed_transaction.operations, verify_authority( proposed_transaction.operations,
available_key_approvals, available_key_approvals,
[&]( account_id_type id ){ return &id(db).active; }, [&]( account_id_type id ){ return &id(db).active; },
[&]( account_id_type id ){ return &id(db).owner; }, [&]( account_id_type id ){ return &id(db).owner; },
[&]( account_id_type id, const operation& op ){
return db.get_account_custom_authorities(id, op); },
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ),
db.get_global_properties().parameters.max_authority_depth, db.get_global_properties().parameters.max_authority_depth,
true, /* allow committee */ true, /* allow committee */
available_active_approvals, available_active_approvals,

View file

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

View file

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

View file

@ -0,0 +1,99 @@
#include <graphene/chain/protocol/nft_ops.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene
{
namespace chain
{
bool is_valid_nft_token_name(const string &name)
{
try
{
const size_t len = name.size();
// NFT_TOKEN_MIN_LENGTH <= len minimum length check
if (len < NFT_TOKEN_MIN_LENGTH)
{
return false;
}
// len <= NFT_TOKEN_MAX_LENGTH max length check
if (len > NFT_TOKEN_MAX_LENGTH)
{
return false;
}
// First character should be a letter between a-z/A-Z
if (!((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A' && name[0] <= 'Z')))
{
return false;
}
// Any character 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 >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch == ' ')))
{
return false;
}
}
return true;
}
FC_CAPTURE_AND_RETHROW((name))
}
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
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
if(name)
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
{
return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
}
share_type nft_metadata_update_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee;
}
share_type nft_mint_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 nft_safe_transfer_from_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 nft_approve_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee;
}
share_type nft_set_approval_for_all_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee;
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,57 @@
#include <graphene/chain/protocol/offer.hpp>
#include <fc/io/raw.hpp>
namespace graphene
{
namespace chain
{
share_type offer_operation::calculate_fee(const fee_parameters_type &schedule) const
{
return schedule.fee + calculate_data_fee( fc::raw::pack_size(*this), schedule.price_per_kbyte );
}
void offer_operation::validate() const
{
FC_ASSERT(item_ids.size() > 0);
FC_ASSERT(fee.amount >= 0);
FC_ASSERT(minimum_price.asset_id == maximum_price.asset_id);
FC_ASSERT(minimum_price.amount >= 0 && maximum_price.amount > 0);
FC_ASSERT(maximum_price >= minimum_price);
}
share_type bid_operation::calculate_fee(const fee_parameters_type &schedule) const
{
share_type core_fee_required = schedule.fee;
return core_fee_required;
}
void bid_operation::validate() const
{
FC_ASSERT(fee.amount.value >= 0);
FC_ASSERT(bid_price.amount.value >= 0);
}
void cancel_offer_operation::validate() const
{
FC_ASSERT(fee.amount.value >= 0);
}
share_type cancel_offer_operation::calculate_fee(const fee_parameters_type &schedule) const
{
share_type core_fee_required = schedule.fee;
return core_fee_required;
}
void finalize_offer_operation::validate() const
{
FC_ASSERT(fee.amount.value >= 0);
}
share_type finalize_offer_operation::calculate_fee(const fee_parameters_type &schedule) const
{
share_type core_fee_required = schedule.fee;
return core_fee_required;
}
} // namespace chain
} // namespace graphene

View file

@ -53,25 +53,38 @@ struct operation_validator
struct operation_get_required_auth struct operation_get_required_auth
{ {
typedef void result_type; using result_type = void;
flat_set<account_id_type>& active; flat_set<account_id_type>& active;
flat_set<account_id_type>& owner; flat_set<account_id_type>& owner;
vector<authority>& other; vector<authority>& other;
bool ignore_custom_op_reqd_auths;
operation_get_required_auth( flat_set<account_id_type>& a, operation_get_required_auth( flat_set<account_id_type>& a,
flat_set<account_id_type>& own, flat_set<account_id_type>& own,
vector<authority>& oth ):active(a),owner(own),other(oth){} vector<authority>& oth,
bool ignore_custom_operation_required_auths )
: active( a ), owner( own ), other( oth ),
ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
template<typename T> template<typename T>
void operator()( const T& v )const void operator()( const T& v ) const {
{
active.insert( v.fee_payer() ); active.insert( v.fee_payer() );
v.get_required_active_authorities( active ); v.get_required_active_authorities( active );
v.get_required_owner_authorities( owner ); v.get_required_owner_authorities( owner );
v.get_required_authorities( other ); v.get_required_authorities( other );
} }
void operator()( const custom_operation& op ) const {
active.insert( op.fee_payer() );
if( !ignore_custom_op_reqd_auths ) {
op.get_required_active_authorities( active );
op.get_required_owner_authorities( owner );
op.get_required_authorities( other );
}
}
}; };
void operation_validate( const operation& op ) void operation_validate( const operation& op )
@ -79,12 +92,13 @@ void operation_validate( const operation& op )
op.visit( operation_validator() ); op.visit( operation_validator() );
} }
void operation_get_required_authorities( const operation& op, void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active, flat_set<account_id_type>& active,
flat_set<account_id_type>& owner, flat_set<account_id_type>& owner,
vector<authority>& other ) vector<authority>& other,
bool ignore_custom_operation_required_auths )
{ {
op.visit( operation_get_required_auth( active, owner, other ) ); op.visit( operation_get_required_auth( active, owner, other, ignore_custom_operation_required_auths ) );
} }
} } // namespace graphene::chain } } // namespace graphene::chain

View file

@ -95,10 +95,13 @@ void transaction::set_reference_block( const block_id_type& reference_block )
ref_block_prefix = reference_block._hash[1]; ref_block_prefix = reference_block._hash[1];
} }
void transaction::get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const void transaction::get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const
{ {
for( const auto& op : operations ) for ( const auto& op : operations )
operation_get_required_authorities( op, active, owner, other ); operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths );
} }
@ -248,8 +251,10 @@ struct sign_state
void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs, void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion_depth, uint32_t max_recursion_depth,
bool allow_committe, bool allow_committee,
const flat_set<account_id_type>& active_aprovals, const flat_set<account_id_type>& active_aprovals,
const flat_set<account_id_type>& owner_approvals ) const flat_set<account_id_type>& owner_approvals )
{ try { { try {
@ -257,13 +262,6 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
flat_set<account_id_type> required_owner; flat_set<account_id_type> required_owner;
vector<authority> other; vector<authority> other;
for( const auto& op : ops )
operation_get_required_authorities( op, required_active, required_owner, other );
if( !allow_committe )
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" );
sign_state s(sigs,get_active); sign_state s(sigs,get_active);
s.max_recursion = max_recursion_depth; s.max_recursion = max_recursion_depth;
for( auto& id : active_aprovals ) for( auto& id : active_aprovals )
@ -271,6 +269,36 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
for( auto& id : owner_approvals ) for( auto& id : owner_approvals )
s.approved_by.insert( id ); s.approved_by.insert( id );
auto approved_by_custom_authority = [&s, &get_custom](
account_id_type account,
operation op ) mutable {
auto custom_auths = get_custom( account, op );
for( const auto& auth : custom_auths )
if( s.check_authority( &auth ) ) return true;
return false;
};
for( const auto& op : ops ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other,
ignore_custom_operation_required_auths );
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_committee )
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 ) for( const auto& auth : other )
{ {
GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) ); GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
@ -325,18 +353,43 @@ set<public_key_type> signed_transaction::get_required_signatures(
const flat_set<public_key_type>& available_keys, const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion_depth )const uint32_t max_recursion_depth )const
{ {
flat_set<account_id_type> required_active; flat_set<account_id_type> required_active;
flat_set<account_id_type> required_owner; flat_set<account_id_type> required_owner;
vector<authority> other; vector<authority> other;
get_required_authorities( required_active, required_owner, other );
const flat_set<public_key_type>& signature_keys = get_signature_keys( chain_id ); const flat_set<public_key_type>& signature_keys = get_signature_keys( chain_id );
sign_state s( signature_keys, get_active, available_keys ); sign_state s( signature_keys, get_active, available_keys );
s.max_recursion = max_recursion_depth; s.max_recursion = max_recursion_depth;
for( const auto& auth : other ) auto approved_by_custom_authority = [&s, &get_custom](
account_id_type account,
operation op ) mutable {
auto custom_auths = get_custom( account, op );
for( const auto& auth : custom_auths )
if( s.check_authority( &auth ) ) return true;
return false;
};
for( const auto& op : operations ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other, ignore_custom_operation_required_authorities );
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); s.check_authority(&auth);
for( auto& owner : required_owner ) for( auto& owner : required_owner )
s.check_authority( get_owner( owner ) ); s.check_authority( get_owner( owner ) );
@ -359,10 +412,13 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
const flat_set<public_key_type>& available_keys, const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion uint32_t max_recursion
) const ) 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,
ignore_custom_operation_required_auths, max_recursion );
flat_set< public_key_type > result( s.begin(), s.end() ); flat_set< public_key_type > result( s.begin(), s.end() );
for( const public_key_type& k : s ) for( const public_key_type& k : s )
@ -370,7 +426,8 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
result.erase( k ); result.erase( k );
try 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,
ignore_custom_operation_required_auths, max_recursion );
continue; // element stays erased if verify_authority is ok continue; // element stays erased if verify_authority is ok
} }
catch( const tx_missing_owner_auth& e ) {} catch( const tx_missing_owner_auth& e ) {}
@ -385,9 +442,11 @@ void signed_transaction::verify_authority(
const chain_id_type& chain_id, const chain_id_type& chain_id,
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion )const uint32_t max_recursion )const
{ try { { 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) ) } } FC_CAPTURE_AND_RETHROW( (*this) ) }
} } // graphene::chain } } // graphene::chain

@ -1 +1 @@
Subproject commit a76b9ff81c6887ebe1dc9fa03ef15e1433029c65 Subproject commit 0358ca257e4ce9d66c1097cd2e8e2d34ff89a297

View file

@ -33,6 +33,7 @@
#include <graphene/chain/evaluator.hpp> #include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operation_history_object.hpp> #include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp> #include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/smart_ref_impl.hpp> #include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp> #include <fc/thread/thread.hpp>
@ -123,12 +124,15 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
// get the set of accounts this operation applies to // get the set of accounts this operation applies to
flat_set<account_id_type> impacted; flat_set<account_id_type> impacted;
vector<authority> other; vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here // fee payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value ) if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() ); impacted.insert( op.result.get<object_id_type>() );
else else
graphene::chain::operation_get_impacted_accounts( op.op, impacted ); graphene::chain::operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) );
if( op.op.which() == operation::tag< lottery_end_operation >::value ) if( op.op.which() == operation::tag< lottery_end_operation >::value )
{ {
auto lop = op.op.get< lottery_end_operation >(); auto lop = op.op.get< lottery_end_operation >();

View file

@ -47,7 +47,7 @@ void debug_witness_plugin::plugin_set_program_options(
{ {
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
command_line_options.add_options() command_line_options.add_options()
("private-key", bpo::value<vector<string>>()->composing()->multitoken()-> ("debug-private-key", bpo::value<vector<string>>()->composing()->multitoken()->
DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
"Tuple of [PublicKey, WIF private key] (may specify multiple times)"); "Tuple of [PublicKey, WIF private key] (may specify multiple times)");
config_file_options.add(command_line_options); config_file_options.add(command_line_options);
@ -63,9 +63,9 @@ void debug_witness_plugin::plugin_initialize(const boost::program_options::varia
ilog("debug_witness plugin: plugin_initialize() begin"); ilog("debug_witness plugin: plugin_initialize() begin");
_options = &options; _options = &options;
if( options.count("private-key") ) if( options.count("debug-private-key") )
{ {
const std::vector<std::string> key_id_to_wif_pair_strings = options["private-key"].as<std::vector<std::string>>(); const std::vector<std::string> key_id_to_wif_pair_strings = options["debug-private-key"].as<std::vector<std::string>>();
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
{ {
auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string> >(key_id_to_wif_pair_string, GRAPHENE_MAX_NESTED_OBJECTS); auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string> >(key_id_to_wif_pair_string, GRAPHENE_MAX_NESTED_OBJECTS);

View file

@ -25,7 +25,7 @@
#include <graphene/elasticsearch/elasticsearch_plugin.hpp> #include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/chain/impacted.hpp> #include <graphene/chain/impacted.hpp>
#include <graphene/chain/account_evaluator.hpp> #include <graphene/chain/account_evaluator.hpp>
#include <fc/smart_ref_impl.hpp> #include <graphene/chain/hardfork.hpp>
#include <curl/curl.h> #include <curl/curl.h>
namespace graphene { namespace elasticsearch { namespace graphene { namespace elasticsearch {
@ -157,12 +157,15 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b
// get the set of accounts this operation applies to // get the set of accounts this operation applies to
flat_set<account_id_type> impacted; flat_set<account_id_type> impacted;
vector<authority> other; vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here // fee_payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value ) if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() ); impacted.insert( op.result.get<object_id_type>() );
else else
graphene::chain::operation_get_impacted_accounts( op.op, impacted ); operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
for( auto& a : other ) for( auto& a : other )
for( auto& item : a.account_auths ) for( auto& item : a.account_auths )

View file

@ -2130,6 +2130,224 @@ class wallet_api
rock_paper_scissors_gesture gesture, rock_paper_scissors_gesture gesture,
bool broadcast); bool broadcast);
signed_transaction create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast = true);
signed_transaction update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast = true);
signed_transaction delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast = true);
signed_transaction create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast = true);
signed_transaction update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast = true);
signed_transaction delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast = true);
vector<custom_permission_object> get_custom_permissions(string owner) const;
fc::optional<custom_permission_object> get_custom_permission_by_name(string owner, string permission_name) const;
vector<custom_account_authority_object> get_custom_account_authorities(string owner) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const;
/////////
// NFT //
/////////
/**
* @brief Creates NFT metadata
* @param owner_account_id_or_name Owner account ID or name
* @param name Name of the token group
* @param symbol Symbol of the token group
* @param base_uri Base URI for token URI
* @param revenue_partner revenue partner for this type of Token
* @param revenue_split revenue split for the sale
* @param is_transferable can transfer the NFT or not
* @param is_sellable can sell NFT or not
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction transfering the funds
*/
signed_transaction nft_metadata_create(string owner_account_id_or_name,
string name,
string symbol,
string base_uri,
optional<string> revenue_partner,
optional<uint16_t> revenue_split,
bool is_transferable,
bool is_sellable,
bool broadcast);
/**
* @brief Updates NFT metadata
* @param owner_account_id_or_name Owner account ID or name
* @param nft_metadata_id Metadata ID to modify
* @param name Name of the token group
* @param symbol Symbol of the token group
* @param base_uri Base URI for token URI
* @param revenue_partner revenue partner for this type of Token
* @param revenue_split revenue split for the sale
* @param is_transferable can transfer the NFT or not
* @param is_sellable can sell NFT or not
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction transfering the funds
*/
signed_transaction nft_metadata_update(string owner_account_id_or_name,
nft_metadata_id_type nft_metadata_id,
optional<string> name,
optional<string> symbol,
optional<string> base_uri,
optional<string> revenue_partner,
optional<uint16_t> revenue_split,
optional<bool> is_transferable,
optional<bool> is_sellable,
bool broadcast);
/**
* @brief Creates NFT
* @param metadata_owner_account_id_or_name NFT metadata owner account ID or name
* @param metadata_id NFT metadata ID to which token will belong
* @param owner_account_id_or_name Owner account ID or name
* @param approved_account_id_or_name Approved account ID or name
* @param token_uri Token URI (Will be combined with metadata base_uri if its not empty)
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction transfering the funds
*/
signed_transaction nft_create(string metadata_owner_account_id_or_name,
nft_metadata_id_type metadata_id,
string owner_account_id_or_name,
string approved_account_id_or_name,
string token_uri,
bool broadcast);
/**
* @brief Returns the number of NFT owned by account
* @param owner_account_id_or_name Owner account ID or name
* @return Number of NFTs owned by account
*/
uint64_t nft_get_balance(string owner_account_id_or_name) const;
/**
* @brief Returns the NFT owner
* @param token_id NFT ID
* @return NFT owner account ID
*/
optional<account_id_type> nft_owner_of(const nft_id_type token_id) const;
/**
* @brief Transfers NFT safely
* @param operator_account_id_or_name Operators account ID or name
* @param from_account_id_or_name Senders account ID or name
* @param to_account_id_or_name Receivers account ID or name
* @param token_id NFT ID
* @param data Non mandatory data
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction transfering NFT
*/
signed_transaction nft_safe_transfer_from(string operator_account_id_or_name,
string from_account_id_or_name,
string to_account_id_or_name,
nft_id_type token_id,
string data,
bool broadcast);
/**
* @brief Transfers NFT
* @param operator_account_id_or_name Operators account ID or name
* @param from_account_id_or_name Senders account ID or name
* @param to_account_id_or_name Receivers account ID or name
* @param token_id NFT ID
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction transfering NFT
*/
signed_transaction nft_transfer_from(string operator_account_id_or_name,
string from_account_id_or_name,
string to_account_id_or_name,
nft_id_type token_id,
bool broadcast);
/**
* @brief Sets approved account for NFT
* @param operator_account_id_or_name Operators account ID or name
* @param approved_account_id_or_name Senders account ID or name
* @param token_id NFT ID
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction setting approving account for NFT
*/
signed_transaction nft_approve(string operator_account_id_or_name,
string approved_account_id_or_name,
nft_id_type token_id,
bool broadcast);
/**
* @brief Sets approval for all NFT owned by owner
* @param owner_account_id_or_name Owner account ID or name
* @param operator_account_id_or_name Operator account ID or name
* @param approved true if approved
* @param broadcast true to broadcast transaction to the network
* @return Signed transaction setting approvals for all NFT owned by owner
*/
signed_transaction nft_set_approval_for_all(string owner_account_id_or_name,
string operator_account_id_or_name,
bool approved,
bool broadcast);
/**
* @brief Returns the NFT approved account ID
* @param token_id NFT ID
* @return NFT approved account ID
*/
optional<account_id_type> nft_get_approved(const nft_id_type token_id) const;
/**
* @brief Returns operator approved state for all NFT owned by owner
* @param owner NFT owner account ID
* @param token_id NFT ID
* @return True if operator is approved for all NFT owned by owner, else False
*/
bool nft_is_approved_for_all(string owner_account_id_or_name, string operator_account_id_or_name) const;
/**
* @brief Returns all tokens
* @return Returns vector of NFT objects, empty vector if none
*/
vector<nft_object> nft_get_all_tokens() const;
signed_transaction create_offer(set<nft_id_type> item_ids,
string issuer_accound_id_or_name,
asset minimum_price,
asset maximum_price,
bool buying_item,
time_point_sec offer_expiration_date,
optional<memo_data> memo,
bool broadcast);
signed_transaction create_bid(string bidder_account_id_or_name,
asset bid_price,
offer_id_type offer_id,
bool broadcast);
signed_transaction cancel_offer(string issuer_account_id_or_name,
offer_id_type offer_id,
bool broadcast);
vector<offer_object> list_offers(uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_object> list_sell_offers(uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_object> list_buy_offers(uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_history_object> list_offer_history(uint32_t limit, optional<offer_history_id_type> lower_id) const;
vector<offer_object> get_offers_by_issuer(string issuer_account_id_or_name,
uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_object> get_offers_by_item(const nft_id_type item, uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_history_object> get_offer_history_by_issuer(string issuer_account_id_or_name, uint32_t limit, optional<offer_history_id_type> lower_id) const;
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;
void dbg_make_uia(string creator, string symbol); void dbg_make_uia(string creator, string symbol);
void dbg_make_mia(string creator, string symbol); void dbg_make_mia(string creator, string symbol);
void dbg_push_blocks( std::string src_filename, uint32_t count ); void dbg_push_blocks( std::string src_filename, uint32_t count );
@ -2399,6 +2617,30 @@ FC_API( graphene::wallet::wallet_api,
(tournament_leave) (tournament_leave)
(rps_throw) (rps_throw)
(create_vesting_balance) (create_vesting_balance)
(nft_metadata_create)
(nft_metadata_update)
(nft_create)
(nft_get_balance)
(nft_owner_of)
(nft_safe_transfer_from)
(nft_transfer_from)
(nft_approve)
(nft_set_approval_for_all)
(nft_get_approved)
(nft_is_approved_for_all)
(nft_get_all_tokens)
(create_offer)
(create_bid)
(cancel_offer)
(list_offers)
(list_sell_offers)
(list_buy_offers)
(list_offer_history)
(get_offers_by_issuer)
(get_offers_by_item)
(get_offer_history_by_issuer)
(get_offer_history_by_item)
(get_offer_history_by_bidder)
(get_upcoming_tournaments) (get_upcoming_tournaments)
(get_tournaments) (get_tournaments)
(get_tournaments_by_state) (get_tournaments_by_state)
@ -2411,4 +2653,16 @@ FC_API( graphene::wallet::wallet_api,
(get_all_matched_bets_for_bettor) (get_all_matched_bets_for_bettor)
(buy_ticket) (buy_ticket)
(quit) (quit)
(create_custom_permission)
(update_custom_permission)
(delete_custom_permission)
(create_custom_account_authority)
(update_custom_account_authority)
(delete_custom_account_authority)
(get_custom_permissions)
(get_custom_permission_by_name)
(get_custom_account_authorities)
(get_custom_account_authorities_by_permission_id)
(get_custom_account_authorities_by_permission_name)
(get_active_custom_account_authorities_by_operation)
) )

View file

@ -1898,7 +1898,7 @@ public:
} }
FC_CAPTURE_AND_RETHROW( (owner_account) ) FC_CAPTURE_AND_RETHROW( (owner_account) )
} }
committee_member_object get_committee_member(string owner_account) committee_member_object get_committee_member(string owner_account)
{ {
try try
@ -3591,6 +3591,140 @@ public:
return sign_transaction(tx, broadcast); return sign_transaction(tx, broadcast);
} }
signed_transaction create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast)
{
custom_permission_create_operation create_op;
create_op.owner_account = get_account(owner).id;
create_op.permission_name = permission_name;
create_op.auth = auth;
signed_transaction tx;
tx.operations.push_back(create_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast)
{
custom_permission_update_operation update_op;
update_op.owner_account = get_account(owner).id;
update_op.permission_id = permission_id;
update_op.new_auth = new_auth;
signed_transaction tx;
tx.operations.push_back(update_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast)
{
custom_permission_delete_operation delete_op;
delete_op.owner_account = get_account(owner).id;
delete_op.permission_id = permission_id;
signed_transaction tx;
tx.operations.push_back(delete_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast)
{
custom_account_authority_create_operation create_op;
create_op.owner_account = get_account(owner).id;
create_op.permission_id = permission_id;
create_op.operation_type = operation_type;
create_op.valid_from = valid_from;
create_op.valid_to = valid_to;
signed_transaction tx;
tx.operations.push_back(create_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast)
{
custom_account_authority_update_operation update_op;
update_op.owner_account = get_account(owner).id;
update_op.auth_id = auth_id;
update_op.new_valid_from = new_valid_from;
update_op.new_valid_to = new_valid_to;
signed_transaction tx;
tx.operations.push_back(update_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
signed_transaction delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast)
{
custom_account_authority_delete_operation delete_op;
delete_op.owner_account = get_account(owner).id;
delete_op.auth_id = auth_id;
signed_transaction tx;
tx.operations.push_back(delete_op);
set_operation_fees(tx, get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
}
vector<custom_permission_object> get_custom_permissions(string owner) const
{
return _remote_db->get_custom_permissions(get_account(owner).id);
}
fc::optional<custom_permission_object> get_custom_permission_by_name(string owner, string permission_name) const
{
return _remote_db->get_custom_permission_by_name(get_account(owner).id, permission_name);
}
vector<custom_account_authority_object> get_custom_account_authorities(string owner) const
{
return _remote_db->get_custom_account_authorities(get_account(owner).id);
}
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const
{
return _remote_db->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const
{
return _remote_db->get_custom_account_authorities_by_permission_name(get_account(owner).id, permission_name);
}
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const
{
return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type);
}
void dbg_make_uia(string creator, string symbol) void dbg_make_uia(string creator, string symbol)
{ {
asset_options opts; asset_options opts;
@ -5045,8 +5179,84 @@ signed_transaction wallet_api::approve_proposal(
return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast ); return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast );
} }
signed_transaction wallet_api::create_custom_permission(string owner,
string permission_name,
authority auth,
bool broadcast)
{
return my->create_custom_permission(owner, permission_name, auth, broadcast);
}
signed_transaction wallet_api::update_custom_permission(string owner,
custom_permission_id_type permission_id,
fc::optional<authority> new_auth,
bool broadcast)
{
return my->update_custom_permission(owner, permission_id, new_auth, broadcast);
}
signed_transaction wallet_api::delete_custom_permission(string owner,
custom_permission_id_type permission_id,
bool broadcast)
{
return my->delete_custom_permission(owner, permission_id, broadcast);
}
signed_transaction wallet_api::create_custom_account_authority(string owner,
custom_permission_id_type permission_id,
int operation_type,
fc::time_point_sec valid_from,
fc::time_point_sec valid_to,
bool broadcast)
{
return my->create_custom_account_authority(owner, permission_id, operation_type, valid_from, valid_to, broadcast);
}
signed_transaction wallet_api::update_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
fc::optional<fc::time_point_sec> new_valid_from,
fc::optional<fc::time_point_sec> new_valid_to,
bool broadcast)
{
return my->update_custom_account_authority(owner, auth_id, new_valid_from, new_valid_to, broadcast);
}
signed_transaction wallet_api::delete_custom_account_authority(string owner,
custom_account_authority_id_type auth_id,
bool broadcast)
{
return my->delete_custom_account_authority(owner, auth_id, broadcast);
}
vector<custom_permission_object> wallet_api::get_custom_permissions(string owner) const
{
return my->get_custom_permissions(owner);
}
fc::optional<custom_permission_object> wallet_api::get_custom_permission_by_name(string owner, string permission_name) const
{
return my->get_custom_permission_by_name(owner, permission_name);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities(string owner) const
{
return my->get_custom_account_authorities(owner);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const
{
return my->get_custom_account_authorities_by_permission_id(permission_id);
}
vector<custom_account_authority_object> wallet_api::get_custom_account_authorities_by_permission_name(string owner, string permission_name) const
{
return my->get_custom_account_authorities_by_permission_name(owner, permission_name);
}
vector<authority> wallet_api::get_active_custom_account_authorities_by_operation(string owner, int operation_type) const
{
return my->get_active_custom_account_authorities_by_operation(owner, operation_type);
}
global_property_object wallet_api::get_global_properties() const global_property_object wallet_api::get_global_properties() const
{ {
@ -6626,6 +6836,358 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id,
return my->sign_transaction( tx, broadcast ); return my->sign_transaction( tx, broadcast );
} }
signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_name,
string name,
string symbol,
string base_uri,
optional<string> revenue_partner,
optional<uint16_t> revenue_split,
bool is_transferable,
bool is_sellable,
bool broadcast)
{
account_object owner_account = my->get_account(owner_account_id_or_name);
nft_metadata_create_operation op;
op.owner = owner_account.id;
op.name = name;
op.symbol = symbol;
op.base_uri = base_uri;
if( revenue_partner )
{
account_object partner_account = my->get_account(*revenue_partner);
op.revenue_partner = partner_account.id;
uint16_t rev_split = 0;
if( revenue_split )
{
rev_split = *revenue_split;
}
op.revenue_split = rev_split;
}
op.is_transferable = is_transferable;
op.is_sellable = is_sellable;
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::nft_metadata_update(string owner_account_id_or_name,
nft_metadata_id_type nft_metadata_id,
optional<string> name,
optional<string> symbol,
optional<string> base_uri,
optional<string> revenue_partner,
optional<uint16_t> revenue_split,
optional<bool> is_transferable,
optional<bool> is_sellable,
bool broadcast)
{
account_object owner_account = my->get_account(owner_account_id_or_name);
nft_metadata_update_operation op;
op.nft_metadata_id = nft_metadata_id;
op.owner = owner_account.id;
op.name = name;
op.symbol = symbol;
op.base_uri = base_uri;
if( revenue_partner )
{
account_object partner_account = my->get_account(*revenue_partner);
op.revenue_partner = partner_account.id;
uint16_t rev_split = 0;
if( revenue_split )
{
rev_split = *revenue_split;
}
op.revenue_split = rev_split;
}
op.is_transferable = is_transferable;
op.is_sellable = is_sellable;
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::nft_create(string metadata_owner_account_id_or_name,
nft_metadata_id_type metadata_id,
string owner_account_id_or_name,
string approved_account_id_or_name,
string token_uri,
bool broadcast)
{
account_object metadata_owner_account = my->get_account(metadata_owner_account_id_or_name);
account_object owner_account = my->get_account(owner_account_id_or_name);
account_object approved_account = my->get_account(approved_account_id_or_name);
nft_mint_operation op;
op.payer = metadata_owner_account.id;
op.nft_metadata_id = metadata_id;
op.owner = owner_account.id;
op.approved = approved_account.id;
op.token_uri = token_uri;
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 );
}
uint64_t wallet_api::nft_get_balance(string owner_account_id_or_name) const
{
account_object owner_account = my->get_account(owner_account_id_or_name);
return my->_remote_db->nft_get_balance(owner_account.id);
}
optional<account_id_type> wallet_api::nft_owner_of(const nft_id_type token_id) const
{
return my->_remote_db->nft_owner_of(token_id);
}
signed_transaction wallet_api::nft_safe_transfer_from(string operator_account_id_or_name,
string from_account_id_or_name,
string to_account_id_or_name,
nft_id_type token_id,
string data,
bool broadcast)
{
account_object operator_account = my->get_account(operator_account_id_or_name);
account_object from_account = my->get_account(from_account_id_or_name);
account_object to_account = my->get_account(to_account_id_or_name);
nft_safe_transfer_from_operation op;
op.operator_ = operator_account.id;
op.from = from_account.id;
op.to = to_account.id;
op.token_id = token_id;
op.data = data;
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::nft_transfer_from(string operator_account_id_or_name,
string from_account_id_or_name,
string to_account_id_or_name,
nft_id_type token_id,
bool broadcast)
{
return nft_safe_transfer_from(operator_account_id_or_name, from_account_id_or_name, to_account_id_or_name, token_id, "", broadcast);
}
signed_transaction wallet_api::nft_approve(string operator_account_id_or_name,
string approved_account_id_or_name,
nft_id_type token_id,
bool broadcast)
{
account_object operator_account = my->get_account(operator_account_id_or_name);
account_object approved_account = my->get_account(approved_account_id_or_name);
nft_approve_operation op;
op.operator_ = operator_account.id;
op.approved = approved_account.id;
op.token_id = token_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 );
}
signed_transaction wallet_api::nft_set_approval_for_all(string owner_account_id_or_name,
string operator_account_id_or_name,
bool approved,
bool broadcast)
{
account_object owner_account = my->get_account(owner_account_id_or_name);
account_object operator_account = my->get_account(operator_account_id_or_name);
nft_set_approval_for_all_operation op;
op.owner = owner_account.id;
op.operator_ = operator_account.id;
op.approved = approved;
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 );
}
optional<account_id_type> wallet_api::nft_get_approved(const nft_id_type token_id) const
{
return my->_remote_db->nft_get_approved(token_id);
}
bool wallet_api::nft_is_approved_for_all(string owner_account_id_or_name, string operator_account_id_or_name) const
{
account_object owner_account = my->get_account(owner_account_id_or_name);
account_object operator_account = my->get_account(operator_account_id_or_name);
return my->_remote_db->nft_is_approved_for_all(owner_account.id, operator_account.id);
}
vector<nft_object> wallet_api::nft_get_all_tokens() const
{
return my->_remote_db->nft_get_all_tokens();
}
signed_transaction wallet_api::create_offer(set<nft_id_type> item_ids,
string issuer_accound_id_or_name,
asset minimum_price,
asset maximum_price,
bool buying_item,
time_point_sec offer_expiration_date,
optional<memo_data> memo,
bool broadcast)
{
account_object issuer_account = my->get_account(issuer_accound_id_or_name);
offer_operation op;
op.item_ids = item_ids;
op.issuer = issuer_account.id;
op.minimum_price = minimum_price;
op.maximum_price = maximum_price;
op.buying_item = buying_item;
op.offer_expiration_date = offer_expiration_date;
op.memo = memo;
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::create_bid(string bidder_account_id_or_name,
asset bid_price,
offer_id_type offer_id,
bool broadcast)
{
account_object bidder_account = my->get_account(bidder_account_id_or_name);
bid_operation op;
op.bidder = bidder_account.id;
op.offer_id = offer_id;
op.bid_price = bid_price;
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::cancel_offer(string issuer_account_id_or_name,
offer_id_type offer_id,
bool broadcast)
{
account_object issuer_account = my->get_account(issuer_account_id_or_name);
cancel_offer_operation op;
op.issuer = issuer_account.id;
op.offer_id = offer_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<offer_object> wallet_api::list_offers(uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->list_offers(lb_id, limit);
}
vector<offer_object> wallet_api::list_sell_offers(uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->list_sell_offers(lb_id, limit);
}
vector<offer_object> wallet_api::list_buy_offers(uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->list_buy_offers(lb_id, limit);
}
vector<offer_history_object> wallet_api::list_offer_history(uint32_t limit, optional<offer_history_id_type> lower_id) const
{
offer_history_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->list_offer_history(lb_id, limit);
}
vector<offer_object> wallet_api::get_offers_by_issuer(string issuer_account_id_or_name,
uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
account_object issuer_account = my->get_account(issuer_account_id_or_name);
return my->_remote_db->get_offers_by_issuer(lb_id, issuer_account.id, limit);
}
vector<offer_object> wallet_api::get_offers_by_item(const nft_id_type item, uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->get_offers_by_item(lb_id, item, limit);
}
vector<offer_history_object> wallet_api::get_offer_history_by_issuer(string issuer_account_id_or_name, uint32_t limit, optional<offer_history_id_type> lower_id) const
{
offer_history_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
account_object issuer_account = my->get_account(issuer_account_id_or_name);
return my->_remote_db->get_offer_history_by_issuer(lb_id, issuer_account.id, limit);
}
vector<offer_history_object> wallet_api::get_offer_history_by_item(const nft_id_type item, uint32_t limit, optional<offer_history_id_type> lower_id) const
{
offer_history_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
return my->_remote_db->get_offer_history_by_item(lb_id, item, limit);
}
vector<offer_history_object> wallet_api::get_offer_history_by_bidder(string bidder_account_id_or_name, uint32_t limit, optional<offer_history_id_type> lower_id) const
{
offer_history_id_type lb_id;
if(lower_id)
lb_id = *lower_id;
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);
}
// default ctor necessary for FC_REFLECT // default ctor necessary for FC_REFLECT
signed_block_with_info::signed_block_with_info() signed_block_with_info::signed_block_with_info()
{ {

View file

@ -209,9 +209,10 @@ int main( int argc, char** argv )
wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " ); wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " );
})); }));
auto _websocket_server = std::make_shared<fc::http::websocket_server>(); std::shared_ptr<fc::http::websocket_server> _websocket_server;
if( options.count("rpc-endpoint") ) if( options.count("rpc-endpoint") )
{ {
_websocket_server = std::make_shared<fc::http::websocket_server>();
_websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){ _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){
std::cout << "here... \n"; std::cout << "here... \n";
wlog("." ); wlog("." );
@ -228,9 +229,10 @@ int main( int argc, char** argv )
if( options.count( "rpc-tls-certificate" ) ) if( options.count( "rpc-tls-certificate" ) )
cert_pem = options.at("rpc-tls-certificate").as<string>(); cert_pem = options.at("rpc-tls-certificate").as<string>();
auto _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(cert_pem); std::shared_ptr<fc::http::websocket_tls_server> _websocket_tls_server;
if( options.count("rpc-tls-endpoint") ) if( options.count("rpc-tls-endpoint") )
{ {
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(cert_pem);
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c, GRAPHENE_MAX_NESTED_OBJECTS); auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c, GRAPHENE_MAX_NESTED_OBJECTS);
wsc->register_api(wapi); wsc->register_api(wapi);

View file

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

View file

@ -11,7 +11,7 @@ endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc graphene_snapshot graphene_es_objects peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc graphene_snapshot graphene_elasticsearch graphene_es_objects peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
install( TARGETS install( TARGETS

View file

@ -375,6 +375,10 @@
"maximum_tournament_start_delay": 604800, "maximum_tournament_start_delay": 604800,
"maximum_tournament_number_of_wins": 100, "maximum_tournament_number_of_wins": 100,
"extensions": { "extensions": {
"min_bet_multiplier": 10100,
"max_bet_multiplier": 10000000,
"betting_rake_fee_percentage": 300,
"live_betting_delay_time": 5,
"sweeps_distribution_percentage": 200, "sweeps_distribution_percentage": 200,
"sweeps_distribution_asset": "1.3.0", "sweeps_distribution_asset": "1.3.0",
"sweeps_vesting_accumulator_account": "1.2.0", "sweeps_vesting_accumulator_account": "1.2.0",

View file

@ -28,7 +28,12 @@
#include <graphene/utilities/tempdir.hpp> #include <graphene/utilities/tempdir.hpp>
#include <graphene/witness/witness.hpp>
#include <graphene/account_history/account_history_plugin.hpp> #include <graphene/account_history/account_history_plugin.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
#include <graphene/accounts_list/accounts_list_plugin.hpp>
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/witness/witness.hpp> #include <graphene/witness/witness.hpp>
#include <graphene/market_history/market_history_plugin.hpp> #include <graphene/market_history/market_history_plugin.hpp>
@ -58,9 +63,13 @@ BOOST_AUTO_TEST_CASE( two_node_network )
BOOST_TEST_MESSAGE( "Creating and initializing app1" ); BOOST_TEST_MESSAGE( "Creating and initializing app1" );
graphene::app::application app1; graphene::app::application app1;
app1.register_plugin<graphene::account_history::account_history_plugin>();
app1.register_plugin<graphene::witness_plugin::witness_plugin>(); app1.register_plugin<graphene::witness_plugin::witness_plugin>();
app1.register_plugin<graphene::account_history::account_history_plugin>();
app1.register_plugin<graphene::bookie::bookie_plugin>();
app1.register_plugin<graphene::accounts_list::accounts_list_plugin>();
app1.register_plugin<graphene::affiliate_stats::affiliate_stats_plugin>();
app1.register_plugin<graphene::market_history::market_history_plugin>(); app1.register_plugin<graphene::market_history::market_history_plugin>();
boost::program_options::variables_map cfg; boost::program_options::variables_map cfg;
cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false));
cfg.emplace("plugins", boost::program_options::variable_value(string(" "), false)); cfg.emplace("plugins", boost::program_options::variable_value(string(" "), false));
@ -76,8 +85,11 @@ BOOST_AUTO_TEST_CASE( two_node_network )
auto cfg2 = cfg; auto cfg2 = cfg;
graphene::app::application app2; graphene::app::application app2;
app2.register_plugin<account_history::account_history_plugin>();
app2.register_plugin<graphene::witness_plugin::witness_plugin>(); app2.register_plugin<graphene::witness_plugin::witness_plugin>();
app2.register_plugin<graphene::account_history::account_history_plugin>();
app2.register_plugin<graphene::bookie::bookie_plugin>();
app2.register_plugin<graphene::accounts_list::accounts_list_plugin>();
app2.register_plugin<graphene::affiliate_stats::affiliate_stats_plugin>();
app2.register_plugin<graphene::market_history::market_history_plugin>(); app2.register_plugin<graphene::market_history::market_history_plugin>();
cfg2.erase("p2p-endpoint"); cfg2.erase("p2p-endpoint");
cfg2.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); cfg2.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false));

View file

@ -8,6 +8,10 @@
#define INVOKE(test) ((struct test*)this)->test_method(); #define INVOKE(test) ((struct test*)this)->test_method();
int get_available_port();
std::shared_ptr<graphene::app::application> start_application(fc::temp_directory& app_dir, int& server_port_number);
/////////// ///////////
/// @brief a class to make connecting to the application server easier /// @brief a class to make connecting to the application server easier
/////////// ///////////

View file

@ -23,8 +23,11 @@
*/ */
#include "cli_fixture.hpp" #include "cli_fixture.hpp"
#include <fc/crypto/aes.hpp>
#include <fc/smart_ref_impl.hpp> #include <fc/smart_ref_impl.hpp>
#include <graphene/utilities/tempdir.hpp>
#define BOOST_TEST_MODULE Test Application #define BOOST_TEST_MODULE Test Application
#include <boost/test/included/unit_test.hpp> #include <boost/test/included/unit_test.hpp>
@ -39,7 +42,17 @@ BOOST_AUTO_TEST_CASE( cli_connect )
BOOST_TEST_MESSAGE("Testing wallet connection."); BOOST_TEST_MESSAGE("Testing wallet connection.");
} }
BOOST_AUTO_TEST_CASE( upgrade_nathan_account ) ////////////////
// Start a server and connect using the same calls as the CLI
// Quit wallet and be sure that file was saved correctly
////////////////
BOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture )
{
BOOST_TEST_MESSAGE("Testing wallet connection and quit command.");
BOOST_CHECK_THROW( con.wallet_api_ptr->quit(), fc::canceled_exception );
}
BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture )
{ {
init_nathan(); init_nathan();
} }
@ -60,8 +73,11 @@ BOOST_AUTO_TEST_CASE( create_new_account )
BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key)); BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key));
con.wallet_api_ptr->save_wallet_file(con.wallet_filename); con.wallet_api_ptr->save_wallet_file(con.wallet_filename);
// attempt to give jmjatlanta some CORE BOOST_CHECK(generate_block());
BOOST_TEST_MESSAGE("Transferring CORE from Nathan to jmjatlanta"); fc::usleep( fc::seconds(1) );
// attempt to give jmjatlanta some peerplays
BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to jmjatlanta");
signed_transaction transfer_tx = con.wallet_api_ptr->transfer( signed_transaction transfer_tx = con.wallet_api_ptr->transfer(
"nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true "nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true
); );
@ -76,20 +92,22 @@ BOOST_AUTO_TEST_CASE( create_new_account )
// Vote for two witnesses, and make sure they both stay there // Vote for two witnesses, and make sure they both stay there
// after a maintenance block // after a maintenance block
/////////////////////// ///////////////////////
BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses )
// Todo: Removed by GPOS, refactor test.
/*
BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
{ {
try try
{ {
BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses"); BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses");
init_nathan(); INVOKE(create_new_account);
// get the details for init1 // get the details for init1
witness_object init1_obj = con.wallet_api_ptr->get_witness("init1"); witness_object init1_obj = con.wallet_api_ptr->get_witness("init1");
int init1_start_votes = init1_obj.total_votes; int init1_start_votes = init1_obj.total_votes;
// Vote for a witness // Vote for a witness
con.wallet_api_ptr->create_vesting_balance("nathan", "10000", "1.3.0", vesting_balance_type::gpos, true); signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init1", true, true);
signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true);
// generate a block to get things started // generate a block to get things started
BOOST_CHECK(generate_block()); BOOST_CHECK(generate_block());
@ -104,7 +122,7 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses )
// Vote for a 2nd witness // Vote for a 2nd witness
int init2_start_votes = init2_obj.total_votes; int init2_start_votes = init2_obj.total_votes;
signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init2", true, true); signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init2", true, true);
// send another block to trigger maintenance interval // send another block to trigger maintenance interval
BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(generate_maintenance_block());
@ -122,6 +140,43 @@ BOOST_AUTO_TEST_CASE( cli_vote_for_2_witnesses )
throw; throw;
} }
} }
*/
BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture )
{
try
{
INVOKE(upgrade_nathan_account);
// register account and transfer funds
const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
con.wallet_api_ptr->register_account(
"test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
);
con.wallet_api_ptr->transfer("nathan", "test", "1000", "1.3.0", "", true);
// import key and save wallet
BOOST_CHECK(con.wallet_api_ptr->import_key("test", test_bki.wif_priv_key));
con.wallet_api_ptr->save_wallet_file(con.wallet_filename);
// create transaction and check expected result
auto signed_trx = con.wallet_api_ptr->transfer("test", "nathan", "10", "1.3.0", "", true);
const auto &test_acc = con.wallet_api_ptr->get_account("test");
flat_set<public_key_type> expected_signers = {test_bki.pub_key};
vector<vector<account_id_type> > expected_key_refs{{test_acc.id, test_acc.id}};
auto signers = con.wallet_api_ptr->get_transaction_signers(signed_trx);
BOOST_CHECK(signers == expected_signers);
auto key_refs = con.wallet_api_ptr->get_key_references({test_bki.pub_key});
BOOST_CHECK(key_refs == expected_key_refs);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
/////////////////////// ///////////////////////
// Check account history pagination // Check account history pagination
@ -132,7 +187,7 @@ BOOST_AUTO_TEST_CASE( account_history_pagination )
{ {
INVOKE(create_new_account); INVOKE(create_new_account);
// attempt to give jmjatlanta some peerplay // attempt to give jmjatlanta some peerplay
BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta"); BOOST_TEST_MESSAGE("Transferring peerplay from Nathan to jmjatlanta");
for(int i = 1; i <= 199; i++) for(int i = 1; i <= 199; i++)
{ {
@ -142,13 +197,13 @@ BOOST_AUTO_TEST_CASE( account_history_pagination )
BOOST_CHECK(generate_block()); BOOST_CHECK(generate_block());
// now get account history and make sure everything is there (and no duplicates) // now get account history and make sure everything is there (and no duplicates)
std::vector<graphene::wallet::operation_detail> history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300); std::vector<graphene::wallet::operation_detail> history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300);
BOOST_CHECK_EQUAL(201u, history.size() ); BOOST_CHECK_EQUAL(201u, history.size() );
std::set<object_id_type> operation_ids; std::set<object_id_type> operation_ids;
for(auto& op : history) for(auto& op : history)
{ {
if( operation_ids.find(op.op.id) != operation_ids.end() ) if( operation_ids.find(op.op.id) != operation_ids.end() )
{ {
@ -162,4 +217,288 @@ BOOST_AUTO_TEST_CASE( account_history_pagination )
} }
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_FIXTURE_TEST_CASE( cli_get_available_transaction_signers, cli_fixture )
{
try
{
INVOKE(upgrade_nathan_account);
// register account
const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
con.wallet_api_ptr->register_account(
"test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
);
const auto &test_acc = con.wallet_api_ptr->get_account("test");
// create and sign transaction
signed_transaction trx;
trx.operations = {transfer_operation()};
// sign with test key
const auto test_privkey = wif_to_key( test_bki.wif_priv_key );
BOOST_REQUIRE( test_privkey );
trx.sign( *test_privkey, con.wallet_data.chain_id );
// sign with other keys
const auto privkey_1 = fc::ecc::private_key::generate();
trx.sign( privkey_1, con.wallet_data.chain_id );
const auto privkey_2 = fc::ecc::private_key::generate();
trx.sign( privkey_2, con.wallet_data.chain_id );
// verify expected result
flat_set<public_key_type> expected_signers = {test_bki.pub_key,
privkey_1.get_public_key(),
privkey_2.get_public_key()};
auto signers = con.wallet_api_ptr->get_transaction_signers(trx);
BOOST_CHECK(signers == expected_signers);
// blockchain has no references to unknown accounts (privkey_1, privkey_2)
// only test account available
vector<vector<account_id_type> > expected_key_refs;
expected_key_refs.push_back(vector<account_id_type>());
expected_key_refs.push_back(vector<account_id_type>());
expected_key_refs.push_back({test_acc.id, test_acc.id});
auto key_refs = con.wallet_api_ptr->get_key_references({expected_signers.begin(), expected_signers.end()});
std::sort(key_refs.begin(), key_refs.end());
BOOST_CHECK(key_refs == expected_key_refs);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_FIXTURE_TEST_CASE( cli_cant_get_signers_from_modified_transaction, cli_fixture )
{
try
{
INVOKE(upgrade_nathan_account);
// register account
const auto test_bki = con.wallet_api_ptr->suggest_brain_key();
con.wallet_api_ptr->register_account(
"test", test_bki.pub_key, test_bki.pub_key, "nathan", "nathan", 0, true
);
// create and sign transaction
signed_transaction trx;
trx.operations = {transfer_operation()};
// sign with test key
const auto test_privkey = wif_to_key( test_bki.wif_priv_key );
BOOST_REQUIRE( test_privkey );
trx.sign( *test_privkey, con.wallet_data.chain_id );
// modify transaction (MITM-attack)
trx.operations.clear();
// verify if transaction has no valid signature of test account
flat_set<public_key_type> expected_signers_of_valid_transaction = {test_bki.pub_key};
auto signers = con.wallet_api_ptr->get_transaction_signers(trx);
BOOST_CHECK(signers != expected_signers_of_valid_transaction);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
///////////////////
// Start a server and connect using the same calls as the CLI
// Set a voting proxy and be assured that it sticks
///////////////////
BOOST_FIXTURE_TEST_CASE( cli_set_voting_proxy, cli_fixture )
{
try {
INVOKE(create_new_account);
// grab account for comparison
account_object prior_voting_account = con.wallet_api_ptr->get_account("jmjatlanta");
// set the voting proxy to nathan
BOOST_TEST_MESSAGE("About to set voting proxy.");
signed_transaction voting_tx = con.wallet_api_ptr->set_voting_proxy("jmjatlanta", "nathan", true);
account_object after_voting_account = con.wallet_api_ptr->get_account("jmjatlanta");
// see if it changed
BOOST_CHECK(prior_voting_account.options.voting_account != after_voting_account.options.voting_account);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
///////////////////////
// Create a multi-sig account and verify that only when all signatures are
// signed, the transaction could be broadcast
///////////////////////
BOOST_AUTO_TEST_CASE( cli_multisig_transaction )
{
using namespace graphene::chain;
using namespace graphene::app;
std::shared_ptr<graphene::app::application> app1;
try {
fc::temp_directory app_dir( graphene::utilities::temp_directory_path() );
int server_port_number = 0;
app1 = start_application(app_dir, server_port_number);
// connect to the server
client_connection con(app1, app_dir, server_port_number);
BOOST_TEST_MESSAGE("Setting wallet password");
con.wallet_api_ptr->set_password("supersecret");
con.wallet_api_ptr->unlock("supersecret");
// import Nathan account
BOOST_TEST_MESSAGE("Importing nathan key");
std::vector<std::string> nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"};
BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3");
BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0]));
BOOST_TEST_MESSAGE("Importing nathan's balance");
std::vector<signed_transaction> import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true);
account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan");
// upgrade nathan
BOOST_TEST_MESSAGE("Upgrading Nathan to LTM");
signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true);
account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan");
// verify that the upgrade was successful
BOOST_CHECK_PREDICATE( std::not_equal_to<uint32_t>(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())(nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) );
BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());
// create a new multisig account
graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr->suggest_brain_key();
graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr->suggest_brain_key();
graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr->suggest_brain_key();
graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr->suggest_brain_key();
BOOST_CHECK(!bki1.brain_priv_key.empty());
BOOST_CHECK(!bki2.brain_priv_key.empty());
BOOST_CHECK(!bki3.brain_priv_key.empty());
BOOST_CHECK(!bki4.brain_priv_key.empty());
signed_transaction create_multisig_acct_tx;
account_create_operation account_create_op;
account_create_op.referrer = nathan_acct_after_upgrade.id;
account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage;
account_create_op.registrar = nathan_acct_after_upgrade.id;
account_create_op.name = "cifer.test";
account_create_op.owner = authority(1, bki1.pub_key, 1);
account_create_op.active = authority(2, bki2.pub_key, 1, bki3.pub_key, 1);
account_create_op.options.memo_key = bki4.pub_key;
account_create_op.fee = asset(1000000); // should be enough for creating account
create_multisig_acct_tx.operations.push_back(account_create_op);
con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true);
// attempt to give cifer.test some peerplays
BOOST_TEST_MESSAGE("Transferring peerplays from Nathan to cifer.test");
signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer("nathan", "cifer.test", "10000", "1.3.0", "Here are some BTS for your new account", true);
// transfer bts from cifer.test to nathan
BOOST_TEST_MESSAGE("Transferring peerplays from cifer.test to nathan");
auto dyn_props = app1->chain_database()->get_dynamic_global_properties();
account_object cifer_test = con.wallet_api_ptr->get_account("cifer.test");
// construct a transfer transaction
signed_transaction transfer_tx2;
transfer_operation xfer_op;
xfer_op.from = cifer_test.id;
xfer_op.to = nathan_acct_after_upgrade.id;
xfer_op.amount = asset(100000000);
xfer_op.fee = asset(3000000); // should be enough for transfer
transfer_tx2.operations.push_back(xfer_op);
// case1: sign a transaction without TaPoS and expiration fields
// expect: return a transaction with TaPoS and expiration filled
transfer_tx2 =
con.wallet_api_ptr->add_transaction_signature( transfer_tx2, false );
BOOST_CHECK( ( transfer_tx2.ref_block_num != 0 &&
transfer_tx2.ref_block_prefix != 0 ) ||
( transfer_tx2.expiration != fc::time_point_sec() ) );
// case2: broadcast without signature
// expect: exception with missing active authority
BOOST_CHECK_THROW(con.wallet_api_ptr->broadcast_transaction(transfer_tx2), fc::exception);
// case3:
// import one of the private keys for this new account in the wallet file,
// sign and broadcast with partial signatures
//
// expect: exception with missing active authority
BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki2.wif_priv_key));
BOOST_CHECK_THROW(con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true), fc::exception);
// case4: sign again as signature exists
// expect: num of signatures not increase
// transfer_tx2 = con.wallet_api_ptr->add_transaction_signature(transfer_tx2, false);
// BOOST_CHECK_EQUAL(transfer_tx2.signatures.size(), 1);
// case5:
// import another private key, sign and broadcast without full signatures
//
// expect: transaction broadcast successfully
BOOST_CHECK(con.wallet_api_ptr->import_key("cifer.test", bki3.wif_priv_key));
con.wallet_api_ptr->add_transaction_signature(transfer_tx2, true);
auto balances = con.wallet_api_ptr->list_account_balances( "cifer.test" );
for (auto b : balances) {
if (b.asset_id == asset_id_type()) {
BOOST_ASSERT(b == asset(900000000 - 3000000));
}
}
// wait for everything to finish up
fc::usleep(fc::seconds(1));
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
app1->shutdown();
}
graphene::wallet::plain_keys decrypt_keys( const std::string& password, const vector<char>& cipher_keys )
{
auto pw = fc::sha512::hash( password.c_str(), password.size() );
vector<char> decrypted = fc::aes_decrypt( pw, cipher_keys );
return fc::raw::unpack<graphene::wallet::plain_keys>( decrypted );
}
BOOST_AUTO_TEST_CASE( saving_keys_wallet_test )
{
cli_fixture cli;
cli.con.wallet_api_ptr->import_balance( "nathan", cli.nathan_keys, true );
cli.con.wallet_api_ptr->upgrade_account( "nathan", true );
std::string brain_key( "FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL" );
cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true );
BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) );
std::string path( cli.app_dir.path().generic_string() + "/wallet.json" );
graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as<graphene::wallet::wallet_data>( 2 * GRAPHENE_MAX_NESTED_OBJECTS );
BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan
BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1
BOOST_CHECK( wallet.pending_account_registrations["account1"].size() == 2 ); // account1 active key + account1 memo key
graphene::wallet::plain_keys pk = decrypt_keys( "supersecret", wallet.cipher_keys );
BOOST_CHECK( pk.keys.size() == 1 ); // nathan key
BOOST_CHECK( generate_block() );
fc::usleep( fc::seconds(1) );
wallet = fc::json::from_file( path ).as<graphene::wallet::wallet_data>( 2 * GRAPHENE_MAX_NESTED_OBJECTS );
BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1
BOOST_CHECK( wallet.pending_account_registrations.empty() );
BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) );
pk = decrypt_keys( "supersecret", wallet.cipher_keys );
BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -43,6 +43,7 @@
#include <graphene/chain/event_group_object.hpp> #include <graphene/chain/event_group_object.hpp>
#include <graphene/chain/event_object.hpp> #include <graphene/chain/event_object.hpp>
#include <graphene/chain/tournament_object.hpp> #include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/utilities/tempdir.hpp> #include <graphene/utilities/tempdir.hpp>
@ -311,6 +312,20 @@ void database_fixture::verify_asset_supplies( const database& db )
total_balances[betting_market_group.asset_id] += o.fees_collected; total_balances[betting_market_group.asset_id] += o.fees_collected;
} }
for (const offer_object &o : db.get_index_type<offer_index>().indices())
{
if (o.buying_item)
{
total_balances[o.maximum_price.asset_id] += o.maximum_price.amount;
}
else
{
if (o.bid_price)
{
total_balances[o.bid_price->asset_id] += o.bid_price->amount;
}
}
}
uint64_t sweeps_vestings = 0; uint64_t sweeps_vestings = 0;
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )

View file

@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
{ {
vector<authority> other; vector<authority> other;
flat_set<account_id_type> active_set, owner_set; flat_set<account_id_type> active_set, owner_set;
operation_get_required_authorities(op,active_set,owner_set,other); operation_get_required_authorities(op, active_set, owner_set, other, false);
BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu);
@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
active_set.clear(); active_set.clear();
other.clear(); other.clear();
operation_get_required_authorities(op.proposed_ops.front().op,active_set,owner_set,other); operation_get_required_authorities(op.proposed_ops.front().op, active_set, owner_set, other, false);
BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu);
@ -1055,7 +1055,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
flat_set<account_id_type> active_set, owner_set; flat_set<account_id_type> active_set, owner_set;
vector<authority> others; vector<authority> others;
trx.get_required_authorities( active_set, owner_set, others ); trx.get_required_authorities(active_set, owner_set, others, false);
PUSH_TX( db, trx, skip ); PUSH_TX( db, trx, skip );
@ -1189,6 +1189,14 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
return &(aid(db).owner); return &(aid(db).owner);
} ; } ;
auto get_custom = [&](
account_id_type id,
const operation& op
) -> vector<authority>
{
return db.get_account_custom_authorities(id, op);
} ;
auto chk = [&]( auto chk = [&](
const signed_transaction& tx, const signed_transaction& tx,
flat_set<public_key_type> available_keys, flat_set<public_key_type> available_keys,
@ -1196,9 +1204,12 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) ); set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) );
@ -1303,6 +1314,14 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
return &(aid(db).owner); return &(aid(db).owner);
} ; } ;
auto get_custom = [&](
account_id_type id,
const operation& op
) -> vector<authority>
{
return db.get_account_custom_authorities(id, op);
} ;
auto chk = [&]( auto chk = [&](
const signed_transaction& tx, const signed_transaction& tx,
flat_set<public_key_type> available_keys, flat_set<public_key_type> available_keys,
@ -1310,9 +1329,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
auto chk_min = [&]( auto chk_min = [&](
@ -1322,9 +1344,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); set<public_key_type> result_set = tx.minimize_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.minimize_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) ); set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) );
@ -1341,9 +1366,13 @@ 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( 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 } ) ); 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, false ),
fc::exception );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true ),
fc::exception );
sign( tx, alice_private_key ); 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, false );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true );
} }
catch(fc::exception& e) catch(fc::exception& e)
{ {

File diff suppressed because it is too large Load diff

View file

@ -630,6 +630,7 @@ BOOST_AUTO_TEST_CASE( voting )
generate_blocks( HARDFORK_GPOS_TIME ); generate_blocks( HARDFORK_GPOS_TIME );
generate_block(); generate_block();
auto now = HARDFORK_GPOS_TIME;
const auto& core = asset_id_type()(db); const auto& core = asset_id_type()(db);
// send some asset to alice and bob // send some asset to alice and bob
@ -651,7 +652,6 @@ BOOST_AUTO_TEST_CASE( voting )
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch()); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch());
// update default gpos for test speed // update default gpos for test speed
auto now = db.head_block_time();
// 5184000 = 60x60x24x60 = 60 days // 5184000 = 60x60x24x60 = 60 days
// 864000 = 60x60x24x10 = 10 days // 864000 = 60x60x24x10 = 10 days
update_gpos_global(5184000, 864000, now); update_gpos_global(5184000, 864000, now);
@ -754,7 +754,7 @@ BOOST_AUTO_TEST_CASE( voting )
advance_x_maint(5); advance_x_maint(5);
// a new GPOS period is in but vote from user is before the start. Whoever votes in 6th sub-period, votes will carry // a new GPOS period is in but vote from user is before the start. Whoever votes in 6th sub-period, votes will carry
now = db.head_block_time(); now = db.head_block_time();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch()); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), HARDFORK_GPOS_TIME.sec_since_epoch() + db.get_global_properties().parameters.gpos_period());
generate_block(); generate_block();

View file

@ -63,6 +63,7 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test )
lottery_options.end_date = db.head_block_time() + fc::minutes(5); lottery_options.end_date = db.head_block_time() + fc::minutes(5);
lottery_options.ticket_price = asset(100); lottery_options.ticket_price = asset(100);
lottery_options.winning_tickets = { 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT }; lottery_options.winning_tickets = { 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT };
//lottery_options.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
lottery_options.is_active = test_asset_id.instance.value % 2; lottery_options.is_active = test_asset_id.instance.value % 2;
lottery_options.ending_on_soldout = true; lottery_options.ending_on_soldout = true;
@ -482,4 +483,64 @@ BOOST_AUTO_TEST_CASE( try_to_end_empty_lottery_test )
} }
} }
BOOST_AUTO_TEST_CASE( lottery_winner_ticket_id_test )
{
try {
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
INVOKE( create_lottery_asset_test );
auto test_asset = test_asset_id(db);
for( int i = 1; i < 4; ++i ) {
transfer(account_id_type(), account_id_type(i), asset(2000000));
}
for( int i = 1; i < 4; ++i ) {
if( i == 4 ) continue;
ticket_purchase_operation tpo;
tpo.buyer = account_id_type(i);
tpo.lottery = test_asset.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
graphene::chain::test::set_expiration(db, trx);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
for( int i = 1; i < 4; ++i ) {
if( i == 4 ) continue;
ticket_purchase_operation tpo;
tpo.buyer = account_id_type(i);
tpo.lottery = test_asset.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
graphene::chain::test::set_expiration(db, trx);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
generate_block();
test_asset = test_asset_id(db);
uint64_t creator_balance_before_end = db.get_balance( account_id_type(), asset_id_type() ).amount.value;
uint64_t jackpot = db.get_balance( test_asset.get_id() ).amount.value;
uint16_t winners_part = 0;
for( uint8_t win: test_asset.lottery_options->winning_tickets )
winners_part += win;
while( db.head_block_time() < ( test_asset.lottery_options->end_date ) )
generate_block();
auto op_history = get_operation_history( account_id_type(1) ); //Can observe operation 79 to verify winner ticket number
for( auto h: op_history ) {
idump((h));
}
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
uint64_t creator_recieved = db.get_balance( account_id_type(), asset_id_type() ).amount.value - creator_balance_before_end;
test_asset = test_asset_id(db);
BOOST_CHECK(jackpot * test_asset.lottery_options->benefactors[0].share / GRAPHENE_100_PERCENT == creator_recieved);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,941 @@
#include <boost/test/unit_test.hpp>
#include "../common/database_fixture.hpp"
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/offer_object.hpp>
using namespace graphene::chain;
using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE(marketplace_tests, database_fixture)
offer_id_type buy_offer;
offer_id_type sell_offer;
BOOST_AUTO_TEST_CASE(nft_metadata_create_test)
{
BOOST_TEST_MESSAGE("nft_metadata_create_test");
generate_blocks(HARDFORK_NFT_TIME);
generate_block();
set_expiration(db, trx);
ACTORS((mdowner));
generate_block();
set_expiration(db, trx);
{
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
nft_metadata_create_operation op;
op.owner = mdowner_id;
op.name = "NFT Test";
op.symbol = "NFT";
op.base_uri = "http://nft.example.com";
op.revenue_partner = mdowner_id;
op.revenue_split = 1000;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
const auto &idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
auto obj = idx.begin();
BOOST_REQUIRE(obj != idx.end());
BOOST_CHECK(obj->owner == mdowner_id);
BOOST_CHECK(obj->name == "NFT Test");
BOOST_CHECK(obj->symbol == "NFT");
BOOST_CHECK(obj->base_uri == "http://nft.example.com");
}
BOOST_AUTO_TEST_CASE(nft_mint_test)
{
BOOST_TEST_MESSAGE("nft_mint_test");
INVOKE(nft_metadata_create_test);
set_expiration(db, trx);
ACTORS((alice)(bob)(charlie)(operator1)(operator2));
upgrade_to_lifetime_member(alice);
upgrade_to_lifetime_member(bob);
upgrade_to_lifetime_member(charlie);
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));
GET_ACTOR(mdowner);
generate_block();
set_expiration(db, trx);
{
BOOST_TEST_MESSAGE("Send nft_mint_operation");
const auto &idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
auto nft_md_obj = idx.begin();
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
op.approved = alice_id;
op.approved_operators.push_back(operator1_id);
op.approved_operators.push_back(operator2_id);
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
trx.clear();
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_mint_operation results");
const auto &idx = db.get_index_type<nft_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
auto obj = idx.begin();
BOOST_REQUIRE(obj != idx.end());
BOOST_CHECK(obj->owner == alice_id);
BOOST_CHECK(obj->approved_operators.size() == 2);
BOOST_CHECK(obj->approved_operators.at(0) == operator1_id);
BOOST_CHECK(obj->approved_operators.at(1) == operator2_id);
{
const auto &idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
auto nft_md_obj = idx.begin();
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
op.approved = alice_id;
op.approved_operators.push_back(operator1_id);
op.approved_operators.push_back(operator2_id);
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
trx.clear();
}
generate_block();
BOOST_REQUIRE(idx.size() == 2);
obj = idx.begin();
BOOST_REQUIRE(obj != idx.end());
BOOST_CHECK(obj->owner == alice_id);
BOOST_CHECK(obj->approved_operators.size() == 2);
BOOST_CHECK(obj->approved_operators.at(0) == operator1_id);
BOOST_CHECK(obj->approved_operators.at(1) == operator2_id);
const auto &nft2 = nft_id_type(1)(db);
BOOST_CHECK(nft2.owner == alice_id);
BOOST_CHECK(nft2.approved_operators.size() == 2);
BOOST_CHECK(nft2.approved_operators.at(0) == operator1_id);
BOOST_CHECK(nft2.approved_operators.at(1) == operator2_id);
}
BOOST_AUTO_TEST_CASE(create_sell_offer_test)
{
try
{
INVOKE(nft_mint_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
GET_ACTOR(operator2);
const asset_object &bitusd = create_bitasset("STUB");
{
offer_operation offer_op;
offer_op.item_ids.emplace(nft_id_type(0));
offer_op.item_ids.emplace(nft_id_type(1));
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);
auto op = trx.operations.back().get<offer_operation>();
REQUIRE_THROW_WITH_VALUE(op, offer_expiration_date, db.head_block_time());
REQUIRE_THROW_WITH_VALUE(op, issuer, bob_id);
// positive prices
REQUIRE_OP_VALIDATION_FAILURE(op, minimum_price, asset(-1));
REQUIRE_OP_VALIDATION_FAILURE(op, maximum_price, asset(-1));
REQUIRE_OP_VALIDATION_FAILURE(op, fee, asset(-1));
// min price > max price check
REQUIRE_OP_VALIDATION_FAILURE(op, maximum_price, asset(1));
// different asset for min/max
REQUIRE_OP_VALIDATION_FAILURE(op, minimum_price, asset(1, bitusd.id));
trx.clear();
trx.operations.push_back(offer_op);
sign(trx, alice_private_key);
PUSH_TX(db, trx);
trx.clear();
//generate_block();
const auto &idx = db.get_index_type<offer_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
const offer_object &d = offer_id_type(0)(db);
BOOST_CHECK(d.space_id == protocol_ids);
BOOST_CHECK(d.type_id == offer_object_type);
//empty bid
BOOST_CHECK(!d.bid_price);
BOOST_CHECK(!d.bidder);
// data integrity
BOOST_CHECK(d.issuer == alice_id);
BOOST_CHECK(d.maximum_price == asset(10000));
BOOST_CHECK(d.minimum_price == asset(10));
BOOST_CHECK(d.buying_item == false);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == true);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == true);
sell_offer = d.id;
}
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(buy_bid_for_sell_offer_test)
{
try
{
INVOKE(create_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
const auto &offer_obj = sell_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset(offer_obj.minimum_price.amount + 1, offer_obj.minimum_price.asset_id);
bid_op.bidder = bob_id;
trx.operations.push_back(bid_op);
asset exp_delta_bidder = -bid_op.bid_price;
int64_t bidder_balance = get_balance(bob_id(db), asset_id_type()(db));
auto op = trx.operations.back().get<bid_operation>();
// Positive asset values
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset(-1, asset_id_type()));
// Max price limit
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset(offer_obj.maximum_price.amount + 1, offer_obj.minimum_price.asset_id));
// Min Price Limit
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset(offer_obj.minimum_price.amount - 1, offer_obj.minimum_price.asset_id));
// Invalid offer
REQUIRE_THROW_WITH_VALUE(op, offer_id, offer_id_type(6));
// Owner bidder
REQUIRE_THROW_WITH_VALUE(op, bidder, alice_id);
// Operator bidder
REQUIRE_THROW_WITH_VALUE(op, bidder, operator1_id);
// Different asset
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset(50, asset_id_type(1)));
trx.clear();
trx.operations.push_back(bid_op);
sign(trx, bob_private_key);
PUSH_TX(db, trx);
trx.clear();
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bidder_balance + exp_delta_bidder.amount).value);
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == bob_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(10000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid_op.bid_price);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(second_buy_bid_for_sell_offer_test)
{
try
{
INVOKE(buy_bid_for_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(charlie);
GET_ACTOR(operator1);
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
int64_t charlie_balance = get_balance(charlie_id(db), asset_id_type()(db));
const auto &offer_obj = sell_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset((*offer_obj.bid_price).amount + 1, offer_obj.minimum_price.asset_id);
bid_op.bidder = charlie_id;
trx.operations.push_back(bid_op);
asset bid = bid_op.bid_price;
asset exp_delta_bidder1 = *offer_obj.bid_price;
asset exp_delta_bidder2 = -bid;
auto op = trx.operations.back().get<bid_operation>();
// Not a better bid than previous
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset((*offer_obj.bid_price).amount, offer_obj.minimum_price.asset_id));
trx.clear();
trx.operations.push_back(bid_op);
sign(trx, charlie_private_key);
PUSH_TX(db, trx);
trx.clear();
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bob_balance + exp_delta_bidder1.amount).value);
BOOST_CHECK_EQUAL(get_balance(charlie_id(db), asset_id_type()(db)),
(charlie_balance + exp_delta_bidder2.amount).value);
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == charlie_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(10000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(best_buy_bid_for_sell_offer)
{
try
{
INVOKE(second_buy_bid_for_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(charlie);
GET_ACTOR(operator1);
GET_ACTOR(mdowner);
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
int64_t charlie_balance = get_balance(charlie_id(db), asset_id_type()(db));
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
const auto &offer_obj = sell_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset(offer_obj.maximum_price.amount, offer_obj.minimum_price.asset_id);
bid_op.bidder = bob_id;
asset bid = bid_op.bid_price;
asset exp_delta_bidder1 = *offer_obj.bid_price;
asset exp_delta_bidder2 = -bid;
trx.operations.push_back(bid_op);
sign(trx, bob_private_key);
PUSH_TX(db, trx);
trx.clear();
// Check balances
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bob_balance + exp_delta_bidder2.amount).value);
BOOST_CHECK_EQUAL(get_balance(charlie_id(db), asset_id_type()(db)),
(charlie_balance + exp_delta_bidder1.amount).value);
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == bob_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(10000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
auto cached_offer_obj = offer_obj;
// Generate a block and offer should be finalized with bid
generate_block();
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + cached_offer_obj.maximum_price.amount).value - partner_fee);
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
mdowner_balance + partner_fee);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 1);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == bob_id) && (nft_id_type(1)(db).owner == bob_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(0)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Expired == history_obj.result);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(expire_with_bid_for_sell_offer_test)
{
INVOKE(second_buy_bid_for_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(charlie);
GET_ACTOR(mdowner);
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
const auto &offer_obj = sell_offer(db);
auto cached_offer_obj = offer_obj;
generate_blocks(5);
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
mdowner_balance + partner_fee);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + (*cached_offer_obj.bid_price).amount).value - partner_fee);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 1);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == charlie_id) && (nft_id_type(1)(db).owner == charlie_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(0)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Expired == history_obj.result);
}
BOOST_AUTO_TEST_CASE(expire_no_bid_for_sell_offer_test)
{
INVOKE(create_sell_offer_test);
GET_ACTOR(alice);
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
const auto &offer_obj = sell_offer(db);
auto cached_offer_obj = offer_obj;
generate_blocks(5);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
alice_balance);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 1);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == alice_id) && (nft_id_type(1)(db).owner == alice_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(0)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::ExpiredNoBid == history_obj.result);
}
BOOST_AUTO_TEST_CASE(create_buy_offer_test)
{
try
{
INVOKE(best_buy_bid_for_sell_offer);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
GET_ACTOR(operator2);
{
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
offer_operation offer_op;
offer_op.item_ids.emplace(nft_id_type(0));
offer_op.item_ids.emplace(nft_id_type(1));
offer_op.issuer = alice_id;
offer_op.buying_item = true;
offer_op.maximum_price = asset(11000);
offer_op.minimum_price = asset(10);
offer_op.offer_expiration_date = db.head_block_time() + fc::seconds(15);
trx.operations.push_back(offer_op);
auto op = trx.operations.back().get<offer_operation>();
REQUIRE_THROW_WITH_VALUE(op, issuer, bob_id);
trx.clear();
trx.operations.push_back(offer_op);
sign(trx, alice_private_key);
PUSH_TX(db, trx);
trx.clear();
asset exp_delta_bidder2 = -offer_op.maximum_price;
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + exp_delta_bidder2.amount).value);
const auto &idx = db.get_index_type<offer_index>().indices().get<by_id>();
BOOST_REQUIRE(idx.size() == 1);
const offer_object &d = offer_id_type(1)(db);
BOOST_CHECK(d.space_id == protocol_ids);
BOOST_CHECK(d.type_id == offer_object_type);
// empty bid
BOOST_CHECK(!d.bid_price);
BOOST_CHECK(!d.bidder);
// data integrity
BOOST_CHECK(d.issuer == alice_id);
BOOST_CHECK(d.maximum_price == asset(11000));
BOOST_CHECK(d.minimum_price == asset(10));
BOOST_CHECK(d.buying_item == true);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
buy_offer = d.id;
}
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(sell_bid_for_buy_offer_test)
{
try
{
INVOKE(create_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
const auto &offer_obj = buy_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset(offer_obj.minimum_price.amount + 2, offer_obj.minimum_price.asset_id);
bid_op.bidder = bob_id;
trx.operations.push_back(bid_op);
auto op = trx.operations.back().get<bid_operation>();
// Non Owner bidder
REQUIRE_THROW_WITH_VALUE(op, bidder, alice_id);
trx.clear();
trx.operations.push_back(bid_op);
sign(trx, bob_private_key);
PUSH_TX(db, trx);
trx.clear();
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == bob_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(11000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid_op.bid_price);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(second_sell_bid_for_buy_offer_test)
{
try
{
INVOKE(sell_bid_for_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(charlie);
GET_ACTOR(operator1);
const auto &offer_obj = buy_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset((*offer_obj.bid_price).amount - 1, offer_obj.minimum_price.asset_id);
bid_op.bidder = bob_id;
trx.operations.push_back(bid_op);
auto op = trx.operations.back().get<bid_operation>();
// Not a better bid than previous
REQUIRE_THROW_WITH_VALUE(op, bid_price, asset((*offer_obj.bid_price).amount, offer_obj.minimum_price.asset_id));
trx.clear();
trx.operations.push_back(bid_op);
sign(trx, bob_private_key);
PUSH_TX(db, trx);
trx.clear();
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == bob_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(11000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid_op.bid_price);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(best_sell_bid_for_buy_offer)
{
try
{
INVOKE(second_sell_bid_for_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(mdowner);
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
const auto &offer_obj = buy_offer(db);
bid_operation bid_op;
bid_op.offer_id = offer_obj.id;
bid_op.bid_price = asset(offer_obj.minimum_price.amount, offer_obj.minimum_price.asset_id);
bid_op.bidder = bob_id;
asset bid = bid_op.bid_price;
asset exp_delta_bidder1 = offer_obj.minimum_price;
asset exp_delta_bidder2 = offer_obj.maximum_price - offer_obj.minimum_price;
trx.operations.push_back(bid_op);
sign(trx, bob_private_key);
PUSH_TX(db, trx);
trx.clear();
//not empty bid
BOOST_CHECK(offer_obj.bid_price);
BOOST_CHECK(offer_obj.bidder);
// data integrity
BOOST_CHECK(offer_obj.bidder == bob_id);
BOOST_CHECK(offer_obj.issuer == alice_id);
BOOST_CHECK(offer_obj.maximum_price == asset(11000));
BOOST_CHECK(offer_obj.minimum_price == asset(10));
BOOST_CHECK(offer_obj.bid_price == bid);
BOOST_CHECK(db.item_locked(nft_id_type(0)));
BOOST_CHECK(db.item_locked(nft_id_type(1)));
auto cached_offer_obj = offer_obj;
// Generate a block and offer should be finalized with bid
generate_block();
// Check balances
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
mdowner_balance + partner_fee);
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bob_balance + exp_delta_bidder1.amount).value - partner_fee);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + exp_delta_bidder2.amount).value);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 2);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == alice_id) && (nft_id_type(1)(db).owner == alice_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(1)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Expired == history_obj.result);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(expire_with_bid_for_buy_offer_test)
{
INVOKE(second_sell_bid_for_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(mdowner);
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
int64_t mdowner_balance = get_balance(mdowner_id(db), asset_id_type()(db));
const auto &offer_obj = buy_offer(db);
auto cached_offer_obj = offer_obj;
generate_blocks(5);
int64_t partner_fee = 2 * static_cast<int64_t>((0.1 * (*cached_offer_obj.bid_price).amount.value)/2);
BOOST_CHECK_EQUAL(get_balance(mdowner_id(db), asset_id_type()(db)),
mdowner_balance + partner_fee);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + cached_offer_obj.maximum_price.amount - (*cached_offer_obj.bid_price).amount).value);
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bob_balance + (*cached_offer_obj.bid_price).amount).value - partner_fee);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 2);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == alice_id) && (nft_id_type(1)(db).owner == alice_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(1)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Expired == history_obj.result);
}
BOOST_AUTO_TEST_CASE(expire_no_bid_for_buy_offer_test)
{
INVOKE(create_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
const auto &offer_obj = buy_offer(db);
auto cached_offer_obj = offer_obj;
generate_blocks(5);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + cached_offer_obj.maximum_price.amount).value);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 2);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK((nft_id_type(0)(db).owner == bob_id) && (nft_id_type(1)(db).owner == bob_id));
// Get offer history object
const auto &history_obj = offer_history_id_type(1)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::ExpiredNoBid == history_obj.result);
}
BOOST_AUTO_TEST_CASE(cancel_sell_offer_no_bid_test)
{
try
{
INVOKE(create_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
const auto &offer_obj = sell_offer(db);
auto cached_offer_obj = offer_obj;
cancel_offer_operation cancel_op;
cancel_op.offer_id = offer_obj.id;
// Add non-issuer
cancel_op.issuer = bob_id;
trx.clear();
trx.operations.push_back(cancel_op);
sign(trx, bob_private_key);
BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception);
trx.clear();
// Add issuer
cancel_op.issuer = alice_id;
trx.operations.push_back(cancel_op);
sign(trx, alice_private_key);
PUSH_TX(db, trx);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 1);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
// Get offer history object
const auto &history_obj = offer_history_id_type(0)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Cancelled == history_obj.result);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(cancel_sell_offer_with_bid_test)
{
try
{
INVOKE(buy_bid_for_sell_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
const auto &offer_obj = sell_offer(db);
auto cached_offer_obj = offer_obj;
int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db));
cancel_offer_operation cancel_op;
cancel_op.offer_id = offer_obj.id;
// Add issuer
cancel_op.issuer = alice_id;
trx.clear();
trx.operations.push_back(cancel_op);
sign(trx, alice_private_key);
PUSH_TX(db, trx);
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 1);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)),
(bob_balance + (*cached_offer_obj.bid_price).amount).value);
// Get offer history object
const auto &history_obj = offer_history_id_type(0)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Cancelled == history_obj.result);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(cancel_buy_offer_with_bid_test)
{
try
{
INVOKE(sell_bid_for_buy_offer_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
GET_ACTOR(operator1);
const auto &offer_obj = buy_offer(db);
auto cached_offer_obj = offer_obj;
int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db));
cancel_offer_operation cancel_op;
cancel_op.offer_id = offer_obj.id;
cancel_op.issuer = alice_id;
trx.clear();
trx.operations.push_back(cancel_op);
sign(trx, alice_private_key);
PUSH_TX(db, trx);
trx.clear();
generate_block();
const auto &oidx = db.get_index_type<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
BOOST_REQUIRE(oidx.size() == 0);
BOOST_REQUIRE(ohidx.size() == 2);
BOOST_CHECK(db.item_locked(nft_id_type(0)) == false);
BOOST_CHECK(db.item_locked(nft_id_type(1)) == false);
BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)),
(alice_balance + cached_offer_obj.maximum_price.amount).value);
// Get offer history object
const auto &history_obj = offer_history_id_type(1)(db);
// History object data check
BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price);
BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder);
BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item);
BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer);
BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price);
BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price);
BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date);
BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids);
BOOST_CHECK(result_type::Cancelled == history_obj.result);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()

412
tests/tests/nft_tests.cpp Normal file
View file

@ -0,0 +1,412 @@
#include <boost/test/unit_test.hpp>
#include "../common/database_fixture.hpp"
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/nft_object.hpp>
using namespace graphene::chain;
using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE( nft_tests, database_fixture )
BOOST_AUTO_TEST_CASE( nft_metadata_create_test ) {
BOOST_TEST_MESSAGE("nft_metadata_create_test");
generate_blocks(HARDFORK_NFT_TIME);
generate_block();
generate_block();
set_expiration(db, trx);
ACTORS((mdowner));
generate_block();
set_expiration(db, trx);
{
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
nft_metadata_create_operation op;
op.owner = mdowner_id;
op.symbol = "NFT";
op.base_uri = "http://nft.example.com";
op.name = "123";
op.is_transferable = true;
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "1ab";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = ".abc";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "abc.";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "ABC";
BOOST_CHECK_NO_THROW(op.validate());
op.name = "abcdefghijklmnopq";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "ab";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "***";
BOOST_CHECK_THROW(op.validate(), fc::exception);
op.name = "a12";
BOOST_CHECK_NO_THROW(op.validate());
op.name = "a1b";
BOOST_CHECK_NO_THROW(op.validate());
op.name = "abc";
BOOST_CHECK_NO_THROW(op.validate());
op.name = "abc123defg12345";
BOOST_CHECK_NO_THROW(op.validate());
op.name = "NFT Test";
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj != idx.end() );
BOOST_CHECK( obj->owner == mdowner_id );
BOOST_CHECK( obj->name == "NFT Test" );
BOOST_CHECK( obj->symbol == "NFT" );
BOOST_CHECK( obj->base_uri == "http://nft.example.com" );
}
BOOST_AUTO_TEST_CASE( nft_metadata_update_test ) {
BOOST_TEST_MESSAGE("nft_metadata_update_test");
INVOKE(nft_metadata_create_test);
GET_ACTOR(mdowner);
{
BOOST_TEST_MESSAGE("Send nft_metadata_update_operation");
nft_metadata_update_operation op;
op.owner = mdowner_id;
op.name = "New NFT Test";
op.symbol = "New NFT";
op.base_uri = "new http://nft.example.com";
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_update_operation results");
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj != idx.end() );
BOOST_CHECK( obj->owner == mdowner_id );
BOOST_CHECK( obj->name == "New NFT Test" );
BOOST_CHECK( obj->symbol == "New NFT" );
BOOST_CHECK( obj->base_uri == "new http://nft.example.com" );
}
BOOST_AUTO_TEST_CASE( nft_mint_test ) {
BOOST_TEST_MESSAGE("nft_mint_test");
generate_block();
set_expiration(db, trx);
INVOKE(nft_metadata_create_test);
ACTORS((alice));
ACTORS((bob));
ACTORS((operator1));
ACTORS((operator2));
GET_ACTOR(mdowner);
generate_block();
set_expiration(db, trx);
{
BOOST_TEST_MESSAGE("Send nft_mint_operation");
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto nft_md_obj = idx.begin();
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
op.approved = alice_id;
op.approved_operators.push_back(operator1_id);
op.approved_operators.push_back(operator2_id);
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_mint_operation results");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj != idx.end() );
BOOST_CHECK( obj->owner == alice_id );
BOOST_CHECK( obj->approved_operators.size() == 2 );
BOOST_CHECK( obj->approved_operators.at(0) == operator1_id );
BOOST_CHECK( obj->approved_operators.at(1) == operator2_id );
}
BOOST_AUTO_TEST_CASE( nft_safe_transfer_from_test ) {
BOOST_TEST_MESSAGE("nft_safe_transfer_from_test");
INVOKE(nft_mint_test);
GET_ACTOR(alice);
GET_ACTOR(bob);
{
BOOST_TEST_MESSAGE("Check nft_safe_transfer_operation preconditions");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj->owner == alice_id );
}
{
BOOST_TEST_MESSAGE("Send nft_safe_transfer_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, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Check nft_safe_transfer_operation results");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj->owner == bob_id );
}
}
BOOST_AUTO_TEST_CASE( nft_approve_operation_test ) {
BOOST_TEST_MESSAGE("nft_approve_operation_test");
INVOKE(nft_mint_test);
GET_ACTOR(alice);
GET_ACTOR(operator1);
GET_ACTOR(operator2);
ACTORS((operator3));
{
BOOST_TEST_MESSAGE("Send nft_approve_operation");
nft_approve_operation op;
op.operator_ = alice_id;
op.approved = operator3_id;
op.token_id = nft_id_type(0);
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Check nft_approve_operation results");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto obj = idx.begin();
BOOST_REQUIRE( obj != idx.end() );
BOOST_CHECK( obj->approved == operator3_id );
BOOST_CHECK( obj->approved_operators.size() == 2 );
BOOST_CHECK( obj->approved_operators.at(0) == operator1_id );
BOOST_CHECK( obj->approved_operators.at(1) == operator2_id );
}
}
BOOST_AUTO_TEST_CASE( nft_set_approval_for_all_test ) {
BOOST_TEST_MESSAGE("nft_set_approval_for_all_test");
generate_block();
set_expiration(db, trx);
ACTORS((alice));
ACTORS((bob));
INVOKE(nft_metadata_create_test);
GET_ACTOR(mdowner);
generate_block();
set_expiration(db, trx);
BOOST_TEST_MESSAGE("Create NFT assets");
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
BOOST_REQUIRE( idx.size() == 1 );
auto nft_md_obj = idx.begin();
{
BOOST_TEST_MESSAGE("Send nft_mint_operation 1");
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_mint_operation 2");
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = bob_id;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_mint_operation 3");
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_mint_operation 4");
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = bob_id;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_mint_operation 5");
nft_mint_operation op;
op.payer = mdowner_id;
op.nft_metadata_id = nft_md_obj->id;
op.owner = alice_id;
trx.operations.push_back(op);
sign(trx, mdowner_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_approve_operation");
nft_set_approval_for_all_operation op;
op.owner = alice_id;
op.operator_ = bob_id;
op.approved = true;
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Send nft_approve_operation");
nft_set_approval_for_all_operation op;
op.owner = alice_id;
op.operator_ = bob_id;
op.approved = true;
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Check nft_approve_operation results");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_owner>();
const auto &idx_range = idx.equal_range(alice_id);
std::for_each(idx_range.first, idx_range.second, [&](const nft_object &obj) {
BOOST_CHECK( obj.approved_operators.size() == 1 );
BOOST_CHECK( obj.approved_operators.at(0) == bob_id );
});
}
{
BOOST_TEST_MESSAGE("Send nft_approve_operation");
nft_set_approval_for_all_operation op;
op.owner = alice_id;
op.operator_ = bob_id;
op.approved = false;
trx.operations.push_back(op);
sign(trx, alice_private_key);
PUSH_TX(db, trx, ~0);
}
generate_block();
{
BOOST_TEST_MESSAGE("Check nft_approve_operation results");
const auto& idx = db.get_index_type<nft_index>().indices().get<by_owner>();
const auto &idx_range = idx.equal_range(alice_id);
std::for_each(idx_range.first, idx_range.second, [&](const nft_object &obj) {
BOOST_CHECK( obj.approved_operators.size() == 0 );
});
}
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -1653,6 +1653,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test )
create_op.amount = amount; create_op.amount = amount;
create_op.policy = cdd_vesting_policy_initializer(vesting_seconds); create_op.policy = cdd_vesting_policy_initializer(vesting_seconds);
create_op.balance_type = vesting_balance_type::normal; create_op.balance_type = vesting_balance_type::normal;
tx.operations.push_back( create_op ); tx.operations.push_back( create_op );
set_expiration( db, tx ); set_expiration( db, tx );