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/******************************************************************************/
505namespace paychan {
506
508create(
509 AccountID const& account,
510 AccountID const& to,
511 STAmount const& amount,
512 NetClock::duration const& settleDelay,
513 PublicKey const& pk,
516
517inline Json::Value
519 Account const& account,
520 Account const& to,
521 STAmount const& amount,
522 NetClock::duration const& settleDelay,
523 PublicKey const& pk,
526{
527 return create(
528 account.id(), to.id(), amount, settleDelay, pk, cancelAfter, dstTag);
529}
530
532fund(
533 AccountID const& account,
534 uint256 const& channel,
535 STAmount const& amount,
537
539claim(
540 AccountID const& account,
541 uint256 const& channel,
544 std::optional<Slice> const& signature = std::nullopt,
546
548channel(
549 AccountID const& account,
550 AccountID const& dst,
551 std::uint32_t seqProxyValue);
552
553inline uint256
554channel(Account const& account, Account const& dst, std::uint32_t seqProxyValue)
555{
556 return channel(account.id(), dst.id(), seqProxyValue);
557}
558
560channelBalance(ReadView const& view, uint256 const& chan);
561
562bool
563channelExists(ReadView const& view, uint256 const& chan);
564
565} // namespace paychan
566
567/* Crossing Limits */
568/******************************************************************************/
569
570void
572 Env& env,
573 std::size_t n,
574 Account const& account,
575 STAmount const& in,
576 STAmount const& out);
577
578/* Pay Strand */
579/***************************************************************/
580
581// Currency path element
583cpe(Currency const& c);
584
585// All path element
587allpe(AccountID const& a, Issue const& iss);
588/***************************************************************/
589
590/* Check */
591/***************************************************************/
592namespace check {
593
595// clang-format off
596template <typename A>
599create(A const& account, A const& dest, STAmount const& sendMax)
600{
601 Json::Value jv;
602 jv[sfAccount.jsonName] = to_string(account);
603 jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
604 jv[sfDestination.jsonName] = to_string(dest);
605 jv[sfTransactionType.jsonName] = jss::CheckCreate;
606 return jv;
607}
608// clang-format on
609
610inline Json::Value
612 jtx::Account const& account,
613 jtx::Account const& dest,
614 STAmount const& sendMax)
615{
616 return create(account.id(), dest.id(), sendMax);
617}
618
619} // namespace check
620
621static constexpr FeeLevel64 baseFeeLevel{256};
623
624template <class Suite>
625void
627 Suite& test,
628 jtx::Env& env,
629 std::size_t expectedCount,
630 std::optional<std::size_t> expectedMaxCount,
631 std::size_t expectedInLedger,
632 std::size_t expectedPerLedger,
633 std::uint64_t expectedMinFeeLevel = baseFeeLevel.fee(),
634 std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(),
635 source_location const location = source_location::current())
636{
637 int line = location.line();
638 char const* file = location.file_name();
639 FeeLevel64 const expectedMin{expectedMinFeeLevel};
640 FeeLevel64 const expectedMed{expectedMedFeeLevel};
641 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
642 using namespace std::string_literals;
643
645 ? test.pass()
646 : test.fail(
647 "reference: "s +
648 std::to_string(metrics.referenceFeeLevel.value()) + "/" +
650 file,
651 line);
652
653 metrics.txCount == expectedCount
654 ? test.pass()
655 : test.fail(
656 "txCount: "s + std::to_string(metrics.txCount) + "/" +
657 std::to_string(expectedCount),
658 file,
659 line);
660
661 metrics.txQMaxSize == expectedMaxCount
662 ? test.pass()
663 : test.fail(
664 "txQMaxSize: "s + std::to_string(metrics.txQMaxSize.value_or(0)) +
665 "/" + std::to_string(expectedMaxCount.value_or(0)),
666 file,
667 line);
668
669 metrics.txInLedger == expectedInLedger
670 ? test.pass()
671 : test.fail(
672 "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" +
673 std::to_string(expectedInLedger),
674 file,
675 line);
676
677 metrics.txPerLedger == expectedPerLedger
678 ? test.pass()
679 : test.fail(
680 "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" +
681 std::to_string(expectedPerLedger),
682 file,
683 line);
684
685 metrics.minProcessingFeeLevel == expectedMin
686 ? test.pass()
687 : test.fail(
688 "minProcessingFeeLevel: "s +
689 std::to_string(metrics.minProcessingFeeLevel.value()) + "/" +
690 std::to_string(expectedMin.value()),
691 file,
692 line);
693
694 metrics.medFeeLevel == expectedMed
695 ? test.pass()
696 : test.fail(
697 "medFeeLevel: "s + std::to_string(metrics.medFeeLevel.value()) +
698 "/" + std::to_string(expectedMed.value()),
699 file,
700 line);
701
702 auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger
703 ? expectedMed * expectedInLedger * expectedInLedger /
704 (expectedPerLedger * expectedPerLedger)
705 : metrics.referenceFeeLevel;
706
707 metrics.openLedgerFeeLevel == expectedCurFeeLevel
708 ? test.pass()
709 : test.fail(
710 "openLedgerFeeLevel: "s +
711 std::to_string(metrics.openLedgerFeeLevel.value()) + "/" +
712 std::to_string(expectedCurFeeLevel.value()),
713 file,
714 line);
715}
716
717} // namespace jtx
718} // namespace test
719} // namespace ripple
720
721#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:146
Json::StaticString const jsonName
Definition SField.h:172
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.
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: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)