1#ifndef XRPL_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED 
    2#define XRPL_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED 
    4#include <test/jtx/envconfig.h> 
    6#include <xrpl/basics/base64.h> 
    7#include <xrpl/basics/random.h> 
    8#include <xrpl/basics/strHex.h> 
    9#include <xrpl/protocol/PublicKey.h> 
   10#include <xrpl/protocol/SecretKey.h> 
   11#include <xrpl/protocol/Sign.h> 
   13#include <boost/algorithm/string/predicate.hpp> 
   14#include <boost/asio.hpp> 
   15#include <boost/asio/ip/tcp.hpp> 
   16#include <boost/asio/ssl/stream.hpp> 
   17#include <boost/beast/core/flat_buffer.hpp> 
   18#include <boost/beast/http.hpp> 
   19#include <boost/beast/ssl.hpp> 
   20#include <boost/beast/version.hpp> 
   21#include <boost/lexical_cast.hpp> 
   37        boost::beast::http::request<boost::beast::http::string_body>;
 
   39        boost::beast::http::response<boost::beast::http::string_body>;
 
   54    boost::asio::ssl::context 
sslCtx_{boost::asio::ssl::context::tlsv12};
 
   65            [](
std::size_t, boost::asio::ssl::context_base::password_purpose) {
 
   70            boost::asio::ssl::context::default_workarounds |
 
   71            boost::asio::ssl::context::no_sslv2 |
 
   72            boost::asio::ssl::context::single_dh_use);
 
   78            boost::asio::buffer(
key().
data(), 
key().size()),
 
   79            boost::asio::ssl::context::file_format::pem);
 
 
  113        st[sfSequence] = 
seq;
 
  114        st[sfPublicKey] = pk;
 
  115        st[sfSigningPubKey] = spk;
 
 
  156        boost::asio::io_context& ioc,
 
  164        bool immediateStart = 
true,
 
  167        , 
ep_{
boost::asio::ip::make_address(
 
  181        blobInfo.
reserve(futures.size() + 1);
 
  189            for (
auto const& val : validators)
 
  191                data += 
"{\"validation_public_key\":\"" +
 
  192                    strHex(val.masterPublic) + 
"\",\"manifest\":\"" +
 
  193                    val.manifest + 
"\"},";
 
  205            l << 
"{\"blob\":\"" << blob << 
"\"" << 
",\"signature\":\"" << 
sig 
  206              << 
"\"" << 
",\"manifest\":\"" << 
manifest << 
"\"" 
  207              << 
",\"refresh_interval\": " << interval
 
  208              << 
",\"version\":" << version << 
'}';
 
  211        for (
auto const& future : futures)
 
  221            for (
auto const& val : validators)
 
  223                data += 
"{\"validation_public_key\":\"" +
 
  224                    strHex(val.masterPublic) + 
"\",\"manifest\":\"" +
 
  225                    val.manifest + 
"\"},";
 
  239            for (
auto const& info : blobInfo)
 
  241                l << 
"{\"blob\":\"" << info.blob << 
"\"" << 
",\"signature\":\"" 
  242                  << info.signature << 
"\"},";
 
  247            l << 
"{\"blobs_v2\": [ " << blobs << 
"],\"manifest\":\"" << 
manifest 
  248              << 
"\"" << 
",\"refresh_interval\": " << interval
 
  249              << 
",\"version\":" << (version + 1) << 
'}';
 
 
  266            boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
 
  268        acceptor_.listen(boost::asio::socket_base::max_listen_connections);
 
  273                if (
auto p = wp.lock())
 
 
  372-----BEGIN CERTIFICATE----- 
  373MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL 
 
  374MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw 
  375bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz 
  376NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh 
  377bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs 
  378ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B 
  379AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ 
  380qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs 
 
  381+T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f 
  382n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3 
  383zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q 
  384S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV 
  385HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL 
  386BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx 
  387hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L 
 
  388nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx 
  389KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT 
  390GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD 
  391X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80= 
  392-----END CERTIFICATE----- 
 
  401-----BEGIN RSA PRIVATE KEY----- 
  402MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc 
  403TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc 
  404/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX 
  4059LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx 
  406R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh 
  407wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+ 
  408kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C 
  409t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK 
  410CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf 
  411K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S 
  412UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F 
  413Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP 
  414pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy 
  415dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy 
  416aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h 
 
  417YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3 
  418Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv 
  4198Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt 
  420N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7 
  421KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z 
  422gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0 
 
  4234PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/ 
 
  424uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD 
  425cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+ 
  426ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw== 
  427-----END RSA PRIVATE KEY----- 
  436-----BEGIN CERTIFICATE----- 
  437MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL 
  438BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n 
  439ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt 
  440cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV 
  441BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV 
  442BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi 
 
  443MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+ 
  444aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq 
  445V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj 
  446uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv 
  447v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo 
  448D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB 
  449AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW 
  450gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 
  451DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q 
  452xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f 
  453c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp 
  4548+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+ 
  455mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5 
  4567X1e3FekJlaD02EBEhtkXURIxogOQALdFncj 
  457-----END CERTIFICATE----- 
  466-----BEGIN DH PARAMETERS----- 
  467MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC 
  468sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d 
  469BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL 
  470ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB 
  471xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt 
  472/8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg== 
  473-----END DH PARAMETERS----- 
  482        TrustedPublisherServer& 
self;
 
  484        boost::asio::executor_work_guard<boost::asio::executor> 
work;
 
  489            TrustedPublisherServer& self_,
 
  519                if (
auto p = wp.lock())
 
  529        using namespace boost::beast;
 
  541            ssl_stream->handshake(ssl::stream_base::server, ec);
 
  553                    http::read(*ssl_stream, sb, req, ec);
 
  555                    http::read(sock, sb, req, ec);
 
  561                res.insert(
"Server", 
"TrustedPublisherServer");
 
  562                res.version(req.version());
 
  563                res.keep_alive(req.keep_alive());
 
  566                if (boost::starts_with(path, 
"/validators2"))
 
  568                    res.result(http::status::ok);
 
  569                    res.insert(
"Content-Type", 
"application/json");
 
  570                    if (path == 
"/validators2/bad")
 
  571                        res.body() = 
"{ 'bad': \"2']";
 
  572                    else if (path == 
"/validators2/missing")
 
  573                        res.body() = 
"{\"version\": 2}";
 
  577                        constexpr char const* refreshPrefix =
 
  578                            "/validators2/refresh/";
 
  579                        if (boost::starts_with(path, refreshPrefix))
 
  580                            refresh = boost::lexical_cast<unsigned int>(
 
  581                                path.substr(
strlen(refreshPrefix)));
 
  585                else if (boost::starts_with(path, 
"/validators"))
 
  587                    res.result(http::status::ok);
 
  588                    res.insert(
"Content-Type", 
"application/json");
 
  589                    if (path == 
"/validators/bad")
 
  590                        res.body() = 
"{ 'bad': \"1']";
 
  591                    else if (path == 
"/validators/missing")
 
  592                        res.body() = 
"{\"version\": 1}";
 
  596                        constexpr char const* refreshPrefix =
 
  597                            "/validators/refresh/";
 
  598                        if (boost::starts_with(path, refreshPrefix))
 
  599                            refresh = boost::lexical_cast<unsigned int>(
 
  600                                path.substr(
strlen(refreshPrefix)));
 
  604                else if (boost::starts_with(path, 
"/textfile"))
 
  607                    res.result(http::status::ok);
 
  608                    res.insert(
"Content-Type", 
"text/example");
 
  611                        boost::starts_with(path, 
"/textfile/huge")
 
 
  614                    res.content_length(cl);
 
  615                    if (req.method() == http::verb::get)
 
  618                        for (
auto i = 0; i < 1024; ++i)
 
  619                            body << 
static_cast<char>(rand_int<short>(32, 126)),
 
  620                                res.body() = body.
str();
 
  623                else if (boost::starts_with(path, 
"/sleep/"))
 
  625                    auto const sleep_sec =
 
  626                        boost::lexical_cast<unsigned int>(path.substr(7));
 
  630                else if (boost::starts_with(path, 
"/redirect"))
 
  632                    if (boost::ends_with(path, 
"/301"))
 
  633                        res.result(http::status::moved_permanently);
 
  634                    else if (boost::ends_with(path, 
"/302"))
 
  635                        res.result(http::status::found);
 
  636                    else if (boost::ends_with(path, 
"/307"))
 
  637                        res.result(http::status::temporary_redirect);
 
  638                    else if (boost::ends_with(path, 
"/308"))
 
  639                        res.result(http::status::permanent_redirect);
 
  642                    if (boost::starts_with(path, 
"/redirect_to/"))
 
  644                        location << path.substr(13);
 
  646                    else if (!boost::starts_with(path, 
"/redirect_nolo"))
 
  649                            << (ssl ? 
"https://" : 
"http://")
 
  651                            << (boost::starts_with(path, 
"/redirect_forever/")
 
  655                    if (!location.
str().empty())
 
  656                        res.insert(
"Location", location.
str());
 
  661                    res.result(boost::beast::http::status::not_found);
 
  662                    res.insert(
"Content-Type", 
"text/html");
 
  669                    res.prepare_payload();
 
  674                res.result(boost::beast::http::status::internal_server_error);
 
  675                res.version(req.version());
 
  676                res.
insert(
"Server", 
"TrustedPublisherServer");
 
  677                res.insert(
"Content-Type", 
"text/html");
 
  680                res.prepare_payload();
 
  684                write(*ssl_stream, res, ec);
 
  686                write(sock, res, ec);
 
  688            if (ec || req.need_eof())
 
  694            ssl_stream->shutdown(ec);
 
 
  700    boost::asio::io_context& ioc,
 
  707    bool immediateStart = 
true,
 
  711        ioc, validators, validUntil, futures, useSSL, version, sequence);
 
 
void add(Serializer &s) const override
 
std::size_t size() const noexcept
 
void const * data() const noexcept
 
boost::beast::http::request< boost::beast::http::string_body > req_type
 
boost::asio::ip::address address_type
 
static std::string const & key()
 
void do_peer(int id, socket_type &&s, bool ssl)
 
static std::string const & cert()
 
TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
 
PublicKey publisherPublic_
 
boost::asio::ip::tcp::socket socket_type
 
void load_server_certificate()
 
boost::beast::http::response< boost::beast::http::string_body > resp_type
 
void on_accept(error_code ec)
 
static std::string const & ca_cert()
 
std::function< std::string(int)> getList2_
 
PublicKey const & publisherPublic() const
 
~TrustedPublisherServer()
 
endpoint_type local_endpoint() const
 
static std::string const & dh()
 
boost::asio::ip::tcp::acceptor acceptor_
 
boost::system::error_code error_code
 
std::function< std::string(int)> getList_
 
boost::asio::ssl::context sslCtx_
 
SecretKey publisherSecret_
 
boost::asio::ip::tcp::endpoint endpoint_type
 
static Validator randomValidator()
 
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
 
Set the regular signature on a JTx.
 
T emplace_back(T... args)
 
void write(nudb::detail::ostream &os, std::size_t t)
 
auto const data
General field definitions, or fields used in multiple transaction namespaces.
 
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
 
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
 
char const * getEnvLocalhostAddr()
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
 
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
 
std::string strHex(FwdIt begin, FwdIt end)
 
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
 
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
 
std::string base64_encode(std::uint8_t const *data, std::size_t len)
 
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
 
T shared_from_this(T... args)
 
BlobInfo(std::string b, std::string s)
 
TrustedPublisherServer & self
 
boost::asio::executor_work_guard< boost::asio::executor > work
 
lambda(int id_, TrustedPublisherServer &self_, socket_type &&sock_, bool ssl_)
 
Set the sequence number on a JTx.
 
T time_since_epoch(T... args)