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));
40 tx_json = result[jss::tx_json];
44 auto const unHexed =
strUnHex(result[jss::tx_blob].asString());
49 BEAST_EXPECT(tx_json[jss::TransactionType] == tx[jss::TransactionType]);
50 BEAST_EXPECT(tx_json[jss::Account] == tx[jss::Account]);
52 tx_json[jss::SigningPubKey] == tx.
get(jss::SigningPubKey,
""));
54 tx_json[jss::TxnSignature] == tx.
get(jss::TxnSignature,
""));
55 BEAST_EXPECT(tx_json[jss::Fee] == tx.
get(jss::Fee, expectedFee));
57 tx_json[jss::Sequence] == tx.
get(jss::Sequence, expectedSequence));
64 int const expectedSequence,
77 bool testSerialized =
true)
82 params[jss::tx_json] = tx;
83 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
85 params[jss::binary] =
true;
86 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
88 validate(env.
rpc(
"simulate",
to_string(tx),
"binary"), tx);
98 if (BEAST_EXPECT(parsed.
object.has_value()))
101 params[jss::tx_blob] = tx_blob;
102 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
103 params[jss::binary] =
true;
104 validate(env.
rpc(
"json",
"simulate",
to_string(params)), tx);
106 validate(env.
rpc(
"simulate", tx_blob), tx);
107 validate(env.
rpc(
"simulate", tx_blob,
"binary"), tx);
130 params[jss::tx_json] = tx;
135 expectedMetadataValue);
140 expectedMetadataValue);
150 if (txResult.
isMember(jss::meta_blob))
152 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
158 return txResult[jss::meta];
173 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
175 resp[jss::result][jss::error_message] ==
176 "Neither `tx_blob` nor `tx_json` included.");
182 params[jss::tx_blob] =
"1200";
184 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
186 resp[jss::result][jss::error_message] ==
187 "Can only include one of `tx_blob` and `tx_json`.");
192 params[jss::tx_blob] =
"1200";
193 params[jss::binary] =
"100";
194 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
196 resp[jss::result][jss::error_message] ==
197 "Invalid field 'binary'.");
202 params[jss::tx_blob] =
"12";
204 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
206 resp[jss::result][jss::error_message] ==
207 "Invalid field 'tx_blob'.");
214 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
216 resp[jss::result][jss::error_message] ==
217 "Missing field 'tx.TransactionType'.");
223 tx_json[jss::TransactionType] = jss::Payment;
224 params[jss::tx_json] = tx_json;
226 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
228 resp[jss::result][jss::error_message] ==
229 "Missing field 'tx.Account'.");
234 params[jss::tx_blob] =
"";
236 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
238 resp[jss::result][jss::error_message] ==
239 "Invalid field 'tx_blob'.");
244 params[jss::tx_blob] = 1.1;
246 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
248 resp[jss::result][jss::error_message] ==
249 "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] ==
259 "Invalid field 'tx_json', not object.");
264 params[jss::seed] =
"doesnt_matter";
266 tx_json[jss::TransactionType] = jss::AccountSet;
268 params[jss::tx_json] = tx_json;
269 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
271 resp[jss::result][jss::error_message] ==
272 "Invalid field 'seed'.");
277 params[jss::secret] =
"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 'secret'.");
290 params[jss::seed_hex] =
"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 'seed_hex'.");
303 params[jss::passphrase] =
"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 'passphrase'.");
317 tx_json[jss::TransactionType] = jss::Payment;
319 params[jss::tx_json] = tx_json;
321 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
323 resp[jss::result][jss::error_exception] ==
324 "Field 'Destination' is required but missing.");
330 tx_json[jss::TransactionType] = jss::AccountSet;
331 tx_json[jss::Account] =
"badAccount";
332 params[jss::tx_json] = tx_json;
334 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
336 resp[jss::result][jss::error] ==
"srcActMalformed",
337 resp[jss::result][jss::error].toStyledString());
339 resp[jss::result][jss::error_message] ==
340 "Invalid field 'tx.Account'.");
346 tx_json[jss::TransactionType] = jss::AccountSet;
347 tx_json[jss::Account] = alice.
human();
348 params[jss::tx_json] = tx_json;
350 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
352 resp[jss::result][jss::error_message] ==
353 "Source account not found.");
359 tx_json[jss::TransactionType] = jss::AccountSet;
361 tx_json[sfSigners] =
"1";
362 params[jss::tx_json] = tx_json;
364 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
366 resp[jss::result][jss::error_message] ==
367 "Invalid field 'tx.Signers'.");
373 tx_json[jss::TransactionType] = jss::AccountSet;
376 tx_json[sfSigners].
append(
"1");
377 params[jss::tx_json] = tx_json;
379 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
381 resp[jss::result][jss::error_message] ==
382 "Invalid field 'tx.Signers[0]'.");
388 tx_json[jss::TransactionType] = jss::AccountSet;
390 tx_json[
"foo"] =
"bar";
391 params[jss::tx_json] = tx_json;
393 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
395 resp[jss::result][jss::error_message] ==
396 "Field 'tx_json.foo' is unknown.");
401 tx_json[jss::TransactionType] = jss::AccountSet;
402 tx_json[jss::Account] = alice.
human();
403 auto const resp = env.
rpc(
"simulate",
to_string(tx_json),
"1");
404 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
410 tx_json[jss::TransactionType] = jss::AccountSet;
412 tx_json[jss::TxnSignature] =
"1200ABCD";
413 params[jss::tx_json] = tx_json;
415 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
417 resp[jss::result][jss::error_message] ==
418 "Transaction should not be signed.");
424 tx_json[jss::TransactionType] = jss::AccountSet;
431 signer[jss::TxnSignature] =
"1200ABCD";
433 signerOuter[sfSigner] =
signer;
434 tx_json[sfSigners].
append(signerOuter);
436 params[jss::tx_json] = tx_json;
438 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
440 resp[jss::result][jss::error_message] ==
441 "Transaction should not be signed.");
453 cfg->section(
"transaction_queue")
454 .
set(
"minimum_txn_in_ledger_standalone",
"3");
464 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
469 params[jss::tx_json] =
noop(alice);
471 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
472 auto const result = resp[jss::result];
473 if (BEAST_EXPECT(result.isMember(jss::error)))
475 BEAST_EXPECT(result[jss::error] ==
"highFee");
476 BEAST_EXPECT(result[jss::error_code] ==
rpcHIGH_FEE);
484 testcase(
"Invalid transaction type");
492 env.
fund(
XRP(1000000), alice, bob);
496 auto const seq = env.
seq(alice);
504 params[jss::tx_json] = jt.jv;
505 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
506 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
508 resp[jss::result][jss::error_message] ==
"Not implemented.");
521 static auto const newDomain =
"123ABC";
526 auto result = resp[jss::result];
528 result, tx, 1, env.
current()->fees().base);
530 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
531 BEAST_EXPECT(result[jss::engine_result_code] == 0);
533 result[jss::engine_result_message] ==
534 "The simulated transaction would have been applied.");
537 result.isMember(jss::meta) ||
538 result.isMember(jss::meta_blob)))
543 metadata.
isMember(sfAffectedNodes.jsonName)))
546 metadata[sfAffectedNodes.jsonName].
size() == 1);
547 auto node = metadata[sfAffectedNodes.jsonName][0u];
549 node.isMember(sfModifiedNode.jsonName)))
551 auto modifiedNode = node[sfModifiedNode];
553 modifiedNode[sfLedgerEntryType] ==
555 auto finalFields = modifiedNode[sfFinalFields];
556 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
559 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
561 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
568 tx[jss::TransactionType] = jss::AccountSet;
569 tx[sfDomain] = newDomain;
572 testTx(env, tx, validateOutput);
574 tx[sfSigningPubKey] =
"";
575 tx[sfTxnSignature] =
"";
577 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
580 testTx(env, tx, validateOutput);
587 testcase(
"Transaction non-tec failure");
597 auto result = resp[jss::result];
599 result, tx, 1, env.
current()->fees().base);
601 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
602 BEAST_EXPECT(result[jss::engine_result_code] == -298);
604 result[jss::engine_result_message] ==
605 "Malformed: Bad amount.");
608 !result.isMember(jss::meta) &&
609 !result.isMember(jss::meta_blob));
615 tx[jss::TransactionType] = jss::Payment;
616 tx[sfDestination] = alice.
human();
620 testTx(env, tx, testSimulation);
622 tx[sfSigningPubKey] =
"";
623 tx[sfTxnSignature] =
"";
625 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
628 testTx(env, tx, testSimulation);
635 testcase(
"Transaction tec failure");
645 auto result = resp[jss::result];
647 result, tx, 1, env.
current()->fees().base);
650 result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
651 BEAST_EXPECT(result[jss::engine_result_code] == 125);
653 result[jss::engine_result_message] ==
654 "Destination does not exist. Too little XRP sent to "
659 result.isMember(jss::meta) ||
660 result.isMember(jss::meta_blob)))
665 metadata.
isMember(sfAffectedNodes.jsonName)))
668 metadata[sfAffectedNodes.jsonName].
size() == 1);
669 auto node = metadata[sfAffectedNodes.jsonName][0u];
671 node.isMember(sfModifiedNode.jsonName)))
673 auto modifiedNode = node[sfModifiedNode];
675 modifiedNode[sfLedgerEntryType] ==
677 auto finalFields = modifiedNode[sfFinalFields];
679 finalFields[sfBalance] ==
681 100'000'000'000'000'000 -
682 env.
current()->fees().base.drops()));
686 metadata[sfTransactionIndex.jsonName] == 0);
688 metadata[sfTransactionResult.jsonName] ==
689 "tecNO_DST_INSUF_XRP");
696 tx[jss::TransactionType] = jss::Payment;
697 tx[sfDestination] = alice.
human();
701 testTx(env, tx, testSimulation);
703 tx[sfSigningPubKey] =
"";
704 tx[sfTxnSignature] =
"";
706 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
709 testTx(env, tx, testSimulation);
716 testcase(
"Successful multi-signed transaction");
720 static auto const newDomain =
"123ABC";
728 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
734 auto result = resp[jss::result];
742 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
743 BEAST_EXPECT(result[jss::engine_result_code] == 0);
745 result[jss::engine_result_message] ==
746 "The simulated transaction would have been applied.");
749 result.isMember(jss::meta) ||
750 result.isMember(jss::meta_blob)))
755 metadata.
isMember(sfAffectedNodes.jsonName)))
758 metadata[sfAffectedNodes.jsonName].
size() == 1);
759 auto node = metadata[sfAffectedNodes.jsonName][0u];
761 node.isMember(sfModifiedNode.jsonName)))
763 auto modifiedNode = node[sfModifiedNode];
765 modifiedNode[sfLedgerEntryType] ==
767 auto finalFields = modifiedNode[sfFinalFields];
768 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
771 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
773 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
779 tx[jss::Account] = alice.
human();
780 tx[jss::TransactionType] = jss::AccountSet;
781 tx[sfDomain] = newDomain;
784 testTx(env, tx, validateOutput,
false);
791 signerOuter[sfSigner] =
signer;
792 tx[sfSigners].
append(signerOuter);
796 testTx(env, tx, validateOutput,
false);
798 tx[sfSigningPubKey] =
"";
799 tx[sfTxnSignature] =
"";
800 tx[sfSequence] = env.
seq(alice);
804 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
805 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
808 testTx(env, tx, validateOutput);
815 testcase(
"Transaction with a key-related failure");
819 static auto const newDomain =
"123ABC";
829 auto result = resp[jss::result];
837 result[jss::engine_result] ==
"tefMASTER_DISABLED");
838 BEAST_EXPECT(result[jss::engine_result_code] == -188);
840 result[jss::engine_result_message] ==
841 "Master key is disabled.");
844 !result.isMember(jss::meta) &&
845 !result.isMember(jss::meta_blob));
851 tx[jss::TransactionType] = jss::AccountSet;
852 tx[sfDomain] = newDomain;
857 testTx(env, tx, testSimulation);
859 tx[sfTxnSignature] =
"";
861 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
864 testTx(env, tx, testSimulation);
872 "Transaction with both single-signing SigningPubKey and "
873 "multi-signing Signers");
877 static auto const newDomain =
"123ABC";
885 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
892 auto result = resp[jss::result];
897 env.
current()->fees().base * 2);
899 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
900 BEAST_EXPECT(result[jss::engine_result_code] == -277);
902 result[jss::engine_result_message] ==
903 "The transaction is ill-formed.");
906 !result.isMember(jss::meta) &&
907 !result.isMember(jss::meta_blob));
913 tx[jss::TransactionType] = jss::AccountSet;
914 tx[sfDomain] = newDomain;
922 signerOuter[sfSigner] =
signer;
923 tx[sfSigners].
append(signerOuter);
927 testTx(env, tx, testSimulation,
false);
929 tx[sfTxnSignature] =
"";
931 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
932 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
934 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
937 testTx(env, tx, testSimulation);
944 testcase(
"Multi-signed transaction with a bad public key");
948 static auto const newDomain =
"123ABC";
957 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
962 auto result = resp[jss::result];
964 result, tx, env.
seq(alice), env.
current()->fees().base * 2);
967 result[jss::engine_result] ==
"tefBAD_SIGNATURE",
968 result[jss::engine_result].toStyledString());
969 BEAST_EXPECT(result[jss::engine_result_code] == -186);
971 result[jss::engine_result_message] ==
972 "A signature is provided for a non-signer.");
975 !result.isMember(jss::meta) &&
976 !result.isMember(jss::meta_blob));
981 tx[jss::Account] = alice.
human();
982 tx[jss::TransactionType] = jss::AccountSet;
983 tx[sfDomain] = newDomain;
990 signerOuter[sfSigner] =
signer;
991 tx[sfSigners].
append(signerOuter);
995 testTx(env, tx, validateOutput,
false);
997 tx[sfSigningPubKey] =
"";
998 tx[sfTxnSignature] =
"";
999 tx[sfSequence] = env.
seq(alice);
1003 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
1006 testTx(env, tx, validateOutput);
1013 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
1017 using namespace jtx;
1020 Account const subject{
"subject"};
1021 Account const issuer{
"issuer"};
1023 env.
fund(
XRP(10000), subject, issuer);
1026 auto const credType =
"123ABC";
1030 env.
current()->info().parentCloseTime.time_since_epoch().count();
1031 jv[sfExpiration.jsonName] = t;
1036 auto validateOutput = [&](
Json::Value const& resp,
1038 auto result = resp[jss::result];
1040 result, tx, env.
seq(subject), env.
current()->fees().base);
1042 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
1043 BEAST_EXPECT(result[jss::engine_result_code] == 148);
1045 result[jss::engine_result_message] ==
1046 "Expiration time is passed.");
1049 result.isMember(jss::meta) ||
1050 result.isMember(jss::meta_blob)))
1055 metadata.
isMember(sfAffectedNodes.jsonName)))
1058 metadata[sfAffectedNodes.jsonName].
size() == 5);
1063 for (
auto const& node :
1064 metadata[sfAffectedNodes.jsonName])
1066 if (node.isMember(sfDeletedNode.jsonName) &&
1067 node[sfDeletedNode.jsonName]
1068 [sfLedgerEntryType.jsonName]
1069 .asString() ==
"Credential")
1071 auto const deleted =
1072 node[sfDeletedNode.jsonName]
1073 [sfFinalFields.jsonName];
1074 found = deleted[jss::Issuer] ==
1076 deleted[jss::Subject] ==
1078 deleted[
"CredentialType"] ==
1083 BEAST_EXPECT(found);
1090 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1092 metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
1099 testTx(env, tx, validateOutput);
1101 tx[sfSigningPubKey] =
"";
1102 tx[sfTxnSignature] =
"";
1103 tx[sfSequence] = env.
seq(subject);
1104 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1107 testTx(env, tx, validateOutput);
1114 jle.isObject() && jle.isMember(jss::result) &&
1115 !jle[jss::result].isMember(jss::error) &&
1116 jle[jss::result].isMember(jss::node) &&
1117 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1118 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1119 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1120 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
1121 jle[jss::result][jss::node][
"CredentialType"] ==
1131 testcase(
"Successful transaction with a custom network ID");
1133 using namespace jtx;
1135 cfg->NETWORK_ID = 1025;
1138 static auto const newDomain =
"123ABC";
1141 auto validateOutput = [&](
Json::Value const& resp,
1143 auto result = resp[jss::result];
1145 result, tx, 1, env.
current()->fees().base);
1147 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1148 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1150 result[jss::engine_result_message] ==
1151 "The simulated transaction would have been applied.");
1154 result.isMember(jss::meta) ||
1155 result.isMember(jss::meta_blob)))
1160 metadata.
isMember(sfAffectedNodes.jsonName)))
1163 metadata[sfAffectedNodes.jsonName].
size() == 1);
1164 auto node = metadata[sfAffectedNodes.jsonName][0u];
1166 node.isMember(sfModifiedNode.jsonName)))
1168 auto modifiedNode = node[sfModifiedNode];
1170 modifiedNode[sfLedgerEntryType] ==
1172 auto finalFields = modifiedNode[sfFinalFields];
1173 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1176 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1178 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1185 tx[jss::TransactionType] = jss::AccountSet;
1186 tx[sfDomain] = newDomain;
1189 testTx(env, tx, validateOutput);
1191 tx[sfSigningPubKey] =
"";
1192 tx[sfTxnSignature] =
"";
1194 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1195 tx[sfNetworkID] = 1025;
1198 testTx(env, tx, validateOutput);
1205 testcase(
"Successful transaction with additional metadata");
1207 using namespace jtx;
1208 using namespace std::chrono_literals;
1210 cfg->NETWORK_ID = 1025;
1217 env.
fund(
XRP(10000), alice, bob);
1224 auto validateOutput =
1229 auto result = resp[jss::result];
1231 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1232 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1234 result[jss::engine_result_message] ==
1235 "The simulated transaction would have been applied.");
1238 result.isMember(jss::meta) ||
1239 result.isMember(jss::meta_blob)))
1244 metadata[sfTransactionIndex.jsonName] == 0);
1246 metadata[sfTransactionResult.jsonName] ==
1249 metadata.
isMember(expectedMetadataKey.asString()));
1251 metadata[expectedMetadataKey.asString()] ==
1252 expectedMetadataValue);
1258 tx[jss::Account] = alice.
human();
1259 tx[jss::TransactionType] = jss::Payment;
1260 tx[sfDestination] = bob.
human();
1261 tx[sfAmount] =
"100";
1265 env, tx, validateOutput, jss::delivered_amount,
"100");
1270 tx[jss::Account] = alice.
human();
1271 tx[jss::TransactionType] = jss::NFTokenMint;
1272 tx[sfNFTokenTaxon] = 1;
1278 env, tx, validateOutput, jss::nftoken_id, nftokenId);
1283 tx[jss::Account] = alice.
human();
1284 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1293 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 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 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)
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)
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.
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)
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Set the sequence number on a JTx.
A signer in a SignerList.