Compare commits
37 commits
master
...
feature/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a360a0df77 | ||
|
|
10cdecd489 | ||
|
|
1e1f109e7e | ||
|
|
5783648cfb | ||
|
|
92eb5c441f | ||
|
|
218d826e65 | ||
|
|
b2e2d7e966 | ||
|
|
ff70a88921 | ||
|
|
7d80c042bf | ||
|
|
82e9d92089 | ||
|
|
10ecad7566 | ||
|
|
212c366b46 | ||
|
|
a62ffd73ea | ||
|
|
957b331d4e | ||
|
|
28b99d123d | ||
|
|
196c2539d9 | ||
|
|
f1e9a79d0b | ||
|
|
76ed35af2e | ||
|
|
d1f64ca919 | ||
|
|
e8ec6ae0e5 | ||
|
|
4cbb01d4e6 | ||
|
|
c0da3a2e84 | ||
|
|
db9a35fa17 | ||
|
|
99a24a6e39 | ||
|
|
3a8d8b9e98 | ||
|
|
e2c375e2a2 | ||
|
|
51cb17e2d8 | ||
|
|
0d79d5d0ee | ||
|
|
d0c72f7cf7 | ||
|
|
5093b94128 | ||
|
|
775eac55a9 | ||
|
|
21606396ee | ||
|
|
31ed9b4ddb | ||
|
|
3b4350b1c7 | ||
|
|
9f03e9a6ff | ||
|
|
de9ede0d5c | ||
|
|
3032185b5c |
51 changed files with 7112 additions and 39 deletions
|
|
@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux
|
||||||
endif( APPLE )
|
endif( APPLE )
|
||||||
|
|
||||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-class-memaccess -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof" )
|
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof" )
|
||||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,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;
|
||||||
|
|
@ -1857,6 +1890,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;
|
||||||
|
|
@ -1892,6 +1928,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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1919,6 +1966,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;
|
||||||
|
|
@ -1934,6 +1984,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;
|
||||||
}
|
}
|
||||||
|
|
@ -2240,6 +2292,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
|
||||||
|
|
@ -2303,6 +2356,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 //
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,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>
|
||||||
|
|
@ -709,8 +714,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;
|
||||||
};
|
};
|
||||||
|
|
@ -848,4 +966,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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,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
|
||||||
|
|
@ -112,9 +115,16 @@ 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
|
||||||
|
|
||||||
${HEADERS}
|
${HEADERS}
|
||||||
${PROTOCOL_HEADERS}
|
${PROTOCOL_HEADERS}
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||||
|
|
|
||||||
152
libraries/chain/custom_account_authority_evaluator.cpp
Normal file
152
libraries/chain/custom_account_authority_evaluator.cpp
Normal 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
|
||||||
133
libraries/chain/custom_permission_evaluator.cpp
Normal file
133
libraries/chain/custom_permission_evaluator.cpp
Normal 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
|
||||||
|
|
@ -721,6 +721,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
|
||||||
|
|
@ -790,7 +791,10 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
|
||||||
{
|
{
|
||||||
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
|
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
|
||||||
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
|
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
|
||||||
trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
|
auto get_custom = [&]( account_id_type id, const operation& op ) {
|
||||||
|
return get_account_custom_authorities(id, op);
|
||||||
|
};
|
||||||
|
trx.verify_authority( chain_id, get_active, get_owner, get_custom, get_global_properties().parameters.max_authority_depth );
|
||||||
}
|
}
|
||||||
|
|
||||||
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is
|
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
#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 <fc/smart_ref_impl.hpp>
|
#include <fc/smart_ref_impl.hpp>
|
||||||
|
|
||||||
|
|
@ -159,4 +162,34 @@ const witness_schedule_object& database::get_witness_schedule_object()const
|
||||||
return *_p_witness_schedule_obj;
|
return *_p_witness_schedule_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<authority> database::get_account_custom_authorities(account_id_type account, const operation& op)const
|
||||||
|
{
|
||||||
|
const auto& pindex = get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
|
||||||
|
const auto& cindex = get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
|
||||||
|
auto prange = pindex.equal_range(boost::make_tuple(account));
|
||||||
|
time_point_sec now = head_block_time();
|
||||||
|
vector<authority> custom_auths;
|
||||||
|
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
|
||||||
|
{
|
||||||
|
auto crange = cindex.equal_range(boost::make_tuple(pobj.id, op.which()));
|
||||||
|
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
|
||||||
|
{
|
||||||
|
if(now >= cobj.valid_from && now < cobj.valid_to)
|
||||||
|
{
|
||||||
|
custom_auths.push_back(pobj.auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return custom_auths;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool database::item_locked(const nft_id_type &item) const
|
||||||
|
{
|
||||||
|
const auto &offer_idx = get_index_type<offer_index>();
|
||||||
|
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());
|
||||||
|
}
|
||||||
} }
|
} }
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,11 @@
|
||||||
#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>
|
||||||
|
|
@ -77,6 +81,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/protocol/fee_schedule.hpp>
|
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||||
|
|
||||||
|
|
@ -163,12 +171,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()
|
||||||
{
|
{
|
||||||
|
|
@ -243,6 +259,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>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void database::initialize_indexes()
|
void database::initialize_indexes()
|
||||||
|
|
@ -284,6 +316,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 > >();
|
||||||
|
|
||||||
//Implementation object indexes
|
//Implementation object indexes
|
||||||
add_index< primary_index<transaction_index > >();
|
add_index< primary_index<transaction_index > >();
|
||||||
|
|
@ -313,6 +352,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 > >();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,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
|
||||||
|
|
||||||
|
|
@ -924,20 +925,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
|
||||||
if(db.head_block_time() >= HARDFORK_GPOS2_TIME)
|
db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
|
||||||
{
|
p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
|
||||||
db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
|
});
|
||||||
p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
db.modify(db.get_global_properties(), [now](global_property_object& p) {
|
|
||||||
p.parameters.extensions.value.gpos_period_start = now.sec_since_epoch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -1712,7 +1715,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();
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,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 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 )
|
||||||
|
|
|
||||||
|
|
@ -705,4 +705,30 @@ 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()}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
// GPOS2 HARDFORK Tuesday, 28 July 2020 01:00:00 GMT
|
|
||||||
#ifndef HARDFORK_GPOS2_TIME
|
|
||||||
#define HARDFORK_GPOS2_TIME (fc::time_point_sec( 1595898000 ))
|
|
||||||
#endif
|
|
||||||
4
libraries/chain/hardfork.d/NFT.hf
Normal file
4
libraries/chain/hardfork.d/NFT.hf
Normal 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
|
||||||
|
|
@ -234,3 +234,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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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) )
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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) )
|
||||||
|
|
@ -282,6 +282,7 @@ namespace graphene { namespace chain {
|
||||||
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 );
|
||||||
const witness_schedule_object& get_witness_schedule_object()const;
|
const witness_schedule_object& get_witness_schedule_object()const;
|
||||||
|
bool item_locked(const nft_id_type& item)const;
|
||||||
|
|
||||||
time_point_sec head_block_time()const;
|
time_point_sec head_block_time()const;
|
||||||
uint32_t head_block_num()const;
|
uint32_t head_block_num()const;
|
||||||
|
|
@ -294,6 +295,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();
|
||||||
|
|
@ -519,6 +521,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
|
||||||
///@{
|
///@{
|
||||||
|
|
|
||||||
59
libraries/chain/include/graphene/chain/nft_evaluator.hpp
Normal file
59
libraries/chain/include/graphene/chain/nft_evaluator.hpp
Normal 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
|
||||||
|
|
||||||
106
libraries/chain/include/graphene/chain/nft_object.hpp
Normal file
106
libraries/chain/include/graphene/chain/nft_object.hpp
Normal 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) )
|
||||||
|
|
||||||
47
libraries/chain/include/graphene/chain/offer_evaluator.hpp
Normal file
47
libraries/chain/include/graphene/chain/offer_evaluator.hpp
Normal 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
|
||||||
109
libraries/chain/include/graphene/chain/offer_object.hpp
Normal file
109
libraries/chain/include/graphene/chain/offer_object.hpp
Normal 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))
|
||||||
|
|
@ -48,6 +48,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chain_parameters
|
struct chain_parameters
|
||||||
|
|
@ -138,7 +142,16 @@ 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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
@ -156,6 +169,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)
|
||||||
)
|
)
|
||||||
|
|
||||||
FC_REFLECT( graphene::chain::chain_parameters,
|
FC_REFLECT( graphene::chain::chain_parameters,
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -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))
|
||||||
135
libraries/chain/include/graphene/chain/protocol/nft_ops.hpp
Normal file
135
libraries/chain/include/graphene/chain/protocol/nft_ops.hpp
Normal 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) )
|
||||||
|
|
||||||
143
libraries/chain/include/graphene/chain/protocol/offer.hpp
Normal file
143
libraries/chain/include/graphene/chain/protocol/offer.hpp
Normal 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));
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
|
@ -135,7 +139,23 @@ namespace graphene { namespace chain {
|
||||||
ticket_purchase_operation,
|
ticket_purchase_operation,
|
||||||
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
|
||||||
> operation;
|
> operation;
|
||||||
|
|
||||||
/// @} // operations group
|
/// @} // operations group
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ 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,
|
||||||
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
|
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
|
||||||
)const;
|
)const;
|
||||||
|
|
||||||
|
|
@ -148,6 +149,7 @@ 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,
|
||||||
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
|
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -162,6 +164,7 @@ namespace graphene { namespace chain {
|
||||||
const flat_set<public_key_type>& available_keys,
|
const 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,
|
||||||
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
|
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
|
|
@ -194,6 +197,7 @@ 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,
|
||||||
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
|
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
|
||||||
bool allow_committe = false,
|
bool allow_committe = 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>(),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -199,7 +204,8 @@ namespace graphene { namespace chain {
|
||||||
impl_betting_market_position_object_type,
|
impl_betting_market_position_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
|
||||||
};
|
};
|
||||||
|
|
||||||
//typedef fc::unsigned_int object_id_type;
|
//typedef fc::unsigned_int object_id_type;
|
||||||
|
|
@ -230,6 +236,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;
|
||||||
|
|
||||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||||
|
|
@ -256,6 +267,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;
|
||||||
|
|
||||||
// implementation types
|
// implementation types
|
||||||
class global_property_object;
|
class global_property_object;
|
||||||
|
|
@ -279,6 +295,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;
|
||||||
|
|
||||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||||
|
|
@ -307,6 +324,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 fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||||
typedef fc::ripemd160 block_id_type;
|
typedef fc::ripemd160 block_id_type;
|
||||||
|
|
@ -436,6 +454,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)
|
||||||
(OBJECT_TYPE_COUNT)
|
(OBJECT_TYPE_COUNT)
|
||||||
)
|
)
|
||||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||||
|
|
@ -463,6 +486,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)
|
||||||
)
|
)
|
||||||
|
|
||||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||||
|
|
@ -489,6 +513,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 )
|
||||||
|
|
@ -505,6 +530,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( graphene::chain::void_t, )
|
FC_REFLECT( graphene::chain::void_t, )
|
||||||
|
|
||||||
|
|
|
||||||
238
libraries/chain/nft_evaluator.cpp
Normal file
238
libraries/chain/nft_evaluator.cpp
Normal 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
|
||||||
|
|
||||||
338
libraries/chain/offer_evaluator.cpp
Normal file
338
libraries/chain/offer_evaluator.cpp
Normal 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
|
||||||
50
libraries/chain/offer_object.cpp
Normal file
50
libraries/chain/offer_object.cpp
Normal 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
|
||||||
|
|
@ -132,6 +132,71 @@ struct proposal_operation_hardfork_visitor
|
||||||
FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
|
FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator()(const custom_permission_create_operation &v) const {
|
||||||
|
FC_ASSERT( block_time >= HARDFORK_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!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// loop and self visit in proposals
|
// loop and self visit in proposals
|
||||||
void operator()(const proposal_create_operation &v) const {
|
void operator()(const proposal_create_operation &v) const {
|
||||||
for (const op_wrapper &op : v.proposed_ops)
|
for (const op_wrapper &op : v.proposed_ops)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ bool proposal_object::is_authorized_to_execute(database& db) const
|
||||||
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); },
|
||||||
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,
|
||||||
|
|
|
||||||
43
libraries/chain/protocol/custom_account_authority.cpp
Normal file
43
libraries/chain/protocol/custom_account_authority.cpp
Normal 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
|
||||||
85
libraries/chain/protocol/custom_permission.cpp
Normal file
85
libraries/chain/protocol/custom_permission.cpp
Normal 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
|
||||||
99
libraries/chain/protocol/nft.cpp
Normal file
99
libraries/chain/protocol/nft.cpp
Normal 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
|
||||||
57
libraries/chain/protocol/offer.cpp
Normal file
57
libraries/chain/protocol/offer.cpp
Normal 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
|
||||||
|
|
@ -248,6 +248,7 @@ 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,
|
||||||
uint32_t max_recursion_depth,
|
uint32_t max_recursion_depth,
|
||||||
bool allow_committe,
|
bool allow_committe,
|
||||||
const flat_set<account_id_type>& active_aprovals,
|
const flat_set<account_id_type>& active_aprovals,
|
||||||
|
|
@ -257,13 +258,6 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
|
||||||
flat_set<account_id_type> required_owner;
|
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 +265,35 @@ 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 );
|
||||||
|
|
||||||
|
auto itr = operation_required_active.begin();
|
||||||
|
while ( itr != operation_required_active.end() ) {
|
||||||
|
if ( approved_by_custom_authority( *itr, op ) )
|
||||||
|
itr = operation_required_active.erase( itr );
|
||||||
|
else
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !allow_committe )
|
||||||
|
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
|
||||||
|
invalid_committee_approval, "Committee account may only propose transactions" );
|
||||||
|
|
||||||
|
|
||||||
for( const auto& auth : other )
|
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,17 +348,41 @@ 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,
|
||||||
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;
|
||||||
|
|
||||||
|
auto approved_by_custom_authority = [&s, &get_custom](
|
||||||
|
account_id_type account,
|
||||||
|
operation op ) mutable {
|
||||||
|
auto custom_auths = get_custom( account, op );
|
||||||
|
for( const auto& auth : custom_auths )
|
||||||
|
if( s.check_authority( &auth ) ) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for( const auto& op : operations ) {
|
||||||
|
flat_set<account_id_type> operation_required_active;
|
||||||
|
operation_get_required_authorities( op, operation_required_active, required_owner, other );
|
||||||
|
|
||||||
|
auto itr = operation_required_active.begin();
|
||||||
|
while ( itr != operation_required_active.end() ) {
|
||||||
|
if ( approved_by_custom_authority( *itr, op ) )
|
||||||
|
itr = operation_required_active.erase( itr );
|
||||||
|
else
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
|
||||||
|
}
|
||||||
|
|
||||||
for( const auto& auth : other )
|
for( const auto& auth : other )
|
||||||
s.check_authority(&auth);
|
s.check_authority(&auth);
|
||||||
for( auto& owner : required_owner )
|
for( auto& owner : required_owner )
|
||||||
|
|
@ -359,10 +406,11 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
|
||||||
const flat_set<public_key_type>& available_keys,
|
const 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,
|
||||||
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, 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 +418,7 @@ 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, 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 +433,10 @@ 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,
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -1895,6 +1895,224 @@ class wallet_api
|
||||||
bool is_gpos,
|
bool is_gpos,
|
||||||
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 );
|
||||||
|
|
@ -2145,6 +2363,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)
|
||||||
|
|
@ -2157,4 +2399,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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3242,6 +3242,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;
|
||||||
|
|
@ -4541,8 +4675,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
|
||||||
{
|
{
|
||||||
|
|
@ -6157,6 +6367,358 @@ signed_transaction wallet_api::create_vesting_balance(string owner,
|
||||||
return my->sign_transaction( trx, broadcast );
|
return my->sign_transaction( trx, 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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 <fc/smart_ref_impl.hpp>
|
#include <fc/smart_ref_impl.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
@ -307,7 +308,21 @@ 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() )
|
||||||
sweeps_vestings += svbo.balance;
|
sweeps_vestings += svbo.balance;
|
||||||
|
|
|
||||||
|
|
@ -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,7 +1204,7 @@ 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, get_active, get_owner, get_custom );
|
||||||
//wdump( (result_set)(ref_set) );
|
//wdump( (result_set)(ref_set) );
|
||||||
return result_set == ref_set;
|
return result_set == ref_set;
|
||||||
} ;
|
} ;
|
||||||
|
|
@ -1303,6 +1311,14 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
|
||||||
return &(aid(db).owner);
|
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,7 +1326,7 @@ 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, get_active, get_owner, get_custom );
|
||||||
//wdump( (result_set)(ref_set) );
|
//wdump( (result_set)(ref_set) );
|
||||||
return result_set == ref_set;
|
return result_set == ref_set;
|
||||||
} ;
|
} ;
|
||||||
|
|
@ -1322,7 +1338,7 @@ 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, get_active, get_owner, get_custom );
|
||||||
//wdump( (result_set)(ref_set) );
|
//wdump( (result_set)(ref_set) );
|
||||||
return result_set == ref_set;
|
return result_set == ref_set;
|
||||||
} ;
|
} ;
|
||||||
|
|
@ -1341,9 +1357,9 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
|
||||||
BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) );
|
BOOST_CHECK( chk( 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 ), 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 );
|
||||||
}
|
}
|
||||||
catch(fc::exception& e)
|
catch(fc::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
1647
tests/tests/custom_permission_tests.cpp
Normal file
1647
tests/tests/custom_permission_tests.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
941
tests/tests/marketplace_tests.cpp
Normal file
941
tests/tests/marketplace_tests.cpp
Normal 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
412
tests/tests/nft_tests.cpp
Normal 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()
|
||||||
|
|
||||||
Loading…
Reference in a new issue