rippled
Loading...
Searching...
No Matches
TrustedPublisherServer.h
1#pragma once
2
3#include <test/jtx/envconfig.h>
4
5#include <xrpl/basics/base64.h>
6#include <xrpl/basics/random.h>
7#include <xrpl/basics/strHex.h>
8#include <xrpl/protocol/PublicKey.h>
9#include <xrpl/protocol/SecretKey.h>
10#include <xrpl/protocol/Sign.h>
11
12#include <boost/algorithm/string/predicate.hpp>
13#include <boost/asio.hpp>
14#include <boost/asio/ip/tcp.hpp>
15#include <boost/asio/ssl/stream.hpp>
16#include <boost/beast/core/flat_buffer.hpp>
17#include <boost/beast/http.hpp>
18#include <boost/beast/ssl.hpp>
19#include <boost/beast/version.hpp>
20#include <boost/lexical_cast.hpp>
21
22#include <memory>
23#include <thread>
24
25namespace xrpl {
26namespace test {
27
28class TrustedPublisherServer : public std::enable_shared_from_this<TrustedPublisherServer>
29{
30 using endpoint_type = boost::asio::ip::tcp::endpoint;
31 using address_type = boost::asio::ip::address;
32 using socket_type = boost::asio::ip::tcp::socket;
33
34 using req_type = boost::beast::http::request<boost::beast::http::string_body>;
35 using resp_type = boost::beast::http::response<boost::beast::http::string_body>;
36 using error_code = boost::system::error_code;
37
40 boost::asio::ip::tcp::acceptor acceptor_;
41 // Generates a version 1 validator list, using the int parameter as the
42 // actual version.
44 // Generates a version 2 validator list, using the int parameter as the
45 // actual version.
47
48 // The SSL context is required, and holds certificates
49 bool useSSL_;
50 boost::asio::ssl::context sslCtx_{boost::asio::ssl::context::tlsv12};
51
54
55 // Load a signed certificate into the ssl context, and configure
56 // the context for use with a server.
57 inline void
59 {
60 sslCtx_.set_password_callback(
61 [](std::size_t, boost::asio::ssl::context_base::password_purpose) { return "test"; });
62
63 sslCtx_.set_options(
64 boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 |
65 boost::asio::ssl::context::single_dh_use);
66
67 sslCtx_.use_certificate_chain(boost::asio::buffer(cert().data(), cert().size()));
68
69 sslCtx_.use_private_key(
70 boost::asio::buffer(key().data(), key().size()), boost::asio::ssl::context::file_format::pem);
71
72 sslCtx_.use_tmp_dh(boost::asio::buffer(dh().data(), dh().size()));
73 }
74
75 struct BlobInfo
76 {
78 {
79 }
80
81 // base-64 encoded JSON containing the validator list.
83 // hex-encoded signature of the blob using the publisher's signing key
85 };
86
87public:
94
95 static std::string
96 makeManifestString(PublicKey const& pk, SecretKey const& sk, PublicKey const& spk, SecretKey const& ssk, int seq)
97 {
99 st[sfSequence] = seq;
100 st[sfPublicKey] = pk;
101 st[sfSigningPubKey] = spk;
102
103 sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
104 sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature);
105
106 Serializer s;
107 st.add(s);
108
109 return base64_encode(std::string(static_cast<char const*>(s.data()), s.size()));
110 }
111
112 static Validator
114 {
115 auto const secret = randomSecretKey();
116 auto const masterPublic = derivePublicKey(KeyType::ed25519, secret);
117 auto const signingKeys = randomKeyPair(KeyType::secp256k1);
118 return {
119 masterPublic,
120 signingKeys.first,
121 makeManifestString(masterPublic, secret, signingKeys.first, signingKeys.second, 1)};
122 }
123
124 // TrustedPublisherServer must be accessed through a shared_ptr.
125 // This constructor is only public so std::make_shared has access.
126 // The function`make_TrustedPublisherServer` should be used to create
127 // instances.
128 // The `futures` member is expected to be structured as
129 // effective / expiration time point pairs for use in version 2 UNLs
131 boost::asio::io_context& ioc,
132 std::vector<Validator> const& validators,
133 NetClock::time_point validUntil,
135 bool useSSL = false,
136 int version = 1,
137 bool immediateStart = true,
138 int sequence = 1)
139 : sock_{ioc}
140 , ep_{boost::asio::ip::make_address(xrpl::test::getEnvLocalhostAddr()),
141 // 0 means let OS pick the port based on what's available
142 0}
143 , acceptor_{ioc}
144 , useSSL_{useSSL}
147 {
148 auto const keys = randomKeyPair(KeyType::secp256k1);
149 auto const manifest = makeManifestString(publisherPublic_, publisherSecret_, keys.first, keys.second, 1);
150
151 std::vector<BlobInfo> blobInfo;
152 blobInfo.reserve(futures.size() + 1);
153 auto const [data, blob] = [&]() -> std::pair<std::string, std::string> {
154 // Builds the validator list, then encodes it into a blob.
155 std::string data = "{\"sequence\":" + std::to_string(sequence) +
156 ",\"expiration\":" + std::to_string(validUntil.time_since_epoch().count()) + ",\"validators\":[";
157
158 for (auto const& val : validators)
159 {
160 data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + "\",\"manifest\":\"" +
161 val.manifest + "\"},";
162 }
163 data.pop_back();
164 data += "]}";
166 return std::make_pair(data, blob);
167 }();
168 auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data)));
169 blobInfo.emplace_back(blob, sig);
170 getList_ = [blob = blob, sig, manifest, version](int interval) {
171 // Build the contents of a version 1 format UNL file
173 l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig << "\"" << ",\"manifest\":\"" << manifest
174 << "\""
175 << ",\"refresh_interval\": " << interval << ",\"version\":" << version << '}';
176 return l.str();
177 };
178 for (auto const& future : futures)
179 {
180 std::string data = "{\"sequence\":" + std::to_string(++sequence) +
181 ",\"effective\":" + std::to_string(future.first.time_since_epoch().count()) +
182 ",\"expiration\":" + std::to_string(future.second.time_since_epoch().count()) + ",\"validators\":[";
183
184 // Use the same set of validators for simplicity
185 for (auto const& val : validators)
186 {
187 data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) + "\",\"manifest\":\"" +
188 val.manifest + "\"},";
189 }
190 data.pop_back();
191 data += "]}";
193 auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data)));
194 blobInfo.emplace_back(blob, sig);
195 }
196 getList2_ = [blobInfo, manifest, version](int interval) {
197 // Build the contents of a version 2 format UNL file
198 // Use `version + 1` to get 2 for most tests, but have
199 // a "bad" version number for tests that provide an override.
201 for (auto const& info : blobInfo)
202 {
203 l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\"" << info.signature << "\"},";
204 }
205 std::string blobs = l.str();
206 blobs.pop_back();
207 l.str(std::string());
208 l << "{\"blobs_v2\": [ " << blobs << "],\"manifest\":\"" << manifest << "\""
209 << ",\"refresh_interval\": " << interval << ",\"version\":" << (version + 1) << '}';
210 return l.str();
211 };
212
213 if (useSSL_)
214 {
215 // This holds the self-signed certificate used by the server
217 }
218 }
219
220 void
222 {
223 error_code ec;
224 acceptor_.open(ep_.protocol());
225 acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
226 acceptor_.bind(ep_);
227 acceptor_.listen(boost::asio::socket_base::max_listen_connections);
229 if (auto p = wp.lock())
230 {
231 p->on_accept(ec);
232 }
233 });
234 }
235
236 void
238 {
239 error_code ec;
240 acceptor_.close(ec);
241 // TODO consider making this join
242 // any running do_peer threads
243 }
244
246 {
247 stop();
248 }
249
252 {
253 return acceptor_.local_endpoint();
254 }
255
256 PublicKey const&
258 {
259 return publisherPublic_;
260 }
261
262 /* CA/self-signed certs :
263 *
264 * The following three methods return certs/keys used by
265 * server and/or client to do the SSL handshake. These strings
266 * were generated using the script below. The server key and cert
267 * are used to configure the server (see load_server_certificate
268 * above). The ca.crt should be used to configure the client
269 * when ssl verification is enabled.
270 *
271 * note:
272 * cert() ==> server.crt
273 * key() ==> server.key
274 * ca_cert() ==> ca.crt
275 * dh() ==> dh.pem
276 ```
277 #!/usr/bin/env bash
278
279 mkdir -p /tmp/__certs__
280 pushd /tmp/__certs__
281 rm *.crt *.key *.pem
282
283 # generate CA
284 openssl genrsa -out ca.key 2048
285 openssl req -new -x509 -nodes -days 10000 -key ca.key -out ca.crt \
286 -subj "/C=US/ST=CA/L=Los
287 Angeles/O=rippled-unit-tests/CN=example.com" # generate private cert
288 openssl genrsa -out server.key 2048
289 # Generate certificate signing request
290 # since our unit tests can run in either ipv4 or ipv6 mode,
291 # we need to use extensions (subjectAltName) so that we can
292 # associate both ipv4 and ipv6 localhost addresses with this cert
293 cat >"extras.cnf" <<EOF
294 [req]
295 req_extensions = v3_req
296 distinguished_name = req_distinguished_name
297
298 [req_distinguished_name]
299
300 [v3_req]
301 subjectAltName = @alt_names
302
303 [alt_names]
304 DNS.1 = localhost
305 IP.1 = ::1
306 EOF
307 openssl req -new -key server.key -out server.csr \
308 -config extras.cnf \
309 -subj "/C=US/ST=California/L=San
310 Francisco/O=rippled-unit-tests/CN=127.0.0.1" \
311
312 # Create public certificate by signing with our CA
313 openssl x509 -req -days 10000 -in server.csr -CA ca.crt -CAkey ca.key
314 -out server.crt \ -extfile extras.cnf -set_serial 01 -extensions v3_req
315
316 # generate DH params for server
317 openssl dhparam -out dh.pem 2048
318 # verify certs
319 openssl verify -CAfile ca.crt server.crt
320 openssl x509 -in server.crt -text -noout
321 popd
322 ```
323 */
324 static std::string const&
326 {
327 static std::string const cert{R"cert(
328-----BEGIN CERTIFICATE-----
329MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL
330MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
331bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz
332NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
333bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
334ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
335AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ
336qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs
337+T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f
338n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3
339zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q
340S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV
341HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
342BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx
343hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L
344nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx
345KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT
346GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD
347X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80=
348-----END CERTIFICATE-----
349)cert"};
350 return cert;
351 }
352
353 static std::string const&
354 key()
356 static std::string const key{R"pkey(
357-----BEGIN RSA PRIVATE KEY-----
358MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc
359TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc
360/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX
3619LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx
362R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh
363wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+
364kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C
365t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK
366CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf
367K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S
368UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F
369Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP
370pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy
371dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy
372aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h
373YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3
374Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv
3758Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt
376N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7
377KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z
378gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0
3794PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/
380uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD
381cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+
382ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw==
383-----END RSA PRIVATE KEY-----
384)pkey"};
385 return key;
386 }
387
388 static std::string const&
389 ca_cert()
391 static std::string const cert{R"cert(
392-----BEGIN CERTIFICATE-----
393MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL
394BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n
395ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt
396cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV
397BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV
398BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi
399MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+
400aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq
401V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj
402uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv
403v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo
404D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB
405AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW
406gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
407DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q
408xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f
409c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp
4108+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+
411mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5
4127X1e3FekJlaD02EBEhtkXURIxogOQALdFncj
413-----END CERTIFICATE-----
414)cert"};
415 return cert;
416 }
417
418 static std::string const&
419 dh()
420 {
421 static std::string const dh{R"dh(
422-----BEGIN DH PARAMETERS-----
423MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC
424sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d
425BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL
426ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB
427xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt
428/8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg==
429-----END DH PARAMETERS-----
430)dh"};
431 return dh;
432 }
433
434private:
435 struct lambda
436 {
437 int id;
438 TrustedPublisherServer& self;
440 boost::asio::executor_work_guard<boost::asio::executor> work;
441 bool ssl;
442
443 lambda(int id_, TrustedPublisherServer& self_, socket_type&& sock_, bool ssl_)
444 : id(id_), self(self_), sock(std::move(sock_)), work(sock_.get_executor()), ssl(ssl_)
445 {
446 }
447
448 void
449 operator()()
450 {
451 self.do_peer(id, std::move(sock), ssl);
452 }
453 };
454
455 void
457 {
458 if (ec || !acceptor_.is_open())
459 return;
460
461 static int id_ = 0;
462 std::thread{lambda{++id_, *this, std::move(sock_), useSSL_}}.detach();
464 if (auto p = wp.lock())
465 {
466 p->on_accept(ec);
467 }
468 });
469 }
470
471 void
472 do_peer(int id, socket_type&& s, bool ssl)
473 {
474 using namespace boost::beast;
475 using namespace boost::asio;
476 socket_type sock(std::move(s));
477 flat_buffer sb;
478 error_code ec;
480
481 if (ssl)
482 {
483 // Construct the stream around the socket
484 ssl_stream.emplace(sock, sslCtx_);
485 // Perform the SSL handshake
486 ssl_stream->handshake(ssl::stream_base::server, ec);
487 if (ec)
488 return;
489 }
490
491 for (;;)
492 {
493 resp_type res;
494 req_type req;
495 try
496 {
497 if (ssl)
498 http::read(*ssl_stream, sb, req, ec);
499 else
500 http::read(sock, sb, req, ec);
501
502 if (ec)
503 break;
504
505 std::string_view const path = req.target();
506 res.insert("Server", "TrustedPublisherServer");
507 res.version(req.version());
508 res.keep_alive(req.keep_alive());
509 bool prepare = true;
510
511 if (boost::starts_with(path, "/validators2"))
512 {
513 res.result(http::status::ok);
514 res.insert("Content-Type", "application/json");
515 if (path == "/validators2/bad")
516 res.body() = "{ 'bad': \"2']";
517 else if (path == "/validators2/missing")
518 res.body() = "{\"version\": 2}";
519 else
520 {
521 int refresh = 5;
522 constexpr char const* refreshPrefix = "/validators2/refresh/";
523 if (boost::starts_with(path, refreshPrefix))
524 refresh = boost::lexical_cast<unsigned int>(path.substr(strlen(refreshPrefix)));
525 res.body() = getList2_(refresh);
526 }
527 }
528 else if (boost::starts_with(path, "/validators"))
529 {
530 res.result(http::status::ok);
531 res.insert("Content-Type", "application/json");
532 if (path == "/validators/bad")
533 res.body() = "{ 'bad': \"1']";
534 else if (path == "/validators/missing")
535 res.body() = "{\"version\": 1}";
536 else
537 {
538 int refresh = 5;
539 constexpr char const* refreshPrefix = "/validators/refresh/";
540 if (boost::starts_with(path, refreshPrefix))
541 refresh = boost::lexical_cast<unsigned int>(path.substr(strlen(refreshPrefix)));
542 res.body() = getList_(refresh);
543 }
544 }
545 else if (boost::starts_with(path, "/textfile"))
546 {
547 prepare = false;
548 res.result(http::status::ok);
549 res.insert("Content-Type", "text/example");
550 // if huge was requested, lie about content length
551 std::uint64_t cl =
552 boost::starts_with(path, "/textfile/huge") ? std::numeric_limits<uint64_t>::max() : 1024;
553 res.content_length(cl);
554 if (req.method() == http::verb::get)
555 {
557 for (auto i = 0; i < 1024; ++i)
558 body << static_cast<char>(rand_int<short>(32, 126)), res.body() = body.str();
559 }
560 }
561 else if (boost::starts_with(path, "/sleep/"))
562 {
563 auto const sleep_sec = boost::lexical_cast<unsigned int>(path.substr(7));
565 }
566 else if (boost::starts_with(path, "/redirect"))
567 {
568 if (boost::ends_with(path, "/301"))
569 res.result(http::status::moved_permanently);
570 else if (boost::ends_with(path, "/302"))
571 res.result(http::status::found);
572 else if (boost::ends_with(path, "/307"))
573 res.result(http::status::temporary_redirect);
574 else if (boost::ends_with(path, "/308"))
575 res.result(http::status::permanent_redirect);
576
577 std::stringstream location;
578 if (boost::starts_with(path, "/redirect_to/"))
579 {
580 location << path.substr(13);
581 }
582 else if (!boost::starts_with(path, "/redirect_nolo"))
583 {
584 location << (ssl ? "https://" : "http://") << local_endpoint()
585 << (boost::starts_with(path, "/redirect_forever/") ? path : "/validators");
586 }
587 if (!location.str().empty())
588 res.insert("Location", location.str());
589 }
590 else
591 {
592 // unknown request
593 res.result(boost::beast::http::status::not_found);
594 res.insert("Content-Type", "text/html");
595 res.body() = "The file '" + std::string(path) +
596 "' was not "
597 "found";
598 }
599
600 if (prepare)
601 res.prepare_payload();
602 }
603 catch (std::exception const& e)
604 {
605 res = {};
606 res.result(boost::beast::http::status::internal_server_error);
607 res.version(req.version());
608 res.insert("Server", "TrustedPublisherServer");
609 res.insert("Content-Type", "text/html");
610 res.body() = std::string{"An internal error occurred"} + e.what();
611 res.prepare_payload();
612 }
613
614 if (ssl)
615 write(*ssl_stream, res, ec);
616 else
617 write(sock, res, ec);
618
619 if (ec || req.need_eof())
620 break;
621 }
622
623 // Perform the SSL shutdown
624 if (ssl)
625 ssl_stream->shutdown(ec);
626 }
627};
628
631 boost::asio::io_context& ioc,
633 NetClock::time_point validUntil,
635 bool useSSL = false,
636 int version = 1,
637 bool immediateStart = true,
638 int sequence = 1)
639{
640 auto const r =
641 std::make_shared<TrustedPublisherServer>(ioc, validators, validUntil, futures, useSSL, version, sequence);
642 if (immediateStart)
643 r->start();
644 return r;
645}
646
647} // namespace test
648} // namespace xrpl
A public key.
Definition PublicKey.h:42
void add(Serializer &s) const override
Definition STObject.cpp:117
A secret key.
Definition SecretKey.h:18
std::size_t size() const noexcept
Definition Serializer.h:50
void const * data() const noexcept
Definition Serializer.h:56
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
boost::beast::http::request< boost::beast::http::string_body > req_type
void do_peer(int id, socket_type &&s, bool ssl)
boost::asio::ip::tcp::acceptor acceptor_
std::function< std::string(int)> getList_
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)
boost::asio::ip::tcp::endpoint endpoint_type
boost::asio::ip::tcp::socket socket_type
boost::beast::http::response< boost::beast::http::string_body > resp_type
std::function< std::string(int)> getList2_
Set the regular signature on a JTx.
Definition sig.h:15
T emplace_back(T... args)
T emplace(T... args)
T insert(T... args)
T is_same_v
T make_pair(T... args)
T max(T... args)
STL namespace.
void write(nudb::detail::ostream &os, std::size_t t)
Definition varint.h:114
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.
Definition utility.cpp:27
char const * getEnvLocalhostAddr()
Definition envconfig.h:16
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)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
KeyType
Definition KeyType.h:8
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
SField const sfGeneric
std::string base64_encode(std::uint8_t const *data, std::size_t len)
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
@ manifest
Manifest.
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)
Definition Slice.h:213
T pop_back(T... args)
T reserve(T... args)
T sleep_for(T... args)
T str(T... args)
T strlen(T... args)
boost::asio::executor_work_guard< boost::asio::executor > work
Set the sequence number on a JTx.
Definition seq.h:14
T substr(T... args)
T time_since_epoch(T... args)
T to_string(T... args)
T what(T... args)