xrpld
Loading...
Searching...
No Matches
Regression_test.cpp
1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/Env_ss.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h> // IWYU pragma: keep
6#include <test/jtx/check.h>
7#include <test/jtx/envconfig.h>
8#include <test/jtx/fee.h>
9#include <test/jtx/jtx_json.h>
10#include <test/jtx/noop.h>
11#include <test/jtx/offer.h>
12#include <test/jtx/owners.h>
13#include <test/jtx/pay.h>
14#include <test/jtx/require.h>
15#include <test/jtx/rpc.h>
16#include <test/jtx/seq.h>
17#include <test/jtx/sig.h>
18#include <test/jtx/tags.h>
19#include <test/jtx/ter.h>
20
21#include <xrpld/app/ledger/LedgerMaster.h>
22
23#include <xrpl/basics/CountedObject.h>
24#include <xrpl/basics/SHAMapHash.h>
25#include <xrpl/basics/StringUtilities.h>
26#include <xrpl/basics/base_uint.h>
27#include <xrpl/beast/unit_test/suite.h>
28#include <xrpl/config/Constants.h>
29#include <xrpl/json/json_reader.h>
30#include <xrpl/json/json_value.h>
31#include <xrpl/ledger/ApplyView.h>
32#include <xrpl/ledger/Ledger.h>
33#include <xrpl/ledger/OpenView.h>
34#include <xrpl/protocol/Indexes.h>
35#include <xrpl/protocol/KeyType.h>
36#include <xrpl/protocol/SField.h>
37#include <xrpl/protocol/STTx.h>
38#include <xrpl/protocol/SystemParameters.h>
39#include <xrpl/protocol/TER.h>
40#include <xrpl/protocol/TxFormats.h>
41#include <xrpl/protocol/XRPAmount.h>
42#include <xrpl/protocol/jss.h>
43#include <xrpl/tx/apply.h>
44
45#include <boost/asio/buffer.hpp>
46
47#include <cassert>
48#include <chrono>
49#include <cstdint>
50#include <map>
51#include <memory>
52#include <optional>
53#include <string>
54#include <vector>
55
56namespace xrpl::test {
57
59{
60 // OfferCreate, then OfferCreate with cancel
61 void
63 {
64 using namespace jtx;
65 Env env(*this);
66 auto const gw = Account("gw");
67 auto const usd = gw["USD"];
68 env.fund(XRP(10000), "alice", gw);
69 env(offer("alice", usd(10), XRP(10)), Require(Owners("alice", 1)));
70 env(offer("alice", usd(20), XRP(10)),
71 Json(R"raw(
72 { "OfferSequence" : 4 }
73 )raw"),
74 Require(Owners("alice", 1)));
75 }
77 void
79 {
80 testcase("Account balance < fee destroys correct amount of XRP");
81 using namespace jtx;
82 Env env(*this);
83 env.memoize("alice");
84
85 // The low balance scenario can not deterministically
86 // be reproduced against an open ledger. Make a local
87 // closed ledger and work with it directly.
88 auto closed = std::make_shared<Ledger>(
90 Rules{env.app().config().features},
91 env.app().config().fees.toFees(),
93 env.app().getNodeFamily());
94 auto expectedDrops = kInitialXrp;
95 BEAST_EXPECT(closed->header().drops == expectedDrops);
96
97 auto const aliceXRP = 400;
98 auto const aliceAmount = XRP(aliceXRP);
99
100 auto next = std::make_shared<Ledger>(*closed, env.app().getTimeKeeper().closeTime());
101 {
102 // Fund alice
103 auto const jt = env.jt(pay(env.master, "alice", aliceAmount));
104 OpenView accum(&*next);
105
106 auto const result = xrpl::apply(env.app(), accum, *jt.stx, TapNone, env.journal);
107 BEAST_EXPECT(isTesSuccess(result.ter));
108 BEAST_EXPECT(result.applied);
109
110 accum.apply(*next);
111 }
112 expectedDrops -= next->fees().base;
113 BEAST_EXPECT(next->header().drops == expectedDrops);
114 {
115 auto const sle = next->read(keylet::account(Account("alice").id()));
116 BEAST_EXPECT(sle);
117 auto balance = sle->getFieldAmount(sfBalance);
118
119 BEAST_EXPECT(balance == aliceAmount);
120 }
121
122 {
123 // Specify the seq manually since the env's open ledger
124 // doesn't know about this account.
125 auto const jt = env.jt(noop("alice"), Fee(expectedDrops), Seq(2));
126
127 OpenView accum(&*next);
128
129 auto const result = xrpl::apply(env.app(), accum, *jt.stx, TapNone, env.journal);
130 BEAST_EXPECT(result.ter == tecINSUFF_FEE);
131 BEAST_EXPECT(result.applied);
132
133 accum.apply(*next);
134 }
135 {
136 auto const sle = next->read(keylet::account(Account("alice").id()));
137 BEAST_EXPECT(sle);
138 auto balance = sle->getFieldAmount(sfBalance);
139
140 BEAST_EXPECT(balance == XRP(0));
141 }
142 expectedDrops -= aliceXRP * kDropsPerXrp;
143 BEAST_EXPECT(next->header().drops == expectedDrops);
144 }
146 void
148 {
149 testcase("Signing with a secp256r1 key should fail gracefully");
150 using namespace jtx;
151 Env env(*this);
152
153 // Test case we'll use.
154 auto test256r1key = [&env](Account const& acct) {
155 auto const baseFee = env.current()->fees().base;
156 std::uint32_t const acctSeq = env.seq(acct);
157 json::Value const jsonNoOp =
158 env.json(noop(acct), Fee(baseFee), Seq(acctSeq), Sig(acct));
159 JTx jt = env.jt(jsonNoOp);
160 jt.fillSig = false;
161
162 // Random secp256r1 public key generated by
163 // https://kjur.github.io/jsrsasign/sample-ecdsa.html
164 std::string const secp256r1PubKey =
165 "045d02995ec24988d9a2ae06a3733aa35ba0741e87527"
166 "ed12909b60bd458052c944b24cbf5893c3e5be321774e"
167 "5082e11c034b765861d0effbde87423f8476bb2c";
168
169 // Set the key in the JSON.
170 jt.jv["SigningPubKey"] = secp256r1PubKey;
171
172 // Set the same key in the STTx.
173 auto secp256r1Sig = std::make_unique<STTx>(*(jt.stx));
174 auto pubKeyBlob = strUnHex(secp256r1PubKey);
175 assert(pubKeyBlob); // Hex for public key must be valid
176 secp256r1Sig->setFieldVL(sfSigningPubKey, *pubKeyBlob);
177 jt.stx.reset(secp256r1Sig.release());
178
179 env(jt, Rpc("invalidTransaction", "fails local checks: Invalid signature."));
180 };
181
182 Account const alice{"alice", KeyType::Secp256k1};
183 Account const becky{"becky", KeyType::Ed25519};
184
185 env.fund(XRP(10000), alice, becky);
186
187 test256r1key(alice);
188 test256r1key(becky);
189 }
191 void
193 {
194 testcase("Autofilled fee should use the escalated fee");
195 using namespace jtx;
196 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
198 cfg->fees.referenceFee = 10;
199 return cfg;
200 }));
201 EnvSs envs(env);
202
203 auto const alice = Account("alice");
204 env.fund(XRP(100000), alice);
205
207 // Max fee = 50k drops
208 params[jss::fee_mult_max] = 5000;
209 std::vector<int> const expectedFees({10, 10, 8889, 13889, 20000});
210
211 // We should be able to submit 5 transactions within
212 // our fee limit.
213 for (int i = 0; i < 5; ++i)
214 {
215 envs(noop(alice), Fee(kNone), Seq(kNone))(params);
216
217 auto tx = env.tx();
218 if (BEAST_EXPECT(tx))
219 {
220 BEAST_EXPECT(tx->getAccountID(sfAccount) == alice.id());
221 BEAST_EXPECT(tx->getTxnType() == ttACCOUNT_SET);
222 auto const fee = tx->getFieldAmount(sfFee);
223 BEAST_EXPECT(fee == drops(expectedFees[i]));
224 }
225 }
226 }
228 void
230 {
231 testcase("Fee escalation shouldn't allocate extreme memory");
232 using clock_type = std::chrono::steady_clock;
233 using namespace jtx;
234 using namespace std::chrono_literals;
235
236 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
237 auto& s = cfg->section(Sections::kTransactionQueue);
238 s.set(Keys::kMinimumTxnInLedgerStandalone, "4294967295");
239 s.set(Keys::kMinimumTxnInLedger, "4294967295");
240 s.set(Keys::kTargetTxnInLedger, "4294967295");
241 s.set(Keys::kNormalConsensusIncreasePercent, "4294967295");
242
243 return cfg;
244 }));
245
246 env(noop(env.master));
247 // This test will probably fail if any breakpoints are encountered,
248 // but should pass on even the slowest machines.
249 auto const start = clock_type::now();
250 env.close();
251 BEAST_EXPECT(clock_type::now() - start < 1s);
252 }
254 void
256 {
257 using namespace jtx;
258 using boost::asio::buffer;
259 testcase("jsonInvalid");
260
261 std::string const request =
262 R"json({"command":"path_find","id":19,"subcommand":"create","source_account":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","destination_account":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","destination_amount":"1000000","source_currencies":[{"currency":"0000000000000000000000000000000000000000"},{"currency":"0000000000000000000000005553440000000000"},{"currency":"0000000000000000000000004254430000000000"},{"issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","currency":"0000000000000000000000004254430000000000"},{"issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","currency":"0000000000000000000000004254430000000000"},{"issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","currency":"0000000000000000000000004555520000000000"},{"currency":"0000000000000000000000004554480000000000"},{"currency":"0000000000000000000000004A50590000000000"},{"issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","currency":"000000000000000000000000434E590000000000"},{"currency":"0000000000000000000000004742490000000000"},{"issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","currency":"0000000000000000000000004341440000000000"}]})json";
263
264 json::Value jvRequest;
265 json::Reader jrReader;
266
268 buffers.emplace_back(buffer(request, 1024));
269 buffers.emplace_back(buffer(request.data() + 1024, request.length() - 1024));
270 BEAST_EXPECT(jrReader.parse(jvRequest, buffers) && jvRequest.isObject());
271 }
273 void
275 {
276 testcase("Invalid Transaction Object ID Type");
277 // Crasher bug introduced in 2.0.1. Fixed in 2.3.0.
278
279 using namespace jtx;
280 Env env(*this);
281
282 Account const alice("alice");
283 Account const bob("bob");
284 env.fund(XRP(10'000), alice, bob);
285 env.close();
286
287 {
288 auto const aliceIndex = keylet::account(alice).key;
289 if (BEAST_EXPECT(aliceIndex.isNonZero()))
290 {
291 env(check::cash(alice, aliceIndex, check::DeliverMin(XRP(100))), Ter(tecNO_ENTRY));
292 }
293 }
294
295 {
296 auto const bobIndex = keylet::account(bob).key;
297
298 auto const digest = [&]() -> std::optional<uint256> {
299 auto const& state = env.app().getLedgerMaster().getClosedLedger()->stateMap();
301 if (!state.peekItem(bobIndex, digest))
302 return std::nullopt;
303 return digest.asUInt256();
304 }();
305
306 auto const mapCounts = [&](CountedObjects::List const& list) {
308 for (auto const& e : list)
309 {
310 result[e.first] = e.second;
311 }
312
313 return result;
314 };
315
316 if (BEAST_EXPECT(bobIndex.isNonZero()) && BEAST_EXPECT(digest.has_value()))
317 {
318 auto& cache = env.app().getCachedSLEs();
319 cache.del(*digest, false); // NOLINT(bugprone-unchecked-optional-access)
320 auto const beforeCounts = mapCounts(CountedObjects::getInstance().getCounts(0));
321
322 env(check::cash(alice, bobIndex, check::DeliverMin(XRP(100))), Ter(tecNO_ENTRY));
323
324 auto const afterCounts = mapCounts(CountedObjects::getInstance().getCounts(0));
325
326 using namespace std::string_literals;
327 BEAST_EXPECT(
328 beforeCounts.at("CachedView::hit"s) == afterCounts.at("CachedView::hit"s));
329 BEAST_EXPECT(
330 beforeCounts.at("CachedView::hitExpired"s) + 1 ==
331 afterCounts.at("CachedView::hitExpired"s));
332 BEAST_EXPECT(
333 beforeCounts.at("CachedView::miss"s) == afterCounts.at("CachedView::miss"s));
334 }
335 }
336 }
348 }
349};
350
351BEAST_DEFINE_TESTSUITE(Regression, app, xrpl);
352
353} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
bool isObject() const
virtual Config & config()=0
std::unordered_set< uint256, beast::Uhash<> > features
Definition Config.h:261
static CountedObjects & getInstance() noexcept
std::vector< Entry > List
std::shared_ptr< Ledger const > getClosedLedger()
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
void apply(TxsRawView &to) const
Apply changes.
Definition OpenView.cpp:126
Rules controlling protocol behavior.
Definition Rules.h:33
virtual LedgerMaster & getLedgerMaster()=0
virtual Family & getNodeFamily()=0
virtual TimeKeeper & getTimeKeeper()=0
virtual CachedSLEs & getCachedSLEs()=0
bool del(key_type const &key, bool valid)
time_point closeTime() const
Returns the predicted close time, in network time.
Definition TimeKeeper.h:56
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
A transaction testing environment wrapper.
Definition Env_ss.h:12
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
json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
Definition Env.h:592
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
Account const & master
Definition Env.h:147
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:566
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:174
beast::Journal const journal
Definition Env.h:184
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Set the fee on a JTx.
Definition fee.h:15
Inject raw JSON.
Definition jtx_json.h:11
Match the number of items in the account's owner directory.
Definition owners.h:52
Check a set of conditions.
Definition require.h:45
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:14
Set the regular signature on a JTx.
Definition sig.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
T data(T... args)
T emplace_back(T... args)
T make_shared(T... args)
T make_unique(T... args)
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
json::Value cash(jtx::Account const &dest, uint256 const &checkId, STAmount const &amount)
Cash a check requiring that a specific amount be delivered.
Definition check.cpp:15
static NoneT const kNone
Definition tags.h:9
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:9
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:28
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition tokens.cpp:139
ApplyResult apply(ServiceRegistry &registry, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition apply.cpp:139
CreateGenesisT const kCreateGenesis
constexpr XRPAmount kDropsPerXrp
Number of drops per 1 XRP.
Definition XRPAmount.h:240
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
@ TapNone
Definition ApplyView.h:13
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
@ tecINSUFF_FEE
Definition TER.h:300
@ tecNO_ENTRY
Definition TER.h:304
constexpr XRPAmount kInitialXrp
Configure the native currency.
T length(T... args)
uint256 key
Definition Keylet.h:20
static constexpr auto kTargetTxnInLedger
Definition Constants.h:164
static constexpr auto kMinimumTxnInLedger
Definition Constants.h:129
static constexpr auto kNormalConsensusIncreasePercent
Definition Constants.h:131
static constexpr auto kMinimumTxnInLedgerStandalone
Definition Constants.h:130
static constexpr auto kTransactionQueue
Definition Constants.h:65
void run() override
Runs the suite.
Execution context for applying a JSON transaction.
Definition JTx.h:23
std::shared_ptr< STTx const > stx
Definition JTx.h:33
json::Value jv
Definition JTx.h:24
Set the sequence number on a JTx.
Definition seq.h:12
Type used to specify DeliverMin for cashing a check.
Definition check.h:20