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 *err != RippledError::rpcSUCCESS;
return true; 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); if (auto object = get_if<boost::json::object>(&v);
object && not shouldSuppressValidatedFlag(ctx)) object && not shouldSuppressValidatedFlag(ctx))
{ {
(*object)["validated"] = true; (*object)[JS(validated)] = true;
} }
return v; return v;

View File

@@ -73,13 +73,13 @@ struct AccountCursor
std::uint32_t hint; std::uint32_t hint;
std::string std::string
toString() toString() const
{ {
return ripple::strHex(index) + "," + std::to_string(hint); return ripple::strHex(index) + "," + std::to_string(hint);
} }
bool bool
isNonZero() isNonZero() const
{ {
return index.isNonZero() || hint != 0; 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); 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> std::optional<AccountCursor>
parseAccountCursor( parseAccountCursor(
BackendInterface const& backend, BackendInterface const& backend,
std::uint32_t seq, std::uint32_t seq,
std::optional<std::string> jsonCursor, std::optional<std::string> jsonCursor,
ripple::AccountID const& accountID,
boost::asio::yield_context& yield) boost::asio::yield_context& yield)
{ {
ripple::uint256 cursorIndex = beast::zero; ripple::uint256 cursorIndex = beast::zero;
@@ -140,19 +116,6 @@ parseAccountCursor(
return {}; 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}); return AccountCursor({cursorIndex, startHint});
} }
@@ -663,13 +626,11 @@ traverseOwnedNodes(
ripple::keylet::account(accountID).key, sequence, yield)) ripple::keylet::account(accountID).key, sequence, yield))
return Status{RippledError::rpcACT_NOT_FOUND}; return Status{RippledError::rpcACT_NOT_FOUND};
auto parsedCursor = auto maybeCursor = parseAccountCursor(backend, sequence, jsonCursor, yield);
parseAccountCursor(backend, sequence, jsonCursor, accountID, yield); if (!maybeCursor)
if (!parsedCursor)
return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor"); return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor");
auto [hexCursor, startHint] = *parsedCursor; auto [hexCursor, startHint] = *maybeCursor;
return traverseOwnedNodes( return traverseOwnedNodes(
backend, backend,
@@ -715,22 +676,21 @@ traverseOwnedNodes(
auto hintDir = auto hintDir =
backend.fetchLedgerObject(hintIndex.key, sequence, yield); backend.fetchLedgerObject(hintIndex.key, sequence, yield);
if (hintDir) if (!hintDir)
{ return Status(ripple::rpcINVALID_PARAMS, "Invalid marker");
ripple::SerialIter it{hintDir->data(), hintDir->size()}; ripple::SerialIter it{hintDir->data(), hintDir->size()};
ripple::SLE sle{it, hintIndex.key}; ripple::SLE sle{it, hintIndex.key};
for (auto const& key : sle.getFieldV256(ripple::sfIndexes)) if (auto const& indexes = sle.getFieldV256(ripple::sfIndexes);
std::find(std::begin(indexes), std::end(indexes), hexMarker) ==
std::end(indexes))
{ {
if (key == hexMarker) // result in empty dataset
{ return AccountCursor({beast::zero, 0});
// We found the hint, we can start here
currentIndex = hintIndex;
break;
}
}
} }
currentIndex = hintIndex;
bool found = false; bool found = false;
for (;;) for (;;)
{ {

View File

@@ -36,7 +36,6 @@ parseAccountCursor(
BackendInterface const& backend, BackendInterface const& backend,
std::uint32_t seq, std::uint32_t seq,
std::optional<std::string> jsonCursor, std::optional<std::string> jsonCursor,
ripple::AccountID const& accountID,
boost::asio::yield_context& yield); boost::asio::yield_context& yield);
// TODO this function should probably be in a different file and namespace // 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)) if (auto status = std::get_if<RPC::Status>(&next))
return *status; return *status;
auto nextMarker = std::get<RPC::AccountCursor>(next); auto const& nextMarker = std::get<RPC::AccountCursor>(next);
if (nextMarker.isNonZero()) if (nextMarker.isNonZero())
response[JS(marker)] = nextMarker.toString(); response[JS(marker)] = nextMarker.toString();

View File

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