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