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);
149 params[jss::tx_json] = tx;
154 expectedMetadataValue);
159 expectedMetadataValue);
169 if (txResult.
isMember(jss::meta_blob))
171 auto unHexed =
strUnHex(txResult[jss::meta_blob].asString());
177 return txResult[jss::meta];
192 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
194 resp[jss::result][jss::error_message] ==
195 "Neither `tx_blob` nor `tx_json` included.");
201 params[jss::tx_blob] =
"1200";
203 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
205 resp[jss::result][jss::error_message] ==
206 "Can only include one of `tx_blob` and `tx_json`.");
211 params[jss::tx_blob] =
"1200";
212 params[jss::binary] =
"100";
213 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
215 resp[jss::result][jss::error_message] ==
216 "Invalid field 'binary'.");
221 params[jss::tx_blob] =
"12";
223 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
225 resp[jss::result][jss::error_message] ==
226 "Invalid field 'tx_blob'.");
233 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
235 resp[jss::result][jss::error_message] ==
236 "Missing field 'tx.TransactionType'.");
242 tx_json[jss::TransactionType] = jss::Payment;
243 params[jss::tx_json] = tx_json;
245 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
247 resp[jss::result][jss::error_message] ==
248 "Missing field 'tx.Account'.");
253 params[jss::tx_blob] =
"";
255 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
257 resp[jss::result][jss::error_message] ==
258 "Invalid field 'tx_blob'.");
263 params[jss::tx_blob] = 1.1;
265 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
267 resp[jss::result][jss::error_message] ==
268 "Invalid field 'tx_blob'.");
273 params[jss::tx_json] =
"";
275 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
277 resp[jss::result][jss::error_message] ==
278 "Invalid field 'tx_json', not object.");
283 params[jss::seed] =
"doesnt_matter";
285 tx_json[jss::TransactionType] = jss::AccountSet;
287 params[jss::tx_json] = tx_json;
288 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
290 resp[jss::result][jss::error_message] ==
291 "Invalid field 'seed'.");
296 params[jss::secret] =
"doesnt_matter";
298 tx_json[jss::TransactionType] = jss::AccountSet;
300 params[jss::tx_json] = tx_json;
301 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
303 resp[jss::result][jss::error_message] ==
304 "Invalid field 'secret'.");
309 params[jss::seed_hex] =
"doesnt_matter";
311 tx_json[jss::TransactionType] = jss::AccountSet;
313 params[jss::tx_json] = tx_json;
314 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
316 resp[jss::result][jss::error_message] ==
317 "Invalid field 'seed_hex'.");
322 params[jss::passphrase] =
"doesnt_matter";
324 tx_json[jss::TransactionType] = jss::AccountSet;
326 params[jss::tx_json] = tx_json;
327 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
329 resp[jss::result][jss::error_message] ==
330 "Invalid field 'passphrase'.");
336 tx_json[jss::TransactionType] = jss::Payment;
338 params[jss::tx_json] = tx_json;
340 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
342 resp[jss::result][jss::error_exception] ==
343 "Field 'Destination' is required but missing.");
349 tx_json[jss::TransactionType] = jss::AccountSet;
350 tx_json[jss::Account] =
"badAccount";
351 params[jss::tx_json] = tx_json;
353 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
355 resp[jss::result][jss::error] ==
"srcActMalformed",
356 resp[jss::result][jss::error].toStyledString());
358 resp[jss::result][jss::error_message] ==
359 "Invalid field 'tx.Account'.");
365 tx_json[jss::TransactionType] = jss::AccountSet;
366 tx_json[jss::Account] = alice.
human();
367 params[jss::tx_json] = tx_json;
369 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
371 resp[jss::result][jss::error_message] ==
372 "Source account not found.");
378 tx_json[jss::TransactionType] = jss::AccountSet;
380 tx_json[sfSigners] =
"1";
381 params[jss::tx_json] = tx_json;
383 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
385 resp[jss::result][jss::error_message] ==
386 "Invalid field 'tx.Signers'.");
392 tx_json[jss::TransactionType] = jss::AccountSet;
395 tx_json[sfSigners].
append(
"1");
396 params[jss::tx_json] = tx_json;
398 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
400 resp[jss::result][jss::error_message] ==
401 "Invalid field 'tx.Signers[0]'.");
407 tx_json[jss::TransactionType] = jss::AccountSet;
409 tx_json[
"foo"] =
"bar";
410 params[jss::tx_json] = tx_json;
412 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
414 resp[jss::result][jss::error_message] ==
415 "Field 'tx_json.foo' is unknown.");
420 tx_json[jss::TransactionType] = jss::AccountSet;
421 tx_json[jss::Account] = alice.
human();
422 auto const resp = env.
rpc(
"simulate",
to_string(tx_json),
"1");
423 BEAST_EXPECT(resp[jss::error_message] ==
"Invalid parameters.");
429 tx_json[jss::TransactionType] = jss::AccountSet;
431 tx_json[jss::TxnSignature] =
"1200ABCD";
432 params[jss::tx_json] = tx_json;
434 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
436 resp[jss::result][jss::error_message] ==
437 "Transaction should not be signed.");
443 tx_json[jss::TransactionType] = jss::AccountSet;
450 signer[jss::TxnSignature] =
"1200ABCD";
452 signerOuter[sfSigner] =
signer;
453 tx_json[sfSigners].
append(signerOuter);
455 params[jss::tx_json] = tx_json;
457 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
459 resp[jss::result][jss::error_message] ==
460 "Transaction should not be signed.");
472 cfg->section(
"transaction_queue")
473 .
set(
"minimum_txn_in_ledger_standalone",
"3");
483 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
488 params[jss::tx_json] =
noop(alice);
490 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
491 auto const result = resp[jss::result];
492 if (BEAST_EXPECT(result.isMember(jss::error)))
494 BEAST_EXPECT(result[jss::error] ==
"highFee");
495 BEAST_EXPECT(result[jss::error_code] ==
rpcHIGH_FEE);
503 testcase(
"Invalid transaction type");
511 env.
fund(
XRP(1000000), alice, bob);
515 auto const seq = env.
seq(alice);
523 params[jss::tx_json] = jt.jv;
524 auto const resp = env.
rpc(
"json",
"simulate",
to_string(params));
525 BEAST_EXPECT(resp[jss::result][jss::error] ==
"notImpl");
527 resp[jss::result][jss::error_message] ==
"Not implemented.");
540 static auto const newDomain =
"123ABC";
545 auto result = resp[jss::result];
547 result, tx, 1, env.
current()->fees().base);
549 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
550 BEAST_EXPECT(result[jss::engine_result_code] == 0);
552 result[jss::engine_result_message] ==
553 "The simulated transaction would have been applied.");
556 result.isMember(jss::meta) ||
557 result.isMember(jss::meta_blob)))
562 metadata.
isMember(sfAffectedNodes.jsonName)))
565 metadata[sfAffectedNodes.jsonName].
size() == 1);
566 auto node = metadata[sfAffectedNodes.jsonName][0u];
568 node.isMember(sfModifiedNode.jsonName)))
570 auto modifiedNode = node[sfModifiedNode];
572 modifiedNode[sfLedgerEntryType] ==
574 auto finalFields = modifiedNode[sfFinalFields];
575 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
578 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
580 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
587 tx[jss::TransactionType] = jss::AccountSet;
588 tx[sfDomain] = newDomain;
591 testTx(env, tx, validateOutput);
593 tx[sfSigningPubKey] =
"";
594 tx[sfTxnSignature] =
"";
596 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
599 testTx(env, tx, validateOutput);
606 testcase(
"Transaction non-tec failure");
616 auto result = resp[jss::result];
618 result, tx, 1, env.
current()->fees().base);
620 BEAST_EXPECT(result[jss::engine_result] ==
"temBAD_AMOUNT");
621 BEAST_EXPECT(result[jss::engine_result_code] == -298);
623 result[jss::engine_result_message] ==
624 "Malformed: Bad amount.");
627 !result.isMember(jss::meta) &&
628 !result.isMember(jss::meta_blob));
634 tx[jss::TransactionType] = jss::Payment;
635 tx[sfDestination] = alice.
human();
639 testTx(env, tx, testSimulation);
641 tx[sfSigningPubKey] =
"";
642 tx[sfTxnSignature] =
"";
644 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
647 testTx(env, tx, testSimulation);
654 testcase(
"Transaction tec failure");
664 auto result = resp[jss::result];
666 result, tx, 1, env.
current()->fees().base);
669 result[jss::engine_result] ==
"tecNO_DST_INSUF_XRP");
670 BEAST_EXPECT(result[jss::engine_result_code] == 125);
672 result[jss::engine_result_message] ==
673 "Destination does not exist. Too little XRP sent to "
678 result.isMember(jss::meta) ||
679 result.isMember(jss::meta_blob)))
684 metadata.
isMember(sfAffectedNodes.jsonName)))
687 metadata[sfAffectedNodes.jsonName].
size() == 1);
688 auto node = metadata[sfAffectedNodes.jsonName][0u];
690 node.isMember(sfModifiedNode.jsonName)))
692 auto modifiedNode = node[sfModifiedNode];
694 modifiedNode[sfLedgerEntryType] ==
696 auto finalFields = modifiedNode[sfFinalFields];
698 finalFields[sfBalance] ==
700 100'000'000'000'000'000 -
701 env.
current()->fees().base.drops()));
705 metadata[sfTransactionIndex.jsonName] == 0);
707 metadata[sfTransactionResult.jsonName] ==
708 "tecNO_DST_INSUF_XRP");
715 tx[jss::TransactionType] = jss::Payment;
716 tx[sfDestination] = alice.
human();
720 testTx(env, tx, testSimulation);
722 tx[sfSigningPubKey] =
"";
723 tx[sfTxnSignature] =
"";
725 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
728 testTx(env, tx, testSimulation);
735 testcase(
"Successful multi-signed transaction");
739 static auto const newDomain =
"123ABC";
747 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
753 auto result = resp[jss::result];
761 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
762 BEAST_EXPECT(result[jss::engine_result_code] == 0);
764 result[jss::engine_result_message] ==
765 "The simulated transaction would have been applied.");
768 result.isMember(jss::meta) ||
769 result.isMember(jss::meta_blob)))
774 metadata.
isMember(sfAffectedNodes.jsonName)))
777 metadata[sfAffectedNodes.jsonName].
size() == 1);
778 auto node = metadata[sfAffectedNodes.jsonName][0u];
780 node.isMember(sfModifiedNode.jsonName)))
782 auto modifiedNode = node[sfModifiedNode];
784 modifiedNode[sfLedgerEntryType] ==
786 auto finalFields = modifiedNode[sfFinalFields];
787 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
790 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
792 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
798 tx[jss::Account] = alice.
human();
799 tx[jss::TransactionType] = jss::AccountSet;
800 tx[sfDomain] = newDomain;
803 testTx(env, tx, validateOutput,
false);
810 signerOuter[sfSigner] =
signer;
811 tx[sfSigners].
append(signerOuter);
815 testTx(env, tx, validateOutput,
false);
817 tx[sfSigningPubKey] =
"";
818 tx[sfTxnSignature] =
"";
819 tx[sfSequence] = env.
seq(alice);
823 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
"";
824 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
827 testTx(env, tx, validateOutput);
834 testcase(
"Transaction with a key-related failure");
838 static auto const newDomain =
"123ABC";
848 auto result = resp[jss::result];
856 result[jss::engine_result] ==
"tefMASTER_DISABLED");
857 BEAST_EXPECT(result[jss::engine_result_code] == -188);
859 result[jss::engine_result_message] ==
860 "Master key is disabled.");
863 !result.isMember(jss::meta) &&
864 !result.isMember(jss::meta_blob));
870 tx[jss::TransactionType] = jss::AccountSet;
871 tx[sfDomain] = newDomain;
876 testTx(env, tx, testSimulation);
878 tx[sfTxnSignature] =
"";
880 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
883 testTx(env, tx, testSimulation);
891 "Transaction with both single-signing SigningPubKey and "
892 "multi-signing Signers");
896 static auto const newDomain =
"123ABC";
904 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
911 auto result = resp[jss::result];
916 env.
current()->fees().base * 2);
918 BEAST_EXPECT(result[jss::engine_result] ==
"temINVALID");
919 BEAST_EXPECT(result[jss::engine_result_code] == -277);
921 result[jss::engine_result_message] ==
922 "The transaction is ill-formed.");
925 !result.isMember(jss::meta) &&
926 !result.isMember(jss::meta_blob));
932 tx[jss::TransactionType] = jss::AccountSet;
933 tx[sfDomain] = newDomain;
941 signerOuter[sfSigner] =
signer;
942 tx[sfSigners].
append(signerOuter);
946 testTx(env, tx, testSimulation,
false);
948 tx[sfTxnSignature] =
"";
950 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
951 tx[sfSigners][0u][sfSigner][jss::SigningPubKey] =
953 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
956 testTx(env, tx, testSimulation);
963 testcase(
"Multi-signed transaction with a bad public key");
967 static auto const newDomain =
"123ABC";
976 env(
signers(alice, 1, {{becky, 1}, {carol, 1}}));
981 auto result = resp[jss::result];
983 result, tx, env.
seq(alice), env.
current()->fees().base * 2);
986 result[jss::engine_result] ==
"tefBAD_SIGNATURE",
987 result[jss::engine_result].toStyledString());
988 BEAST_EXPECT(result[jss::engine_result_code] == -186);
990 result[jss::engine_result_message] ==
991 "A signature is provided for a non-signer.");
994 !result.isMember(jss::meta) &&
995 !result.isMember(jss::meta_blob));
1000 tx[jss::Account] = alice.
human();
1001 tx[jss::TransactionType] = jss::AccountSet;
1002 tx[sfDomain] = newDomain;
1009 signerOuter[sfSigner] =
signer;
1010 tx[sfSigners].
append(signerOuter);
1014 testTx(env, tx, validateOutput,
false);
1016 tx[sfSigningPubKey] =
"";
1017 tx[sfTxnSignature] =
"";
1018 tx[sfSequence] = env.
seq(alice);
1022 tx[sfSigners][0u][sfSigner][jss::TxnSignature] =
"";
1025 testTx(env, tx, validateOutput);
1032 testcase(
"Credentials aren't actually deleted on `tecEXPIRED`");
1036 using namespace jtx;
1039 Account const subject{
"subject"};
1040 Account const issuer{
"issuer"};
1042 env.
fund(
XRP(10000), subject, issuer);
1045 auto const credType =
"123ABC";
1049 env.
current()->info().parentCloseTime.time_since_epoch().count();
1050 jv[sfExpiration.jsonName] = t;
1055 auto validateOutput = [&](
Json::Value const& resp,
1057 auto result = resp[jss::result];
1059 result, tx, env.
seq(subject), env.
current()->fees().base);
1061 BEAST_EXPECT(result[jss::engine_result] ==
"tecEXPIRED");
1062 BEAST_EXPECT(result[jss::engine_result_code] == 148);
1064 result[jss::engine_result_message] ==
1065 "Expiration time is passed.");
1068 result.isMember(jss::meta) ||
1069 result.isMember(jss::meta_blob)))
1074 metadata.
isMember(sfAffectedNodes.jsonName)))
1077 metadata[sfAffectedNodes.jsonName].
size() == 5);
1082 for (
auto const& node :
1083 metadata[sfAffectedNodes.jsonName])
1085 if (node.isMember(sfDeletedNode.jsonName) &&
1086 node[sfDeletedNode.jsonName]
1087 [sfLedgerEntryType.jsonName]
1088 .asString() ==
"Credential")
1090 auto const deleted =
1091 node[sfDeletedNode.jsonName]
1092 [sfFinalFields.jsonName];
1093 found = deleted[jss::Issuer] ==
1095 deleted[jss::Subject] ==
1097 deleted[
"CredentialType"] ==
1102 BEAST_EXPECT(found);
1109 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1111 metadata[sfTransactionResult.jsonName] ==
"tecEXPIRED");
1118 testTx(env, tx, validateOutput);
1120 tx[sfSigningPubKey] =
"";
1121 tx[sfTxnSignature] =
"";
1122 tx[sfSequence] = env.
seq(subject);
1123 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1126 testTx(env, tx, validateOutput);
1133 jle.isObject() && jle.isMember(jss::result) &&
1134 !jle[jss::result].isMember(jss::error) &&
1135 jle[jss::result].isMember(jss::node) &&
1136 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1137 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1138 jle[jss::result][jss::node][jss::Issuer] == issuer.human() &&
1139 jle[jss::result][jss::node][jss::Subject] == subject.human() &&
1140 jle[jss::result][jss::node][
"CredentialType"] ==
1150 testcase(
"Successful transaction with a custom network ID");
1152 using namespace jtx;
1154 cfg->NETWORK_ID = 1025;
1157 static auto const newDomain =
"123ABC";
1160 auto validateOutput = [&](
Json::Value const& resp,
1162 auto result = resp[jss::result];
1164 result, tx, 1, env.
current()->fees().base);
1166 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1167 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1169 result[jss::engine_result_message] ==
1170 "The simulated transaction would have been applied.");
1173 result.isMember(jss::meta) ||
1174 result.isMember(jss::meta_blob)))
1179 metadata.
isMember(sfAffectedNodes.jsonName)))
1182 metadata[sfAffectedNodes.jsonName].
size() == 1);
1183 auto node = metadata[sfAffectedNodes.jsonName][0u];
1185 node.isMember(sfModifiedNode.jsonName)))
1187 auto modifiedNode = node[sfModifiedNode];
1189 modifiedNode[sfLedgerEntryType] ==
1191 auto finalFields = modifiedNode[sfFinalFields];
1192 BEAST_EXPECT(finalFields[sfDomain] == newDomain);
1195 BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0);
1197 metadata[sfTransactionResult.jsonName] ==
"tesSUCCESS");
1204 tx[jss::TransactionType] = jss::AccountSet;
1205 tx[sfDomain] = newDomain;
1208 testTx(env, tx, validateOutput);
1210 tx[sfSigningPubKey] =
"";
1211 tx[sfTxnSignature] =
"";
1213 tx[sfFee] = env.
current()->fees().base.jsonClipped().asString();
1214 tx[sfNetworkID] = 1025;
1217 testTx(env, tx, validateOutput);
1224 testcase(
"Successful transaction with additional metadata");
1226 using namespace jtx;
1227 using namespace std::chrono_literals;
1229 cfg->NETWORK_ID = 1025;
1236 env.
fund(
XRP(10000), alice, bob);
1243 auto validateOutput =
1248 auto result = resp[jss::result];
1250 BEAST_EXPECT(result[jss::engine_result] ==
"tesSUCCESS");
1251 BEAST_EXPECT(result[jss::engine_result_code] == 0);
1253 result[jss::engine_result_message] ==
1254 "The simulated transaction would have been applied.");
1257 result.isMember(jss::meta) ||
1258 result.isMember(jss::meta_blob)))
1263 metadata[sfTransactionIndex.jsonName] == 0);
1265 metadata[sfTransactionResult.jsonName] ==
1268 metadata.
isMember(expectedMetadataKey.asString()));
1270 metadata[expectedMetadataKey.asString()] ==
1271 expectedMetadataValue);
1277 tx[jss::Account] = alice.
human();
1278 tx[jss::TransactionType] = jss::Payment;
1279 tx[sfDestination] = bob.
human();
1280 tx[sfAmount] =
"100";
1284 env, tx, validateOutput, jss::delivered_amount,
"100");
1289 tx[jss::Account] = alice.
human();
1290 tx[jss::TransactionType] = jss::NFTokenMint;
1291 tx[sfNFTokenTaxon] = 1;
1297 env, tx, validateOutput, jss::nftoken_id, nftokenId);
1302 tx[jss::Account] = alice.
human();
1303 tx[jss::TransactionType] = jss::MPTokenIssuanceCreate;
1312 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.