From d0c72f7cf775f73e664f6893ad4432398207a5f4 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Mon, 22 Jun 2020 21:36:42 +0200 Subject: [PATCH] Database API --- libraries/app/database_api.cpp | 74 +++++++++++++++++++ .../app/include/graphene/app/database_api.hpp | 40 ++++++++++ .../include/graphene/chain/nft_object.hpp | 2 + .../graphene/chain/protocol/nft_ops.hpp | 9 ++- libraries/chain/nft_evaluator.cpp | 19 +++-- tests/tests/nft_tests.cpp | 5 +- 6 files changed, 138 insertions(+), 11 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index df6458f3..67abe782 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -184,6 +184,12 @@ class database_api_impl : public std::enable_shared_from_this // gpos gpos_info get_gpos_info(const account_id_type account) const; + // NFT + uint64_t nft_get_balance(const account_id_type owner) const; + optional nft_owner_of(const nft_id_type token_id) const; + optional 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; + //private: const account_object* get_account_from_string( const std::string& name_or_id, bool throw_if_not_found = true ) const; @@ -2240,6 +2246,7 @@ graphene::app::gpos_info database_api::get_gpos_info(const account_id_type accou return my->get_gpos_info(account); } + 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 @@ -2303,6 +2310,73 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type return result; } +////////////////////////////////////////////////////////////////////// +// // +// 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().indices().get(); + const auto &idx_nft_range = idx_nft.equal_range(owner); + return std::distance(idx_nft_range.first, idx_nft_range.second); +} + +optional database_api::nft_owner_of(const nft_id_type token_id) const +{ + return my->nft_owner_of(token_id); +} + +optional database_api_impl::nft_owner_of(const nft_id_type token_id) const +{ + const auto &idx_nft = _db.get_index_type().indices().get(); + auto itr_nft = idx_nft.find(token_id); + if (itr_nft != idx_nft.end()) { + return itr_nft->owner; + } + return {}; +} + +optional database_api::nft_get_approved(const nft_id_type token_id) const +{ + return my->nft_get_approved(token_id); +} + +optional database_api_impl::nft_get_approved(const nft_id_type token_id) const +{ + const auto &idx_nft = _db.get_index_type().indices().get(); + 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().indices().get(); + 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; +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index dc8aba52..b7e9f5c3 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -48,6 +48,8 @@ #include #include +#include + #include #include @@ -709,6 +711,37 @@ class database_api */ gpos_info get_gpos_info(const account_id_type account) 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 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 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; private: @@ -848,4 +881,11 @@ FC_API(graphene::app::database_api, // gpos (get_gpos_info) + + // NFT + (nft_get_balance) + (nft_owner_of) + (nft_get_approved) + (nft_is_approved_for_all) + ) diff --git a/libraries/chain/include/graphene/chain/nft_object.hpp b/libraries/chain/include/graphene/chain/nft_object.hpp index 901d902c..8930cc91 100644 --- a/libraries/chain/include/graphene/chain/nft_object.hpp +++ b/libraries/chain/include/graphene/chain/nft_object.hpp @@ -13,6 +13,7 @@ namespace graphene { namespace chain { static const uint8_t type_id = nft_object_type; account_id_type owner; + account_id_type approved; vector approved_operators; std::string metadata; }; @@ -42,6 +43,7 @@ namespace graphene { namespace chain { FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object), (owner) + (approved) (approved_operators) (metadata) ) diff --git a/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp b/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp index fe55f7c3..bfe63eab 100644 --- a/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp @@ -10,6 +10,7 @@ namespace graphene { namespace chain { asset fee; account_id_type owner; + account_id_type approved; vector approved_operators; std::string metadata; @@ -21,12 +22,14 @@ namespace graphene { namespace chain { struct fee_parameters_type { uint64_t fee = 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 from; } + account_id_type fee_payer()const { return operator_; } }; struct nft_approve_operation : public base_operation @@ -62,8 +65,8 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::nft_create_operation, (fee) (owner) (approved_operators) (metadata) ) -FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (from) (to) (token_id) (data) ) +FC_REFLECT( graphene::chain::nft_create_operation, (fee) (owner) (approved) (approved_operators) (metadata) ) +FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) ) FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (owner) (approved) (token_id) ) FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) ) diff --git a/libraries/chain/nft_evaluator.cpp b/libraries/chain/nft_evaluator.cpp index e7e80aa1..8374e2e9 100644 --- a/libraries/chain/nft_evaluator.cpp +++ b/libraries/chain/nft_evaluator.cpp @@ -13,6 +13,7 @@ object_id_type nft_create_evaluator::do_apply( const nft_create_operation& op ) { try { const auto& new_nft_object = db().create( [&]( nft_object& obj ){ obj.owner = op.owner; + obj.approved = op.approved; obj.approved_operators = op.approved_operators; obj.metadata = op.metadata; }); @@ -28,17 +29,21 @@ void_result nft_safe_transfer_from_evaluator::do_evaluate( const nft_safe_transf auto itr_nft = idx_nft.find(op.token_id); FC_ASSERT( itr_nft != idx_nft.end(), "NFT does not exists" ); + 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.from); - FC_ASSERT( (itr_nft->owner == itr_from->id) || (itr_approved_op != itr_nft->approved_operators.end()), "Sender is not NFT owner or approved operator" ); + 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_nft->approved == itr_operator->id) || (itr_approved_op != itr_nft->approved_operators.end()), "Operator is not NFT owner or approved operator" ); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -51,6 +56,7 @@ object_id_type nft_safe_transfer_from_evaluator::do_apply( const nft_safe_transf { db().modify(*itr, [&op](nft_object &obj) { obj.owner = op.to; + obj.approved = {}; obj.approved_operators.clear(); }); } @@ -85,10 +91,11 @@ object_id_type nft_approve_evaluator::do_apply( const nft_approve_operation& op if (itr != idx.end()) { db().modify(*itr, [&op](nft_object &obj) { - 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); - } + 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; diff --git a/tests/tests/nft_tests.cpp b/tests/tests/nft_tests.cpp index de95225f..28e238b5 100644 --- a/tests/tests/nft_tests.cpp +++ b/tests/tests/nft_tests.cpp @@ -30,6 +30,7 @@ BOOST_AUTO_TEST_CASE( nft_create_test ) { nft_create_operation op; op.owner = alice_id; + op.approved = alice_id; op.approved_operators.push_back(operator1_id); op.approved_operators.push_back(operator2_id); op.metadata = "metadata"; @@ -130,10 +131,10 @@ BOOST_AUTO_TEST_CASE( nft_approve_operation_test ) { BOOST_REQUIRE( idx.size() == 1 ); auto obj = idx.begin(); BOOST_REQUIRE( obj != idx.end() ); - BOOST_CHECK( obj->approved_operators.size() == 3 ); + 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_CHECK( obj->approved_operators.at(2) == operator3_id ); } }