mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
first half of support for books stream
This commit is contained in:
@@ -98,7 +98,9 @@ target_sources(clio PRIVATE
|
|||||||
src/rpc/handlers/ChannelAuthorize.cpp
|
src/rpc/handlers/ChannelAuthorize.cpp
|
||||||
src/rpc/handlers/ChannelVerify.cpp
|
src/rpc/handlers/ChannelVerify.cpp
|
||||||
# Subscribe
|
# Subscribe
|
||||||
src/rpc/handlers/Subscribe.cpp)
|
src/rpc/handlers/Subscribe.cpp
|
||||||
|
# Server
|
||||||
|
src/rpc/handlers/ServerInfo.cpp)
|
||||||
|
|
||||||
|
|
||||||
message(${Boost_LIBRARIES})
|
message(${Boost_LIBRARIES})
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ BackendInterface::fetchBookOffers(
|
|||||||
page.offers.push_back({keys[i], objs[i]});
|
page.offers.push_back({keys[i], objs[i]});
|
||||||
}
|
}
|
||||||
auto end = std::chrono::system_clock::now();
|
auto end = std::chrono::system_clock::now();
|
||||||
BOOST_LOG_TRIVIAL(info)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< __func__ << " "
|
<< __func__ << " "
|
||||||
<< "Fetching " << std::to_string(keys.size()) << " keys took "
|
<< "Fetching " << std::to_string(keys.size()) << " keys took "
|
||||||
<< std::to_string(getMillis(mid - begin))
|
<< std::to_string(getMillis(mid - begin))
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
#include <rpc/RPC.h>
|
#include <rpc/RPC.h>
|
||||||
|
|
||||||
namespace RPC
|
namespace RPC {
|
||||||
{
|
/*
|
||||||
/*
|
* This file just contains declarations for all of the handlers
|
||||||
* This file just contains declarations for all of the handlers
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
// account state methods
|
// account state methods
|
||||||
Result
|
Result
|
||||||
doAccountInfo(Context const& context);
|
doAccountInfo(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
@@ -22,7 +21,7 @@ doAccountCurrencies(Context const& context);
|
|||||||
Result
|
Result
|
||||||
doAccountLines(Context const& context);
|
doAccountLines(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doAccountObjects(Context const& context);
|
doAccountObjects(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
@@ -30,7 +29,7 @@ doAccountOffers(Context const& context);
|
|||||||
|
|
||||||
// channels methods
|
// channels methods
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doChannelAuthorize(Context const& context);
|
doChannelAuthorize(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
@@ -41,7 +40,7 @@ Result
|
|||||||
doBookOffers(Context const& context);
|
doBookOffers(Context const& context);
|
||||||
|
|
||||||
// ledger methods
|
// ledger methods
|
||||||
Result
|
Result
|
||||||
doLedger(Context const& context);
|
doLedger(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
@@ -54,18 +53,22 @@ Result
|
|||||||
doLedgerRange(Context const& context);
|
doLedgerRange(Context const& context);
|
||||||
|
|
||||||
// transaction methods
|
// transaction methods
|
||||||
Result
|
Result
|
||||||
doTx(Context const& context);
|
doTx(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doAccountTx(Context const& context);
|
doAccountTx(Context const& context);
|
||||||
|
|
||||||
// subscriptions
|
// subscriptions
|
||||||
Result
|
Result
|
||||||
doSubscribe(Context const& context);
|
doSubscribe(Context const& context);
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doUnsubscribe(Context const& context);
|
doUnsubscribe(Context const& context);
|
||||||
|
|
||||||
} // namespace RPC
|
// server methods
|
||||||
|
Result
|
||||||
|
doServerInfo(Context const& context);
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#include <unordered_map>
|
|
||||||
#include <etl/ETLSource.h>
|
#include <etl/ETLSource.h>
|
||||||
#include <rpc/Handlers.h>
|
#include <rpc/Handlers.h>
|
||||||
namespace RPC
|
#include <unordered_map>
|
||||||
{
|
namespace RPC {
|
||||||
|
|
||||||
std::optional<Context>
|
std::optional<Context>
|
||||||
make_WsContext(
|
make_WsContext(
|
||||||
@@ -15,19 +14,11 @@ make_WsContext(
|
|||||||
{
|
{
|
||||||
if (!request.contains("command"))
|
if (!request.contains("command"))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::string command = request.at("command").as_string().c_str();
|
std::string command = request.at("command").as_string().c_str();
|
||||||
|
|
||||||
return Context{
|
return Context{
|
||||||
command,
|
command, 1, request, backend, subscriptions, balancer, session, range};
|
||||||
1,
|
|
||||||
request,
|
|
||||||
backend,
|
|
||||||
subscriptions,
|
|
||||||
balancer,
|
|
||||||
session,
|
|
||||||
range
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Context>
|
std::optional<Context>
|
||||||
@@ -53,10 +44,10 @@ make_HttpContext(
|
|||||||
|
|
||||||
if (array.size() != 1)
|
if (array.size() != 1)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!array.at(0).is_object())
|
if (!array.at(0).is_object())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return Context{
|
return Context{
|
||||||
command,
|
command,
|
||||||
1,
|
1,
|
||||||
@@ -65,11 +56,8 @@ make_HttpContext(
|
|||||||
subscriptions,
|
subscriptions,
|
||||||
balancer,
|
balancer,
|
||||||
nullptr,
|
nullptr,
|
||||||
range
|
range};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
inject_error(Error err, boost::json::object& json)
|
inject_error(Error err, boost::json::object& json)
|
||||||
@@ -126,6 +114,7 @@ static std::unordered_map<std::string, std::function<Result(Context const&)>>
|
|||||||
{"ledger_range", &doLedgerRange},
|
{"ledger_range", &doLedgerRange},
|
||||||
{"ledger_data", &doLedgerData},
|
{"ledger_data", &doLedgerData},
|
||||||
{"subscribe", &doSubscribe},
|
{"subscribe", &doSubscribe},
|
||||||
|
{"server_info", &doServerInfo},
|
||||||
{"unsubscribe", &doUnsubscribe},
|
{"unsubscribe", &doUnsubscribe},
|
||||||
{"tx", &doTx},
|
{"tx", &doTx},
|
||||||
};
|
};
|
||||||
@@ -172,4 +161,4 @@ buildResponse(Context const& ctx)
|
|||||||
|
|
||||||
return method(ctx);
|
return method(ctx);
|
||||||
}
|
}
|
||||||
}
|
} // namespace RPC
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
std::optional<ripple::STAmount>
|
std::optional<ripple::STAmount>
|
||||||
getDeliveredAmount(
|
getDeliveredAmount(
|
||||||
@@ -211,8 +212,8 @@ toJson(ripple::LedgerInfo const& lgrInfo)
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<RPC::Status, ripple::LedgerInfo>
|
std::variant<Status, ripple::LedgerInfo>
|
||||||
ledgerInfoFromRequest(RPC::Context const& ctx)
|
ledgerInfoFromRequest(Context const& ctx)
|
||||||
{
|
{
|
||||||
auto indexValue = ctx.params.contains("ledger_index")
|
auto indexValue = ctx.params.contains("ledger_index")
|
||||||
? ctx.params.at("ledger_index")
|
? ctx.params.at("ledger_index")
|
||||||
@@ -226,13 +227,11 @@ ledgerInfoFromRequest(RPC::Context const& ctx)
|
|||||||
if (!hashValue.is_null())
|
if (!hashValue.is_null())
|
||||||
{
|
{
|
||||||
if (!hashValue.is_string())
|
if (!hashValue.is_string())
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
|
||||||
|
|
||||||
ripple::uint256 ledgerHash;
|
ripple::uint256 ledgerHash;
|
||||||
if (!ledgerHash.parseHex(hashValue.as_string().c_str()))
|
if (!ledgerHash.parseHex(hashValue.as_string().c_str()))
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
|
||||||
|
|
||||||
lgrInfo = ctx.backend->fetchLedgerByHash(ledgerHash);
|
lgrInfo = ctx.backend->fetchLedgerByHash(ledgerHash);
|
||||||
}
|
}
|
||||||
@@ -244,8 +243,7 @@ ledgerInfoFromRequest(RPC::Context const& ctx)
|
|||||||
else if (!indexValue.is_string() && indexValue.is_int64())
|
else if (!indexValue.is_string() && indexValue.is_int64())
|
||||||
ledgerSequence = indexValue.as_int64();
|
ledgerSequence = indexValue.as_int64();
|
||||||
else
|
else
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
|
||||||
|
|
||||||
lgrInfo = ctx.backend->fetchLedgerBySequence(ledgerSequence);
|
lgrInfo = ctx.backend->fetchLedgerBySequence(ledgerSequence);
|
||||||
}
|
}
|
||||||
@@ -255,7 +253,7 @@ ledgerInfoFromRequest(RPC::Context const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!lgrInfo)
|
if (!lgrInfo)
|
||||||
return RPC::Status{RPC::Error::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
return Status{Error::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
|
|
||||||
return *lgrInfo;
|
return *lgrInfo;
|
||||||
}
|
}
|
||||||
@@ -362,7 +360,7 @@ parseRippleLibSeed(boost::json::value const& value)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<RPC::Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
std::variant<Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
||||||
keypairFromRequst(boost::json::object const& request)
|
keypairFromRequst(boost::json::object const& request)
|
||||||
{
|
{
|
||||||
bool const has_key_type = request.contains("key_type");
|
bool const has_key_type = request.contains("key_type");
|
||||||
@@ -385,13 +383,12 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "missing field secret"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "missing field secret"};
|
|
||||||
|
|
||||||
if (count > 1)
|
if (count > 1)
|
||||||
{
|
{
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS,
|
||||||
"Exactly one of the following must be specified: "
|
"Exactly one of the following must be specified: "
|
||||||
" passphrase, secret, seed, or seed_hex"};
|
" passphrase, secret, seed, or seed_hex"};
|
||||||
}
|
}
|
||||||
@@ -402,19 +399,17 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
if (has_key_type)
|
if (has_key_type)
|
||||||
{
|
{
|
||||||
if (!request.at("key_type").is_string())
|
if (!request.at("key_type").is_string())
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "keyTypeNotString"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "keyTypeNotString"};
|
|
||||||
|
|
||||||
std::string key_type = request.at("key_type").as_string().c_str();
|
std::string key_type = request.at("key_type").as_string().c_str();
|
||||||
keyType = ripple::keyTypeFromString(key_type);
|
keyType = ripple::keyTypeFromString(key_type);
|
||||||
|
|
||||||
if (!keyType)
|
if (!keyType)
|
||||||
return RPC::Status{
|
return Status{Error::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
||||||
RPC::Error::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
|
||||||
|
|
||||||
if (secretType == "secret")
|
if (secretType == "secret")
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS,
|
||||||
"The secret field is not allowed if key_type is used."};
|
"The secret field is not allowed if key_type is used."};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,8 +426,8 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
// requested another key type, return an error.
|
// requested another key type, return an error.
|
||||||
if (keyType.value_or(ripple::KeyType::ed25519) !=
|
if (keyType.value_or(ripple::KeyType::ed25519) !=
|
||||||
ripple::KeyType::ed25519)
|
ripple::KeyType::ed25519)
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS,
|
||||||
"Specified seed is for an Ed25519 wallet."};
|
"Specified seed is for an Ed25519 wallet."};
|
||||||
|
|
||||||
keyType = ripple::KeyType::ed25519;
|
keyType = ripple::KeyType::ed25519;
|
||||||
@@ -447,9 +442,8 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
if (has_key_type)
|
if (has_key_type)
|
||||||
{
|
{
|
||||||
if (!request.at(secretType).is_string())
|
if (!request.at(secretType).is_string())
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS, "secret value must be string"};
|
||||||
"secret value must be string"};
|
|
||||||
|
|
||||||
std::string key = request.at(secretType).as_string().c_str();
|
std::string key = request.at(secretType).as_string().c_str();
|
||||||
|
|
||||||
@@ -467,8 +461,8 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!request.at("secret").is_string())
|
if (!request.at("secret").is_string())
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS,
|
||||||
"field secret should be a string"};
|
"field secret should be a string"};
|
||||||
|
|
||||||
std::string secret = request.at("secret").as_string().c_str();
|
std::string secret = request.at("secret").as_string().c_str();
|
||||||
@@ -477,15 +471,13 @@ keypairFromRequst(boost::json::object const& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!seed)
|
if (!seed)
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcBAD_SEED,
|
Error::rpcBAD_SEED, "Bad Seed: invalid field message secretType"};
|
||||||
"Bad Seed: invalid field message secretType"};
|
|
||||||
|
|
||||||
if (keyType != ripple::KeyType::secp256k1 &&
|
if (keyType != ripple::KeyType::secp256k1 &&
|
||||||
keyType != ripple::KeyType::ed25519)
|
keyType != ripple::KeyType::ed25519)
|
||||||
return RPC::Status{
|
return Status{
|
||||||
RPC::Error::rpcINVALID_PARAMS,
|
Error::rpcINVALID_PARAMS, "keypairForSignature: invalid key type"};
|
||||||
"keypairForSignature: invalid key type"};
|
|
||||||
|
|
||||||
return generateKeyPair(*keyType, *seed);
|
return generateKeyPair(*keyType, *seed);
|
||||||
}
|
}
|
||||||
@@ -672,3 +664,130 @@ transferRate(
|
|||||||
|
|
||||||
return ripple::parityRate;
|
return ripple::parityRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::variant<Status, ripple::Book>
|
||||||
|
parseBook(boost::json::object const& request)
|
||||||
|
{
|
||||||
|
if (!request.contains("taker_pays"))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingTakerPays"};
|
||||||
|
|
||||||
|
if (!request.contains("taker_gets"))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingTakerGets"};
|
||||||
|
|
||||||
|
if (!request.at("taker_pays").is_object())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "takerPaysNotObject"};
|
||||||
|
|
||||||
|
if (!request.at("taker_gets").is_object())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "takerGetsNotObject"};
|
||||||
|
|
||||||
|
auto taker_pays = request.at("taker_pays").as_object();
|
||||||
|
if (!taker_pays.contains("currency"))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingTakerPaysCurrency"};
|
||||||
|
|
||||||
|
if (!taker_pays.at("currency").is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "takerPaysCurrencyNotString"};
|
||||||
|
|
||||||
|
auto taker_gets = request.at("taker_gets").as_object();
|
||||||
|
if (!taker_gets.contains("currency"))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "missingTakerGetsCurrency"};
|
||||||
|
|
||||||
|
if (!taker_gets.at("currency").is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"};
|
||||||
|
|
||||||
|
ripple::Currency pay_currency;
|
||||||
|
if (!ripple::to_currency(
|
||||||
|
pay_currency, taker_pays.at("currency").as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "badTakerPaysCurrency"};
|
||||||
|
|
||||||
|
ripple::Currency get_currency;
|
||||||
|
if (!ripple::to_currency(
|
||||||
|
get_currency, taker_gets["currency"].as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "badTakerGetsCurrency"};
|
||||||
|
|
||||||
|
ripple::AccountID pay_issuer;
|
||||||
|
if (taker_pays.contains("issuer"))
|
||||||
|
{
|
||||||
|
if (!taker_pays.at("issuer").is_string())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "takerPaysIssuerNotString"};
|
||||||
|
|
||||||
|
if (!ripple::to_issuer(
|
||||||
|
pay_issuer, taker_pays.at("issuer").as_string().c_str()))
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "badTakerPaysIssuer"};
|
||||||
|
|
||||||
|
if (pay_issuer == ripple::noAccount())
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS, "badTakerPaysIssuerAccountOne"};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pay_issuer = ripple::xrpAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
||||||
|
"specification."};
|
||||||
|
|
||||||
|
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
||||||
|
"issuer."};
|
||||||
|
|
||||||
|
ripple::AccountID get_issuer;
|
||||||
|
|
||||||
|
if (taker_gets.contains("issuer"))
|
||||||
|
{
|
||||||
|
if (!taker_gets["issuer"].is_string())
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS, "taker_gets.issuer should be string"};
|
||||||
|
|
||||||
|
if (!ripple::to_issuer(
|
||||||
|
get_issuer, taker_gets.at("issuer").as_string().c_str()))
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Invalid field 'taker_gets.issuer', bad issuer."};
|
||||||
|
|
||||||
|
if (get_issuer == ripple::noAccount())
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Invalid field 'taker_gets.issuer', bad issuer account "
|
||||||
|
"one."};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_issuer = ripple::xrpAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
||||||
|
"specification."};
|
||||||
|
|
||||||
|
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
||||||
|
return Status{
|
||||||
|
Error::rpcINVALID_PARAMS,
|
||||||
|
"Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
|
||||||
|
|
||||||
|
if (pay_currency == get_currency && pay_issuer == get_issuer)
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "badMarket"};
|
||||||
|
|
||||||
|
return ripple::Book{{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
||||||
|
}
|
||||||
|
std::variant<Status, ripple::AccountID>
|
||||||
|
parseTaker(boost::json::value const& taker)
|
||||||
|
{
|
||||||
|
std::optional<ripple::AccountID> takerID = {};
|
||||||
|
if (!taker.is_string())
|
||||||
|
return {Status{Error::rpcINVALID_PARAMS, "takerNotString"}};
|
||||||
|
|
||||||
|
takerID = accountFromStringStrict(taker.as_string().c_str());
|
||||||
|
|
||||||
|
if (!takerID)
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "invalidTakerAccount"};
|
||||||
|
return *takerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
|
|||||||
@@ -14,14 +14,17 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPC.h>
|
#include <rpc/RPC.h>
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
std::optional<ripple::AccountID>
|
std::optional<ripple::AccountID>
|
||||||
accountFromStringStrict(std::string const& account);
|
accountFromStringStrict(std::string const& account);
|
||||||
|
|
||||||
|
// TODO this function should probably be in a different file and namespace
|
||||||
std::pair<
|
std::pair<
|
||||||
std::shared_ptr<ripple::STTx const>,
|
std::shared_ptr<ripple::STTx const>,
|
||||||
std::shared_ptr<ripple::STObject const>>
|
std::shared_ptr<ripple::STObject const>>
|
||||||
deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs);
|
deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs);
|
||||||
|
|
||||||
|
// TODO this function should probably be in a different file and namespace
|
||||||
std::pair<
|
std::pair<
|
||||||
std::shared_ptr<ripple::STTx const>,
|
std::shared_ptr<ripple::STTx const>,
|
||||||
std::shared_ptr<ripple::TxMeta const>>
|
std::shared_ptr<ripple::TxMeta const>>
|
||||||
@@ -48,15 +51,15 @@ using RippledJson = Json::Value;
|
|||||||
boost::json::value
|
boost::json::value
|
||||||
toBoostJson(RippledJson const& value);
|
toBoostJson(RippledJson const& value);
|
||||||
|
|
||||||
|
|
||||||
boost::json::object
|
boost::json::object
|
||||||
generatePubLedgerMessage(ripple::LedgerInfo const& lgrInfo,
|
generatePubLedgerMessage(
|
||||||
ripple::Fees const& fees,
|
ripple::LedgerInfo const& lgrInfo,
|
||||||
std::string const& ledgerRange,
|
ripple::Fees const& fees,
|
||||||
uint32_t txnCount);
|
std::string const& ledgerRange,
|
||||||
|
uint32_t txnCount);
|
||||||
|
|
||||||
std::variant<RPC::Status, ripple::LedgerInfo>
|
std::variant<Status, ripple::LedgerInfo>
|
||||||
ledgerInfoFromRequest(RPC::Context const& ctx);
|
ledgerInfoFromRequest(Context const& ctx);
|
||||||
|
|
||||||
std::optional<ripple::uint256>
|
std::optional<ripple::uint256>
|
||||||
traverseOwnedNodes(
|
traverseOwnedNodes(
|
||||||
@@ -66,7 +69,7 @@ traverseOwnedNodes(
|
|||||||
ripple::uint256 const& cursor,
|
ripple::uint256 const& cursor,
|
||||||
std::function<bool(ripple::SLE)> atOwnedNode);
|
std::function<bool(ripple::SLE)> atOwnedNode);
|
||||||
|
|
||||||
std::variant<RPC::Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
std::variant<Status, std::pair<ripple::PublicKey, ripple::SecretKey>>
|
||||||
keypairFromRequst(boost::json::object const& request);
|
keypairFromRequst(boost::json::object const& request);
|
||||||
|
|
||||||
std::vector<ripple::AccountID>
|
std::vector<ripple::AccountID>
|
||||||
@@ -109,4 +112,11 @@ xrpLiquid(
|
|||||||
std::uint32_t sequence,
|
std::uint32_t sequence,
|
||||||
ripple::AccountID const& id);
|
ripple::AccountID const& id);
|
||||||
|
|
||||||
|
std::variant<Status, ripple::Book>
|
||||||
|
parseBook(boost::json::object const& request);
|
||||||
|
|
||||||
|
std::variant<Status, ripple::AccountID>
|
||||||
|
parseTaker(boost::json::value const& request);
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,8 +12,7 @@
|
|||||||
#include <backend/DBHelpers.h>
|
#include <backend/DBHelpers.h>
|
||||||
#include <backend/Pg.h>
|
#include <backend/Pg.h>
|
||||||
|
|
||||||
namespace RPC
|
namespace RPC {
|
||||||
{
|
|
||||||
|
|
||||||
Result
|
Result
|
||||||
doBookOffers(Context const& context)
|
doBookOffers(Context const& context)
|
||||||
@@ -39,113 +38,20 @@ doBookOffers(Context const& context)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!request.contains("taker_pays"))
|
auto parsed = parseBook(request);
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerPays"};
|
if (auto status = std::get_if<Status>(&parsed))
|
||||||
|
return *status;
|
||||||
if (!request.contains("taker_gets"))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerGets"};
|
|
||||||
|
|
||||||
if (!request.at("taker_pays").is_object())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerPaysNotObject"};
|
|
||||||
|
|
||||||
if (!request.at("taker_gets").is_object())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerGetsNotObject"};
|
|
||||||
|
|
||||||
auto taker_pays = request.at("taker_pays").as_object();
|
|
||||||
if (!taker_pays.contains("currency"))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerPaysCurrency"};
|
|
||||||
|
|
||||||
if (!taker_pays.at("currency").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerPaysCurrencyNotString"};
|
|
||||||
|
|
||||||
auto taker_gets = request.at("taker_gets").as_object();
|
|
||||||
if (!taker_gets.contains("currency"))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "missingTakerGetsCurrency"};
|
|
||||||
|
|
||||||
if (!taker_gets.at("currency").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"};
|
|
||||||
|
|
||||||
ripple::Currency pay_currency;
|
|
||||||
if (!ripple::to_currency(
|
|
||||||
pay_currency, taker_pays.at("currency").as_string().c_str()))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "badTakerPaysCurrency"};
|
|
||||||
|
|
||||||
ripple::Currency get_currency;
|
|
||||||
if (!ripple::to_currency(
|
|
||||||
get_currency, taker_gets["currency"].as_string().c_str()))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "badTakerGetsCurrency"};
|
|
||||||
|
|
||||||
ripple::AccountID pay_issuer;
|
|
||||||
if (taker_pays.contains("issuer"))
|
|
||||||
{
|
|
||||||
if (!taker_pays.at("issuer").is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerPaysIssuerNotString"};
|
|
||||||
|
|
||||||
if (!ripple::to_issuer(
|
|
||||||
pay_issuer, taker_pays.at("issuer").as_string().c_str()))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "badTakerPaysIssuer"};
|
|
||||||
|
|
||||||
if (pay_issuer == ripple::noAccount())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "badTakerPaysIssuerAccountOne"};
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pay_issuer = ripple::xrpAccount();
|
book = std::get<ripple::Book>(parsed);
|
||||||
|
bookBase = getBookBase(book);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
|
||||||
"issuer."};
|
|
||||||
|
|
||||||
ripple::AccountID get_issuer;
|
|
||||||
|
|
||||||
if (taker_gets.contains("issuer"))
|
|
||||||
{
|
|
||||||
if (!taker_gets["issuer"].is_string())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"taker_gets.issuer should be string"};
|
|
||||||
|
|
||||||
if (!ripple::to_issuer(
|
|
||||||
get_issuer, taker_gets.at("issuer").as_string().c_str()))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Invalid field 'taker_gets.issuer', bad issuer."};
|
|
||||||
|
|
||||||
if (get_issuer == ripple::noAccount())
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Invalid field 'taker_gets.issuer', bad issuer account "
|
|
||||||
"one."};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
get_issuer = ripple::xrpAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
|
||||||
return Status{Error::rpcINVALID_PARAMS,
|
|
||||||
"Invalid field 'taker_gets.issuer', expected non-XRP issuer."};
|
|
||||||
|
|
||||||
if (pay_currency == get_currency && pay_issuer == get_issuer)
|
|
||||||
return Status{Error::rpcINVALID_PARAMS, "badMarket"};
|
|
||||||
|
|
||||||
book = {{pay_currency, pay_issuer}, {get_currency, get_issuer}};
|
|
||||||
bookBase = getBookBase(book);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t limit = 200;
|
std::uint32_t limit = 200;
|
||||||
if (request.contains("limit"))
|
if (request.contains("limit"))
|
||||||
{
|
{
|
||||||
if(!request.at("limit").is_int64())
|
if (!request.at("limit").is_int64())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
return Status{Error::rpcINVALID_PARAMS, "limitNotInt"};
|
||||||
|
|
||||||
limit = request.at("limit").as_int64();
|
limit = request.at("limit").as_int64();
|
||||||
@@ -153,24 +59,22 @@ doBookOffers(Context const& context)
|
|||||||
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
return Status{Error::rpcINVALID_PARAMS, "limitNotPositive"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<ripple::AccountID> takerID = {};
|
std::optional<ripple::AccountID> takerID = {};
|
||||||
if (request.contains("taker"))
|
if (request.contains("taker"))
|
||||||
{
|
{
|
||||||
if (!request.at("taker").is_string())
|
auto parsed = parseTaker(request["taker"]);
|
||||||
return Status{Error::rpcINVALID_PARAMS, "takerNotString"};
|
if (auto status = std::get_if<Status>(&parsed))
|
||||||
|
return *status;
|
||||||
takerID =
|
else
|
||||||
accountFromStringStrict(request.at("taker").as_string().c_str());
|
{
|
||||||
|
takerID = std::get<ripple::AccountID>(parsed);
|
||||||
if (!takerID)
|
}
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidTakerAccount"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ripple::uint256 cursor = beast::zero;
|
ripple::uint256 cursor = beast::zero;
|
||||||
if (request.contains("cursor"))
|
if (request.contains("cursor"))
|
||||||
{
|
{
|
||||||
if(!request.at("cursor").is_string())
|
if (!request.at("cursor").is_string())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "cursorNotString"};
|
return Status{Error::rpcINVALID_PARAMS, "cursorNotString"};
|
||||||
|
|
||||||
if (!cursor.parseHex(request.at("cursor").as_string().c_str()))
|
if (!cursor.parseHex(request.at("cursor").as_string().c_str()))
|
||||||
@@ -182,8 +86,8 @@ doBookOffers(Context const& context)
|
|||||||
context.backend->fetchBookOffers(bookBase, lgrInfo.seq, limit, cursor);
|
context.backend->fetchBookOffers(bookBase, lgrInfo.seq, limit, cursor);
|
||||||
auto end = std::chrono::system_clock::now();
|
auto end = std::chrono::system_clock::now();
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Time loading books: "
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
<< ((end - start).count() / 1000000000.0);
|
<< "Time loading books: " << ((end - start).count() / 1000000000.0);
|
||||||
|
|
||||||
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
response["ledger_hash"] = ripple::strHex(lgrInfo.hash);
|
||||||
response["ledger_index"] = lgrInfo.seq;
|
response["ledger_index"] = lgrInfo.seq;
|
||||||
@@ -193,7 +97,7 @@ doBookOffers(Context const& context)
|
|||||||
|
|
||||||
std::map<ripple::AccountID, ripple::STAmount> umBalance;
|
std::map<ripple::AccountID, ripple::STAmount> umBalance;
|
||||||
|
|
||||||
bool globalFreeze =
|
bool globalFreeze =
|
||||||
isGlobalFrozen(*context.backend, lgrInfo.seq, book.out.account) ||
|
isGlobalFrozen(*context.backend, lgrInfo.seq, book.out.account) ||
|
||||||
isGlobalFrozen(*context.backend, lgrInfo.seq, book.out.account);
|
isGlobalFrozen(*context.backend, lgrInfo.seq, book.out.account);
|
||||||
|
|
||||||
@@ -209,7 +113,8 @@ doBookOffers(Context const& context)
|
|||||||
{
|
{
|
||||||
ripple::SerialIter it{obj.blob.data(), obj.blob.size()};
|
ripple::SerialIter it{obj.blob.data(), obj.blob.size()};
|
||||||
ripple::SLE offer{it, obj.key};
|
ripple::SLE offer{it, obj.key};
|
||||||
ripple::uint256 bookDir = offer.getFieldH256(ripple::sfBookDirectory);
|
ripple::uint256 bookDir =
|
||||||
|
offer.getFieldH256(ripple::sfBookDirectory);
|
||||||
|
|
||||||
auto const uOfferOwnerID = offer.getAccountID(ripple::sfAccount);
|
auto const uOfferOwnerID = offer.getAccountID(ripple::sfAccount);
|
||||||
auto const& saTakerGets = offer.getFieldAmount(ripple::sfTakerGets);
|
auto const& saTakerGets = offer.getFieldAmount(ripple::sfTakerGets);
|
||||||
@@ -239,7 +144,8 @@ doBookOffers(Context const& context)
|
|||||||
saOwnerFunds = umBalanceEntry->second;
|
saOwnerFunds = umBalanceEntry->second;
|
||||||
firstOwnerOffer = false;
|
firstOwnerOffer = false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
saOwnerFunds = accountHolds(
|
saOwnerFunds = accountHolds(
|
||||||
*context.backend,
|
*context.backend,
|
||||||
lgrInfo.seq,
|
lgrInfo.seq,
|
||||||
@@ -281,19 +187,20 @@ doBookOffers(Context const& context)
|
|||||||
{
|
{
|
||||||
saTakerGetsFunded = saOwnerFundsLimit;
|
saTakerGetsFunded = saOwnerFundsLimit;
|
||||||
offerJson["taker_gets_funded"] = saTakerGetsFunded.getText();
|
offerJson["taker_gets_funded"] = saTakerGetsFunded.getText();
|
||||||
offerJson["taker_pays_funded"] = toBoostJson(std::min(
|
offerJson["taker_pays_funded"] = toBoostJson(
|
||||||
saTakerPays,
|
std::min(
|
||||||
ripple::multiply(
|
saTakerPays,
|
||||||
saTakerGetsFunded, dirRate, saTakerPays.issue()))
|
ripple::multiply(
|
||||||
.getJson(ripple::JsonOptions::none));
|
saTakerGetsFunded, dirRate, saTakerPays.issue()))
|
||||||
|
.getJson(ripple::JsonOptions::none));
|
||||||
}
|
}
|
||||||
|
|
||||||
ripple::STAmount saOwnerPays = (ripple::parityRate == offerRate)
|
ripple::STAmount saOwnerPays = (ripple::parityRate == offerRate)
|
||||||
? saTakerGetsFunded
|
? saTakerGetsFunded
|
||||||
: std::min(
|
: std::min(
|
||||||
saOwnerFunds,
|
saOwnerFunds,
|
||||||
ripple::multiply(saTakerGetsFunded, offerRate));
|
ripple::multiply(saTakerGetsFunded, offerRate));
|
||||||
|
|
||||||
umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays;
|
umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays;
|
||||||
|
|
||||||
if (firstOwnerOffer)
|
if (firstOwnerOffer)
|
||||||
@@ -303,7 +210,9 @@ doBookOffers(Context const& context)
|
|||||||
|
|
||||||
jsonOffers.push_back(offerJson);
|
jsonOffers.push_back(offerJson);
|
||||||
}
|
}
|
||||||
catch (std::exception const& e) {}
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end = std::chrono::system_clock::now();
|
end = std::chrono::system_clock::now();
|
||||||
@@ -322,4 +231,4 @@ doBookOffers(Context const& context)
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
26
src/rpc/handlers/ServerInfo.cpp
Normal file
26
src/rpc/handlers/ServerInfo.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#include <backend/BackendInterface.h>
|
||||||
|
#include <rpc/RPCHelpers.h>
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
Result
|
||||||
|
doServerInfo(Context const& context)
|
||||||
|
{
|
||||||
|
boost::json::object response = {};
|
||||||
|
|
||||||
|
auto range = context.backend->fetchLedgerRange();
|
||||||
|
if (!range)
|
||||||
|
{
|
||||||
|
return Status{Error::rpcNOT_READY, "rangeNotFound"};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response["info"] = boost::json::object{};
|
||||||
|
response["info"].as_object()["complete_ledgers"] =
|
||||||
|
std::to_string(range->minSequence) + " - " +
|
||||||
|
std::to_string(range->maxSequence);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
} // namespace RPC
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
#include <webserver/WsBase.h>
|
|
||||||
#include <webserver/SubscriptionManager.h>
|
#include <webserver/SubscriptionManager.h>
|
||||||
|
#include <webserver/WsBase.h>
|
||||||
|
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
|
||||||
namespace RPC
|
namespace RPC {
|
||||||
{
|
|
||||||
|
|
||||||
static std::unordered_set<std::string> validStreams {
|
// these are the streams that take no arguments
|
||||||
|
static std::unordered_set<std::string> validCommonStreams{
|
||||||
"ledger",
|
"ledger",
|
||||||
"transactions",
|
"transactions",
|
||||||
"transactions_proposed" };
|
"transactions_proposed"};
|
||||||
|
|
||||||
Status
|
Status
|
||||||
validateStreams(boost::json::object const& request)
|
validateStreams(boost::json::object const& request)
|
||||||
@@ -24,7 +24,7 @@ validateStreams(boost::json::object const& request)
|
|||||||
|
|
||||||
std::string s = stream.as_string().c_str();
|
std::string s = stream.as_string().c_str();
|
||||||
|
|
||||||
if (validStreams.find(s) == validStreams.end())
|
if (validCommonStreams.find(s) == validCommonStreams.end())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "invalidStream" + s};
|
return Status{Error::rpcINVALID_PARAMS, "invalidStream" + s};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ subscribeToAccounts(
|
|||||||
|
|
||||||
auto accountID = accountFromStringStrict(s);
|
auto accountID = accountFromStringStrict(s);
|
||||||
|
|
||||||
if(!accountID)
|
if (!accountID)
|
||||||
{
|
{
|
||||||
assert(false);
|
assert(false);
|
||||||
continue;
|
continue;
|
||||||
@@ -133,7 +133,7 @@ unsubscribeToAccounts(
|
|||||||
|
|
||||||
auto accountID = accountFromStringStrict(s);
|
auto accountID = accountFromStringStrict(s);
|
||||||
|
|
||||||
if(!accountID)
|
if (!accountID)
|
||||||
{
|
{
|
||||||
assert(false);
|
assert(false);
|
||||||
continue;
|
continue;
|
||||||
@@ -149,7 +149,8 @@ subscribeToAccountsProposed(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts = request.at("accounts_proposed").as_array();
|
boost::json::array const& accounts =
|
||||||
|
request.at("accounts_proposed").as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -157,7 +158,7 @@ subscribeToAccountsProposed(
|
|||||||
|
|
||||||
auto accountID = ripple::parseBase58<ripple::AccountID>(s);
|
auto accountID = ripple::parseBase58<ripple::AccountID>(s);
|
||||||
|
|
||||||
if(!accountID)
|
if (!accountID)
|
||||||
{
|
{
|
||||||
assert(false);
|
assert(false);
|
||||||
continue;
|
continue;
|
||||||
@@ -173,7 +174,8 @@ unsubscribeToAccountsProposed(
|
|||||||
std::shared_ptr<WsBase> session,
|
std::shared_ptr<WsBase> session,
|
||||||
SubscriptionManager& manager)
|
SubscriptionManager& manager)
|
||||||
{
|
{
|
||||||
boost::json::array const& accounts = request.at("accounts_proposed").as_array();
|
boost::json::array const& accounts =
|
||||||
|
request.at("accounts_proposed").as_array();
|
||||||
|
|
||||||
for (auto const& account : accounts)
|
for (auto const& account : accounts)
|
||||||
{
|
{
|
||||||
@@ -181,7 +183,7 @@ unsubscribeToAccountsProposed(
|
|||||||
|
|
||||||
auto accountID = ripple::parseBase58<ripple::AccountID>(s);
|
auto accountID = ripple::parseBase58<ripple::AccountID>(s);
|
||||||
|
|
||||||
if(!accountID)
|
if (!accountID)
|
||||||
{
|
{
|
||||||
assert(false);
|
assert(false);
|
||||||
continue;
|
continue;
|
||||||
@@ -191,7 +193,40 @@ unsubscribeToAccountsProposed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::variant<Status, std::vector<ripple::Book>>
|
||||||
|
validateAndGetBooks(boost::json::object const& request)
|
||||||
|
{
|
||||||
|
if (!request.at("books").is_array())
|
||||||
|
return Status{Error::rpcINVALID_PARAMS, "booksNotArray"};
|
||||||
|
boost::json::array const& books = request.at("books").as_array();
|
||||||
|
|
||||||
|
std::vector<ripple::Book> booksToSub;
|
||||||
|
for (auto const& book : books)
|
||||||
|
{
|
||||||
|
auto parsed = parseBook(book.as_object());
|
||||||
|
if (auto status = std::get_if<Status>(&parsed))
|
||||||
|
return *status;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto b = std::get<ripple::Book>(parsed);
|
||||||
|
booksToSub.push_back(b);
|
||||||
|
if (book.as_object().contains("both"))
|
||||||
|
booksToSub.push_back(ripple::reversed(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return booksToSub;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
subscribeToBooks(
|
||||||
|
std::vector<ripple::Book> const& books,
|
||||||
|
std::shared_ptr<WsBase> session,
|
||||||
|
SubscriptionManager& manager)
|
||||||
|
{
|
||||||
|
for (auto const book : books)
|
||||||
|
{
|
||||||
|
manager.subBook(book, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
Result
|
Result
|
||||||
doSubscribe(Context const& context)
|
doSubscribe(Context const& context)
|
||||||
{
|
{
|
||||||
@@ -210,7 +245,6 @@ doSubscribe(Context const& context)
|
|||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains("accounts"))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!request.at("accounts").is_array())
|
if (!request.at("accounts").is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
@@ -226,12 +260,21 @@ doSubscribe(Context const& context)
|
|||||||
if (!request.at("accounts_proposed").is_array())
|
if (!request.at("accounts_proposed").is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts = request.at("accounts_proposed").as_array();
|
boost::json::array accounts =
|
||||||
|
request.at("accounts_proposed").as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if(status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
std::vector<ripple::Book> books;
|
||||||
|
if (request.contains("books"))
|
||||||
|
{
|
||||||
|
auto parsed = validateAndGetBooks(request);
|
||||||
|
if (auto status = std::get_if<Status>(&parsed))
|
||||||
|
return *status;
|
||||||
|
books = std::get<std::vector<ripple::Book>>(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
if (request.contains("streams"))
|
if (request.contains("streams"))
|
||||||
subscribeToStreams(request, context.session, *context.subscriptions);
|
subscribeToStreams(request, context.session, *context.subscriptions);
|
||||||
@@ -240,7 +283,11 @@ doSubscribe(Context const& context)
|
|||||||
subscribeToAccounts(request, context.session, *context.subscriptions);
|
subscribeToAccounts(request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains("accounts_proposed"))
|
||||||
subscribeToAccountsProposed(request, context.session, *context.subscriptions);
|
subscribeToAccountsProposed(
|
||||||
|
request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
|
if (request.contains("books"))
|
||||||
|
subscribeToBooks(books, context.session, *context.subscriptions);
|
||||||
|
|
||||||
boost::json::object response = {{"status", "success"}};
|
boost::json::object response = {{"status", "success"}};
|
||||||
return response;
|
return response;
|
||||||
@@ -251,8 +298,7 @@ doUnsubscribe(Context const& context)
|
|||||||
{
|
{
|
||||||
auto request = context.params;
|
auto request = context.params;
|
||||||
|
|
||||||
|
if (request.contains("streams"))
|
||||||
if (request.contains("streams"))
|
|
||||||
{
|
{
|
||||||
if (!request.at("streams").is_array())
|
if (!request.at("streams").is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "streamsNotArray"};
|
||||||
@@ -265,7 +311,6 @@ doUnsubscribe(Context const& context)
|
|||||||
|
|
||||||
if (request.contains("accounts"))
|
if (request.contains("accounts"))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!request.at("accounts").is_array())
|
if (!request.at("accounts").is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsNotArray"};
|
||||||
|
|
||||||
@@ -281,10 +326,11 @@ doUnsubscribe(Context const& context)
|
|||||||
if (!request.at("accounts_proposed").is_array())
|
if (!request.at("accounts_proposed").is_array())
|
||||||
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
return Status{Error::rpcINVALID_PARAMS, "accountsProposedNotArray"};
|
||||||
|
|
||||||
boost::json::array accounts = request.at("accounts_proposed").as_array();
|
boost::json::array accounts =
|
||||||
|
request.at("accounts_proposed").as_array();
|
||||||
auto status = validateAccounts(accounts);
|
auto status = validateAccounts(accounts);
|
||||||
|
|
||||||
if(status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,10 +341,11 @@ doUnsubscribe(Context const& context)
|
|||||||
unsubscribeToAccounts(request, context.session, *context.subscriptions);
|
unsubscribeToAccounts(request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
if (request.contains("accounts_proposed"))
|
if (request.contains("accounts_proposed"))
|
||||||
unsubscribeToAccountsProposed(request, context.session, *context.subscriptions);
|
unsubscribeToAccountsProposed(
|
||||||
|
request, context.session, *context.subscriptions);
|
||||||
|
|
||||||
boost::json::object response = {{"status", "success"}};
|
boost::json::object response = {{"status", "success"}};
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
|
|||||||
@@ -30,16 +30,17 @@ SubscriptionManager::pubLedger(
|
|||||||
pubMsg["ledger_hash"] = to_string(lgrInfo.hash);
|
pubMsg["ledger_hash"] = to_string(lgrInfo.hash);
|
||||||
pubMsg["ledger_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
pubMsg["ledger_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
||||||
|
|
||||||
pubMsg["fee_ref"] = toBoostJson(fees.units.jsonClipped());
|
pubMsg["fee_ref"] = RPC::toBoostJson(fees.units.jsonClipped());
|
||||||
pubMsg["fee_base"] = toBoostJson(fees.base.jsonClipped());
|
pubMsg["fee_base"] = RPC::toBoostJson(fees.base.jsonClipped());
|
||||||
pubMsg["reserve_base"] = toBoostJson(fees.accountReserve(0).jsonClipped());
|
pubMsg["reserve_base"] =
|
||||||
pubMsg["reserve_inc"] = toBoostJson(fees.increment.jsonClipped());
|
RPC::toBoostJson(fees.accountReserve(0).jsonClipped());
|
||||||
|
pubMsg["reserve_inc"] = RPC::toBoostJson(fees.increment.jsonClipped());
|
||||||
|
|
||||||
pubMsg["validated_ledgers"] = ledgerRange;
|
pubMsg["validated_ledgers"] = ledgerRange;
|
||||||
pubMsg["txn_count"] = txnCount;
|
pubMsg["txn_count"] = txnCount;
|
||||||
|
|
||||||
std::lock_guard lk(m_);
|
std::lock_guard lk(m_);
|
||||||
for (auto const& session: streamSubscribers_[Ledgers])
|
for (auto const& session : streamSubscribers_[Ledgers])
|
||||||
session->send(boost::json::serialize(pubMsg));
|
session->send(boost::json::serialize(pubMsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,18 +76,35 @@ SubscriptionManager::unsubAccount(
|
|||||||
accountSubscribers_[account].erase(session);
|
accountSubscribers_[account].erase(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubscriptionManager::subBook(
|
||||||
|
ripple::Book const& book,
|
||||||
|
std::shared_ptr<WsBase>& session)
|
||||||
|
{
|
||||||
|
std::lock_guard lk(m_);
|
||||||
|
bookSubscribers_[book].emplace(std::move(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SubscriptionManager::unsubBook(
|
||||||
|
ripple::Book const& book,
|
||||||
|
std::shared_ptr<WsBase>& session)
|
||||||
|
{
|
||||||
|
std::lock_guard lk(m_);
|
||||||
|
bookSubscribers_[book].erase(session);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SubscriptionManager::pubTransaction(
|
SubscriptionManager::pubTransaction(
|
||||||
Backend::TransactionAndMetadata const& blob,
|
Backend::TransactionAndMetadata const& blobs,
|
||||||
std::uint32_t seq)
|
std::uint32_t seq)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(m_);
|
std::lock_guard lk(m_);
|
||||||
|
|
||||||
auto [tx, meta] = deserializeTxPlusMeta(blob, seq);
|
auto [tx, meta] = RPC::deserializeTxPlusMeta(blobs, seq);
|
||||||
|
|
||||||
boost::json::object pubMsg;
|
boost::json::object pubMsg;
|
||||||
pubMsg["transaction"] = toJson(*tx);
|
pubMsg["transaction"] = RPC::toJson(*tx);
|
||||||
pubMsg["meta"] = toJson(*meta);
|
pubMsg["meta"] = RPC::toJson(*meta);
|
||||||
|
|
||||||
for (auto const& session : streamSubscribers_[Transactions])
|
for (auto const& session : streamSubscribers_[Transactions])
|
||||||
session->send(boost::json::serialize(pubMsg));
|
session->send(boost::json::serialize(pubMsg));
|
||||||
@@ -108,7 +126,7 @@ SubscriptionManager::forwardProposedTransaction(
|
|||||||
session->send(boost::json::serialize(response));
|
session->send(boost::json::serialize(response));
|
||||||
|
|
||||||
auto transaction = response.at("transaction").as_object();
|
auto transaction = response.at("transaction").as_object();
|
||||||
auto accounts = getAccountsFromTransaction(transaction);
|
auto accounts = RPC::getAccountsFromTransaction(transaction);
|
||||||
|
|
||||||
for (ripple::AccountID const& account : accounts)
|
for (ripple::AccountID const& account : accounts)
|
||||||
for (auto const& session : accountProposedSubscribers_[account])
|
for (auto const& session : accountProposedSubscribers_[account])
|
||||||
@@ -153,17 +171,17 @@ SubscriptionManager::clearSession(WsBase* s)
|
|||||||
std::lock_guard lk(m_);
|
std::lock_guard lk(m_);
|
||||||
|
|
||||||
// need the == operator. No-op delete
|
// need the == operator. No-op delete
|
||||||
std::shared_ptr<WsBase> targetSession(s, [](WsBase*){});
|
std::shared_ptr<WsBase> targetSession(s, [](WsBase*) {});
|
||||||
for(auto& stream : streamSubscribers_)
|
for (auto& stream : streamSubscribers_)
|
||||||
stream.erase(targetSession);
|
stream.erase(targetSession);
|
||||||
|
|
||||||
for(auto& [account, subscribers] : accountSubscribers_)
|
for (auto& [account, subscribers] : accountSubscribers_)
|
||||||
{
|
{
|
||||||
if (subscribers.find(targetSession) != subscribers.end())
|
if (subscribers.find(targetSession) != subscribers.end())
|
||||||
accountSubscribers_[account].erase(targetSession);
|
accountSubscribers_[account].erase(targetSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& [account, subscribers] : accountProposedSubscribers_)
|
for (auto& [account, subscribers] : accountProposedSubscribers_)
|
||||||
{
|
{
|
||||||
if (subscribers.find(targetSession) != subscribers.end())
|
if (subscribers.find(targetSession) != subscribers.end())
|
||||||
accountProposedSubscribers_[account].erase(targetSession);
|
accountProposedSubscribers_[account].erase(targetSession);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <backend/BackendInterface.h>
|
|
||||||
|
|
||||||
class WsBase;
|
class WsBase;
|
||||||
|
|
||||||
@@ -37,12 +36,12 @@ class SubscriptionManager
|
|||||||
|
|
||||||
finalEntry
|
finalEntry
|
||||||
};
|
};
|
||||||
|
|
||||||
std::mutex m_;
|
std::mutex m_;
|
||||||
std::array<subscriptions, finalEntry> streamSubscribers_;
|
std::array<subscriptions, finalEntry> streamSubscribers_;
|
||||||
std::unordered_map<ripple::AccountID, subscriptions> accountSubscribers_;
|
std::unordered_map<ripple::AccountID, subscriptions> accountSubscribers_;
|
||||||
std::unordered_map<ripple::AccountID, subscriptions>
|
std::unordered_map<ripple::AccountID, subscriptions>
|
||||||
accountProposedSubscribers_;
|
accountProposedSubscribers_;
|
||||||
|
std::unordered_map<ripple::Book, subscriptions> bookSubscribers_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<SubscriptionManager>
|
static std::shared_ptr<SubscriptionManager>
|
||||||
@@ -72,7 +71,7 @@ public:
|
|||||||
|
|
||||||
void
|
void
|
||||||
pubTransaction(
|
pubTransaction(
|
||||||
Backend::TransactionAndMetadata const& blob,
|
Backend::TransactionAndMetadata const& blobs,
|
||||||
std::uint32_t seq);
|
std::uint32_t seq);
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -85,6 +84,12 @@ public:
|
|||||||
ripple::AccountID const& account,
|
ripple::AccountID const& account,
|
||||||
std::shared_ptr<WsBase>& session);
|
std::shared_ptr<WsBase>& session);
|
||||||
|
|
||||||
|
void
|
||||||
|
subBook(ripple::Book const& book, std::shared_ptr<WsBase>& session);
|
||||||
|
|
||||||
|
void
|
||||||
|
unsubBook(ripple::Book const& book, std::shared_ptr<WsBase>& session);
|
||||||
|
|
||||||
void
|
void
|
||||||
forwardProposedTransaction(boost::json::object const& response);
|
forwardProposedTransaction(boost::json::object const& response);
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ TEST(BackendTest, Basic)
|
|||||||
return uint.fromVoid((void const*)bin.data());
|
return uint.fromVoid((void const*)bin.data());
|
||||||
};
|
};
|
||||||
auto ledgerInfoToBinaryString = [](auto const& info) {
|
auto ledgerInfoToBinaryString = [](auto const& info) {
|
||||||
auto blob = ledgerInfoToBlob(info);
|
auto blob = RPC::ledgerInfoToBlob(info);
|
||||||
std::string strBlob;
|
std::string strBlob;
|
||||||
for (auto c : blob)
|
for (auto c : blob)
|
||||||
{
|
{
|
||||||
@@ -105,7 +105,8 @@ TEST(BackendTest, Basic)
|
|||||||
auto retLgr = backend->fetchLedgerBySequence(lgrInfo.seq);
|
auto retLgr = backend->fetchLedgerBySequence(lgrInfo.seq);
|
||||||
EXPECT_TRUE(retLgr.has_value());
|
EXPECT_TRUE(retLgr.has_value());
|
||||||
EXPECT_EQ(retLgr->seq, lgrInfo.seq);
|
EXPECT_EQ(retLgr->seq, lgrInfo.seq);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(lgrInfo), ledgerInfoToBlob(*retLgr));
|
EXPECT_EQ(
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfo), RPC::ledgerInfoToBlob(*retLgr));
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
@@ -138,12 +139,20 @@ TEST(BackendTest, Basic)
|
|||||||
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
||||||
EXPECT_TRUE(retLgr.has_value());
|
EXPECT_TRUE(retLgr.has_value());
|
||||||
EXPECT_EQ(retLgr->seq, lgrInfoNext.seq);
|
EXPECT_EQ(retLgr->seq, lgrInfoNext.seq);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoNext));
|
EXPECT_EQ(
|
||||||
EXPECT_NE(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoOld));
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoNext));
|
||||||
|
EXPECT_NE(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoOld));
|
||||||
retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq - 1);
|
retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq - 1);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoOld));
|
EXPECT_EQ(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoOld));
|
||||||
|
|
||||||
EXPECT_NE(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoNext));
|
EXPECT_NE(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoNext));
|
||||||
retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq - 2);
|
retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq - 2);
|
||||||
EXPECT_FALSE(backend->fetchLedgerBySequence(lgrInfoNext.seq - 2)
|
EXPECT_FALSE(backend->fetchLedgerBySequence(lgrInfoNext.seq - 2)
|
||||||
.has_value());
|
.has_value());
|
||||||
@@ -265,7 +274,9 @@ TEST(BackendTest, Basic)
|
|||||||
EXPECT_EQ(rng->maxSequence, lgrInfoNext.seq);
|
EXPECT_EQ(rng->maxSequence, lgrInfoNext.seq);
|
||||||
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
||||||
EXPECT_TRUE(retLgr);
|
EXPECT_TRUE(retLgr);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoNext));
|
EXPECT_EQ(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoNext));
|
||||||
auto txns = backend->fetchAllTransactionsInLedger(lgrInfoNext.seq);
|
auto txns = backend->fetchAllTransactionsInLedger(lgrInfoNext.seq);
|
||||||
EXPECT_EQ(txns.size(), 1);
|
EXPECT_EQ(txns.size(), 1);
|
||||||
EXPECT_STREQ(
|
EXPECT_STREQ(
|
||||||
@@ -332,7 +343,9 @@ TEST(BackendTest, Basic)
|
|||||||
EXPECT_EQ(rng->maxSequence, lgrInfoNext.seq);
|
EXPECT_EQ(rng->maxSequence, lgrInfoNext.seq);
|
||||||
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
auto retLgr = backend->fetchLedgerBySequence(lgrInfoNext.seq);
|
||||||
EXPECT_TRUE(retLgr);
|
EXPECT_TRUE(retLgr);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfoNext));
|
EXPECT_EQ(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
RPC::ledgerInfoToBlob(lgrInfoNext));
|
||||||
auto txns = backend->fetchAllTransactionsInLedger(lgrInfoNext.seq);
|
auto txns = backend->fetchAllTransactionsInLedger(lgrInfoNext.seq);
|
||||||
EXPECT_EQ(txns.size(), 0);
|
EXPECT_EQ(txns.size(), 0);
|
||||||
|
|
||||||
@@ -466,9 +479,7 @@ TEST(BackendTest, Basic)
|
|||||||
if (isOffer(obj.data()))
|
if (isOffer(obj.data()))
|
||||||
bookDir = getBook(obj);
|
bookDir = getBook(obj);
|
||||||
backend->writeLedgerObject(
|
backend->writeLedgerObject(
|
||||||
std::move(key),
|
std::move(key), lgrInfo.seq, std::move(obj));
|
||||||
lgrInfo.seq,
|
|
||||||
std::move(obj));
|
|
||||||
}
|
}
|
||||||
backend->writeAccountTransactions(std::move(accountTx));
|
backend->writeAccountTransactions(std::move(accountTx));
|
||||||
|
|
||||||
@@ -486,10 +497,12 @@ TEST(BackendTest, Basic)
|
|||||||
EXPECT_GE(rng->maxSequence, seq);
|
EXPECT_GE(rng->maxSequence, seq);
|
||||||
auto retLgr = backend->fetchLedgerBySequence(seq);
|
auto retLgr = backend->fetchLedgerBySequence(seq);
|
||||||
EXPECT_TRUE(retLgr);
|
EXPECT_TRUE(retLgr);
|
||||||
EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfo));
|
EXPECT_EQ(
|
||||||
|
RPC::ledgerInfoToBlob(*retLgr), RPC::ledgerInfoToBlob(lgrInfo));
|
||||||
// retLgr = backend->fetchLedgerByHash(lgrInfo.hash);
|
// retLgr = backend->fetchLedgerByHash(lgrInfo.hash);
|
||||||
// EXPECT_TRUE(retLgr);
|
// EXPECT_TRUE(retLgr);
|
||||||
// EXPECT_EQ(ledgerInfoToBlob(*retLgr), ledgerInfoToBlob(lgrInfo));
|
// EXPECT_EQ(RPC::ledgerInfoToBlob(*retLgr),
|
||||||
|
// RPC::ledgerInfoToBlob(lgrInfo));
|
||||||
auto retTxns = backend->fetchAllTransactionsInLedger(seq);
|
auto retTxns = backend->fetchAllTransactionsInLedger(seq);
|
||||||
for (auto [hash, txn, meta] : txns)
|
for (auto [hash, txn, meta] : txns)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user