mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-27 15:15:52 +00:00
Fixes #2183. Please review and commit clang-tidy fixes. Co-authored-by: mathbunnyru <12270691+mathbunnyru@users.noreply.github.com>
292 lines
10 KiB
C++
292 lines
10 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of clio: https://github.com/XRPLF/clio
|
|
Copyright (c) 2023, the clio developers.
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include "rpc/handlers/AccountLines.hpp"
|
|
|
|
#include "rpc/Errors.hpp"
|
|
#include "rpc/JS.hpp"
|
|
#include "rpc/RPCHelpers.hpp"
|
|
#include "rpc/common/Types.hpp"
|
|
#include "util/Assert.hpp"
|
|
|
|
#include <boost/json/conversion.hpp>
|
|
#include <boost/json/object.hpp>
|
|
#include <boost/json/value.hpp>
|
|
#include <boost/json/value_to.hpp>
|
|
#include <xrpl/basics/strHex.h>
|
|
#include <xrpl/protocol/AccountID.h>
|
|
#include <xrpl/protocol/Indexes.h>
|
|
#include <xrpl/protocol/LedgerFormats.h>
|
|
#include <xrpl/protocol/LedgerHeader.h>
|
|
#include <xrpl/protocol/SField.h>
|
|
#include <xrpl/protocol/STAmount.h>
|
|
#include <xrpl/protocol/STLedgerEntry.h>
|
|
#include <xrpl/protocol/UintTypes.h>
|
|
#include <xrpl/protocol/jss.h>
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace rpc {
|
|
|
|
void
|
|
AccountLinesHandler::addLine(
|
|
std::vector<LineResponse>& lines,
|
|
ripple::SLE const& lineSle,
|
|
ripple::AccountID const& account,
|
|
std::optional<ripple::AccountID> const& peerAccount
|
|
)
|
|
{
|
|
auto const flags = lineSle.getFieldU32(ripple::sfFlags);
|
|
auto const lowLimit = lineSle.getFieldAmount(ripple::sfLowLimit);
|
|
auto const highLimit = lineSle.getFieldAmount(ripple::sfHighLimit);
|
|
auto const lowID = lowLimit.getIssuer();
|
|
auto const highID = highLimit.getIssuer();
|
|
auto const lowQualityIn = lineSle.getFieldU32(ripple::sfLowQualityIn);
|
|
auto const lowQualityOut = lineSle.getFieldU32(ripple::sfLowQualityOut);
|
|
auto const highQualityIn = lineSle.getFieldU32(ripple::sfHighQualityIn);
|
|
auto const highQualityOut = lineSle.getFieldU32(ripple::sfHighQualityOut);
|
|
auto balance = lineSle.getFieldAmount(ripple::sfBalance);
|
|
|
|
auto const viewLowest = (lowID == account);
|
|
auto const lineLimit = viewLowest ? lowLimit : highLimit;
|
|
auto const lineLimitPeer = not viewLowest ? lowLimit : highLimit;
|
|
auto const lineAccountIDPeer = not viewLowest ? lowID : highID;
|
|
auto const lineQualityIn = viewLowest ? lowQualityIn : highQualityIn;
|
|
auto const lineQualityOut = viewLowest ? lowQualityOut : highQualityOut;
|
|
|
|
if (peerAccount && peerAccount != lineAccountIDPeer)
|
|
return;
|
|
|
|
if (not viewLowest)
|
|
balance.negate();
|
|
|
|
bool const lineAuth = (flags & (viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth)) != 0u;
|
|
bool const lineAuthPeer = (flags & (not viewLowest ? ripple::lsfLowAuth : ripple::lsfHighAuth)) != 0u;
|
|
bool const lineNoRipple = (flags & (viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple)) != 0u;
|
|
bool const lineNoRipplePeer = (flags & (not viewLowest ? ripple::lsfLowNoRipple : ripple::lsfHighNoRipple)) != 0u;
|
|
bool const lineFreeze = (flags & (viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
|
|
bool const lineFreezePeer = (flags & (not viewLowest ? ripple::lsfLowFreeze : ripple::lsfHighFreeze)) != 0u;
|
|
bool const lineDeepFreeze = (flags & (viewLowest ? ripple::lsfLowDeepFreeze : ripple::lsfHighDeepFreeze)) != 0u;
|
|
bool const lineDeepFreezePeer =
|
|
(flags & (not viewLowest ? ripple::lsfLowDeepFreeze : ripple::lsfHighDeepFreeze)) != 0u;
|
|
|
|
ripple::STAmount const& saBalance = balance;
|
|
ripple::STAmount const& saLimit = lineLimit;
|
|
ripple::STAmount const& saLimitPeer = lineLimitPeer;
|
|
|
|
LineResponse line;
|
|
line.account = ripple::to_string(lineAccountIDPeer);
|
|
line.balance = saBalance.getText();
|
|
line.currency = ripple::to_string(saBalance.issue().currency);
|
|
line.limit = saLimit.getText();
|
|
line.limitPeer = saLimitPeer.getText();
|
|
line.qualityIn = lineQualityIn;
|
|
line.qualityOut = lineQualityOut;
|
|
|
|
if (lineNoRipple)
|
|
line.noRipple = true;
|
|
|
|
if (lineNoRipplePeer)
|
|
line.noRipplePeer = true;
|
|
|
|
if (lineAuth)
|
|
line.authorized = true;
|
|
|
|
if (lineAuthPeer)
|
|
line.peerAuthorized = true;
|
|
|
|
if (lineFreeze)
|
|
line.freeze = true;
|
|
|
|
if (lineFreezePeer)
|
|
line.freezePeer = true;
|
|
|
|
if (lineDeepFreeze)
|
|
line.deepFreeze = true;
|
|
|
|
if (lineDeepFreezePeer)
|
|
line.deepFreezePeer = true;
|
|
|
|
lines.push_back(line);
|
|
}
|
|
|
|
AccountLinesHandler::Result
|
|
AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ctx) const
|
|
{
|
|
auto const range = sharedPtrBackend_->fetchLedgerRange();
|
|
ASSERT(range.has_value(), "AccountLines' ledger range must be available");
|
|
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
|
|
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
|
|
);
|
|
|
|
if (!expectedLgrInfo.has_value())
|
|
return Error{expectedLgrInfo.error()};
|
|
|
|
auto const& lgrInfo = expectedLgrInfo.value();
|
|
auto const accountID = accountFromStringStrict(input.account);
|
|
auto const accountLedgerObject =
|
|
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);
|
|
|
|
if (not accountLedgerObject)
|
|
return Error{Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}};
|
|
|
|
auto const peerAccountID = input.peer ? accountFromStringStrict(*(input.peer)) : std::optional<ripple::AccountID>{};
|
|
|
|
Output response;
|
|
response.lines.reserve(input.limit);
|
|
|
|
auto const addToResponse = [&](ripple::SLE const sle) {
|
|
if (sle.getType() == ripple::ltRIPPLE_STATE) {
|
|
auto ignore = false;
|
|
if (input.ignoreDefault) {
|
|
if (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() == accountID) {
|
|
ignore = ((sle.getFieldU32(ripple::sfFlags) & ripple::lsfLowReserve) == 0u);
|
|
} else {
|
|
ignore = ((sle.getFieldU32(ripple::sfFlags) & ripple::lsfHighReserve) == 0u);
|
|
}
|
|
}
|
|
|
|
if (not ignore)
|
|
addLine(response.lines, sle, *accountID, peerAccountID);
|
|
}
|
|
};
|
|
|
|
auto const expectedNext = traverseOwnedNodes(
|
|
*sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse
|
|
);
|
|
|
|
if (!expectedNext.has_value())
|
|
return Error{expectedNext.error()};
|
|
|
|
auto const nextMarker = expectedNext.value();
|
|
|
|
response.account = input.account;
|
|
response.limit = input.limit; // not documented,
|
|
// https://github.com/XRPLF/xrpl-dev-portal/issues/1838
|
|
response.ledgerHash = ripple::strHex(lgrInfo.hash);
|
|
response.ledgerIndex = lgrInfo.seq;
|
|
|
|
if (nextMarker.isNonZero())
|
|
response.marker = nextMarker.toString();
|
|
|
|
return response;
|
|
}
|
|
|
|
AccountLinesHandler::Input
|
|
tag_invoke(boost::json::value_to_tag<AccountLinesHandler::Input>, boost::json::value const& jv)
|
|
{
|
|
auto input = AccountLinesHandler::Input{};
|
|
auto const& jsonObject = jv.as_object();
|
|
|
|
input.account = boost::json::value_to<std::string>(jv.at(JS(account)));
|
|
if (jsonObject.contains(JS(limit)))
|
|
input.limit = jv.at(JS(limit)).as_int64();
|
|
|
|
if (jsonObject.contains(JS(marker)))
|
|
input.marker = boost::json::value_to<std::string>(jv.at(JS(marker)));
|
|
|
|
if (jsonObject.contains(JS(ledger_hash)))
|
|
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
|
|
|
|
if (jsonObject.contains(JS(peer)))
|
|
input.peer = boost::json::value_to<std::string>(jv.at(JS(peer)));
|
|
|
|
if (jsonObject.contains(JS(ignore_default)))
|
|
input.ignoreDefault = jv.at(JS(ignore_default)).as_bool();
|
|
|
|
if (jsonObject.contains(JS(ledger_index))) {
|
|
if (!jsonObject.at(JS(ledger_index)).is_string()) {
|
|
input.ledgerIndex = jv.at(JS(ledger_index)).as_int64();
|
|
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
|
|
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
|
|
}
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
void
|
|
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountLinesHandler::Output const& output)
|
|
{
|
|
using boost::json::value_from;
|
|
|
|
auto obj = boost::json::object{
|
|
{JS(account), output.account},
|
|
{JS(ledger_hash), output.ledgerHash},
|
|
{JS(ledger_index), output.ledgerIndex},
|
|
{JS(validated), output.validated},
|
|
{JS(limit), output.limit},
|
|
{JS(lines), value_from(output.lines)},
|
|
};
|
|
|
|
if (output.marker)
|
|
obj[JS(marker)] = output.marker.value();
|
|
|
|
jv = std::move(obj);
|
|
}
|
|
|
|
void
|
|
tag_invoke(
|
|
boost::json::value_from_tag,
|
|
boost::json::value& jv,
|
|
[[maybe_unused]] AccountLinesHandler::LineResponse const& line
|
|
)
|
|
{
|
|
auto obj = boost::json::object{
|
|
{JS(account), line.account},
|
|
{JS(balance), line.balance},
|
|
{JS(currency), line.currency},
|
|
{JS(limit), line.limit},
|
|
{JS(limit_peer), line.limitPeer},
|
|
{JS(quality_in), line.qualityIn},
|
|
{JS(quality_out), line.qualityOut},
|
|
};
|
|
|
|
if (line.noRipple)
|
|
obj[JS(no_ripple)] = *(line.noRipple);
|
|
|
|
if (line.noRipplePeer)
|
|
obj[JS(no_ripple_peer)] = *(line.noRipplePeer);
|
|
|
|
if (line.authorized)
|
|
obj[JS(authorized)] = *(line.authorized);
|
|
|
|
if (line.peerAuthorized)
|
|
obj[JS(peer_authorized)] = *(line.peerAuthorized);
|
|
|
|
if (line.freeze)
|
|
obj[JS(freeze)] = *(line.freeze);
|
|
|
|
if (line.freezePeer)
|
|
obj[JS(freeze_peer)] = *(line.freezePeer);
|
|
|
|
if (line.deepFreeze)
|
|
obj[JS(deep_freeze)] = *(line.deepFreeze);
|
|
|
|
if (line.deepFreezePeer)
|
|
obj[JS(deep_freeze_peer)] = *(line.deepFreezePeer);
|
|
|
|
jv = std::move(obj);
|
|
}
|
|
|
|
} // namespace rpc
|