rippled
Loading...
Searching...
No Matches
AccountSet_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2016 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#include <test/jtx.h>
21
22#include <xrpl/protocol/AmountConversions.h>
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/Quality.h>
25#include <xrpl/protocol/Rate.h>
26#include <xrpl/protocol/jss.h>
27
28namespace ripple {
29
31{
32public:
33 void
35 {
36 testcase("No AccountSet");
37
38 using namespace test::jtx;
39 Env env(*this);
40 Account const alice("alice");
41 env.fund(XRP(10000), noripple(alice));
42 // ask for the ledger entry - account root, to check its flags
43 auto const jrr = env.le(alice);
44 BEAST_EXPECT(jrr && jrr->at(sfFlags) == 0u);
45 }
46
47 void
49 {
50 testcase("Most Flags");
51
52 using namespace test::jtx;
53 Account const alice("alice");
54
55 // Test without DepositAuth enabled initially.
56 Env env(*this, testable_amendments() - featureDepositAuth);
57 env.fund(XRP(10000), noripple(alice));
58
59 // Give alice a regular key so she can legally set and clear
60 // her asfDisableMaster flag.
61 Account const alie{"alie", KeyType::secp256k1};
62 env(regkey(alice, alie));
63 env.close();
64
65 auto testFlags = [this, &alice, &alie, &env](
67 std::uint32_t const orig_flags = (*env.le(alice))[sfFlags];
68 for (std::uint32_t flag{1u};
69 flag < std::numeric_limits<std::uint32_t>::digits;
70 ++flag)
71 {
72 if (flag == asfNoFreeze)
73 {
74 // The asfNoFreeze flag can't be cleared. It is tested
75 // elsewhere.
76 continue;
77 }
78
80 {
81 // The asfAuthorizedNFTokenMinter flag requires the
82 // presence or absence of the sfNFTokenMinter field in
83 // the transaction. It is tested elsewhere.
84 continue;
85 }
86
87 if (flag == asfDisallowIncomingCheck ||
91 {
92 // These flags are part of the DisallowIncoming amendment
93 // and are tested elsewhere
94 continue;
95 }
96 if (flag == asfAllowTrustLineClawback)
97 {
98 // The asfAllowTrustLineClawback flag can't be cleared. It
99 // is tested elsewhere.
100 continue;
101 }
102 if (flag == asfAllowTrustLineLocking)
103 {
104 // These flags are part of the AllowTokenLocking amendment
105 // and are tested elsewhere
106 continue;
107 }
108
109 if (std::find(goodFlags.begin(), goodFlags.end(), flag) !=
110 goodFlags.end())
111 {
112 // Good flag
113 env.require(nflags(alice, flag));
114 env(fset(alice, flag), sig(alice));
115 env.close();
116 env.require(flags(alice, flag));
117 env(fclear(alice, flag), sig(alie));
118 env.close();
119 env.require(nflags(alice, flag));
120 std::uint32_t const now_flags = (*env.le(alice))[sfFlags];
121 BEAST_EXPECT(now_flags == orig_flags);
122 }
123 else
124 {
125 // Bad flag
126 BEAST_EXPECT((*env.le(alice))[sfFlags] == orig_flags);
127 env(fset(alice, flag), sig(alice));
128 env.close();
129 BEAST_EXPECT((*env.le(alice))[sfFlags] == orig_flags);
130 env(fclear(alice, flag), sig(alie));
131 env.close();
132 BEAST_EXPECT((*env.le(alice))[sfFlags] == orig_flags);
133 }
134 }
135 };
136
137 // Test with featureDepositAuth disabled.
138 testFlags(
145
146 // Enable featureDepositAuth and retest.
147 env.enableFeature(featureDepositAuth);
148 env.close();
149 testFlags(
157 }
158
159 void
161 {
162 testcase("Set and reset AccountTxnID");
163
164 using namespace test::jtx;
165 Env env(*this);
166 Account const alice("alice");
167 env.fund(XRP(10000), noripple(alice));
168
169 std::uint32_t const orig_flags = (*env.le(alice))[sfFlags];
170
171 // asfAccountTxnID is special and not actually set as a flag,
172 // so we check the field presence instead
173 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfAccountTxnID));
174 env(fset(alice, asfAccountTxnID), sig(alice));
175 BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
176 env(fclear(alice, asfAccountTxnID));
177 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfAccountTxnID));
178 std::uint32_t const now_flags = (*env.le(alice))[sfFlags];
179 BEAST_EXPECT(now_flags == orig_flags);
180 }
181
182 void
184 {
185 testcase("Set NoFreeze");
186
187 using namespace test::jtx;
188 Env env(*this);
189 Account const alice("alice");
190 env.fund(XRP(10000), noripple(alice));
191 env.memoize("eric");
192 env(regkey(alice, "eric"));
193
194 env.require(nflags(alice, asfNoFreeze));
195 env(fset(alice, asfNoFreeze), sig("eric"), ter(tecNEED_MASTER_KEY));
196 env(fset(alice, asfNoFreeze), sig(alice));
197 env.require(flags(alice, asfNoFreeze));
198 env(fclear(alice, asfNoFreeze), sig(alice));
199 // verify flag is still set (clear does not clear in this case)
200 env.require(flags(alice, asfNoFreeze));
201 }
202
203 void
205 {
206 testcase("Domain");
207
208 using namespace test::jtx;
209 Env env(*this);
210 Account const alice("alice");
211 env.fund(XRP(10000), alice);
212 auto jt = noop(alice);
213 // The Domain field is represented as the hex string of the lowercase
214 // ASCII of the domain. For example, the domain example.com would be
215 // represented as "6578616d706c652e636f6d".
216 //
217 // To remove the Domain field from an account, send an AccountSet with
218 // the Domain set to an empty string.
219 std::string const domain = "example.com";
220 jt[sfDomain.fieldName] = strHex(domain);
221 env(jt);
222 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain));
223
224 jt[sfDomain.fieldName] = "";
225 env(jt);
226 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfDomain));
227
228 // The upper limit on the length is 256 bytes
229 // (defined as DOMAIN_BYTES_MAX in SetAccount)
230 // test the edge cases: 255, 256, 257.
231 std::size_t const maxLength = 256;
232 for (std::size_t len = maxLength - 1; len <= maxLength + 1; ++len)
233 {
234 std::string domain2 =
235 std::string(len - domain.length() - 1, 'a') + "." + domain;
236
237 BEAST_EXPECT(domain2.length() == len);
238
239 jt[sfDomain.fieldName] = strHex(domain2);
240
241 if (len <= maxLength)
242 {
243 env(jt);
244 BEAST_EXPECT((*env.le(alice))[sfDomain] == makeSlice(domain2));
245 }
246 else
247 {
248 env(jt, ter(telBAD_DOMAIN));
249 }
250 }
251 }
252
253 void
255 {
256 testcase("MessageKey");
257
258 using namespace test::jtx;
259 Env env(*this);
260 Account const alice("alice");
261 env.fund(XRP(10000), alice);
262 auto jt = noop(alice);
263
264 auto const rkp = randomKeyPair(KeyType::ed25519);
265 jt[sfMessageKey.fieldName] = strHex(rkp.first.slice());
266 env(jt);
267 BEAST_EXPECT(
268 strHex((*env.le(alice))[sfMessageKey]) ==
269 strHex(rkp.first.slice()));
270
271 jt[sfMessageKey.fieldName] = "";
272 env(jt);
273 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfMessageKey));
274
275 using namespace std::string_literals;
276 jt[sfMessageKey.fieldName] = strHex("NOT_REALLY_A_PUBKEY"s);
277 env(jt, ter(telBAD_PUBLIC_KEY));
278 }
279
280 void
282 {
283 testcase("WalletID");
284
285 using namespace test::jtx;
286 Env env(*this);
287 Account const alice("alice");
288 env.fund(XRP(10000), alice);
289 auto jt = noop(alice);
290
291 std::string const locator =
292 "9633EC8AF54F16B5286DB1D7B519EF49EEFC050C0C8AC4384F1D88ACD1BFDF05";
293 jt[sfWalletLocator.fieldName] = locator;
294 env(jt);
295 BEAST_EXPECT(to_string((*env.le(alice))[sfWalletLocator]) == locator);
296
297 jt[sfWalletLocator.fieldName] = "";
298 env(jt);
299 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfWalletLocator));
300 }
301
302 void
304 {
305 testcase("EmailHash");
306
307 using namespace test::jtx;
308 Env env(*this);
309 Account const alice("alice");
310 env.fund(XRP(10000), alice);
311 auto jt = noop(alice);
312
313 std::string const mh("5F31A79367DC3137FADA860C05742EE6");
314 jt[sfEmailHash.fieldName] = mh;
315 env(jt);
316 BEAST_EXPECT(to_string((*env.le(alice))[sfEmailHash]) == mh);
317
318 jt[sfEmailHash.fieldName] = "";
319 env(jt);
320 BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfEmailHash));
321 }
322
323 void
325 {
326 struct test_results
327 {
328 double set;
329 TER code;
330 double get;
331 };
332
333 testcase("TransferRate");
334
335 using namespace test::jtx;
336 auto doTests = [this](
337 FeatureBitset const& features,
339 Env env(*this, features);
340
341 Account const alice("alice");
342 env.fund(XRP(10000), alice);
343
344 for (auto const& r : testData)
345 {
346 env(rate(alice, r.set), ter(r.code));
347 env.close();
348
349 // If the field is not present expect the default value
350 if (!(*env.le(alice))[~sfTransferRate])
351 BEAST_EXPECT(r.get == 1.0);
352 else
353 BEAST_EXPECT(
354 *(*env.le(alice))[~sfTransferRate] ==
355 r.get * QUALITY_ONE);
356 }
357 };
358
359 doTests(
360 testable_amendments(),
361 {{1.0, tesSUCCESS, 1.0},
362 {1.1, tesSUCCESS, 1.1},
363 {2.0, tesSUCCESS, 2.0},
364 {2.1, temBAD_TRANSFER_RATE, 2.0},
365 {0.0, tesSUCCESS, 1.0},
366 {2.0, tesSUCCESS, 2.0},
367 {0.9, temBAD_TRANSFER_RATE, 2.0}});
368 }
369
370 void
372 {
373 testcase("Gateway");
374
375 using namespace test::jtx;
376
377 Account const alice("alice");
378 Account const bob("bob");
379 Account const gw("gateway");
380 auto const USD = gw["USD"];
381
382 // Test gateway with a variety of allowed transfer rates
383 for (double transferRate = 1.0; transferRate <= 2.0;
384 transferRate += 0.03125)
385 {
386 Env env(*this);
387 env.fund(XRP(10000), gw, alice, bob);
388 env.close();
389 env.trust(USD(10), alice, bob);
390 env.close();
391 env(rate(gw, transferRate));
392 env.close();
393
394 auto const amount = USD(1);
395 Rate const rate(transferRate * QUALITY_ONE);
396 auto const amountWithRate =
397 toAmount<STAmount>(multiply(amount.value(), rate));
398
399 env(pay(gw, alice, USD(10)));
400 env.close();
401 env(pay(alice, bob, USD(1)), sendmax(USD(10)));
402 env.close();
403
404 env.require(balance(alice, USD(10) - amountWithRate));
405 env.require(balance(bob, USD(1)));
406 }
407
408 // Since fix1201 was enabled on Nov 14 2017 a rate in excess of
409 // 2.0 has been blocked by the transactor. But there are a few
410 // accounts on the MainNet that have larger-than-currently-allowed
411 // TransferRates. We'll bypass the transactor so we can check
412 // operation of these legacy TransferRates.
413 //
414 // Two out-of-bound values are currently in the ledger (March 2020)
415 // They are 4.0 and 4.294967295. So those are the values we test.
416 for (double transferRate : {4.0, 4.294967295})
417 {
418 Env env(*this);
419 env.fund(XRP(10000), gw, alice, bob);
420 env.close();
421 env.trust(USD(10), alice, bob);
422 env.close();
423
424 // We'd like to use transferRate here, but the transactor
425 // blocks transfer rates that large. So we use an acceptable
426 // transfer rate here and later hack the ledger to replace
427 // the acceptable value with an out-of-bounds value.
428 env(rate(gw, 2.0));
429 env.close();
430
431 // Because we're hacking the ledger we need the account to have
432 // non-zero sfMintedNFTokens and sfBurnedNFTokens fields. This
433 // prevents an exception when the AccountRoot template is applied.
434 {
435 uint256 const nftId0{token::getNextID(env, gw, 0u)};
436 env(token::mint(gw, 0u));
437 env.close();
438
439 env(token::burn(gw, nftId0));
440 env.close();
441 }
442
443 // Note that we're bypassing almost all of the ledger's safety
444 // checks with this modify() call. If you call close() between
445 // here and the end of the test all the effort will be lost.
446 env.app().openLedger().modify(
447 [&gw, transferRate](OpenView& view, beast::Journal j) {
448 // Get the account root we want to hijack.
449 auto const sle = view.read(keylet::account(gw.id()));
450 if (!sle)
451 return false; // This would be really surprising!
452
453 // We'll insert a replacement for the account root
454 // with the higher (currently invalid) transfer rate.
455 auto replacement = std::make_shared<SLE>(*sle, sle->key());
456 (*replacement)[sfTransferRate] =
457 static_cast<std::uint32_t>(transferRate * QUALITY_ONE);
458 view.rawReplace(replacement);
459 return true;
460 });
461
462 auto const amount = USD(1);
463 auto const amountWithRate = toAmount<STAmount>(
464 multiply(amount.value(), Rate(transferRate * QUALITY_ONE)));
465
466 env(pay(gw, alice, USD(10)));
467 env(pay(alice, bob, amount), sendmax(USD(10)));
468
469 env.require(balance(alice, USD(10) - amountWithRate));
470 env.require(balance(bob, amount));
471 }
472 }
473
474 void
476 {
477 testcase("Bad inputs");
478
479 using namespace test::jtx;
480 Env env(*this);
481 Account const alice("alice");
482 env.fund(XRP(10000), alice);
483
484 auto jt = fset(alice, asfDisallowXRP);
485 jt[jss::ClearFlag] = asfDisallowXRP;
486 env(jt, ter(temINVALID_FLAG));
487
488 jt = fset(alice, asfRequireAuth);
489 jt[jss::ClearFlag] = asfRequireAuth;
490 env(jt, ter(temINVALID_FLAG));
491
492 jt = fset(alice, asfRequireDest);
493 jt[jss::ClearFlag] = asfRequireDest;
494 env(jt, ter(temINVALID_FLAG));
495
496 jt = fset(alice, asfDisallowXRP);
497 jt[sfFlags.fieldName] = tfAllowXRP;
498 env(jt, ter(temINVALID_FLAG));
499
500 jt = fset(alice, asfRequireAuth);
501 jt[sfFlags.fieldName] = tfOptionalAuth;
502 env(jt, ter(temINVALID_FLAG));
503
504 jt = fset(alice, asfRequireDest);
505 jt[sfFlags.fieldName] = tfOptionalDestTag;
506 env(jt, ter(temINVALID_FLAG));
507
508 jt = fset(alice, asfRequireDest);
509 jt[sfFlags.fieldName] = tfAccountSetMask;
510 env(jt, ter(temINVALID_FLAG));
511
512 env(fset(alice, asfDisableMaster),
513 sig(alice),
515 }
516
517 void
519 {
520 testcase("Require auth");
521
522 using namespace test::jtx;
523 Env env(*this);
524 Account const alice("alice");
525 Account const bob("bob");
526
527 env.fund(XRP(10000), alice);
528 env.close();
529
530 // alice should have an empty directory.
531 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
532
533 // Give alice a signer list, then there will be stuff in the directory.
534 env(signers(alice, 1, {{bob, 1}}));
535 env.close();
536 BEAST_EXPECT(!dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
537
538 env(fset(alice, asfRequireAuth), ter(tecOWNERS));
539
540 // Remove the signer list. After that asfRequireAuth should succeed.
541 env(signers(alice, test::jtx::none));
542 env.close();
543 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
544
545 env(fset(alice, asfRequireAuth));
546 }
547
548 void
550 {
551 using namespace test::jtx;
552 Env env(*this);
553 Account const alice("alice");
554
555 env.fund(XRP(10000), alice);
556 env.close();
557
558 std::uint32_t const ticketSeq{env.seq(alice) + 1};
559 env(ticket::create(alice, 1));
560 env.close();
561 env.require(owners(alice, 1), tickets(alice, 1));
562
563 // Try using a ticket that alice doesn't have.
564 env(noop(alice), ticket::use(ticketSeq + 1), ter(terPRE_TICKET));
565 env.close();
566 env.require(owners(alice, 1), tickets(alice, 1));
567
568 // Actually use alice's ticket. Note that if a transaction consumes
569 // a ticket then the account's sequence number does not advance.
570 std::uint32_t const aliceSeq{env.seq(alice)};
571 env(noop(alice), ticket::use(ticketSeq));
572 env.close();
573 env.require(owners(alice, 0), tickets(alice, 0));
574 BEAST_EXPECT(aliceSeq == env.seq(alice));
575
576 // Try re-using a ticket that alice already used.
577 env(noop(alice), ticket::use(ticketSeq), ter(tefNO_TICKET));
578 env.close();
579 }
580
581 void
598};
599
600BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, ripple, 1);
601
602} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
void run() override
Runs the suite.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:65
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition OpenView.cpp:169
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition OpenView.cpp:243
T find(T... args)
T is_same_v
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
static none_t const none
Definition tags.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t tfAllowXRP
Definition TxFlags.h:71
constexpr std::uint32_t asfGlobalFreeze
Definition TxFlags.h:83
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:85
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer
Definition TxFlags.h:90
@ telBAD_PUBLIC_KEY
Definition TER.h:55
@ telBAD_DOMAIN
Definition TER.h:53
constexpr std::uint32_t asfAllowTrustLineLocking
Definition TxFlags.h:95
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:77
STAmount toAmount< STAmount >(STAmount const &amt)
constexpr std::uint32_t asfAuthorizedNFTokenMinter
Definition TxFlags.h:86
constexpr std::uint32_t tfOptionalDestTag
Definition TxFlags.h:67
constexpr std::uint32_t tfAccountSetMask
Definition TxFlags.h:72
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,...
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
constexpr std::uint32_t asfNoFreeze
Definition TxFlags.h:82
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:80
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition View.cpp:904
constexpr std::uint32_t asfDisallowIncomingTrustline
Definition TxFlags.h:93
@ tefNO_TICKET
Definition TER.h:185
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
constexpr std::uint32_t asfAccountTxnID
Definition TxFlags.h:81
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:84
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
constexpr std::uint32_t asfDisallowIncomingCheck
Definition TxFlags.h:91
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:759
@ tecNEED_MASTER_KEY
Definition TER.h:308
@ tecOWNERS
Definition TER.h:298
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:296
@ tesSUCCESS
Definition TER.h:244
constexpr std::uint32_t tfOptionalAuth
Definition TxFlags.h:69
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
constexpr std::uint32_t asfDisallowIncomingPayChan
Definition TxFlags.h:92
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
constexpr std::uint32_t asfAllowTrustLineClawback
Definition TxFlags.h:94
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:78
@ terPRE_TICKET
Definition TER.h:226
constexpr std::uint32_t asfDisallowXRP
Definition TxFlags.h:79
@ temINVALID_FLAG
Definition TER.h:111
@ temBAD_TRANSFER_RATE
Definition TER.h:107
T length(T... args)
Represents a transfer rate.
Definition Rate.h:40