xrpld
Loading...
Searching...
No Matches
compression_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/WSClient.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/envconfig.h>
6#include <test/jtx/pay.h>
7
8#include <xrpld/core/Config.h>
9#include <xrpld/overlay/Compression.h>
10#include <xrpld/overlay/Message.h>
11#include <xrpld/overlay/detail/Handshake.h>
12#include <xrpld/overlay/detail/ProtocolMessage.h>
13#include <xrpld/overlay/detail/ZeroCopyStream.h>
14
15#include <xrpl/basics/Log.h>
16#include <xrpl/basics/Slice.h>
17#include <xrpl/basics/StringUtilities.h>
18#include <xrpl/basics/base_uint.h>
19#include <xrpl/basics/chrono.h>
20#include <xrpl/basics/random.h>
21#include <xrpl/basics/strHex.h>
22#include <xrpl/beast/net/IPAddress.h>
23#include <xrpl/beast/unit_test/suite.h>
24#include <xrpl/beast/utility/Journal.h>
25#include <xrpl/json/json_value.h>
26#include <xrpl/protocol/HashPrefix.h>
27#include <xrpl/protocol/KeyType.h>
28#include <xrpl/protocol/LedgerHeader.h>
29#include <xrpl/protocol/SField.h>
30#include <xrpl/protocol/STObject.h>
31#include <xrpl/protocol/SecretKey.h>
32#include <xrpl/protocol/Seed.h>
33#include <xrpl/protocol/Serializer.h>
34#include <xrpl/protocol/Sign.h>
35#include <xrpl/protocol/digest.h>
36#include <xrpl/protocol/jss.h>
37#include <xrpl/shamap/SHAMapNodeID.h>
38
39#include <boost/asio/buffer.hpp>
40#include <boost/asio/ip/address.hpp>
41#include <boost/beast/core/multi_buffer.hpp>
42#include <boost/system/detail/error_code.hpp>
43
44#include <xrpl.pb.h>
45
46#include <algorithm>
47#include <cstdint>
48#include <memory>
49#include <sstream>
50#include <string>
51#include <vector>
52
53namespace xrpl::test {
54
55using namespace xrpl::test;
56using namespace xrpl::test::jtx;
57
58static uint256
73
75{
78
79public:
80 compression_test() = default;
81
82 template <typename T>
83 void
84 doTest(std::shared_ptr<T> proto, protocol::MessageType mt, uint16_t nbuffers, std::string msg)
85 {
86 testcase("Compress/Decompress: " + msg);
87
88 Message m(*proto, mt);
89
90 auto& buffer = m.getBuffer(Compressed::On);
91
92 boost::beast::multi_buffer buffers;
93
94 // simulate multi-buffer
95 auto sz = buffer.size() / nbuffers;
96 for (int i = 0; i < nbuffers; i++)
97 {
98 auto start = buffer.begin() + sz * i;
99 auto end = i < nbuffers - 1 ? (buffer.begin() + sz * (i + 1)) : buffer.end();
100 std::vector<std::uint8_t> slice(start, end);
101 buffers.commit(
102 boost::asio::buffer_copy(
103 buffers.prepare(slice.size()), boost::asio::buffer(slice)));
104 }
105
106 boost::system::error_code ec;
107 auto header = xrpl::detail::parseMessageHeader(ec, buffers.data(), buffer.size());
108
109 BEAST_EXPECT(header);
110
111 if (!header || header->algorithm == Algorithm::None)
112 return;
113
114 std::vector<std::uint8_t> decompressed;
115 decompressed.resize(header->uncompressedSize);
116
117 BEAST_EXPECT(header->payloadWireSize == buffer.size() - header->headerSize);
118
119 ZeroCopyInputStream stream(buffers.data());
120 stream.Skip(header->headerSize);
121
122 auto decompressedSize = xrpl::compression::decompress(
123 stream, header->payloadWireSize, decompressed.data(), header->uncompressedSize);
124 BEAST_EXPECT(decompressedSize == header->uncompressedSize);
125 auto const proto1 = std::make_shared<T>();
126
127 BEAST_EXPECT(proto1->ParseFromArray(decompressed.data(), decompressedSize));
128 auto uncompressed = m.getBuffer(Compressed::Off);
129 BEAST_EXPECT(
131 uncompressed.begin() + xrpl::compression::kHeaderBytes,
132 uncompressed.end(),
133 decompressed.begin()));
134 }
135
138 {
140 manifests->mutable_list()->Reserve(n);
141 for (int i = 0; i < n; i++)
142 {
143 auto master = randomKeyPair(KeyType::Ed25519);
144 auto signing = randomKeyPair(KeyType::Ed25519);
146 st[sfSequence] = i;
147 st[sfPublicKey] = std::get<0>(master);
148 st[sfSigningPubKey] = std::get<0>(signing);
149 st[sfDomain] =
150 makeSlice(std::string("example") + std::to_string(i) + std::string(".com"));
151 sign(
152 st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(master), sfMasterSignature);
153 sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(signing));
154 Serializer s;
155 st.add(s);
156 auto* manifest = manifests->add_list();
157 manifest->set_stobject(s.data(), s.size());
158 }
159 return manifests;
160 }
161
164 {
166 endpoints->mutable_endpoints_v2()->Reserve(n);
167 for (int i = 0; i < n; i++)
168 {
169 auto ep = endpoints->add_endpoints_v2();
170 ep->set_endpoint(std::string("10.0.1.") + std::to_string(i));
171 ep->set_hops(i);
172 }
173 endpoints->set_version(2);
174
175 return endpoints;
176 }
177
180 {
181 Env env(*this, envconfig());
182 int const fund = 10000;
183 auto const alice = Account("alice");
184 auto const bob = Account("bob");
185 env.fund(XRP(fund), "alice", "bob");
186 env.trust(bob["USD"](fund), alice);
187 env.close();
188
189 auto toBinary = [this](std::string const& text) {
190 auto blob = strUnHex(text);
191 BEAST_EXPECT(blob);
192 return std::string{reinterpret_cast<char const*>(blob->data()), blob->size()};
193 };
194
195 std::string usdTxBlob;
196 auto wsc = makeWSClient(env.app().config());
197 {
198 json::Value requestUSD;
199 requestUSD[jss::secret] = toBase58(generateSeed("bob"));
200 requestUSD[jss::tx_json] = pay("bob", "alice", bob["USD"](fund / 2));
201 json::Value replyUSD = wsc->invoke("sign", requestUSD);
202
203 usdTxBlob = toBinary(replyUSD[jss::result][jss::tx_blob].asString());
204 }
205
207 transaction->set_rawtransaction(usdTxBlob);
208 transaction->set_status(protocol::tsNEW);
209 transaction->set_receivetimestamp(randInt<std::uint64_t>());
210 transaction->set_deferred(true);
211
212 return transaction;
213 }
214
217 {
219 getLedger->set_itype(protocol::liTS_CANDIDATE);
220 getLedger->set_ltype(protocol::TMLedgerType::ltACCEPTED);
221 uint256 const hash(xrpl::sha512Half(123456789));
222 getLedger->set_ledgerhash(hash.begin(), hash.size());
223 getLedger->set_ledgerseq(123456789);
224 xrpl::SHAMapNodeID const sha(64, hash);
225 getLedger->add_nodeids(sha.getRawString());
226 getLedger->set_requestcookie(123456789);
227 getLedger->set_querytype(protocol::qtINDIRECT);
228 getLedger->set_querydepth(3);
229 return getLedger;
230 }
231
233 buildLedgerData(uint32_t n, Logs& logs)
234 {
236 uint256 const hash(xrpl::sha512Half(12356789));
237 ledgerData->set_ledgerhash(hash.data(), hash.size());
238 ledgerData->set_ledgerseq(123456789);
239 ledgerData->set_type(protocol::TMLedgerInfoType::liAS_NODE);
240 ledgerData->set_requestcookie(123456789);
241 ledgerData->set_error(protocol::TMReplyError::reNO_LEDGER);
242 ledgerData->mutable_nodes()->Reserve(n);
243 uint256 parentHash(0);
244
245 NetClock::duration const resolution{10};
246 NetClock::time_point ct{resolution};
247
248 for (int i = 0; i < n; i++)
249 {
250 LedgerHeader info;
251 info.seq = i;
252 info.parentCloseTime = ct;
253 info.hash = xrpl::sha512Half(i);
254 info.txHash = xrpl::sha512Half(i + 1);
255 info.accountHash = xrpl::sha512Half(i + 2);
256 info.parentHash = parentHash;
257 info.drops = XRPAmount(10);
258 info.closeTimeResolution = resolution;
259 info.closeTime = ct;
260 ct += resolution;
261 parentHash = ledgerHash(info);
262 Serializer nData;
263 xrpl::addRaw(info, nData);
264 ledgerData->add_nodes()->set_nodedata(nData.getDataPtr(), nData.getLength());
265 }
266
267 return ledgerData;
268 }
269
272 {
274
275 getObject->set_type(
276 protocol::TMGetObjectByHash_ObjectType::TMGetObjectByHash_ObjectType_otTRANSACTION);
277 getObject->set_query(true);
278 uint256 hash(xrpl::sha512Half(123456789));
279 getObject->set_ledgerhash(hash.data(), hash.size());
280 getObject->set_fat(true);
281 for (int i = 0; i < 100; i++)
282 {
283 uint256 hash(xrpl::sha512Half(i));
284 auto object = getObject->add_objects();
285 object->set_hash(hash.data(), hash.size());
286 xrpl::SHAMapNodeID const sha(64, hash);
287 object->set_nodeid(sha.getRawString());
288 object->set_index("");
289 object->set_data("");
290 object->set_ledgerseq(i);
291 }
292 return getObject;
293 }
294
297 {
299
300 auto master = randomKeyPair(KeyType::Ed25519);
301 auto signing = randomKeyPair(KeyType::Ed25519);
303 st[sfSequence] = 0;
304 st[sfPublicKey] = std::get<0>(master);
305 st[sfSigningPubKey] = std::get<0>(signing);
306 st[sfDomain] = makeSlice(std::string("example.com"));
307 sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(master), sfMasterSignature);
308 sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(signing));
309 Serializer s;
310 st.add(s);
311 list->set_manifest(s.data(), s.size());
312 list->set_version(3);
313 STObject const signature(sfSignature);
314 xrpl::sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(signing));
315 Serializer s1;
316 st.add(s1);
317 list->set_signature(s1.data(), s1.size());
318 list->set_blob(strHex(s.slice()));
319 return list;
320 }
321
324 {
326
327 auto master = randomKeyPair(KeyType::Ed25519);
328 auto signing = randomKeyPair(KeyType::Ed25519);
330 st[sfSequence] = 0;
331 st[sfPublicKey] = std::get<0>(master);
332 st[sfSigningPubKey] = std::get<0>(signing);
333 st[sfDomain] = makeSlice(std::string("example.com"));
334 sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(master), sfMasterSignature);
335 sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(signing));
336 Serializer s;
337 st.add(s);
338 list->set_manifest(s.data(), s.size());
339 list->set_version(4);
340 STObject const signature(sfSignature);
341 xrpl::sign(st, HashPrefix::Manifest, KeyType::Ed25519, std::get<1>(signing));
342 Serializer s1;
343 st.add(s1);
344 auto& blob = *list->add_blobs();
345 blob.set_signature(s1.data(), s1.size());
346 blob.set_blob(strHex(s.slice()));
347 return list;
348 }
349
350 void
352 {
353 auto thresh = beast::Severity::Info;
354 auto logs = std::make_unique<Logs>(thresh);
355
356 protocol::TMManifests const manifests;
357 protocol::TMEndpoints const endpoints;
358 protocol::TMTransaction const transaction;
359 protocol::TMGetLedger const getLedger;
360 protocol::TMLedgerData const ledgerData;
361 protocol::TMGetObjectByHash const getObject;
362 protocol::TMValidatorList const validatorList;
363 protocol::TMValidatorListCollection const validatorListCollection;
364
365 // 4.5KB
366 doTest(buildManifests(20), protocol::mtMANIFESTS, 4, "TMManifests20");
367 // 22KB
368 doTest(buildManifests(100), protocol::mtMANIFESTS, 4, "TMManifests100");
369 // 131B
370 doTest(buildEndpoints(10), protocol::mtENDPOINTS, 4, "TMEndpoints10");
371 // 1.3KB
372 doTest(buildEndpoints(100), protocol::mtENDPOINTS, 4, "TMEndpoints100");
373 // 242B
374 doTest(buildTransaction(*logs), protocol::mtTRANSACTION, 1, "TMTransaction");
375 // 87B
376 doTest(buildGetLedger(), protocol::mtGET_LEDGER, 1, "TMGetLedger");
377 // 61KB
378 doTest(buildLedgerData(500, *logs), protocol::mtLEDGER_DATA, 10, "TMLedgerData500");
379 // 122 KB
380 doTest(buildLedgerData(1000, *logs), protocol::mtLEDGER_DATA, 20, "TMLedgerData1000");
381 // 1.2MB
382 doTest(buildLedgerData(10000, *logs), protocol::mtLEDGER_DATA, 50, "TMLedgerData10000");
383 // 12MB
384 doTest(buildLedgerData(100000, *logs), protocol::mtLEDGER_DATA, 100, "TMLedgerData100000");
385 // 61MB
386 doTest(buildLedgerData(500000, *logs), protocol::mtLEDGER_DATA, 100, "TMLedgerData500000");
387 // 7.7KB
388 doTest(buildGetObjectByHash(), protocol::mtGET_OBJECTS, 4, "TMGetObjectByHash");
389 // 895B
390 doTest(buildValidatorList(), protocol::mtVALIDATOR_LIST, 4, "TMValidatorList");
391 doTest(
393 protocol::mtVALIDATOR_LIST_COLLECTION,
394 4,
395 "TMValidatorListCollection");
396 }
397
398 void
400 {
401 testcase("Handshake");
402 auto getEnv = [&](bool enable) {
403 Config c;
405 str << "[reduce_relay]\n"
406 << "vp_base_squelch_enable=1\n"
407 << "[compression]\n"
408 << enable << "\n";
409 c.loadFromString(str.str());
410 auto env = std::make_shared<jtx::Env>(*this);
411 env->app().config().compression = c.compression;
412 env->app().config().vpReduceRelayBaseSquelchEnable = c.vpReduceRelayBaseSquelchEnable;
413 return env;
414 };
415 auto handshake = [&](int outboundEnable, int inboundEnable) {
416 beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100");
417
418 auto env = getEnv(outboundEnable);
419 auto request = xrpl::makeRequest(
420 true,
421 env->app().config().compression,
422 false,
423 env->app().config().txReduceRelayEnable,
424 env->app().config().vpReduceRelayBaseSquelchEnable);
425 http_request_type httpRequest;
426 httpRequest.version(request.version());
427 httpRequest.base() = request.base();
428 // feature enabled on the peer's connection only if both sides are
429 // enabled
430 auto const peerEnabled = inboundEnable && outboundEnable;
431 // inbound is enabled if the request's header has the feature
432 // enabled and the peer's configuration is enabled
433 auto const inboundEnabled =
434 peerFeatureEnabled(httpRequest, kFeatureCompr, "lz4", inboundEnable);
435 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
436
437 env.reset();
438 env = getEnv(inboundEnable);
439 auto httpResp = xrpl::makeResponse(
440 true, httpRequest, addr, addr, uint256{1}, 1, {1, 0}, env->app());
441 // outbound is enabled if the response's header has the feature
442 // enabled and the peer's configuration is enabled
443 auto const outboundEnabled =
444 peerFeatureEnabled(httpResp, kFeatureCompr, "lz4", outboundEnable);
445 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
446 };
447 handshake(1, 1);
448 handshake(1, 0);
449 handshake(0, 1);
450 handshake(0, 0);
451 }
452
453 void
454 run() override
455 {
456 testProtocol();
458 }
459};
460
462
463} // namespace xrpl::test
T begin(T... args)
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
virtual Config & config()=0
pointer data()
Definition base_uint.h:106
iterator begin()
Definition base_uint.h:117
static constexpr std::size_t size()
Definition base_uint.h:530
bool vpReduceRelayBaseSquelchEnable
Definition Config.h:233
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition Config.cpp:473
bool compression
Definition Config.h:205
Manages partitions for logging.
Definition Log.h:20
std::vector< uint8_t > const & getBuffer(Compressed tryCompressed)
Retrieve the packed message data.
Definition Message.cpp:201
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
std::chrono::duration< rep, period > duration
Definition chrono.h:45
Identifies a node inside a SHAMap.
std::string getRawString() const
void add(Serializer &s) const override
Definition STObject.cpp:120
void const * getDataPtr() const
Definition Serializer.h:197
int getLength() const
Definition Serializer.h:207
Slice slice() const noexcept
Definition Serializer.h:44
std::size_t size() const noexcept
Definition Serializer.h:50
void const * data() const noexcept
Definition Serializer.h:56
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:159
Implements ZeroCopyInputStream around a buffer sequence.
static std::shared_ptr< protocol::TMEndpoints > buildEndpoints(int n)
void doTest(std::shared_ptr< T > proto, protocol::MessageType mt, uint16_t nbuffers, std::string msg)
static std::shared_ptr< protocol::TMValidatorList > buildValidatorList()
compression::Compressed Compressed
static std::shared_ptr< protocol::TMLedgerData > buildLedgerData(uint32_t n, Logs &logs)
std::shared_ptr< protocol::TMTransaction > buildTransaction(Logs &logs)
static std::shared_ptr< protocol::TMValidatorListCollection > buildValidatorListCollection()
compression::Algorithm Algorithm
static std::shared_ptr< protocol::TMGetLedger > buildGetLedger()
void run() override
Runs the suite.
static std::shared_ptr< protocol::TMGetObjectByHash > buildGetObjectByHash()
static std::shared_ptr< protocol::TMManifests > buildManifests(int n)
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
T data(T... args)
T equal(T... args)
T make_shared(T... args)
T make_unique(T... args)
boost::asio::ip::address Address
Definition IPAddress.h:19
constexpr std::size_t kHeaderBytes
Definition Compression.h:8
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:27
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:14
std::vector< STAmount > fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:34
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
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:40
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:28
static uint256 ledgerHash(LedgerHeader const &info)
BEAST_DEFINE_TESTSUITE_MANUAL(AMMCalc, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:48
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpcVersion, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition WSClient.cpp:329
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:204
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
std::enable_if_t< std::is_integral_v< Integral > &&detail::is_engine< Engine >::value, Integral > randInt(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
SField const sfGeneric
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:58
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
http_response_type makeResponse(bool crawlPublic, http_request_type const &req, beast::IP::Address publicIp, beast::IP::Address remoteIp, uint256 const &sharedValue, std::optional< std::uint32_t > networkID, ProtocolVersion protocol, Application &app)
Make http response.
@ LedgerMaster
ledger master data for signing
Definition HashPrefix.h:48
@ Manifest
Manifest.
Definition HashPrefix.h:63
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
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:171
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition Handoff.h:12
static constexpr char kFeatureCompr[]
Definition Handshake.h:118
BaseUInt< 256 > uint256
Definition base_uint.h:562
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
T resize(T... args)
T size(T... args)
T str(T... args)
Information about the notional ledger backing the view.
NetClock::time_point parentCloseTime
NetClock::duration closeTimeResolution
NetClock::time_point closeTime
T time_since_epoch(T... args)
T to_string(T... args)