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