mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 03:35:55 +00:00
Add NFT RPC infrastructure
This commit is contained in:
@@ -62,6 +62,8 @@ target_sources(clio PRIVATE
|
|||||||
src/rpc/handlers/AccountTx.cpp
|
src/rpc/handlers/AccountTx.cpp
|
||||||
# Dex
|
# Dex
|
||||||
src/rpc/handlers/BookOffers.cpp
|
src/rpc/handlers/BookOffers.cpp
|
||||||
|
# NFT
|
||||||
|
src/rpc/handlers/NFTOffers.cpp
|
||||||
# Payment Channel
|
# Payment Channel
|
||||||
src/rpc/handlers/ChannelAuthorize.cpp
|
src/rpc/handlers/ChannelAuthorize.cpp
|
||||||
src/rpc/handlers/ChannelVerify.cpp
|
src/rpc/handlers/ChannelVerify.cpp
|
||||||
|
|||||||
@@ -259,7 +259,8 @@ BackendInterface::fetchLedgerPage(
|
|||||||
ripple::uint256 const& curCursor = keys.size() ? keys.back()
|
ripple::uint256 const& curCursor = keys.size() ? keys.back()
|
||||||
: cursor ? *cursor
|
: cursor ? *cursor
|
||||||
: firstKey;
|
: firstKey;
|
||||||
uint32_t seq = outOfOrder ? range->maxSequence : ledgerSequence;
|
std::uint32_t const seq =
|
||||||
|
outOfOrder ? range->maxSequence : ledgerSequence;
|
||||||
auto succ = fetchSuccessorKey(curCursor, seq, yield);
|
auto succ = fetchSuccessorKey(curCursor, seq, yield);
|
||||||
if (!succ)
|
if (!succ)
|
||||||
reachedEnd = true;
|
reachedEnd = true;
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ doAccountCurrencies(Context const& context);
|
|||||||
Result
|
Result
|
||||||
doAccountLines(Context const& context);
|
doAccountLines(Context const& context);
|
||||||
|
|
||||||
|
Result
|
||||||
|
doAccountNFTs(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doAccountObjects(Context const& context);
|
doAccountObjects(Context const& context);
|
||||||
|
|
||||||
@@ -45,6 +48,13 @@ doChannelVerify(Context const& context);
|
|||||||
Result
|
Result
|
||||||
doBookOffers(Context const& context);
|
doBookOffers(Context const& context);
|
||||||
|
|
||||||
|
// NFT methods
|
||||||
|
Result
|
||||||
|
doNFTBuyOffers(Context const& context);
|
||||||
|
|
||||||
|
Result
|
||||||
|
doNFTSellOffers(Context const& context);
|
||||||
|
|
||||||
// ledger methods
|
// ledger methods
|
||||||
Result
|
Result
|
||||||
doLedger(Context const& context);
|
doLedger(Context const& context);
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ static std::unordered_map<std::string, std::function<Result(Context const&)>>
|
|||||||
{"account_currencies", &doAccountCurrencies},
|
{"account_currencies", &doAccountCurrencies},
|
||||||
{"account_info", &doAccountInfo},
|
{"account_info", &doAccountInfo},
|
||||||
{"account_lines", &doAccountLines},
|
{"account_lines", &doAccountLines},
|
||||||
|
{"account_nfts", &doAccountNFTs},
|
||||||
{"account_objects", &doAccountObjects},
|
{"account_objects", &doAccountObjects},
|
||||||
{"account_offers", &doAccountOffers},
|
{"account_offers", &doAccountOffers},
|
||||||
{"account_tx", &doAccountTx},
|
{"account_tx", &doAccountTx},
|
||||||
@@ -137,6 +138,8 @@ static std::unordered_map<std::string, std::function<Result(Context const&)>>
|
|||||||
{"ledger_entry", &doLedgerEntry},
|
{"ledger_entry", &doLedgerEntry},
|
||||||
{"ledger_range", &doLedgerRange},
|
{"ledger_range", &doLedgerRange},
|
||||||
{"ledger_data", &doLedgerData},
|
{"ledger_data", &doLedgerData},
|
||||||
|
{"nft_buy_offers", &doNFTBuyOffers},
|
||||||
|
{"nft_sell_offers", &doNFTSellOffers},
|
||||||
{"subscribe", &doSubscribe},
|
{"subscribe", &doSubscribe},
|
||||||
{"server_info", &doServerInfo},
|
{"server_info", &doServerInfo},
|
||||||
{"unsubscribe", &doUnsubscribe},
|
{"unsubscribe", &doUnsubscribe},
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ struct Status
|
|||||||
: error(error_), message(message_)
|
: error(error_), message(message_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Status(Error error_, std::string strCode_, std::string message_)
|
Status(Error error_, std::string strCode_, std::string message_)
|
||||||
: error(error_), strCode(strCode_), message(message_)
|
: error(error_), strCode(strCode_), message(message_)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ getBool(boost::json::object const& request, std::string const& field)
|
|||||||
else
|
else
|
||||||
throw InvalidParamsError("Invalid field " + field + ", not bool.");
|
throw InvalidParamsError("Invalid field " + field + ", not bool.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
getBool(
|
getBool(
|
||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
@@ -24,6 +25,7 @@ getBool(
|
|||||||
else
|
else
|
||||||
return dfault;
|
return dfault;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
getRequiredBool(boost::json::object const& request, std::string const& field)
|
getRequiredBool(boost::json::object const& request, std::string const& field)
|
||||||
{
|
{
|
||||||
@@ -152,6 +154,7 @@ getString(boost::json::object const& request, std::string const& field)
|
|||||||
else
|
else
|
||||||
throw InvalidParamsError("Invalid field " + field + ", not string.");
|
throw InvalidParamsError("Invalid field " + field + ", not string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
getRequiredString(boost::json::object const& request, std::string const& field)
|
getRequiredString(boost::json::object const& request, std::string const& field)
|
||||||
{
|
{
|
||||||
@@ -160,6 +163,7 @@ getRequiredString(boost::json::object const& request, std::string const& field)
|
|||||||
else
|
else
|
||||||
throw InvalidParamsError("Missing field " + field);
|
throw InvalidParamsError("Missing field " + field);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
getString(
|
getString(
|
||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
@@ -172,6 +176,112 @@ getString(
|
|||||||
return dfault;
|
return dfault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getHexMarker(boost::json::object const& request, ripple::uint256& marker)
|
||||||
|
{
|
||||||
|
if (request.contains(JS(marker)))
|
||||||
|
{
|
||||||
|
if (!request.at(JS(marker)).is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
|
if (!marker.parseHex(request.at(JS(marker)).as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "malformedMarker"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getLimit(boost::json::object const& request, std::uint32_t& limit)
|
||||||
|
{
|
||||||
|
if (request.contains(JS(limit)))
|
||||||
|
{
|
||||||
|
if (!request.at(JS(limit)).is_int64())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
||||||
|
|
||||||
|
limit = request.at(JS(limit)).as_int64();
|
||||||
|
if (limit <= 0)
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getAccount(
|
||||||
|
boost::json::object const& request,
|
||||||
|
ripple::AccountID& account,
|
||||||
|
boost::string_view const& field,
|
||||||
|
bool required)
|
||||||
|
{
|
||||||
|
if (!request.contains(field))
|
||||||
|
{
|
||||||
|
if (required)
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS, field.to_string() + "Missing"};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request.at(field).is_string())
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS, field.to_string() + "NotString"};
|
||||||
|
|
||||||
|
if (auto a = accountFromStringStrict(request.at(field).as_string().c_str());
|
||||||
|
a)
|
||||||
|
{
|
||||||
|
account = a.value();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, field.to_string() + "Malformed"};
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getAccount(boost::json::object const& request, ripple::AccountID& accountId)
|
||||||
|
{
|
||||||
|
return getAccount(request, accountId, JS(account), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getAccount(
|
||||||
|
boost::json::object const& request,
|
||||||
|
ripple::AccountID& destAccount,
|
||||||
|
boost::string_view const& field)
|
||||||
|
{
|
||||||
|
return getAccount(request, destAccount, field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getTaker(boost::json::object const& request, ripple::AccountID& takerID)
|
||||||
|
{
|
||||||
|
if (request.contains(JS(taker)))
|
||||||
|
{
|
||||||
|
auto parsed = parseTaker(request.at(JS(taker)));
|
||||||
|
if (auto status = std::get_if<Status>(&parsed))
|
||||||
|
return *status;
|
||||||
|
else
|
||||||
|
takerID = std::get<ripple::AccountID>(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Status
|
||||||
|
getChannelId(boost::json::object const& request, ripple::uint256& channelId)
|
||||||
|
{
|
||||||
|
if (!request.contains(JS(channel_id)))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingChannelID"};
|
||||||
|
|
||||||
|
if (!request.at(JS(channel_id)).is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "channelIDNotString"};
|
||||||
|
|
||||||
|
if (!channelId.parseHex(request.at(JS(channel_id)).as_string().c_str()))
|
||||||
|
return Status{Error::rpcCHANNEL_MALFORMED, "malformedChannelID"};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ripple::STAmount>
|
std::optional<ripple::STAmount>
|
||||||
getDeliveredAmount(
|
getDeliveredAmount(
|
||||||
std::shared_ptr<ripple::STTx const> const& txn,
|
std::shared_ptr<ripple::STTx const> const& txn,
|
||||||
@@ -537,11 +647,35 @@ traverseOwnedNodes(
|
|||||||
if (!parsedCursor)
|
if (!parsedCursor)
|
||||||
return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor");
|
return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor");
|
||||||
|
|
||||||
auto cursor = AccountCursor({beast::zero, 0});
|
|
||||||
|
|
||||||
auto [hexCursor, startHint] = *parsedCursor;
|
auto [hexCursor, startHint] = *parsedCursor;
|
||||||
|
|
||||||
auto const rootIndex = ripple::keylet::ownerDir(accountID);
|
return traverseOwnedNodes(
|
||||||
|
backend,
|
||||||
|
ripple::keylet::ownerDir(accountID),
|
||||||
|
hexCursor,
|
||||||
|
startHint,
|
||||||
|
sequence,
|
||||||
|
limit,
|
||||||
|
jsonCursor,
|
||||||
|
yield,
|
||||||
|
atOwnedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<Status, AccountCursor>
|
||||||
|
traverseOwnedNodes(
|
||||||
|
BackendInterface const& backend,
|
||||||
|
ripple::Keylet const& owner,
|
||||||
|
ripple::uint256 const& hexMarker,
|
||||||
|
std::uint32_t const startHint,
|
||||||
|
std::uint32_t sequence,
|
||||||
|
std::uint32_t limit,
|
||||||
|
std::optional<std::string> jsonCursor,
|
||||||
|
boost::asio::yield_context& yield,
|
||||||
|
std::function<void(ripple::SLE)> atOwnedNode)
|
||||||
|
{
|
||||||
|
auto cursor = AccountCursor({beast::zero, 0});
|
||||||
|
|
||||||
|
auto const rootIndex = owner;
|
||||||
auto currentIndex = rootIndex;
|
auto currentIndex = rootIndex;
|
||||||
|
|
||||||
std::vector<ripple::uint256> keys;
|
std::vector<ripple::uint256> keys;
|
||||||
@@ -550,7 +684,7 @@ traverseOwnedNodes(
|
|||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
// If startAfter is not zero try jumping to that page using the hint
|
// If startAfter is not zero try jumping to that page using the hint
|
||||||
if (hexCursor.isNonZero())
|
if (hexMarker.isNonZero())
|
||||||
{
|
{
|
||||||
auto const hintIndex = ripple::keylet::page(rootIndex, startHint);
|
auto const hintIndex = ripple::keylet::page(rootIndex, startHint);
|
||||||
auto hintDir =
|
auto hintDir =
|
||||||
@@ -563,7 +697,7 @@ traverseOwnedNodes(
|
|||||||
|
|
||||||
for (auto const& key : sle.getFieldV256(ripple::sfIndexes))
|
for (auto const& key : sle.getFieldV256(ripple::sfIndexes))
|
||||||
{
|
{
|
||||||
if (key == hexCursor)
|
if (key == hexMarker)
|
||||||
{
|
{
|
||||||
// We found the hint, we can start here
|
// We found the hint, we can start here
|
||||||
currentIndex = hintIndex;
|
currentIndex = hintIndex;
|
||||||
@@ -589,7 +723,7 @@ traverseOwnedNodes(
|
|||||||
{
|
{
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
if (key == hexCursor)
|
if (key == hexMarker)
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -678,6 +812,23 @@ traverseOwnedNodes(
|
|||||||
return AccountCursor({beast::zero, 0});
|
return AccountCursor({beast::zero, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ripple::SLE const>
|
||||||
|
read(
|
||||||
|
ripple::Keylet const& keylet,
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
Context const& context)
|
||||||
|
{
|
||||||
|
if (auto const blob = context.backend->fetchLedgerObject(
|
||||||
|
keylet.key, lgrInfo.seq, context.yield);
|
||||||
|
blob)
|
||||||
|
{
|
||||||
|
return std::make_shared<ripple::SLE const>(
|
||||||
|
ripple::SerialIter{blob->data(), blob->size()}, keylet.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ripple::Seed>
|
std::optional<ripple::Seed>
|
||||||
parseRippleLibSeed(boost::json::value const& value)
|
parseRippleLibSeed(boost::json::value const& value)
|
||||||
{
|
{
|
||||||
@@ -1280,6 +1431,7 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<Status, ripple::AccountID>
|
std::variant<Status, ripple::AccountID>
|
||||||
parseTaker(boost::json::value const& taker)
|
parseTaker(boost::json::value const& taker)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,13 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPC.h>
|
#include <rpc/RPC.h>
|
||||||
|
|
||||||
|
// Useful macro for borrowing from ripple::jss
|
||||||
|
// static strings. (J)son (S)trings
|
||||||
|
#define JS(x) ripple::jss::x.c_str()
|
||||||
|
|
||||||
|
// Access (SF)ield name (S)trings
|
||||||
|
#define SFS(x) ripple::x.jsonName.c_str()
|
||||||
|
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
std::optional<ripple::AccountID>
|
std::optional<ripple::AccountID>
|
||||||
accountFromStringStrict(std::string const& account);
|
accountFromStringStrict(std::string const& account);
|
||||||
@@ -93,6 +100,24 @@ traverseOwnedNodes(
|
|||||||
boost::asio::yield_context& yield,
|
boost::asio::yield_context& yield,
|
||||||
std::function<void(ripple::SLE)> atOwnedNode);
|
std::function<void(ripple::SLE)> atOwnedNode);
|
||||||
|
|
||||||
|
std::variant<Status, AccountCursor>
|
||||||
|
traverseOwnedNodes(
|
||||||
|
BackendInterface const& backend,
|
||||||
|
ripple::Keylet const& owner,
|
||||||
|
ripple::uint256 const& hexMarker,
|
||||||
|
std::uint32_t const startHint,
|
||||||
|
std::uint32_t sequence,
|
||||||
|
std::uint32_t limit,
|
||||||
|
std::optional<std::string> jsonCursor,
|
||||||
|
boost::asio::yield_context& yield,
|
||||||
|
std::function<void(ripple::SLE)> atOwnedNode);
|
||||||
|
|
||||||
|
std::shared_ptr<ripple::SLE const>
|
||||||
|
read(
|
||||||
|
ripple::Keylet const& keylet,
|
||||||
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
|
Context const& context);
|
||||||
|
|
||||||
std::variant<Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
std::variant<Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
||||||
keypairFromRequst(boost::json::object const& request);
|
keypairFromRequst(boost::json::object const& request);
|
||||||
|
|
||||||
@@ -200,5 +225,27 @@ getString(
|
|||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
std::string const& field,
|
std::string const& field,
|
||||||
std::string dfault);
|
std::string dfault);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getHexMarker(boost::json::object const& request, ripple::uint256& marker);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getLimit(boost::json::object const& request, std::uint32_t& limit);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getAccount(boost::json::object const& request, ripple::AccountID& accountId);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getAccount(
|
||||||
|
boost::json::object const& request,
|
||||||
|
ripple::AccountID& destAccount,
|
||||||
|
boost::string_view const& field);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getTaker(boost::json::object const& request, ripple::AccountID& takerID);
|
||||||
|
|
||||||
|
Status
|
||||||
|
getChannelId(boost::json::object const& request, ripple::uint256& channelId);
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,27 +17,27 @@ void
|
|||||||
addChannel(boost::json::array& jsonLines, ripple::SLE const& line)
|
addChannel(boost::json::array& jsonLines, ripple::SLE const& line)
|
||||||
{
|
{
|
||||||
boost::json::object jDst;
|
boost::json::object jDst;
|
||||||
jDst["channel_id"] = ripple::to_string(line.key());
|
jDst[JS(channel_id)] = ripple::to_string(line.key());
|
||||||
jDst["account"] = ripple::to_string(line.getAccountID(ripple::sfAccount));
|
jDst[JS(account)] = ripple::to_string(line.getAccountID(ripple::sfAccount));
|
||||||
jDst["destination_account"] =
|
jDst[JS(destination_account)] =
|
||||||
ripple::to_string(line.getAccountID(ripple::sfDestination));
|
ripple::to_string(line.getAccountID(ripple::sfDestination));
|
||||||
jDst["amount"] = line[ripple::sfAmount].getText();
|
jDst[JS(amount)] = line[ripple::sfAmount].getText();
|
||||||
jDst["balance"] = line[ripple::sfBalance].getText();
|
jDst[JS(balance)] = line[ripple::sfBalance].getText();
|
||||||
if (publicKeyType(line[ripple::sfPublicKey]))
|
if (publicKeyType(line[ripple::sfPublicKey]))
|
||||||
{
|
{
|
||||||
ripple::PublicKey const pk(line[ripple::sfPublicKey]);
|
ripple::PublicKey const pk(line[ripple::sfPublicKey]);
|
||||||
jDst["public_key"] = toBase58(ripple::TokenType::AccountPublic, pk);
|
jDst[JS(public_key)] = toBase58(ripple::TokenType::AccountPublic, pk);
|
||||||
jDst["public_key_hex"] = strHex(pk);
|
jDst[JS(public_key_hex)] = strHex(pk);
|
||||||
}
|
}
|
||||||
jDst["settle_delay"] = line[ripple::sfSettleDelay];
|
jDst[JS(settle_delay)] = line[ripple::sfSettleDelay];
|
||||||
if (auto const& v = line[~ripple::sfExpiration])
|
if (auto const& v = line[~ripple::sfExpiration])
|
||||||
jDst["expiration"] = *v;
|
jDst[JS(expiration)] = *v;
|
||||||
if (auto const& v = line[~ripple::sfCancelAfter])
|
if (auto const& v = line[~ripple::sfCancelAfter])
|
||||||
jDst["cancel_after"] = *v;
|
jDst[JS(cancel_after)] = *v;
|
||||||
if (auto const& v = line[~ripple::sfSourceTag])
|
if (auto const& v = line[~ripple::sfSourceTag])
|
||||||
jDst["source_tag"] = *v;
|
jDst[JS(source_tag)] = *v;
|
||||||
if (auto const& v = line[~ripple::sfDestinationTag])
|
if (auto const& v = line[~ripple::sfDestinationTag])
|
||||||
jDst["destination_tag"] = *v;
|
jDst[JS(destination_tag)] = *v;
|
||||||
|
|
||||||
jsonLines.push_back(jDst);
|
jsonLines.push_back(jDst);
|
||||||
}
|
}
|
||||||
@@ -54,60 +54,38 @@ doAccountChannels(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
if (!request.at("account").is_string())
|
ripple::AccountID destAccount;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
if (auto const status =
|
||||||
|
getAccount(request, destAccount, JS(destination_account));
|
||||||
auto accountID =
|
status)
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
return status;
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::optional<ripple::AccountID> destAccount = {};
|
|
||||||
if (request.contains("destination_account"))
|
|
||||||
{
|
|
||||||
if (!request.at("destination_account").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "destinationNotString"};
|
|
||||||
|
|
||||||
destAccount = accountFromStringStrict(
|
|
||||||
request.at("destination_account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!destAccount)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "destinationMalformed"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
|
||||||
if (limit <= 0)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> marker = {};
|
std::optional<std::string> marker = {};
|
||||||
if (request.contains("marker"))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
if (!request.at(JS(marker)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
marker = request.at("marker").as_string().c_str();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
response["account"] = ripple::to_string(*accountID);
|
response[JS(account)] = ripple::to_string(accountID);
|
||||||
response["channels"] = boost::json::value(boost::json::array_kind);
|
response[JS(channels)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonChannels = response.at("channels").as_array();
|
boost::json::array& jsonChannels = response.at(JS(channels)).as_array();
|
||||||
|
|
||||||
auto const addToResponse = [&](ripple::SLE const& sle) {
|
auto const addToResponse = [&](ripple::SLE const& sle) {
|
||||||
if (sle.getType() == ripple::ltPAYCHAN &&
|
if (sle.getType() == ripple::ltPAYCHAN &&
|
||||||
sle.getAccountID(ripple::sfAccount) == *accountID &&
|
sle.getAccountID(ripple::sfAccount) == accountID &&
|
||||||
(!destAccount ||
|
(!destAccount ||
|
||||||
*destAccount == sle.getAccountID(ripple::sfDestination)))
|
destAccount == sle.getAccountID(ripple::sfDestination)))
|
||||||
{
|
{
|
||||||
if (limit-- == 0)
|
if (limit-- == 0)
|
||||||
{
|
{
|
||||||
@@ -122,23 +100,23 @@ doAccountChannels(Context const& context)
|
|||||||
|
|
||||||
auto next = traverseOwnedNodes(
|
auto next = traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
limit,
|
limit,
|
||||||
marker,
|
marker,
|
||||||
context.yield,
|
context.yield,
|
||||||
addToResponse);
|
addToResponse);
|
||||||
|
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&next))
|
if (auto status = std::get_if<RPC::Status>(&next))
|
||||||
return *status;
|
return *status;
|
||||||
|
|
||||||
auto nextCursor = std::get<RPC::AccountCursor>(next);
|
auto nextMarker = std::get<RPC::AccountCursor>(next);
|
||||||
|
|
||||||
if (nextCursor.isNonZero())
|
if (nextMarker.isNonZero())
|
||||||
response["marker"] = nextCursor.toString();
|
response[JS(marker)] = nextMarker.toString();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,9 @@ doAccountCurrencies(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
if (!request.at("account").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
|
||||||
|
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::set<std::string> send, receive;
|
std::set<std::string> send, receive;
|
||||||
auto const addToResponse = [&](ripple::SLE const& sle) {
|
auto const addToResponse = [&](ripple::SLE const& sle) {
|
||||||
@@ -61,26 +53,26 @@ doAccountCurrencies(Context const& context)
|
|||||||
|
|
||||||
traverseOwnedNodes(
|
traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
std::numeric_limits<std::uint32_t>::max(),
|
std::numeric_limits<std::uint32_t>::max(),
|
||||||
{},
|
{},
|
||||||
context.yield,
|
context.yield,
|
||||||
addToResponse);
|
addToResponse);
|
||||||
|
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
response["receive_currencies"] =
|
response[JS(receive_currencies)] =
|
||||||
boost::json::value(boost::json::array_kind);
|
boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonReceive =
|
boost::json::array& jsonReceive =
|
||||||
response.at("receive_currencies").as_array();
|
response.at(JS(receive_currencies)).as_array();
|
||||||
|
|
||||||
for (auto const& currency : receive)
|
for (auto const& currency : receive)
|
||||||
jsonReceive.push_back(currency.c_str());
|
jsonReceive.push_back(currency.c_str());
|
||||||
|
|
||||||
response["send_currencies"] = boost::json::value(boost::json::array_kind);
|
response[JS(send_currencies)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonSend = response.at("send_currencies").as_array();
|
boost::json::array& jsonSend = response.at(JS(send_currencies)).as_array();
|
||||||
|
|
||||||
for (auto const& currency : send)
|
for (auto const& currency : send)
|
||||||
jsonSend.push_back(currency.c_str());
|
jsonSend.push_back(currency.c_str());
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ doAccountInfo(Context const& context)
|
|||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
std::string strIdent;
|
std::string strIdent;
|
||||||
if (request.contains("account"))
|
if (request.contains(JS(account)))
|
||||||
strIdent = request.at("account").as_string().c_str();
|
strIdent = request.at(JS(account)).as_string().c_str();
|
||||||
else if (request.contains("ident"))
|
else if (request.contains(JS(ident)))
|
||||||
strIdent = request.at("ident").as_string().c_str();
|
strIdent = request.at(JS(ident)).as_string().c_str();
|
||||||
else
|
else
|
||||||
return Status{Error::rpcACT_MALFORMED};
|
return Status{Error::rpcACT_MALFORMED};
|
||||||
|
|
||||||
@@ -71,18 +71,18 @@ doAccountInfo(Context const& context)
|
|||||||
return Status{Error::rpcDB_DESERIALIZATION};
|
return Status{Error::rpcDB_DESERIALIZATION};
|
||||||
|
|
||||||
// if (!binary)
|
// if (!binary)
|
||||||
// response["account_data"] = getJson(sle);
|
// response[JS(account_data)] = getJson(sle);
|
||||||
// else
|
// else
|
||||||
// response["account_data"] = ripple::strHex(*dbResponse);
|
// response[JS(account_data)] = ripple::strHex(*dbResponse);
|
||||||
// response["db_time"] = time;
|
// response[JS(db_time)] = time;
|
||||||
|
|
||||||
response["account_data"] = toJson(sle);
|
response[JS(account_data)] = toJson(sle);
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
// Return SignerList(s) if that is requested.
|
// Return SignerList(s) if that is requested.
|
||||||
if (request.contains("signer_lists") &&
|
if (request.contains(JS(signer_lists)) &&
|
||||||
request.at("signer_lists").as_bool())
|
request.at(JS(signer_lists)).as_bool())
|
||||||
{
|
{
|
||||||
// We put the SignerList in an array because of an anticipated
|
// We put the SignerList in an array because of an anticipated
|
||||||
// future when we support multiple signer lists on one account.
|
// future when we support multiple signer lists on one account.
|
||||||
@@ -104,7 +104,7 @@ doAccountInfo(Context const& context)
|
|||||||
signerList.push_back(toJson(sleSigners));
|
signerList.push_back(toJson(sleSigners));
|
||||||
}
|
}
|
||||||
|
|
||||||
response["account_data"].as_object()["signer_lists"] =
|
response[JS(account_data)].as_object()[JS(signer_lists)] =
|
||||||
std::move(signerList);
|
std::move(signerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,25 +64,25 @@ addLine(
|
|||||||
ripple::STAmount const& saLimitPeer(lineLimitPeer);
|
ripple::STAmount const& saLimitPeer(lineLimitPeer);
|
||||||
|
|
||||||
boost::json::object jPeer;
|
boost::json::object jPeer;
|
||||||
jPeer["account"] = ripple::to_string(lineAccountIDPeer);
|
jPeer[JS(account)] = ripple::to_string(lineAccountIDPeer);
|
||||||
jPeer["balance"] = saBalance.getText();
|
jPeer[JS(balance)] = saBalance.getText();
|
||||||
jPeer["currency"] = ripple::to_string(saBalance.issue().currency);
|
jPeer[JS(currency)] = ripple::to_string(saBalance.issue().currency);
|
||||||
jPeer["limit"] = saLimit.getText();
|
jPeer[JS(limit)] = saLimit.getText();
|
||||||
jPeer["limit_peer"] = saLimitPeer.getText();
|
jPeer[JS(limit_peer)] = saLimitPeer.getText();
|
||||||
jPeer["quality_in"] = lineQualityIn;
|
jPeer[JS(quality_in)] = lineQualityIn;
|
||||||
jPeer["quality_out"] = lineQualityOut;
|
jPeer[JS(quality_out)] = lineQualityOut;
|
||||||
if (lineAuth)
|
if (lineAuth)
|
||||||
jPeer["authorized"] = true;
|
jPeer[JS(authorized)] = true;
|
||||||
if (lineAuthPeer)
|
if (lineAuthPeer)
|
||||||
jPeer["peer_authorized"] = true;
|
jPeer[JS(peer_authorized)] = true;
|
||||||
if (lineNoRipple || !lineDefaultRipple)
|
if (lineNoRipple || !lineDefaultRipple)
|
||||||
jPeer["no_ripple"] = lineNoRipple;
|
jPeer[JS(no_ripple)] = lineNoRipple;
|
||||||
if (lineNoRipple || !lineDefaultRipple)
|
if (lineNoRipple || !lineDefaultRipple)
|
||||||
jPeer["no_ripple_peer"] = lineNoRipplePeer;
|
jPeer[JS(no_ripple_peer)] = lineNoRipplePeer;
|
||||||
if (lineFreeze)
|
if (lineFreeze)
|
||||||
jPeer["freeze"] = true;
|
jPeer[JS(freeze)] = true;
|
||||||
if (lineFreezePeer)
|
if (lineFreezePeer)
|
||||||
jPeer["freeze_peer"] = true;
|
jPeer[JS(freeze_peer)] = true;
|
||||||
|
|
||||||
jsonLines.push_back(jPeer);
|
jsonLines.push_back(jPeer);
|
||||||
}
|
}
|
||||||
@@ -99,82 +99,58 @@ doAccountLines(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
if (!request.at("account").is_string())
|
ripple::AccountID peerAccount;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
if (auto const status = getAccount(request, peerAccount, JS(peer)); status)
|
||||||
|
return status;
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::optional<ripple::AccountID> peerAccount;
|
|
||||||
if (request.contains("peer"))
|
|
||||||
{
|
|
||||||
if (!request.at("peer").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "peerNotString"};
|
|
||||||
|
|
||||||
peerAccount =
|
|
||||||
accountFromStringStrict(request.at("peer").as_string().c_str());
|
|
||||||
|
|
||||||
if (!peerAccount)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "peerMalformed"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
std::optional<std::string> marker = {};
|
||||||
if (limit <= 0)
|
if (request.contains(JS(marker)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cursor = {};
|
|
||||||
if (request.contains("marker"))
|
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
if (!request.at(JS(marker)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
cursor = request.at("marker").as_string().c_str();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
response["account"] = ripple::to_string(*accountID);
|
response[JS(account)] = ripple::to_string(accountID);
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
response["lines"] = boost::json::value(boost::json::array_kind);
|
response[JS(lines)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonLines = response.at("lines").as_array();
|
boost::json::array& jsonLines = response.at(JS(lines)).as_array();
|
||||||
|
|
||||||
auto const addToResponse = [&](ripple::SLE const& sle) -> void {
|
auto const addToResponse = [&](ripple::SLE const& sle) -> void {
|
||||||
if (sle.getType() == ripple::ltRIPPLE_STATE)
|
if (sle.getType() == ripple::ltRIPPLE_STATE)
|
||||||
{
|
{
|
||||||
addLine(jsonLines, sle, *accountID, peerAccount);
|
addLine(jsonLines, sle, accountID, peerAccount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto next = traverseOwnedNodes(
|
auto next = traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
limit,
|
limit,
|
||||||
cursor,
|
marker,
|
||||||
context.yield,
|
context.yield,
|
||||||
addToResponse);
|
addToResponse);
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&next))
|
if (auto status = std::get_if<RPC::Status>(&next))
|
||||||
return *status;
|
return *status;
|
||||||
|
|
||||||
auto nextCursor = std::get<RPC::AccountCursor>(next);
|
auto nextMarker = std::get<RPC::AccountCursor>(next);
|
||||||
|
|
||||||
if (nextCursor.isNonZero())
|
if (nextMarker.isNonZero())
|
||||||
response["marker"] = nextCursor.toString();
|
response[JS(marker)] = nextMarker.toString();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
#include <ripple/app/paths/TrustLine.h>
|
#include <ripple/app/paths/TrustLine.h>
|
||||||
|
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||||
#include <ripple/basics/StringUtilities.h>
|
#include <ripple/basics/StringUtilities.h>
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
#include <ripple/protocol/Indexes.h>
|
#include <ripple/protocol/Indexes.h>
|
||||||
#include <ripple/protocol/STLedgerEntry.h>
|
#include <ripple/protocol/STLedgerEntry.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
|
#include <ripple/protocol/nftPageMask.h>
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
@@ -23,7 +25,126 @@ std::unordered_map<std::string, ripple::LedgerEntryType> types{
|
|||||||
{"escrow", ripple::ltESCROW},
|
{"escrow", ripple::ltESCROW},
|
||||||
{"deposit_preauth", ripple::ltDEPOSIT_PREAUTH},
|
{"deposit_preauth", ripple::ltDEPOSIT_PREAUTH},
|
||||||
{"check", ripple::ltCHECK},
|
{"check", ripple::ltCHECK},
|
||||||
};
|
{"nft_page", ripple::ltNFTOKEN_PAGE},
|
||||||
|
{"nft_offer", ripple::ltNFTOKEN_OFFER}};
|
||||||
|
|
||||||
|
Result
|
||||||
|
doAccountNFTs(Context const& context)
|
||||||
|
{
|
||||||
|
auto request = context.params;
|
||||||
|
boost::json::object response = {};
|
||||||
|
|
||||||
|
auto v = ledgerInfoFromRequest(context);
|
||||||
|
if (auto status = std::get_if<Status>(&v))
|
||||||
|
return *status;
|
||||||
|
|
||||||
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
|
ripple::AccountID accountID;
|
||||||
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (!accountID)
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
||||||
|
|
||||||
|
// TODO: just check for existence without pulling
|
||||||
|
if (!context.backend->fetchLedgerObject(
|
||||||
|
ripple::keylet::account(accountID).key, lgrInfo.seq, context.yield))
|
||||||
|
return Status{Error::rpcACT_NOT_FOUND, "accountNotFound"};
|
||||||
|
|
||||||
|
std::uint32_t limit = 200;
|
||||||
|
if (auto const status = getLimit(request, limit); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
ripple::uint256 marker;
|
||||||
|
if (auto const status = getHexMarker(request, marker); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
auto const first =
|
||||||
|
ripple::keylet::nftpage(ripple::keylet::nftpage_min(accountID), marker);
|
||||||
|
auto const last = ripple::keylet::nftpage_max(accountID);
|
||||||
|
|
||||||
|
auto const key =
|
||||||
|
context.backend
|
||||||
|
->fetchSuccessorKey(first.key, lgrInfo.seq, context.yield)
|
||||||
|
.value_or(last.key);
|
||||||
|
auto const blob = context.backend->fetchLedgerObject(
|
||||||
|
ripple::Keylet(ripple::ltNFTOKEN_PAGE, key).key,
|
||||||
|
lgrInfo.seq,
|
||||||
|
context.yield);
|
||||||
|
|
||||||
|
std::optional<ripple::SLE const> cp{
|
||||||
|
ripple::SLE{ripple::SerialIter{blob->data(), blob->size()}, key}};
|
||||||
|
|
||||||
|
std::uint32_t cnt = 0;
|
||||||
|
response[JS(account_nfts)] = boost::json::value(boost::json::array_kind);
|
||||||
|
auto& nfts = response.at(JS(account_nfts)).as_array();
|
||||||
|
|
||||||
|
bool pastMarker = marker.isZero();
|
||||||
|
ripple::uint256 const maskedMarker = marker & ripple::nft::pageMask;
|
||||||
|
|
||||||
|
// Continue iteration from the current page:
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto arr = cp->getFieldArray(ripple::sfNFTokens);
|
||||||
|
|
||||||
|
for (auto const& o : arr)
|
||||||
|
{
|
||||||
|
ripple::uint256 const nftokenID = o[ripple::sfNFTokenID];
|
||||||
|
ripple::uint256 const maskedNftokenID =
|
||||||
|
nftokenID & ripple::nft::pageMask;
|
||||||
|
|
||||||
|
if (!pastMarker && maskedNftokenID < maskedMarker)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!pastMarker && maskedNftokenID == maskedMarker &&
|
||||||
|
nftokenID <= marker)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
{
|
||||||
|
nfts.push_back(
|
||||||
|
toBoostJson(o.getJson(ripple::JsonOptions::none)));
|
||||||
|
auto& obj = nfts.back().as_object();
|
||||||
|
|
||||||
|
// Pull out the components of the nft ID.
|
||||||
|
obj[SFS(sfFlags)] = ripple::nft::getFlags(nftokenID);
|
||||||
|
obj[SFS(sfIssuer)] =
|
||||||
|
to_string(ripple::nft::getIssuer(nftokenID));
|
||||||
|
obj[SFS(sfNFTokenTaxon)] =
|
||||||
|
ripple::nft::toUInt32(ripple::nft::getTaxon(nftokenID));
|
||||||
|
obj[JS(nft_serial)] = ripple::nft::getSerial(nftokenID);
|
||||||
|
|
||||||
|
if (std::uint16_t xferFee = {
|
||||||
|
ripple::nft::getTransferFee(nftokenID)})
|
||||||
|
obj[SFS(sfTransferFee)] = xferFee;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++cnt == limit)
|
||||||
|
{
|
||||||
|
response[JS(limit)] = limit;
|
||||||
|
response[JS(marker)] =
|
||||||
|
to_string(o.getFieldH256(ripple::sfNFTokenID));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto npm = (*cp)[~ripple::sfNextPageMin])
|
||||||
|
{
|
||||||
|
auto const nextKey = ripple::Keylet(ripple::ltNFTOKEN_PAGE, *npm);
|
||||||
|
auto const nextBlob = context.backend->fetchLedgerObject(
|
||||||
|
nextKey.key, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
|
cp.emplace(ripple::SLE{
|
||||||
|
ripple::SerialIter{nextBlob->data(), nextBlob->size()},
|
||||||
|
nextKey.key});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
response[JS(account)] = ripple::toBase58(accountID);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doAccountObjects(Context const& context)
|
doAccountObjects(Context const& context)
|
||||||
@@ -37,54 +158,40 @@ doAccountObjects(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
if (!request.at("account").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
|
||||||
|
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
std::optional<std::string> marker = {};
|
||||||
if (limit <= 0)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cursor = {};
|
|
||||||
if (request.contains("marker"))
|
if (request.contains("marker"))
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
if (!request.at("marker").is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
cursor = request.at("marker").as_string().c_str();
|
marker = request.at("marker").as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ripple::LedgerEntryType> objectType = {};
|
std::optional<ripple::LedgerEntryType> objectType = {};
|
||||||
if (request.contains("type"))
|
if (request.contains(JS(type)))
|
||||||
{
|
{
|
||||||
if (!request.at("type").is_string())
|
if (!request.at(JS(type)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "typeNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "typeNotString"};
|
||||||
|
|
||||||
std::string typeAsString = request.at("type").as_string().c_str();
|
std::string typeAsString = request.at(JS(type)).as_string().c_str();
|
||||||
if (types.find(typeAsString) == types.end())
|
if (types.find(typeAsString) == types.end())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "typeInvalid"};
|
return Status{Error::rpcINVALID_PARAMS, "typeInvalid"};
|
||||||
|
|
||||||
objectType = types[typeAsString];
|
objectType = types[typeAsString];
|
||||||
}
|
}
|
||||||
|
|
||||||
response["account"] = ripple::to_string(*accountID);
|
response[JS(account)] = ripple::to_string(accountID);
|
||||||
response["account_objects"] = boost::json::value(boost::json::array_kind);
|
response[JS(account_objects)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonObjects = response.at("account_objects").as_array();
|
boost::json::array& jsonObjects =
|
||||||
|
response.at(JS(account_objects)).as_array();
|
||||||
|
|
||||||
auto const addToResponse = [&](ripple::SLE const& sle) {
|
auto const addToResponse = [&](ripple::SLE const& sle) {
|
||||||
if (!objectType || objectType == sle.getType())
|
if (!objectType || objectType == sle.getType())
|
||||||
@@ -95,23 +202,23 @@ doAccountObjects(Context const& context)
|
|||||||
|
|
||||||
auto next = traverseOwnedNodes(
|
auto next = traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
limit,
|
limit,
|
||||||
cursor,
|
marker,
|
||||||
context.yield,
|
context.yield,
|
||||||
addToResponse);
|
addToResponse);
|
||||||
|
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&next))
|
if (auto status = std::get_if<RPC::Status>(&next))
|
||||||
return *status;
|
return *status;
|
||||||
|
|
||||||
auto nextCursor = std::get<RPC::AccountCursor>(next);
|
auto nextMarker = std::get<RPC::AccountCursor>(next);
|
||||||
|
|
||||||
if (nextCursor.isNonZero())
|
if (nextMarker.isNonZero())
|
||||||
response["marker"] = nextCursor.toString();
|
response[JS(marker)] = nextMarker.toString();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,37 +27,39 @@ addOffer(boost::json::array& offersJson, ripple::SLE const& offer)
|
|||||||
|
|
||||||
if (!takerPays.native())
|
if (!takerPays.native())
|
||||||
{
|
{
|
||||||
obj["taker_pays"] = boost::json::value(boost::json::object_kind);
|
obj[JS(taker_pays)] = boost::json::value(boost::json::object_kind);
|
||||||
boost::json::object& takerPaysJson = obj.at("taker_pays").as_object();
|
boost::json::object& takerPaysJson = obj.at(JS(taker_pays)).as_object();
|
||||||
|
|
||||||
takerPaysJson["value"] = takerPays.getText();
|
takerPaysJson[JS(value)] = takerPays.getText();
|
||||||
takerPaysJson["currency"] = ripple::to_string(takerPays.getCurrency());
|
takerPaysJson[JS(currency)] =
|
||||||
takerPaysJson["issuer"] = ripple::to_string(takerPays.getIssuer());
|
ripple::to_string(takerPays.getCurrency());
|
||||||
|
takerPaysJson[JS(issuer)] = ripple::to_string(takerPays.getIssuer());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
obj["taker_pays"] = takerPays.getText();
|
obj[JS(taker_pays)] = takerPays.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!takerGets.native())
|
if (!takerGets.native())
|
||||||
{
|
{
|
||||||
obj["taker_gets"] = boost::json::value(boost::json::object_kind);
|
obj[JS(taker_gets)] = boost::json::value(boost::json::object_kind);
|
||||||
boost::json::object& takerGetsJson = obj.at("taker_gets").as_object();
|
boost::json::object& takerGetsJson = obj.at(JS(taker_gets)).as_object();
|
||||||
|
|
||||||
takerGetsJson["value"] = takerGets.getText();
|
takerGetsJson[JS(value)] = takerGets.getText();
|
||||||
takerGetsJson["currency"] = ripple::to_string(takerGets.getCurrency());
|
takerGetsJson[JS(currency)] =
|
||||||
takerGetsJson["issuer"] = ripple::to_string(takerGets.getIssuer());
|
ripple::to_string(takerGets.getCurrency());
|
||||||
|
takerGetsJson[JS(issuer)] = ripple::to_string(takerGets.getIssuer());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
obj["taker_gets"] = takerGets.getText();
|
obj[JS(taker_gets)] = takerGets.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj["seq"] = offer.getFieldU32(ripple::sfSequence);
|
obj[JS(seq)] = offer.getFieldU32(ripple::sfSequence);
|
||||||
obj["flags"] = offer.getFieldU32(ripple::sfFlags);
|
obj[JS(flags)] = offer.getFieldU32(ripple::sfFlags);
|
||||||
obj["quality"] = rate.getText();
|
obj[JS(quality)] = rate.getText();
|
||||||
if (offer.isFieldPresent(ripple::sfExpiration))
|
if (offer.isFieldPresent(ripple::sfExpiration))
|
||||||
obj["expiration"] = offer.getFieldU32(ripple::sfExpiration);
|
obj[JS(expiration)] = offer.getFieldU32(ripple::sfExpiration);
|
||||||
|
|
||||||
offersJson.push_back(obj);
|
offersJson.push_back(obj);
|
||||||
};
|
};
|
||||||
@@ -74,43 +76,28 @@ doAccountOffers(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
if (!request.at("account").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
|
||||||
|
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
std::optional<std::string> marker = {};
|
||||||
if (limit <= 0)
|
if (request.contains(JS(marker)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cursor = {};
|
|
||||||
if (request.contains("marker"))
|
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
if (!request.at(JS(marker)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
cursor = request.at("marker").as_string().c_str();
|
marker = request.at(JS(marker)).as_string().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
response["account"] = ripple::to_string(*accountID);
|
response[JS(account)] = ripple::to_string(accountID);
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
response["offers"] = boost::json::value(boost::json::array_kind);
|
response[JS(offers)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonLines = response.at("offers").as_array();
|
boost::json::array& jsonLines = response.at(JS(offers)).as_array();
|
||||||
|
|
||||||
auto const addToResponse = [&](ripple::SLE const& sle) {
|
auto const addToResponse = [&](ripple::SLE const& sle) {
|
||||||
if (sle.getType() == ripple::ltOFFER)
|
if (sle.getType() == ripple::ltOFFER)
|
||||||
@@ -128,22 +115,22 @@ doAccountOffers(Context const& context)
|
|||||||
|
|
||||||
auto next = traverseOwnedNodes(
|
auto next = traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
limit,
|
limit,
|
||||||
cursor,
|
marker,
|
||||||
context.yield,
|
context.yield,
|
||||||
addToResponse);
|
addToResponse);
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&next))
|
if (auto status = std::get_if<RPC::Status>(&next))
|
||||||
return *status;
|
return *status;
|
||||||
|
|
||||||
auto nextCursor = std::get<RPC::AccountCursor>(next);
|
auto nextMarker = std::get<RPC::AccountCursor>(next);
|
||||||
|
|
||||||
if (nextCursor.isNonZero())
|
if (nextMarker.isNonZero())
|
||||||
response["marker"] = nextCursor.toString();
|
response[JS(marker)] = nextMarker.toString();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -12,60 +12,38 @@ doAccountTx(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
if (!request.at("account").is_string())
|
bool const binary = getBool(request, JS(binary), false);
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
bool const forward = getBool(request, JS(forward), false);
|
||||||
|
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
bool binary = false;
|
|
||||||
if (request.contains("binary"))
|
|
||||||
{
|
|
||||||
if (!request.at("binary").is_bool())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
|
||||||
|
|
||||||
binary = request.at("binary").as_bool();
|
|
||||||
}
|
|
||||||
bool forward = false;
|
|
||||||
if (request.contains("forward"))
|
|
||||||
{
|
|
||||||
if (!request.at("forward").is_bool())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "forwardNotBool"};
|
|
||||||
|
|
||||||
forward = request.at("forward").as_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Backend::AccountTransactionsCursor> cursor;
|
std::optional<Backend::AccountTransactionsCursor> cursor;
|
||||||
|
|
||||||
if (request.contains("marker"))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
auto const& obj = request.at("marker").as_object();
|
auto const& obj = request.at(JS(marker)).as_object();
|
||||||
|
|
||||||
std::optional<std::uint32_t> transactionIndex = {};
|
std::optional<std::uint32_t> transactionIndex = {};
|
||||||
if (obj.contains("seq"))
|
if (obj.contains(JS(seq)))
|
||||||
{
|
{
|
||||||
if (!obj.at("seq").is_int64())
|
if (!obj.at(JS(seq)).is_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "transactionIndexNotInt"};
|
Error::rpcINVALID_PARAMS, "transactionIndexNotInt"};
|
||||||
|
|
||||||
transactionIndex =
|
transactionIndex =
|
||||||
boost::json::value_to<std::uint32_t>(obj.at("seq"));
|
boost::json::value_to<std::uint32_t>(obj.at(JS(seq)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::uint32_t> ledgerIndex = {};
|
std::optional<std::uint32_t> ledgerIndex = {};
|
||||||
if (obj.contains("ledger"))
|
if (obj.contains(JS(ledger)))
|
||||||
{
|
{
|
||||||
if (!obj.at("ledger").is_int64())
|
if (!obj.at(JS(ledger)).is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotInt"};
|
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotInt"};
|
||||||
|
|
||||||
ledgerIndex =
|
ledgerIndex =
|
||||||
boost::json::value_to<std::uint32_t>(obj.at("ledger"));
|
boost::json::value_to<std::uint32_t>(obj.at(JS(ledger)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transactionIndex || !ledgerIndex)
|
if (!transactionIndex || !ledgerIndex)
|
||||||
@@ -75,9 +53,9 @@ doAccountTx(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto minIndex = context.range.minSequence;
|
auto minIndex = context.range.minSequence;
|
||||||
if (request.contains("ledger_index_min"))
|
if (request.contains(JS(ledger_index_min)))
|
||||||
{
|
{
|
||||||
auto& min = request.at("ledger_index_min");
|
auto& min = request.at(JS(ledger_index_min));
|
||||||
|
|
||||||
if (!min.is_int64())
|
if (!min.is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"};
|
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"};
|
||||||
@@ -97,9 +75,9 @@ doAccountTx(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto maxIndex = context.range.maxSequence;
|
auto maxIndex = context.range.maxSequence;
|
||||||
if (request.contains("ledger_index_max"))
|
if (request.contains(JS(ledger_index_max)))
|
||||||
{
|
{
|
||||||
auto& max = request.at("ledger_index_max");
|
auto& max = request.at(JS(ledger_index_max));
|
||||||
|
|
||||||
if (!max.is_int64())
|
if (!max.is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"};
|
return Status{Error::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"};
|
||||||
@@ -121,24 +99,25 @@ doAccountTx(Context const& context)
|
|||||||
cursor = {maxIndex, INT32_MAX};
|
cursor = {maxIndex, INT32_MAX};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("ledger_index"))
|
if (request.contains(JS(ledger_index)))
|
||||||
{
|
{
|
||||||
if (!request.at("ledger_index").is_int64())
|
if (!request.at(JS(ledger_index)).is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotNumber"};
|
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexNotNumber"};
|
||||||
|
|
||||||
auto ledgerIndex =
|
auto ledgerIndex =
|
||||||
boost::json::value_to<std::uint32_t>(request.at("ledger_index"));
|
boost::json::value_to<std::uint32_t>(request.at(JS(ledger_index)));
|
||||||
maxIndex = minIndex = ledgerIndex;
|
maxIndex = minIndex = ledgerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("ledger_hash"))
|
if (request.contains(JS(ledger_hash)))
|
||||||
{
|
{
|
||||||
if (!request.at("ledger_hash").is_string())
|
if (!request.at(JS(ledger_hash)).is_string())
|
||||||
return RPC::Status{
|
return RPC::Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
RPC::Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||||
|
|
||||||
ripple::uint256 ledgerHash;
|
ripple::uint256 ledgerHash;
|
||||||
if (!ledgerHash.parseHex(request.at("ledger_hash").as_string().c_str()))
|
if (!ledgerHash.parseHex(
|
||||||
|
request.at(JS(ledger_hash)).as_string().c_str()))
|
||||||
return RPC::Status{
|
return RPC::Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
RPC::Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||||
|
|
||||||
@@ -156,36 +135,30 @@ doAccountTx(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
if (request.contains(JS(limit)))
|
||||||
if (limit <= 0)
|
response[JS(limit)] = limit;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
|
|
||||||
response["limit"] = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::json::array txns;
|
boost::json::array txns;
|
||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
auto [blobs, retCursor] = context.backend->fetchAccountTransactions(
|
auto [blobs, retCursor] = context.backend->fetchAccountTransactions(
|
||||||
*accountID, limit, forward, cursor, context.yield);
|
accountID, limit, forward, cursor, context.yield);
|
||||||
|
|
||||||
auto end = std::chrono::system_clock::now();
|
auto end = std::chrono::system_clock::now();
|
||||||
BOOST_LOG_TRIVIAL(info) << __func__ << " db fetch took "
|
BOOST_LOG_TRIVIAL(info) << __func__ << " db fetch took "
|
||||||
<< ((end - start).count() / 1000000000.0)
|
<< ((end - start).count() / 1000000000.0)
|
||||||
<< " num blobs = " << blobs.size();
|
<< " num blobs = " << blobs.size();
|
||||||
|
|
||||||
response["account"] = ripple::to_string(*accountID);
|
response[JS(account)] = ripple::to_string(accountID);
|
||||||
|
|
||||||
if (retCursor)
|
if (retCursor)
|
||||||
{
|
{
|
||||||
boost::json::object cursorJson;
|
boost::json::object cursorJson;
|
||||||
cursorJson["ledger"] = retCursor->ledgerSequence;
|
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
|
||||||
cursorJson["seq"] = retCursor->transactionIndex;
|
cursorJson[JS(seq)] = retCursor->transactionIndex;
|
||||||
response["marker"] = cursorJson;
|
response[JS(marker)] = cursorJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> maxReturnedIndex;
|
std::optional<size_t> maxReturnedIndex;
|
||||||
@@ -206,17 +179,18 @@ doAccountTx(Context const& context)
|
|||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
||||||
obj["meta"] = meta;
|
obj[JS(meta)] = meta;
|
||||||
obj["tx"] = txn;
|
obj[JS(tx)] = txn;
|
||||||
obj["tx"].as_object()["ledger_index"] = txnPlusMeta.ledgerSequence;
|
obj[JS(tx)].as_object()[JS(ledger_index)] =
|
||||||
obj["tx"].as_object()["date"] = txnPlusMeta.date;
|
txnPlusMeta.ledgerSequence;
|
||||||
|
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
obj["meta"] = ripple::strHex(txnPlusMeta.metadata);
|
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
|
||||||
obj["tx_blob"] = ripple::strHex(txnPlusMeta.transaction);
|
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
|
||||||
obj["ledger_index"] = txnPlusMeta.ledgerSequence;
|
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||||
obj["date"] = txnPlusMeta.date;
|
obj[JS(date)] = txnPlusMeta.date;
|
||||||
}
|
}
|
||||||
|
|
||||||
txns.push_back(obj);
|
txns.push_back(obj);
|
||||||
@@ -229,22 +203,22 @@ doAccountTx(Context const& context)
|
|||||||
assert(cursor);
|
assert(cursor);
|
||||||
if (forward)
|
if (forward)
|
||||||
{
|
{
|
||||||
response["ledger_index_min"] = cursor->ledgerSequence;
|
response[JS(ledger_index_min)] = cursor->ledgerSequence;
|
||||||
if (blobs.size() >= limit)
|
if (blobs.size() >= limit)
|
||||||
response["ledger_index_max"] = *maxReturnedIndex;
|
response[JS(ledger_index_max)] = *maxReturnedIndex;
|
||||||
else
|
else
|
||||||
response["ledger_index_max"] = maxIndex;
|
response[JS(ledger_index_max)] = maxIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response["ledger_index_max"] = cursor->ledgerSequence;
|
response[JS(ledger_index_max)] = cursor->ledgerSequence;
|
||||||
if (blobs.size() >= limit)
|
if (blobs.size() >= limit)
|
||||||
response["ledger_index_min"] = *minReturnedIndex;
|
response[JS(ledger_index_min)] = *minReturnedIndex;
|
||||||
else
|
else
|
||||||
response["ledger_index_min"] = minIndex;
|
response[JS(ledger_index_min)] = minIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
response["transactions"] = txns;
|
response[JS(transactions)] = txns;
|
||||||
|
|
||||||
auto end2 = std::chrono::system_clock::now();
|
auto end2 = std::chrono::system_clock::now();
|
||||||
BOOST_LOG_TRIVIAL(info) << __func__ << " serialization took "
|
BOOST_LOG_TRIVIAL(info) << __func__ << " serialization took "
|
||||||
|
|||||||
@@ -49,41 +49,20 @@ doBookOffers(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (auto const status = getLimit(request, limit); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
|
||||||
if (limit <= 0)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
|
||||||
}
|
|
||||||
|
|
||||||
ripple::AccountID takerID = beast::zero;
|
ripple::AccountID takerID = beast::zero;
|
||||||
if (request.contains("taker"))
|
if (auto const status = getTaker(request, takerID); status)
|
||||||
{
|
return status;
|
||||||
auto parsed = parseTaker(request["taker"]);
|
|
||||||
if (auto status = std::get_if<Status>(&parsed))
|
|
||||||
return *status;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
takerID = std::get<ripple::AccountID>(parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ripple::uint256 cursor = beast::zero;
|
ripple::uint256 marker = beast::zero;
|
||||||
if (request.contains("cursor"))
|
if (auto const status = getHexMarker(request, marker); status)
|
||||||
{
|
return status;
|
||||||
if (!request.at("cursor").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "cursorNotString"};
|
|
||||||
|
|
||||||
if (!cursor.parseHex(request.at("cursor").as_string().c_str()))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedCursor"};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
auto [offers, retCursor] = context.backend->fetchBookOffers(
|
auto [offers, retMarker] = context.backend->fetchBookOffers(
|
||||||
bookBase, lgrInfo.seq, limit, cursor, context.yield);
|
bookBase, lgrInfo.seq, limit, marker, context.yield);
|
||||||
auto end = std::chrono::system_clock::now();
|
auto end = std::chrono::system_clock::now();
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning)
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
@@ -92,10 +71,10 @@ doBookOffers(Context const& context)
|
|||||||
.count()
|
.count()
|
||||||
<< " milliseconds - request = " << request;
|
<< " milliseconds - request = " << request;
|
||||||
|
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
response["offers"] = postProcessOrderBook(
|
response[JS(offers)] = postProcessOrderBook(
|
||||||
offers, book, takerID, *context.backend, lgrInfo.seq, context.yield);
|
offers, book, takerID, *context.backend, lgrInfo.seq, context.yield);
|
||||||
|
|
||||||
auto end2 = std::chrono::system_clock::now();
|
auto end2 = std::chrono::system_clock::now();
|
||||||
@@ -106,8 +85,8 @@ doBookOffers(Context const& context)
|
|||||||
.count()
|
.count()
|
||||||
<< " milliseconds - request = " << request;
|
<< " milliseconds - request = " << request;
|
||||||
|
|
||||||
if (retCursor)
|
if (retMarker)
|
||||||
response["marker"] = ripple::strHex(*retCursor);
|
response["marker"] = ripple::strHex(*retMarker);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,19 +27,13 @@ doChannelAuthorize(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains("channel_id"))
|
if (!request.contains(JS(amount)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingChannelID"};
|
|
||||||
|
|
||||||
if (!request.at("channel_id").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "channelIDNotString"};
|
|
||||||
|
|
||||||
if (!request.contains("amount"))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
||||||
|
|
||||||
if (!request.at("amount").is_string())
|
if (!request.at(JS(amount)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "amountNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "amountNotString"};
|
||||||
|
|
||||||
if (!request.contains("key_type") && !request.contains("secret"))
|
if (!request.contains(JS(key_type)) && !request.contains(JS(secret)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingKeyTypeOrSecret"};
|
return Status{Error::rpcINVALID_PARAMS, "missingKeyTypeOrSecret"};
|
||||||
|
|
||||||
auto v = keypairFromRequst(request);
|
auto v = keypairFromRequst(request);
|
||||||
@@ -50,10 +44,11 @@ doChannelAuthorize(Context const& context)
|
|||||||
std::get<std::pair<ripple::PublicKey, ripple::SecretKey>>(v);
|
std::get<std::pair<ripple::PublicKey, ripple::SecretKey>>(v);
|
||||||
|
|
||||||
ripple::uint256 channelId;
|
ripple::uint256 channelId;
|
||||||
if (!channelId.parseHex(request.at("channel_id").as_string().c_str()))
|
if (auto const status = getChannelId(request, channelId); status)
|
||||||
return Status{Error::rpcCHANNEL_MALFORMED, "malformedChannelID"};
|
return status;
|
||||||
|
|
||||||
auto optDrops = ripple::to_uint64(request.at("amount").as_string().c_str());
|
auto optDrops =
|
||||||
|
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
||||||
|
|
||||||
if (!optDrops)
|
if (!optDrops)
|
||||||
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
||||||
@@ -67,7 +62,7 @@ doChannelAuthorize(Context const& context)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto const buf = ripple::sign(pk, sk, msg.slice());
|
auto const buf = ripple::sign(pk, sk, msg.slice());
|
||||||
response["signature"] = ripple::strHex(buf);
|
response[JS(signature)] = ripple::strHex(buf);
|
||||||
}
|
}
|
||||||
catch (std::exception&)
|
catch (std::exception&)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,33 +16,28 @@ doChannelVerify(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains("channel_id"))
|
if (!request.contains(JS(amount)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingChannelID"};
|
|
||||||
|
|
||||||
if (!request.at("channel_id").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "channelIDNotString"};
|
|
||||||
|
|
||||||
if (!request.contains("amount"))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
return Status{Error::rpcINVALID_PARAMS, "missingAmount"};
|
||||||
|
|
||||||
if (!request.at("amount").is_string())
|
if (!request.at(JS(amount)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "amountNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "amountNotString"};
|
||||||
|
|
||||||
if (!request.contains("signature"))
|
if (!request.contains(JS(signature)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingSignature"};
|
return Status{Error::rpcINVALID_PARAMS, "missingSignature"};
|
||||||
|
|
||||||
if (!request.at("signature").is_string())
|
if (!request.at(JS(signature)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "signatureNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "signatureNotString"};
|
||||||
|
|
||||||
if (!request.contains("public_key"))
|
if (!request.contains(JS(public_key)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingPublicKey"};
|
return Status{Error::rpcINVALID_PARAMS, "missingPublicKey"};
|
||||||
|
|
||||||
if (!request.at("public_key").is_string())
|
if (!request.at(JS(public_key)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "publicKeyNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "publicKeyNotString"};
|
||||||
|
|
||||||
std::optional<ripple::PublicKey> pk;
|
std::optional<ripple::PublicKey> pk;
|
||||||
{
|
{
|
||||||
std::string const strPk = request.at("public_key").as_string().c_str();
|
std::string const strPk =
|
||||||
|
request.at(JS(public_key)).as_string().c_str();
|
||||||
pk = ripple::parseBase58<ripple::PublicKey>(
|
pk = ripple::parseBase58<ripple::PublicKey>(
|
||||||
ripple::TokenType::AccountPublic, strPk);
|
ripple::TokenType::AccountPublic, strPk);
|
||||||
|
|
||||||
@@ -62,17 +57,18 @@ doChannelVerify(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ripple::uint256 channelId;
|
ripple::uint256 channelId;
|
||||||
if (!channelId.parseHex(request.at("channel_id").as_string().c_str()))
|
if (auto const status = getChannelId(request, channelId); status)
|
||||||
return Status{Error::rpcCHANNEL_MALFORMED, "malformedChannelID"};
|
return status;
|
||||||
|
|
||||||
auto optDrops = ripple::to_uint64(request.at("amount").as_string().c_str());
|
auto optDrops =
|
||||||
|
ripple::to_uint64(request.at(JS(amount)).as_string().c_str());
|
||||||
|
|
||||||
if (!optDrops)
|
if (!optDrops)
|
||||||
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
return Status{Error::rpcCHANNEL_AMT_MALFORMED, "couldNotParseAmount"};
|
||||||
|
|
||||||
std::uint64_t drops = *optDrops;
|
std::uint64_t drops = *optDrops;
|
||||||
|
|
||||||
auto sig = ripple::strUnHex(request.at("signature").as_string().c_str());
|
auto sig = ripple::strUnHex(request.at(JS(signature)).as_string().c_str());
|
||||||
|
|
||||||
if (!sig || !sig->size())
|
if (!sig || !sig->size())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidSignature"};
|
return Status{Error::rpcINVALID_PARAMS, "invalidSignature"};
|
||||||
@@ -81,7 +77,7 @@ doChannelVerify(Context const& context)
|
|||||||
ripple::serializePayChanAuthorization(
|
ripple::serializePayChanAuthorization(
|
||||||
msg, channelId, ripple::XRPAmount(drops));
|
msg, channelId, ripple::XRPAmount(drops));
|
||||||
|
|
||||||
response["signature_verified"] =
|
response[JS(signature_verified)] =
|
||||||
ripple::verify(*pk, msg.slice(), ripple::makeSlice(*sig), true);
|
ripple::verify(*pk, msg.slice(), ripple::makeSlice(*sig), true);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -9,17 +9,9 @@ doGatewayBalances(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains("account"))
|
ripple::AccountID accountID;
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingAccount"};
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
if (!request.at("account").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountNotString"};
|
|
||||||
|
|
||||||
auto accountID =
|
|
||||||
accountFromStringStrict(request.at("account").as_string().c_str());
|
|
||||||
|
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
auto v = ledgerInfoFromRequest(context);
|
auto v = ledgerInfoFromRequest(context);
|
||||||
if (auto status = std::get_if<Status>(&v))
|
if (auto status = std::get_if<Status>(&v))
|
||||||
@@ -81,7 +73,7 @@ doGatewayBalances(Context const& context)
|
|||||||
|
|
||||||
if (!valid)
|
if (!valid)
|
||||||
{
|
{
|
||||||
response["error"] = "invalidHotWallet";
|
response[JS(error)] = "invalidHotWallet";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +140,7 @@ doGatewayBalances(Context const& context)
|
|||||||
|
|
||||||
traverseOwnedNodes(
|
traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
std::numeric_limits<std::uint32_t>::max(),
|
std::numeric_limits<std::uint32_t>::max(),
|
||||||
{},
|
{},
|
||||||
@@ -162,7 +154,7 @@ doGatewayBalances(Context const& context)
|
|||||||
{
|
{
|
||||||
obj[ripple::to_string(k)] = v.getText();
|
obj[ripple::to_string(k)] = v.getText();
|
||||||
}
|
}
|
||||||
response["obligations"] = std::move(obj);
|
response[JS(obligations)] = std::move(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto toJson =
|
auto toJson =
|
||||||
@@ -177,9 +169,9 @@ doGatewayBalances(Context const& context)
|
|||||||
for (auto const& balance : accBalances)
|
for (auto const& balance : accBalances)
|
||||||
{
|
{
|
||||||
boost::json::object entry;
|
boost::json::object entry;
|
||||||
entry["currency"] =
|
entry[JS(currency)] =
|
||||||
ripple::to_string(balance.issue().currency);
|
ripple::to_string(balance.issue().currency);
|
||||||
entry["value"] = balance.getText();
|
entry[JS(value)] = balance.getText();
|
||||||
arr.push_back(std::move(entry));
|
arr.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
obj[ripple::to_string(accId)] = std::move(arr);
|
obj[ripple::to_string(accId)] = std::move(arr);
|
||||||
@@ -189,14 +181,14 @@ doGatewayBalances(Context const& context)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (auto balances = toJson(hotBalances); balances.size())
|
if (auto balances = toJson(hotBalances); balances.size())
|
||||||
response["balances"] = balances;
|
response[JS(balances)] = balances;
|
||||||
if (auto balances = toJson(frozenBalances); balances.size())
|
if (auto balances = toJson(frozenBalances); balances.size())
|
||||||
response["frozen_balances"] = balances;
|
response[JS(frozen_balances)] = balances;
|
||||||
if (auto balances = toJson(assets); assets.size())
|
if (auto balances = toJson(assets); assets.size())
|
||||||
response["assets"] = toJson(assets);
|
response[JS(assets)] = toJson(assets);
|
||||||
response["account"] = request.at("account");
|
response[JS(account)] = request.at(JS(account));
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -10,30 +10,30 @@ doLedger(Context const& context)
|
|||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
bool binary = false;
|
bool binary = false;
|
||||||
if (params.contains("binary"))
|
if (params.contains(JS(binary)))
|
||||||
{
|
{
|
||||||
if (!params.at("binary").is_bool())
|
if (!params.at(JS(binary)).is_bool())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
||||||
|
|
||||||
binary = params.at("binary").as_bool();
|
binary = params.at(JS(binary)).as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool transactions = false;
|
bool transactions = false;
|
||||||
if (params.contains("transactions"))
|
if (params.contains(JS(transactions)))
|
||||||
{
|
{
|
||||||
if (!params.at("transactions").is_bool())
|
if (!params.at(JS(transactions)).is_bool())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "transactionsFlagNotBool"};
|
return Status{Error::rpcINVALID_PARAMS, "transactionsFlagNotBool"};
|
||||||
|
|
||||||
transactions = params.at("transactions").as_bool();
|
transactions = params.at(JS(transactions)).as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expand = false;
|
bool expand = false;
|
||||||
if (params.contains("expand"))
|
if (params.contains(JS(expand)))
|
||||||
{
|
{
|
||||||
if (!params.at("expand").is_bool())
|
if (!params.at(JS(expand)).is_bool())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "expandFlagNotBool"};
|
return Status{Error::rpcINVALID_PARAMS, "expandFlagNotBool"};
|
||||||
|
|
||||||
expand = params.at("expand").as_bool();
|
expand = params.at(JS(expand)).as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool diff = false;
|
bool diff = false;
|
||||||
@@ -54,35 +54,34 @@ doLedger(Context const& context)
|
|||||||
boost::json::object header;
|
boost::json::object header;
|
||||||
if (binary)
|
if (binary)
|
||||||
{
|
{
|
||||||
header["ledger_data"] = ripple::strHex(ledgerInfoToBlob(lgrInfo));
|
header[JS(ledger_data)] = ripple::strHex(ledgerInfoToBlob(lgrInfo));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
header["accepted"] = true;
|
header[JS(accepted)] = true;
|
||||||
header["account_hash"] = ripple::strHex(lgrInfo.accountHash);
|
header[JS(account_hash)] = ripple::strHex(lgrInfo.accountHash);
|
||||||
header["close_flags"] = lgrInfo.closeFlags;
|
header[JS(close_flags)] = lgrInfo.closeFlags;
|
||||||
header["close_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
header[JS(close_time)] = lgrInfo.closeTime.time_since_epoch().count();
|
||||||
header["close_time_human"] = ripple::to_string(lgrInfo.closeTime);
|
header[JS(close_time_human)] = ripple::to_string(lgrInfo.closeTime);
|
||||||
;
|
header[JS(close_time_resolution)] = lgrInfo.closeTimeResolution.count();
|
||||||
header["close_time_resolution"] = lgrInfo.closeTimeResolution.count();
|
header[JS(closed)] = true;
|
||||||
header["closed"] = true;
|
header[JS(hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
header["hash"] = ripple::strHex(lgrInfo.hash);
|
header[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
header["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
header[JS(ledger_index)] = std::to_string(lgrInfo.seq);
|
||||||
header["ledger_index"] = std::to_string(lgrInfo.seq);
|
header[JS(parent_close_time)] =
|
||||||
header["parent_close_time"] =
|
|
||||||
lgrInfo.parentCloseTime.time_since_epoch().count();
|
lgrInfo.parentCloseTime.time_since_epoch().count();
|
||||||
header["parent_hash"] = ripple::strHex(lgrInfo.parentHash);
|
header[JS(parent_hash)] = ripple::strHex(lgrInfo.parentHash);
|
||||||
header["seqNum"] = std::to_string(lgrInfo.seq);
|
header[JS(seqNum)] = std::to_string(lgrInfo.seq);
|
||||||
header["totalCoins"] = ripple::to_string(lgrInfo.drops);
|
header[JS(totalCoins)] = ripple::to_string(lgrInfo.drops);
|
||||||
header["total_coins"] = ripple::to_string(lgrInfo.drops);
|
header[JS(total_coins)] = ripple::to_string(lgrInfo.drops);
|
||||||
header["transaction_hash"] = ripple::strHex(lgrInfo.txHash);
|
header[JS(transaction_hash)] = ripple::strHex(lgrInfo.txHash);
|
||||||
}
|
}
|
||||||
header["closed"] = true;
|
header[JS(closed)] = true;
|
||||||
|
|
||||||
if (transactions)
|
if (transactions)
|
||||||
{
|
{
|
||||||
header["transactions"] = boost::json::value(boost::json::array_kind);
|
header[JS(transactions)] = boost::json::value(boost::json::array_kind);
|
||||||
boost::json::array& jsonTxs = header.at("transactions").as_array();
|
boost::json::array& jsonTxs = header.at(JS(transactions)).as_array();
|
||||||
if (expand)
|
if (expand)
|
||||||
{
|
{
|
||||||
auto txns = context.backend->fetchAllTransactionsInLedger(
|
auto txns = context.backend->fetchAllTransactionsInLedger(
|
||||||
@@ -98,14 +97,14 @@ doLedger(Context const& context)
|
|||||||
{
|
{
|
||||||
auto [txn, meta] = toExpandedJson(obj);
|
auto [txn, meta] = toExpandedJson(obj);
|
||||||
entry = txn;
|
entry = txn;
|
||||||
entry["metaData"] = meta;
|
entry[JS(metaData)] = meta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
entry["tx_blob"] = ripple::strHex(obj.transaction);
|
entry[JS(tx_blob)] = ripple::strHex(obj.transaction);
|
||||||
entry["meta"] = ripple::strHex(obj.metadata);
|
entry[JS(meta)] = ripple::strHex(obj.metadata);
|
||||||
}
|
}
|
||||||
// entry["ledger_index"] = obj.ledgerSequence;
|
// entry[JS(ledger_index)] = obj.ledgerSequence;
|
||||||
return entry;
|
return entry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -133,7 +132,7 @@ doLedger(Context const& context)
|
|||||||
for (auto const& obj : diff)
|
for (auto const& obj : diff)
|
||||||
{
|
{
|
||||||
boost::json::object entry;
|
boost::json::object entry;
|
||||||
entry["id"] = ripple::strHex(obj.key);
|
entry[JS(id)] = ripple::strHex(obj.key);
|
||||||
if (binary)
|
if (binary)
|
||||||
entry["object"] = ripple::strHex(obj.blob);
|
entry["object"] = ripple::strHex(obj.blob);
|
||||||
else if (obj.blob.size())
|
else if (obj.blob.size())
|
||||||
@@ -149,9 +148,9 @@ doLedger(Context const& context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response["ledger"] = header;
|
response[JS(ledger)] = header;
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,23 +28,12 @@ doLedgerData(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
bool binary = false;
|
bool const binary = getBool(request, "binary", false);
|
||||||
if (request.contains("binary"))
|
|
||||||
{
|
|
||||||
if (!request.at("binary").is_bool())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
|
||||||
|
|
||||||
binary = request.at("binary").as_bool();
|
std::uint32_t limit = binary ? 2048 : 256;
|
||||||
}
|
if (auto const status = getLimit(request, limit); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
std::size_t limit = binary ? 2048 : 256;
|
|
||||||
if (request.contains("limit"))
|
|
||||||
{
|
|
||||||
if (!request.at("limit").is_int64())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInteger"};
|
|
||||||
|
|
||||||
limit = boost::json::value_to<int>(request.at("limit"));
|
|
||||||
}
|
|
||||||
bool outOfOrder = false;
|
bool outOfOrder = false;
|
||||||
if (request.contains("out_of_order"))
|
if (request.contains("out_of_order"))
|
||||||
{
|
{
|
||||||
@@ -53,18 +42,18 @@ doLedgerData(Context const& context)
|
|||||||
outOfOrder = request.at("out_of_order").as_bool();
|
outOfOrder = request.at("out_of_order").as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ripple::uint256> cursor;
|
std::optional<ripple::uint256> marker;
|
||||||
std::optional<uint32_t> diffCursor;
|
std::optional<uint32_t> diffMarker;
|
||||||
if (request.contains("marker"))
|
if (request.contains(JS(marker)))
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_string())
|
if (!request.at(JS(marker)).is_string())
|
||||||
{
|
{
|
||||||
if (outOfOrder)
|
if (outOfOrder)
|
||||||
{
|
{
|
||||||
if (!request.at("marker").is_int64())
|
if (!request.at(JS(marker)).is_int64())
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "markerNotStringOrInt"};
|
Error::rpcINVALID_PARAMS, "markerNotStringOrInt"};
|
||||||
diffCursor = value_to<uint32_t>(request.at("marker"));
|
diffMarker = value_to<uint32_t>(request.at(JS(marker)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
@@ -73,8 +62,8 @@ doLedgerData(Context const& context)
|
|||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : parsing marker";
|
BOOST_LOG_TRIVIAL(debug) << __func__ << " : parsing marker";
|
||||||
|
|
||||||
cursor = ripple::uint256{};
|
marker = ripple::uint256{};
|
||||||
if (!cursor->parseHex(request.at("marker").as_string().c_str()))
|
if (!marker->parseHex(request.at(JS(marker)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "markerMalformed"};
|
return Status{Error::rpcINVALID_PARAMS, "markerMalformed"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,48 +74,49 @@ doLedgerData(Context const& context)
|
|||||||
|
|
||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
boost::json::object header;
|
boost::json::object header;
|
||||||
// no cursor means this is the first call, so we return header info
|
// no marker means this is the first call, so we return header info
|
||||||
if (!cursor)
|
if (!marker)
|
||||||
{
|
{
|
||||||
if (binary)
|
if (binary)
|
||||||
{
|
{
|
||||||
header["ledger_data"] = ripple::strHex(ledgerInfoToBlob(lgrInfo));
|
header[JS(ledger_data)] = ripple::strHex(ledgerInfoToBlob(lgrInfo));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
header["accepted"] = true;
|
header[JS(accepted)] = true;
|
||||||
header["account_hash"] = ripple::strHex(lgrInfo.accountHash);
|
header[JS(account_hash)] = ripple::strHex(lgrInfo.accountHash);
|
||||||
header["close_flags"] = lgrInfo.closeFlags;
|
header[JS(close_flags)] = lgrInfo.closeFlags;
|
||||||
header["close_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
header[JS(close_time)] =
|
||||||
header["close_time_human"] = ripple::to_string(lgrInfo.closeTime);
|
lgrInfo.closeTime.time_since_epoch().count();
|
||||||
;
|
header[JS(close_time_human)] = ripple::to_string(lgrInfo.closeTime);
|
||||||
header["close_time_resolution"] =
|
header[JS(close_time_resolution)] =
|
||||||
lgrInfo.closeTimeResolution.count();
|
lgrInfo.closeTimeResolution.count();
|
||||||
header["closed"] = true;
|
header[JS(closed)] = true;
|
||||||
header["hash"] = ripple::strHex(lgrInfo.hash);
|
header[JS(hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
header["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
header[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
header["ledger_index"] = std::to_string(lgrInfo.seq);
|
header[JS(ledger_index)] = std::to_string(lgrInfo.seq);
|
||||||
header["parent_close_time"] =
|
header[JS(parent_close_time)] =
|
||||||
lgrInfo.parentCloseTime.time_since_epoch().count();
|
lgrInfo.parentCloseTime.time_since_epoch().count();
|
||||||
header["parent_hash"] = ripple::strHex(lgrInfo.parentHash);
|
header[JS(parent_hash)] = ripple::strHex(lgrInfo.parentHash);
|
||||||
header["seqNum"] = std::to_string(lgrInfo.seq);
|
header[JS(seqNum)] = std::to_string(lgrInfo.seq);
|
||||||
header["totalCoins"] = ripple::to_string(lgrInfo.drops);
|
header[JS(totalCoins)] = ripple::to_string(lgrInfo.drops);
|
||||||
header["total_coins"] = ripple::to_string(lgrInfo.drops);
|
header[JS(total_coins)] = ripple::to_string(lgrInfo.drops);
|
||||||
header["transaction_hash"] = ripple::strHex(lgrInfo.txHash);
|
header[JS(transaction_hash)] = ripple::strHex(lgrInfo.txHash);
|
||||||
|
|
||||||
response["ledger"] = header;
|
response[JS(ledger)] = header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto start = std::chrono::system_clock::now();
|
||||||
std::vector<Backend::LedgerObject> results;
|
std::vector<Backend::LedgerObject> results;
|
||||||
if (diffCursor)
|
if (diffMarker)
|
||||||
{
|
{
|
||||||
assert(outOfOrder);
|
assert(outOfOrder);
|
||||||
auto diff =
|
auto diff =
|
||||||
context.backend->fetchLedgerDiff(*diffCursor, context.yield);
|
context.backend->fetchLedgerDiff(*diffMarker, context.yield);
|
||||||
std::vector<ripple::uint256> keys;
|
std::vector<ripple::uint256> keys;
|
||||||
for (auto&& [key, object] : diff)
|
for (auto&& [key, object] : diff)
|
||||||
{
|
{
|
||||||
@@ -143,13 +133,13 @@ doLedgerData(Context const& context)
|
|||||||
if (obj.size())
|
if (obj.size())
|
||||||
results.push_back({std::move(keys[i]), std::move(obj)});
|
results.push_back({std::move(keys[i]), std::move(obj)});
|
||||||
}
|
}
|
||||||
if (*diffCursor > lgrInfo.seq)
|
if (*diffMarker > lgrInfo.seq)
|
||||||
response["marker"] = *diffCursor - 1;
|
response["marker"] = *diffMarker - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto page = context.backend->fetchLedgerPage(
|
auto page = context.backend->fetchLedgerPage(
|
||||||
cursor, lgrInfo.seq, limit, outOfOrder, context.yield);
|
marker, lgrInfo.seq, limit, outOfOrder, context.yield);
|
||||||
results = std::move(page.objects);
|
results = std::move(page.objects);
|
||||||
if (page.cursor)
|
if (page.cursor)
|
||||||
response["marker"] = ripple::strHex(*(page.cursor));
|
response["marker"] = ripple::strHex(*(page.cursor));
|
||||||
@@ -175,14 +165,14 @@ doLedgerData(Context const& context)
|
|||||||
if (binary)
|
if (binary)
|
||||||
{
|
{
|
||||||
boost::json::object entry;
|
boost::json::object entry;
|
||||||
entry["data"] = ripple::serializeHex(sle);
|
entry[JS(data)] = ripple::serializeHex(sle);
|
||||||
entry["index"] = ripple::to_string(sle.key());
|
entry[JS(index)] = ripple::to_string(sle.key());
|
||||||
objects.push_back(std::move(entry));
|
objects.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
objects.push_back(toJson(sle));
|
objects.push_back(toJson(sle));
|
||||||
}
|
}
|
||||||
response["state"] = std::move(objects);
|
response[JS(state)] = std::move(objects);
|
||||||
auto end2 = std::chrono::system_clock::now();
|
auto end2 = std::chrono::system_clock::now();
|
||||||
|
|
||||||
time = std::chrono::duration_cast<std::chrono::microseconds>(end2 - end)
|
time = std::chrono::duration_cast<std::chrono::microseconds>(end2 - end)
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ doLedgerEntry(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
bool binary =
|
bool const binary = getBool(request, "binary", false);
|
||||||
request.contains("binary") ? request.at("binary").as_bool() : false;
|
|
||||||
|
|
||||||
auto v = ledgerInfoFromRequest(context);
|
auto v = ledgerInfoFromRequest(context);
|
||||||
if (auto status = std::get_if<Status>(&v))
|
if (auto status = std::get_if<Status>(&v))
|
||||||
@@ -30,59 +29,64 @@ doLedgerEntry(Context const& context)
|
|||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
ripple::uint256 key;
|
ripple::uint256 key;
|
||||||
if (request.contains("index"))
|
if (request.contains(JS(index)))
|
||||||
{
|
{
|
||||||
if (!request.at("index").is_string())
|
if (!request.at(JS(index)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "indexNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "indexNotString"};
|
||||||
|
|
||||||
if (!key.parseHex(request.at("index").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(index)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedIndex"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedIndex"};
|
||||||
}
|
}
|
||||||
else if (request.contains("account_root"))
|
else if (request.contains(JS(account_root)))
|
||||||
{
|
{
|
||||||
if (!request.at("account_root").is_string())
|
if (!request.at(JS(account_root)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "account_rootNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "account_rootNotString"};
|
||||||
|
|
||||||
auto const account = ripple::parseBase58<ripple::AccountID>(
|
auto const account = ripple::parseBase58<ripple::AccountID>(
|
||||||
request.at("account_root").as_string().c_str());
|
request.at(JS(account_root)).as_string().c_str());
|
||||||
if (!account || account->isZero())
|
if (!account || account->isZero())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedAddress"};
|
||||||
else
|
else
|
||||||
key = ripple::keylet::account(*account).key;
|
key = ripple::keylet::account(*account).key;
|
||||||
}
|
}
|
||||||
else if (request.contains("check"))
|
else if (request.contains(JS(check)))
|
||||||
{
|
{
|
||||||
if (!request.at("check").is_string())
|
if (!request.at(JS(check)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "checkNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "checkNotString"};
|
||||||
|
|
||||||
if (!key.parseHex(request.at("check").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(check)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "checkMalformed"};
|
return Status{Error::rpcINVALID_PARAMS, "checkMalformed"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.contains("deposit_preauth"))
|
else if (request.contains(JS(deposit_preauth)))
|
||||||
{
|
{
|
||||||
if (!request.at("deposit_preauth").is_object())
|
if (!request.at(JS(deposit_preauth)).is_object())
|
||||||
{
|
{
|
||||||
if (!request.at("deposit_preauth").is_string() ||
|
if (!request.at(JS(deposit_preauth)).is_string() ||
|
||||||
!key.parseHex(
|
!key.parseHex(
|
||||||
request.at("deposit_preauth").as_string().c_str()))
|
request.at(JS(deposit_preauth)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{
|
return Status{
|
||||||
Error::rpcINVALID_PARAMS, "deposit_preauthMalformed"};
|
Error::rpcINVALID_PARAMS, "deposit_preauthMalformed"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("deposit_preauth").as_object().contains("owner") ||
|
!request.at(JS(deposit_preauth)).as_object().contains(JS(owner)) ||
|
||||||
!request.at("deposit_preauth").as_object().at("owner").is_string())
|
!request.at(JS(deposit_preauth))
|
||||||
|
.as_object()
|
||||||
|
.at(JS(owner))
|
||||||
|
.is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ownerNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "ownerNotString"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("deposit_preauth").as_object().contains("authorized") ||
|
!request.at(JS(deposit_preauth))
|
||||||
!request.at("deposit_preauth")
|
|
||||||
.as_object()
|
.as_object()
|
||||||
.at("authorized")
|
.contains(JS(authorized)) ||
|
||||||
|
!request.at(JS(deposit_preauth))
|
||||||
|
.as_object()
|
||||||
|
.at(JS(authorized))
|
||||||
.is_string())
|
.is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "authorizedNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "authorizedNotString"};
|
||||||
@@ -90,13 +94,13 @@ doLedgerEntry(Context const& context)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
boost::json::object const& deposit_preauth =
|
boost::json::object const& deposit_preauth =
|
||||||
request.at("deposit_preauth").as_object();
|
request.at(JS(deposit_preauth)).as_object();
|
||||||
|
|
||||||
auto const owner = ripple::parseBase58<ripple::AccountID>(
|
auto const owner = ripple::parseBase58<ripple::AccountID>(
|
||||||
deposit_preauth.at("owner").as_string().c_str());
|
deposit_preauth.at(JS(owner)).as_string().c_str());
|
||||||
|
|
||||||
auto const authorized = ripple::parseBase58<ripple::AccountID>(
|
auto const authorized = ripple::parseBase58<ripple::AccountID>(
|
||||||
deposit_preauth.at("authorized").as_string().c_str());
|
deposit_preauth.at(JS(authorized)).as_string().c_str());
|
||||||
|
|
||||||
if (!owner)
|
if (!owner)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
||||||
@@ -106,37 +110,37 @@ doLedgerEntry(Context const& context)
|
|||||||
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
|
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.contains("directory"))
|
else if (request.contains(JS(directory)))
|
||||||
{
|
{
|
||||||
if (!request.at("directory").is_object())
|
if (!request.at(JS(directory)).is_object())
|
||||||
{
|
{
|
||||||
if (!request.at("directory").is_string())
|
if (!request.at(JS(directory)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "directoryNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "directoryNotString"};
|
||||||
|
|
||||||
if (!key.parseHex(request.at("directory").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(directory)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedDirectory"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedDirectory"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
request.at("directory").as_object().contains("sub_index") &&
|
request.at(JS(directory)).as_object().contains(JS(sub_index)) &&
|
||||||
!request.at("directory").as_object().at("sub_index").is_int64())
|
!request.at(JS(directory)).as_object().at(JS(sub_index)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "sub_indexNotInt"};
|
return Status{Error::rpcINVALID_PARAMS, "sub_indexNotInt"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto directory = request.at("directory").as_object();
|
auto directory = request.at(JS(directory)).as_object();
|
||||||
std::uint64_t subIndex = directory.contains("sub_index")
|
std::uint64_t subIndex = directory.contains(JS(sub_index))
|
||||||
? boost::json::value_to<std::uint64_t>(
|
? boost::json::value_to<std::uint64_t>(
|
||||||
directory.at("sub_index"))
|
directory.at(JS(sub_index)))
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
if (directory.contains("dir_root"))
|
if (directory.contains(JS(dir_root)))
|
||||||
{
|
{
|
||||||
ripple::uint256 uDirRoot;
|
ripple::uint256 uDirRoot;
|
||||||
|
|
||||||
if (directory.contains("owner"))
|
if (directory.contains(JS(owner)))
|
||||||
{
|
{
|
||||||
// May not specify both dir_root and owner.
|
// May not specify both dir_root and owner.
|
||||||
return Status{
|
return Status{
|
||||||
@@ -144,7 +148,7 @@ doLedgerEntry(Context const& context)
|
|||||||
"mayNotSpecifyBothDirRootAndOwner"};
|
"mayNotSpecifyBothDirRootAndOwner"};
|
||||||
}
|
}
|
||||||
else if (!uDirRoot.parseHex(
|
else if (!uDirRoot.parseHex(
|
||||||
directory.at("dir_root").as_string().c_str()))
|
directory.at(JS(dir_root)).as_string().c_str()))
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedDirRoot"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedDirRoot"};
|
||||||
}
|
}
|
||||||
@@ -153,10 +157,10 @@ doLedgerEntry(Context const& context)
|
|||||||
key = ripple::keylet::page(uDirRoot, subIndex).key;
|
key = ripple::keylet::page(uDirRoot, subIndex).key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (directory.contains("owner"))
|
else if (directory.contains(JS(owner)))
|
||||||
{
|
{
|
||||||
auto const ownerID = ripple::parseBase58<ripple::AccountID>(
|
auto const ownerID = ripple::parseBase58<ripple::AccountID>(
|
||||||
directory.at("owner").as_string().c_str());
|
directory.at(JS(owner)).as_string().c_str());
|
||||||
|
|
||||||
if (!ownerID)
|
if (!ownerID)
|
||||||
{
|
{
|
||||||
@@ -176,31 +180,31 @@ doLedgerEntry(Context const& context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.contains("escrow"))
|
else if (request.contains(JS(escrow)))
|
||||||
{
|
{
|
||||||
if (!request.at("escrow").is_object())
|
if (!request.at(JS(escrow)).is_object())
|
||||||
{
|
{
|
||||||
if (!key.parseHex(request.at("escrow").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(escrow)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedEscrow"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedEscrow"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("escrow").as_object().contains("owner") ||
|
!request.at(JS(escrow)).as_object().contains(JS(owner)) ||
|
||||||
!request.at("escrow").as_object().at("owner").is_string())
|
!request.at(JS(escrow)).as_object().at(JS(owner)).is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedOwner"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("escrow").as_object().contains("seq") ||
|
!request.at(JS(escrow)).as_object().contains(JS(seq)) ||
|
||||||
!request.at("escrow").as_object().at("seq").is_int64())
|
!request.at(JS(escrow)).as_object().at(JS(seq)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const id =
|
auto const id =
|
||||||
ripple::parseBase58<ripple::AccountID>(request.at("escrow")
|
ripple::parseBase58<ripple::AccountID>(request.at(JS(escrow))
|
||||||
.as_object()
|
.as_object()
|
||||||
.at("owner")
|
.at(JS(owner))
|
||||||
.as_string()
|
.as_string()
|
||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
@@ -209,120 +213,122 @@ doLedgerEntry(Context const& context)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq =
|
std::uint32_t seq =
|
||||||
request.at("escrow").as_object().at("seq").as_int64();
|
request.at(JS(escrow)).as_object().at(JS(seq)).as_int64();
|
||||||
key = ripple::keylet::escrow(*id, seq).key;
|
key = ripple::keylet::escrow(*id, seq).key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.contains("offer"))
|
else if (request.contains(JS(offer)))
|
||||||
{
|
{
|
||||||
if (!request.at("offer").is_object())
|
if (!request.at(JS(offer)).is_object())
|
||||||
{
|
{
|
||||||
if (!key.parseHex(request.at("offer").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(offer)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedOffer"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedOffer"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("offer").as_object().contains("account") ||
|
!request.at(JS(offer)).as_object().contains(JS(account)) ||
|
||||||
!request.at("offer").as_object().at("account").is_string())
|
!request.at(JS(offer)).as_object().at(JS(account)).is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("offer").as_object().contains("seq") ||
|
!request.at(JS(offer)).as_object().contains(JS(seq)) ||
|
||||||
!request.at("offer").as_object().at("seq").is_int64())
|
!request.at(JS(offer)).as_object().at(JS(seq)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedSeq"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto offer = request.at("offer").as_object();
|
auto offer = request.at(JS(offer)).as_object();
|
||||||
auto const id = ripple::parseBase58<ripple::AccountID>(
|
auto const id = ripple::parseBase58<ripple::AccountID>(
|
||||||
offer.at("account").as_string().c_str());
|
offer.at(JS(account)).as_string().c_str());
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq =
|
std::uint32_t seq =
|
||||||
boost::json::value_to<std::uint32_t>(offer.at("seq"));
|
boost::json::value_to<std::uint32_t>(offer.at(JS(seq)));
|
||||||
key = ripple::keylet::offer(*id, seq).key;
|
key = ripple::keylet::offer(*id, seq).key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.contains("payment_channel"))
|
else if (request.contains(JS(payment_channel)))
|
||||||
{
|
{
|
||||||
if (!request.at("payment_channel").is_string())
|
if (!request.at(JS(payment_channel)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "paymentChannelNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "paymentChannelNotString"};
|
||||||
|
|
||||||
if (!key.parseHex(request.at("payment_channel").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(payment_channel)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedPaymentChannel"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedPaymentChannel"};
|
||||||
}
|
}
|
||||||
else if (request.contains("ripple_state"))
|
else if (request.contains(JS(ripple_state)))
|
||||||
{
|
{
|
||||||
if (!request.at("ripple_state").is_object())
|
if (!request.at(JS(ripple_state)).is_object())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "rippleStateNotObject"};
|
return Status{Error::rpcINVALID_PARAMS, "rippleStateNotObject"};
|
||||||
|
|
||||||
ripple::Currency currency;
|
ripple::Currency currency;
|
||||||
boost::json::object const& state =
|
boost::json::object const& state =
|
||||||
request.at("ripple_state").as_object();
|
request.at(JS(ripple_state)).as_object();
|
||||||
|
|
||||||
if (!state.contains("currency") || !state.at("currency").is_string())
|
if (!state.contains(JS(currency)) ||
|
||||||
|
!state.at(JS(currency)).is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.contains("accounts") || !state.at("accounts").is_array() ||
|
if (!state.contains(JS(accounts)) ||
|
||||||
2 != state.at("accounts").as_array().size() ||
|
!state.at(JS(accounts)).is_array() ||
|
||||||
!state.at("accounts").as_array().at(0).is_string() ||
|
2 != state.at(JS(accounts)).as_array().size() ||
|
||||||
!state.at("accounts").as_array().at(1).is_string() ||
|
!state.at(JS(accounts)).as_array().at(0).is_string() ||
|
||||||
(state.at("accounts").as_array().at(0).as_string() ==
|
!state.at(JS(accounts)).as_array().at(1).is_string() ||
|
||||||
state.at("accounts").as_array().at(1).as_string()))
|
(state.at(JS(accounts)).as_array().at(0).as_string() ==
|
||||||
|
state.at(JS(accounts)).as_array().at(1).as_string()))
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const id1 = ripple::parseBase58<ripple::AccountID>(
|
auto const id1 = ripple::parseBase58<ripple::AccountID>(
|
||||||
state.at("accounts").as_array().at(0).as_string().c_str());
|
state.at(JS(accounts)).as_array().at(0).as_string().c_str());
|
||||||
auto const id2 = ripple::parseBase58<ripple::AccountID>(
|
auto const id2 = ripple::parseBase58<ripple::AccountID>(
|
||||||
state.at("accounts").as_array().at(1).as_string().c_str());
|
state.at(JS(accounts)).as_array().at(1).as_string().c_str());
|
||||||
|
|
||||||
if (!id1 || !id2)
|
if (!id1 || !id2)
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedAccounts"};
|
||||||
|
|
||||||
else if (!ripple::to_currency(
|
else if (!ripple::to_currency(
|
||||||
currency, state.at("currency").as_string().c_str()))
|
currency, state.at(JS(currency)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedCurrency"};
|
||||||
|
|
||||||
key = ripple::keylet::line(*id1, *id2, currency).key;
|
key = ripple::keylet::line(*id1, *id2, currency).key;
|
||||||
}
|
}
|
||||||
else if (request.contains("ticket"))
|
else if (request.contains(JS(ticket)))
|
||||||
{
|
{
|
||||||
if (!request.at("ticket").is_object())
|
if (!request.at(JS(ticket)).is_object())
|
||||||
{
|
{
|
||||||
if (!request.at("ticket").is_string())
|
if (!request.at(JS(ticket)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "ticketNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "ticketNotString"};
|
||||||
|
|
||||||
if (!key.parseHex(request.at("ticket").as_string().c_str()))
|
if (!key.parseHex(request.at(JS(ticket)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedTicket"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedTicket"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("ticket").as_object().contains("account") ||
|
!request.at(JS(ticket)).as_object().contains(JS(account)) ||
|
||||||
!request.at("ticket").as_object().at("account").is_string())
|
!request.at(JS(ticket)).as_object().at(JS(account)).is_string())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!request.at("ticket").as_object().contains("ticket_seq") ||
|
!request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) ||
|
||||||
!request.at("ticket").as_object().at("ticket_seq").is_int64())
|
!request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64())
|
||||||
{
|
{
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedTicketSeq"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedTicketSeq"};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const id =
|
auto const id =
|
||||||
ripple::parseBase58<ripple::AccountID>(request.at("ticket")
|
ripple::parseBase58<ripple::AccountID>(request.at(JS(ticket))
|
||||||
.as_object()
|
.as_object()
|
||||||
.at("account")
|
.at(JS(account))
|
||||||
.as_string()
|
.as_string()
|
||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
@@ -331,8 +337,10 @@ doLedgerEntry(Context const& context)
|
|||||||
Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
|
Error::rpcINVALID_PARAMS, "malformedTicketAccount"};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint32_t seq =
|
std::uint32_t seq = request.at(JS(offer))
|
||||||
request.at("offer").as_object().at("ticket_seq").as_int64();
|
.as_object()
|
||||||
|
.at(JS(ticket_seq))
|
||||||
|
.as_int64();
|
||||||
|
|
||||||
key = ripple::getTicketIndex(*id, seq);
|
key = ripple::getTicketIndex(*id, seq);
|
||||||
}
|
}
|
||||||
@@ -351,19 +359,19 @@ doLedgerEntry(Context const& context)
|
|||||||
if (!dbResponse or dbResponse->size() == 0)
|
if (!dbResponse or dbResponse->size() == 0)
|
||||||
return Status{Error::rpcOBJECT_NOT_FOUND, "entryNotFound"};
|
return Status{Error::rpcOBJECT_NOT_FOUND, "entryNotFound"};
|
||||||
|
|
||||||
response["index"] = ripple::strHex(key);
|
response[JS(index)] = ripple::strHex(key);
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
|
|
||||||
if (binary)
|
if (binary)
|
||||||
{
|
{
|
||||||
response["node_binary"] = ripple::strHex(*dbResponse);
|
response[JS(node_binary)] = ripple::strHex(*dbResponse);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ripple::STLedgerEntry sle{
|
ripple::STLedgerEntry sle{
|
||||||
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key};
|
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key};
|
||||||
response["node"] = toJson(sle);
|
response[JS(node)] = toJson(sle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ doLedgerRange(Context const& context)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response["ledger_index_min"] = range->minSequence;
|
response[JS(ledger_index_min)] = range->minSequence;
|
||||||
response["ledger_index_max"] = range->maxSequence;
|
response[JS(ledger_index_max)] = range->maxSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
178
src/rpc/handlers/NFTOffers.cpp
Normal file
178
src/rpc/handlers/NFTOffers.cpp
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
|
#include <ripple/basics/StringUtilities.h>
|
||||||
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
#include <ripple/protocol/STLedgerEntry.h>
|
||||||
|
#include <ripple/protocol/jss.h>
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <rpc/RPCHelpers.h>
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
static void
|
||||||
|
appendNftOfferJson(ripple::SLE const& offer, boost::json::array& offers)
|
||||||
|
{
|
||||||
|
offers.push_back(boost::json::object_kind);
|
||||||
|
boost::json::object& obj(offers.back().as_object());
|
||||||
|
|
||||||
|
obj.at(JS(index)) = ripple::to_string(offer.key());
|
||||||
|
obj.at(JS(flags)) = (offer)[ripple::sfFlags];
|
||||||
|
obj.at(JS(owner)) = ripple::toBase58(offer.getAccountID(ripple::sfOwner));
|
||||||
|
|
||||||
|
if (offer.isFieldPresent(ripple::sfDestination))
|
||||||
|
obj[JS(destination)] =
|
||||||
|
ripple::toBase58(offer.getAccountID(ripple::sfDestination));
|
||||||
|
|
||||||
|
if (offer.isFieldPresent(ripple::sfExpiration))
|
||||||
|
obj.at(JS(expiration)) = offer.getFieldU32(ripple::sfExpiration);
|
||||||
|
|
||||||
|
obj.at(JS(amount)) = toBoostJson(offer.getFieldAmount(ripple::sfAmount)
|
||||||
|
.getJson(ripple::JsonOptions::none));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result
|
||||||
|
enumerateNFTOffers(
|
||||||
|
Context const& context,
|
||||||
|
ripple::uint256 const& tokenid,
|
||||||
|
ripple::Keylet const& directory)
|
||||||
|
{
|
||||||
|
auto const& request = context.params;
|
||||||
|
|
||||||
|
auto v = ledgerInfoFromRequest(context);
|
||||||
|
if (auto status = std::get_if<Status>(&v))
|
||||||
|
return *status;
|
||||||
|
|
||||||
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
|
// TODO: just check for existence without pulling
|
||||||
|
if (!context.backend->fetchLedgerObject(
|
||||||
|
directory.key, lgrInfo.seq, context.yield))
|
||||||
|
return Status{Error::rpcOBJECT_NOT_FOUND, "notFound"};
|
||||||
|
|
||||||
|
std::uint32_t limit = 200;
|
||||||
|
if (auto const status = getLimit(request, limit); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
boost::json::object response = {};
|
||||||
|
response[JS(nft_id)] = ripple::to_string(tokenid);
|
||||||
|
response[JS(offers)] = boost::json::value(boost::json::array_kind);
|
||||||
|
|
||||||
|
auto& jsonOffers = response[JS(offers)].as_array();
|
||||||
|
|
||||||
|
std::vector<ripple::SLE> offers;
|
||||||
|
std::uint64_t reserve(limit);
|
||||||
|
ripple::uint256 cursor;
|
||||||
|
|
||||||
|
if (request.contains(JS(marker)))
|
||||||
|
{
|
||||||
|
// We have a start point. Use limit - 1 from the result and use the
|
||||||
|
// very last one for the resume.
|
||||||
|
auto const& marker(request.at(JS(marker)));
|
||||||
|
|
||||||
|
if (!marker.is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "markerNotString"};
|
||||||
|
|
||||||
|
if (!cursor.parseHex(marker.as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "malformedCursor"};
|
||||||
|
|
||||||
|
auto const sle =
|
||||||
|
read(ripple::keylet::nftoffer(cursor), lgrInfo, context);
|
||||||
|
|
||||||
|
if (!sle || tokenid != sle->getFieldH256(ripple::sfNFTokenID))
|
||||||
|
return Status{Error::rpcOBJECT_NOT_FOUND, "notFound"};
|
||||||
|
|
||||||
|
if (tokenid != sle->getFieldH256(ripple::sfNFTokenID))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "invalidTokenid"};
|
||||||
|
|
||||||
|
appendNftOfferJson(*sle, jsonOffers);
|
||||||
|
offers.reserve(reserve);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We have no start point, limit should be one higher than requested.
|
||||||
|
offers.reserve(++reserve);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = traverseOwnedNodes(
|
||||||
|
*context.backend,
|
||||||
|
directory,
|
||||||
|
cursor,
|
||||||
|
0,
|
||||||
|
lgrInfo.seq,
|
||||||
|
limit,
|
||||||
|
{},
|
||||||
|
context.yield,
|
||||||
|
[&offers](ripple::SLE const& offer) {
|
||||||
|
if (offer.getType() == ripple::ltNFTOKEN_OFFER)
|
||||||
|
{
|
||||||
|
offers.emplace_back(offer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (auto status = std::get_if<RPC::Status>(&result))
|
||||||
|
return *status;
|
||||||
|
|
||||||
|
if (offers.size() == reserve)
|
||||||
|
{
|
||||||
|
response.at(JS(limit)) = limit;
|
||||||
|
response.at(JS(marker)) = to_string(offers.back().key());
|
||||||
|
offers.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& offer : offers)
|
||||||
|
appendNftOfferJson(offer, jsonOffers);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<ripple::uint256, Status>
|
||||||
|
getTokenid(boost::json::object const& request)
|
||||||
|
{
|
||||||
|
if (!request.contains(JS(nft_id)))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingTokenid"};
|
||||||
|
|
||||||
|
if (!request.at(JS(nft_id)).is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "tokenidNotString"};
|
||||||
|
|
||||||
|
ripple::uint256 tokenid;
|
||||||
|
if (!tokenid.parseHex(request.at(JS(nft_id)).as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "malformedCursor"};
|
||||||
|
|
||||||
|
return tokenid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result
|
||||||
|
doNFTOffers(Context const& context, bool sells)
|
||||||
|
{
|
||||||
|
auto const v = getTokenid(context.params);
|
||||||
|
if (auto const status = std::get_if<Status>(&v))
|
||||||
|
return *status;
|
||||||
|
|
||||||
|
auto const getKeylet = [sells, &v]() {
|
||||||
|
if (sells)
|
||||||
|
return ripple::keylet::nft_sells(std::get<ripple::uint256>(v));
|
||||||
|
|
||||||
|
return ripple::keylet::nft_buys(std::get<ripple::uint256>(v));
|
||||||
|
};
|
||||||
|
|
||||||
|
return enumerateNFTOffers(
|
||||||
|
context, std::get<ripple::uint256>(v), getKeylet());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result
|
||||||
|
doNFTSellOffers(Context const& context)
|
||||||
|
{
|
||||||
|
return doNFTOffers(context, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result
|
||||||
|
doNFTBuyOffers(Context const& context)
|
||||||
|
{
|
||||||
|
return doNFTOffers(context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
@@ -10,9 +10,9 @@ getBaseTx(
|
|||||||
ripple::Fees const& fees)
|
ripple::Fees const& fees)
|
||||||
{
|
{
|
||||||
boost::json::object tx;
|
boost::json::object tx;
|
||||||
tx["Sequence"] = accountSeq;
|
tx[JS(Sequence)] = accountSeq;
|
||||||
tx["Account"] = ripple::toBase58(accountID);
|
tx[JS(Account)] = ripple::toBase58(accountID);
|
||||||
tx["Fee"] = RPC::toBoostJson(fees.units.jsonClipped());
|
tx[JS(Fee)] = RPC::toBoostJson(fees.units.jsonClipped());
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,11 +21,9 @@ doNoRippleCheck(Context const& context)
|
|||||||
{
|
{
|
||||||
auto const& request = context.params;
|
auto const& request = context.params;
|
||||||
|
|
||||||
auto accountID =
|
ripple::AccountID accountID;
|
||||||
accountFromStringStrict(getRequiredString(request, "account"));
|
if (auto const status = getAccount(request, accountID); status)
|
||||||
|
return status;
|
||||||
if (!accountID)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedAccount"};
|
|
||||||
|
|
||||||
std::string role = getRequiredString(request, "role");
|
std::string role = getRequiredString(request, "role");
|
||||||
bool roleGateway = false;
|
bool roleGateway = false;
|
||||||
@@ -36,7 +34,9 @@ doNoRippleCheck(Context const& context)
|
|||||||
return Status{Error::rpcINVALID_PARAMS, "role field is invalid"};
|
return Status{Error::rpcINVALID_PARAMS, "role field is invalid"};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t limit = getUInt(request, "limit", 300);
|
std::uint32_t limit = 300;
|
||||||
|
if (auto const status = getLimit(request, limit); status)
|
||||||
|
return status;
|
||||||
|
|
||||||
bool includeTxs = getBool(request, "transactions", false);
|
bool includeTxs = getBool(request, "transactions", false);
|
||||||
|
|
||||||
@@ -51,11 +51,11 @@ doNoRippleCheck(Context const& context)
|
|||||||
|
|
||||||
boost::json::array transactions;
|
boost::json::array transactions;
|
||||||
|
|
||||||
auto keylet = ripple::keylet::account(*accountID);
|
auto keylet = ripple::keylet::account(accountID);
|
||||||
auto accountObj = context.backend->fetchLedgerObject(
|
auto accountObj = context.backend->fetchLedgerObject(
|
||||||
keylet.key, lgrInfo.seq, context.yield);
|
keylet.key, lgrInfo.seq, context.yield);
|
||||||
if (!accountObj)
|
if (!accountObj)
|
||||||
throw AccountNotFoundError(ripple::toBase58(*accountID));
|
throw AccountNotFoundError(ripple::toBase58(accountID));
|
||||||
|
|
||||||
ripple::SerialIter it{accountObj->data(), accountObj->size()};
|
ripple::SerialIter it{accountObj->data(), accountObj->size()};
|
||||||
ripple::SLE sle{it, keylet.key};
|
ripple::SLE sle{it, keylet.key};
|
||||||
@@ -79,16 +79,16 @@ doNoRippleCheck(Context const& context)
|
|||||||
"You should immediately set your default ripple flag");
|
"You should immediately set your default ripple flag");
|
||||||
if (includeTxs)
|
if (includeTxs)
|
||||||
{
|
{
|
||||||
auto tx = getBaseTx(*accountID, accountSeq++, *fees);
|
auto tx = getBaseTx(accountID, accountSeq++, *fees);
|
||||||
tx["TransactionType"] = "AccountSet";
|
tx[JS(TransactionType)] = JS(AccountSet);
|
||||||
tx["SetFlag"] = 8;
|
tx[JS(SetFlag)] = 8;
|
||||||
transactions.push_back(tx);
|
transactions.push_back(tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
traverseOwnedNodes(
|
traverseOwnedNodes(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
*accountID,
|
accountID,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
std::numeric_limits<std::uint32_t>::max(),
|
std::numeric_limits<std::uint32_t>::max(),
|
||||||
{},
|
{},
|
||||||
@@ -141,12 +141,12 @@ doNoRippleCheck(Context const& context)
|
|||||||
ripple::STAmount limitAmount(ownedItem.getFieldAmount(
|
ripple::STAmount limitAmount(ownedItem.getFieldAmount(
|
||||||
bLow ? ripple::sfLowLimit : ripple::sfHighLimit));
|
bLow ? ripple::sfLowLimit : ripple::sfHighLimit));
|
||||||
limitAmount.setIssuer(peer);
|
limitAmount.setIssuer(peer);
|
||||||
auto tx = getBaseTx(*accountID, accountSeq++, *fees);
|
auto tx = getBaseTx(accountID, accountSeq++, *fees);
|
||||||
tx["TransactionType"] = "TrustSet";
|
tx[JS(TransactionType)] = JS(TrustSet);
|
||||||
tx["LimitAmount"] = RPC::toBoostJson(
|
tx[JS(LimitAmount)] = RPC::toBoostJson(
|
||||||
limitAmount.getJson(ripple::JsonOptions::none));
|
limitAmount.getJson(ripple::JsonOptions::none));
|
||||||
tx["Flags"] = bNoRipple ? ripple::tfClearNoRipple
|
tx[JS(Flags)] = bNoRipple ? ripple::tfClearNoRipple
|
||||||
: ripple::tfSetNoRipple;
|
: ripple::tfSetNoRipple;
|
||||||
transactions.push_back(tx);
|
transactions.push_back(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +158,11 @@ doNoRippleCheck(Context const& context)
|
|||||||
});
|
});
|
||||||
|
|
||||||
boost::json::object response;
|
boost::json::object response;
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
response["problems"] = std::move(problems);
|
response["problems"] = std::move(problems);
|
||||||
if (includeTxs)
|
if (includeTxs)
|
||||||
response["transactions"] = std::move(transactions);
|
response[JS(transactions)] = std::move(transactions);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
// rngfill.h doesn't compile without this include
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <ripple/beast/utility/rngfill.h>
|
#include <ripple/beast/utility/rngfill.h>
|
||||||
#include <ripple/crypto/csprng.h>
|
#include <ripple/crypto/csprng.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
|
||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
Result
|
Result
|
||||||
@@ -10,7 +14,8 @@ doRandom(Context const& context)
|
|||||||
|
|
||||||
beast::rngfill(rand.begin(), rand.size(), ripple::crypto_prng());
|
beast::rngfill(rand.begin(), rand.size(), ripple::crypto_prng());
|
||||||
boost::json::object result;
|
boost::json::object result;
|
||||||
result["random"] = ripple::strHex(rand);
|
result[JS(random)] = ripple::strHex(rand);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -36,42 +36,42 @@ doServerInfo(Context const& context)
|
|||||||
if (age < 0)
|
if (age < 0)
|
||||||
age = 0;
|
age = 0;
|
||||||
|
|
||||||
response["info"] = boost::json::object{};
|
response[JS(info)] = boost::json::object{};
|
||||||
boost::json::object& info = response["info"].as_object();
|
boost::json::object& info = response[JS(info)].as_object();
|
||||||
|
|
||||||
info["complete_ledgers"] = std::to_string(range->minSequence) + "-" +
|
info[JS(complete_ledgers)] = std::to_string(range->minSequence) + "-" +
|
||||||
std::to_string(range->maxSequence);
|
std::to_string(range->maxSequence);
|
||||||
|
|
||||||
info["counters"] = boost::json::object{};
|
info[JS(counters)] = boost::json::object{};
|
||||||
info["counters"].as_object()["rpc"] = context.counters.report();
|
info[JS(counters)].as_object()[JS(rpc)] = context.counters.report();
|
||||||
|
|
||||||
auto serverInfoRippled = context.balancer->forwardToRippled(
|
auto serverInfoRippled = context.balancer->forwardToRippled(
|
||||||
{{"command", "server_info"}}, context.clientIp, context.yield);
|
{{"counters", "server_info"}}, context.clientIp, context.yield);
|
||||||
|
|
||||||
info["load_factor"] = 1;
|
info[JS(load_factor)] = 1;
|
||||||
if (serverInfoRippled && !serverInfoRippled->contains("error"))
|
if (serverInfoRippled && !serverInfoRippled->contains(JS(error)))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto& rippledResult = serverInfoRippled->at("result").as_object();
|
auto& rippledResult = serverInfoRippled->at(JS(result)).as_object();
|
||||||
auto& rippledInfo = rippledResult.at("info").as_object();
|
auto& rippledInfo = rippledResult.at(JS(info)).as_object();
|
||||||
info["load_factor"] = rippledInfo["load_factor"];
|
info[JS(load_factor)] = rippledInfo[JS(load_factor)];
|
||||||
info["validation_quorum"] = rippledInfo["validation_quorum"];
|
info[JS(validation_quorum)] = rippledInfo[JS(validation_quorum)];
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (std::exception const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info["validated_ledger"] = boost::json::object{};
|
info[JS(validated_ledger)] = boost::json::object{};
|
||||||
boost::json::object& validated = info["validated_ledger"].as_object();
|
boost::json::object& validated = info[JS(validated_ledger)].as_object();
|
||||||
|
|
||||||
validated["age"] = age;
|
validated[JS(age)] = age;
|
||||||
validated["hash"] = ripple::strHex(lgrInfo->hash);
|
validated[JS(hash)] = ripple::strHex(lgrInfo->hash);
|
||||||
validated["seq"] = lgrInfo->seq;
|
validated[JS(seq)] = lgrInfo->seq;
|
||||||
validated["base_fee_xrp"] = fees->base.decimalXRP();
|
validated[JS(base_fee_xrp)] = fees->base.decimalXRP();
|
||||||
validated["reserve_base_xrp"] = fees->reserve.decimalXRP();
|
validated[JS(reserve_base_xrp)] = fees->reserve.decimalXRP();
|
||||||
validated["reserve_inc_xrp"] = fees->increment.decimalXRP();
|
validated[JS(reserve_inc_xrp)] = fees->increment.decimalXRP();
|
||||||
|
|
||||||
response["cache"] = boost::json::object{};
|
response["cache"] = boost::json::object{};
|
||||||
auto& cache = response["cache"].as_object();
|
auto& cache = response["cache"].as_object();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ static std::unordered_set<std::string> validCommonStreams{
|
|||||||
Status
|
Status
|
||||||
validateStreams(boost::json::object const& request)
|
validateStreams(boost::json::object const& request)
|
||||||
{
|
{
|
||||||
boost::json::array const& streams = request.at("streams").as_array();
|
boost::json::array const& streams = request.at(JS(streams)).as_array();
|
||||||
|
|
||||||
for (auto const& stream : streams)
|
for (auto const& stream : streams)
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ subscribeToStreams(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& streams = request.at("streams").as_array();
|
boost::json::array const& streams = request.at(JS(streams)).as_array();
|
||||||
|
|
||||||
boost::json::object response;
|
boost::json::object response;
|
||||||
for (auto const& stream : streams)
|
for (auto const& stream : streams)
|
||||||
@@ -69,7 +69,7 @@ unsubscribeToStreams(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& streams = request.at("streams").as_array();
|
boost::json::array const& streams = request.at(JS(streams)).as_array();
|
||||||
|
|
||||||
for (auto const& stream : streams)
|
for (auto const& stream : streams)
|
||||||
{
|
{
|
||||||
@@ -114,7 +114,7 @@ subscribeToAccounts(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts = request.at("accounts").as_array();
|
boost::json::array const& accounts = request.at(JS(accounts)).as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -138,7 +138,7 @@ unsubscribeToAccounts(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts = request.at("accounts").as_array();
|
boost::json::array const& accounts = request.at(JS(accounts)).as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -163,7 +163,7 @@ subscribeToAccountsProposed(
|
|||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts =
|
boost::json::array const& accounts =
|
||||||
request.at("accounts_proposed").as_array();
|
request.at(JS(accounts_proposed)).as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -188,7 +188,7 @@ unsubscribeToAccountsProposed(
|
|||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts =
|
boost::json::array const& accounts =
|
||||||
request.at("accounts_proposed").as_array();
|
request.at(JS(accounts_proposed)).as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -212,68 +212,57 @@ validateAndGetBooks(
|
|||||||
boost::json::object const& request,
|
boost::json::object const& request,
|
||||||
std::shared_ptr<Backend::BackendInterface const> const& backend)
|
std::shared_ptr<Backend::BackendInterface const> const& backend)
|
||||||
{
|
{
|
||||||
if (!request.at("books").is_array())
|
if (!request.at(JS(books)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "booksNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "booksNotArray"};
|
||||||
boost::json::array const& books = request.at("books").as_array();
|
boost::json::array const& books = request.at(JS(books)).as_array();
|
||||||
|
|
||||||
std::vector<ripple::Book> booksToSub;
|
std::vector<ripple::Book> booksToSub;
|
||||||
std::optional<Backend::LedgerRange> rng;
|
std::optional<Backend::LedgerRange> rng;
|
||||||
boost::json::array snapshot;
|
boost::json::array snapshot;
|
||||||
for (auto const& book : books)
|
for (auto const& book : books)
|
||||||
{
|
{
|
||||||
auto parsed = parseBook(book.as_object());
|
auto parsedBook = parseBook(book.as_object());
|
||||||
if (auto status = std::get_if<Status>(&parsed))
|
if (auto status = std::get_if<Status>(&parsedBook))
|
||||||
return *status;
|
return *status;
|
||||||
else
|
|
||||||
|
auto b = std::get<ripple::Book>(parsedBook);
|
||||||
|
booksToSub.push_back(b);
|
||||||
|
bool both = book.as_object().contains(JS(both));
|
||||||
|
if (both)
|
||||||
|
booksToSub.push_back(ripple::reversed(b));
|
||||||
|
|
||||||
|
if (book.as_object().contains(JS(snapshot)))
|
||||||
{
|
{
|
||||||
auto b = std::get<ripple::Book>(parsed);
|
if (!rng)
|
||||||
booksToSub.push_back(b);
|
rng = backend->fetchLedgerRange();
|
||||||
bool both = book.as_object().contains("both");
|
ripple::AccountID takerID = beast::zero;
|
||||||
|
if (book.as_object().contains(JS(taker)))
|
||||||
|
if (auto const status = getTaker(book.as_object(), takerID);
|
||||||
|
status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
auto getOrderBook = [&snapshot, &backend, &rng, &takerID](
|
||||||
|
auto book,
|
||||||
|
boost::asio::yield_context& yield) {
|
||||||
|
auto bookBase = getBookBase(book);
|
||||||
|
auto [offers, retMarker] = backend->fetchBookOffers(
|
||||||
|
bookBase, rng->maxSequence, 200, {}, yield);
|
||||||
|
|
||||||
|
auto orderBook = postProcessOrderBook(
|
||||||
|
offers, book, takerID, *backend, rng->maxSequence, yield);
|
||||||
|
std::copy(
|
||||||
|
orderBook.begin(),
|
||||||
|
orderBook.end(),
|
||||||
|
std::back_inserter(snapshot));
|
||||||
|
};
|
||||||
|
getOrderBook(b, yield);
|
||||||
if (both)
|
if (both)
|
||||||
booksToSub.push_back(ripple::reversed(b));
|
getOrderBook(ripple::reversed(b), yield);
|
||||||
|
|
||||||
if (book.as_object().contains("snapshot"))
|
|
||||||
{
|
|
||||||
if (!rng)
|
|
||||||
rng = backend->fetchLedgerRange();
|
|
||||||
ripple::AccountID takerID = beast::zero;
|
|
||||||
if (book.as_object().contains("taker"))
|
|
||||||
{
|
|
||||||
auto parsed = parseTaker(request.at("taker"));
|
|
||||||
if (auto status = std::get_if<Status>(&parsed))
|
|
||||||
return *status;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
takerID = std::get<ripple::AccountID>(parsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto getOrderBook = [&snapshot, &backend, &rng, &takerID](
|
|
||||||
auto book,
|
|
||||||
boost::asio::yield_context& yield) {
|
|
||||||
auto bookBase = getBookBase(book);
|
|
||||||
auto [offers, retCursor] = backend->fetchBookOffers(
|
|
||||||
bookBase, rng->maxSequence, 200, {}, yield);
|
|
||||||
|
|
||||||
auto orderBook = postProcessOrderBook(
|
|
||||||
offers,
|
|
||||||
book,
|
|
||||||
takerID,
|
|
||||||
*backend,
|
|
||||||
rng->maxSequence,
|
|
||||||
yield);
|
|
||||||
std::copy(
|
|
||||||
orderBook.begin(),
|
|
||||||
orderBook.end(),
|
|
||||||
std::back_inserter(snapshot));
|
|
||||||
};
|
|
||||||
getOrderBook(b, yield);
|
|
||||||
if (both)
|
|
||||||
getOrderBook(ripple::reversed(b), yield);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::make_pair(booksToSub, snapshot);
|
return std::make_pair(booksToSub, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
subscribeToBooks(
|
subscribeToBooks(
|
||||||
std::vector<ripple::Book> const& books,
|
std::vector<ripple::Book> const& books,
|
||||||
@@ -285,14 +274,15 @@ subscribeToBooks(
|
|||||||
manager.subBook(book, session);
|
manager.subBook(book, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doSubscribe(Context const& context)
|
doSubscribe(Context const& context)
|
||||||
{
|
{
|
||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
|
|
||||||
if (request.contains("streams"))
|
if (request.contains(JS(streams)))
|
||||||
{
|
{
|
||||||
if (!request.at("streams").is_array())
|
if (!request.at(JS(streams)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
||||||
|
|
||||||
auto status = validateStreams(request);
|
auto status = validateStreams(request);
|
||||||
@@ -301,25 +291,25 @@ doSubscribe(Context const& context)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains(JS(accounts)))
|
||||||
{
|
{
|
||||||
if (!request.at("accounts").is_array())
|
if (!request.at(JS(accounts)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts = request.at("accounts").as_array();
|
boost::json::array accounts = request.at(JS(accounts)).as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains(JS(accounts_proposed)))
|
||||||
{
|
{
|
||||||
if (!request.at("accounts_proposed").is_array())
|
if (!request.at(JS(accounts_proposed)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts =
|
boost::json::array accounts =
|
||||||
request.at("accounts_proposed").as_array();
|
request.at(JS(accounts_proposed)).as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
@@ -327,7 +317,7 @@ doSubscribe(Context const& context)
|
|||||||
}
|
}
|
||||||
std::vector<ripple::Book> books;
|
std::vector<ripple::Book> books;
|
||||||
boost::json::array snapshot;
|
boost::json::array snapshot;
|
||||||
if (request.contains("books"))
|
if (request.contains(JS(books)))
|
||||||
{
|
{
|
||||||
auto parsed =
|
auto parsed =
|
||||||
validateAndGetBooks(context.yield, request, context.backend);
|
validateAndGetBooks(context.yield, request, context.backend);
|
||||||
@@ -341,22 +331,22 @@ doSubscribe(Context const& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::json::object response;
|
boost::json::object response;
|
||||||
if (request.contains("streams"))
|
if (request.contains(JS(streams)))
|
||||||
response = subscribeToStreams(
|
response = subscribeToStreams(
|
||||||
context.yield, request, context.session, *context.subscriptions);
|
context.yield, request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains(JS(accounts)))
|
||||||
subscribeToAccounts(request, context.session, *context.subscriptions);
|
subscribeToAccounts(request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains(JS(accounts_proposed)))
|
||||||
subscribeToAccountsProposed(
|
subscribeToAccountsProposed(
|
||||||
request, context.session, *context.subscriptions);
|
request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("books"))
|
if (request.contains(JS(books)))
|
||||||
subscribeToBooks(books, context.session, *context.subscriptions);
|
subscribeToBooks(books, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (snapshot.size())
|
if (snapshot.size())
|
||||||
response["offers"] = snapshot;
|
response[JS(offers)] = snapshot;
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,9 +355,9 @@ doUnsubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
|
|
||||||
if (request.contains("streams"))
|
if (request.contains(JS(streams)))
|
||||||
{
|
{
|
||||||
if (!request.at("streams").is_array())
|
if (!request.at(JS(streams)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
||||||
|
|
||||||
auto status = validateStreams(request);
|
auto status = validateStreams(request);
|
||||||
@@ -376,38 +366,38 @@ doUnsubscribe(Context const& context)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains(JS(accounts)))
|
||||||
{
|
{
|
||||||
if (!request.at("accounts").is_array())
|
if (!request.at(JS(accounts)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts = request.at("accounts").as_array();
|
boost::json::array accounts = request.at(JS(accounts)).as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains(JS(accounts_proposed)))
|
||||||
{
|
{
|
||||||
if (!request.at("accounts_proposed").is_array())
|
if (!request.at(JS(accounts_proposed)).is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts =
|
boost::json::array accounts =
|
||||||
request.at("accounts_proposed").as_array();
|
request.at(JS(accounts_proposed)).as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.contains("streams"))
|
if (request.contains(JS(streams)))
|
||||||
unsubscribeToStreams(request, context.session, *context.subscriptions);
|
unsubscribeToStreams(request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains(JS(accounts)))
|
||||||
unsubscribeToAccounts(request, context.session, *context.subscriptions);
|
unsubscribeToAccounts(request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains(JS(accounts_proposed)))
|
||||||
unsubscribeToAccountsProposed(
|
unsubscribeToAccountsProposed(
|
||||||
request, context.session, *context.subscriptions);
|
request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ doTransactionEntry(Context const& context)
|
|||||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||||
|
|
||||||
ripple::uint256 hash;
|
ripple::uint256 hash;
|
||||||
if (!hash.parseHex(getRequiredString(context.params, "tx_hash")))
|
if (!hash.parseHex(getRequiredString(context.params, JS(tx_hash))))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"};
|
||||||
|
|
||||||
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
||||||
@@ -33,10 +33,10 @@ doTransactionEntry(Context const& context)
|
|||||||
"Transaction not found."};
|
"Transaction not found."};
|
||||||
|
|
||||||
auto [txn, meta] = toExpandedJson(*dbResponse);
|
auto [txn, meta] = toExpandedJson(*dbResponse);
|
||||||
response["tx_json"] = std::move(txn);
|
response[JS(tx_json)] = std::move(txn);
|
||||||
response["metadata"] = std::move(meta);
|
response[JS(metadata)] = std::move(meta);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response[JS(ledger_index)] = lgrInfo.seq;
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,23 +14,23 @@ doTx(Context const& context)
|
|||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
boost::json::object response = {};
|
boost::json::object response = {};
|
||||||
|
|
||||||
if (!request.contains("transaction"))
|
if (!request.contains(JS(transaction)))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "specifyTransaction"};
|
return Status{Error::rpcINVALID_PARAMS, "specifyTransaction"};
|
||||||
|
|
||||||
if (!request.at("transaction").is_string())
|
if (!request.at(JS(transaction)).is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "transactionNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "transactionNotString"};
|
||||||
|
|
||||||
ripple::uint256 hash;
|
ripple::uint256 hash;
|
||||||
if (!hash.parseHex(request.at("transaction").as_string().c_str()))
|
if (!hash.parseHex(request.at(JS(transaction)).as_string().c_str()))
|
||||||
return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"};
|
return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"};
|
||||||
|
|
||||||
bool binary = false;
|
bool binary = false;
|
||||||
if (request.contains("binary"))
|
if (request.contains(JS(binary)))
|
||||||
{
|
{
|
||||||
if (!request.at("binary").is_bool())
|
if (!request.at(JS(binary)).is_bool())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
||||||
|
|
||||||
binary = request.at("binary").as_bool();
|
binary = request.at(JS(binary)).as_bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto range = context.backend->fetchLedgerRange();
|
auto range = context.backend->fetchLedgerRange();
|
||||||
@@ -45,16 +45,16 @@ doTx(Context const& context)
|
|||||||
{
|
{
|
||||||
auto [txn, meta] = toExpandedJson(*dbResponse);
|
auto [txn, meta] = toExpandedJson(*dbResponse);
|
||||||
response = txn;
|
response = txn;
|
||||||
response["meta"] = meta;
|
response[JS(meta)] = meta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response["tx"] = ripple::strHex(dbResponse->transaction);
|
response[JS(tx)] = ripple::strHex(dbResponse->transaction);
|
||||||
response["meta"] = ripple::strHex(dbResponse->metadata);
|
response[JS(meta)] = ripple::strHex(dbResponse->metadata);
|
||||||
response["hash"] = std::move(request.at("transaction").as_string());
|
response[JS(hash)] = std::move(request.at(JS(transaction)).as_string());
|
||||||
}
|
}
|
||||||
response["date"] = dbResponse->date;
|
response[JS(date)] = dbResponse->date;
|
||||||
response["ledger_index"] = dbResponse->ledgerSequence;
|
response[JS(ledger_index)] = dbResponse->ledgerSequence;
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,7 +253,6 @@ SubscriptionManager::pubTransaction(
|
|||||||
std::string pubMsg{boost::json::serialize(pubObj)};
|
std::string pubMsg{boost::json::serialize(pubObj)};
|
||||||
txSubscribers_.publish(pubMsg);
|
txSubscribers_.publish(pubMsg);
|
||||||
|
|
||||||
auto journal = ripple::debugLog();
|
|
||||||
auto accounts = meta->getAffectedAccounts();
|
auto accounts = meta->getAffectedAccounts();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
|
|||||||
Reference in New Issue
Block a user