1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/TestHelpers.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/batch.h>
6#include <test/jtx/credentials.h>
7#include <test/jtx/envconfig.h>
8#include <test/jtx/flags.h>
9#include <test/jtx/multisign.h>
10#include <test/jtx/noop.h>
11#include <test/jtx/pay.h>
12#include <test/jtx/regkey.h>
13#include <test/jtx/sig.h>
14#include <test/jtx/token.h>
16#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
18#include <xrpl/basics/Slice.h>
19#include <xrpl/basics/StringUtilities.h>
20#include <xrpl/basics/base_uint.h>
21#include <xrpl/basics/chrono.h>
22#include <xrpl/basics/strHex.h>
23#include <xrpl/beast/unit_test/suite.h>
24#include <xrpl/config/Constants.h>
25#include <xrpl/json/json_value.h>
26#include <xrpl/json/to_string.h>
27#include <xrpl/protocol/ErrorCodes.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/SField.h>
30#include <xrpl/protocol/STBase.h>
31#include <xrpl/protocol/STParsedJSON.h>
32#include <xrpl/protocol/Serializer.h>
33#include <xrpl/protocol/TxFlags.h>
34#include <xrpl/protocol/XRPAmount.h>
35#include <xrpl/protocol/jss.h>
52 int const expectedSequence,
55 BEAST_EXPECT(result[jss::applied] ==
false);
56 BEAST_EXPECT(result.
isMember(jss::engine_result));
57 BEAST_EXPECT(result.
isMember(jss::engine_result_code));
58 BEAST_EXPECT(result.
isMember(jss::engine_result_message));
59 BEAST_EXPECT(result.
isMember(jss::tx_json) || result.
isMember(jss::tx_blob));
64 txJson = result[jss::tx_json];
68 auto const unHexed =
strUnHex(result[jss::tx_blob].asString());
72 BEAST_EXPECT(txJson[jss::TransactionType] == tx[jss::TransactionType]);
73 BEAST_EXPECT(txJson[jss::Account] == tx[jss::Account]);
74 BEAST_EXPECT(txJson[jss::SigningPubKey] == tx.
get(jss::SigningPubKey,
""));
75 BEAST_EXPECT(txJson[jss::TxnSignature] == tx.
get(jss::TxnSignature,
""));
76 BEAST_EXPECT(txJson[jss::Fee] == tx.
get(jss::Fee, expectedFee));
77 BEAST_EXPECT(txJson[jss::Sequence] == tx.
get(jss::Sequence, expectedSequence));
84 int const expectedSequence,
96 bool testSerialized =
true)
101 params[jss::tx_json] = tx;
102 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
104 params[jss::binary] =
true;
105 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
107 validate(env.
rpc(
"simulate",
to_string(tx),
"binary"), tx);
116 auto const txBlob =
strHex(parsed.
object->getSerializer().peekData());
117 if (BEAST_EXPECT(parsed.
object.has_value()))
120 params[jss::tx_blob] = txBlob;
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);
125 validate(env.
rpc(
"simulate", txBlob), tx);
126 validate(env.
rpc(
"simulate", txBlob,
"binary"), tx);
147 params[jss::tx_json] = tx;
152 expectedMetadataValue);
154 env.
rpc(
"simulate",
to_string(tx)), tx, expectedMetadataKey, expectedMetadataValue);
162 if (txResult.
isMember(jss::meta_blob))
164 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
169 return txResult[jss::meta];
184 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
186 resp[jss::result][jss::error_message] ==
187 "Neither `tx_blob` nor `tx_json` included.");
193 params[jss::tx_blob] =
"1200";
195 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
197 resp[jss::result][jss::error_message] ==
198 "Can only include one of `tx_blob` and `tx_json`.");
203 params[jss::tx_blob] =
"1200";
204 params[jss::binary] =
"100";
205 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
206 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'binary'.");
211 params[jss::tx_blob] =
"12";
213 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
214 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
221 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
223 resp[jss::result][jss::error_message] ==
"Missing field 'tx.TransactionType'.");
229 txJson[jss::TransactionType] = jss::Payment;
230 params[jss::tx_json] = txJson;
232 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
233 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Missing field 'tx.Account'.");
238 params[jss::tx_blob] =
"";
240 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
241 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
246 params[jss::tx_blob] = 1.1;
248 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
249 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
254 params[jss::tx_json] =
"";
256 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
258 resp[jss::result][jss::error_message] ==
"Invalid field 'tx_json', not object.");
263 params[jss::seed] =
"random_data";
265 txJson[jss::TransactionType] = jss::AccountSet;
267 params[jss::tx_json] = txJson;
268 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
269 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed'.");
274 params[jss::secret] =
"random_data";
276 txJson[jss::TransactionType] = jss::AccountSet;
278 params[jss::tx_json] = txJson;
279 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
280 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'secret'.");
285 params[jss::seed_hex] =
"random_data";
287 txJson[jss::TransactionType] = jss::AccountSet;
289 params[jss::tx_json] = txJson;
290 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
291 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed_hex'.");
296 params[jss::passphrase] =
"random_data";
298 txJson[jss::TransactionType] = jss::AccountSet;
300 params[jss::tx_json] = txJson;
301 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
302 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'passphrase'.");
308 txJson[jss::TransactionType] = jss::Payment;
310 params[jss::tx_json] = txJson;
312 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
314 resp[jss::result][jss::error_exception] ==
315 "Field 'Destination' is required but missing.");
321 txJson[jss::TransactionType] = jss::AccountSet;
322 txJson[jss::Account] =
"badAccount";
323 params[jss::tx_json] = txJson;
325 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
327 resp[jss::result][jss::error] ==
"srcActMalformed",
328 resp[jss::result][jss::error].toStyledString());
329 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Account'.");
335 txJson[jss::TransactionType] = jss::AccountSet;
336 txJson[jss::Account] = alice.
human();
337 params[jss::tx_json] = txJson;
339 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
340 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Source account not found.");
346 txJson[jss::TransactionType] = jss::AccountSet;
348 txJson[sfSigners] =
"1";
349 params[jss::tx_json] = txJson;
351 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
352 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers'.");
358 txJson[jss::TransactionType] = jss::AccountSet;
361 txJson[sfSigners].
append(
"1");
362 params[jss::tx_json] = txJson;
364 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
365 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers[0]'.");
371 txJson[jss::TransactionType] = jss::AccountSet;
373 txJson[
"foo"] =
"bar";
374 params[jss::tx_json] = txJson;
376 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
378 resp[jss::result][jss::error_message] ==
"Field 'tx_json.foo' is unknown.");
383 txJson[jss::TransactionType] = jss::AccountSet;
384 txJson[jss::Account] = alice.
human();
385 auto const resp = env.
rpc(
"simulate",
to_string(txJson),
"1");
386 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
392 txJson[jss::TransactionType] = jss::AccountSet;
394 txJson[jss::TxnSignature] =
"1200ABCD";
395 params[jss::tx_json] = txJson;
397 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
399 resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
405 txJson[jss::TransactionType] = jss::AccountSet;
410 signer[jss::Account] = alice.
human();
411 signer[jss::SigningPubKey] = alice.
human();
412 signer[jss::TxnSignature] =
"1200ABCD";
414 signerOuter[sfSigner] = signer;
415 txJson[sfSigners].
append(signerOuter);
417 params[jss::tx_json] = txJson;
419 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
421 resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
448 params[jss::tx_json] =
noop(alice);
450 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
451 auto const result = resp[jss::result];
452 if (BEAST_EXPECT(result.isMember(jss::error)))
454 BEAST_EXPECT(result[jss::error] ==
"highFee");
455 BEAST_EXPECT(result[jss::error_code] ==
RpcHighFee);
463 testcase(
"Invalid transaction type");
471 env.
fund(
XRP(1000000), alice, bob);
475 auto const seq = env.
seq(alice);
483 params[jss::tx_json] = jt.jv;
484 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
485 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
486 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Not implemented.");
499 static auto const kNewDomain =
"123ABC";
503 auto result = resp[jss::result];
506 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
507 BEAST_EXPECT(result[jss::engine_result_code] == 0);
509 result[jss::engine_result_message] ==
510 "The simulated transaction would have been applied.");
512 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
516 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
518 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
519 auto node = metadata[sfAffectedNodes.jsonName][0u];
520 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
522 auto modifiedNode = node[sfModifiedNode];
523 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
524 auto finalFields = modifiedNode[sfFinalFields];
525 BEAST_EXPECT(finalFields[sfDomain] == kNewDomain);
528 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
529 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
536 tx[jss::TransactionType] = jss::AccountSet;
537 tx[sfDomain] = kNewDomain;
540 testTx(env, tx, validateOutput);
542 tx[sfSigningPubKey] =
"";
543 tx[sfTxnSignature] =
"";
545 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
548 testTx(env, tx, validateOutput);
555 testcase(
"Transaction non-tec failure");
564 auto result = resp[jss::result];
567 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
568 BEAST_EXPECT(result[jss::engine_result_code] == -298);
569 BEAST_EXPECT(result[jss::engine_result_message] ==
"Malformed: Bad amount.");
571 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
577 tx[jss::TransactionType] = jss::Payment;
578 tx[sfDestination] = alice.
human();
582 testTx(env, tx, testSimulation);
584 tx[sfSigningPubKey] =
"";
585 tx[sfTxnSignature] =
"";
587 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
590 testTx(env, tx, testSimulation);
597 testcase(
"Transaction tec failure");
606 auto result = resp[jss::result];
609 BEAST_EXPECT(result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
610 BEAST_EXPECT(result[jss::engine_result_code] == 125);
612 result[jss::engine_result_message] ==
613 "Destination does not exist. Too little XRP sent to "
617 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
621 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
623 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
624 auto node = metadata[sfAffectedNodes.jsonName][0u];
625 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
627 auto modifiedNode = node[sfModifiedNode];
628 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
629 auto finalFields = modifiedNode[sfFinalFields];
631 finalFields[sfBalance] ==
633 100'000'000'000'000'000 -
634 env.
current()->fees().base.drops()));
637 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
639 metadata[sfTransactionResult.jsonName] ==
"tecNO_DST_INSUF_XRP");
646 tx[jss::TransactionType] = jss::Payment;
647 tx[sfDestination] = alice.
human();
651 testTx(env, tx, testSimulation);
653 tx[sfSigningPubKey] =
"";
654 tx[sfTxnSignature] =
"";
656 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
659 testTx(env, tx, testSimulation);
666 testcase(
"Successful multi-signed transaction");
670 static auto const kNewDomain =
"123ABC";
678 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
683 auto result = resp[jss::result];
691 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
692 BEAST_EXPECT(result[jss::engine_result_code] == 0);
694 result[jss::engine_result_message] ==
695 "The simulated transaction would have been applied.");
697 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
701 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
703 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
704 auto node = metadata[sfAffectedNodes.jsonName][0u];
705 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
707 auto modifiedNode = node[sfModifiedNode];
708 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
709 auto finalFields = modifiedNode[sfFinalFields];
710 BEAST_EXPECT(finalFields[sfDomain] == kNewDomain);
713 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
714 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
720 tx[jss::Account] = alice.
human();
721 tx[jss::TransactionType] = jss::AccountSet;
722 tx[sfDomain] = kNewDomain;
725 testTx(env, tx, validateOutput,
false);
730 signer[jss::Account] = becky.
human();
732 signerOuter[sfSigner] = signer;
733 tx[sfSigners].
append(signerOuter);
737 testTx(env, tx, validateOutput,
false);
739 tx[sfSigningPubKey] =
"";
740 tx[sfTxnSignature] =
"";
741 tx[sfSequence] = env.
seq(alice);
744 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
745 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
748 testTx(env, tx, validateOutput);
755 testcase(
"Transaction with a key-related failure");
759 static auto const kNewDomain =
"123ABC";
768 auto result = resp[jss::result];
772 BEAST_EXPECT(result[jss::engine_result] ==
"tefMASTER_DISABLED");
773 BEAST_EXPECT(result[jss::engine_result_code] == -188);
774 BEAST_EXPECT(result[jss::engine_result_message] ==
"Master key is disabled.");
776 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
782 tx[jss::TransactionType] = jss::AccountSet;
783 tx[sfDomain] = kNewDomain;
788 testTx(env, tx, testSimulation);
790 tx[sfTxnSignature] =
"";
792 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
795 testTx(env, tx, testSimulation);
803 "Transaction with both single-signing SigningPubKey and "
804 "multi-signing Signers");
808 static auto const kNewDomain =
"123ABC";
816 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
822 auto result = resp[jss::result];
826 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
827 BEAST_EXPECT(result[jss::engine_result_code] == -277);
829 result[jss::engine_result_message] ==
"The transaction is ill-formed.");
831 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
837 tx[jss::TransactionType] = jss::AccountSet;
838 tx[sfDomain] = kNewDomain;
844 signer[jss::Account] = becky.
human();
846 signerOuter[sfSigner] = signer;
847 tx[sfSigners].
append(signerOuter);
851 testTx(env, tx, testSimulation,
false);
853 tx[sfTxnSignature] =
"";
855 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
856 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
strHex(becky.
pk().
slice());
857 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
860 testTx(env, tx, testSimulation);
867 testcase(
"Multi-signed transaction with a bad public key");
871 static auto const kNewDomain =
"123ABC";
880 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
884 auto result = resp[jss::result];
886 result, tx, env.
seq(alice), env.
current()->fees().base * 2);
889 result[jss::engine_result] ==
"tefBAD_SIGNATURE",
890 result[jss::engine_result].toStyledString());
891 BEAST_EXPECT(result[jss::engine_result_code] == -186);
893 result[jss::engine_result_message] ==
894 "A signature is provided for a non-signer.");
896 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
901 tx[jss::Account] = alice.
human();
902 tx[jss::TransactionType] = jss::AccountSet;
903 tx[sfDomain] = kNewDomain;
907 signer[jss::Account] = becky.
human();
910 signerOuter[sfSigner] = signer;
911 tx[sfSigners].
append(signerOuter);
915 testTx(env, tx, validateOutput,
false);
917 tx[sfSigningPubKey] =
"";
918 tx[sfTxnSignature] =
"";
919 tx[sfSequence] = env.
seq(alice);
922 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
925 testTx(env, tx, validateOutput);
932 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
939 Account const subject{
"subject"};
940 Account const issuer{
"issuer"};
942 env.
fund(
XRP(10000), subject, issuer);
945 auto const credType =
"123ABC";
948 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
949 jv[sfExpiration.jsonName] = t;
955 auto result = resp[jss::result];
958 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
959 BEAST_EXPECT(result[jss::engine_result_code] == 148);
960 BEAST_EXPECT(result[jss::engine_result_message] ==
"Expiration time is passed.");
962 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
966 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
968 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 5);
973 for (
auto const& node : metadata[sfAffectedNodes.jsonName])
975 if (node.isMember(sfDeletedNode.jsonName) &&
976 node[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName]
977 .asString() ==
"Credential")
980 node[sfDeletedNode.jsonName][sfFinalFields.jsonName];
981 found = deleted[jss::Issuer] == issuer.
human() &&
982 deleted[jss::Subject] == subject.
human() &&
983 deleted[
"CredentialType"] ==
995 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
996 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
1003 testTx(env, tx, validateOutput);
1005 tx[sfSigningPubKey] =
"";
1006 tx[sfTxnSignature] =
"";
1007 tx[sfSequence] = env.
seq(subject);
1008 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1011 testTx(env, tx, validateOutput);
1017 jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) &&
1018 jle[jss::result].isMember(jss::node) &&
1019 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1020 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1021 jle[jss::result][jss::node][jss::Issuer] == issuer.
human() &&
1022 jle[jss::result][jss::node][jss::Subject] == subject.
human() &&
1032 testcase(
"Successful transaction with a custom network ID");
1034 using namespace jtx;
1036 cfg->networkId = 1025;
1039 static auto const kNewDomain =
"123ABC";
1043 auto result = resp[jss::result];
1046 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1047 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1049 result[jss::engine_result_message] ==
1050 "The simulated transaction would have been applied.");
1052 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
1056 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
1058 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
1059 auto node = metadata[sfAffectedNodes.jsonName][0u];
1060 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
1062 auto modifiedNode = node[sfModifiedNode];
1063 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
1064 auto finalFields = modifiedNode[sfFinalFields];
1065 BEAST_EXPECT(finalFields[sfDomain] == kNewDomain);
1068 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1069 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1076 tx[jss::TransactionType] = jss::AccountSet;
1077 tx[sfDomain] = kNewDomain;
1080 testTx(env, tx, validateOutput);
1082 tx[sfSigningPubKey] =
"";
1083 tx[sfTxnSignature] =
"";
1085 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1086 tx[sfNetworkID] = 1025;
1089 testTx(env, tx, validateOutput);
1096 testcase(
"Successful transaction with additional metadata");
1098 using namespace jtx;
1099 using namespace std::chrono_literals;
1101 cfg->networkId = 1025;
1108 env.
fund(
XRP(10000), alice, bob);
1115 auto validateOutput = [&](
json::Value const& resp,
1119 auto result = resp[jss::result];
1121 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1122 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1124 result[jss::engine_result_message] ==
1125 "The simulated transaction would have been applied.");
1127 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
1131 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1132 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1133 BEAST_EXPECT(metadata.
isMember(expectedMetadataKey.asString()));
1134 BEAST_EXPECT(metadata[expectedMetadataKey.asString()] == expectedMetadataValue);
1140 tx[jss::Account] = alice.
human();
1141 tx[jss::TransactionType] = jss::Payment;
1142 tx[sfDestination] = bob.
human();
1143 tx[sfAmount] =
"100";
1151 tx[jss::Account] = alice.
human();
1152 tx[jss::TransactionType] = jss::NFTokenMint;
1153 tx[sfNFTokenTaxon] = 1;
1162 tx[jss::Account] = alice.
human();
1163 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1168 env, tx, validateOutput, jss::mpt_issuance_id, mptIssuanceId);
void fail(String const &reason, char const *file, int line)
Record a failure.
TestcaseT testcase
Memberspace for declaring test cases.
Value removeMember(char const *key)
Remove and return the named member.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
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.
std::chrono::time_point< NetClock > time_point
Slice slice() const noexcept
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
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.
json::Value jsonClipped() const
void testTransactionTecFailure()
void testTransactionSigningFailure()
void testMultisignedBadPubKey()
void testDeleteExpiredCredentials()
void checkBasicReturnValidity(json::Value const &result, json::Value const &tx, int const expectedSequence, std::string const &expectedFee)
static json::Value getJsonMetadata(json::Value txResult)
void run() override
Runs the suite.
void testSuccessfulTransactionNetworkID()
void testSuccessfulTransactionMultisigned()
void testTx(jtx::Env &env, json::Value const &tx, std::function< void(json::Value const &, json::Value const &)> const &validate, bool testSerialized=true)
void checkBasicReturnValidity(json::Value const &result, json::Value const &tx, int const expectedSequence, XRPAmount const &expectedFee)
void testTransactionNonTecFailure()
void testSuccessfulTransaction()
void testInvalidSingleAndMultiSigningTransaction()
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 testSuccessfulTransactionAdditionalMetadata()
void testInvalidTransactionType()
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
PublicKey const & pk() const
Return the public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
JTx jtnofill(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Set the regular signature on a JTx.
Adds an inner Batch transaction to a JTx and autofills it.
@ Array
array value (ordered list)
@ Object
object value (collection of name/value pairs).
json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Build an outer Batch transaction JSON object.
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate the expected outer Batch transaction fee.
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
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.
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
json::Value regkey(Account const &account, DisabledT)
Disable the regular key.
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value noop(Account const &account)
The null transaction.
std::uint32_t ownerCount(Env const &env, Account const &account)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::string strHex(FwdIt begin, FwdIt end)
std::string to_string(BaseUInt< Bits, Tag > const &a)
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
static constexpr auto kMinimumTxnInLedgerStandalone
static constexpr auto kTransactionQueue