rippled
Loading...
Searching...
No Matches
Simulate_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2017 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#include <test/jtx/Env.h>
22#include <test/jtx/envconfig.h>
23
24#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
25#include <xrpld/rpc/CTID.h>
26
27#include <xrpl/protocol/ErrorCodes.h>
28#include <xrpl/protocol/STBase.h>
29#include <xrpl/protocol/STParsedJSON.h>
30#include <xrpl/protocol/jss.h>
31#include <xrpl/protocol/serialize.h>
32
33#include <optional>
34#include <tuple>
35
36namespace ripple {
37
38namespace test {
39
41{
42 void
44 Json::Value const& result,
45 Json::Value const& tx,
46 int const expectedSequence,
47 std::string const& expectedFee)
48 {
49 BEAST_EXPECT(result[jss::applied] == false);
50 BEAST_EXPECT(result.isMember(jss::engine_result));
51 BEAST_EXPECT(result.isMember(jss::engine_result_code));
52 BEAST_EXPECT(result.isMember(jss::engine_result_message));
53 BEAST_EXPECT(
54 result.isMember(jss::tx_json) || result.isMember(jss::tx_blob));
55
56 Json::Value tx_json;
57 if (result.isMember(jss::tx_json))
58 {
59 tx_json = result[jss::tx_json];
60 }
61 else
62 {
63 auto const unHexed = strUnHex(result[jss::tx_blob].asString());
64 SerialIter sitTrans(makeSlice(*unHexed));
65 tx_json = STObject(std::ref(sitTrans), sfGeneric)
67 }
68 BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]);
69 BEAST_EXPECT(tx_json[jss::Account] == tx[jss::Account]);
70 BEAST_EXPECT(
71 tx_json[jss::SigningPubKey] == tx.get(jss::SigningPubKey, ""));
72 BEAST_EXPECT(
73 tx_json[jss::TxnSignature] == tx.get(jss::TxnSignature, ""));
74 BEAST_EXPECT(tx_json[jss::Fee] == tx.get(jss::Fee, expectedFee));
75 BEAST_EXPECT(
76 tx_json[jss::Sequence] == tx.get(jss::Sequence, expectedSequence));
77 }
78
79 void
81 Json::Value const& result,
82 Json::Value const& tx,
83 int const expectedSequence,
84 XRPAmount const& expectedFee)
85 {
87 result, tx, expectedSequence, expectedFee.jsonClipped().asString());
88 }
89
90 void
92 jtx::Env& env,
93 Json::Value const& tx,
94 std::function<void(Json::Value const&, Json::Value const&)> const&
95 validate,
96 bool testSerialized = true)
97 {
98 env.close();
99
100 Json::Value params;
101 params[jss::tx_json] = tx;
102 validate(env.rpc("json", "simulate", to_string(params)), tx);
103
104 params[jss::binary] = true;
105 validate(env.rpc("json", "simulate", to_string(params)), tx);
106 validate(env.rpc("simulate", to_string(tx)), tx);
107 validate(env.rpc("simulate", to_string(tx), "binary"), tx);
108
109 if (testSerialized)
110 {
111 // This cannot be tested in the multisign autofill scenario
112 // It is technically not a valid STObject, so the following line
113 // will crash
114 STParsedJSONObject const parsed(std::string(jss::tx_json), tx);
115 auto const tx_blob =
116 strHex(parsed.object->getSerializer().peekData());
117 if (BEAST_EXPECT(parsed.object.has_value()))
118 {
119 Json::Value params;
120 params[jss::tx_blob] = tx_blob;
121 validate(env.rpc("json", "simulate", to_string(params)), tx);
122 params[jss::binary] = true;
123 validate(env.rpc("json", "simulate", to_string(params)), tx);
124 }
125 validate(env.rpc("simulate", tx_blob), tx);
126 validate(env.rpc("simulate", tx_blob, "binary"), tx);
127 }
128
129 BEAST_EXPECTS(
130 env.current()->txCount() == 0,
131 std::to_string(env.current()->txCount()));
132 }
133
134 void
136 jtx::Env& env,
137 Json::Value const& tx,
138 std::function<void(
139 Json::Value const&,
140 Json::Value const&,
141 Json::Value const&,
142 Json::Value const&)> const& validate,
143 Json::Value const& expectedMetadataKey,
144 Json::Value const& expectedMetadataValue)
145 {
146 env.close();
147
148 Json::Value params;
149 params[jss::tx_json] = tx;
150 validate(
151 env.rpc("json", "simulate", to_string(params)),
152 tx,
153 expectedMetadataKey,
154 expectedMetadataValue);
155 validate(
156 env.rpc("simulate", to_string(tx)),
157 tx,
158 expectedMetadataKey,
159 expectedMetadataValue);
160
161 BEAST_EXPECTS(
162 env.current()->txCount() == 0,
163 std::to_string(env.current()->txCount()));
164 }
165
168 {
169 if (txResult.isMember(jss::meta_blob))
170 {
171 auto unHexed = strUnHex(txResult[jss::meta_blob].asString());
172 SerialIter sitTrans(makeSlice(*unHexed));
173 return STObject(std::ref(sitTrans), sfGeneric)
175 }
176
177 return txResult[jss::meta];
178 }
179
180 void
182 {
183 testcase("Test parameter errors");
184
185 using namespace jtx;
186 Env env(*this);
187 Account const alice("alice");
188
189 {
190 // No params
191 Json::Value const params = Json::objectValue;
192 auto const resp = env.rpc("json", "simulate", to_string(params));
193 BEAST_EXPECT(
194 resp[jss::result][jss::error_message] ==
195 "Neither `tx_blob` nor `tx_json` included.");
196 }
197 {
198 // Providing both `tx_json` and `tx_blob`
200 params[jss::tx_json] = Json::objectValue;
201 params[jss::tx_blob] = "1200";
202
203 auto const resp = env.rpc("json", "simulate", to_string(params));
204 BEAST_EXPECT(
205 resp[jss::result][jss::error_message] ==
206 "Can only include one of `tx_blob` and `tx_json`.");
207 }
208 {
209 // `binary` isn't a boolean
211 params[jss::tx_blob] = "1200";
212 params[jss::binary] = "100";
213 auto const resp = env.rpc("json", "simulate", to_string(params));
214 BEAST_EXPECT(
215 resp[jss::result][jss::error_message] ==
216 "Invalid field 'binary'.");
217 }
218 {
219 // Invalid `tx_blob`
221 params[jss::tx_blob] = "12";
222
223 auto const resp = env.rpc("json", "simulate", to_string(params));
224 BEAST_EXPECT(
225 resp[jss::result][jss::error_message] ==
226 "Invalid field 'tx_blob'.");
227 }
228 {
229 // Empty `tx_json`
231 params[jss::tx_json] = Json::objectValue;
232
233 auto const resp = env.rpc("json", "simulate", to_string(params));
234 BEAST_EXPECT(
235 resp[jss::result][jss::error_message] ==
236 "Missing field 'tx.TransactionType'.");
237 }
238 {
239 // No tx.Account
242 tx_json[jss::TransactionType] = jss::Payment;
243 params[jss::tx_json] = tx_json;
244
245 auto const resp = env.rpc("json", "simulate", to_string(params));
246 BEAST_EXPECT(
247 resp[jss::result][jss::error_message] ==
248 "Missing field 'tx.Account'.");
249 }
250 {
251 // Empty `tx_blob`
253 params[jss::tx_blob] = "";
254
255 auto const resp = env.rpc("json", "simulate", to_string(params));
256 BEAST_EXPECT(
257 resp[jss::result][jss::error_message] ==
258 "Invalid field 'tx_blob'.");
259 }
260 {
261 // Non-string `tx_blob`
262 Json::Value params;
263 params[jss::tx_blob] = 1.1;
264
265 auto const resp = env.rpc("json", "simulate", to_string(params));
266 BEAST_EXPECT(
267 resp[jss::result][jss::error_message] ==
268 "Invalid field 'tx_blob'.");
269 }
270 {
271 // Non-object `tx_json`
273 params[jss::tx_json] = "";
274
275 auto const resp = env.rpc("json", "simulate", to_string(params));
276 BEAST_EXPECT(
277 resp[jss::result][jss::error_message] ==
278 "Invalid field 'tx_json', not object.");
279 }
280 {
281 // `seed` field included
283 params[jss::seed] = "doesnt_matter";
285 tx_json[jss::TransactionType] = jss::AccountSet;
286 tx_json[jss::Account] = env.master.human();
287 params[jss::tx_json] = tx_json;
288 auto const resp = env.rpc("json", "simulate", to_string(params));
289 BEAST_EXPECT(
290 resp[jss::result][jss::error_message] ==
291 "Invalid field 'seed'.");
292 }
293 {
294 // `secret` field included
296 params[jss::secret] = "doesnt_matter";
298 tx_json[jss::TransactionType] = jss::AccountSet;
299 tx_json[jss::Account] = env.master.human();
300 params[jss::tx_json] = tx_json;
301 auto const resp = env.rpc("json", "simulate", to_string(params));
302 BEAST_EXPECT(
303 resp[jss::result][jss::error_message] ==
304 "Invalid field 'secret'.");
305 }
306 {
307 // `seed_hex` field included
309 params[jss::seed_hex] = "doesnt_matter";
311 tx_json[jss::TransactionType] = jss::AccountSet;
312 tx_json[jss::Account] = env.master.human();
313 params[jss::tx_json] = tx_json;
314 auto const resp = env.rpc("json", "simulate", to_string(params));
315 BEAST_EXPECT(
316 resp[jss::result][jss::error_message] ==
317 "Invalid field 'seed_hex'.");
318 }
319 {
320 // `passphrase` field included
322 params[jss::passphrase] = "doesnt_matter";
324 tx_json[jss::TransactionType] = jss::AccountSet;
325 tx_json[jss::Account] = env.master.human();
326 params[jss::tx_json] = tx_json;
327 auto const resp = env.rpc("json", "simulate", to_string(params));
328 BEAST_EXPECT(
329 resp[jss::result][jss::error_message] ==
330 "Invalid field 'passphrase'.");
331 }
332 {
333 // Invalid transaction
336 tx_json[jss::TransactionType] = jss::Payment;
337 tx_json[jss::Account] = env.master.human();
338 params[jss::tx_json] = tx_json;
339
340 auto const resp = env.rpc("json", "simulate", to_string(params));
341 BEAST_EXPECT(
342 resp[jss::result][jss::error_exception] ==
343 "Field 'Destination' is required but missing.");
344 }
345 {
346 // Bad account
347 Json::Value params;
349 tx_json[jss::TransactionType] = jss::AccountSet;
350 tx_json[jss::Account] = "badAccount";
351 params[jss::tx_json] = tx_json;
352
353 auto const resp = env.rpc("json", "simulate", to_string(params));
354 BEAST_EXPECTS(
355 resp[jss::result][jss::error] == "srcActMalformed",
356 resp[jss::result][jss::error].toStyledString());
357 BEAST_EXPECT(
358 resp[jss::result][jss::error_message] ==
359 "Invalid field 'tx.Account'.");
360 }
361 {
362 // Account doesn't exist for Sequence autofill
363 Json::Value params;
365 tx_json[jss::TransactionType] = jss::AccountSet;
366 tx_json[jss::Account] = alice.human();
367 params[jss::tx_json] = tx_json;
368
369 auto const resp = env.rpc("json", "simulate", to_string(params));
370 BEAST_EXPECT(
371 resp[jss::result][jss::error_message] ==
372 "Source account not found.");
373 }
374 {
375 // Invalid Signers field
376 Json::Value params;
378 tx_json[jss::TransactionType] = jss::AccountSet;
379 tx_json[jss::Account] = env.master.human();
380 tx_json[sfSigners] = "1";
381 params[jss::tx_json] = tx_json;
382
383 auto const resp = env.rpc("json", "simulate", to_string(params));
384 BEAST_EXPECT(
385 resp[jss::result][jss::error_message] ==
386 "Invalid field 'tx.Signers'.");
387 }
388 {
389 // Invalid Signers field
390 Json::Value params;
392 tx_json[jss::TransactionType] = jss::AccountSet;
393 tx_json[jss::Account] = env.master.human();
394 tx_json[sfSigners] = Json::arrayValue;
395 tx_json[sfSigners].append("1");
396 params[jss::tx_json] = tx_json;
397
398 auto const resp = env.rpc("json", "simulate", to_string(params));
399 BEAST_EXPECT(
400 resp[jss::result][jss::error_message] ==
401 "Invalid field 'tx.Signers[0]'.");
402 }
403 {
404 // Invalid transaction
405 Json::Value params;
407 tx_json[jss::TransactionType] = jss::AccountSet;
408 tx_json[jss::Account] = env.master.human();
409 tx_json["foo"] = "bar";
410 params[jss::tx_json] = tx_json;
411
412 auto const resp = env.rpc("json", "simulate", to_string(params));
413 BEAST_EXPECT(
414 resp[jss::result][jss::error_message] ==
415 "Field 'tx_json.foo' is unknown.");
416 }
417 {
418 // non-`"binary"` second param for CLI
420 tx_json[jss::TransactionType] = jss::AccountSet;
421 tx_json[jss::Account] = alice.human();
422 auto const resp = env.rpc("simulate", to_string(tx_json), "1");
423 BEAST_EXPECT(resp[jss::error_message] == "Invalid parameters.");
424 }
425 {
426 // Signed transaction
427 Json::Value params;
429 tx_json[jss::TransactionType] = jss::AccountSet;
430 tx_json[jss::Account] = env.master.human();
431 tx_json[jss::TxnSignature] = "1200ABCD";
432 params[jss::tx_json] = tx_json;
433
434 auto const resp = env.rpc("json", "simulate", to_string(params));
435 BEAST_EXPECT(
436 resp[jss::result][jss::error_message] ==
437 "Transaction should not be signed.");
438 }
439 {
440 // Signed multisig transaction
441 Json::Value params;
443 tx_json[jss::TransactionType] = jss::AccountSet;
444 tx_json[jss::Account] = env.master.human();
445 tx_json[sfSigners] = Json::arrayValue;
446 {
448 signer[jss::Account] = alice.human();
449 signer[jss::SigningPubKey] = alice.human();
450 signer[jss::TxnSignature] = "1200ABCD";
451 Json::Value signerOuter;
452 signerOuter[sfSigner] = signer;
453 tx_json[sfSigners].append(signerOuter);
454 }
455 params[jss::tx_json] = tx_json;
456
457 auto const resp = env.rpc("json", "simulate", to_string(params));
458 BEAST_EXPECT(
459 resp[jss::result][jss::error_message] ==
460 "Transaction should not be signed.");
461 }
462 }
463
464 void
466 {
467 testcase("Fee failure");
468
469 using namespace jtx;
470
471 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
472 cfg->section("transaction_queue")
473 .set("minimum_txn_in_ledger_standalone", "3");
474 return cfg;
475 }));
476
477 Account const alice{"alice"};
478 env.fund(XRP(1000000), alice);
479 env.close();
480
481 // fill queue
482 auto metrics = env.app().getTxQ().getMetrics(*env.current());
483 for (int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
484 env(noop(alice));
485
486 {
487 Json::Value params;
488 params[jss::tx_json] = noop(alice);
489
490 auto const resp = env.rpc("json", "simulate", to_string(params));
491 auto const result = resp[jss::result];
492 if (BEAST_EXPECT(result.isMember(jss::error)))
493 {
494 BEAST_EXPECT(result[jss::error] == "highFee");
495 BEAST_EXPECT(result[jss::error_code] == rpcHIGH_FEE);
496 }
497 }
498 }
499
500 void
502 {
503 testcase("Invalid transaction type");
504
505 using namespace jtx;
506
507 Env env(*this);
508
509 Account const alice{"alice"};
510 Account const bob{"bob"};
511 env.fund(XRP(1000000), alice, bob);
512 env.close();
513
514 auto const batchFee = batch::calcBatchFee(env, 0, 2);
515 auto const seq = env.seq(alice);
516 auto jt = env.jtnofill(
517 batch::outer(alice, env.seq(alice), batchFee, tfAllOrNothing),
518 batch::inner(pay(alice, bob, XRP(10)), seq + 1),
519 batch::inner(pay(alice, bob, XRP(10)), seq + 1));
520
521 jt.jv.removeMember(jss::TxnSignature);
522 Json::Value params;
523 params[jss::tx_json] = jt.jv;
524 auto const resp = env.rpc("json", "simulate", to_string(params));
525 BEAST_EXPECT(resp[jss::result][jss::error] == "notImpl");
526 BEAST_EXPECT(
527 resp[jss::result][jss::error_message] == "Not implemented.");
528 }
529
530 void
532 {
533 testcase("Successful transaction");
534
535 using namespace jtx;
536 Env env{*this, envconfig([&](std::unique_ptr<Config> cfg) {
537 cfg->NETWORK_ID = 0;
538 return cfg;
539 })};
540 static auto const newDomain = "123ABC";
541
542 {
543 auto validateOutput = [&](Json::Value const& resp,
544 Json::Value const& tx) {
545 auto result = resp[jss::result];
547 result, tx, 1, env.current()->fees().base);
548
549 BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS");
550 BEAST_EXPECT(result[jss::engine_result_code] == 0);
551 BEAST_EXPECT(
552 result[jss::engine_result_message] ==
553 "The simulated transaction would have been applied.");
554
555 if (BEAST_EXPECT(
556 result.isMember(jss::meta) ||
557 result.isMember(jss::meta_blob)))
558 {
559 Json::Value const metadata = getJsonMetadata(result);
560
561 if (BEAST_EXPECT(
562 metadata.isMember(sfAffectedNodes.jsonName)))
563 {
564 BEAST_EXPECT(
565 metadata[sfAffectedNodes.jsonName].size() == 1);
566 auto node = metadata[sfAffectedNodes.jsonName][0u];
567 if (BEAST_EXPECT(
568 node.isMember(sfModifiedNode.jsonName)))
569 {
570 auto modifiedNode = node[sfModifiedNode];
571 BEAST_EXPECT(
572 modifiedNode[sfLedgerEntryType] ==
573 "AccountRoot");
574 auto finalFields = modifiedNode[sfFinalFields];
575 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
576 }
577 }
578 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
579 BEAST_EXPECT(
580 metadata[sfTransactionResult.jsonName] == "tesSUCCESS");
581 }
582 };
583
584 Json::Value tx;
585
586 tx[jss::Account] = env.master.human();
587 tx[jss::TransactionType] = jss::AccountSet;
588 tx[sfDomain] = newDomain;
589
590 // test with autofill
591 testTx(env, tx, validateOutput);
592
593 tx[sfSigningPubKey] = "";
594 tx[sfTxnSignature] = "";
595 tx[sfSequence] = 1;
596 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
597
598 // test without autofill
599 testTx(env, tx, validateOutput);
600 }
601 }
602
603 void
605 {
606 testcase("Transaction non-tec failure");
607
608 using namespace jtx;
609 Env env(*this);
610 Account const alice("alice");
611
612 {
613 std::function<void(Json::Value const&, Json::Value const&)> const&
614 testSimulation = [&](Json::Value const& resp,
615 Json::Value const& tx) {
616 auto result = resp[jss::result];
618 result, tx, 1, env.current()->fees().base);
619
620 BEAST_EXPECT(result[jss::engine_result] == "temBAD_AMOUNT");
621 BEAST_EXPECT(result[jss::engine_result_code] == -298);
622 BEAST_EXPECT(
623 result[jss::engine_result_message] ==
624 "Malformed: Bad amount.");
625
626 BEAST_EXPECT(
627 !result.isMember(jss::meta) &&
628 !result.isMember(jss::meta_blob));
629 };
630
631 Json::Value tx;
632
633 tx[jss::Account] = env.master.human();
634 tx[jss::TransactionType] = jss::Payment;
635 tx[sfDestination] = alice.human();
636 tx[sfAmount] = "0"; // invalid amount
637
638 // test with autofill
639 testTx(env, tx, testSimulation);
640
641 tx[sfSigningPubKey] = "";
642 tx[sfTxnSignature] = "";
643 tx[sfSequence] = 1;
644 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
645
646 // test without autofill
647 testTx(env, tx, testSimulation);
648 }
649 }
650
651 void
653 {
654 testcase("Transaction tec failure");
655
656 using namespace jtx;
657 Env env(*this);
658 Account const alice("alice");
659
660 {
661 std::function<void(Json::Value const&, Json::Value const&)> const&
662 testSimulation = [&](Json::Value const& resp,
663 Json::Value const& tx) {
664 auto result = resp[jss::result];
666 result, tx, 1, env.current()->fees().base);
667
668 BEAST_EXPECT(
669 result[jss::engine_result] == "tecNO_DST_INSUF_XRP");
670 BEAST_EXPECT(result[jss::engine_result_code] == 125);
671 BEAST_EXPECT(
672 result[jss::engine_result_message] ==
673 "Destination does not exist. Too little XRP sent to "
674 "create "
675 "it.");
676
677 if (BEAST_EXPECT(
678 result.isMember(jss::meta) ||
679 result.isMember(jss::meta_blob)))
680 {
681 Json::Value const metadata = getJsonMetadata(result);
682
683 if (BEAST_EXPECT(
684 metadata.isMember(sfAffectedNodes.jsonName)))
685 {
686 BEAST_EXPECT(
687 metadata[sfAffectedNodes.jsonName].size() == 1);
688 auto node = metadata[sfAffectedNodes.jsonName][0u];
689 if (BEAST_EXPECT(
690 node.isMember(sfModifiedNode.jsonName)))
691 {
692 auto modifiedNode = node[sfModifiedNode];
693 BEAST_EXPECT(
694 modifiedNode[sfLedgerEntryType] ==
695 "AccountRoot");
696 auto finalFields = modifiedNode[sfFinalFields];
697 BEAST_EXPECT(
698 finalFields[sfBalance] ==
700 100'000'000'000'000'000 -
701 env.current()->fees().base.drops()));
702 }
703 }
704 BEAST_EXPECT(
705 metadata[sfTransactionIndex.jsonName] == 0);
706 BEAST_EXPECT(
707 metadata[sfTransactionResult.jsonName] ==
708 "tecNO_DST_INSUF_XRP");
709 }
710 };
711
712 Json::Value tx;
713
714 tx[jss::Account] = env.master.human();
715 tx[jss::TransactionType] = jss::Payment;
716 tx[sfDestination] = alice.human();
717 tx[sfAmount] = "1"; // not enough to create an account
718
719 // test with autofill
720 testTx(env, tx, testSimulation);
721
722 tx[sfSigningPubKey] = "";
723 tx[sfTxnSignature] = "";
724 tx[sfSequence] = 1;
725 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
726
727 // test without autofill
728 testTx(env, tx, testSimulation);
729 }
730 }
731
732 void
734 {
735 testcase("Successful multi-signed transaction");
736
737 using namespace jtx;
738 Env env(*this);
739 static auto const newDomain = "123ABC";
740 Account const alice("alice");
741 Account const becky("becky");
742 Account const carol("carol");
743 env.fund(XRP(10000), alice);
744 env.close();
745
746 // set up valid multisign
747 env(signers(alice, 1, {{becky, 1}, {carol, 1}}));
748 env.close();
749
750 {
751 auto validateOutput = [&](Json::Value const& resp,
752 Json::Value const& tx) {
753 auto result = resp[jss::result];
755 result,
756 tx,
757 env.seq(alice),
758 tx.isMember(jss::Signers) ? env.current()->fees().base * 2
759 : env.current()->fees().base);
760
761 BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS");
762 BEAST_EXPECT(result[jss::engine_result_code] == 0);
763 BEAST_EXPECT(
764 result[jss::engine_result_message] ==
765 "The simulated transaction would have been applied.");
766
767 if (BEAST_EXPECT(
768 result.isMember(jss::meta) ||
769 result.isMember(jss::meta_blob)))
770 {
771 Json::Value const metadata = getJsonMetadata(result);
772
773 if (BEAST_EXPECT(
774 metadata.isMember(sfAffectedNodes.jsonName)))
775 {
776 BEAST_EXPECT(
777 metadata[sfAffectedNodes.jsonName].size() == 1);
778 auto node = metadata[sfAffectedNodes.jsonName][0u];
779 if (BEAST_EXPECT(
780 node.isMember(sfModifiedNode.jsonName)))
781 {
782 auto modifiedNode = node[sfModifiedNode];
783 BEAST_EXPECT(
784 modifiedNode[sfLedgerEntryType] ==
785 "AccountRoot");
786 auto finalFields = modifiedNode[sfFinalFields];
787 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
788 }
789 }
790 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
791 BEAST_EXPECT(
792 metadata[sfTransactionResult.jsonName] == "tesSUCCESS");
793 }
794 };
795
796 Json::Value tx;
797
798 tx[jss::Account] = alice.human();
799 tx[jss::TransactionType] = jss::AccountSet;
800 tx[sfDomain] = newDomain;
801
802 // test with autofill
803 testTx(env, tx, validateOutput, false);
804
805 tx[sfSigners] = Json::arrayValue;
806 {
808 signer[jss::Account] = becky.human();
809 Json::Value signerOuter;
810 signerOuter[sfSigner] = signer;
811 tx[sfSigners].append(signerOuter);
812 }
813
814 // test with just signer accounts
815 testTx(env, tx, validateOutput, false);
816
817 tx[sfSigningPubKey] = "";
818 tx[sfTxnSignature] = "";
819 tx[sfSequence] = env.seq(alice);
820 // transaction requires a non-base fee
821 tx[sfFee] =
822 (env.current()->fees().base * 2).jsonClipped().asString();
823 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] = "";
824 tx[sfSigners][0u][sfSigner][jss::TxnSignature] = "";
825
826 // test without autofill
827 testTx(env, tx, validateOutput);
828 }
829 }
830
831 void
833 {
834 testcase("Transaction with a key-related failure");
835
836 using namespace jtx;
837 Env env(*this);
838 static auto const newDomain = "123ABC";
839 Account const alice{"alice"};
840 env(regkey(env.master, alice));
841 env(fset(env.master, asfDisableMaster), sig(env.master));
842 env.close();
843
844 {
845 std::function<void(Json::Value const&, Json::Value const&)> const&
846 testSimulation =
847 [&](Json::Value const& resp, Json::Value const& tx) {
848 auto result = resp[jss::result];
850 result,
851 tx,
852 env.seq(env.master),
853 env.current()->fees().base);
854
855 BEAST_EXPECT(
856 result[jss::engine_result] == "tefMASTER_DISABLED");
857 BEAST_EXPECT(result[jss::engine_result_code] == -188);
858 BEAST_EXPECT(
859 result[jss::engine_result_message] ==
860 "Master key is disabled.");
861
862 BEAST_EXPECT(
863 !result.isMember(jss::meta) &&
864 !result.isMember(jss::meta_blob));
865 };
866
867 Json::Value tx;
868
869 tx[jss::Account] = env.master.human();
870 tx[jss::TransactionType] = jss::AccountSet;
871 tx[sfDomain] = newDomain;
872 // master key is disabled, so this is invalid
873 tx[jss::SigningPubKey] = strHex(env.master.pk().slice());
874
875 // test with autofill
876 testTx(env, tx, testSimulation);
877
878 tx[sfTxnSignature] = "";
879 tx[sfSequence] = env.seq(env.master);
880 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
881
882 // test without autofill
883 testTx(env, tx, testSimulation);
884 }
885 }
886
887 void
889 {
890 testcase(
891 "Transaction with both single-signing SigningPubKey and "
892 "multi-signing Signers");
893
894 using namespace jtx;
895 Env env(*this);
896 static auto const newDomain = "123ABC";
897 Account const alice("alice");
898 Account const becky("becky");
899 Account const carol("carol");
900 env.fund(XRP(10000), alice);
901 env.close();
902
903 // set up valid multisign
904 env(signers(alice, 1, {{becky, 1}, {carol, 1}}));
905 env.close();
906
907 {
908 std::function<void(Json::Value const&, Json::Value const&)> const&
909 testSimulation = [&](Json::Value const& resp,
910 Json::Value const& tx) {
911 auto result = resp[jss::result];
913 result,
914 tx,
915 env.seq(env.master),
916 env.current()->fees().base * 2);
917
918 BEAST_EXPECT(result[jss::engine_result] == "temINVALID");
919 BEAST_EXPECT(result[jss::engine_result_code] == -277);
920 BEAST_EXPECT(
921 result[jss::engine_result_message] ==
922 "The transaction is ill-formed.");
923
924 BEAST_EXPECT(
925 !result.isMember(jss::meta) &&
926 !result.isMember(jss::meta_blob));
927 };
928
929 Json::Value tx;
930
931 tx[jss::Account] = env.master.human();
932 tx[jss::TransactionType] = jss::AccountSet;
933 tx[sfDomain] = newDomain;
934 // master key is disabled, so this is invalid
935 tx[jss::SigningPubKey] = strHex(env.master.pk().slice());
936 tx[sfSigners] = Json::arrayValue;
937 {
939 signer[jss::Account] = becky.human();
940 Json::Value signerOuter;
941 signerOuter[sfSigner] = signer;
942 tx[sfSigners].append(signerOuter);
943 }
944
945 // test with autofill
946 testTx(env, tx, testSimulation, false);
947
948 tx[sfTxnSignature] = "";
949 tx[sfSequence] = env.seq(env.master);
950 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
951 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
952 strHex(becky.pk().slice());
953 tx[sfSigners][0u][sfSigner][jss::TxnSignature] = "";
954
955 // test without autofill
956 testTx(env, tx, testSimulation);
957 }
958 }
959
960 void
962 {
963 testcase("Multi-signed transaction with a bad public key");
964
965 using namespace jtx;
966 Env env(*this);
967 static auto const newDomain = "123ABC";
968 Account const alice("alice");
969 Account const becky("becky");
970 Account const carol("carol");
971 Account const dylan("dylan");
972 env.fund(XRP(10000), alice);
973 env.close();
974
975 // set up valid multisign
976 env(signers(alice, 1, {{becky, 1}, {carol, 1}}));
977
978 {
979 auto validateOutput = [&](Json::Value const& resp,
980 Json::Value const& tx) {
981 auto result = resp[jss::result];
983 result, tx, env.seq(alice), env.current()->fees().base * 2);
984
985 BEAST_EXPECTS(
986 result[jss::engine_result] == "tefBAD_SIGNATURE",
987 result[jss::engine_result].toStyledString());
988 BEAST_EXPECT(result[jss::engine_result_code] == -186);
989 BEAST_EXPECT(
990 result[jss::engine_result_message] ==
991 "A signature is provided for a non-signer.");
992
993 BEAST_EXPECT(
994 !result.isMember(jss::meta) &&
995 !result.isMember(jss::meta_blob));
996 };
997
998 Json::Value tx;
999
1000 tx[jss::Account] = alice.human();
1001 tx[jss::TransactionType] = jss::AccountSet;
1002 tx[sfDomain] = newDomain;
1003 tx[sfSigners] = Json::arrayValue;
1004 {
1006 signer[jss::Account] = becky.human();
1007 signer[jss::SigningPubKey] = strHex(dylan.pk().slice());
1008 Json::Value signerOuter;
1009 signerOuter[sfSigner] = signer;
1010 tx[sfSigners].append(signerOuter);
1011 }
1012
1013 // test with autofill
1014 testTx(env, tx, validateOutput, false);
1015
1016 tx[sfSigningPubKey] = "";
1017 tx[sfTxnSignature] = "";
1018 tx[sfSequence] = env.seq(alice);
1019 // transaction requires a non-base fee
1020 tx[sfFee] =
1021 (env.current()->fees().base * 2).jsonClipped().asString();
1022 tx[sfSigners][0u][sfSigner][jss::TxnSignature] = "";
1023
1024 // test without autofill
1025 testTx(env, tx, validateOutput);
1026 }
1027 }
1028
1029 void
1031 {
1032 testcase("Credentials aren't actually deleted on `tecEXPIRED`");
1033
1034 // scenario setup
1035
1036 using namespace jtx;
1037 Env env(*this);
1038
1039 Account const subject{"subject"};
1040 Account const issuer{"issuer"};
1041
1042 env.fund(XRP(10000), subject, issuer);
1043 env.close();
1044
1045 auto const credType = "123ABC";
1046
1047 auto jv = credentials::create(subject, issuer, credType);
1048 uint32_t const t =
1049 env.current()->info().parentCloseTime.time_since_epoch().count();
1050 jv[sfExpiration.jsonName] = t;
1051 env(jv);
1052 env.close();
1053
1054 {
1055 auto validateOutput = [&](Json::Value const& resp,
1056 Json::Value const& tx) {
1057 auto result = resp[jss::result];
1059 result, tx, env.seq(subject), env.current()->fees().base);
1060
1061 BEAST_EXPECT(result[jss::engine_result] == "tecEXPIRED");
1062 BEAST_EXPECT(result[jss::engine_result_code] == 148);
1063 BEAST_EXPECT(
1064 result[jss::engine_result_message] ==
1065 "Expiration time is passed.");
1066
1067 if (BEAST_EXPECT(
1068 result.isMember(jss::meta) ||
1069 result.isMember(jss::meta_blob)))
1070 {
1071 Json::Value const metadata = getJsonMetadata(result);
1072
1073 if (BEAST_EXPECT(
1074 metadata.isMember(sfAffectedNodes.jsonName)))
1075 {
1076 BEAST_EXPECT(
1077 metadata[sfAffectedNodes.jsonName].size() == 5);
1078
1079 try
1080 {
1081 bool found = false;
1082 for (auto const& node :
1083 metadata[sfAffectedNodes.jsonName])
1084 {
1085 if (node.isMember(sfDeletedNode.jsonName) &&
1086 node[sfDeletedNode.jsonName]
1087 [sfLedgerEntryType.jsonName]
1088 .asString() == "Credential")
1089 {
1090 auto const deleted =
1091 node[sfDeletedNode.jsonName]
1092 [sfFinalFields.jsonName];
1093 found = deleted[jss::Issuer] ==
1094 issuer.human() &&
1095 deleted[jss::Subject] ==
1096 subject.human() &&
1097 deleted["CredentialType"] ==
1098 strHex(std::string_view(credType));
1099 break;
1100 }
1101 }
1102 BEAST_EXPECT(found);
1103 }
1104 catch (...)
1105 {
1106 fail();
1107 }
1108 }
1109 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1110 BEAST_EXPECT(
1111 metadata[sfTransactionResult.jsonName] == "tecEXPIRED");
1112 }
1113 };
1114
1115 Json::Value tx = credentials::accept(subject, issuer, credType);
1116
1117 // test with autofill
1118 testTx(env, tx, validateOutput);
1119
1120 tx[sfSigningPubKey] = "";
1121 tx[sfTxnSignature] = "";
1122 tx[sfSequence] = env.seq(subject);
1123 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
1124
1125 // test without autofill
1126 testTx(env, tx, validateOutput);
1127 }
1128
1129 // check that expired credentials weren't deleted
1130 auto const jle =
1131 credentials::ledgerEntry(env, subject, issuer, credType);
1132 BEAST_EXPECT(
1133 jle.isObject() && jle.isMember(jss::result) &&
1134 !jle[jss::result].isMember(jss::error) &&
1135 jle[jss::result].isMember(jss::node) &&
1136 jle[jss::result][jss::node].isMember("LedgerEntryType") &&
1137 jle[jss::result][jss::node]["LedgerEntryType"] == jss::Credential &&
1138 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1139 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
1140 jle[jss::result][jss::node]["CredentialType"] ==
1141 strHex(std::string_view(credType)));
1142
1143 BEAST_EXPECT(ownerCount(env, issuer) == 1);
1144 BEAST_EXPECT(ownerCount(env, subject) == 0);
1145 }
1146
1147 void
1149 {
1150 testcase("Successful transaction with a custom network ID");
1151
1152 using namespace jtx;
1153 Env env{*this, envconfig([&](std::unique_ptr<Config> cfg) {
1154 cfg->NETWORK_ID = 1025;
1155 return cfg;
1156 })};
1157 static auto const newDomain = "123ABC";
1158
1159 {
1160 auto validateOutput = [&](Json::Value const& resp,
1161 Json::Value const& tx) {
1162 auto result = resp[jss::result];
1164 result, tx, 1, env.current()->fees().base);
1165
1166 BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS");
1167 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1168 BEAST_EXPECT(
1169 result[jss::engine_result_message] ==
1170 "The simulated transaction would have been applied.");
1171
1172 if (BEAST_EXPECT(
1173 result.isMember(jss::meta) ||
1174 result.isMember(jss::meta_blob)))
1175 {
1176 Json::Value const metadata = getJsonMetadata(result);
1177
1178 if (BEAST_EXPECT(
1179 metadata.isMember(sfAffectedNodes.jsonName)))
1180 {
1181 BEAST_EXPECT(
1182 metadata[sfAffectedNodes.jsonName].size() == 1);
1183 auto node = metadata[sfAffectedNodes.jsonName][0u];
1184 if (BEAST_EXPECT(
1185 node.isMember(sfModifiedNode.jsonName)))
1186 {
1187 auto modifiedNode = node[sfModifiedNode];
1188 BEAST_EXPECT(
1189 modifiedNode[sfLedgerEntryType] ==
1190 "AccountRoot");
1191 auto finalFields = modifiedNode[sfFinalFields];
1192 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1193 }
1194 }
1195 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1196 BEAST_EXPECT(
1197 metadata[sfTransactionResult.jsonName] == "tesSUCCESS");
1198 }
1199 };
1200
1201 Json::Value tx;
1202
1203 tx[jss::Account] = env.master.human();
1204 tx[jss::TransactionType] = jss::AccountSet;
1205 tx[sfDomain] = newDomain;
1206
1207 // test with autofill
1208 testTx(env, tx, validateOutput);
1209
1210 tx[sfSigningPubKey] = "";
1211 tx[sfTxnSignature] = "";
1212 tx[sfSequence] = 1;
1213 tx[sfFee] = env.current()->fees().base.jsonClipped().asString();
1214 tx[sfNetworkID] = 1025;
1215
1216 // test without autofill
1217 testTx(env, tx, validateOutput);
1218 }
1219 }
1220
1221 void
1223 {
1224 testcase("Successful transaction with additional metadata");
1225
1226 using namespace jtx;
1227 using namespace std::chrono_literals;
1228 Env env{*this, envconfig([&](std::unique_ptr<Config> cfg) {
1229 cfg->NETWORK_ID = 1025;
1230 return cfg;
1231 })};
1232
1233 Account const alice("alice");
1234 Account const bob("bob");
1235
1236 env.fund(XRP(10000), alice, bob);
1237 env.close();
1238 // deliver_amount is unavailable in the metadata before 2014-02-01
1239 // so proceed to 2014-02-01
1240 env.close(NetClock::time_point{446000000s});
1241
1242 {
1243 auto validateOutput =
1244 [&](Json::Value const& resp,
1245 Json::Value const& tx,
1246 Json::Value const& expectedMetadataKey,
1247 Json::Value const& expectedMetadataValue) {
1248 auto result = resp[jss::result];
1249
1250 BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS");
1251 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1252 BEAST_EXPECT(
1253 result[jss::engine_result_message] ==
1254 "The simulated transaction would have been applied.");
1255
1256 if (BEAST_EXPECT(
1257 result.isMember(jss::meta) ||
1258 result.isMember(jss::meta_blob)))
1259 {
1260 Json::Value const metadata = getJsonMetadata(result);
1261
1262 BEAST_EXPECT(
1263 metadata[sfTransactionIndex.jsonName] == 0);
1264 BEAST_EXPECT(
1265 metadata[sfTransactionResult.jsonName] ==
1266 "tesSUCCESS");
1267 BEAST_EXPECT(
1268 metadata.isMember(expectedMetadataKey.asString()));
1269 BEAST_EXPECT(
1270 metadata[expectedMetadataKey.asString()] ==
1271 expectedMetadataValue);
1272 }
1273 };
1274
1275 {
1276 Json::Value tx;
1277 tx[jss::Account] = alice.human();
1278 tx[jss::TransactionType] = jss::Payment;
1279 tx[sfDestination] = bob.human();
1280 tx[sfAmount] = "100";
1281
1282 // test delivered amount
1284 env, tx, validateOutput, jss::delivered_amount, "100");
1285 }
1286
1287 {
1288 Json::Value tx;
1289 tx[jss::Account] = alice.human();
1290 tx[jss::TransactionType] = jss::NFTokenMint;
1291 tx[sfNFTokenTaxon] = 1;
1292
1293 Json::Value nftokenId =
1294 to_string(token::getNextID(env, alice, 1));
1295 // test nft synthetic
1297 env, tx, validateOutput, jss::nftoken_id, nftokenId);
1298 }
1299
1300 {
1301 Json::Value tx;
1302 tx[jss::Account] = alice.human();
1303 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1304
1305 Json::Value mptIssuanceId =
1306 to_string(makeMptID(env.seq(alice), alice));
1307 // test mpt issuance id
1309 env,
1310 tx,
1311 validateOutput,
1312 jss::mpt_issuance_id,
1313 mptIssuanceId);
1314 }
1315 }
1316 }
1317
1318public:
1319 void
1336};
1337
1338BEAST_DEFINE_TESTSUITE(Simulate, rpc, ripple);
1339
1340} // namespace test
1341
1342} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:533
virtual TxQ & getTxQ()=0
Slice slice() const noexcept
Definition PublicKey.h:122
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STObject.cpp:837
Holds the serialized result of parsing an input JSON object.
std::optional< STObject > object
The STObject if the parse was successful.
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1778
Json::Value jsonClipped() const
Definition XRPAmount.h:218
Json::Value getJsonMetadata(Json::Value txResult) const
void testTx(jtx::Env &env, Json::Value const &tx, std::function< void(Json::Value const &, Json::Value const &)> const &validate, bool testSerialized=true)
void run() override
Runs the suite.
void testTxJsonMetadataField(jtx::Env &env, Json::Value const &tx, std::function< void(Json::Value const &, Json::Value const &, Json::Value const &, Json::Value const &)> const &validate, Json::Value const &expectedMetadataKey, Json::Value const &expectedMetadataValue)
void checkBasicReturnValidity(Json::Value const &result, Json::Value const &tx, int const expectedSequence, XRPAmount const &expectedFee)
void checkBasicReturnValidity(Json::Value const &result, Json::Value const &tx, int const expectedSequence, std::string const &expectedFee)
Immutable cryptographic account descriptor.
Definition Account.h:39
PublicKey const & pk() const
Return the public key.
Definition Account.h:94
std::string const & human() const
Returns the human readable public key.
Definition Account.h:118
A transaction testing environment.
Definition Env.h:121
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:268
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
Account const & master
Definition Env.h:125
Application & app()
Definition Env.h:261
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:791
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:289
JTx jtnofill(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:520
Adds a new Batch Txn on a JTx and autofills.
Definition batch.h:61
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
@ arrayValue
array value (ordered list)
Definition json_value.h:44
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
Json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Batch.
Definition batch.cpp:49
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate Batch Fee.
Definition batch.cpp:38
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:32
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:48
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:78
uint256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Definition token.cpp:68
std::uint32_t ownerCount(Env const &env, Account const &account)
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:29
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:34
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:29
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:30
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:54
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:111
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t tfAllOrNothing
Definition TxFlags.h:276
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
@ rpcHIGH_FEE
Definition ErrorCodes.h:58
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,...
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:80
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
SField const sfGeneric
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:170
T ref(T... args)
Json::Value jv
Definition JTx.h:46
Set the sequence number on a JTx.
Definition seq.h:34
A signer in a SignerList.
Definition multisign.h:39
T to_string(T... args)