fix: Return specific and consistent errors from vault_info (#8015)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Timur Yalymov
2026-08-20 16:05:18 +00:00
committed by GitHub
parent 422e5245e4
commit cc76708563
3 changed files with 135 additions and 38 deletions

View File

@@ -9,11 +9,13 @@
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/json/json_forwards.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/to_string.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
@@ -122,6 +124,22 @@ private:
}
};
// An error response must carry a registered token together with the matching code and
// message, so that clients dispatching on either of them reach the same conclusion.
auto const checkError = [this](
json::Value const& result,
std::string const& token,
ErrorCodeI const code,
std::string const& message) {
BEAST_EXPECT(result[jss::error].asString() == token);
BEAST_EXPECT(result[jss::error_code].asInt() == code);
BEAST_EXPECT(result[jss::error_message].asString() == message);
};
std::string const badSeqMessage = "Invalid field 'seq', not a positive 32-bit integer.";
std::string const badFieldsMessage =
"Must specify either 'vault_id' or both 'owner' and 'seq'.";
{
testcase("RPC ledger_entry selected by key");
json::Value jvParams;
@@ -276,16 +294,57 @@ private:
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::vault_id] = "foobar";
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(
jv[jss::result],
"invalidParams",
RpcInvalidParams,
"Invalid field 'vault_id', not hex string.");
}
{
testcase("RPC vault_info json invalid index");
testcase("RPC vault_info json numeric vault_id");
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::vault_id] = 0;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(
jv[jss::result],
"invalidParams",
RpcInvalidParams,
"Invalid field 'vault_id', not hex string.");
}
{
testcase("RPC vault_info json object vault_id");
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::vault_id] = json::Value(json::ValueType::Object);
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
checkError(
jv[jss::result],
"invalidParams",
RpcInvalidParams,
"Invalid field 'vault_id', not hex string.");
}
{
// An all-zero key is a well-formed request for a vault that cannot exist, not a
// malformed one. parseHex accepts both the padded form and the short "0".
testcase("RPC vault_info json all zero vault_id");
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::vault_id] = strHex(uint256(beast::kZero));
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
checkError(jv[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
}
{
testcase("RPC vault_info json short zero vault_id");
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::vault_id] = "0";
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
checkError(jv[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
}
{
@@ -308,7 +367,7 @@ private:
jvParams[jss::owner] = owner.human();
jvParams[jss::seq] = "foobar";
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badSeqMessage);
}
{
@@ -318,7 +377,7 @@ private:
jvParams[jss::owner] = owner.human();
jvParams[jss::seq] = 0;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badSeqMessage);
}
{
@@ -328,7 +387,7 @@ private:
jvParams[jss::owner] = owner.human();
jvParams[jss::seq] = -1;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badSeqMessage);
}
{
@@ -338,7 +397,7 @@ private:
jvParams[jss::owner] = owner.human();
jvParams[jss::seq] = 1e20;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badSeqMessage);
}
{
@@ -348,7 +407,7 @@ private:
jvParams[jss::owner] = owner.human();
jvParams[jss::seq] = true;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badSeqMessage);
}
{
@@ -358,7 +417,25 @@ private:
jvParams[jss::owner] = "foobar";
jvParams[jss::seq] = sequence;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(
jv[jss::result],
"actMalformed",
RpcActMalformed,
"Invalid field 'owner', not AccountID.");
}
{
testcase("RPC vault_info json array owner");
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::owner] = json::Value(json::ValueType::Array);
jvParams[jss::seq] = sequence;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
checkError(
jv[jss::result],
"actMalformed",
RpcActMalformed,
"Invalid field 'owner', not AccountID.");
}
{
@@ -367,7 +444,7 @@ private:
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::owner] = owner.human();
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -376,7 +453,7 @@ private:
jvParams[jss::ledger_index] = jss::validated;
jvParams[jss::seq] = sequence;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -386,7 +463,7 @@ private:
jvParams[jss::vault_id] = strHex(keylet.key);
jvParams[jss::seq] = sequence;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -396,7 +473,7 @@ private:
jvParams[jss::vault_id] = strHex(keylet.key);
jvParams[jss::owner] = owner.human();
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -409,7 +486,7 @@ private:
jvParams[jss::seq] = sequence;
jvParams[jss::owner] = owner.human();
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -417,7 +494,7 @@ private:
json::Value jvParams;
jvParams[jss::ledger_index] = jss::validated;
auto jv = env.rpc("json", "vault_info", to_string(jvParams));
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "invalidParams", RpcInvalidParams, badFieldsMessage);
}
{
@@ -427,15 +504,15 @@ private:
}
{
testcase("RPC vault_info command line invalid index");
testcase("RPC vault_info command line zero index");
json::Value jv = env.rpc("vault_info", "0", "validated");
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
checkError(jv[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
}
{
testcase("RPC vault_info command line invalid index");
testcase("RPC vault_info command line unknown index");
json::Value jv = env.rpc("vault_info", strHex(uint256(42)), "validated");
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "entryNotFound");
checkError(jv[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
}
{