1#include <xrpld/app/main/GRPCServer.h>
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/rpc/Context.h>
6#include <xrpld/rpc/GRPCHandlers.h>
7#include <xrpld/rpc/Role.h>
8#include <xrpld/rpc/detail/Handler.h>
10#include <xrpl/basics/FileUtilities.h>
11#include <xrpl/basics/Log.h>
12#include <xrpl/basics/StringUtilities.h>
13#include <xrpl/basics/contract.h>
14#include <xrpl/beast/core/CurrentThreadName.h>
15#include <xrpl/beast/net/IPAddressConversion.h>
16#include <xrpl/beast/net/IPEndpoint.h>
17#include <xrpl/beast/utility/instrumentation.h>
18#include <xrpl/config/BasicConfig.h>
19#include <xrpl/config/Constants.h>
20#include <xrpl/core/Job.h>
21#include <xrpl/core/JobQueue.h>
22#include <xrpl/protocol/ErrorCodes.h>
23#include <xrpl/resource/Charge.h>
24#include <xrpl/resource/Consumer.h>
25#include <xrpl/resource/Fees.h>
26#include <xrpl/server/InfoSub.h>
28#include <boost/asio/ip/address.hpp>
29#include <boost/asio/ip/tcp.hpp>
30#include <boost/icl/interval_set.hpp>
32#include <grpc/grpc_security_constants.h>
33#include <grpcpp/completion_queue.h>
34#include <grpcpp/security/server_credentials.h>
35#include <grpcpp/server_builder.h>
36#include <grpcpp/support/status.h>
37#include <org/xrpl/rpc/v1/get_ledger.pb.h>
38#include <org/xrpl/rpc/v1/get_ledger_data.pb.h>
39#include <org/xrpl/rpc/v1/get_ledger_diff.pb.h>
40#include <org/xrpl/rpc/v1/get_ledger_entry.pb.h>
41#include <org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
62std::optional<boost::asio::ip::tcp::endpoint>
63getEndpoint(std::string
const& peer)
69 std::string peerClean(peer);
72 peerClean = peer.
substr(first + 1);
75 std::optional<beast::ip::Endpoint> endpoint =
80 catch (std::exception
const&)
88template <
class Request,
class Response>
90 org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService& service,
91 grpc::ServerCompletionQueue& cq,
116template <
class Request,
class Response>
132template <
class Request,
class Response>
150 auto coro =
app_.getJobQueue().postCoro(
152 thisShared->process(coro);
158 grpc::Status
const status{grpc::StatusCode::INTERNAL,
"Job Queue is already stopped"};
163template <
class Request,
class Response>
173 grpc::Status
const status{
174 grpc::StatusCode::RESOURCE_EXHAUSTED,
"usage balance exceeds threshold"};
180 usage.charge(loadType);
185 toLog <<
"role = " << (int)role;
187 toLog <<
" address = ";
189 toLog << clientIp.value();
193 toLog << user.value();
196 JLOG(
app_.getJournal(
"GRPCServer::Calldata").debug()) << toLog.
str();
200 {
app_.getJournal(
"gRPCServer"),
204 app_.getLedgerMaster(),
218 grpc::Status
const status{
219 grpc::StatusCode::FAILED_PRECONDITION, errorInfo.
message.
cStr()};
232 grpc::Status
const status{grpc::StatusCode::INTERNAL, ex.
what()};
237template <
class Request,
class Response>
244template <
class Request,
class Response>
251template <
class Request,
class Response>
263template <
class Request,
class Response>
267 if (
auto descriptor = Request::GetDescriptor()->FindFieldByName(
"user"))
278template <
class Request,
class Response>
284 return endpoint->address();
288template <
class Request,
class Response>
292 return xrpl::getEndpoint(
ctx_.peer());
295template <
class Request,
class Response>
313template <
class Request,
class Response>
319 if (
auto descriptor = Response::GetDescriptor()->FindFieldByName(
"is_unlimited"))
321 Response::GetReflection()->SetBool(&response, descriptor,
true);
326template <
class Request,
class Response>
342 Section const& section = app_.config().section(Sections::kPortGrpc);
344 auto const optIp = section.get(Keys::kIp);
348 auto const optPort = section.get(Keys::kPort);
353 boost::asio::ip::tcp::endpoint const endpoint(
354 boost::asio::ip::make_address(*optIp), std::stoi(*optPort));
356 std::stringstream ss;
358 serverAddress_ = ss.str();
362 JLOG(
journal_.error()) <<
"Error setting grpc server address";
367 if (optSecureGateway)
376 auto const addr = boost::asio::ip::make_address(ip);
378 if (addr.is_unspecified())
380 JLOG(
journal_.error()) <<
"Can't pass unspecified IP in "
381 <<
"secure_gateway section of port_grpc";
390 JLOG(
journal_.error()) <<
"Error parsing secure gateway IPs for grpc server";
404 if (!sslCertPath_.has_value() || !sslKeyPath_.has_value())
406 JLOG(journal_.error())
407 <<
"Both ssl_cert and ssl_key must be specified for gRPC TLS";
408 Throw<std::runtime_error>(
"Incomplete TLS configuration for gRPC");
415 if (sslCertChainPath_.has_value() &&
416 (!sslCertPath_.has_value() || !sslKeyPath_.has_value()))
418 JLOG(journal_.error())
419 <<
"ssl_cert_chain specified for gRPC without both ssl_cert and ssl_key; "
420 <<
"this is an invalid TLS configuration";
421 Throw<std::runtime_error>(
422 "Invalid gRPC TLS configuration: ssl_cert_chain requires both ssl_cert and "
428 if (sslClientCAPath_.has_value() && (!sslCertPath_.has_value() || !sslKeyPath_.has_value()))
430 JLOG(journal_.error())
431 <<
"ssl_client_ca specified for gRPC without both ssl_cert and ssl_key; "
432 <<
"this is an invalid TLS configuration";
433 Throw<std::runtime_error>(
434 "Invalid gRPC TLS configuration: ssl_client_ca requires both ssl_cert and ssl_key");
442 JLOG(
journal_.debug()) <<
"Shutting down";
452 JLOG(
journal_.debug()) <<
"Server has been shutdown";
458 JLOG(
journal_.debug()) <<
"Completion Queue has been shutdown";
472 BOOST_ASSERT(it != requests.
end());
473 it->swap(requests.
back());
493 while (
cq_->Next(&tag, &ok))
496 JLOG(
journal_.trace()) <<
"Processing CallData object."
497 <<
" ptr = " << ptr <<
" ok = " << ok;
501 JLOG(
journal_.debug()) <<
"Request listener cancelled. "
502 <<
"Destroying object";
507 if (!ptr->isFinished())
509 JLOG(
journal_.debug()) <<
"Received new request. Processing";
512 auto cloned = ptr->clone();
519 JLOG(
journal_.debug()) <<
"Sent response. Destroying object";
524 JLOG(
journal_.debug()) <<
"Completion Queue drained";
534 auto addToRequests = [&requests](
auto callData) { requests.
push_back(std::move(callData)); };
545 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedger,
547 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedger,
548 Condition::NoCondition,
554 org::xrpl::rpc::v1::GetLedgerDataRequest,
555 org::xrpl::rpc::v1::GetLedgerDataResponse>;
562 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerData,
564 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerData,
565 Condition::NoCondition,
571 org::xrpl::rpc::v1::GetLedgerDiffRequest,
572 org::xrpl::rpc::v1::GetLedgerDiffResponse>;
579 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerDiff,
581 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerDiff,
582 Condition::NoCondition,
588 org::xrpl::rpc::v1::GetLedgerEntryRequest,
589 org::xrpl::rpc::v1::GetLedgerEntryResponse>;
596 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerEntry,
598 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerEntry,
599 Condition::NoCondition,
611 JLOG(
journal_.info()) <<
"Configuring gRPC server without TLS";
612 return grpc::InsecureServerCredentials();
615 JLOG(
journal_.info()) <<
"Configuring gRPC server with TLS";
620 grpc::SslServerCredentialsOptions sslOpts;
621 grpc::SslServerCredentialsOptions::PemKeyCertPair keyCertPair;
639 keyCertPair.private_key = keyContents;
667 if (clientCAContents.empty())
671 <<
" - failed to configure mutual TLS";
675 sslOpts.pem_root_certs = clientCAContents;
676 sslOpts.client_certificate_request =
677 GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
678 JLOG(
journal_.info()) <<
"gRPC mutual TLS enabled - client certificates will be "
679 "required and verified";
683 keyCertPair.cert_chain = certContents;
684 if (!certChainContents.
empty())
686 keyCertPair.cert_chain +=
'\n' + certChainContents;
687 JLOG(
journal_.info()) <<
"gRPC server certificate chain configured with "
688 "intermediate CA certificates";
691 sslOpts.pem_key_cert_pairs.
push_back(keyCertPair);
693 JLOG(
journal_.info()) <<
"gRPC TLS credentials configured successfully";
694 return grpc::SslServerCredentials(sslOpts);
698 JLOG(
journal_.error()) <<
"Exception while configuring gRPC TLS: "
718 tlsMode =
"with mutual TLS (mTLS)";
722 tlsMode =
"with TLS";
728 grpc::ServerBuilder builder;
736 <<
" (TLS mode: " << tlsMode
737 <<
") - server will not start";
750 cq_ = builder.AddCompletionQueue();
753 server_ = builder.BuildAndStart();
763 <<
"Failed to start gRPC server at " <<
serverAddress_ <<
" (TLS mode: " << tlsMode
764 <<
"); Possible causes: address already in use, invalid address format, or permission "
771boost::asio::ip::tcp::endpoint
775 return boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(addr),
serverPort_);
787 this->
impl_.handleRpcs();
806 XRPL_ASSERT(!
running_,
"xrpl::GRPCServer::~GRPCServer : is not running");
809boost::asio::ip::tcp::endpoint
812 return impl_.getEndpoint();
static std::optional< Endpoint > fromStringChecked(std::string const &s)
Create an Endpoint from a string.
constexpr char const * cStr() const
Forward< Request, Response > forward_
resource::Charge loadType_
std::vector< boost::asio::ip::address > const & secureGatewayIPs_
std::optional< boost::asio::ip::address > getClientIpAddress()
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService & service_
bool isFinished() override
Handler< Request, Response > handler_
std::optional< std::string > getUser()
BindListener< Request, Response > bindListener_
Role getRole(bool isUnlimited)
resource::Consumer getUsage()
std::optional< boost::asio::ip::tcp::endpoint > getClientEndpoint()
std::atomic_bool finished_
resource::Charge getLoadType()
CallData(org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService &service, grpc::ServerCompletionQueue &cq, Application &app, BindListener< Request, Response > bindListener, Handler< Request, Response > handler, Forward< Request, Response > forward, rpc::Condition requiredCondition, resource::Charge loadType, std::vector< boost::asio::ip::address > const &secureGatewayIPs)
grpc::ServerAsyncResponseWriter< Response > responder_
std::shared_ptr< Processor > clone() override
void setIsUnlimited(Response &response, bool isUnlimited)
grpc::ServerCompletionQueue & cq_
rpc::Condition requiredCondition_
std::uint16_t serverPort_
std::optional< std::string > sslKeyPath_
std::optional< std::string > sslCertPath_
std::function< grpc::Status( org::xrpl::rpc::v1::XRPLedgerAPIService::Stub *, grpc::ClientContext *, Request, Response *)> Forward
std::vector< boost::asio::ip::address > secureGatewayIPs_
std::optional< std::string > sslCertChainPath_
std::unique_ptr< grpc::ServerCompletionQueue > cq_
std::vector< std::shared_ptr< Processor > > setupListeners()
std::optional< std::string > sslClientCAPath_
GRPCServerImpl(Application &app)
std::function< std::pair< Response, grpc::Status >(rpc::GRPCContext< Request > &)> Handler
std::unique_ptr< grpc::Server > server_
boost::asio::ip::tcp::endpoint getEndpoint() const
std::string serverAddress_
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService service_
std::function< void( org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService &, grpc::ServerContext *, Request *, grpc::ServerAsyncResponseWriter< Response > *, grpc::CompletionQueue *, grpc::ServerCompletionQueue *, void *)> BindListener
static constexpr unsigned kApiVersion
std::shared_ptr< grpc::ServerCredentials > createServerCredentials()
boost::asio::ip::tcp::endpoint getEndpoint() const
std::shared_ptr< InfoSub > pointer
An endpoint that consumes resources.
T find_first_of(T... args)
T find_last_of(T... args)
boost::asio::ip::tcp::endpoint toAsioEndpoint(Endpoint const &endpoint)
Convert to asio::ip::tcp::endpoint.
Endpoint fromAsio(boost::asio::ip::address const &address)
Convert to Endpoint.
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
Charge const kFeeMediumBurdenRpc
ErrorCodeI conditionMet(Condition conditionRequired, T &context)
ErrorInfo const & getErrorInfo(ErrorCodeI code)
Returns an ErrorInfo that reflects the error code.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::pair< org::xrpl::rpc::v1::GetLedgerDataResponse, grpc::Status > doLedgerDataGrpc(rpc::GRPCContext< org::xrpl::rpc::v1::GetLedgerDataRequest > &context)
std::string trimWhitespace(std::string str)
Remove leading and trailing ASCII whitespace.
std::pair< org::xrpl::rpc::v1::GetLedgerDiffResponse, grpc::Status > doLedgerDiffGrpc(rpc::GRPCContext< org::xrpl::rpc::v1::GetLedgerDiffRequest > &context)
Role
Indicates the level of administrative permission to grant.
std::string getFileContents(std::error_code &ec, std::filesystem::path const &sourcePath, std::optional< std::size_t > maxSize=std::nullopt)
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(rpc::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(rpc::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
T shared_from_this(T... args)
static constexpr auto kSslCertChain
static constexpr auto kSslKey
static constexpr auto kSslClientCa
static constexpr auto kSslCert
static constexpr auto kSecureGateway
static constexpr auto kPortGrpc
Maps an rpc error code to its token, default message, and HTTP status.
json::StaticString message