2#include <test/jtx/Env.h>
3#include <test/jtx/envconfig.h>
5#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
6#include <xrpld/rpc/CTID.h>
8#include <xrpl/protocol/ErrorCodes.h>
9#include <xrpl/protocol/STBase.h>
10#include <xrpl/protocol/STParsedJSON.h>
11#include <xrpl/protocol/jss.h>
12#include <xrpl/protocol/serialize.h>
27 int const expectedSequence,
30 BEAST_EXPECT(result[jss::applied] ==
false);
31 BEAST_EXPECT(result.
isMember(jss::engine_result));
32 BEAST_EXPECT(result.
isMember(jss::engine_result_code));
33 BEAST_EXPECT(result.
isMember(jss::engine_result_message));
34 BEAST_EXPECT(result.
isMember(jss::tx_json) || result.
isMember(jss::tx_blob));
39 tx_json = result[jss::tx_json];
43 auto const unHexed =
strUnHex(result[jss::tx_blob].asString());
47 BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]);
48 BEAST_EXPECT(tx_json[jss::Account] == tx[jss::Account]);
49 BEAST_EXPECT(tx_json[jss::SigningPubKey] == tx.
get(jss::SigningPubKey,
""));
50 BEAST_EXPECT(tx_json[jss::TxnSignature] == tx.
get(jss::TxnSignature,
""));
51 BEAST_EXPECT(tx_json[jss::Fee] == tx.
get(jss::Fee, expectedFee));
52 BEAST_EXPECT(tx_json[jss::Sequence] == tx.
get(jss::Sequence, expectedSequence));
59 int const expectedSequence,
70 bool testSerialized =
true)
75 params[jss::tx_json] = tx;
76 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
78 params[jss::binary] =
true;
79 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
81 validate(env.
rpc(
"simulate",
to_string(tx),
"binary"), tx);
89 auto const tx_blob =
strHex(parsed.
object->getSerializer().peekData());
90 if (BEAST_EXPECT(parsed.
object.has_value()))
93 params[jss::tx_blob] = tx_blob;
94 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
95 params[jss::binary] =
true;
96 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
98 validate(env.
rpc(
"simulate", tx_blob), tx);
99 validate(env.
rpc(
"simulate", tx_blob,
"binary"), tx);
117 params[jss::tx_json] = tx;
118 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx, expectedMetadataKey, expectedMetadataValue);
119 validate(env.
rpc(
"simulate",
to_string(tx)), tx, expectedMetadataKey, expectedMetadataValue);
127 if (txResult.
isMember(jss::meta_blob))
129 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
134 return txResult[jss::meta];
149 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
150 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Neither `tx_blob` nor `tx_json` included.");
156 params[jss::tx_blob] =
"1200";
158 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
159 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Can only include one of `tx_blob` and `tx_json`.");
164 params[jss::tx_blob] =
"1200";
165 params[jss::binary] =
"100";
166 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
167 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'binary'.");
172 params[jss::tx_blob] =
"12";
174 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
175 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
182 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
183 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Missing field 'tx.TransactionType'.");
189 tx_json[jss::TransactionType] = jss::Payment;
190 params[jss::tx_json] = tx_json;
192 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
193 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Missing field 'tx.Account'.");
198 params[jss::tx_blob] =
"";
200 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
201 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
206 params[jss::tx_blob] = 1.1;
208 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
209 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
214 params[jss::tx_json] =
"";
216 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
217 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_json', not object.");
222 params[jss::seed] =
"random_data";
224 tx_json[jss::TransactionType] = jss::AccountSet;
226 params[jss::tx_json] = tx_json;
227 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
228 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed'.");
233 params[jss::secret] =
"random_data";
235 tx_json[jss::TransactionType] = jss::AccountSet;
237 params[jss::tx_json] = tx_json;
238 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
239 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'secret'.");
244 params[jss::seed_hex] =
"random_data";
246 tx_json[jss::TransactionType] = jss::AccountSet;
248 params[jss::tx_json] = tx_json;
249 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
250 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed_hex'.");
255 params[jss::passphrase] =
"random_data";
257 tx_json[jss::TransactionType] = jss::AccountSet;
259 params[jss::tx_json] = tx_json;
260 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
261 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'passphrase'.");
267 tx_json[jss::TransactionType] = jss::Payment;
269 params[jss::tx_json] = tx_json;
271 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
272 BEAST_EXPECT(resp[jss::result][jss::error_exception] ==
"Field 'Destination' is required but missing.");
278 tx_json[jss::TransactionType] = jss::AccountSet;
279 tx_json[jss::Account] =
"badAccount";
280 params[jss::tx_json] = tx_json;
282 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
284 resp[jss::result][jss::error] ==
"srcActMalformed", resp[jss::result][jss::error].toStyledString());
285 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Account'.");
291 tx_json[jss::TransactionType] = jss::AccountSet;
292 tx_json[jss::Account] = alice.
human();
293 params[jss::tx_json] = tx_json;
295 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
296 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Source account not found.");
302 tx_json[jss::TransactionType] = jss::AccountSet;
304 tx_json[sfSigners] =
"1";
305 params[jss::tx_json] = tx_json;
307 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
308 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers'.");
314 tx_json[jss::TransactionType] = jss::AccountSet;
317 tx_json[sfSigners].
append(
"1");
318 params[jss::tx_json] = tx_json;
320 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
321 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers[0]'.");
327 tx_json[jss::TransactionType] = jss::AccountSet;
329 tx_json[
"foo"] =
"bar";
330 params[jss::tx_json] = tx_json;
332 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
333 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Field 'tx_json.foo' is unknown.");
338 tx_json[jss::TransactionType] = jss::AccountSet;
339 tx_json[jss::Account] = alice.
human();
340 auto const resp = env.
rpc(
"simulate",
to_string(tx_json),
"1");
341 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
347 tx_json[jss::TransactionType] = jss::AccountSet;
349 tx_json[jss::TxnSignature] =
"1200ABCD";
350 params[jss::tx_json] = tx_json;
352 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
353 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
359 tx_json[jss::TransactionType] = jss::AccountSet;
366 signer[jss::TxnSignature] =
"1200ABCD";
368 signerOuter[sfSigner] =
signer;
369 tx_json[sfSigners].
append(signerOuter);
371 params[jss::tx_json] = tx_json;
373 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
374 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
386 cfg->section(
"transaction_queue").
set(
"minimum_txn_in_ledger_standalone",
"3");
396 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
401 params[jss::tx_json] =
noop(alice);
403 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
404 auto const result = resp[jss::result];
405 if (BEAST_EXPECT(result.isMember(jss::error)))
407 BEAST_EXPECT(result[jss::error] ==
"highFee");
408 BEAST_EXPECT(result[jss::error_code] ==
rpcHIGH_FEE);
416 testcase(
"Invalid transaction type");
424 env.
fund(
XRP(1000000), alice, bob);
428 auto const seq = env.
seq(alice);
436 params[jss::tx_json] = jt.jv;
437 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
438 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
439 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Not implemented.");
452 static auto const newDomain =
"123ABC";
456 auto result = resp[jss::result];
459 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
460 BEAST_EXPECT(result[jss::engine_result_code] == 0);
462 result[jss::engine_result_message] ==
"The simulated transaction would have been applied.");
464 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
468 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
470 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
471 auto node = metadata[sfAffectedNodes.jsonName][0u];
472 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
474 auto modifiedNode = node[sfModifiedNode];
475 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
476 auto finalFields = modifiedNode[sfFinalFields];
477 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
480 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
481 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
488 tx[jss::TransactionType] = jss::AccountSet;
489 tx[sfDomain] = newDomain;
492 testTx(env, tx, validateOutput);
494 tx[sfSigningPubKey] =
"";
495 tx[sfTxnSignature] =
"";
497 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
500 testTx(env, tx, validateOutput);
507 testcase(
"Transaction non-tec failure");
516 auto result = resp[jss::result];
519 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
520 BEAST_EXPECT(result[jss::engine_result_code] == -298);
521 BEAST_EXPECT(result[jss::engine_result_message] ==
"Malformed: Bad amount.");
523 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
529 tx[jss::TransactionType] = jss::Payment;
530 tx[sfDestination] = alice.
human();
534 testTx(env, tx, testSimulation);
536 tx[sfSigningPubKey] =
"";
537 tx[sfTxnSignature] =
"";
539 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
542 testTx(env, tx, testSimulation);
549 testcase(
"Transaction tec failure");
558 auto result = resp[jss::result];
561 BEAST_EXPECT(result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
562 BEAST_EXPECT(result[jss::engine_result_code] == 125);
564 result[jss::engine_result_message] ==
565 "Destination does not exist. Too little XRP sent to "
569 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
573 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
575 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
576 auto node = metadata[sfAffectedNodes.jsonName][0u];
577 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
579 auto modifiedNode = node[sfModifiedNode];
580 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
581 auto finalFields = modifiedNode[sfFinalFields];
583 finalFields[sfBalance] ==
587 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
588 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tecNO_DST_INSUF_XRP");
595 tx[jss::TransactionType] = jss::Payment;
596 tx[sfDestination] = alice.
human();
600 testTx(env, tx, testSimulation);
602 tx[sfSigningPubKey] =
"";
603 tx[sfTxnSignature] =
"";
605 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
608 testTx(env, tx, testSimulation);
615 testcase(
"Successful multi-signed transaction");
619 static auto const newDomain =
"123ABC";
627 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
632 auto result = resp[jss::result];
639 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
640 BEAST_EXPECT(result[jss::engine_result_code] == 0);
642 result[jss::engine_result_message] ==
"The simulated transaction would have been applied.");
644 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
648 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
650 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
651 auto node = metadata[sfAffectedNodes.jsonName][0u];
652 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
654 auto modifiedNode = node[sfModifiedNode];
655 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
656 auto finalFields = modifiedNode[sfFinalFields];
657 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
660 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
661 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
667 tx[jss::Account] = alice.
human();
668 tx[jss::TransactionType] = jss::AccountSet;
669 tx[sfDomain] = newDomain;
672 testTx(env, tx, validateOutput,
false);
679 signerOuter[sfSigner] =
signer;
680 tx[sfSigners].
append(signerOuter);
684 testTx(env, tx, validateOutput,
false);
686 tx[sfSigningPubKey] =
"";
687 tx[sfTxnSignature] =
"";
688 tx[sfSequence] = env.
seq(alice);
691 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
692 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
695 testTx(env, tx, validateOutput);
702 testcase(
"Transaction with a key-related failure");
706 static auto const newDomain =
"123ABC";
715 auto result = resp[jss::result];
718 BEAST_EXPECT(result[jss::engine_result] ==
"tefMASTER_DISABLED");
719 BEAST_EXPECT(result[jss::engine_result_code] == -188);
720 BEAST_EXPECT(result[jss::engine_result_message] ==
"Master key is disabled.");
722 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
728 tx[jss::TransactionType] = jss::AccountSet;
729 tx[sfDomain] = newDomain;
734 testTx(env, tx, testSimulation);
736 tx[sfTxnSignature] =
"";
738 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
741 testTx(env, tx, testSimulation);
749 "Transaction with both single-signing SigningPubKey and "
750 "multi-signing Signers");
754 static auto const newDomain =
"123ABC";
762 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
768 auto result = resp[jss::result];
771 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
772 BEAST_EXPECT(result[jss::engine_result_code] == -277);
773 BEAST_EXPECT(result[jss::engine_result_message] ==
"The transaction is ill-formed.");
775 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
781 tx[jss::TransactionType] = jss::AccountSet;
782 tx[sfDomain] = newDomain;
790 signerOuter[sfSigner] =
signer;
791 tx[sfSigners].
append(signerOuter);
795 testTx(env, tx, testSimulation,
false);
797 tx[sfTxnSignature] =
"";
799 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
800 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
strHex(becky.
pk().
slice());
801 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
804 testTx(env, tx, testSimulation);
811 testcase(
"Multi-signed transaction with a bad public key");
815 static auto const newDomain =
"123ABC";
824 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
828 auto result = resp[jss::result];
832 result[jss::engine_result] ==
"tefBAD_SIGNATURE", result[jss::engine_result].toStyledString());
833 BEAST_EXPECT(result[jss::engine_result_code] == -186);
834 BEAST_EXPECT(result[jss::engine_result_message] ==
"A signature is provided for a non-signer.");
836 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
841 tx[jss::Account] = alice.
human();
842 tx[jss::TransactionType] = jss::AccountSet;
843 tx[sfDomain] = newDomain;
850 signerOuter[sfSigner] =
signer;
851 tx[sfSigners].
append(signerOuter);
855 testTx(env, tx, validateOutput,
false);
857 tx[sfSigningPubKey] =
"";
858 tx[sfTxnSignature] =
"";
859 tx[sfSequence] = env.
seq(alice);
862 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
865 testTx(env, tx, validateOutput);
872 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
879 Account const subject{
"subject"};
880 Account const issuer{
"issuer"};
882 env.
fund(
XRP(10000), subject, issuer);
885 auto const credType =
"123ABC";
888 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
889 jv[sfExpiration.jsonName] = t;
895 auto result = resp[jss::result];
898 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
899 BEAST_EXPECT(result[jss::engine_result_code] == 148);
900 BEAST_EXPECT(result[jss::engine_result_message] ==
"Expiration time is passed.");
902 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
906 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
908 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 5);
913 for (
auto const& node : metadata[sfAffectedNodes.jsonName])
915 if (node.isMember(sfDeletedNode.jsonName) &&
916 node[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName].asString() ==
"Credential")
918 auto const deleted = node[sfDeletedNode.jsonName][sfFinalFields.jsonName];
919 found = deleted[jss::Issuer] == issuer.human() &&
920 deleted[jss::Subject] == subject.human() &&
932 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
933 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
940 testTx(env, tx, validateOutput);
942 tx[sfSigningPubKey] =
"";
943 tx[sfTxnSignature] =
"";
944 tx[sfSequence] = env.
seq(subject);
945 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
948 testTx(env, tx, validateOutput);
954 jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) &&
955 jle[jss::result].isMember(jss::node) && jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
956 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
957 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
958 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
968 testcase(
"Successful transaction with a custom network ID");
972 cfg->NETWORK_ID = 1025;
975 static auto const newDomain =
"123ABC";
979 auto result = resp[jss::result];
982 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
983 BEAST_EXPECT(result[jss::engine_result_code] == 0);
985 result[jss::engine_result_message] ==
"The simulated transaction would have been applied.");
987 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
991 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
993 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
994 auto node = metadata[sfAffectedNodes.jsonName][0u];
995 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
997 auto modifiedNode = node[sfModifiedNode];
998 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
999 auto finalFields = modifiedNode[sfFinalFields];
1000 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1003 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1004 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1011 tx[jss::TransactionType] = jss::AccountSet;
1012 tx[sfDomain] = newDomain;
1015 testTx(env, tx, validateOutput);
1017 tx[sfSigningPubKey] =
"";
1018 tx[sfTxnSignature] =
"";
1020 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1021 tx[sfNetworkID] = 1025;
1024 testTx(env, tx, validateOutput);
1031 testcase(
"Successful transaction with additional metadata");
1033 using namespace jtx;
1034 using namespace std::chrono_literals;
1036 cfg->NETWORK_ID = 1025;
1043 env.
fund(
XRP(10000), alice, bob);
1050 auto validateOutput = [&](
Json::Value const& resp,
1054 auto result = resp[jss::result];
1056 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1057 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1059 result[jss::engine_result_message] ==
"The simulated transaction would have been applied.");
1061 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
1065 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1066 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1067 BEAST_EXPECT(metadata.
isMember(expectedMetadataKey.asString()));
1068 BEAST_EXPECT(metadata[expectedMetadataKey.asString()] == expectedMetadataValue);
1074 tx[jss::Account] = alice.
human();
1075 tx[jss::TransactionType] = jss::Payment;
1076 tx[sfDestination] = bob.
human();
1077 tx[sfAmount] =
"100";
1085 tx[jss::Account] = alice.
human();
1086 tx[jss::TransactionType] = jss::NFTokenMint;
1087 tx[sfNFTokenTaxon] = 1;
1096 tx[jss::Account] = alice.
human();
1097 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
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...
testcase_t testcase
Memberspace for declaring test cases.
void fail(String const &reason, char const *file, int line)
Record a failure.
Slice slice() const noexcept
Json::Value getJson(JsonOptions=JsonOptions::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 checkBasicReturnValidity(Json::Value const &result, Json::Value const &tx, int const expectedSequence, XRPAmount const &expectedFee)
void testDeleteExpiredCredentials()
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 testSuccessfulTransactionNetworkID()
void checkBasicReturnValidity(Json::Value const &result, Json::Value const &tx, int const expectedSequence, std::string const &expectedFee)
Json::Value getJsonMetadata(Json::Value txResult) const
void testSuccessfulTransactionMultisigned()
void testTransactionNonTecFailure()
void testSuccessfulTransaction()
void testInvalidSingleAndMultiSigningTransaction()
void testSuccessfulTransactionAdditionalMetadata()
void testInvalidTransactionType()
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)
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.
Adds a new Batch Txn on a JTx and autofills.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Set the regular signature on a JTx.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
Json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Batch.
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate Batch Fee.
Json::Value accept(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)
Json::Value create(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 signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
std::uint32_t ownerCount(Env const &env, Account const &account)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool set(T &target, std::string const &name, Section const §ion)
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
std::string to_string(base_uint< Bits, Tag > const &a)
std::string strHex(FwdIt begin, FwdIt end)
constexpr std::uint32_t tfAllOrNothing
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
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)
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Set the sequence number on a JTx.
A signer in a SignerList.