rippled
Loading...
Searching...
No Matches
compression_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright 2020 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx/Account.h>
21#include <test/jtx/Env.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/amount.h>
24#include <test/jtx/pay.h>
25
26#include <xrpld/app/ledger/Ledger.h>
27#include <xrpld/app/ledger/LedgerMaster.h>
28#include <xrpld/overlay/Compression.h>
29#include <xrpld/overlay/Message.h>
30#include <xrpld/overlay/detail/Handshake.h>
31#include <xrpld/overlay/detail/ProtocolMessage.h>
32#include <xrpld/overlay/detail/ZeroCopyStream.h>
33#include <xrpld/shamap/SHAMapNodeID.h>
34
35#include <xrpl/basics/random.h>
36#include <xrpl/beast/unit_test.h>
37#include <xrpl/beast/utility/Journal.h>
38#include <xrpl/protocol/HashPrefix.h>
39#include <xrpl/protocol/PublicKey.h>
40#include <xrpl/protocol/SecretKey.h>
41#include <xrpl/protocol/Sign.h>
42#include <xrpl/protocol/digest.h>
43#include <xrpl/protocol/jss.h>
44#include <xrpl/protocol/messages.h>
45
46#include <boost/asio/ip/address_v4.hpp>
47#include <boost/beast/core/multi_buffer.hpp>
48#include <boost/endian/conversion.hpp>
49
50#include <algorithm>
51
52namespace ripple {
53
54namespace test {
55
56using namespace ripple::test;
57using namespace ripple::test::jtx;
58
59static uint256
61{
62 return ripple::sha512Half(
64 std::uint32_t(info.seq),
66 info.parentHash,
67 info.txHash,
68 info.accountHash,
73}
74
76{
79
80public:
82 {
83 }
84
85 template <typename T>
86 void
89 protocol::MessageType mt,
90 uint16_t nbuffers,
91 std::string msg)
92 {
93 testcase("Compress/Decompress: " + msg);
94
95 Message m(*proto, mt);
96
97 auto& buffer = m.getBuffer(Compressed::On);
98
99 boost::beast::multi_buffer buffers;
100
101 // simulate multi-buffer
102 auto sz = buffer.size() / nbuffers;
103 for (int i = 0; i < nbuffers; i++)
104 {
105 auto start = buffer.begin() + sz * i;
106 auto end = i < nbuffers - 1 ? (buffer.begin() + sz * (i + 1))
107 : buffer.end();
108 std::vector<std::uint8_t> slice(start, end);
109 buffers.commit(boost::asio::buffer_copy(
110 buffers.prepare(slice.size()), boost::asio::buffer(slice)));
111 }
112
113 boost::system::error_code ec;
115 ec, buffers.data(), buffer.size());
116
117 BEAST_EXPECT(header);
118
119 if (!header || header->algorithm == Algorithm::None)
120 return;
121
122 std::vector<std::uint8_t> decompressed;
123 decompressed.resize(header->uncompressed_size);
124
125 BEAST_EXPECT(
126 header->payload_wire_size == buffer.size() - header->header_size);
127
128 ZeroCopyInputStream stream(buffers.data());
129 stream.Skip(header->header_size);
130
131 auto decompressedSize = ripple::compression::decompress(
132 stream,
133 header->payload_wire_size,
134 decompressed.data(),
135 header->uncompressed_size);
136 BEAST_EXPECT(decompressedSize == header->uncompressed_size);
137 auto const proto1 = std::make_shared<T>();
138
139 BEAST_EXPECT(
140 proto1->ParseFromArray(decompressed.data(), decompressedSize));
141 auto uncompressed = m.getBuffer(Compressed::Off);
142 BEAST_EXPECT(std::equal(
143 uncompressed.begin() + ripple::compression::headerBytes,
144 uncompressed.end(),
145 decompressed.begin()));
146 }
147
150 {
151 auto manifests = std::make_shared<protocol::TMManifests>();
152 manifests->mutable_list()->Reserve(n);
153 for (int i = 0; i < n; i++)
154 {
155 auto master = randomKeyPair(KeyType::ed25519);
156 auto signing = randomKeyPair(KeyType::ed25519);
158 st[sfSequence] = i;
159 st[sfPublicKey] = std::get<0>(master);
160 st[sfSigningPubKey] = std::get<0>(signing);
161 st[sfDomain] = makeSlice(
162 std::string("example") + std::to_string(i) +
163 std::string(".com"));
164 sign(
165 st,
168 std::get<1>(master),
169 sfMasterSignature);
170 sign(
171 st,
174 std::get<1>(signing));
175 Serializer s;
176 st.add(s);
177 auto* manifest = manifests->add_list();
178 manifest->set_stobject(s.data(), s.size());
179 }
180 return manifests;
181 }
182
185 {
186 auto endpoints = std::make_shared<protocol::TMEndpoints>();
187 endpoints->mutable_endpoints_v2()->Reserve(n);
188 for (int i = 0; i < n; i++)
189 {
190 auto ep = endpoints->add_endpoints_v2();
191 ep->set_endpoint(std::string("10.0.1.") + std::to_string(i));
192 ep->set_hops(i);
193 }
194 endpoints->set_version(2);
195
196 return endpoints;
197 }
198
201 {
202 Env env(*this, envconfig());
203 int fund = 10000;
204 auto const alice = Account("alice");
205 auto const bob = Account("bob");
206 env.fund(XRP(fund), "alice", "bob");
207 env.trust(bob["USD"](fund), alice);
208 env.close();
209
210 auto toBinary = [this](std::string const& text) {
211 auto blob = strUnHex(text);
212 BEAST_EXPECT(blob);
213 return std::string{
214 reinterpret_cast<char const*>(blob->data()), blob->size()};
215 };
216
217 std::string usdTxBlob = "";
218 auto wsc = makeWSClient(env.app().config());
219 {
220 Json::Value jrequestUsd;
221 jrequestUsd[jss::secret] = toBase58(generateSeed("bob"));
222 jrequestUsd[jss::tx_json] =
223 pay("bob", "alice", bob["USD"](fund / 2));
224 Json::Value jreply_usd = wsc->invoke("sign", jrequestUsd);
225
226 usdTxBlob =
227 toBinary(jreply_usd[jss::result][jss::tx_blob].asString());
228 }
229
230 auto transaction = std::make_shared<protocol::TMTransaction>();
231 transaction->set_rawtransaction(usdTxBlob);
232 transaction->set_status(protocol::tsNEW);
233 transaction->set_receivetimestamp(rand_int<std::uint64_t>());
234 transaction->set_deferred(true);
235
236 return transaction;
237 }
238
241 {
242 auto getLedger = std::make_shared<protocol::TMGetLedger>();
243 getLedger->set_itype(protocol::liTS_CANDIDATE);
244 getLedger->set_ltype(protocol::TMLedgerType::ltACCEPTED);
245 uint256 const hash(ripple::sha512Half(123456789));
246 getLedger->set_ledgerhash(hash.begin(), hash.size());
247 getLedger->set_ledgerseq(123456789);
248 ripple::SHAMapNodeID sha(64, hash);
249 getLedger->add_nodeids(sha.getRawString());
250 getLedger->set_requestcookie(123456789);
251 getLedger->set_querytype(protocol::qtINDIRECT);
252 getLedger->set_querydepth(3);
253 return getLedger;
254 }
255
257 buildLedgerData(uint32_t n, Logs& logs)
258 {
259 auto ledgerData = std::make_shared<protocol::TMLedgerData>();
260 uint256 const hash(ripple::sha512Half(12356789));
261 ledgerData->set_ledgerhash(hash.data(), hash.size());
262 ledgerData->set_ledgerseq(123456789);
263 ledgerData->set_type(protocol::TMLedgerInfoType::liAS_NODE);
264 ledgerData->set_requestcookie(123456789);
265 ledgerData->set_error(protocol::TMReplyError::reNO_LEDGER);
266 ledgerData->mutable_nodes()->Reserve(n);
267 uint256 parentHash(0);
268
269 NetClock::duration const resolution{10};
270 NetClock::time_point ct{resolution};
271
272 for (int i = 0; i < n; i++)
273 {
274 LedgerInfo info;
275 info.seq = i;
276 info.parentCloseTime = ct;
277 info.hash = ripple::sha512Half(i);
278 info.txHash = ripple::sha512Half(i + 1);
279 info.accountHash = ripple::sha512Half(i + 2);
280 info.parentHash = parentHash;
281 info.drops = XRPAmount(10);
282 info.closeTimeResolution = resolution;
283 info.closeTime = ct;
284 ct += resolution;
285 parentHash = ledgerHash(info);
286 Serializer nData;
287 ripple::addRaw(info, nData);
288 ledgerData->add_nodes()->set_nodedata(
289 nData.getDataPtr(), nData.getLength());
290 }
291
292 return ledgerData;
293 }
294
297 {
298 auto getObject = std::make_shared<protocol::TMGetObjectByHash>();
299
300 getObject->set_type(protocol::TMGetObjectByHash_ObjectType::
301 TMGetObjectByHash_ObjectType_otTRANSACTION);
302 getObject->set_query(true);
303 getObject->set_seq(123456789);
304 uint256 hash(ripple::sha512Half(123456789));
305 getObject->set_ledgerhash(hash.data(), hash.size());
306 getObject->set_fat(true);
307 for (int i = 0; i < 100; i++)
308 {
310 auto object = getObject->add_objects();
311 object->set_hash(hash.data(), hash.size());
312 ripple::SHAMapNodeID sha(64, hash);
313 object->set_nodeid(sha.getRawString());
314 object->set_index("");
315 object->set_data("");
316 object->set_ledgerseq(i);
317 }
318 return getObject;
319 }
320
323 {
324 auto list = std::make_shared<protocol::TMValidatorList>();
325
326 auto master = randomKeyPair(KeyType::ed25519);
327 auto signing = randomKeyPair(KeyType::ed25519);
329 st[sfSequence] = 0;
330 st[sfPublicKey] = std::get<0>(master);
331 st[sfSigningPubKey] = std::get<0>(signing);
332 st[sfDomain] = makeSlice(std::string("example.com"));
333 sign(
334 st,
337 std::get<1>(master),
338 sfMasterSignature);
339 sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing));
340 Serializer s;
341 st.add(s);
342 list->set_manifest(s.data(), s.size());
343 list->set_version(3);
344 STObject signature(sfSignature);
346 st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing));
347 Serializer s1;
348 st.add(s1);
349 list->set_signature(s1.data(), s1.size());
350 list->set_blob(strHex(s.slice()));
351 return list;
352 }
353
356 {
357 auto list = std::make_shared<protocol::TMValidatorListCollection>();
358
359 auto master = randomKeyPair(KeyType::ed25519);
360 auto signing = randomKeyPair(KeyType::ed25519);
362 st[sfSequence] = 0;
363 st[sfPublicKey] = std::get<0>(master);
364 st[sfSigningPubKey] = std::get<0>(signing);
365 st[sfDomain] = makeSlice(std::string("example.com"));
366 sign(
367 st,
370 std::get<1>(master),
371 sfMasterSignature);
372 sign(st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing));
373 Serializer s;
374 st.add(s);
375 list->set_manifest(s.data(), s.size());
376 list->set_version(4);
377 STObject signature(sfSignature);
379 st, HashPrefix::manifest, KeyType::ed25519, std::get<1>(signing));
380 Serializer s1;
381 st.add(s1);
382 auto& blob = *list->add_blobs();
383 blob.set_signature(s1.data(), s1.size());
384 blob.set_blob(strHex(s.slice()));
385 return list;
386 }
387
388 void
390 {
392 auto logs = std::make_unique<Logs>(thresh);
393
394 protocol::TMManifests manifests;
395 protocol::TMEndpoints endpoints;
396 protocol::TMTransaction transaction;
397 protocol::TMGetLedger get_ledger;
398 protocol::TMLedgerData ledger_data;
399 protocol::TMGetObjectByHash get_object;
400 protocol::TMValidatorList validator_list;
401 protocol::TMValidatorListCollection validator_list_collection;
402
403 // 4.5KB
404 doTest(buildManifests(20), protocol::mtMANIFESTS, 4, "TMManifests20");
405 // 22KB
406 doTest(buildManifests(100), protocol::mtMANIFESTS, 4, "TMManifests100");
407 // 131B
408 doTest(buildEndpoints(10), protocol::mtENDPOINTS, 4, "TMEndpoints10");
409 // 1.3KB
410 doTest(buildEndpoints(100), protocol::mtENDPOINTS, 4, "TMEndpoints100");
411 // 242B
412 doTest(
413 buildTransaction(*logs),
414 protocol::mtTRANSACTION,
415 1,
416 "TMTransaction");
417 // 87B
418 doTest(buildGetLedger(), protocol::mtGET_LEDGER, 1, "TMGetLedger");
419 // 61KB
420 doTest(
421 buildLedgerData(500, *logs),
422 protocol::mtLEDGER_DATA,
423 10,
424 "TMLedgerData500");
425 // 122 KB
426 doTest(
427 buildLedgerData(1000, *logs),
428 protocol::mtLEDGER_DATA,
429 20,
430 "TMLedgerData1000");
431 // 1.2MB
432 doTest(
433 buildLedgerData(10000, *logs),
434 protocol::mtLEDGER_DATA,
435 50,
436 "TMLedgerData10000");
437 // 12MB
438 doTest(
439 buildLedgerData(100000, *logs),
440 protocol::mtLEDGER_DATA,
441 100,
442 "TMLedgerData100000");
443 // 61MB
444 doTest(
445 buildLedgerData(500000, *logs),
446 protocol::mtLEDGER_DATA,
447 100,
448 "TMLedgerData500000");
449 // 7.7KB
450 doTest(
452 protocol::mtGET_OBJECTS,
453 4,
454 "TMGetObjectByHash");
455 // 895B
456 doTest(
458 protocol::mtVALIDATORLIST,
459 4,
460 "TMValidatorList");
461 doTest(
463 protocol::mtVALIDATORLISTCOLLECTION,
464 4,
465 "TMValidatorListCollection");
466 }
467
468 void
470 {
471 testcase("Handshake");
472 auto getEnv = [&](bool enable) {
473 Config c;
475 str << "[reduce_relay]\n"
476 << "vp_base_squelch_enable=1\n"
477 << "[compression]\n"
478 << enable << "\n";
479 c.loadFromString(str.str());
480 auto env = std::make_shared<jtx::Env>(*this);
481 env->app().config().COMPRESSION = c.COMPRESSION;
482 env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE =
484 return env;
485 };
486 auto handshake = [&](int outboundEnable, int inboundEnable) {
487 beast::IP::Address addr =
488 boost::asio::ip::address::from_string("172.1.1.100");
489
490 auto env = getEnv(outboundEnable);
491 auto request = ripple::makeRequest(
492 true,
493 env->app().config().COMPRESSION,
494 false,
495 env->app().config().TX_REDUCE_RELAY_ENABLE,
496 env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE);
497 http_request_type http_request;
498 http_request.version(request.version());
499 http_request.base() = request.base();
500 // feature enabled on the peer's connection only if both sides are
501 // enabled
502 auto const peerEnabled = inboundEnable && outboundEnable;
503 // inbound is enabled if the request's header has the feature
504 // enabled and the peer's configuration is enabled
505 auto const inboundEnabled = peerFeatureEnabled(
506 http_request, FEATURE_COMPR, "lz4", inboundEnable);
507 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
508
509 env.reset();
510 env = getEnv(inboundEnable);
511 auto http_resp = ripple::makeResponse(
512 true,
513 http_request,
514 addr,
515 addr,
516 uint256{1},
517 1,
518 {1, 0},
519 env->app());
520 // outbound is enabled if the response's header has the feature
521 // enabled and the peer's configuration is enabled
522 auto const outboundEnabled = peerFeatureEnabled(
523 http_resp, FEATURE_COMPR, "lz4", outboundEnable);
524 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
525 };
526 handshake(1, 1);
527 handshake(1, 0);
528 handshake(0, 1);
529 handshake(0, 0);
530 }
531
532 void
533 run() override
534 {
535 testProtocol();
537 }
538};
539
540BEAST_DEFINE_TESTSUITE_MANUAL(compression, ripple_data, ripple);
541
542} // namespace test
543} // namespace ripple
T begin(T... args)
Represents a JSON value.
Definition: json_value.h:150
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
virtual Config & config()=0
bool VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE
Definition: Config.h:248
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:478
bool COMPRESSION
Definition: Config.h:220
Manages partitions for logging.
Definition: Log.h:51
std::vector< uint8_t > const & getBuffer(Compressed tryCompressed)
Retrieve the packed message data.
Definition: Message.cpp:210
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:34
std::string getRawString() const
void add(Serializer &s) const override
Definition: STObject.cpp:141
std::size_t size() const noexcept
Definition: Serializer.h:73
void const * data() const noexcept
Definition: Serializer.h:79
Slice slice() const noexcept
Definition: Serializer.h:67
int getLength() const
Definition: Serializer.h:234
void const * getDataPtr() const
Definition: Serializer.h:224
constexpr value_type drops() const
Returns the number of drops.
Definition: XRPAmount.h:177
Implements ZeroCopyInputStream around a buffer sequence.
iterator begin()
Definition: base_uint.h:136
pointer data()
Definition: base_uint.h:125
static constexpr std::size_t size()
Definition: base_uint.h:526
std::shared_ptr< protocol::TMGetObjectByHash > buildGetObjectByHash()
void run() override
Runs the suite.
std::shared_ptr< protocol::TMManifests > buildManifests(int n)
std::shared_ptr< protocol::TMLedgerData > buildLedgerData(uint32_t n, Logs &logs)
std::shared_ptr< protocol::TMValidatorListCollection > buildValidatorListCollection()
void doTest(std::shared_ptr< T > proto, protocol::MessageType mt, uint16_t nbuffers, std::string msg)
std::shared_ptr< protocol::TMValidatorList > buildValidatorList()
std::shared_ptr< protocol::TMGetLedger > buildGetLedger()
std::shared_ptr< protocol::TMTransaction > buildTransaction(Logs &logs)
std::shared_ptr< protocol::TMEndpoints > buildEndpoints(int n)
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:121
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:264
Application & app()
Definition: Env.h:261
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
T data(T... args)
T equal(T... args)
boost::asio::ip::address Address
Definition: IPAddress.h:43
std::size_t constexpr headerBytes
Definition: Compression.h:30
std::size_t decompress(InputStream &in, std::size_t inSize, std::uint8_t *decompressed, std::size_t decompressedSize, Algorithm algorithm=Algorithm::LZ4)
Decompress input stream.
Definition: Compression.h:49
std::optional< MessageHeader > parseMessageHeader(boost::system::error_code &ec, BufferSequence const &bufs, std::size_t size)
Parse a message header.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition: AMMTest.cpp:37
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
void sign(Json::Value &jv, Account const &account)
Sign automatically.
Definition: utility.cpp:47
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
static uint256 ledgerHash(LedgerInfo const &info)
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition: WSClient.cpp:302
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:114
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
static constexpr char FEATURE_COMPR[]
Definition: Handshake.h:141
SField const sfGeneric
http_response_type makeResponse(bool crawlPublic, http_request_type const &req, beast::IP::Address public_ip, beast::IP::Address remote_ip, uint256 const &sharedValue, std::optional< std::uint32_t > networkID, ProtocolVersion protocol, Application &app)
Make http response.
Definition: Handshake.cpp:392
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
Definition: SecretKey.cpp:256
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
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:244
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition: Handoff.h:33
bool peerFeatureEnabled(headers const &request, std::string const &feature, std::string value, bool config)
Check if a feature should be enabled for a peer.
Definition: Handshake.h:198
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
Definition: Handshake.cpp:365
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:386
@ manifest
Manifest.
@ ledgerMaster
ledger master data for signing
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition: Seed.cpp:76
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:225
T resize(T... args)
T size(T... args)
T str(T... args)
Information about the notional ledger backing the view.
Definition: LedgerHeader.h:34
NetClock::time_point closeTime
Definition: LedgerHeader.h:72
NetClock::duration closeTimeResolution
Definition: LedgerHeader.h:66
NetClock::time_point parentCloseTime
Definition: LedgerHeader.h:42
T time_since_epoch(T... args)
T to_string(T... args)