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 <xrpld/app/misc/TxQ.h>
26
27#include <xrpl/basics/base_uint.h>
28#include <xrpl/beast/unit_test/suite.h>
29#include <xrpl/json/json_value.h>
30#include <xrpl/protocol/AccountID.h>
31#include <xrpl/protocol/Quality.h>
32#include <xrpl/protocol/STNumber.h>
33#include <xrpl/protocol/TxFlags.h>
34#include <xrpl/protocol/Units.h>
35#include <xrpl/protocol/jss.h>
36
37#include <vector>
38
39#if (defined(__clang_major__) && __clang_major__ < 15)
40#include <experimental/source_location>
42#else
43#include <source_location>
45#endif
46
47namespace ripple {
48namespace test {
49namespace jtx {
50
56template <
57 class SField,
58 class StoredValue = typename SField::type::value_type,
59 class OutputValue = StoredValue>
61{
62 using SF = SField;
63 using SV = StoredValue;
64 using OV = OutputValue;
65
66protected:
67 SF const& sfield_;
69
70public:
71 explicit JTxField(SF const& sfield, SV const& value)
72 : sfield_(sfield), value_(value)
73 {
74 }
75
76 virtual ~JTxField() = default;
77
78 virtual OV
79 value() const = 0;
80
81 virtual void
82 operator()(Env&, JTx& jt) const
83 {
84 jt.jv[sfield_.jsonName] = value();
85 }
86};
87
88template <class SField, class StoredValue>
89struct JTxField<SField, StoredValue, StoredValue>
90{
91 using SF = SField;
92 using SV = StoredValue;
93 using OV = SV;
94
95protected:
96 SF const& sfield_;
98
99public:
100 explicit JTxField(SF const& sfield, SV const& value)
101 : sfield_(sfield), value_(value)
102 {
103 }
104
105 void
106 operator()(Env&, JTx& jt) const
107 {
109 }
110};
111
113 : public JTxField<SF_UINT32, NetClock::time_point, NetClock::rep>
114{
115 using SF = SF_UINT32;
119
120protected:
121 using base::value_;
122
123public:
124 explicit timePointField(SF const& sfield, SV const& value)
125 : JTxField(sfield, value)
126 {
127 }
128
129 OV
130 value() const override
131 {
132 return value_.time_since_epoch().count();
133 }
134};
135
136struct uint256Field : public JTxField<SF_UINT256, uint256, std::string>
137{
138 using SF = SF_UINT256;
139 using SV = uint256;
142
143protected:
144 using base::value_;
145
146public:
147 explicit uint256Field(SF const& sfield, SV const& value)
148 : JTxField(sfield, value)
149 {
150 }
151
152 OV
153 value() const override
154 {
155 return to_string(value_);
156 }
157};
158
159struct accountIDField : public JTxField<SF_ACCOUNT, AccountID, std::string>
160{
161 using SF = SF_ACCOUNT;
162 using SV = AccountID;
165
166protected:
167 using base::value_;
168
169public:
170 explicit accountIDField(SF const& sfield, SV const& value)
171 : JTxField(sfield, value)
172 {
173 }
174
175 OV
176 value() const override
177 {
178 return toBase58(value_);
179 }
180};
181
182struct blobField : public JTxField<SF_VL, std::string>
183{
184 using SF = SF_VL;
187
188 using JTxField::JTxField;
189
190 explicit blobField(SF const& sfield, Slice const& cond)
191 : JTxField(sfield, strHex(cond))
192 {
193 }
194
195 template <size_t N>
196 explicit blobField(SF const& sfield, std::array<std::uint8_t, N> const& c)
197 : blobField(sfield, makeSlice(c))
198 {
199 }
200};
201
202template <class SField, class UnitTag, class ValueType>
204 : public JTxField<SField, unit::ValueUnit<UnitTag, ValueType>, ValueType>
205{
206 using SF = SField;
208 using OV = ValueType;
210
212
213protected:
214 using base::value_;
215
216public:
217 using JTxField<SF, SV, OV>::JTxField;
218
219 OV
220 value() const override
221 {
222 return value_.value();
223 }
224};
225
226template <class JTxField>
228{
229 using JF = JTxField;
230 using SF = typename JF::SF;
231 using SV = typename JF::SV;
232
233protected:
234 SF const& sfield_;
235
236public:
237 explicit JTxFieldWrapper(SF const& sfield) : sfield_(sfield)
238 {
239 }
240
241 JF
242 operator()(SV const& value) const
243 {
244 return JTxField(sfield_, value);
245 }
246};
247
248template <>
250{
251 using JF = blobField;
252 using SF = JF::SF;
253 using SV = JF::SV;
254
255protected:
256 SF const& sfield_;
257
258public:
259 explicit JTxFieldWrapper(SF const& sfield) : sfield_(sfield)
260 {
261 }
262
263 JF
264 operator()(SV const& cond) const
265 {
266 return JF(sfield_, makeSlice(cond));
267 }
268
269 JF
270 operator()(Slice const& cond) const
271 {
272 return JF(sfield_, cond);
273 }
274
275 template <size_t N>
276 JF
278 {
279 return operator()(makeSlice(c));
280 }
281};
282
283template <
284 class SField,
285 class UnitTag,
286 class ValueType = typename SField::type::value_type>
289
290template <class SField, class StoredValue = typename SField::type::value_type>
292
295auto const data = JTxFieldWrapper<blobField>(sfData);
296
297// TODO We only need this long "requires" clause as polyfill, for C++20
298// implementations which are missing <ranges> header. Replace with
299// `std::ranges::range<Input>`, and accordingly use std::ranges::begin/end
300// when we have moved to better compilers.
301template <typename Input>
302auto
303make_vector(Input const& input)
304 requires requires(Input& v) {
305 std::begin(v);
306 std::end(v);
307 }
308{
309 return std::vector(std::begin(input), std::end(input));
310}
311
312// Functions used in debugging
314getAccountOffers(Env& env, AccountID const& acct, bool current = false);
315
316inline Json::Value
317getAccountOffers(Env& env, Account const& acct, bool current = false)
318{
319 return getAccountOffers(env, acct.id(), current);
320}
321
323getAccountLines(Env& env, AccountID const& acctId);
324
325inline Json::Value
326getAccountLines(Env& env, Account const& acct)
327{
328 return getAccountLines(env, acct.id());
329}
330
331template <typename... IOU>
333getAccountLines(Env& env, AccountID const& acctId, IOU... ious)
334{
335 auto const jrr = getAccountLines(env, acctId);
336 Json::Value res;
337 for (auto const& line : jrr[jss::lines])
338 {
339 for (auto const& iou : {ious...})
340 {
341 if (line[jss::currency].asString() == to_string(iou.currency))
342 {
343 Json::Value v;
344 v[jss::currency] = line[jss::currency];
345 v[jss::balance] = line[jss::balance];
346 v[jss::limit] = line[jss::limit];
347 v[jss::account] = line[jss::account];
348 res[jss::lines].append(v);
349 }
350 }
351 }
352 if (!res.isNull())
353 return res;
354 return jrr;
355}
356
357[[nodiscard]] bool
358checkArraySize(Json::Value const& val, unsigned int size);
359
360// Helper function that returns the owner count on an account.
362ownerCount(test::jtx::Env const& env, test::jtx::Account const& account);
363
364[[nodiscard]]
365inline bool
366checkVL(Slice const& result, std::string const& expected)
367{
368 Serializer s;
369 s.addRaw(result);
370 return s.getString() == expected;
371}
372
373[[nodiscard]]
374inline bool
377 SField const& field,
378 std::string const& expected)
379{
380 return strHex(expected) == strHex(sle->getFieldVL(field));
381}
382
383/* Path finding */
384/******************************************************************************/
385void
386stpath_append_one(STPath& st, Account const& account);
387
388template <class T>
390stpath_append_one(STPath& st, T const& t)
391{
393}
394
395void
397
398template <class T, class... Args>
399void
400stpath_append(STPath& st, T const& t, Args const&... args)
401{
402 stpath_append_one(st, t);
403 if constexpr (sizeof...(args) > 0)
404 stpath_append(st, args...);
405}
406
407template <class... Args>
408void
409stpathset_append(STPathSet& st, STPath const& p, Args const&... args)
410{
411 st.push_back(p);
412 if constexpr (sizeof...(args) > 0)
413 stpathset_append(st, args...);
414}
415
416bool
417equal(STAmount const& sa1, STAmount const& sa2);
418
419// Issue path element
421IPE(Issue const& iss);
422
423template <class... Args>
424STPath
425stpath(Args const&... args)
426{
427 STPath st;
428 stpath_append(st, args...);
429 return st;
430}
431
432template <class... Args>
433bool
434same(STPathSet const& st1, Args const&... args)
435{
436 STPathSet st2;
437 stpathset_append(st2, args...);
438 if (st1.size() != st2.size())
439 return false;
440
441 for (auto const& p : st2)
442 {
443 if (std::find(st1.begin(), st1.end(), p) == st1.end())
444 return false;
445 }
446 return true;
447}
448
449/******************************************************************************/
450
452txfee(Env const& env, std::uint16_t n);
453
455xrpMinusFee(Env const& env, std::int64_t xrpAmount);
456
457bool
459 Env& env,
460 AccountID const& account,
461 STAmount const& value,
462 bool defaultLimits = false);
463
464template <typename... Amts>
465bool
467 Env& env,
468 AccountID const& account,
469 STAmount const& value,
470 Amts const&... amts)
471{
472 return expectHolding(env, account, value, false) &&
473 expectHolding(env, account, amts...);
474}
475
476bool
477expectHolding(Env& env, AccountID const& account, None const& value);
478
479bool
481 Env& env,
482 AccountID const& account,
483 std::uint16_t size,
484 std::vector<Amounts> const& toMatch = {});
485
487ledgerEntryRoot(Env& env, Account const& acct);
488
491 Env& env,
492 Account const& acct_a,
493 Account const& acct_b,
494 std::string const& currency);
495
497accountBalance(Env& env, Account const& acct);
498
499[[nodiscard]] bool
501 Env& env,
502 Account const& acct,
503 STAmount const& expectedValue);
504
505/* Payment Channel */
506/******************************************************************************/
507namespace paychan {
508
510create(
511 AccountID const& account,
512 AccountID const& to,
513 STAmount const& amount,
514 NetClock::duration const& settleDelay,
515 PublicKey const& pk,
518
519inline Json::Value
521 Account const& account,
522 Account const& to,
523 STAmount const& amount,
524 NetClock::duration const& settleDelay,
525 PublicKey const& pk,
528{
529 return create(
530 account.id(), to.id(), amount, settleDelay, pk, cancelAfter, dstTag);
531}
532
534fund(
535 AccountID const& account,
536 uint256 const& channel,
537 STAmount const& amount,
539
541claim(
542 AccountID const& account,
543 uint256 const& channel,
546 std::optional<Slice> const& signature = std::nullopt,
548
550channel(
551 AccountID const& account,
552 AccountID const& dst,
553 std::uint32_t seqProxyValue);
554
555inline uint256
556channel(Account const& account, Account const& dst, std::uint32_t seqProxyValue)
557{
558 return channel(account.id(), dst.id(), seqProxyValue);
559}
560
562channelBalance(ReadView const& view, uint256 const& chan);
563
564bool
565channelExists(ReadView const& view, uint256 const& chan);
566
567} // namespace paychan
568
569/* Crossing Limits */
570/******************************************************************************/
571
572void
574 Env& env,
575 std::size_t n,
576 Account const& account,
577 STAmount const& in,
578 STAmount const& out);
579
580/* Pay Strand */
581/***************************************************************/
582
583// Currency path element
585cpe(Currency const& c);
586
587// All path element
589allpe(AccountID const& a, Issue const& iss);
590/***************************************************************/
591
592/* Check */
593/***************************************************************/
594namespace check {
595
597// clang-format off
598template <typename A>
601create(A const& account, A const& dest, STAmount const& sendMax)
602{
603 Json::Value jv;
604 jv[sfAccount.jsonName] = to_string(account);
605 jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
606 jv[sfDestination.jsonName] = to_string(dest);
607 jv[sfTransactionType.jsonName] = jss::CheckCreate;
608 return jv;
609}
610// clang-format on
611
612inline Json::Value
614 jtx::Account const& account,
615 jtx::Account const& dest,
616 STAmount const& sendMax)
617{
618 return create(account.id(), dest.id(), sendMax);
619}
620
621} // namespace check
622
625
626template <class Suite>
627void
629 Suite& test,
630 jtx::Env& env,
631 std::size_t expectedCount,
632 std::optional<std::size_t> expectedMaxCount,
633 std::size_t expectedInLedger,
634 std::size_t expectedPerLedger,
635 std::uint64_t expectedMinFeeLevel = baseFeeLevel.fee(),
636 std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(),
637 source_location const location = source_location::current())
638{
639 int line = location.line();
640 char const* file = location.file_name();
641 FeeLevel64 const expectedMin{expectedMinFeeLevel};
642 FeeLevel64 const expectedMed{expectedMedFeeLevel};
643 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
644 using namespace std::string_literals;
645
647 ? test.pass()
648 : test.fail(
649 "reference: "s +
650 std::to_string(metrics.referenceFeeLevel.value()) + "/" +
652 file,
653 line);
654
655 metrics.txCount == expectedCount
656 ? test.pass()
657 : test.fail(
658 "txCount: "s + std::to_string(metrics.txCount) + "/" +
659 std::to_string(expectedCount),
660 file,
661 line);
662
663 metrics.txQMaxSize == expectedMaxCount
664 ? test.pass()
665 : test.fail(
666 "txQMaxSize: "s + std::to_string(metrics.txQMaxSize.value_or(0)) +
667 "/" + std::to_string(expectedMaxCount.value_or(0)),
668 file,
669 line);
670
671 metrics.txInLedger == expectedInLedger
672 ? test.pass()
673 : test.fail(
674 "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" +
675 std::to_string(expectedInLedger),
676 file,
677 line);
678
679 metrics.txPerLedger == expectedPerLedger
680 ? test.pass()
681 : test.fail(
682 "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" +
683 std::to_string(expectedPerLedger),
684 file,
685 line);
686
687 metrics.minProcessingFeeLevel == expectedMin
688 ? test.pass()
689 : test.fail(
690 "minProcessingFeeLevel: "s +
691 std::to_string(metrics.minProcessingFeeLevel.value()) + "/" +
692 std::to_string(expectedMin.value()),
693 file,
694 line);
695
696 metrics.medFeeLevel == expectedMed
697 ? test.pass()
698 : test.fail(
699 "medFeeLevel: "s + std::to_string(metrics.medFeeLevel.value()) +
700 "/" + std::to_string(expectedMed.value()),
701 file,
702 line);
703
704 auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger
705 ? expectedMed * expectedInLedger * expectedInLedger /
706 (expectedPerLedger * expectedPerLedger)
707 : metrics.referenceFeeLevel;
708
709 metrics.openLedgerFeeLevel == expectedCurFeeLevel
710 ? test.pass()
711 : test.fail(
712 "openLedgerFeeLevel: "s +
713 std::to_string(metrics.openLedgerFeeLevel.value()) + "/" +
714 std::to_string(expectedCurFeeLevel.value()),
715 file,
716 line);
717}
718
719} // namespace jtx
720} // namespace test
721} // namespace ripple
722
723#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:62
A view into a ledger.
Definition ReadView.h:51
Identifies fields.
Definition SField.h:146
Json::StaticString const jsonName
Definition SField.h:172
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:772
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:1776
static constexpr FeeLevel64 baseLevel
Fee level for single-signed reference transaction.
Definition TxQ.h:64
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.
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: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:369
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
TypedField< STBitString< 256 > > SF_UINT256
Definition SField.h:357
TypedField< STInteger< std::uint32_t > > SF_UINT32
Definition SField.h:351
base_uint< 256 > uint256
Definition base_uint.h:558
TypedField< STAccount > SF_ACCOUNT
Definition SField.h:364
@ 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:320
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:61
virtual OV value() const =0
JTxField(SF const &sfield, SV const &value)
Definition TestHelpers.h:71
virtual ~JTxField()=default
virtual void operator()(Env &, JTx &jt) const
Definition TestHelpers.h:82
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)