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,
71 bool testSerialized =
true)
76 params[jss::tx_json] = tx;
77 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
79 params[jss::binary] =
true;
80 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
82 validate(env.
rpc(
"simulate",
to_string(tx),
"binary"), tx);
91 auto const tx_blob =
strHex(parsed.
object->getSerializer().peekData());
92 if (BEAST_EXPECT(parsed.
object.has_value()))
95 params[jss::tx_blob] = tx_blob;
96 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
97 params[jss::binary] =
true;
98 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
100 validate(env.
rpc(
"simulate", tx_blob), tx);
101 validate(env.
rpc(
"simulate", tx_blob,
"binary"), tx);
122 params[jss::tx_json] = tx;
127 expectedMetadataValue);
129 env.
rpc(
"simulate",
to_string(tx)), tx, expectedMetadataKey, expectedMetadataValue);
137 if (txResult.
isMember(jss::meta_blob))
139 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
144 return txResult[jss::meta];
159 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
161 resp[jss::result][jss::error_message] ==
162 "Neither `tx_blob` nor `tx_json` included.");
168 params[jss::tx_blob] =
"1200";
170 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
172 resp[jss::result][jss::error_message] ==
173 "Can only include one of `tx_blob` and `tx_json`.");
178 params[jss::tx_blob] =
"1200";
179 params[jss::binary] =
"100";
180 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
181 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'binary'.");
186 params[jss::tx_blob] =
"12";
188 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
189 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
196 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
198 resp[jss::result][jss::error_message] ==
"Missing field 'tx.TransactionType'.");
204 tx_json[jss::TransactionType] = jss::Payment;
205 params[jss::tx_json] = tx_json;
207 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
208 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Missing field 'tx.Account'.");
213 params[jss::tx_blob] =
"";
215 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
216 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
221 params[jss::tx_blob] = 1.1;
223 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
224 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx_blob'.");
229 params[jss::tx_json] =
"";
231 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
233 resp[jss::result][jss::error_message] ==
"Invalid field 'tx_json', not object.");
238 params[jss::seed] =
"random_data";
240 tx_json[jss::TransactionType] = jss::AccountSet;
242 params[jss::tx_json] = tx_json;
243 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
244 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed'.");
249 params[jss::secret] =
"random_data";
251 tx_json[jss::TransactionType] = jss::AccountSet;
253 params[jss::tx_json] = tx_json;
254 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
255 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'secret'.");
260 params[jss::seed_hex] =
"random_data";
262 tx_json[jss::TransactionType] = jss::AccountSet;
264 params[jss::tx_json] = tx_json;
265 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
266 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'seed_hex'.");
271 params[jss::passphrase] =
"random_data";
273 tx_json[jss::TransactionType] = jss::AccountSet;
275 params[jss::tx_json] = tx_json;
276 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
277 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'passphrase'.");
283 tx_json[jss::TransactionType] = jss::Payment;
285 params[jss::tx_json] = tx_json;
287 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
289 resp[jss::result][jss::error_exception] ==
290 "Field 'Destination' is required but missing.");
296 tx_json[jss::TransactionType] = jss::AccountSet;
297 tx_json[jss::Account] =
"badAccount";
298 params[jss::tx_json] = tx_json;
300 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
302 resp[jss::result][jss::error] ==
"srcActMalformed",
303 resp[jss::result][jss::error].toStyledString());
304 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Account'.");
310 tx_json[jss::TransactionType] = jss::AccountSet;
311 tx_json[jss::Account] = alice.
human();
312 params[jss::tx_json] = tx_json;
314 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
315 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Source account not found.");
321 tx_json[jss::TransactionType] = jss::AccountSet;
323 tx_json[sfSigners] =
"1";
324 params[jss::tx_json] = tx_json;
326 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
327 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers'.");
333 tx_json[jss::TransactionType] = jss::AccountSet;
336 tx_json[sfSigners].
append(
"1");
337 params[jss::tx_json] = tx_json;
339 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
340 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Invalid field 'tx.Signers[0]'.");
346 tx_json[jss::TransactionType] = jss::AccountSet;
348 tx_json[
"foo"] =
"bar";
349 params[jss::tx_json] = tx_json;
351 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
353 resp[jss::result][jss::error_message] ==
"Field 'tx_json.foo' is unknown.");
358 tx_json[jss::TransactionType] = jss::AccountSet;
359 tx_json[jss::Account] = alice.
human();
360 auto const resp = env.
rpc(
"simulate",
to_string(tx_json),
"1");
361 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
367 tx_json[jss::TransactionType] = jss::AccountSet;
369 tx_json[jss::TxnSignature] =
"1200ABCD";
370 params[jss::tx_json] = tx_json;
372 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
374 resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
380 tx_json[jss::TransactionType] = jss::AccountSet;
387 signer[jss::TxnSignature] =
"1200ABCD";
389 signerOuter[sfSigner] =
signer;
390 tx_json[sfSigners].
append(signerOuter);
392 params[jss::tx_json] = tx_json;
394 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
396 resp[jss::result][jss::error_message] ==
"Transaction should not be signed.");
408 cfg->section(
"transaction_queue").
set(
"minimum_txn_in_ledger_standalone",
"3");
418 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
423 params[jss::tx_json] =
noop(alice);
425 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
426 auto const result = resp[jss::result];
427 if (BEAST_EXPECT(result.isMember(jss::error)))
429 BEAST_EXPECT(result[jss::error] ==
"highFee");
430 BEAST_EXPECT(result[jss::error_code] ==
rpcHIGH_FEE);
438 testcase(
"Invalid transaction type");
446 env.
fund(
XRP(1000000), alice, bob);
450 auto const seq = env.
seq(alice);
458 params[jss::tx_json] = jt.jv;
459 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
460 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
461 BEAST_EXPECT(resp[jss::result][jss::error_message] ==
"Not implemented.");
474 static auto const newDomain =
"123ABC";
478 auto result = resp[jss::result];
481 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
482 BEAST_EXPECT(result[jss::engine_result_code] == 0);
484 result[jss::engine_result_message] ==
485 "The simulated transaction would have been applied.");
487 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
491 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
493 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
494 auto node = metadata[sfAffectedNodes.jsonName][0u];
495 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
497 auto modifiedNode = node[sfModifiedNode];
498 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
499 auto finalFields = modifiedNode[sfFinalFields];
500 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
503 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
504 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
511 tx[jss::TransactionType] = jss::AccountSet;
512 tx[sfDomain] = newDomain;
515 testTx(env, tx, validateOutput);
517 tx[sfSigningPubKey] =
"";
518 tx[sfTxnSignature] =
"";
520 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
523 testTx(env, tx, validateOutput);
530 testcase(
"Transaction non-tec failure");
539 auto result = resp[jss::result];
542 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
543 BEAST_EXPECT(result[jss::engine_result_code] == -298);
544 BEAST_EXPECT(result[jss::engine_result_message] ==
"Malformed: Bad amount.");
546 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
552 tx[jss::TransactionType] = jss::Payment;
553 tx[sfDestination] = alice.
human();
557 testTx(env, tx, testSimulation);
559 tx[sfSigningPubKey] =
"";
560 tx[sfTxnSignature] =
"";
562 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
565 testTx(env, tx, testSimulation);
572 testcase(
"Transaction tec failure");
581 auto result = resp[jss::result];
584 BEAST_EXPECT(result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
585 BEAST_EXPECT(result[jss::engine_result_code] == 125);
587 result[jss::engine_result_message] ==
588 "Destination does not exist. Too little XRP sent to "
592 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
596 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
598 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
599 auto node = metadata[sfAffectedNodes.jsonName][0u];
600 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
602 auto modifiedNode = node[sfModifiedNode];
603 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
604 auto finalFields = modifiedNode[sfFinalFields];
606 finalFields[sfBalance] ==
608 100'000'000'000'000'000 -
609 env.
current()->fees().base.drops()));
612 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
614 metadata[sfTransactionResult.jsonName] ==
"tecNO_DST_INSUF_XRP");
621 tx[jss::TransactionType] = jss::Payment;
622 tx[sfDestination] = alice.
human();
626 testTx(env, tx, testSimulation);
628 tx[sfSigningPubKey] =
"";
629 tx[sfTxnSignature] =
"";
631 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
634 testTx(env, tx, testSimulation);
641 testcase(
"Successful multi-signed transaction");
645 static auto const newDomain =
"123ABC";
653 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
658 auto result = resp[jss::result];
666 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
667 BEAST_EXPECT(result[jss::engine_result_code] == 0);
669 result[jss::engine_result_message] ==
670 "The simulated transaction would have been applied.");
672 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
676 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
678 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
679 auto node = metadata[sfAffectedNodes.jsonName][0u];
680 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
682 auto modifiedNode = node[sfModifiedNode];
683 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
684 auto finalFields = modifiedNode[sfFinalFields];
685 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
688 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
689 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
695 tx[jss::Account] = alice.
human();
696 tx[jss::TransactionType] = jss::AccountSet;
697 tx[sfDomain] = newDomain;
700 testTx(env, tx, validateOutput,
false);
707 signerOuter[sfSigner] =
signer;
708 tx[sfSigners].
append(signerOuter);
712 testTx(env, tx, validateOutput,
false);
714 tx[sfSigningPubKey] =
"";
715 tx[sfTxnSignature] =
"";
716 tx[sfSequence] = env.
seq(alice);
719 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
720 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
723 testTx(env, tx, validateOutput);
730 testcase(
"Transaction with a key-related failure");
734 static auto const newDomain =
"123ABC";
743 auto result = resp[jss::result];
747 BEAST_EXPECT(result[jss::engine_result] ==
"tefMASTER_DISABLED");
748 BEAST_EXPECT(result[jss::engine_result_code] == -188);
749 BEAST_EXPECT(result[jss::engine_result_message] ==
"Master key is disabled.");
751 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
757 tx[jss::TransactionType] = jss::AccountSet;
758 tx[sfDomain] = newDomain;
763 testTx(env, tx, testSimulation);
765 tx[sfTxnSignature] =
"";
767 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
770 testTx(env, tx, testSimulation);
778 "Transaction with both single-signing SigningPubKey and "
779 "multi-signing Signers");
783 static auto const newDomain =
"123ABC";
791 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
797 auto result = resp[jss::result];
801 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
802 BEAST_EXPECT(result[jss::engine_result_code] == -277);
804 result[jss::engine_result_message] ==
"The transaction is ill-formed.");
806 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
812 tx[jss::TransactionType] = jss::AccountSet;
813 tx[sfDomain] = newDomain;
821 signerOuter[sfSigner] =
signer;
822 tx[sfSigners].
append(signerOuter);
826 testTx(env, tx, testSimulation,
false);
828 tx[sfTxnSignature] =
"";
830 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
831 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
strHex(becky.
pk().
slice());
832 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
835 testTx(env, tx, testSimulation);
842 testcase(
"Multi-signed transaction with a bad public key");
846 static auto const newDomain =
"123ABC";
855 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
859 auto result = resp[jss::result];
861 result, tx, env.
seq(alice), env.
current()->fees().base * 2);
864 result[jss::engine_result] ==
"tefBAD_SIGNATURE",
865 result[jss::engine_result].toStyledString());
866 BEAST_EXPECT(result[jss::engine_result_code] == -186);
868 result[jss::engine_result_message] ==
869 "A signature is provided for a non-signer.");
871 BEAST_EXPECT(!result.isMember(jss::meta) && !result.isMember(jss::meta_blob));
876 tx[jss::Account] = alice.
human();
877 tx[jss::TransactionType] = jss::AccountSet;
878 tx[sfDomain] = newDomain;
885 signerOuter[sfSigner] =
signer;
886 tx[sfSigners].
append(signerOuter);
890 testTx(env, tx, validateOutput,
false);
892 tx[sfSigningPubKey] =
"";
893 tx[sfTxnSignature] =
"";
894 tx[sfSequence] = env.
seq(alice);
897 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
900 testTx(env, tx, validateOutput);
907 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
914 Account const subject{
"subject"};
915 Account const issuer{
"issuer"};
917 env.
fund(
XRP(10000), subject, issuer);
920 auto const credType =
"123ABC";
923 uint32_t
const t = env.
current()->header().parentCloseTime.time_since_epoch().count();
924 jv[sfExpiration.jsonName] = t;
930 auto result = resp[jss::result];
933 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
934 BEAST_EXPECT(result[jss::engine_result_code] == 148);
935 BEAST_EXPECT(result[jss::engine_result_message] ==
"Expiration time is passed.");
937 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
941 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
943 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 5);
948 for (
auto const& node : metadata[sfAffectedNodes.jsonName])
950 if (node.isMember(sfDeletedNode.jsonName) &&
951 node[sfDeletedNode.jsonName][sfLedgerEntryType.jsonName]
952 .asString() ==
"Credential")
955 node[sfDeletedNode.jsonName][sfFinalFields.jsonName];
956 found = deleted[jss::Issuer] == issuer.human() &&
957 deleted[jss::Subject] == subject.human() &&
958 deleted[
"CredentialType"] ==
970 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
971 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
978 testTx(env, tx, validateOutput);
980 tx[sfSigningPubKey] =
"";
981 tx[sfTxnSignature] =
"";
982 tx[sfSequence] = env.
seq(subject);
983 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
986 testTx(env, tx, validateOutput);
992 jle.isObject() && jle.isMember(jss::result) && !jle[jss::result].isMember(jss::error) &&
993 jle[jss::result].isMember(jss::node) &&
994 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
995 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
996 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
997 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
1007 testcase(
"Successful transaction with a custom network ID");
1009 using namespace jtx;
1011 cfg->NETWORK_ID = 1025;
1014 static auto const newDomain =
"123ABC";
1018 auto result = resp[jss::result];
1021 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1022 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1024 result[jss::engine_result_message] ==
1025 "The simulated transaction would have been applied.");
1027 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
1031 if (BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName)))
1033 BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
size() == 1);
1034 auto node = metadata[sfAffectedNodes.jsonName][0u];
1035 if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName)))
1037 auto modifiedNode = node[sfModifiedNode];
1038 BEAST_EXPECT(modifiedNode[sfLedgerEntryType] ==
"AccountRoot");
1039 auto finalFields = modifiedNode[sfFinalFields];
1040 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1043 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1044 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1051 tx[jss::TransactionType] = jss::AccountSet;
1052 tx[sfDomain] = newDomain;
1055 testTx(env, tx, validateOutput);
1057 tx[sfSigningPubKey] =
"";
1058 tx[sfTxnSignature] =
"";
1060 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1061 tx[sfNetworkID] = 1025;
1064 testTx(env, tx, validateOutput);
1071 testcase(
"Successful transaction with additional metadata");
1073 using namespace jtx;
1074 using namespace std::chrono_literals;
1076 cfg->NETWORK_ID = 1025;
1083 env.
fund(
XRP(10000), alice, bob);
1090 auto validateOutput = [&](
Json::Value const& resp,
1094 auto result = resp[jss::result];
1096 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1097 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1099 result[jss::engine_result_message] ==
1100 "The simulated transaction would have been applied.");
1102 if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob)))
1106 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1107 BEAST_EXPECT(metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1108 BEAST_EXPECT(metadata.
isMember(expectedMetadataKey.asString()));
1109 BEAST_EXPECT(metadata[expectedMetadataKey.asString()] == expectedMetadataValue);
1115 tx[jss::Account] = alice.
human();
1116 tx[jss::TransactionType] = jss::Payment;
1117 tx[sfDestination] = bob.
human();
1118 tx[sfAmount] =
"100";
1126 tx[jss::Account] = alice.
human();
1127 tx[jss::TransactionType] = jss::NFTokenMint;
1128 tx[sfNFTokenTaxon] = 1;
1137 tx[jss::Account] = alice.
human();
1138 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1143 env, tx, validateOutput, jss::mpt_issuance_id, mptIssuanceId);
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)
static Json::Value getJsonMetadata(Json::Value txResult)
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,...
std::string to_string(base_uint< Bits, Tag > const &a)
std::string strHex(FwdIt begin, FwdIt end)
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.