mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
@@ -91,6 +91,7 @@ getErrorInfo(ClioError code)
|
|||||||
{ClioError::rpcINVALID_HOT_WALLET, "invalidHotWallet", "Invalid hot wallet."},
|
{ClioError::rpcINVALID_HOT_WALLET, "invalidHotWallet", "Invalid hot wallet."},
|
||||||
{ClioError::rpcUNKNOWN_OPTION, "unknownOption", "Unknown option."},
|
{ClioError::rpcUNKNOWN_OPTION, "unknownOption", "Unknown option."},
|
||||||
{ClioError::rpcFIELD_NOT_FOUND_TRANSACTION, "fieldNotFoundTransaction", "Missing field."},
|
{ClioError::rpcFIELD_NOT_FOUND_TRANSACTION, "fieldNotFoundTransaction", "Missing field."},
|
||||||
|
{ClioError::rpcMALFORMED_ORACLE_DOCUMENT_ID, "malformedDocumentID", "Malformed oracle_document_id."},
|
||||||
// special system errors
|
// special system errors
|
||||||
{ClioError::rpcINVALID_API_VERSION, JS(invalid_API_version), "Invalid API version."},
|
{ClioError::rpcINVALID_API_VERSION, JS(invalid_API_version), "Invalid API version."},
|
||||||
{ClioError::rpcCOMMAND_IS_MISSING, JS(missingCommand), "Method is not specified or is not a string."},
|
{ClioError::rpcCOMMAND_IS_MISSING, JS(missingCommand), "Method is not specified or is not a string."},
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ enum class ClioError {
|
|||||||
rpcINVALID_HOT_WALLET = 5004,
|
rpcINVALID_HOT_WALLET = 5004,
|
||||||
rpcUNKNOWN_OPTION = 5005,
|
rpcUNKNOWN_OPTION = 5005,
|
||||||
rpcFIELD_NOT_FOUND_TRANSACTION = 5006,
|
rpcFIELD_NOT_FOUND_TRANSACTION = 5006,
|
||||||
|
rpcMALFORMED_ORACLE_DOCUMENT_ID = 5007,
|
||||||
|
|
||||||
// special system errors start with 6000
|
// special system errors start with 6000
|
||||||
rpcINVALID_API_VERSION = 6000,
|
rpcINVALID_API_VERSION = 6000,
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
key = ripple::keylet::xChainCreateAccountClaimID(input.bridge->value(), input.createAccountClaimId.value())
|
key = ripple::keylet::xChainCreateAccountClaimID(input.bridge->value(), input.createAccountClaimId.value())
|
||||||
.key;
|
.key;
|
||||||
}
|
}
|
||||||
|
} else if (input.oracleNode) {
|
||||||
|
key = input.oracleNode.value();
|
||||||
} else {
|
} else {
|
||||||
// Must specify 1 of the following fields to indicate what type
|
// Must specify 1 of the following fields to indicate what type
|
||||||
if (ctx.apiVersion == 1)
|
if (ctx.apiVersion == 1)
|
||||||
@@ -257,6 +259,7 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
{JS(amm), ripple::ltAMM},
|
{JS(amm), ripple::ltAMM},
|
||||||
{JS(xchain_owned_create_account_claim_id), ripple::ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
|
{JS(xchain_owned_create_account_claim_id), ripple::ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
|
||||||
{JS(xchain_owned_claim_id), ripple::ltXCHAIN_OWNED_CLAIM_ID},
|
{JS(xchain_owned_claim_id), ripple::ltXCHAIN_OWNED_CLAIM_ID},
|
||||||
|
{JS(oracle), ripple::ltORACLE},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
||||||
@@ -274,6 +277,14 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
return ripple::STXChainBridge{lockingDoor, lockingIssue, issuingDoor, issuingIssue};
|
return ripple::STXChainBridge{lockingDoor, lockingIssue, issuingDoor, issuingIssue};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto const parseOracleFromJson = [](boost::json::value const& json) {
|
||||||
|
auto const account =
|
||||||
|
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(json.at(JS(account))));
|
||||||
|
auto const documentId = boost::json::value_to<uint32_t>(json.at(JS(oracle_document_id)));
|
||||||
|
|
||||||
|
return ripple::keylet::oracle(*account, documentId).key;
|
||||||
|
};
|
||||||
|
|
||||||
auto const indexFieldType =
|
auto const indexFieldType =
|
||||||
std::find_if(indexFieldTypeMap.begin(), indexFieldTypeMap.end(), [&jsonObject](auto const& pair) {
|
std::find_if(indexFieldTypeMap.begin(), indexFieldTypeMap.end(), [&jsonObject](auto const& pair) {
|
||||||
auto const& [field, _] = pair;
|
auto const& [field, _] = pair;
|
||||||
@@ -318,6 +329,8 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
input.createAccountClaimId = boost::json::value_to<std::int32_t>(
|
input.createAccountClaimId = boost::json::value_to<std::int32_t>(
|
||||||
jv.at(JS(xchain_owned_create_account_claim_id)).at(JS(xchain_owned_create_account_claim_id))
|
jv.at(JS(xchain_owned_create_account_claim_id)).at(JS(xchain_owned_create_account_claim_id))
|
||||||
);
|
);
|
||||||
|
} else if (jsonObject.contains(JS(oracle))) {
|
||||||
|
input.oracleNode = parseOracleFromJson(jv.at(JS(oracle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return input;
|
return input;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <boost/json/value.hpp>
|
#include <boost/json/value.hpp>
|
||||||
#include <boost/json/value_to.hpp>
|
#include <boost/json/value_to.hpp>
|
||||||
#include <ripple/basics/base_uint.h>
|
#include <ripple/basics/base_uint.h>
|
||||||
|
#include <ripple/beast/core/LexicalCast.h>
|
||||||
#include <ripple/protocol/AccountID.h>
|
#include <ripple/protocol/AccountID.h>
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
#include <ripple/protocol/Issue.h>
|
#include <ripple/protocol/Issue.h>
|
||||||
@@ -98,6 +99,7 @@ public:
|
|||||||
std::optional<std::string> bridgeAccount;
|
std::optional<std::string> bridgeAccount;
|
||||||
std::optional<uint32_t> chainClaimId;
|
std::optional<uint32_t> chainClaimId;
|
||||||
std::optional<uint32_t> createAccountClaimId;
|
std::optional<uint32_t> createAccountClaimId;
|
||||||
|
std::optional<ripple::uint256> oracleNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Result = HandlerReturnType<Output>;
|
using Result = HandlerReturnType<Output>;
|
||||||
@@ -278,6 +280,24 @@ public:
|
|||||||
}},
|
}},
|
||||||
Status(ClioError::rpcMALFORMED_REQUEST)
|
Status(ClioError::rpcMALFORMED_REQUEST)
|
||||||
}},
|
}},
|
||||||
|
{JS(oracle),
|
||||||
|
meta::WithCustomError{
|
||||||
|
validation::Type<std::string, boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)
|
||||||
|
},
|
||||||
|
meta::IfType<std::string>{
|
||||||
|
meta::WithCustomError{malformedRequestHexStringValidator, Status(ClioError::rpcMALFORMED_ADDRESS)}
|
||||||
|
},
|
||||||
|
meta::IfType<boost::json::object>{meta::Section{
|
||||||
|
{JS(account),
|
||||||
|
meta::WithCustomError{validation::Required{}, Status(ClioError::rpcMALFORMED_REQUEST)},
|
||||||
|
meta::WithCustomError{validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_ADDRESS)}},
|
||||||
|
// note: Unlike `rippled`, Clio only supports UInt as input, no string, no `null`, etc.:
|
||||||
|
{JS(oracle_document_id),
|
||||||
|
meta::WithCustomError{validation::Required{}, Status(ClioError::rpcMALFORMED_REQUEST)},
|
||||||
|
meta::WithCustomError{
|
||||||
|
validation::Type<uint32_t>{}, Status(ClioError::rpcMALFORMED_ORACLE_DOCUMENT_ID)
|
||||||
|
}},
|
||||||
|
}}}
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ public:
|
|||||||
case rpc::ClioError::rpcMALFORMED_ADDRESS:
|
case rpc::ClioError::rpcMALFORMED_ADDRESS:
|
||||||
case rpc::ClioError::rpcINVALID_HOT_WALLET:
|
case rpc::ClioError::rpcINVALID_HOT_WALLET:
|
||||||
case rpc::ClioError::rpcFIELD_NOT_FOUND_TRANSACTION:
|
case rpc::ClioError::rpcFIELD_NOT_FOUND_TRANSACTION:
|
||||||
|
case rpc::ClioError::rpcMALFORMED_ORACLE_DOCUMENT_ID:
|
||||||
ASSERT(
|
ASSERT(
|
||||||
false, "Unknown rpc error code {}", static_cast<int>(*clioCode)
|
false, "Unknown rpc error code {}", static_cast<int>(*clioCode)
|
||||||
); // this should never happen
|
); // this should never happen
|
||||||
|
|||||||
@@ -1269,7 +1269,7 @@ generateTestValuesForParametersTest()
|
|||||||
R"({{
|
R"({{
|
||||||
"bridge_account": "{}",
|
"bridge_account": "{}",
|
||||||
"bridge": "invalid"
|
"bridge": "invalid"
|
||||||
}})",
|
}})",
|
||||||
ACCOUNT
|
ACCOUNT
|
||||||
),
|
),
|
||||||
"malformedRequest",
|
"malformedRequest",
|
||||||
@@ -1278,8 +1278,8 @@ generateTestValuesForParametersTest()
|
|||||||
ParamTestCaseBundle{
|
ParamTestCaseBundle{
|
||||||
"OwnedClaimIdInvalidType",
|
"OwnedClaimIdInvalidType",
|
||||||
R"({
|
R"({
|
||||||
"xchain_owned_claim_id": 123
|
"xchain_owned_claim_id": 123
|
||||||
})",
|
})",
|
||||||
"malformedRequest",
|
"malformedRequest",
|
||||||
"Malformed request."
|
"Malformed request."
|
||||||
},
|
},
|
||||||
@@ -1547,6 +1547,219 @@ generateTestValuesForParametersTest()
|
|||||||
"malformedRequest",
|
"malformedRequest",
|
||||||
"Malformed request."
|
"Malformed request."
|
||||||
},
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdMissing",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}"
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidNegative",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": -1
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidTypeString",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": "invalid"
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidTypeDouble",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": 3.21
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidTypeObject",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": {{}}
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidTypeArray",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": []
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectDocumentIdInvalidTypeNull",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": null
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
"malformedDocumentID",
|
||||||
|
"Malformed oracle_document_id."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountMissing",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidTypeInteger",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": 123,
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidTypeDouble",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": 123.45,
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidTypeNull",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": null,
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidTypeObject",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": {"test": "test"},
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidTypeArray",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": [{"test": "test"}],
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleObjectAccountInvalidFormat",
|
||||||
|
R"({
|
||||||
|
"oracle": {
|
||||||
|
"account": "NotHex",
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleStringInvalidFormat",
|
||||||
|
R"({
|
||||||
|
"oracle": "NotHex"
|
||||||
|
})",
|
||||||
|
"malformedAddress",
|
||||||
|
"Malformed address."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleStringInvalidTypeInteger",
|
||||||
|
R"({
|
||||||
|
"oracle": 123
|
||||||
|
})",
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleStringInvalidTypeDouble",
|
||||||
|
R"({
|
||||||
|
"oracle": 123.45
|
||||||
|
})",
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleStringInvalidTypeArray",
|
||||||
|
R"({
|
||||||
|
"oracle": [{"test": "test"}]
|
||||||
|
})",
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
|
ParamTestCaseBundle{
|
||||||
|
"OracleStringInvalidTypeNull",
|
||||||
|
R"({
|
||||||
|
"oracle": null
|
||||||
|
})",
|
||||||
|
"malformedRequest",
|
||||||
|
"Malformed request."
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1637,13 +1850,11 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryNotFound)
|
|||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
// return valid ledgerinfo
|
// return valid ledgerinfo
|
||||||
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(ledgerinfo));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(ledgerinfo));
|
|
||||||
|
|
||||||
// return null for ledger entry
|
// return null for ledger entry
|
||||||
auto const key = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key;
|
auto const key = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*backend, doFetchLedgerObject(key, RANGEMAX, _)).WillRepeatedly(Return(std::optional<Blob>{}));
|
||||||
ON_CALL(*backend, doFetchLedgerObject(key, RANGEMAX, _)).WillByDefault(Return(std::optional<Blob>{}));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2119,6 +2330,57 @@ generateTestValuesForNormalPathTest()
|
|||||||
.key,
|
.key,
|
||||||
CreateChainOwnedClaimIDObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3, ACCOUNT)
|
CreateChainOwnedClaimIDObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3, ACCOUNT)
|
||||||
},
|
},
|
||||||
|
NormalPathTestBundle{
|
||||||
|
"OracleEntryFoundViaObject",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"binary": true,
|
||||||
|
"oracle": {{
|
||||||
|
"account": "{}",
|
||||||
|
"oracle_document_id": 1
|
||||||
|
}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
ripple::keylet::oracle(GetAccountIDWithString(ACCOUNT), 1).key,
|
||||||
|
CreateOracleObject(
|
||||||
|
ACCOUNT,
|
||||||
|
"70726F7669646572",
|
||||||
|
32u,
|
||||||
|
1234u,
|
||||||
|
ripple::Blob(8, 's'),
|
||||||
|
ripple::Blob(8, 's'),
|
||||||
|
RANGEMAX - 2,
|
||||||
|
ripple::uint256{"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321"},
|
||||||
|
CreatePriceDataSeries(
|
||||||
|
{CreateOraclePriceData(2e4, ripple::to_currency("XRP"), ripple::to_currency("USD"), 3)}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
NormalPathTestBundle{
|
||||||
|
"OracleEntryFoundViaString",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"binary": true,
|
||||||
|
"oracle": "{}"
|
||||||
|
}})",
|
||||||
|
ripple::to_string(ripple::keylet::oracle(GetAccountIDWithString(ACCOUNT), 1).key)
|
||||||
|
),
|
||||||
|
ripple::keylet::oracle(GetAccountIDWithString(ACCOUNT), 1).key,
|
||||||
|
CreateOracleObject(
|
||||||
|
ACCOUNT,
|
||||||
|
"70726F7669646572",
|
||||||
|
64u,
|
||||||
|
4321u,
|
||||||
|
ripple::Blob(8, 'a'),
|
||||||
|
ripple::Blob(8, 'a'),
|
||||||
|
RANGEMAX - 4,
|
||||||
|
ripple::uint256{"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321"},
|
||||||
|
CreatePriceDataSeries(
|
||||||
|
{CreateOraclePriceData(1e3, ripple::to_currency("USD"), ripple::to_currency("XRP"), 2)}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2138,12 +2400,10 @@ TEST_P(RPCLedgerEntryNormalPathTest, NormalPath)
|
|||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
// return valid ledgerinfo
|
// return valid ledgerinfo
|
||||||
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(ledgerinfo));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(ledgerinfo));
|
|
||||||
|
|
||||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*backend, doFetchLedgerObject(testBundle.expectedIndex, RANGEMAX, _))
|
||||||
ON_CALL(*backend, doFetchLedgerObject(testBundle.expectedIndex, RANGEMAX, _))
|
.WillRepeatedly(Return(testBundle.mockedEntity.getSerializer().peekData()));
|
||||||
.WillByDefault(Return(testBundle.mockedEntity.getSerializer().peekData()));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2190,14 +2450,12 @@ TEST_F(RPCLedgerEntryTest, BinaryFalse)
|
|||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
// return valid ledgerinfo
|
// return valid ledgerinfo
|
||||||
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(ledgerinfo));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(ledgerinfo));
|
|
||||||
|
|
||||||
// return valid ledger entry which can be deserialized
|
// return valid ledger entry which can be deserialized
|
||||||
auto const ledgerEntry = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 200, 300, INDEX1, 400);
|
auto const ledgerEntry = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 200, 300, INDEX1, 400);
|
||||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*backend, doFetchLedgerObject(ripple::uint256{INDEX1}, RANGEMAX, _))
|
||||||
ON_CALL(*backend, doFetchLedgerObject(ripple::uint256{INDEX1}, RANGEMAX, _))
|
.WillRepeatedly(Return(ledgerEntry.getSerializer().peekData()));
|
||||||
.WillByDefault(Return(ledgerEntry.getSerializer().peekData()));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2218,14 +2476,12 @@ TEST_F(RPCLedgerEntryTest, UnexpectedLedgerType)
|
|||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
// return valid ledgerinfo
|
// return valid ledgerinfo
|
||||||
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, RANGEMAX);
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(ledgerinfo));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(ledgerinfo));
|
|
||||||
|
|
||||||
// return valid ledger entry which can be deserialized
|
// return valid ledger entry which can be deserialized
|
||||||
auto const ledgerEntry = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 200, 300, INDEX1, 400);
|
auto const ledgerEntry = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 200, 300, INDEX1, 400);
|
||||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*backend, doFetchLedgerObject(ripple::uint256{INDEX1}, RANGEMAX, _))
|
||||||
ON_CALL(*backend, doFetchLedgerObject(ripple::uint256{INDEX1}, RANGEMAX, _))
|
.WillRepeatedly(Return(ledgerEntry.getSerializer().peekData()));
|
||||||
.WillByDefault(Return(ledgerEntry.getSerializer().peekData()));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2246,8 +2502,7 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaIntSequence)
|
|||||||
{
|
{
|
||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(std::nullopt));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(std::nullopt));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2271,8 +2526,7 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaStringSequence)
|
|||||||
{
|
{
|
||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillRepeatedly(Return(std::nullopt));
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillByDefault(Return(std::nullopt));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
@@ -2296,8 +2550,7 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaHash)
|
|||||||
{
|
{
|
||||||
backend->setRange(RANGEMIN, RANGEMAX);
|
backend->setRange(RANGEMIN, RANGEMAX);
|
||||||
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillRepeatedly(Return(std::nullopt));
|
||||||
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(std::nullopt));
|
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
auto const handler = AnyHandler{LedgerEntryHandler{backend}};
|
||||||
|
|||||||
@@ -1025,3 +1025,62 @@ CreateLPTCurrency(std::string_view assetCurrency, std::string_view asset2Currenc
|
|||||||
ripple::to_currency(std::string(assetCurrency)), ripple::to_currency(std::string(asset2Currency))
|
ripple::to_currency(std::string(assetCurrency)), ripple::to_currency(std::string(asset2Currency))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ripple::STObject
|
||||||
|
CreateOraclePriceData(
|
||||||
|
uint64_t assetPrice,
|
||||||
|
ripple::Currency baseAssetCurrency,
|
||||||
|
ripple::Currency quoteAssetCurrency,
|
||||||
|
uint8_t scale
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto priceData = ripple::STObject(ripple::sfPriceData);
|
||||||
|
priceData.setFieldU64(ripple::sfAssetPrice, assetPrice);
|
||||||
|
priceData.setFieldCurrency(ripple::sfBaseAsset, ripple::STCurrency{ripple::sfBaseAsset, baseAssetCurrency});
|
||||||
|
priceData.setFieldCurrency(ripple::sfQuoteAsset, ripple::STCurrency{ripple::sfQuoteAsset, quoteAssetCurrency});
|
||||||
|
priceData.setFieldU8(ripple::sfScale, scale);
|
||||||
|
|
||||||
|
return priceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
ripple::STArray
|
||||||
|
CreatePriceDataSeries(std::vector<ripple::STObject> const& series)
|
||||||
|
{
|
||||||
|
auto priceDataSeries = ripple::STArray{series.size()};
|
||||||
|
|
||||||
|
for (auto& data : series) {
|
||||||
|
auto serializer = data.getSerializer();
|
||||||
|
priceDataSeries.add(serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return priceDataSeries;
|
||||||
|
}
|
||||||
|
|
||||||
|
ripple::STObject
|
||||||
|
CreateOracleObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view provider,
|
||||||
|
uint64_t ownerNode,
|
||||||
|
uint32_t lastUpdateTime,
|
||||||
|
ripple::Blob uri,
|
||||||
|
ripple::Blob assetClass,
|
||||||
|
uint32_t previousTxSeq,
|
||||||
|
ripple::uint256 previousTxId,
|
||||||
|
ripple::STArray priceDataSeries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto ledgerObject = ripple::STObject(ripple::sfLedgerEntry);
|
||||||
|
ledgerObject.setFieldU16(ripple::sfLedgerEntryType, ripple::ltORACLE);
|
||||||
|
ledgerObject.setFieldU32(ripple::sfFlags, 0);
|
||||||
|
ledgerObject.setAccountID(ripple::sfOwner, GetAccountIDWithString(accountId));
|
||||||
|
ledgerObject.setFieldVL(ripple::sfProvider, ripple::Blob{provider.begin(), provider.end()});
|
||||||
|
ledgerObject.setFieldU64(ripple::sfOwnerNode, ownerNode);
|
||||||
|
ledgerObject.setFieldU32(ripple::sfLastUpdateTime, lastUpdateTime);
|
||||||
|
ledgerObject.setFieldVL(ripple::sfURI, uri);
|
||||||
|
ledgerObject.setFieldVL(ripple::sfAssetClass, assetClass);
|
||||||
|
ledgerObject.setFieldU32(ripple::sfPreviousTxnLgrSeq, previousTxSeq);
|
||||||
|
ledgerObject.setFieldH256(ripple::sfPreviousTxnID, previousTxId);
|
||||||
|
ledgerObject.setFieldArray(ripple::sfPriceDataSeries, priceDataSeries);
|
||||||
|
|
||||||
|
return ledgerObject;
|
||||||
|
}
|
||||||
|
|||||||
@@ -383,3 +383,27 @@ CreateDidObject(std::string_view accountId, std::string_view didDoc, std::string
|
|||||||
|
|
||||||
[[nodiscard]] ripple::Currency
|
[[nodiscard]] ripple::Currency
|
||||||
CreateLPTCurrency(std::string_view assetCurrency, std::string_view asset2Currency);
|
CreateLPTCurrency(std::string_view assetCurrency, std::string_view asset2Currency);
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STObject
|
||||||
|
CreateOraclePriceData(
|
||||||
|
uint64_t assetPrice,
|
||||||
|
ripple::Currency baseAssetCurrency,
|
||||||
|
ripple::Currency quoteAssetCurrency,
|
||||||
|
uint8_t scale
|
||||||
|
);
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STArray
|
||||||
|
CreatePriceDataSeries(std::vector<ripple::STObject> const& series);
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STObject
|
||||||
|
CreateOracleObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view provider,
|
||||||
|
uint64_t ownerNode,
|
||||||
|
uint32_t lastUpdateTime,
|
||||||
|
ripple::Blob uri,
|
||||||
|
ripple::Blob assetClass,
|
||||||
|
uint32_t previousTxSeq,
|
||||||
|
ripple::uint256 previousTxId,
|
||||||
|
ripple::STArray priceDataSeries
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user