21#include <test/jtx/Env.h>
22#include <test/jtx/envconfig.h>
24#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
25#include <xrpld/rpc/CTID.h>
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>
46 int const expectedSequence,
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));
59 tx_json = result[jss::tx_json];
63 auto const unHexed =
strUnHex(result[jss::tx_blob].asString());
68 BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]);
69 BEAST_EXPECT(tx_json[jss::Account] == tx[jss::Account]);
71 tx_json[jss::SigningPubKey] == tx.
get(jss::SigningPubKey,
""));
73 tx_json[jss::TxnSignature] == tx.
get(jss::TxnSignature,
""));
74 BEAST_EXPECT(tx_json[jss::Fee] == tx.
get(jss::Fee, expectedFee));
76 tx_json[jss::Sequence] == tx.
get(jss::Sequence, expectedSequence));
83 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);
117 if (BEAST_EXPECT(parsed.
object.has_value()))
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);
125 validate(env.
rpc(
"simulate", tx_blob), tx);
126 validate(env.
rpc(
"simulate", tx_blob,
"binary"), tx);
143 bool testSerialized =
true)
148 params[jss::tx_json] = tx;
152 expectedMetadataKey);
153 validate(env.
rpc(
"simulate",
to_string(tx)), tx, expectedMetadataKey);
163 if (txResult.
isMember(jss::meta_blob))
165 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
171 return txResult[jss::meta];
186 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
188 resp[jss::result][jss::error_message] ==
189 "Neither `tx_blob` nor `tx_json` included.");
195 params[jss::tx_blob] =
"1200";
197 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
199 resp[jss::result][jss::error_message] ==
200 "Can only include one of `tx_blob` and `tx_json`.");
205 params[jss::tx_blob] =
"1200";
206 params[jss::binary] =
"100";
207 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
209 resp[jss::result][jss::error_message] ==
210 "Invalid field 'binary'.");
215 params[jss::tx_blob] =
"12";
217 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
219 resp[jss::result][jss::error_message] ==
220 "Invalid field 'tx_blob'.");
227 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
229 resp[jss::result][jss::error_message] ==
230 "Missing field 'tx.TransactionType'.");
236 tx_json[jss::TransactionType] = jss::Payment;
237 params[jss::tx_json] = tx_json;
239 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
241 resp[jss::result][jss::error_message] ==
242 "Missing field 'tx.Account'.");
247 params[jss::tx_blob] =
"";
249 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
251 resp[jss::result][jss::error_message] ==
252 "Invalid field 'tx_blob'.");
257 params[jss::tx_blob] = 1.1;
259 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
261 resp[jss::result][jss::error_message] ==
262 "Invalid field 'tx_blob'.");
267 params[jss::tx_json] =
"";
269 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
271 resp[jss::result][jss::error_message] ==
272 "Invalid field 'tx_json', not object.");
277 params[jss::seed] =
"doesnt_matter";
279 tx_json[jss::TransactionType] = jss::AccountSet;
281 params[jss::tx_json] = tx_json;
282 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
284 resp[jss::result][jss::error_message] ==
285 "Invalid field 'seed'.");
290 params[jss::secret] =
"doesnt_matter";
292 tx_json[jss::TransactionType] = jss::AccountSet;
294 params[jss::tx_json] = tx_json;
295 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
297 resp[jss::result][jss::error_message] ==
298 "Invalid field 'secret'.");
303 params[jss::seed_hex] =
"doesnt_matter";
305 tx_json[jss::TransactionType] = jss::AccountSet;
307 params[jss::tx_json] = tx_json;
308 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
310 resp[jss::result][jss::error_message] ==
311 "Invalid field 'seed_hex'.");
316 params[jss::passphrase] =
"doesnt_matter";
318 tx_json[jss::TransactionType] = jss::AccountSet;
320 params[jss::tx_json] = tx_json;
321 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
323 resp[jss::result][jss::error_message] ==
324 "Invalid field 'passphrase'.");
330 tx_json[jss::TransactionType] = jss::Payment;
332 params[jss::tx_json] = tx_json;
334 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
336 resp[jss::result][jss::error_exception] ==
337 "Field 'Destination' is required but missing.");
343 tx_json[jss::TransactionType] = jss::AccountSet;
344 tx_json[jss::Account] =
"badAccount";
345 params[jss::tx_json] = tx_json;
347 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
349 resp[jss::result][jss::error] ==
"srcActMalformed",
350 resp[jss::result][jss::error].toStyledString());
352 resp[jss::result][jss::error_message] ==
353 "Invalid field 'tx.Account'.");
359 tx_json[jss::TransactionType] = jss::AccountSet;
360 tx_json[jss::Account] = alice.
human();
361 params[jss::tx_json] = tx_json;
363 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
365 resp[jss::result][jss::error_message] ==
366 "Source account not found.");
372 tx_json[jss::TransactionType] = jss::AccountSet;
374 tx_json[sfSigners] =
"1";
375 params[jss::tx_json] = tx_json;
377 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
379 resp[jss::result][jss::error_message] ==
380 "Invalid field 'tx.Signers'.");
386 tx_json[jss::TransactionType] = jss::AccountSet;
389 tx_json[sfSigners].
append(
"1");
390 params[jss::tx_json] = tx_json;
392 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
394 resp[jss::result][jss::error_message] ==
395 "Invalid field 'tx.Signers[0]'.");
401 tx_json[jss::TransactionType] = jss::AccountSet;
403 tx_json[
"foo"] =
"bar";
404 params[jss::tx_json] = tx_json;
406 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
408 resp[jss::result][jss::error_message] ==
409 "Field 'tx_json.foo' is unknown.");
414 tx_json[jss::TransactionType] = jss::AccountSet;
415 tx_json[jss::Account] = alice.
human();
416 auto const resp = env.
rpc(
"simulate",
to_string(tx_json),
"1");
417 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
423 tx_json[jss::TransactionType] = jss::AccountSet;
425 tx_json[jss::TxnSignature] =
"1200ABCD";
426 params[jss::tx_json] = tx_json;
428 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
430 resp[jss::result][jss::error_message] ==
431 "Transaction should not be signed.");
437 tx_json[jss::TransactionType] = jss::AccountSet;
444 signer[jss::TxnSignature] =
"1200ABCD";
446 signerOuter[sfSigner] =
signer;
447 tx_json[sfSigners].
append(signerOuter);
449 params[jss::tx_json] = tx_json;
451 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
453 resp[jss::result][jss::error_message] ==
454 "Transaction should not be signed.");
466 cfg->section(
"transaction_queue")
467 .
set(
"minimum_txn_in_ledger_standalone",
"3");
477 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
482 params[jss::tx_json] =
noop(alice);
484 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
485 auto const result = resp[jss::result];
486 if (BEAST_EXPECT(result.isMember(jss::error)))
488 BEAST_EXPECT(result[jss::error] ==
"highFee");
489 BEAST_EXPECT(result[jss::error_code] ==
rpcHIGH_FEE);
497 testcase(
"Invalid transaction type");
505 env.
fund(
XRP(1000000), alice, bob);
509 auto const seq = env.
seq(alice);
517 params[jss::tx_json] = jt.jv;
518 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
519 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
521 resp[jss::result][jss::error_message] ==
"Not implemented.");
534 static auto const newDomain =
"123ABC";
539 auto result = resp[jss::result];
541 result, tx, 1, env.
current()->fees().base);
543 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
544 BEAST_EXPECT(result[jss::engine_result_code] == 0);
546 result[jss::engine_result_message] ==
547 "The simulated transaction would have been applied.");
550 result.isMember(jss::meta) ||
551 result.isMember(jss::meta_blob)))
556 metadata.
isMember(sfAffectedNodes.jsonName)))
559 metadata[sfAffectedNodes.jsonName].
size() == 1);
560 auto node = metadata[sfAffectedNodes.jsonName][0u];
562 node.isMember(sfModifiedNode.jsonName)))
564 auto modifiedNode = node[sfModifiedNode];
566 modifiedNode[sfLedgerEntryType] ==
568 auto finalFields = modifiedNode[sfFinalFields];
569 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
572 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
574 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
581 tx[jss::TransactionType] = jss::AccountSet;
582 tx[sfDomain] = newDomain;
585 testTx(env, tx, validateOutput);
587 tx[sfSigningPubKey] =
"";
588 tx[sfTxnSignature] =
"";
590 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
593 testTx(env, tx, validateOutput);
600 testcase(
"Transaction non-tec failure");
610 auto result = resp[jss::result];
612 result, tx, 1, env.
current()->fees().base);
614 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
615 BEAST_EXPECT(result[jss::engine_result_code] == -298);
617 result[jss::engine_result_message] ==
618 "Malformed: Bad amount.");
621 !result.isMember(jss::meta) &&
622 !result.isMember(jss::meta_blob));
628 tx[jss::TransactionType] = jss::Payment;
629 tx[sfDestination] = alice.
human();
633 testTx(env, tx, testSimulation);
635 tx[sfSigningPubKey] =
"";
636 tx[sfTxnSignature] =
"";
638 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
641 testTx(env, tx, testSimulation);
648 testcase(
"Transaction tec failure");
658 auto result = resp[jss::result];
660 result, tx, 1, env.
current()->fees().base);
663 result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
664 BEAST_EXPECT(result[jss::engine_result_code] == 125);
666 result[jss::engine_result_message] ==
667 "Destination does not exist. Too little XRP sent to "
672 result.isMember(jss::meta) ||
673 result.isMember(jss::meta_blob)))
678 metadata.
isMember(sfAffectedNodes.jsonName)))
681 metadata[sfAffectedNodes.jsonName].
size() == 1);
682 auto node = metadata[sfAffectedNodes.jsonName][0u];
684 node.isMember(sfModifiedNode.jsonName)))
686 auto modifiedNode = node[sfModifiedNode];
688 modifiedNode[sfLedgerEntryType] ==
690 auto finalFields = modifiedNode[sfFinalFields];
692 finalFields[sfBalance] ==
694 100'000'000'000'000'000 -
695 env.
current()->fees().base.drops()));
699 metadata[sfTransactionIndex.jsonName] == 0);
701 metadata[sfTransactionResult.jsonName] ==
702 "tecNO_DST_INSUF_XRP");
709 tx[jss::TransactionType] = jss::Payment;
710 tx[sfDestination] = alice.
human();
714 testTx(env, tx, testSimulation);
716 tx[sfSigningPubKey] =
"";
717 tx[sfTxnSignature] =
"";
719 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
722 testTx(env, tx, testSimulation);
729 testcase(
"Successful multi-signed transaction");
733 static auto const newDomain =
"123ABC";
741 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
747 auto result = resp[jss::result];
755 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
756 BEAST_EXPECT(result[jss::engine_result_code] == 0);
758 result[jss::engine_result_message] ==
759 "The simulated transaction would have been applied.");
762 result.isMember(jss::meta) ||
763 result.isMember(jss::meta_blob)))
768 metadata.
isMember(sfAffectedNodes.jsonName)))
771 metadata[sfAffectedNodes.jsonName].
size() == 1);
772 auto node = metadata[sfAffectedNodes.jsonName][0u];
774 node.isMember(sfModifiedNode.jsonName)))
776 auto modifiedNode = node[sfModifiedNode];
778 modifiedNode[sfLedgerEntryType] ==
780 auto finalFields = modifiedNode[sfFinalFields];
781 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
784 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
786 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
792 tx[jss::Account] = alice.
human();
793 tx[jss::TransactionType] = jss::AccountSet;
794 tx[sfDomain] = newDomain;
797 testTx(env, tx, validateOutput,
false);
804 signerOuter[sfSigner] =
signer;
805 tx[sfSigners].
append(signerOuter);
809 testTx(env, tx, validateOutput,
false);
811 tx[sfSigningPubKey] =
"";
812 tx[sfTxnSignature] =
"";
813 tx[sfSequence] = env.
seq(alice);
817 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
818 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
821 testTx(env, tx, validateOutput);
828 testcase(
"Transaction with a key-related failure");
832 static auto const newDomain =
"123ABC";
842 auto result = resp[jss::result];
850 result[jss::engine_result] ==
"tefMASTER_DISABLED");
851 BEAST_EXPECT(result[jss::engine_result_code] == -188);
853 result[jss::engine_result_message] ==
854 "Master key is disabled.");
857 !result.isMember(jss::meta) &&
858 !result.isMember(jss::meta_blob));
864 tx[jss::TransactionType] = jss::AccountSet;
865 tx[sfDomain] = newDomain;
870 testTx(env, tx, testSimulation);
872 tx[sfTxnSignature] =
"";
874 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
877 testTx(env, tx, testSimulation);
885 "Transaction with both single-signing SigningPubKey and "
886 "multi-signing Signers");
890 static auto const newDomain =
"123ABC";
898 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
905 auto result = resp[jss::result];
910 env.
current()->fees().base * 2);
912 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
913 BEAST_EXPECT(result[jss::engine_result_code] == -277);
915 result[jss::engine_result_message] ==
916 "The transaction is ill-formed.");
919 !result.isMember(jss::meta) &&
920 !result.isMember(jss::meta_blob));
926 tx[jss::TransactionType] = jss::AccountSet;
927 tx[sfDomain] = newDomain;
935 signerOuter[sfSigner] =
signer;
936 tx[sfSigners].
append(signerOuter);
940 testTx(env, tx, testSimulation,
false);
942 tx[sfTxnSignature] =
"";
944 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
945 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
947 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
950 testTx(env, tx, testSimulation);
957 testcase(
"Multi-signed transaction with a bad public key");
961 static auto const newDomain =
"123ABC";
970 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
975 auto result = resp[jss::result];
977 result, tx, env.
seq(alice), env.
current()->fees().base * 2);
980 result[jss::engine_result] ==
"tefBAD_SIGNATURE",
981 result[jss::engine_result].toStyledString());
982 BEAST_EXPECT(result[jss::engine_result_code] == -186);
984 result[jss::engine_result_message] ==
985 "A signature is provided for a non-signer.");
988 !result.isMember(jss::meta) &&
989 !result.isMember(jss::meta_blob));
994 tx[jss::Account] = alice.
human();
995 tx[jss::TransactionType] = jss::AccountSet;
996 tx[sfDomain] = newDomain;
1003 signerOuter[sfSigner] =
signer;
1004 tx[sfSigners].
append(signerOuter);
1008 testTx(env, tx, validateOutput,
false);
1010 tx[sfSigningPubKey] =
"";
1011 tx[sfTxnSignature] =
"";
1012 tx[sfSequence] = env.
seq(alice);
1016 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
1019 testTx(env, tx, validateOutput);
1026 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
1030 using namespace jtx;
1033 Account const subject{
"subject"};
1034 Account const issuer{
"issuer"};
1036 env.
fund(
XRP(10000), subject, issuer);
1039 auto const credType =
"123ABC";
1043 env.
current()->info().parentCloseTime.time_since_epoch().count();
1044 jv[sfExpiration.jsonName] = t;
1049 auto validateOutput = [&](
Json::Value const& resp,
1051 auto result = resp[jss::result];
1053 result, tx, env.
seq(subject), env.
current()->fees().base);
1055 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
1056 BEAST_EXPECT(result[jss::engine_result_code] == 148);
1058 result[jss::engine_result_message] ==
1059 "Expiration time is passed.");
1062 result.isMember(jss::meta) ||
1063 result.isMember(jss::meta_blob)))
1068 metadata.
isMember(sfAffectedNodes.jsonName)))
1071 metadata[sfAffectedNodes.jsonName].
size() == 5);
1076 for (
auto const& node :
1077 metadata[sfAffectedNodes.jsonName])
1079 if (node.isMember(sfDeletedNode.jsonName) &&
1080 node[sfDeletedNode.jsonName]
1081 [sfLedgerEntryType.jsonName]
1082 .asString() ==
"Credential")
1084 auto const deleted =
1085 node[sfDeletedNode.jsonName]
1086 [sfFinalFields.jsonName];
1087 found = deleted[jss::Issuer] ==
1089 deleted[jss::Subject] ==
1091 deleted[
"CredentialType"] ==
1096 BEAST_EXPECT(found);
1103 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1105 metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
1112 testTx(env, tx, validateOutput);
1114 tx[sfSigningPubKey] =
"";
1115 tx[sfTxnSignature] =
"";
1116 tx[sfSequence] = env.
seq(subject);
1117 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1120 testTx(env, tx, validateOutput);
1127 jle.isObject() && jle.isMember(jss::result) &&
1128 !jle[jss::result].isMember(jss::error) &&
1129 jle[jss::result].isMember(jss::node) &&
1130 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1131 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1132 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1133 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
1134 jle[jss::result][jss::node][
"CredentialType"] ==
1144 testcase(
"Successful transaction with a custom network ID");
1146 using namespace jtx;
1148 cfg->NETWORK_ID = 1025;
1151 static auto const newDomain =
"123ABC";
1154 auto validateOutput = [&](
Json::Value const& resp,
1156 auto result = resp[jss::result];
1158 result, tx, 1, env.
current()->fees().base);
1160 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1161 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1163 result[jss::engine_result_message] ==
1164 "The simulated transaction would have been applied.");
1167 result.isMember(jss::meta) ||
1168 result.isMember(jss::meta_blob)))
1173 metadata.
isMember(sfAffectedNodes.jsonName)))
1176 metadata[sfAffectedNodes.jsonName].
size() == 1);
1177 auto node = metadata[sfAffectedNodes.jsonName][0u];
1179 node.isMember(sfModifiedNode.jsonName)))
1181 auto modifiedNode = node[sfModifiedNode];
1183 modifiedNode[sfLedgerEntryType] ==
1185 auto finalFields = modifiedNode[sfFinalFields];
1186 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1189 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1191 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1198 tx[jss::TransactionType] = jss::AccountSet;
1199 tx[sfDomain] = newDomain;
1202 testTx(env, tx, validateOutput);
1204 tx[sfSigningPubKey] =
"";
1205 tx[sfTxnSignature] =
"";
1207 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1208 tx[sfNetworkID] = 1025;
1211 testTx(env, tx, validateOutput);
1218 testcase(
"Successful transaction with additional metadata");
1220 using namespace jtx;
1222 cfg->NETWORK_ID = 1025;
1232 auto validateOutput = [&](
Json::Value const& resp,
1235 auto result = resp[jss::result];
1237 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1238 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1240 result[jss::engine_result_message] ==
1241 "The simulated transaction would have been applied.");
1244 result.isMember(jss::meta) ||
1245 result.isMember(jss::meta_blob)))
1249 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1251 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1253 metadata.
isMember(expectedMetadataKey.asString()));
1260 tx[jss::TransactionType] = jss::Payment;
1261 tx[sfDestination] = alice.
human();
1262 tx[sfAmount] =
"100";
1266 env, tx, validateOutput, jss::delivered_amount);
1272 tx[jss::TransactionType] = jss::NFTokenMint;
1273 tx[sfNFTokenTaxon] = 1;
1277 env, tx, validateOutput, jss::nftoken_id);
1283 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1287 env, tx, validateOutput, jss::mpt_issuance_id);
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
Json::Value getJsonMetadata(Json::Value txResult) const
void testTransactionNonTecFailure()
void testTx(jtx::Env &env, Json::Value const &tx, std::function< void(Json::Value const &, Json::Value const &)> const &validate, bool testSerialized=true)
void testSuccessfulTransactionAdditionalMetadata()
void testInvalidTransactionType()
void testSuccessfulTransactionNetworkID()
void run() override
Runs the suite.
void testInvalidSingleAndMultiSigningTransaction()
void testSuccessfulTransaction()
void testTransactionTecFailure()
void testMultisignedBadPubKey()
void testSuccessfulTransactionMultisigned()
void testDeleteExpiredCredentials()
void testTransactionSigningFailure()
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)
void testTxJsonMetadataField(jtx::Env &env, Json::Value const &tx, std::function< void(Json::Value const &, Json::Value const &, Json::Value const &)> const &validate, Json::Value const &expectedMetadataKey, bool testSerialized=true)
Immutable cryptographic account descriptor.
PublicKey const & pk() const
Return the public key.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
JTx jtnofill(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
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 create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
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)
std::uint32_t ownerCount(Env const &env, Account const &account)
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
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.
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
XRP_t const XRP
Converts to XRP Issue or STAmount.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfAllOrNothing
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
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 strHex(FwdIt begin, FwdIt 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)
std::string to_string(base_uint< Bits, Tag > const &a)
Set the sequence number on a JTx.
A signer in a SignerList.