325 testcase(
"unsteppedThenSteppedWithNFTs");
334 auto const USD1 = gw1[
"USD"];
335 auto const USD2 = gw2[
"USD"];
337 env.
fund(
XRP(1000), gw1, gw2, bob);
344 params[jss::account] = bob.human();
345 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
346 BEAST_EXPECT(!resp.isMember(jss::marker));
347 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
350 params[jss::limit] = 1;
351 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
352 BEAST_EXPECT(!resp.isMember(jss::marker));
353 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
365 params[jss::account] = bob.human();
366 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
367 BEAST_EXPECT(!resp.isMember(jss::marker));
369 unpaged = resp[jss::result][jss::account_objects];
370 BEAST_EXPECT(unpaged.
size() == 1);
375 params[jss::account] = bob.human();
376 params[jss::type] = jss::nft_page;
377 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
378 BEAST_EXPECT(!resp.isMember(jss::marker));
379 Json::Value& aobjs = resp[jss::result][jss::account_objects];
380 BEAST_EXPECT(aobjs.
size() == 1);
381 BEAST_EXPECT(aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
382 BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].
size() == 1);
387 params[jss::account] = bob.human();
388 params[jss::limit] = 1;
391 Json::Value& aobjs = resp[jss::result][jss::account_objects];
392 BEAST_EXPECT(aobjs.
size() == 1);
393 auto& aobj = aobjs[0U];
394 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
395 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
397 BEAST_EXPECT(aobj == unpaged[0u]);
401 env.
trust(USD1(1000), bob);
402 env.
trust(USD2(1000), bob);
404 env(
pay(gw1, bob, USD1(1000)));
405 env(
pay(gw2, bob, USD2(1000)));
414 params[jss::account] = bob.human();
415 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
416 BEAST_EXPECT(!resp.isMember(jss::marker));
418 unpaged = resp[jss::result][jss::account_objects];
419 BEAST_EXPECT(unpaged.
size() == 5);
424 params[jss::account] = bob.human();
425 params[jss::type] = jss::nft_page;
426 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
427 BEAST_EXPECT(!resp.isMember(jss::marker));
428 Json::Value& aobjs = resp[jss::result][jss::account_objects];
429 BEAST_EXPECT(aobjs.
size() == 1);
430 BEAST_EXPECT(aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
431 BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].
size() == 1);
436 params[jss::account] = bob.human();
437 params[jss::limit] = 1;
438 for (
int i = 0; i < 5; ++i)
441 Json::Value& aobjs = resp[jss::result][jss::account_objects];
442 BEAST_EXPECT(aobjs.
size() == 1);
443 auto& aobj = aobjs[0U];
446 BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
447 BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
451 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
452 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
455 BEAST_EXPECT(aobj == unpaged[i]);
457 params[jss::marker] = resp[jss::result][jss::marker];
462 for (
int i = 0; i < 32; ++i)
470 params[jss::account] = bob.human();
471 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
472 BEAST_EXPECT(!resp.isMember(jss::marker));
474 unpaged = resp[jss::result][jss::account_objects];
475 BEAST_EXPECT(unpaged.
size() == 6);
480 params[jss::account] = bob.human();
481 params[jss::type] = jss::nft_page;
482 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
483 BEAST_EXPECT(!resp.isMember(jss::marker));
484 Json::Value& aobjs = resp[jss::result][jss::account_objects];
485 BEAST_EXPECT(aobjs.
size() == 2);
490 params[jss::account] = bob.human();
491 params[jss::limit] = 1;
492 for (
int i = 0; i < 6; ++i)
495 Json::Value& aobjs = resp[jss::result][jss::account_objects];
496 BEAST_EXPECT(aobjs.
size() == 1);
497 auto& aobj = aobjs[0U];
500 BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
501 BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
505 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
506 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
509 BEAST_EXPECT(aobj == unpaged[i]);
511 params[jss::marker] = resp[jss::result][jss::marker];
527 auto const USD = gw[
"USD"];
529 auto const features =
testable_amendments() | featureXChainBridge | featurePermissionedDomains;
530 Env env(*
this, features);
533 auto acctObjs = [&env](
541 params[jss::type] = *type;
543 params[jss::limit] = *limit;
545 params[jss::marker] = *marker;
546 params[jss::ledger_index] =
"validated";
547 return env.
rpc(
"json",
"account_objects",
to_string(params));
551 auto acctObjsIsSize = [](
Json::Value const& resp,
unsigned size) {
552 return resp[jss::result][jss::account_objects].
isArray() &&
553 (resp[jss::result][jss::account_objects].
size() == size);
557 auto acctObjsTypeIsInvalid = [](
Json::Value const& resp) {
558 return resp[jss::result].
isMember(jss::error) &&
559 resp[jss::result][jss::error_message] ==
"Invalid field \'type\'.";
562 env.
fund(
XRP(10000), gw, alice);
567 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
568 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::check), 0));
569 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::deposit_preauth), 0));
570 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::escrow), 0));
571 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::nft_page), 0));
572 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::offer), 0));
573 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::payment_channel), 0));
574 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::signer_list), 0));
575 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::state), 0));
576 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::ticket), 0));
577 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
578 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::did), 0));
579 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::permissioned_domain), 0));
582 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
583 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
584 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
585 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
586 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
594 Json::Value const resp = acctObjs(gw, jss::nft_page);
595 BEAST_EXPECT(acctObjsIsSize(resp, 1));
597 auto const& nftPage = resp[jss::result][jss::account_objects][0u];
598 BEAST_EXPECT(nftPage[sfNFTokens.jsonName].size() == 1);
600 nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName][sfNFTokenID.jsonName] ==
to_string(nftID));
604 env.
trust(USD(1000), alice);
606 env(
pay(gw, alice, USD(5)));
611 BEAST_EXPECT(acctObjsIsSize(resp, 1));
613 auto const& state = resp[jss::result][jss::account_objects][0u];
614 BEAST_EXPECT(state[sfBalance.jsonName][jss::value].asInt() == -5);
615 BEAST_EXPECT(state[sfHighLimit.jsonName][jss::value].asUInt() == 1000);
623 BEAST_EXPECT(acctObjsIsSize(resp, 1));
625 auto const& check = resp[jss::result][jss::account_objects][0u];
626 BEAST_EXPECT(check[sfAccount.jsonName] == gw.human());
627 BEAST_EXPECT(check[sfDestination.jsonName] == alice.human());
628 BEAST_EXPECT(check[sfSendMax.jsonName][jss::value].asUInt() == 10);
635 Json::Value const resp = acctObjs(gw, jss::deposit_preauth);
636 BEAST_EXPECT(acctObjsIsSize(resp, 1));
638 auto const& preauth = resp[jss::result][jss::account_objects][0u];
639 BEAST_EXPECT(preauth[sfAccount.jsonName] == gw.human());
640 BEAST_EXPECT(preauth[sfAuthorize.jsonName] == alice.human());
645 jvEscrow[jss::TransactionType] = jss::EscrowCreate;
646 jvEscrow[jss::Account] = gw.human();
647 jvEscrow[jss::Destination] = gw.human();
655 Json::Value const resp = acctObjs(gw, jss::escrow);
656 BEAST_EXPECT(acctObjsIsSize(resp, 1));
658 auto const& escrow = resp[jss::result][jss::account_objects][0u];
659 BEAST_EXPECT(escrow[sfAccount.jsonName] == gw.human());
660 BEAST_EXPECT(escrow[sfDestination.jsonName] == gw.human());
661 BEAST_EXPECT(escrow[sfAmount.jsonName].asUInt() == 100'000'000);
674 Json::Value const resp = acctObjs(gw, jss::permissioned_domain);
675 BEAST_EXPECT(acctObjsIsSize(resp, 1));
677 auto const& permissionedDomain = resp[jss::result][jss::account_objects][0u];
678 BEAST_EXPECT(permissionedDomain.isMember(jss::Owner) && (permissionedDomain[jss::Owner] == gw.human()));
679 bool const check1 = BEAST_EXPECT(
680 permissionedDomain.isMember(jss::AcceptedCredentials) &&
681 permissionedDomain[jss::AcceptedCredentials].isArray() &&
682 (permissionedDomain[jss::AcceptedCredentials].size() == 1) &&
683 (permissionedDomain[jss::AcceptedCredentials][0u].isMember(jss::Credential)));
687 auto const& credential = permissionedDomain[jss::AcceptedCredentials][0u][jss::Credential];
689 credential.isMember(sfIssuer.jsonName) && (credential[sfIssuer.jsonName] == issuer.
human()));
691 credential.isMember(sfCredentialType.jsonName) &&
692 (credential[sfCredentialType.jsonName] ==
strHex(credentialType1)));
702 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
704 params[jss::account] = acct.
human();
705 params[jss::type] = type;
706 params[jss::ledger_index] =
"validated";
707 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
712 BEAST_EXPECT(acctObjsIsSize(resp, 1));
713 auto const& acct_bridge = resp[jss::result][jss::account_objects][0u];
715 BEAST_EXPECT(acct_bridge[sfLedgerEntryType.getJsonName()] ==
"Bridge");
716 BEAST_EXPECT(acct_bridge[sfXChainClaimID.getJsonName()].asUInt() == 0);
717 BEAST_EXPECT(acct_bridge[sfXChainAccountClaimCount.getJsonName()].asUInt() == 0);
718 BEAST_EXPECT(acct_bridge[sfXChainAccountCreateCount.getJsonName()].asUInt() == 0);
719 BEAST_EXPECT(acct_bridge[sfMinAccountCreateAmount.getJsonName()].asUInt() == 20000000);
720 BEAST_EXPECT(acct_bridge[sfSignatureReward.getJsonName()].asUInt() == 1000000);
721 BEAST_EXPECT(acct_bridge[sfXChainBridge.getJsonName()] == x.
jvb);
735 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
737 params[jss::account] = acct.
human();
738 params[jss::type] = type;
739 params[jss::ledger_index] =
"validated";
740 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
746 BEAST_EXPECT(acctObjsIsSize(resp, 1));
748 auto const& xchain_seq = resp[jss::result][jss::account_objects][0u];
749 BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.
scAlice.
human());
750 BEAST_EXPECT(xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 1);
754 Json::Value const resp = scEnvAcctObjs(x.
scBob, jss::xchain_owned_claim_id);
755 BEAST_EXPECT(acctObjsIsSize(resp, 1));
757 auto const& xchain_seq = resp[jss::result][jss::account_objects][0u];
758 BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.
scBob.
human());
759 BEAST_EXPECT(xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 2);
766 auto const amt =
XRP(1000);
776 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
778 params[jss::account] = acct.
human();
779 params[jss::type] = type;
780 params[jss::ledger_index] =
"validated";
781 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
787 BEAST_EXPECT(acctObjsIsSize(resp, 1));
789 auto const& xchain_create_account_claim_id = resp[jss::result][jss::account_objects][0u];
791 BEAST_EXPECT(xchain_create_account_claim_id[sfXChainAccountCreateCount.getJsonName()].asUInt() == 1);
801 BEAST_EXPECT(acctObjsIsSize(resp, 1));
803 auto const&
offer = resp[jss::result][jss::account_objects][0u];
804 BEAST_EXPECT(
offer[sfAccount.jsonName] == gw.human());
805 BEAST_EXPECT(
offer[sfTakerGets.jsonName].
asUInt() == 14'000'000);
806 BEAST_EXPECT(
offer[sfTakerPays.jsonName][jss::value].
asUInt() == 7);
812 jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
813 jvPayChan[jss::Account] = gw.human();
814 jvPayChan[jss::Destination] = alice.human();
816 jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
817 jvPayChan[sfPublicKey.jsonName] =
strHex(gw.pk().slice());
823 Json::Value const resp = acctObjs(gw, jss::payment_channel);
824 BEAST_EXPECT(acctObjsIsSize(resp, 1));
826 auto const& payChan = resp[jss::result][jss::account_objects][0u];
827 BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
828 BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
829 BEAST_EXPECT(payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
835 jvDID[jss::TransactionType] = jss::DIDSet;
836 jvDID[jss::Account] = gw.human();
844 BEAST_EXPECT(acctObjsIsSize(resp, 1));
846 auto const& did = resp[jss::result][jss::account_objects][0u];
847 BEAST_EXPECT(did[sfAccount.jsonName] == gw.human());
855 Json::Value const resp = acctObjs(gw, jss::signer_list);
856 BEAST_EXPECT(acctObjsIsSize(resp, 1));
858 auto const& signerList = resp[jss::result][jss::account_objects][0u];
859 BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
860 auto const& entry = signerList[sfSignerEntries.jsonName][0u][sfSignerEntry.jsonName];
861 BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
862 BEAST_EXPECT(entry[sfSignerWeight.jsonName].
asUInt() == 7);
866 auto const seq = env.
seq(gw);
872 Json::Value const resp = acctObjs(gw, jss::ticket);
873 BEAST_EXPECT(acctObjsIsSize(resp, 1));
875 auto const& ticket = resp[jss::result][jss::account_objects][0u];
876 BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
877 BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
878 BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() ==
seq + 1);
884 params[jss::account] = gw.human();
885 params[jss::deletion_blockers_only] =
true;
886 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
892 jss::NFTokenPage.c_str(),
893 jss::RippleState.c_str(),
894 jss::PayChannel.c_str(),
895 jss::PermissionedDomain.c_str()};
902 if (BEAST_EXPECT(acctObjsIsSize(resp, expectedAccountObjects)))
904 auto const& aobjs = resp[jss::result][jss::account_objects];
906 gotLedgerTypes.
reserve(expectedAccountObjects);
909 gotLedgerTypes.
push_back(aobjs[i][
"LedgerEntryType"].asString());
912 BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
919 params[jss::account] = gw.human();
920 params[jss::deletion_blockers_only] =
true;
921 params[jss::type] = jss::escrow;
922 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
924 if (BEAST_EXPECT(acctObjsIsSize(resp, 1u)))
926 auto const& aobjs = resp[jss::result][jss::account_objects];
927 BEAST_EXPECT(aobjs[0u][
"LedgerEntryType"] == jss::Escrow);
933 auto const objs = resp[jss::result][jss::account_objects];
934 for (
auto const& obj : resp[jss::result][jss::account_objects])
935 typesOut.push_back(obj[sfLedgerEntryType.fieldName].asString());
936 std::sort(typesOut.begin(), typesOut.end());
941 if (!acctObjsIsSize(resp, types.
size()))
944 getTypes(resp, typesOut);
945 return types == typesOut;
948 AMM amm(env, gw,
XRP(1'000), USD(1'000));
949 amm.deposit(alice, USD(1));
952 BEAST_EXPECT(
lines[jss::lines].size() == 3);
954 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::amm), 1));
956 auto resp = acctObjs(amm.ammAccount(),
std::nullopt, 2);
958 getTypes(resp, typesOut);
961 getTypes(resp, typesOut);
965 jss::AMM.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
967 resp = acctObjs(amm.ammAccount(), jss::state, 10);
969 expectObjects(resp, {jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
971 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::offer), 0));
973 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
977 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
978 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
979 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
980 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
981 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
985 for (
int d = 1'000'032; d >= 1'000'000; --d)
992 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
1138 using namespace jtx;
1144 env.
fund(
XRP(10000), alice, bob, carol);
1146 unsigned const accountObjectSize = 30;
1147 for (
unsigned i = 0; i < accountObjectSize; i++)
1150 for (
unsigned i = 0; i < 10; i++)
1155 unsigned const limit = 11;
1161 params[jss::account] = bob.human();
1162 params[jss::limit] = limit;
1163 params[jss::ledger_index] =
"validated";
1164 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1165 auto& accountObjects = resp[jss::result][jss::account_objects];
1166 marker = resp[jss::result][jss::marker];
1167 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1168 BEAST_EXPECT(accountObjects.size() == limit);
1174 params[jss::account] = bob.human();
1175 params[jss::limit] = limit;
1176 params[jss::marker] = marker;
1177 params[jss::ledger_index] =
"validated";
1178 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1179 auto& accountObjects = resp[jss::result][jss::account_objects];
1180 marker = resp[jss::result][jss::marker];
1181 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1182 BEAST_EXPECT(accountObjects.size() == limit);
1186 auto testInvalidMarker = [&](
std::string& marker) {
1188 params[jss::account] = bob.human();
1189 params[jss::limit] = limit;
1190 params[jss::ledger_index] = jss::validated;
1191 params[jss::marker] = marker;
1193 return resp[jss::result][jss::error_message] ==
"Invalid field \'marker\'.";
1196 auto const markerStr = marker.
asString();
1197 auto const& idx = markerStr.
find(
',');
1198 auto const dirIndex = markerStr.substr(0, idx);
1199 auto const entryIndex = markerStr.substr(idx + 1);
1204 BEAST_EXPECT(testInvalidMarker(s));
1211 BEAST_EXPECT(testInvalidMarker(s));
1219 BEAST_EXPECT(testInvalidMarker(s));
1227 s = dirIndex +
',' + s;
1228 BEAST_EXPECT(testInvalidMarker(s));
1235 BEAST_EXPECT(testInvalidMarker(s));
1244 params[jss::account] = bob.human();
1245 params[jss::limit] = limit;
1246 params[jss::marker] = s;
1247 params[jss::ledger_index] =
"validated";
1248 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1249 auto& accountObjects = resp[jss::result][jss::account_objects];
1250 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1251 BEAST_EXPECT(accountObjects.size() == limit);
1258 BEAST_EXPECT(testInvalidMarker(s));
1265 BEAST_EXPECT(testInvalidMarker(s));
1272 params[jss::account] = bob.human();
1273 params[jss::limit] = limit;
1274 params[jss::marker] = marker;
1275 params[jss::ledger_index] =
"validated";
1276 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1277 auto& accountObjects = resp[jss::result][jss::account_objects];
1278 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1279 BEAST_EXPECT(accountObjects.size() == accountObjectSize - limit * 2);
1280 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
1287 params[jss::account] = carol.human();
1288 params[jss::limit] = 10;
1289 params[jss::marker] =
"0," + entryIndex;
1290 params[jss::ledger_index] =
"validated";
1291 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1292 auto& accountObjects = resp[jss::result][jss::account_objects];
1293 BEAST_EXPECT(accountObjects.size() == 0);