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