rippled
NFTOffers.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2022 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/main/Application.h>
21 #include <ripple/json/json_value.h>
22 #include <ripple/ledger/ReadView.h>
23 #include <ripple/ledger/View.h>
24 #include <ripple/net/RPCErr.h>
25 #include <ripple/protocol/ErrorCodes.h>
26 #include <ripple/protocol/jss.h>
27 #include <ripple/resource/Fees.h>
28 #include <ripple/rpc/Context.h>
29 #include <ripple/rpc/impl/RPCHelpers.h>
30 #include <ripple/rpc/impl/Tuning.h>
31 
32 namespace ripple {
33 
34 static void
36  Application const& app,
37  std::shared_ptr<SLE const> const& offer,
38  Json::Value& offers)
39 {
40  Json::Value& obj(offers.append(Json::objectValue));
41 
42  obj[jss::nft_offer_index] = to_string(offer->key());
43  obj[jss::flags] = (*offer)[sfFlags];
44  obj[jss::owner] = toBase58(offer->getAccountID(sfOwner));
45 
46  if (offer->isFieldPresent(sfDestination))
47  obj[jss::destination] = toBase58(offer->getAccountID(sfDestination));
48 
49  if (offer->isFieldPresent(sfExpiration))
50  obj[jss::expiration] = offer->getFieldU32(sfExpiration);
51 
52  offer->getFieldAmount(sfAmount).setJson(obj[jss::amount]);
53 }
54 
55 // {
56 // nft_id: <token hash>
57 // ledger_hash : <ledger>
58 // ledger_index : <ledger_index>
59 // limit: integer // optional
60 // marker: opaque // optional, resume previous query
61 // }
62 static Json::Value
64  RPC::JsonContext& context,
65  uint256 const& nftId,
66  Keylet const& directory)
67 {
68  unsigned int limit;
69  if (auto err = readLimitField(limit, RPC::Tuning::nftOffers, context))
70  return *err;
71 
73 
74  if (auto result = RPC::lookupLedger(ledger, context); !ledger)
75  return result;
76 
77  if (!ledger->exists(directory))
79 
80  Json::Value result;
81  result[jss::nft_id] = to_string(nftId);
82 
83  Json::Value& jsonOffers(result[jss::offers] = Json::arrayValue);
84 
86  unsigned int reserve(limit);
87  uint256 startAfter;
88  std::uint64_t startHint = 0;
89 
90  if (context.params.isMember(jss::marker))
91  {
92  // We have a start point. Use limit - 1 from the result and use the
93  // very last one for the resume.
94  Json::Value const& marker(context.params[jss::marker]);
95 
96  if (!marker.isString())
97  return RPC::expected_field_error(jss::marker, "string");
98 
99  if (!startAfter.parseHex(marker.asString()))
100  return rpcError(rpcINVALID_PARAMS);
101 
102  auto const sle = ledger->read(keylet::nftoffer(startAfter));
103 
104  if (!sle || nftId != sle->getFieldH256(sfNFTokenID))
105  return rpcError(rpcINVALID_PARAMS);
106 
107  startHint = sle->getFieldU64(sfNFTokenOfferNode);
108  appendNftOfferJson(context.app, sle, jsonOffers);
109  offers.reserve(reserve);
110  }
111  else
112  {
113  // We have no start point, limit should be one higher than requested.
114  offers.reserve(++reserve);
115  }
116 
117  if (!forEachItemAfter(
118  *ledger,
119  directory,
120  startAfter,
121  startHint,
122  reserve,
123  [&offers](std::shared_ptr<SLE const> const& offer) {
124  if (offer->getType() == ltNFTOKEN_OFFER)
125  {
126  offers.emplace_back(offer);
127  return true;
128  }
129 
130  return false;
131  }))
132  {
133  return rpcError(rpcINVALID_PARAMS);
134  }
135 
136  if (offers.size() == reserve)
137  {
138  result[jss::limit] = limit;
139  result[jss::marker] = to_string(offers.back()->key());
140  offers.pop_back();
141  }
142 
143  for (auto const& offer : offers)
144  appendNftOfferJson(context.app, offer, jsonOffers);
145 
147  return result;
148 }
149 
152 {
153  if (!context.params.isMember(jss::nft_id))
154  return RPC::missing_field_error(jss::nft_id);
155 
156  uint256 nftId;
157 
158  if (!nftId.parseHex(context.params[jss::nft_id].asString()))
159  return RPC::invalid_field_error(jss::nft_id);
160 
161  return enumerateNFTOffers(context, nftId, keylet::nft_sells(nftId));
162 }
163 
166 {
167  if (!context.params.isMember(jss::nft_id))
168  return RPC::missing_field_error(jss::nft_id);
169 
170  uint256 nftId;
171 
172  if (!nftId.parseHex(context.params[jss::nft_id].asString()))
173  return RPC::invalid_field_error(jss::nft_id);
174 
175  return enumerateNFTOffers(context, nftId, keylet::nft_buys(nftId));
176 }
177 
178 } // namespace ripple
ripple::enumerateNFTOffers
static Json::Value enumerateNFTOffers(RPC::JsonContext &context, uint256 const &nftId, Keylet const &directory)
Definition: NFTOffers.cpp:63
ripple::Application
Definition: Application.h:115
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
std::shared_ptr
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
ripple::rpcError
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
ripple::forEachItemAfter
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition: View.cpp:394
ripple::sfDestination
const SF_ACCOUNT sfDestination
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
ripple::sfNFTokenID
const SF_UINT256 sfNFTokenID
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::sfOwner
const SF_ACCOUNT sfOwner
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
std::vector
STL class.
ripple::keylet::nftoffer
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition: Indexes.cpp:355
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::RPC::lookupLedger
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Definition: RPCHelpers.cpp:675
ripple::sfNFTokenOfferNode
const SF_UINT64 sfNFTokenOfferNode
ripple::rpcOBJECT_NOT_FOUND
@ rpcOBJECT_NOT_FOUND
Definition: ErrorCodes.h:143
ripple::RPC::expected_field_error
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition: ErrorCodes.h:328
ripple::sfExpiration
const SF_UINT32 sfExpiration
ripple::RPC::missing_field_error
Json::Value missing_field_error(std::string const &name)
Definition: ErrorCodes.h:262
ripple::doNFTBuyOffers
Json::Value doNFTBuyOffers(RPC::JsonContext &)
Definition: NFTOffers.cpp:165
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 256 >
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::RPC::Tuning::nftOffers
static constexpr LimitRange nftOffers
Limits for the nft_buy_offers & nft_sell_offers commands.
Definition: rpc/impl/Tuning.h:58
ripple::doNFTSellOffers
Json::Value doNFTSellOffers(RPC::JsonContext &)
Definition: NFTOffers.cpp:151
ripple::appendNftOfferJson
static void appendNftOfferJson(Application const &app, std::shared_ptr< SLE const > const &offer, Json::Value &offers)
Definition: NFTOffers.cpp:35
ripple::ltNFTOKEN_OFFER
@ ltNFTOKEN_OFFER
A ledger object which identifies an offer to buy or sell an NFT.
Definition: LedgerFormats.h:162
ripple::keylet::nft_sells
Keylet nft_sells(uint256 const &id) noexcept
The directory of sell offers for the specified NFT.
Definition: Indexes.cpp:368
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
std::uint64_t
ripple::keylet::nft_buys
Keylet nft_buys(uint256 const &id) noexcept
The directory of buy offers for the specified NFT.
Definition: Indexes.cpp:362
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::base_uint::parseHex
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:496
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::RPC::invalid_field_error
Json::Value invalid_field_error(std::string const &name)
Definition: ErrorCodes.h:304
Json::Value
Represents a JSON value.
Definition: json_value.h:145
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469