rippled
Loading...
Searching...
No Matches
Regression_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*/
16//==============================================================================
17
18#include <test/jtx.h>
19#include <test/jtx/check.h>
20#include <test/jtx/envconfig.h>
21#include <xrpld/app/ledger/LedgerMaster.h>
22#include <xrpld/app/tx/apply.h>
23#include <xrpl/basics/CountedObject.h>
24#include <xrpl/basics/StringUtilities.h>
25#include <xrpl/json/json_reader.h>
26#include <xrpl/protocol/Indexes.h>
27#include <xrpl/protocol/jss.h>
28
29namespace ripple {
30namespace test {
31
33{
34 // OfferCreate, then OfferCreate with cancel
35 void
37 {
38 using namespace jtx;
39 Env env(*this);
40 auto const gw = Account("gw");
41 auto const USD = gw["USD"];
42 env.fund(XRP(10000), "alice", gw);
43 env(offer("alice", USD(10), XRP(10)), require(owners("alice", 1)));
44 env(offer("alice", USD(20), XRP(10)),
45 json(R"raw(
46 { "OfferSequence" : 4 }
47 )raw"),
48 require(owners("alice", 1)));
49 }
50
51 void
53 {
54 testcase("Account balance < fee destroys correct amount of XRP");
55 using namespace jtx;
56 Env env(*this);
57 env.memoize("alice");
58
59 // The low balance scenario can not deterministically
60 // be reproduced against an open ledger. Make a local
61 // closed ledger and work with it directly.
62 auto closed = std::make_shared<Ledger>(
64 env.app().config(),
66 env.app().getNodeFamily());
67 auto expectedDrops = INITIAL_XRP;
68 BEAST_EXPECT(closed->info().drops == expectedDrops);
69
70 auto const aliceXRP = 400;
71 auto const aliceAmount = XRP(aliceXRP);
72
73 auto next = std::make_shared<Ledger>(
74 *closed, env.app().timeKeeper().closeTime());
75 {
76 // Fund alice
77 auto const jt = env.jt(pay(env.master, "alice", aliceAmount));
78 OpenView accum(&*next);
79
80 auto const result =
81 ripple::apply(env.app(), accum, *jt.stx, tapNONE, env.journal);
82 BEAST_EXPECT(result.ter == tesSUCCESS);
83 BEAST_EXPECT(result.applied);
84
85 accum.apply(*next);
86 }
87 expectedDrops -= next->fees().base;
88 BEAST_EXPECT(next->info().drops == expectedDrops);
89 {
90 auto const sle = next->read(keylet::account(Account("alice").id()));
91 BEAST_EXPECT(sle);
92 auto balance = sle->getFieldAmount(sfBalance);
93
94 BEAST_EXPECT(balance == aliceAmount);
95 }
96
97 {
98 // Specify the seq manually since the env's open ledger
99 // doesn't know about this account.
100 auto const jt = env.jt(noop("alice"), fee(expectedDrops), seq(2));
101
102 OpenView accum(&*next);
103
104 auto const result =
105 ripple::apply(env.app(), accum, *jt.stx, tapNONE, env.journal);
106 BEAST_EXPECT(result.ter == tecINSUFF_FEE);
107 BEAST_EXPECT(result.applied);
108
109 accum.apply(*next);
110 }
111 {
112 auto const sle = next->read(keylet::account(Account("alice").id()));
113 BEAST_EXPECT(sle);
114 auto balance = sle->getFieldAmount(sfBalance);
115
116 BEAST_EXPECT(balance == XRP(0));
117 }
118 expectedDrops -= aliceXRP * dropsPerXRP;
119 BEAST_EXPECT(next->info().drops == expectedDrops);
120 }
121
122 void
124 {
125 testcase("Signing with a secp256r1 key should fail gracefully");
126 using namespace jtx;
127 Env env(*this);
128
129 // Test case we'll use.
130 auto test256r1key = [&env](Account const& acct) {
131 auto const baseFee = env.current()->fees().base;
132 std::uint32_t const acctSeq = env.seq(acct);
133 Json::Value jsonNoop =
134 env.json(noop(acct), fee(baseFee), seq(acctSeq), sig(acct));
135 JTx jt = env.jt(jsonNoop);
136 jt.fill_sig = false;
137
138 // Random secp256r1 public key generated by
139 // https://kjur.github.io/jsrsasign/sample-ecdsa.html
140 std::string const secp256r1PubKey =
141 "045d02995ec24988d9a2ae06a3733aa35ba0741e87527"
142 "ed12909b60bd458052c944b24cbf5893c3e5be321774e"
143 "5082e11c034b765861d0effbde87423f8476bb2c";
144
145 // Set the key in the JSON.
146 jt.jv["SigningPubKey"] = secp256r1PubKey;
147
148 // Set the same key in the STTx.
149 auto secp256r1Sig = std::make_unique<STTx>(*(jt.stx));
150 auto pubKeyBlob = strUnHex(secp256r1PubKey);
151 assert(pubKeyBlob); // Hex for public key must be valid
152 secp256r1Sig->setFieldVL(sfSigningPubKey, *pubKeyBlob);
153 jt.stx.reset(secp256r1Sig.release());
154
155 env(jt,
156 rpc("invalidTransaction",
157 "fails local checks: Invalid signature."));
158 };
159
160 Account const alice{"alice", KeyType::secp256k1};
161 Account const becky{"becky", KeyType::ed25519};
162
163 env.fund(XRP(10000), alice, becky);
164
165 test256r1key(alice);
166 test256r1key(becky);
167 }
168
169 void
171 {
172 testcase("Autofilled fee should use the escalated fee");
173 using namespace jtx;
174 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
175 cfg->section("transaction_queue")
176 .set("minimum_txn_in_ledger_standalone", "3");
177 return cfg;
178 }));
179 Env_ss envs(env);
180
181 auto const alice = Account("alice");
182 env.fund(XRP(100000), alice);
183
184 auto params = Json::Value(Json::objectValue);
185 // Max fee = 50k drops
186 params[jss::fee_mult_max] = 5000;
187 std::vector<int> const expectedFees({10, 10, 8889, 13889, 20000});
188
189 // We should be able to submit 5 transactions within
190 // our fee limit.
191 for (int i = 0; i < 5; ++i)
192 {
193 envs(noop(alice), fee(none), seq(none))(params);
194
195 auto tx = env.tx();
196 if (BEAST_EXPECT(tx))
197 {
198 BEAST_EXPECT(tx->getAccountID(sfAccount) == alice.id());
199 BEAST_EXPECT(tx->getTxnType() == ttACCOUNT_SET);
200 auto const fee = tx->getFieldAmount(sfFee);
201 BEAST_EXPECT(fee == drops(expectedFees[i]));
202 }
203 }
204 }
205
206 void
208 {
209 testcase("Fee escalation shouldn't allocate extreme memory");
210 using clock_type = std::chrono::steady_clock;
211 using namespace jtx;
212 using namespace std::chrono_literals;
213
214 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
215 auto& s = cfg->section("transaction_queue");
216 s.set("minimum_txn_in_ledger_standalone", "4294967295");
217 s.set("minimum_txn_in_ledger", "4294967295");
218 s.set("target_txn_in_ledger", "4294967295");
219 s.set("normal_consensus_increase_percent", "4294967295");
220
221 return cfg;
222 }));
223
224 env(noop(env.master));
225 // This test will probably fail if any breakpoints are encountered,
226 // but should pass on even the slowest machines.
227 auto const start = clock_type::now();
228 env.close();
229 BEAST_EXPECT(clock_type::now() - start < 1s);
230 }
231
232 void
234 {
235 using namespace jtx;
236 using boost::asio::buffer;
237 testcase("jsonInvalid");
238
239 std::string const request =
240 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";
241
242 Json::Value jvRequest;
243 Json::Reader jrReader;
244
246 buffers.emplace_back(buffer(request, 1024));
247 buffers.emplace_back(
248 buffer(request.data() + 1024, request.length() - 1024));
249 BEAST_EXPECT(
250 jrReader.parse(jvRequest, buffers) && jvRequest.isObject());
251 }
252
253 void
255 {
256 testcase("Invalid Transaction Object ID Type");
257 // Crasher bug introduced in 2.0.1. Fixed in 2.3.0.
258
259 using namespace jtx;
260 Env env(*this);
261
262 Account alice("alice");
263 Account bob("bob");
264 env.fund(XRP(10'000), alice, bob);
265 env.close();
266
267 {
268 auto const alice_index = keylet::account(alice).key;
269 if (BEAST_EXPECT(alice_index.isNonZero()))
270 {
271 env(check::cash(
272 alice, alice_index, check::DeliverMin(XRP(100))),
274 }
275 }
276
277 {
278 auto const bob_index = keylet::account(bob).key;
279
280 auto const digest = [&]() -> std::optional<uint256> {
281 auto const& state =
282 env.app().getLedgerMaster().getClosedLedger()->stateMap();
284 if (!state.peekItem(bob_index, digest))
285 return std::nullopt;
286 return digest.as_uint256();
287 }();
288
289 auto const mapCounts = [&](CountedObjects::List const& list) {
291 for (auto const& e : list)
292 {
293 result[e.first] = e.second;
294 }
295
296 return result;
297 };
298
299 if (BEAST_EXPECT(bob_index.isNonZero()) &&
300 BEAST_EXPECT(digest.has_value()))
301 {
302 auto& cache = env.app().cachedSLEs();
303 cache.del(*digest, false);
304 auto const beforeCounts =
305 mapCounts(CountedObjects::getInstance().getCounts(0));
306
307 env(check::cash(alice, bob_index, check::DeliverMin(XRP(100))),
309
310 auto const afterCounts =
311 mapCounts(CountedObjects::getInstance().getCounts(0));
312
313 using namespace std::string_literals;
314 BEAST_EXPECT(
315 beforeCounts.at("CachedView::hit"s) ==
316 afterCounts.at("CachedView::hit"s));
317 BEAST_EXPECT(
318 beforeCounts.at("CachedView::hitExpired"s) + 1 ==
319 afterCounts.at("CachedView::hitExpired"s));
320 BEAST_EXPECT(
321 beforeCounts.at("CachedView::miss"s) ==
322 afterCounts.at("CachedView::miss"s));
323 }
324 }
325 }
326
327 void
328 run() override
329 {
330 testOffer1();
337 }
338};
339
340BEAST_DEFINE_TESTSUITE(Regression, app, ripple);
341
342} // namespace test
343} // namespace ripple
Unserialize a JSON document into a Value.
Definition: json_reader.h:39
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:78
Represents a JSON value.
Definition: json_value.h:148
bool isObject() const
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
virtual CachedSLEs & cachedSLEs()=0
virtual Config & config()=0
virtual TimeKeeper & timeKeeper()=0
virtual Family & getNodeFamily()=0
virtual LedgerMaster & getLedgerMaster()=0
static CountedObjects & getInstance() noexcept
std::shared_ptr< Ledger const > getClosedLedger()
Definition: LedgerMaster.h:78
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:56
void apply(TxsRawView &to) const
Apply changes.
Definition: OpenView.cpp:130
bool del(const key_type &key, bool valid)
Definition: TaggedCache.h:270
time_point closeTime() const
Returns the predicted close time, in network time.
Definition: TimeKeeper.h:76
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment wrapper.
Definition: Env_ss.h:34
A transaction testing environment.
Definition: Env.h:118
Json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
Definition: Env.h:517
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:210
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition: Env.cpp:453
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:326
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
Account const & master
Definition: Env.h:122
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition: Env.h:491
Application & app()
Definition: Env.h:256
beast::Journal const journal
Definition: Env.h:159
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
void memoize(Account const &account)
Associate AccountID with account.
Definition: Env.cpp:150
A balance matches.
Definition: balance.h:39
Set the fee on a JTx.
Definition: fee.h:36
Inject raw JSON.
Definition: jtx_json.h:32
Match the number of items in the account's owner directory.
Definition: owners.h:71
Check a set of conditions.
Definition: require.h:65
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: rpc.h:35
Set the regular signature on a JTx.
Definition: sig.h:35
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T data(T... args)
T emplace_back(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
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:32
static none_t const none
Definition: tags.h:34
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
constexpr XRPAmount dropsPerXRP
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value noop(Account const &account)
The null transaction.
Definition: noop.h:31
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:28
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:315
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition: tokens.cpp:156
@ tecNO_ENTRY
Definition: TER.h:293
@ tecINSUFF_FEE
Definition: TER.h:289
@ tesSUCCESS
Definition: TER.h:242
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition: apply.cpp:109
create_genesis_t const create_genesis
Definition: Ledger.cpp:50
@ tapNONE
Definition: ApplyView.h:31
T length(T... args)
uint256 key
Definition: Keylet.h:40
void run() override
Runs the suite.
Execution context for applying a JSON transaction.
Definition: JTx.h:44
std::shared_ptr< STTx const > stx
Definition: JTx.h:55
Json::Value jv
Definition: JTx.h:45
Type used to specify DeliverMin for cashing a check.
Definition: check.h:40
Set the sequence number on a JTx.
Definition: seq.h:34