rippled
Loading...
Searching...
No Matches
TestHelpers.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2023 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#ifndef RIPPLE_TEST_JTX_TESTHELPERS_H_INCLUDED
21#define RIPPLE_TEST_JTX_TESTHELPERS_H_INCLUDED
22
23#include <test/jtx/Env.h>
24
25#include <xrpl/basics/base_uint.h>
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/json/json_value.h>
28#include <xrpl/protocol/AccountID.h>
29#include <xrpl/protocol/Quality.h>
30#include <xrpl/protocol/STNumber.h>
31#include <xrpl/protocol/TxFlags.h>
32#include <xrpl/protocol/Units.h>
33#include <xrpl/protocol/jss.h>
34
35#include <vector>
36
37#if (defined(__clang_major__) && __clang_major__ < 15)
38#include <experimental/source_location>
40#else
41#include <source_location>
43#endif
44
45namespace ripple {
46namespace test {
47namespace jtx {
48
54template <
55 class SField,
56 class StoredValue = typename SField::type::value_type,
57 class OutputValue = StoredValue>
59{
60 using SF = SField;
61 using SV = StoredValue;
62 using OV = OutputValue;
63
64protected:
65 SF const& sfield_;
67
68public:
69 explicit JTxField(SF const& sfield, SV const& value)
70 : sfield_(sfield), value_(value)
71 {
72 }
73
74 virtual ~JTxField() = default;
75
76 virtual OV
77 value() const = 0;
78
79 virtual void
80 operator()(Env&, JTx& jt) const
81 {
82 jt.jv[sfield_.jsonName] = value();
83 }
84};
85
86template <class SField, class StoredValue>
87struct JTxField<SField, StoredValue, StoredValue>
88{
89 using SF = SField;
90 using SV = StoredValue;
91 using OV = SV;
92
93protected:
94 SF const& sfield_;
96
97public:
98 explicit JTxField(SF const& sfield, SV const& value)
99 : sfield_(sfield), value_(value)
100 {
101 }
102
103 void
104 operator()(Env&, JTx& jt) const
105 {
107 }
108};
109
111 : public JTxField<SF_UINT32, NetClock::time_point, NetClock::rep>
112{
113 using SF = SF_UINT32;
117
118protected:
119 using base::value_;
120
121public:
122 explicit timePointField(SF const& sfield, SV const& value)
123 : JTxField(sfield, value)
124 {
125 }
126
127 OV
128 value() const override
129 {
130 return value_.time_since_epoch().count();
131 }
132};
133
134struct uint256Field : public JTxField<SF_UINT256, uint256, std::string>
135{
136 using SF = SF_UINT256;
137 using SV = uint256;
140
141protected:
142 using base::value_;
143
144public:
145 explicit uint256Field(SF const& sfield, SV const& value)
146 : JTxField(sfield, value)
147 {
148 }
149
150 OV
151 value() const override
152 {
153 return to_string(value_);
154 }
155};
156
157struct accountIDField : public JTxField<SF_ACCOUNT, AccountID, std::string>
158{
159 using SF = SF_ACCOUNT;
160 using SV = AccountID;
163
164protected:
165 using base::value_;
166
167public:
168 explicit accountIDField(SF const& sfield, SV const& value)
169 : JTxField(sfield, value)
170 {
171 }
172
173 OV
174 value() const override
175 {
176 return toBase58(value_);
177 }
178};
179
180struct blobField : public JTxField<SF_VL, std::string>
181{
182 using SF = SF_VL;
185
186 using JTxField::JTxField;
187
188 explicit blobField(SF const& sfield, Slice const& cond)
189 : JTxField(sfield, strHex(cond))
190 {
191 }
192
193 template <size_t N>
194 explicit blobField(SF const& sfield, std::array<std::uint8_t, N> const& c)
195 : blobField(sfield, makeSlice(c))
196 {
197 }
198};
199
200template <class SField, class UnitTag, class ValueType>
202 : public JTxField<SField, unit::ValueUnit<UnitTag, ValueType>, ValueType>
203{
204 using SF = SField;
206 using OV = ValueType;
208
210
211protected:
212 using base::value_;
213
214public:
215 using JTxField<SF, SV, OV>::JTxField;
216
217 OV
218 value() const override
219 {
220 return value_.value();
221 }
222};
223
224template <class JTxField>
226{
227 using JF = JTxField;
228 using SF = typename JF::SF;
229 using SV = typename JF::SV;
230
231protected:
232 SF const& sfield_;
233
234public:
235 explicit JTxFieldWrapper(SF const& sfield) : sfield_(sfield)
236 {
237 }
238
239 JF
240 operator()(SV const& value) const
241 {
242 return JTxField(sfield_, value);
243 }
244};
245
246template <>
248{
249 using JF = blobField;
250 using SF = JF::SF;
251 using SV = JF::SV;
252
253protected:
254 SF const& sfield_;
255
256public:
257 explicit JTxFieldWrapper(SF const& sfield) : sfield_(sfield)
258 {
259 }
260
261 JF
262 operator()(SV const& cond) const
263 {
264 return JF(sfield_, makeSlice(cond));
265 }
266
267 JF
268 operator()(Slice const& cond) const
269 {
270 return JF(sfield_, cond);
271 }
272
273 template <size_t N>
274 JF
276 {
277 return operator()(makeSlice(c));
278 }
279};
280
281template <
282 class SField,
283 class UnitTag,
284 class ValueType = typename SField::type::value_type>
287
288template <class SField, class StoredValue = typename SField::type::value_type>
290
293auto const data = JTxFieldWrapper<blobField>(sfData);
294
295// TODO We only need this long "requires" clause as polyfill, for C++20
296// implementations which are missing <ranges> header. Replace with
297// `std::ranges::range<Input>`, and accordingly use std::ranges::begin/end
298// when we have moved to better compilers.
299template <typename Input>
300auto
301make_vector(Input const& input)
302 requires requires(Input& v) {
303 std::begin(v);
304 std::end(v);
305 }
306{
307 return std::vector(std::begin(input), std::end(input));
308}
309
310// Functions used in debugging
312getAccountOffers(Env& env, AccountID const& acct, bool current = false);
313
314inline Json::Value
315getAccountOffers(Env& env, Account const& acct, bool current = false)
316{
317 return getAccountOffers(env, acct.id(), current);
318}
319
321getAccountLines(Env& env, AccountID const& acctId);
322
323inline Json::Value
324getAccountLines(Env& env, Account const& acct)
325{
326 return getAccountLines(env, acct.id());
327}
328
329template <typename... IOU>
331getAccountLines(Env& env, AccountID const& acctId, IOU... ious)
332{
333 auto const jrr = getAccountLines(env, acctId);
334 Json::Value res;
335 for (auto const& line : jrr[jss::lines])
336 {
337 for (auto const& iou : {ious...})
338 {
339 if (line[jss::currency].asString() == to_string(iou.currency))
340 {
341 Json::Value v;
342 v[jss::currency] = line[jss::currency];
343 v[jss::balance] = line[jss::balance];
344 v[jss::limit] = line[jss::limit];
345 v[jss::account] = line[jss::account];
346 res[jss::lines].append(v);
347 }
348 }
349 }
350 if (!res.isNull())
351 return res;
352 return jrr;
353}
354
355[[nodiscard]] bool
356checkArraySize(Json::Value const& val, unsigned int size);
357
358// Helper function that returns the owner count on an account.
360ownerCount(test::jtx::Env const& env, test::jtx::Account const& account);
361
362[[nodiscard]]
363inline bool
364checkVL(Slice const& result, std::string const& expected)
365{
366 Serializer s;
367 s.addRaw(result);
368 return s.getString() == expected;
369}
370
371[[nodiscard]]
372inline bool
375 SField const& field,
376 std::string const& expected)
377{
378 return strHex(expected) == strHex(sle->getFieldVL(field));
379}
380
381/* Path finding */
382/******************************************************************************/
383void
384stpath_append_one(STPath& st, Account const& account);
385
386template <class T>
388stpath_append_one(STPath& st, T const& t)
389{
391}
392
393void
395
396template <class T, class... Args>
397void
398stpath_append(STPath& st, T const& t, Args const&... args)
399{
400 stpath_append_one(st, t);
401 if constexpr (sizeof...(args) > 0)
402 stpath_append(st, args...);
403}
404
405template <class... Args>
406void
407stpathset_append(STPathSet& st, STPath const& p, Args const&... args)
408{
409 st.push_back(p);
410 if constexpr (sizeof...(args) > 0)
411 stpathset_append(st, args...);
412}
413
414bool
415equal(STAmount const& sa1, STAmount const& sa2);
416
417// Issue path element
419IPE(Issue const& iss);
420
421template <class... Args>
422STPath
423stpath(Args const&... args)
424{
425 STPath st;
426 stpath_append(st, args...);
427 return st;
428}
429
430template <class... Args>
431bool
432same(STPathSet const& st1, Args const&... args)
433{
434 STPathSet st2;
435 stpathset_append(st2, args...);
436 if (st1.size() != st2.size())
437 return false;
438
439 for (auto const& p : st2)
440 {
441 if (std::find(st1.begin(), st1.end(), p) == st1.end())
442 return false;
443 }
444 return true;
445}
446
447/******************************************************************************/
448
450txfee(Env const& env, std::uint16_t n);
451
453xrpMinusFee(Env const& env, std::int64_t xrpAmount);
454
455bool
457 Env& env,
458 AccountID const& account,
459 STAmount const& value,
460 bool defaultLimits = false);
461
462template <typename... Amts>
463bool
465 Env& env,
466 AccountID const& account,
467 STAmount const& value,
468 Amts const&... amts)
469{
470 return expectHolding(env, account, value, false) &&
471 expectHolding(env, account, amts...);
472}
473
474bool
475expectHolding(Env& env, AccountID const& account, None const& value);
476
477bool
479 Env& env,
480 AccountID const& account,
481 std::uint16_t size,
482 std::vector<Amounts> const& toMatch = {});
483
485ledgerEntryRoot(Env& env, Account const& acct);
486
489 Env& env,
490 Account const& acct_a,
491 Account const& acct_b,
492 std::string const& currency);
493
495accountBalance(Env& env, Account const& acct);
496
497[[nodiscard]] bool
499 Env& env,
500 Account const& acct,
501 STAmount const& expectedValue);
502
503/* Payment Channel */
504/******************************************************************************/
505
507create(
508 AccountID const& account,
509 AccountID const& to,
510 STAmount const& amount,
511 NetClock::duration const& settleDelay,
512 PublicKey const& pk,
515
516inline Json::Value
518 Account const& account,
519 Account const& to,
520 STAmount const& amount,
521 NetClock::duration const& settleDelay,
522 PublicKey const& pk,
525{
526 return create(
527 account.id(), to.id(), amount, settleDelay, pk, cancelAfter, dstTag);
528}
529
531fund(
532 AccountID const& account,
533 uint256 const& channel,
534 STAmount const& amount,
536
538claim(
539 AccountID const& account,
540 uint256 const& channel,
543 std::optional<Slice> const& signature = std::nullopt,
545
547channel(
548 AccountID const& account,
549 AccountID const& dst,
550 std::uint32_t seqProxyValue);
551
552inline uint256
553channel(Account const& account, Account const& dst, std::uint32_t seqProxyValue)
554{
555 return channel(account.id(), dst.id(), seqProxyValue);
556}
557
559channelBalance(ReadView const& view, uint256 const& chan);
560
561bool
562channelExists(ReadView const& view, uint256 const& chan);
563
564/* Crossing Limits */
565/******************************************************************************/
566
567void
569 Env& env,
570 std::size_t n,
571 Account const& account,
572 STAmount const& in,
573 STAmount const& out);
574
575/* Pay Strand */
576/***************************************************************/
577
578// Currency path element
580cpe(Currency const& c);
581
582// All path element
584allpe(AccountID const& a, Issue const& iss);
585/***************************************************************/
586
587/* Check */
588/***************************************************************/
589namespace check {
590
592// clang-format off
593template <typename A>
596create(A const& account, A const& dest, STAmount const& sendMax)
597{
598 Json::Value jv;
599 jv[sfAccount.jsonName] = to_string(account);
600 jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
601 jv[sfDestination.jsonName] = to_string(dest);
602 jv[sfTransactionType.jsonName] = jss::CheckCreate;
603 return jv;
604}
605// clang-format on
606
607inline Json::Value
609 jtx::Account const& account,
610 jtx::Account const& dest,
611 STAmount const& sendMax)
612{
613 return create(account.id(), dest.id(), sendMax);
614}
615
616} // namespace check
617
618static constexpr FeeLevel64 baseFeeLevel{256};
620
621template <class Suite>
622void
624 Suite& test,
625 jtx::Env& env,
626 std::size_t expectedCount,
627 std::optional<std::size_t> expectedMaxCount,
628 std::size_t expectedInLedger,
629 std::size_t expectedPerLedger,
630 std::uint64_t expectedMinFeeLevel = baseFeeLevel.fee(),
631 std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(),
632 source_location const location = source_location::current())
633{
634 int line = location.line();
635 char const* file = location.file_name();
636 FeeLevel64 const expectedMin{expectedMinFeeLevel};
637 FeeLevel64 const expectedMed{expectedMedFeeLevel};
638 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
639 using namespace std::string_literals;
640
642 ? test.pass()
643 : test.fail(
644 "reference: "s +
645 std::to_string(metrics.referenceFeeLevel.value()) + "/" +
647 file,
648 line);
649
650 metrics.txCount == expectedCount
651 ? test.pass()
652 : test.fail(
653 "txCount: "s + std::to_string(metrics.txCount) + "/" +
654 std::to_string(expectedCount),
655 file,
656 line);
657
658 metrics.txQMaxSize == expectedMaxCount
659 ? test.pass()
660 : test.fail(
661 "txQMaxSize: "s + std::to_string(metrics.txQMaxSize.value_or(0)) +
662 "/" + std::to_string(expectedMaxCount.value_or(0)),
663 file,
664 line);
665
666 metrics.txInLedger == expectedInLedger
667 ? test.pass()
668 : test.fail(
669 "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" +
670 std::to_string(expectedInLedger),
671 file,
672 line);
673
674 metrics.txPerLedger == expectedPerLedger
675 ? test.pass()
676 : test.fail(
677 "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" +
678 std::to_string(expectedPerLedger),
679 file,
680 line);
681
682 metrics.minProcessingFeeLevel == expectedMin
683 ? test.pass()
684 : test.fail(
685 "minProcessingFeeLevel: "s +
686 std::to_string(metrics.minProcessingFeeLevel.value()) + "/" +
687 std::to_string(expectedMin.value()),
688 file,
689 line);
690
691 metrics.medFeeLevel == expectedMed
692 ? test.pass()
693 : test.fail(
694 "medFeeLevel: "s + std::to_string(metrics.medFeeLevel.value()) +
695 "/" + std::to_string(expectedMed.value()),
696 file,
697 line);
698
699 auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger
700 ? expectedMed * expectedInLedger * expectedInLedger /
701 (expectedPerLedger * expectedPerLedger)
702 : metrics.referenceFeeLevel;
703
704 metrics.openLedgerFeeLevel == expectedCurFeeLevel
705 ? test.pass()
706 : test.fail(
707 "openLedgerFeeLevel: "s +
708 std::to_string(metrics.openLedgerFeeLevel.value()) + "/" +
709 std::to_string(expectedCurFeeLevel.value()),
710 file,
711 line);
712}
713
714} // namespace jtx
715} // namespace test
716} // namespace ripple
717
718#endif // RIPPLE_TEST_JTX_TESTHELPERS_H_INCLUDED
T begin(T... args)
Represents a JSON value.
Definition json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
bool isNull() const
isNull() tests to see if this field is null.
virtual TxQ & getTxQ()=0
A currency issued by an account.
Definition Issue.h:33
std::chrono::time_point< NetClock > time_point
Definition chrono.h:69
std::uint32_t rep
Definition chrono.h:66
A public key.
Definition PublicKey.h:61
A view into a ledger.
Definition ReadView.h:51
Identifies fields.
Definition SField.h:144
Json::StaticString const jsonName
Definition SField.h:170
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:795
std::vector< STPath >::const_iterator end() const
Definition STPathSet.h:496
void push_back(STPath const &e)
Definition STPathSet.h:514
std::vector< STPath >::const_iterator begin() const
Definition STPathSet.h:490
std::vector< STPath >::size_type size() const
Definition STPathSet.h:502
int addRaw(Blob const &vector)
std::string getString() const
Definition Serializer.h:238
An immutable linear range of bytes.
Definition Slice.h:46
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1778
Immutable cryptographic account descriptor.
Definition Account.h:39
AccountID id() const
Returns the Account ID.
Definition Account.h:111
A transaction testing environment.
Definition Env.h:121
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
Application & app()
Definition Env.h:261
Converts to IOU Issue or STAmount.
A balance matches.
Definition balance.h:39
Set Expiration on a JTx.
constexpr value_type fee() const
Returns the number of drops.
Definition Units.h:296
constexpr value_type value() const
Returns the underlying value.
Definition Units.h:343
T end(T... args)
T find(T... args)
T is_same_v
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
static constexpr FeeLevel64 minEscalationFeeLevel
bool checkArraySize(Json::Value const &val, unsigned int size)
std::uint32_t ownerCount(Env const &env, Account const &account)
Json::Value ledgerEntryRoot(Env &env, Account const &acct)
auto make_vector(Input const &input)
auto const data
General field definitions, or fields used in multiple transaction namespaces.
bool expectOffers(Env &env, AccountID const &account, std::uint16_t size, std::vector< Amounts > const &toMatch)
STAmount channelBalance(ReadView const &view, uint256 const &chan)
uint256 channel(AccountID const &account, AccountID const &dst, std::uint32_t seqProxyValue)
PrettyAmount xrpMinusFee(Env const &env, std::int64_t xrpAmount)
bool equal(STAmount const &sa1, STAmount const &sa2)
bool checkVL(Slice const &result, std::string const &expected)
Json::Value getAccountLines(Env &env, AccountID const &acctId)
bool same(STPathSet const &st1, Args const &... args)
Json::Value ledgerEntryState(Env &env, Account const &acct_a, Account const &acct_b, std::string const &currency)
void stpath_append_one(STPath &st, Account const &account)
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
void stpath_append(STPath &st, T const &t, Args const &... args)
void n_offers(Env &env, std::size_t n, Account const &account, STAmount const &in, STAmount const &out)
Json::Value accountBalance(Env &env, Account const &acct)
STPathElement IPE(Issue const &iss)
void checkMetrics(Suite &test, jtx::Env &env, std::size_t expectedCount, std::optional< std::size_t > expectedMaxCount, std::size_t expectedInLedger, std::size_t expectedPerLedger, std::uint64_t expectedMinFeeLevel=baseFeeLevel.fee(), std::uint64_t expectedMedFeeLevel=minEscalationFeeLevel.fee(), source_location const location=source_location::current())
Json::Value claim(AccountID const &account, uint256 const &channel, std::optional< STAmount > const &balance, std::optional< STAmount > const &amount, std::optional< Slice > const &signature, std::optional< PublicKey > const &pk)
static constexpr FeeLevel64 baseFeeLevel
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
bool expectHolding(Env &env, AccountID const &account, STAmount const &value, bool defaultLimits)
void stpathset_append(STPathSet &st, STPath const &p, Args const &... args)
STPathElement cpe(Currency const &c)
bool expectLedgerEntryRoot(Env &env, Account const &acct, STAmount const &expectedValue)
STPathElement allpe(AccountID const &a, Issue const &iss)
bool channelExists(ReadView const &view, uint256 const &chan)
XRPAmount txfee(Env const &env, std::uint16_t n)
STPath stpath(Args const &... args)
Json::Value getAccountOffers(Env &env, AccountID const &acct, bool current)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
TypedField< STBlob > SF_VL
Definition SField.h:364
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
TypedField< STBitString< 256 > > SF_UINT256
Definition SField.h:355
TypedField< STInteger< std::uint32_t > > SF_UINT32
Definition SField.h:349
base_uint< 256 > uint256
Definition base_uint.h:558
TypedField< STAccount > SF_ACCOUNT
Definition SField.h:359
@ current
This was a new validation and was added.
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
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
Definition TxQ.h:178
A field with a type known at compile time.
Definition SField.h:318
JF operator()(std::array< std::uint8_t, N > const &c) const
JF operator()(SV const &value) const
Generic helper class for helper clases that set a field on a JTx.
Definition TestHelpers.h:59
virtual OV value() const =0
JTxField(SF const &sfield, SV const &value)
Definition TestHelpers.h:69
virtual ~JTxField()=default
virtual void operator()(Env &, JTx &jt) const
Definition TestHelpers.h:80
Execution context for applying a JSON transaction.
Definition JTx.h:45
Json::Value jv
Definition JTx.h:46
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
accountIDField(SF const &sfield, SV const &value)
blobField(SF const &sfield, Slice const &cond)
blobField(SF const &sfield, std::array< std::uint8_t, N > const &c)
timePointField(SF const &sfield, SV const &value)
uint256Field(SF const &sfield, SV const &value)
unit::ValueUnit< UnitTag, ValueType > SV
T time_since_epoch(T... args)
T to_string(T... args)
T value_or(T... args)