Implement account ownership check and fix paging (#383)

Fixes #222
This commit is contained in:
Alex Kremer
2022-11-18 18:51:18 +01:00
committed by GitHub
parent b13c44eb12
commit 041aba9a0b
7 changed files with 48 additions and 63 deletions

View File

@@ -81,6 +81,34 @@ struct Status
return *err != RippledError::rpcSUCCESS;
return true;
}
/**
* @brief Returns true if the Status contains the desired @ref RippledError
*
* @param other The RippledError to match
* @return bool true if status matches given error; false otherwise
*/
bool
operator==(RippledError other) const
{
if (auto err = std::get_if<RippledError>(&code))
return *err == other;
return false;
}
/**
* @brief Returns true if the Status contains the desired @ref ClioError
*
* @param other The RippledError to match
* @return bool true if status matches given error; false otherwise
*/
bool
operator==(ClioError other) const
{
if (auto err = std::get_if<ClioError>(&code))
return *err == other;
return false;
}
};
/**

View File

@@ -352,7 +352,7 @@ buildResponse(Context const& ctx)
if (auto object = get_if<boost::json::object>(&v);
object && not shouldSuppressValidatedFlag(ctx))
{
(*object)["validated"] = true;
(*object)[JS(validated)] = true;
}
return v;

View File

@@ -73,13 +73,13 @@ struct AccountCursor
std::uint32_t hint;
std::string
toString()
toString() const
{
return ripple::strHex(index) + "," + std::to_string(hint);
}
bool
isNonZero()
isNonZero() const
{
return index.isNonZero() || hint != 0;
}

View File

@@ -81,35 +81,11 @@ getRequiredUInt(boost::json::object const& request, std::string const& field)
throw InvalidParamsError("Missing field " + field);
}
bool
isOwnedByAccount(ripple::SLE const& sle, ripple::AccountID const& accountID)
{
if (sle.getType() == ripple::ltRIPPLE_STATE)
{
return (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() ==
accountID) ||
(sle.getFieldAmount(ripple::sfHighLimit).getIssuer() == accountID);
}
else if (sle.isFieldPresent(ripple::sfAccount))
{
return sle.getAccountID(ripple::sfAccount) == accountID;
}
else if (sle.getType() == ripple::ltSIGNER_LIST)
{
ripple::Keylet const accountSignerList =
ripple::keylet::signers(accountID);
return sle.key() == accountSignerList.key;
}
return false;
}
std::optional<AccountCursor>
parseAccountCursor(
BackendInterface const& backend,
std::uint32_t seq,
std::optional<std::string> jsonCursor,
ripple::AccountID const& accountID,
boost::asio::yield_context& yield)
{
ripple::uint256 cursorIndex = beast::zero;
@@ -140,19 +116,6 @@ parseAccountCursor(
return {};
}
// We then must check if the object pointed to by the marker is actually
// owned by the account in the request.
auto const ownedNode = backend.fetchLedgerObject(cursorIndex, seq, yield);
if (!ownedNode)
return {};
ripple::SerialIter it{ownedNode->data(), ownedNode->size()};
ripple::SLE sle{it, cursorIndex};
if (!isOwnedByAccount(sle, accountID))
return {};
return AccountCursor({cursorIndex, startHint});
}
@@ -663,13 +626,11 @@ traverseOwnedNodes(
ripple::keylet::account(accountID).key, sequence, yield))
return Status{RippledError::rpcACT_NOT_FOUND};
auto parsedCursor =
parseAccountCursor(backend, sequence, jsonCursor, accountID, yield);
if (!parsedCursor)
auto maybeCursor = parseAccountCursor(backend, sequence, jsonCursor, yield);
if (!maybeCursor)
return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor");
auto [hexCursor, startHint] = *parsedCursor;
auto [hexCursor, startHint] = *maybeCursor;
return traverseOwnedNodes(
backend,
@@ -715,22 +676,21 @@ traverseOwnedNodes(
auto hintDir =
backend.fetchLedgerObject(hintIndex.key, sequence, yield);
if (hintDir)
{
ripple::SerialIter it{hintDir->data(), hintDir->size()};
ripple::SLE sle{it, hintIndex.key};
if (!hintDir)
return Status(ripple::rpcINVALID_PARAMS, "Invalid marker");
for (auto const& key : sle.getFieldV256(ripple::sfIndexes))
{
if (key == hexMarker)
{
// We found the hint, we can start here
currentIndex = hintIndex;
break;
}
}
ripple::SerialIter it{hintDir->data(), hintDir->size()};
ripple::SLE sle{it, hintIndex.key};
if (auto const& indexes = sle.getFieldV256(ripple::sfIndexes);
std::find(std::begin(indexes), std::end(indexes), hexMarker) ==
std::end(indexes))
{
// result in empty dataset
return AccountCursor({beast::zero, 0});
}
currentIndex = hintIndex;
bool found = false;
for (;;)
{

View File

@@ -36,7 +36,6 @@ parseAccountCursor(
BackendInterface const& backend,
std::uint32_t seq,
std::optional<std::string> jsonCursor,
ripple::AccountID const& accountID,
boost::asio::yield_context& yield);
// TODO this function should probably be in a different file and namespace

View File

@@ -202,8 +202,7 @@ doAccountObjects(Context const& context)
if (auto status = std::get_if<RPC::Status>(&next))
return *status;
auto nextMarker = std::get<RPC::AccountCursor>(next);
auto const& nextMarker = std::get<RPC::AccountCursor>(next);
if (nextMarker.isNonZero())
response[JS(marker)] = nextMarker.toString();

View File

@@ -442,8 +442,7 @@ doUnsubscribe(Context const& context)
if (request.contains("books"))
unsubscribeToBooks(books, context.session, *context.subscriptions);
boost::json::object response = {{"status", "success"}};
return response;
return boost::json::object{};
}
} // namespace RPC